Skip to content

Commit

Permalink
fix(es/parser): Fix parsing of import type from from (#8309)
Browse files Browse the repository at this point in the history
**Related issue:**

- Closes #8308.
  • Loading branch information
magic-akari committed Nov 20, 2023
1 parent af8c1d3 commit 00b8839
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 35 deletions.
72 changes: 38 additions & 34 deletions crates/swc_ecma_parser/src/parser/stmt/module_item.rs
Expand Up @@ -42,15 +42,6 @@ impl<I: Tokens> Parser<I> {

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) {
Expand Down Expand Up @@ -84,35 +75,46 @@ impl<I: Tokens> Parser<I> {
})));
}

let type_only = self.input.syntax().typescript()
&& is!(self, "type")
&& (peeked_is!(self, '{') || !peeked_is!(self, "from") && !peeked_is!(self, ','));
let mut type_only = false;
let mut specifiers = vec![];

if type_only {
assert_and_bump!(self, "type");
'import_maybe_ident: {
if is!(self, BindingIdent) {
let mut local = self.parse_imported_default_binding()?;

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);
}
}
if self.input.syntax().typescript() && local.sym == "type" {
if is_one_of!(self, '*', '{') {
type_only = true;
break 'import_maybe_ident;
}

let mut specifiers = vec![];
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 is!(self, BindingIdent) {
let local = self.parse_imported_default_binding()?;
//TODO: Better error reporting
if !is!(self, "from") {
expect!(self, ',');
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,
}));
}

{
Expand Down Expand Up @@ -373,9 +375,11 @@ impl<I: Tokens> Parser<I> {
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);
}

Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_parser/src/parser/typescript.rs
Expand Up @@ -1133,12 +1133,12 @@ impl<I: Tokens> Parser<I> {
pub(super) fn parse_ts_import_equals_decl(
&mut self,
start: BytePos,
id: Ident,
is_export: bool,
is_type_only: bool,
) -> PResult<Box<TsImportEqualsDecl>> {
debug_assert!(self.input.syntax().typescript());

let id = self.parse_ident_name()?;
expect!(self, '=');

let module_ref = self.parse_ts_module_ref()?;
Expand Down
@@ -0,0 +1 @@
import type from from "foo";
51 changes: 51 additions & 0 deletions 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
}
@@ -0,0 +1 @@
import type from = require("foo");
49 changes: 49 additions & 0 deletions 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
}
@@ -0,0 +1 @@
import type async = require("foo");
49 changes: 49 additions & 0 deletions 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
}

0 comments on commit 00b8839

Please sign in to comment.