From c0417bdf6276b44de711d4ed68e5a54d3cd208af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Sun, 25 Aug 2024 12:21:35 +0800 Subject: [PATCH 1/6] feat(module_lexer): distinguish for types-only imports and exports --- crates/oxc_module_lexer/src/lib.rs | 25 ++++++++- crates/oxc_module_lexer/tests/typescript.rs | 56 +++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 crates/oxc_module_lexer/tests/typescript.rs diff --git a/crates/oxc_module_lexer/src/lib.rs b/crates/oxc_module_lexer/src/lib.rs index 3484fba3c28ca..c032326bd2b4b 100644 --- a/crates/oxc_module_lexer/src/lib.rs +++ b/crates/oxc_module_lexer/src/lib.rs @@ -36,6 +36,9 @@ pub struct ImportSpecifier<'a> { /// If this import has an import assertion, this is the start value pub a: Option, + + /// If this import is for types only + pub t: bool, } #[derive(Debug, Clone)] @@ -57,6 +60,9 @@ pub struct ExportSpecifier<'a> { /// End of local name pub le: Option, + + /// If this export is for types only + pub t: bool, } #[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] @@ -142,6 +148,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { se: prop.span.end, d: ImportType::ImportMeta, a: None, + t: false, }); } walk_meta_property(self, prop); @@ -164,6 +171,8 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { se: expr.span.end, d: ImportType::DynamicImport(expr.span.start + 6), a: expr.arguments.first().map(|e| e.span().start), + // TODO + t: false, }); walk_import_expression(self, expr); } @@ -182,6 +191,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { se: decl.span.end, d: ImportType::StaticImport, a: assertions, + t: decl.import_kind.is_type(), }); walk_import_declaration(self, decl); } @@ -197,6 +207,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { se: decl.span.end, d: ImportType::StaticImport, a: None, + t: decl.export_kind.is_type(), }); } @@ -213,6 +224,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { e: ident.span.end, ls: None, le: None, + t: false, }); }); } @@ -232,6 +244,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { e: exported_end, ls: Some(s.local.span().start), le: Some(s.local.span().end), + t: decl.export_kind.is_type(), } })); walk_export_named_declaration(self, decl); @@ -254,6 +267,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { e: decl.exported.span().end, ls: None, le: None, + t: false, }); } @@ -263,7 +277,15 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { let n = exported.name().clone(); let s = exported.span().start; let e = exported.span().end; - self.exports.push(ExportSpecifier { n: n.clone(), ln: None, s, e, ls: None, le: None }); + self.exports.push(ExportSpecifier { + n: n.clone(), + ln: None, + s, + e, + ls: None, + le: None, + t: decl.export_kind.is_type(), + }); self.imports.push(ImportSpecifier { n: Some(n), s, @@ -272,6 +294,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { se: decl.span.end, d: ImportType::StaticImport, a: None, + t: decl.export_kind.is_type(), }); } walk_export_all_declaration(self, decl); diff --git a/crates/oxc_module_lexer/tests/typescript.rs b/crates/oxc_module_lexer/tests/typescript.rs new file mode 100644 index 0000000000000..ef72438ec541d --- /dev/null +++ b/crates/oxc_module_lexer/tests/typescript.rs @@ -0,0 +1,56 @@ +use oxc_allocator::Allocator; +use oxc_parser::Parser; +use oxc_span::SourceType; + +#[non_exhaustive] +struct ModuleLexer { + imports: Vec, + exports: Vec, +} + +fn parse(source: &str) -> ModuleLexer { + let allocator = Allocator::default(); + let source_type = SourceType::default().with_module(true).with_typescript_definition(true); + let ret = Parser::new(&allocator, source, source_type).parse(); + assert!(ret.errors.is_empty(), "{source} should not produce errors.\n{:?}", ret.errors); + let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); + ModuleLexer { + imports: module_lexer.imports.into_iter().map(|i| i.t).collect(), + exports: module_lexer.exports.into_iter().map(|e| e.t).collect(), + } +} + +#[test] +fn import_type_named() { + let source = "import type { foo } from 'foo'"; + let impt = &parse(source).imports[0]; + assert!(impt); +} + +#[test] +fn import_type_namespace() { + let source = "import type * as foo from 'foo'"; + let impt = &parse(source).imports[0]; + assert!(impt); +} + +#[test] +fn import_type_default() { + let source = "import type foo from 'foo'"; + let impt = &parse(source).imports[0]; + assert!(impt); +} + +#[test] +fn export_type_named() { + let source = "export type { foo } from 'foo'"; + let expt = &parse(source).exports[0]; + assert!(expt); +} + +#[test] +fn export_type_namespace() { + let source = "export type * as foo from 'foo'"; + let expt = &parse(source).exports[0]; + assert!(expt); +} From 008f1385fab39b212dc0767681bb0680cc695c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Sun, 25 Aug 2024 13:07:49 +0800 Subject: [PATCH 2/6] feat: visit_ts_import_type --- crates/oxc_module_lexer/src/lib.rs | 27 ++++++++- crates/oxc_module_lexer/tests/esm.rs | 42 ++++++++------ crates/oxc_module_lexer/tests/typescript.rs | 64 ++++++++++++++++----- 3 files changed, 100 insertions(+), 33 deletions(-) diff --git a/crates/oxc_module_lexer/src/lib.rs b/crates/oxc_module_lexer/src/lib.rs index c032326bd2b4b..7fbe5c8384d22 100644 --- a/crates/oxc_module_lexer/src/lib.rs +++ b/crates/oxc_module_lexer/src/lib.rs @@ -171,12 +171,37 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { se: expr.span.end, d: ImportType::DynamicImport(expr.span.start + 6), a: expr.arguments.first().map(|e| e.span().start), - // TODO t: false, }); walk_import_expression(self, expr); } + fn visit_ts_import_type(&mut self, impt: &TSImportType<'a>) { + let (source, source_span_start, source_span_end) = + if let TSType::TSLiteralType(literal) = &impt.parameter { + if let TSLiteral::StringLiteral(s) = &literal.literal { + (Some(s.value.clone()), s.span.start, s.span.end) + } else { + let span = literal.span(); + (None, span.start, span.end) + } + } else { + let span = impt.parameter.span(); + (None, span.start, span.end) + }; + + self.imports.push(ImportSpecifier { + n: source, + s: source_span_start, + e: source_span_end, + ss: impt.span.start, + se: impt.span.end, + d: ImportType::DynamicImport(impt.span.start + 6), + a: None, + t: true, + }); + } + fn visit_import_declaration(&mut self, decl: &ImportDeclaration<'a>) { let assertions = decl .with_clause diff --git a/crates/oxc_module_lexer/tests/esm.rs b/crates/oxc_module_lexer/tests/esm.rs index f84389e122fa3..83ee3ccd628ec 100644 --- a/crates/oxc_module_lexer/tests/esm.rs +++ b/crates/oxc_module_lexer/tests/esm.rs @@ -6,32 +6,34 @@ use oxc_parser::Parser; use oxc_span::SourceType; #[derive(Debug, Clone)] -struct ImportSpecifier { - n: Option, - s: u32, - e: u32, - ss: u32, - se: u32, - d: ImportType, - a: Option, +pub struct ImportSpecifier { + pub n: Option, + pub s: u32, + pub e: u32, + pub ss: u32, + pub se: u32, + pub d: ImportType, + pub a: Option, + pub t: bool, } #[derive(Debug, Clone)] pub struct ExportSpecifier { - n: String, - ln: Option, - s: u32, - e: u32, - ls: Option, - le: Option, + pub n: String, + pub ln: Option, + pub s: u32, + pub e: u32, + pub ls: Option, + pub le: Option, + pub t: bool, } #[non_exhaustive] -struct ModuleLexer { - imports: Vec, - exports: Vec, - has_module_syntax: bool, - facade: bool, +pub struct ModuleLexer { + pub imports: Vec, + pub exports: Vec, + pub has_module_syntax: bool, + pub facade: bool, } fn parse(source: &str) -> ModuleLexer { @@ -53,6 +55,7 @@ fn parse(source: &str) -> ModuleLexer { se: i.se, d: i.d, a: i.a, + t: i.t, }) .collect(), exports: module_lexer @@ -65,6 +68,7 @@ fn parse(source: &str) -> ModuleLexer { e: e.e, ls: e.ls, le: e.le, + t: e.t, }) .collect(), has_module_syntax: module_lexer.has_module_syntax, diff --git a/crates/oxc_module_lexer/tests/typescript.rs b/crates/oxc_module_lexer/tests/typescript.rs index ef72438ec541d..b876b31b681cd 100644 --- a/crates/oxc_module_lexer/tests/typescript.rs +++ b/crates/oxc_module_lexer/tests/typescript.rs @@ -1,13 +1,9 @@ +mod esm; +use esm::{ExportSpecifier, ImportSpecifier, ModuleLexer}; use oxc_allocator::Allocator; use oxc_parser::Parser; use oxc_span::SourceType; -#[non_exhaustive] -struct ModuleLexer { - imports: Vec, - exports: Vec, -} - fn parse(source: &str) -> ModuleLexer { let allocator = Allocator::default(); let source_type = SourceType::default().with_module(true).with_typescript_definition(true); @@ -15,8 +11,35 @@ fn parse(source: &str) -> ModuleLexer { assert!(ret.errors.is_empty(), "{source} should not produce errors.\n{:?}", ret.errors); let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); ModuleLexer { - imports: module_lexer.imports.into_iter().map(|i| i.t).collect(), - exports: module_lexer.exports.into_iter().map(|e| e.t).collect(), + imports: module_lexer + .imports + .into_iter() + .map(|i| ImportSpecifier { + n: i.n.map(|n| n.to_string()), + s: i.s, + e: i.e, + ss: i.ss, + se: i.se, + d: i.d, + a: i.a, + t: i.t, + }) + .collect(), + exports: module_lexer + .exports + .into_iter() + .map(|e| ExportSpecifier { + n: e.n.to_string(), + ln: e.ln.map(|ln| ln.to_string()), + s: e.s, + e: e.e, + ls: e.ls, + le: e.le, + t: e.t, + }) + .collect(), + has_module_syntax: module_lexer.has_module_syntax, + facade: module_lexer.facade, } } @@ -24,33 +47,48 @@ fn parse(source: &str) -> ModuleLexer { fn import_type_named() { let source = "import type { foo } from 'foo'"; let impt = &parse(source).imports[0]; - assert!(impt); + assert!(impt.t); } #[test] fn import_type_namespace() { let source = "import type * as foo from 'foo'"; let impt = &parse(source).imports[0]; - assert!(impt); + assert!(impt.t); } #[test] fn import_type_default() { let source = "import type foo from 'foo'"; let impt = &parse(source).imports[0]; - assert!(impt); + assert!(impt.t); +} + +#[test] +fn dynamic_import_value() { + let source = "import('foo')"; + let impt = &parse(source).imports[0]; + assert!(!impt.t); +} + +#[test] +fn dynamic_import_type() { + let source = "const foo: import('foo')"; + let impt = &parse(source).imports[0]; + assert!(impt.t); + assert_eq!(impt.n.as_ref().unwrap(), "foo"); } #[test] fn export_type_named() { let source = "export type { foo } from 'foo'"; let expt = &parse(source).exports[0]; - assert!(expt); + assert!(expt.t); } #[test] fn export_type_namespace() { let source = "export type * as foo from 'foo'"; let expt = &parse(source).exports[0]; - assert!(expt); + assert!(expt.t); } From f9c45d112e09704761d9e2e14f0215c41eb5b912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Sun, 25 Aug 2024 13:49:42 +0800 Subject: [PATCH 3/6] refactor: simplify --- crates/oxc_module_lexer/src/lib.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/crates/oxc_module_lexer/src/lib.rs b/crates/oxc_module_lexer/src/lib.rs index 7fbe5c8384d22..b7c6ae21422da 100644 --- a/crates/oxc_module_lexer/src/lib.rs +++ b/crates/oxc_module_lexer/src/lib.rs @@ -177,23 +177,18 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { } fn visit_ts_import_type(&mut self, impt: &TSImportType<'a>) { - let (source, source_span_start, source_span_end) = - if let TSType::TSLiteralType(literal) = &impt.parameter { - if let TSLiteral::StringLiteral(s) = &literal.literal { - (Some(s.value.clone()), s.span.start, s.span.end) - } else { - let span = literal.span(); - (None, span.start, span.end) - } - } else { - let span = impt.parameter.span(); - (None, span.start, span.end) - }; + let (source, span) = match &impt.parameter { + TSType::TSLiteralType(literal_type) => match &literal_type.literal { + TSLiteral::StringLiteral(s) => (Some(s.value.clone()), s.span()), + _ => (None, literal_type.span()), + }, + _ => (None, impt.parameter.span()), + }; self.imports.push(ImportSpecifier { n: source, - s: source_span_start, - e: source_span_end, + s: span.start, + e: span.end, ss: impt.span.start, se: impt.span.end, d: ImportType::DynamicImport(impt.span.start + 6), From c70003f953642fff77e439d7219be439bec694db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Sun, 25 Aug 2024 13:52:28 +0800 Subject: [PATCH 4/6] refactor: rename --- crates/oxc_module_lexer/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/oxc_module_lexer/src/lib.rs b/crates/oxc_module_lexer/src/lib.rs index b7c6ae21422da..650929cb5d70b 100644 --- a/crates/oxc_module_lexer/src/lib.rs +++ b/crates/oxc_module_lexer/src/lib.rs @@ -177,7 +177,7 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { } fn visit_ts_import_type(&mut self, impt: &TSImportType<'a>) { - let (source, span) = match &impt.parameter { + let (source, source_span) = match &impt.parameter { TSType::TSLiteralType(literal_type) => match &literal_type.literal { TSLiteral::StringLiteral(s) => (Some(s.value.clone()), s.span()), _ => (None, literal_type.span()), @@ -187,8 +187,8 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { self.imports.push(ImportSpecifier { n: source, - s: span.start, - e: span.end, + s: source_span.start, + e: source_span.end, ss: impt.span.start, se: impt.span.end, d: ImportType::DynamicImport(impt.span.start + 6), From d54d85ee9ba64a17cf7d28d93d30e6714e296f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Sun, 25 Aug 2024 17:02:25 +0800 Subject: [PATCH 5/6] refactor --- crates/oxc_module_lexer/tests/esm.rs | 58 +++++++++++---------- crates/oxc_module_lexer/tests/typescript.rs | 31 ++--------- 2 files changed, 34 insertions(+), 55 deletions(-) diff --git a/crates/oxc_module_lexer/tests/esm.rs b/crates/oxc_module_lexer/tests/esm.rs index 83ee3ccd628ec..be51f33d5e3a2 100644 --- a/crates/oxc_module_lexer/tests/esm.rs +++ b/crates/oxc_module_lexer/tests/esm.rs @@ -17,6 +17,21 @@ pub struct ImportSpecifier { pub t: bool, } +impl From> for ImportSpecifier { + fn from(value: oxc_module_lexer::ImportSpecifier) -> Self { + Self { + n: value.n.map(|n| n.to_string()), + s: value.s, + e: value.e, + ss: value.ss, + se: value.se, + d: value.d, + a: value.a, + t: value.t, + } + } +} + #[derive(Debug, Clone)] pub struct ExportSpecifier { pub n: String, @@ -28,6 +43,20 @@ pub struct ExportSpecifier { pub t: bool, } +impl From> for ExportSpecifier { + fn from(value: oxc_module_lexer::ExportSpecifier) -> Self { + Self { + n: value.n.to_string(), + ln: value.ln.map(|ln| ln.to_string()), + s: value.s, + e: value.e, + ls: value.ls, + le: value.le, + t: value.t, + } + } +} + #[non_exhaustive] pub struct ModuleLexer { pub imports: Vec, @@ -44,33 +73,8 @@ fn parse(source: &str) -> ModuleLexer { let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); // Copy data over because `ModuleLexer<'a>` can't be returned ModuleLexer { - imports: module_lexer - .imports - .into_iter() - .map(|i| ImportSpecifier { - n: i.n.map(|n| n.to_string()), - s: i.s, - e: i.e, - ss: i.ss, - se: i.se, - d: i.d, - a: i.a, - t: i.t, - }) - .collect(), - exports: module_lexer - .exports - .into_iter() - .map(|e| ExportSpecifier { - n: e.n.to_string(), - ln: e.ln.map(|ln| ln.to_string()), - s: e.s, - e: e.e, - ls: e.ls, - le: e.le, - t: e.t, - }) - .collect(), + imports: module_lexer.imports.into_iter().map(|i| i.into()).collect(), + exports: module_lexer.exports.into_iter().map(|e| e.into()).collect(), has_module_syntax: module_lexer.has_module_syntax, facade: module_lexer.facade, } diff --git a/crates/oxc_module_lexer/tests/typescript.rs b/crates/oxc_module_lexer/tests/typescript.rs index b876b31b681cd..a3b140015969a 100644 --- a/crates/oxc_module_lexer/tests/typescript.rs +++ b/crates/oxc_module_lexer/tests/typescript.rs @@ -1,5 +1,5 @@ mod esm; -use esm::{ExportSpecifier, ImportSpecifier, ModuleLexer}; +use esm::ModuleLexer; use oxc_allocator::Allocator; use oxc_parser::Parser; use oxc_span::SourceType; @@ -11,33 +11,8 @@ fn parse(source: &str) -> ModuleLexer { assert!(ret.errors.is_empty(), "{source} should not produce errors.\n{:?}", ret.errors); let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); ModuleLexer { - imports: module_lexer - .imports - .into_iter() - .map(|i| ImportSpecifier { - n: i.n.map(|n| n.to_string()), - s: i.s, - e: i.e, - ss: i.ss, - se: i.se, - d: i.d, - a: i.a, - t: i.t, - }) - .collect(), - exports: module_lexer - .exports - .into_iter() - .map(|e| ExportSpecifier { - n: e.n.to_string(), - ln: e.ln.map(|ln| ln.to_string()), - s: e.s, - e: e.e, - ls: e.ls, - le: e.le, - t: e.t, - }) - .collect(), + imports: module_lexer.imports.into_iter().map(|i| i.into()).collect(), + exports: module_lexer.exports.into_iter().map(|e| e.into()).collect(), has_module_syntax: module_lexer.has_module_syntax, facade: module_lexer.facade, } From 2ae7c79e1316accb4d7e96f2474105916bc6749a Mon Sep 17 00:00:00 2001 From: Boshen Date: Sun, 25 Aug 2024 18:18:22 +0800 Subject: [PATCH 6/6] u --- crates/oxc_module_lexer/tests/esm.rs | 4 ++-- crates/oxc_module_lexer/tests/typescript.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/oxc_module_lexer/tests/esm.rs b/crates/oxc_module_lexer/tests/esm.rs index be51f33d5e3a2..5870d3113fd67 100644 --- a/crates/oxc_module_lexer/tests/esm.rs +++ b/crates/oxc_module_lexer/tests/esm.rs @@ -73,8 +73,8 @@ fn parse(source: &str) -> ModuleLexer { let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); // Copy data over because `ModuleLexer<'a>` can't be returned ModuleLexer { - imports: module_lexer.imports.into_iter().map(|i| i.into()).collect(), - exports: module_lexer.exports.into_iter().map(|e| e.into()).collect(), + imports: module_lexer.imports.into_iter().map(Into::into).collect(), + exports: module_lexer.exports.into_iter().map(Into::into).collect(), has_module_syntax: module_lexer.has_module_syntax, facade: module_lexer.facade, } diff --git a/crates/oxc_module_lexer/tests/typescript.rs b/crates/oxc_module_lexer/tests/typescript.rs index a3b140015969a..60a6acc552dc4 100644 --- a/crates/oxc_module_lexer/tests/typescript.rs +++ b/crates/oxc_module_lexer/tests/typescript.rs @@ -11,8 +11,8 @@ fn parse(source: &str) -> ModuleLexer { assert!(ret.errors.is_empty(), "{source} should not produce errors.\n{:?}", ret.errors); let module_lexer = oxc_module_lexer::ModuleLexer::new().build(&ret.program); ModuleLexer { - imports: module_lexer.imports.into_iter().map(|i| i.into()).collect(), - exports: module_lexer.exports.into_iter().map(|e| e.into()).collect(), + imports: module_lexer.imports.into_iter().map(Into::into).collect(), + exports: module_lexer.exports.into_iter().map(Into::into).collect(), has_module_syntax: module_lexer.has_module_syntax, facade: module_lexer.facade, }