Repository: swc-project/swc (rust)

Issue:
Cannot parse `import type from from`
### Describe the bug

- https://github.com/microsoft/TypeScript/issues/56376

### Input code

```typescript
import type from from "foo";
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": false
    },
    "loose": false,
    "minify": {
      "compress": false,
      "mangle": false
    },
    "target": "es2022"
  },
  "module": {
    "type": "es6"
  },
  "minify": false,
  "isModule": true
}
```


### Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.3.94-nightly-20231016.1&code=H4sIAAAAAAAAA8vMLcgvKlEoqSxIVUgrys%2BFEEpp%2BflK1gAF8uDMHAAAAA%3D%3D&config=H4sIAAAAAAAAA22PPQrDMAyF95zCaO5QPHToHXoI4yrBwX9YCtSE3L12YocUuknve5Ke1kEImEnDU6ylLE1UiTCdfVEoe1afogDniKSTiQy3TpkqGpUl3KXtIGBDIOykac54M%2Bbrbh1cTEj0a6xW5SeLfzezShNyzYMk71LC0Bi48F72ofZLzXv4HhdTD3EeBEOvPslpwWH7AvDL3IgXAQAA

### SWC Info output

_No response_

### Expected behavior

Parsed without error.

### Actual behavior

Parse error

### Version

1.3.94-nightly-20231016.1

### Additional context

_No response_

Pull request:
fix(es/parser): `import type from from`
**Related issue:**

- Closes #8308.


Patch:
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 9c500448ed..173d9ed095 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<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) {
@@ -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,
-            }));
         }
 
         {
@@ -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);
             }
 
diff --git a/crates/swc_ecma_parser/src/parser/typescript.rs b/crates/swc_ecma_parser/src/parser/typescript.rs
index 1c8e8337da..808de93c56 100644
--- a/crates/swc_ecma_parser/src/parser/typescript.rs
+++ b/crates/swc_ecma_parser/src/parser/typescript.rs
@@ -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()?;


Test patch:
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 0000000000..0aad6c7868
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/1/input.ts
@@ -0,0 +1,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 0000000000..d2a2f06670
--- /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 0000000000..5f245467b9
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/2/input.ts
@@ -0,0 +1,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 0000000000..a7902a2372
--- /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 0000000000..ccfd648b42
--- /dev/null
+++ b/crates/swc_ecma_parser/tests/typescript/issue-8308/3/input.ts
@@ -0,0 +1,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 0000000000..34307549c4
--- /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
+}