diff --git a/crates/oxc_linter/src/module_record.rs b/crates/oxc_linter/src/module_record.rs index a99d78adb..439b7c552 100644 --- a/crates/oxc_linter/src/module_record.rs +++ b/crates/oxc_linter/src/module_record.rs @@ -291,6 +291,19 @@ pub struct ExportEntry { /// The name that is used to locally access the exported value from within the importing module. /// null if the exported value is not locally accessible from within the module. pub local_name: ExportLocalName, + + /// Whether the export is a TypeScript `export type`. + /// + /// Examples: + /// + /// ```ts + /// export type * from 'mod' + /// export type * as ns from 'mod' + /// export type { foo } + /// export { type foo } + /// export type { foo } from 'mod' + /// ``` + pub is_type: bool, } impl<'a> From<&oxc_syntax::module_record::ExportEntry<'a>> for ExportEntry { @@ -301,6 +314,7 @@ impl<'a> From<&oxc_syntax::module_record::ExportEntry<'a>> for ExportEntry { import_name: ExportImportName::from(&other.import_name), export_name: ExportExportName::from(&other.export_name), local_name: ExportLocalName::from(&other.local_name), + is_type: other.is_type, } } } diff --git a/crates/oxc_linter/src/rules/import/export.rs b/crates/oxc_linter/src/rules/import/export.rs index 04bc88f81..96f87fb7d 100644 --- a/crates/oxc_linter/src/rules/import/export.rs +++ b/crates/oxc_linter/src/rules/import/export.rs @@ -56,6 +56,9 @@ impl Rule for Export { let mut visited = FxHashSet::default(); module_record.star_export_entries.iter().for_each(|star_export_entry| { + if star_export_entry.is_type { + return; + } let mut export_names = FxHashSet::default(); let Some(module_request) = &star_export_entry.module_request else { @@ -266,6 +269,7 @@ fn test() { const Bar = 2; export {Bar as default}; "#), + "export type * from './export-props.js'", ]; let fail = vec![ (r#"let foo; export { foo }; export * from "./export-all""#), diff --git a/crates/oxc_parser/src/module_record.rs b/crates/oxc_parser/src/module_record.rs index 5b295244a..228b0b6fe 100644 --- a/crates/oxc_parser/src/module_record.rs +++ b/crates/oxc_parser/src/module_record.rs @@ -154,6 +154,7 @@ impl<'a> ModuleRecordBuilder<'a> { }, export_name: ee.export_name.clone(), local_name: ExportLocalName::default(), + is_type: ie.is_type, }; self.append_indirect_export_entry(export_entry); } @@ -256,6 +257,7 @@ impl<'a> ModuleRecordBuilder<'a> { ExportExportName::Name(NameSpan::new(exported_name.name(), exported_name.span())) }), local_name: ExportLocalName::default(), + is_type: decl.export_kind.is_type(), }; self.add_export_entry(export_entry); if let Some(exported_name) = &decl.exported { @@ -302,6 +304,7 @@ impl<'a> ModuleRecordBuilder<'a> { import_name: ExportImportName::default(), export_name: ExportExportName::Default(exported_name.span()), local_name, + is_type: false, }; self.add_export_entry(export_entry); } @@ -343,6 +346,7 @@ impl<'a> ModuleRecordBuilder<'a> { import_name: ExportImportName::Null, export_name, local_name, + is_type: decl.export_kind.is_type(), }; self.add_export_entry(export_entry); self.add_export_binding(ident.name.clone(), ident.span); @@ -377,6 +381,7 @@ impl<'a> ModuleRecordBuilder<'a> { import_name, export_name, local_name, + is_type: specifier.export_kind.is_type() || decl.export_kind.is_type(), }; self.add_export_entry(export_entry); self.add_export_binding(specifier.exported.name().clone(), specifier.exported.span()); @@ -670,6 +675,7 @@ mod module_record_tests { import_name: ExportImportName::Name(NameSpan::new("x".into(), Span::new(9, 10))), export_name: ExportExportName::Name(NameSpan::new("x".into(), Span::new(33, 34))), local_name: ExportLocalName::Null, + is_type: false } ); assert_eq!( @@ -681,6 +687,7 @@ mod module_record_tests { import_name: ExportImportName::All, export_name: ExportExportName::Name(NameSpan::new("ns".into(), Span::new(49, 51))), local_name: ExportLocalName::Null, + is_type: false } ); } diff --git a/crates/oxc_syntax/src/module_record.rs b/crates/oxc_syntax/src/module_record.rs index 21836d827..b5e6844fe 100644 --- a/crates/oxc_syntax/src/module_record.rs +++ b/crates/oxc_syntax/src/module_record.rs @@ -229,6 +229,19 @@ pub struct ExportEntry<'a> { /// The name that is used to locally access the exported value from within the importing module. /// null if the exported value is not locally accessible from within the module. pub local_name: ExportLocalName<'a>, + + /// Whether the export is a TypeScript `export type`. + /// + /// Examples: + /// + /// ```ts + /// export type * from 'mod' + /// export type * as ns from 'mod' + /// export type { foo } + /// export { type foo } + /// export type { foo } from 'mod' + /// ``` + pub is_type: bool, } /// `ImportName` for `ExportEntry`