From 00b88399a0ea10dfd6d48cb168dd5ae914f11d54 Mon Sep 17 00:00:00 2001 From: magic-akari Date: Mon, 20 Nov 2023 14:20:06 +0800 Subject: [PATCH] fix(es/parser): Fix parsing of `import type from from` (#8309) **Related issue:** - Closes #8308. --- .../src/parser/stmt/module_item.rs | 76 ++++++++++--------- .../swc_ecma_parser/src/parser/typescript.rs | 2 +- .../tests/typescript/issue-8308/1/input.ts | 1 + .../typescript/issue-8308/1/input.ts.json | 51 +++++++++++++ .../tests/typescript/issue-8308/2/input.ts | 1 + .../typescript/issue-8308/2/input.ts.json | 49 ++++++++++++ .../tests/typescript/issue-8308/3/input.ts | 1 + .../typescript/issue-8308/3/input.ts.json | 49 ++++++++++++ 8 files changed, 193 insertions(+), 37 deletions(-) create mode 100644 crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts create mode 100644 crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts.json create mode 100644 crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts create mode 100644 crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts.json create mode 100644 crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts create mode 100644 crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts.json diff --git a/crates/swc_ecma_parser/src/parser/stmt/module_item.rs b/crates/swc_ecma_parser/src/parser/stmt/module_item.rs index 9c500448ed7..173d9ed095a 100644 --- a/crates/swc_ecma_parser/src/parser/stmt/module_item.rs +++ b/crates/swc_ecma_parser/src/parser/stmt/module_item.rs @@ -42,15 +42,6 @@ impl Parser { expect!(self, "import"); - if self.input.syntax().typescript() && is!(self, IdentRef) && peeked_is!(self, '=') { - return self - .parse_ts_import_equals_decl( - start, /* is_export */ false, /* is_type_only */ false, - ) - .map(ModuleDecl::from) - .map(ModuleItem::from); - } - // Handle import 'mod.js' let str_start = cur_pos!(self); if let Ok(&Token::Str { .. }) = cur!(self, false) { @@ -84,35 +75,46 @@ impl Parser { }))); } - let type_only = self.input.syntax().typescript() - && is!(self, "type") - && (peeked_is!(self, '{') || !peeked_is!(self, "from") && !peeked_is!(self, ',')); - - if type_only { - assert_and_bump!(self, "type"); - - if is!(self, IdentRef) && peeked_is!(self, '=') { - return self - .parse_ts_import_equals_decl( - start, /* is_export */ false, /* is_type_only */ true, - ) - .map(ModuleDecl::from) - .map(ModuleItem::from); - } - } - + let mut type_only = false; let mut specifiers = vec![]; - if is!(self, BindingIdent) { - let local = self.parse_imported_default_binding()?; - //TODO: Better error reporting - if !is!(self, "from") { - expect!(self, ','); + 'import_maybe_ident: { + if is!(self, BindingIdent) { + let mut local = self.parse_imported_default_binding()?; + + if self.input.syntax().typescript() && local.sym == "type" { + if is_one_of!(self, '*', '{') { + type_only = true; + break 'import_maybe_ident; + } + + if is!(self, BindingIdent) { + if !is!(self, "from") || peeked_is!(self, "from") { + type_only = true; + local = self.parse_imported_default_binding()?; + } else if peeked_is!(self, '=') { + type_only = true; + local = self.parse_ident_name()?; + } + } + } + + if self.input.syntax().typescript() && is!(self, '=') { + return self + .parse_ts_import_equals_decl(start, local, false, type_only) + .map(ModuleDecl::from) + .map(ModuleItem::from); + } + + //TODO: Better error reporting + if !is!(self, "from") { + expect!(self, ','); + } + specifiers.push(ImportSpecifier::Default(ImportDefaultSpecifier { + span: local.span, + local, + })); } - specifiers.push(ImportSpecifier::Default(ImportDefaultSpecifier { - span: local.span, - local, - })); } { @@ -373,9 +375,11 @@ impl Parser { assert_and_bump!(self, "type"); } + let id = self.parse_ident_name()?; + // export import A = B return self - .parse_ts_import_equals_decl(start, /* is_export */ true, is_type_only) + .parse_ts_import_equals_decl(start, id, /* is_export */ true, is_type_only) .map(From::from); } diff --git a/crates/swc_ecma_parser/src/parser/typescript.rs b/crates/swc_ecma_parser/src/parser/typescript.rs index 1c8e8337dae..808de93c56e 100644 --- a/crates/swc_ecma_parser/src/parser/typescript.rs +++ b/crates/swc_ecma_parser/src/parser/typescript.rs @@ -1133,12 +1133,12 @@ impl Parser { pub(super) fn parse_ts_import_equals_decl( &mut self, start: BytePos, + id: Ident, is_export: bool, is_type_only: bool, ) -> PResult> { debug_assert!(self.input.syntax().typescript()); - let id = self.parse_ident_name()?; expect!(self, '='); let module_ref = self.parse_ts_module_ref()?; diff --git a/crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts b/crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts new file mode 100644 index 00000000000..0aad6c7868d --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts @@ -0,0 +1 @@ +import type from from "foo"; \ No newline at end of file diff --git a/crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts.json b/crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts.json new file mode 100644 index 00000000000..d2a2f066709 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts.json @@ -0,0 +1,51 @@ +{ + "type": "Module", + "span": { + "start": 1, + "end": 29, + "ctxt": 0 + }, + "body": [ + { + "type": "ImportDeclaration", + "span": { + "start": 1, + "end": 29, + "ctxt": 0 + }, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "span": { + "start": 13, + "end": 17, + "ctxt": 0 + }, + "local": { + "type": "Identifier", + "span": { + "start": 13, + "end": 17, + "ctxt": 0 + }, + "value": "from", + "optional": false + } + } + ], + "source": { + "type": "StringLiteral", + "span": { + "start": 23, + "end": 28, + "ctxt": 0 + }, + "value": "foo", + "raw": "\"foo\"" + }, + "typeOnly": true, + "with": null + } + ], + "interpreter": null +} diff --git a/crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts b/crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts new file mode 100644 index 00000000000..5f245467b9c --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts @@ -0,0 +1 @@ +import type from = require("foo"); \ No newline at end of file diff --git a/crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts.json b/crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts.json new file mode 100644 index 00000000000..a7902a2372e --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts.json @@ -0,0 +1,49 @@ +{ + "type": "Module", + "span": { + "start": 1, + "end": 35, + "ctxt": 0 + }, + "body": [ + { + "type": "TsImportEqualsDeclaration", + "span": { + "start": 1, + "end": 35, + "ctxt": 0 + }, + "isExport": false, + "isTypeOnly": true, + "id": { + "type": "Identifier", + "span": { + "start": 13, + "end": 17, + "ctxt": 0 + }, + "value": "from", + "optional": false + }, + "moduleRef": { + "type": "TsExternalModuleReference", + "span": { + "start": 20, + "end": 34, + "ctxt": 0 + }, + "expression": { + "type": "StringLiteral", + "span": { + "start": 28, + "end": 33, + "ctxt": 0 + }, + "value": "foo", + "raw": "\"foo\"" + } + } + } + ], + "interpreter": null +} diff --git a/crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts b/crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts new file mode 100644 index 00000000000..ccfd648b429 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts @@ -0,0 +1 @@ +import type async = require("foo"); \ No newline at end of file diff --git a/crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts.json b/crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts.json new file mode 100644 index 00000000000..34307549c40 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts.json @@ -0,0 +1,49 @@ +{ + "type": "Module", + "span": { + "start": 1, + "end": 36, + "ctxt": 0 + }, + "body": [ + { + "type": "TsImportEqualsDeclaration", + "span": { + "start": 1, + "end": 36, + "ctxt": 0 + }, + "isExport": false, + "isTypeOnly": true, + "id": { + "type": "Identifier", + "span": { + "start": 13, + "end": 18, + "ctxt": 0 + }, + "value": "async", + "optional": false + }, + "moduleRef": { + "type": "TsExternalModuleReference", + "span": { + "start": 21, + "end": 35, + "ctxt": 0 + }, + "expression": { + "type": "StringLiteral", + "span": { + "start": 29, + "end": 34, + "ctxt": 0 + }, + "value": "foo", + "raw": "\"foo\"" + } + } + } + ], + "interpreter": null +}