From 9b91e84526d1e0cd3c6357adc5bae9f0b0c70bb7 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 1 Oct 2021 07:36:58 +0900 Subject: [PATCH 01/25] Implement parser --- packages/babel-parser/src/parser/statement.js | 45 ++++++++--- .../babel-parser/src/plugins/flow/index.js | 4 +- .../src/plugins/typescript/index.js | 77 +++++++++++++++++++ 3 files changed, 115 insertions(+), 11 deletions(-) diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index d07690d075c4..bdb5e82f6847 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -1872,7 +1872,8 @@ export default class StatementParser extends ExpressionParser { maybeParseExportNamedSpecifiers(node: N.Node): boolean { if (this.match(tt.braceL)) { if (!node.specifiers) node.specifiers = []; - node.specifiers.push(...this.parseExportSpecifiers()); + const isTypeExport = node.exportKind === "type"; + node.specifiers.push(...this.parseExportSpecifiers(isTypeExport)); node.source = null; node.declaration = null; @@ -2152,7 +2153,7 @@ export default class StatementParser extends ExpressionParser { // Parses a comma-separated list of module exports. - parseExportSpecifiers(): Array { + parseExportSpecifiers(isInTypeExport: boolean): Array { const nodes = []; let first = true; @@ -2169,14 +2170,19 @@ export default class StatementParser extends ExpressionParser { const node = this.startNode(); const isString = this.match(tt.string); - const local = this.parseModuleExportName(); - node.local = local; - if (this.eatContextual(tt._as)) { + node.local = this.parseModuleExportName(); + const canParseAsKeyword = this.parseTypeOnlyImportExportSpecifier( + node, + /* isImport */ false, + isString, + isInTypeExport, + ); + if (canParseAsKeyword && this.eatContextual(tt._as)) { node.exported = this.parseModuleExportName(); } else if (isString) { - node.exported = cloneStringLiteral(local); - } else { - node.exported = cloneIdentifier(local); + node.exported = cloneStringLiteral(node.local); + } else if (!node.exported) { + node.exported = cloneIdentifier(node.local); } nodes.push(this.finishNode(node, "ExportSpecifier")); } @@ -2436,12 +2442,29 @@ export default class StatementParser extends ExpressionParser { } } + parseTypeOnlyImportExportSpecifier( + /* eslint-disable no-unused-vars -- used in typescript plugin */ + node: any, + isImport: boolean, + isStringSpecifier: boolean, + isInTypeOnlyImportExport: boolean, + /* eslint-enable no-unused-vars */ + ): boolean { + return true; + } + // https://tc39.es/ecma262/#prod-ImportSpecifier parseImportSpecifier(node: N.ImportDeclaration): void { const specifier = this.startNode(); const importedIsString = this.match(tt.string); specifier.imported = this.parseModuleExportName(); - if (this.eatContextual(tt._as)) { + const canParseAsKeyworkd = this.parseTypeOnlyImportExportSpecifier( + specifier, + /* isImport */ true, + importedIsString, + /* isInTypeOnlyImportExport */ node.importKind === "type", + ); + if (canParseAsKeyworkd && this.eatContextual(tt._as)) { specifier.local = this.parseIdentifier(); } else { const { imported } = specifier; @@ -2453,7 +2476,9 @@ export default class StatementParser extends ExpressionParser { ); } this.checkReservedWord(imported.name, specifier.start, true, true); - specifier.local = cloneIdentifier(imported); + if (!specifier.local) { + specifier.local = cloneIdentifier(imported); + } } this.checkLVal(specifier.local, "import specifier", BIND_LEXICAL); node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index 666a33c3a7fb..e7fc375a4bb6 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2123,7 +2123,9 @@ export default (superClass: Class): Class => if (this.match(tt.braceL)) { // export type { foo, bar }; - node.specifiers = this.parseExportSpecifiers(); + node.specifiers = this.parseExportSpecifiers( + /* isInTypeExport */ true, + ); this.parseExportFrom(node); return null; } else { diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 6d90951f0672..f709162f4a00 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -147,6 +147,10 @@ const TSErrors = makeErrorTemplates( "Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`.", TypeImportCannotSpecifyDefaultAndNamed: "A type-only import can specify a default import or named bindings, but not both.", + TypeModifierIsUsedInTypeExports: + "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.", + TypeModifierIsUsedInTypeImports: + "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.", UnexpectedParameterModifier: "A parameter property is only allowed in a constructor implementation.", UnexpectedReadonly: @@ -3311,4 +3315,77 @@ export default (superClass: Class): Class => } return super.getExpression(); } + + parseTypeOnlyImportExportSpecifier( + node: any, + isImport: boolean, + isStringSpecifier: boolean, + isInTypeOnlyImportExport: boolean, + ): boolean { + const leftOfAsKey = isImport ? "imported" : "local"; + const rightOfAsKey = isImport ? "local" : "exported"; + + let leftOfAs = node[leftOfAsKey]; + let rightOfAs; + + let isTypeOnly = false; + let canParseAsKeyword = true; + + const pos = leftOfAs.start; + + // https://github.com/microsoft/TypeScript/blob/fc4f9d83d5939047aa6bb2a43965c6e9bbfbc35b/src/compiler/parser.ts#L7411-L7456 + if (!isStringSpecifier && leftOfAs.name === "type") { + // import { type } from "mod"; - isTypeOnly: false, leftOfAs: type + // import { type as } from "mod"; - isTypeOnly: true, leftOfAs: as + // import { type as as } from "mod"; - isTypeOnly: false, leftOfAs: type, rightOfAs: as + // import { type as as as } from "mod"; - isTypeOnly: true, leftOfAs: as, rightOfAs: as + if (this.isContextual(tt._as)) { + // { type as ...? } + const firstAs = this.parseIdentifier(); + if (this.isContextual(tt._as)) { + // { type as as ...? } + const secondAs = this.parseIdentifier(); + if (this.match(tt.name)) { + // { type as as something } + isTypeOnly = true; + leftOfAs = firstAs; + rightOfAs = this.parseIdentifier(); + canParseAsKeyword = false; + } else { + // { type as as } + rightOfAs = secondAs; + canParseAsKeyword = false; + } + } else if (this.match(tt.name)) { + // { type as something } + canParseAsKeyword = false; + rightOfAs = this.parseIdentifier(); + } else { + // { type as } + isTypeOnly = true; + leftOfAs = firstAs; + } + } else if (this.match(tt.name)) { + // { type something ...? } + isTypeOnly = true; + leftOfAs = this.parseIdentifier(); + } + } + node[leftOfAsKey] = leftOfAs; + node[rightOfAsKey] = rightOfAs; + + const kindKey = isImport ? "importKind" : "exportKind"; + node[kindKey] = isTypeOnly ? "type" : "value"; + + if (isTypeOnly && isInTypeOnlyImportExport) { + this.raise( + pos, + isImport + ? TSErrors.TypeModifierIsUsedInTypeImports + : TSErrors.TypeModifierIsUsedInTypeExports, + ); + } + + return canParseAsKeyword; + } }; From 1b2059684650afe8d333e1b17348c6ad5d4e74db Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 1 Oct 2021 07:37:20 +0900 Subject: [PATCH 02/25] Add parser tests --- .../export/export-type-from/output.json | 1 + .../typescript/export/export-type/output.json | 1 + .../export/internal-comments/output.json | 170 +++++++------- .../export-named-import-require/output.json | 1 + .../output.json | 1 + .../import/import-named/output.json | 1 + .../import/internal-comments/output.json | 216 +++++++++--------- .../export-declare-function-after/output.json | 5 +- .../output.json | 5 +- .../scope/export-enum-after/output.json | 1 + .../scope/export-enum-before/output.json | 1 + .../scope/export-interface-after/output.json | 1 + .../scope/export-interface-before/output.json | 1 + .../scope/export-namespace/output.json | 1 + .../scope/export-type-after/output.json | 1 + .../scope/export-type-before/output.json | 1 + .../output.json | 1 + .../export-basic/input.ts | 1 + .../export-basic/output.json | 75 ++++++ .../export-invalid-type-in-type/input.ts | 1 + .../export-invalid-type-in-type/output.json | 48 ++++ .../export-named-and-named-type/input.ts | 1 + .../export-named-and-named-type/output.json | 60 +++++ .../export-named-type-as-as/input.ts | 1 + .../export-named-type-as-as/output.json | 45 ++++ .../export-named-type/input.ts | 1 + .../export-named-type/output.json | 45 ++++ .../export-type-only-named-as/input.ts | 1 + .../export-type-only-named-as/output.json | 45 ++++ .../import-basic/input.ts | 1 + .../import-basic/output.json | 74 ++++++ .../import-invalid-type-in-type/input.ts | 1 + .../import-invalid-type-in-type/output.json | 47 ++++ .../import-named-and-named-type/input.ts | 1 + .../import-named-and-named-type/output.json | 59 +++++ .../import-named-type-as-as/input.ts | 1 + .../import-named-type-as-as/output.json | 44 ++++ .../import-named-type/input.ts | 1 + .../import-named-type/output.json | 44 ++++ .../import-type-only-named-as/input.ts | 1 + .../import-type-only-named-as/output.json | 44 ++++ .../import-type-declaration-error/output.json | 2 + .../types/import-type-declaration/output.json | 2 + 43 files changed, 860 insertions(+), 195 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-basic/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-basic/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-and-named-type/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-and-named-type/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-as/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-as/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-named-as/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-named-as/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-basic/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-basic/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-and-named-type/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-and-named-type/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type-as-as/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type-as-as/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-named-as/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-named-as/output.json diff --git a/packages/babel-parser/test/fixtures/typescript/export/export-type-from/output.json b/packages/babel-parser/test/fixtures/typescript/export/export-type-from/output.json index e8399c38636e..517cff8768d0 100644 --- a/packages/babel-parser/test/fixtures/typescript/export/export-type-from/output.json +++ b/packages/babel-parser/test/fixtures/typescript/export/export-type-from/output.json @@ -20,6 +20,7 @@ "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15},"identifierName":"T"}, "name": "T" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15},"identifierName":"T"}, diff --git a/packages/babel-parser/test/fixtures/typescript/export/export-type/output.json b/packages/babel-parser/test/fixtures/typescript/export/export-type/output.json index 5c1d6a9647b6..2ab17f0fe674 100644 --- a/packages/babel-parser/test/fixtures/typescript/export/export-type/output.json +++ b/packages/babel-parser/test/fixtures/typescript/export/export-type/output.json @@ -42,6 +42,7 @@ "start":26,"end":27,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":15},"identifierName":"A"}, "name": "A" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":26,"end":27,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":15},"identifierName":"A"}, diff --git a/packages/babel-parser/test/fixtures/typescript/export/internal-comments/output.json b/packages/babel-parser/test/fixtures/typescript/export/internal-comments/output.json index 28d3759266d5..6da84f915b29 100644 --- a/packages/babel-parser/test/fixtures/typescript/export/internal-comments/output.json +++ b/packages/babel-parser/test/fixtures/typescript/export/internal-comments/output.json @@ -10,162 +10,164 @@ { "type": "ExportNamedDeclaration", "start":6,"end":93,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":93}}, - "leadingComments": [ - { - "type": "CommentBlock", - "value": "1", - "start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}} - } - ], - "trailingComments": [ - { - "type": "CommentBlock", - "value": "1", - "start":94,"end":99,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}} - } - ], - "innerComments": [ - { - "type": "CommentBlock", - "value": "2", - "start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}} - }, - { - "type": "CommentBlock", - "value": "9", - "start":69,"end":74,"loc":{"start":{"line":1,"column":69},"end":{"line":1,"column":74}} - } - ], "exportKind": "value", "specifiers": [ { "type": "ExportSpecifier", "start":27,"end":28,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":28}}, - "leadingComments": [ - { - "type": "CommentBlock", - "value": "3", - "start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}} - } - ], - "trailingComments": [ - { - "type": "CommentBlock", - "value": "4", - "start":29,"end":34,"loc":{"start":{"line":1,"column":29},"end":{"line":1,"column":34}} - } - ], "local": { "type": "Identifier", "start":27,"end":28,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":28},"identifierName":"A"}, "name": "A" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":27,"end":28,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":28},"identifierName":"A"}, "name": "A" - } - }, - { - "type": "ExportSpecifier", - "start":42,"end":60,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":60}}, - "leadingComments": [ + }, + "trailingComments": [ { "type": "CommentBlock", - "value": "5", - "start":36,"end":41,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":41}} + "value": "4", + "start":29,"end":34,"loc":{"start":{"line":1,"column":29},"end":{"line":1,"column":34}} } ], - "trailingComments": [ + "leadingComments": [ { "type": "CommentBlock", - "value": "8", - "start":61,"end":66,"loc":{"start":{"line":1,"column":61},"end":{"line":1,"column":66}} + "value": "3", + "start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}} } - ], + ] + }, + { + "type": "ExportSpecifier", + "start":42,"end":60,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":60}}, "local": { "type": "Identifier", "start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43},"identifierName":"B"}, + "name": "B", "trailingComments": [ { "type": "CommentBlock", "value": "6", "start":44,"end":49,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":49}} } - ], - "name": "B" + ] }, + "exportKind": "value", "exported": { "type": "Identifier", "start":59,"end":60,"loc":{"start":{"line":1,"column":59},"end":{"line":1,"column":60},"identifierName":"C"}, + "name": "C", "leadingComments": [ { "type": "CommentBlock", "value": "7", "start":53,"end":58,"loc":{"start":{"line":1,"column":53},"end":{"line":1,"column":58}} } - ], - "name": "C" - } + ] + }, + "trailingComments": [ + { + "type": "CommentBlock", + "value": "8", + "start":61,"end":66,"loc":{"start":{"line":1,"column":61},"end":{"line":1,"column":66}} + } + ], + "leadingComments": [ + { + "type": "CommentBlock", + "value": "5", + "start":36,"end":41,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":41}} + } + ] } ], "source": { "type": "StringLiteral", "start":87,"end":92,"loc":{"start":{"line":1,"column":87},"end":{"line":1,"column":92}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo", "leadingComments": [ { "type": "CommentBlock", "value": "10", "start":80,"end":86,"loc":{"start":{"line":1,"column":80},"end":{"line":1,"column":86}} } - ], - "extra": { - "rawValue": "foo", - "raw": "\"foo\"" - }, - "value": "foo" + ] }, - "declaration": null - }, - { - "type": "ExportAllDeclaration", - "start":100,"end":137,"loc":{"start":{"line":2,"column":6},"end":{"line":2,"column":43}}, - "leadingComments": [ - { - "type": "CommentBlock", - "value": "1", - "start":94,"end":99,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}} - } - ], + "declaration": null, "innerComments": [ { "type": "CommentBlock", "value": "2", - "start":107,"end":112,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18}} + "start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}} }, { "type": "CommentBlock", - "value": "3", - "start":115,"end":120,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}} + "value": "9", + "start":69,"end":74,"loc":{"start":{"line":1,"column":69},"end":{"line":1,"column":74}} + } + ], + "trailingComments": [ + { + "type": "CommentBlock", + "value": "1", + "start":94,"end":99,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}} } ], + "leadingComments": [ + { + "type": "CommentBlock", + "value": "1", + "start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}} + } + ] + }, + { + "type": "ExportAllDeclaration", + "start":100,"end":137,"loc":{"start":{"line":2,"column":6},"end":{"line":2,"column":43}}, "exportKind": "value", "source": { "type": "StringLiteral", "start":132,"end":137,"loc":{"start":{"line":2,"column":38},"end":{"line":2,"column":43}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo", "leadingComments": [ { "type": "CommentBlock", "value": "4", "start":126,"end":131,"loc":{"start":{"line":2,"column":32},"end":{"line":2,"column":37}} } - ], - "extra": { - "rawValue": "foo", - "raw": "\"foo\"" + ] + }, + "innerComments": [ + { + "type": "CommentBlock", + "value": "2", + "start":107,"end":112,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18}} }, - "value": "foo" - } + { + "type": "CommentBlock", + "value": "3", + "start":115,"end":120,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}} + } + ], + "leadingComments": [ + { + "type": "CommentBlock", + "value": "1", + "start":94,"end":99,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}} + } + ] } ], "directives": [] diff --git a/packages/babel-parser/test/fixtures/typescript/import/export-named-import-require/output.json b/packages/babel-parser/test/fixtures/typescript/import/export-named-import-require/output.json index b7915365cced..8c514405ab97 100644 --- a/packages/babel-parser/test/fixtures/typescript/import/export-named-import-require/output.json +++ b/packages/babel-parser/test/fixtures/typescript/import/export-named-import-require/output.json @@ -44,6 +44,7 @@ "start":34,"end":35,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":10},"identifierName":"a"}, "name": "a" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":34,"end":35,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":10},"identifierName":"a"}, diff --git a/packages/babel-parser/test/fixtures/typescript/import/import-default-and-named-id-type/output.json b/packages/babel-parser/test/fixtures/typescript/import/import-default-and-named-id-type/output.json index b91fcd5078b0..753dd0f30f1b 100644 --- a/packages/babel-parser/test/fixtures/typescript/import/import-default-and-named-id-type/output.json +++ b/packages/babel-parser/test/fixtures/typescript/import/import-default-and-named-id-type/output.json @@ -29,6 +29,7 @@ "start":15,"end":18,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":18},"identifierName":"bar"}, "name": "bar" }, + "importKind": "value", "local": { "type": "Identifier", "start":15,"end":18,"loc":{"start":{"line":1,"column":15},"end":{"line":1,"column":18},"identifierName":"bar"}, diff --git a/packages/babel-parser/test/fixtures/typescript/import/import-named/output.json b/packages/babel-parser/test/fixtures/typescript/import/import-named/output.json index 4f8221972167..ac1e40ac7928 100644 --- a/packages/babel-parser/test/fixtures/typescript/import/import-named/output.json +++ b/packages/babel-parser/test/fixtures/typescript/import/import-named/output.json @@ -20,6 +20,7 @@ "start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12},"identifierName":"foo"}, "name": "foo" }, + "importKind": "value", "local": { "type": "Identifier", "start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12},"identifierName":"foo"}, diff --git a/packages/babel-parser/test/fixtures/typescript/import/internal-comments/output.json b/packages/babel-parser/test/fixtures/typescript/import/internal-comments/output.json index cdec7ba32561..9667fbf9c33f 100644 --- a/packages/babel-parser/test/fixtures/typescript/import/internal-comments/output.json +++ b/packages/babel-parser/test/fixtures/typescript/import/internal-comments/output.json @@ -10,165 +10,172 @@ { "type": "ImportDeclaration", "start":6,"end":110,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":110}}, - "leadingComments": [ - { - "type": "CommentBlock", - "value": "1", - "start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}} - } - ], - "trailingComments": [ - { - "type": "CommentBlock", - "value": "1", - "start":111,"end":116,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}} - } - ], - "innerComments": [ - { - "type": "CommentBlock", - "value": "4", - "start":28,"end":33,"loc":{"start":{"line":1,"column":28},"end":{"line":1,"column":33}} - }, - { - "type": "CommentBlock", - "value": "11", - "start":85,"end":91,"loc":{"start":{"line":1,"column":85},"end":{"line":1,"column":91}} - } - ], "importKind": "value", "specifiers": [ { "type": "ImportDefaultSpecifier", "start":19,"end":20,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":20}}, - "leadingComments": [ - { - "type": "CommentBlock", - "value": "2", - "start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}} - } - ], - "trailingComments": [ - { - "type": "CommentBlock", - "value": "3", - "start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}} - } - ], "local": { "type": "Identifier", "start":19,"end":20,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":20},"identifierName":"D"}, "name": "D" - } - }, - { - "type": "ImportSpecifier", - "start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43}}, - "leadingComments": [ + }, + "trailingComments": [ { "type": "CommentBlock", - "value": "5", - "start":36,"end":41,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":41}} + "value": "3", + "start":21,"end":26,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":26}} } ], - "trailingComments": [ + "leadingComments": [ { "type": "CommentBlock", - "value": "6", - "start":44,"end":49,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":49}} + "value": "2", + "start":13,"end":18,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":18}} } - ], + ] + }, + { + "type": "ImportSpecifier", + "start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43}}, "imported": { "type": "Identifier", "start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43},"identifierName":"A"}, "name": "A" }, + "importKind": "value", "local": { "type": "Identifier", "start":42,"end":43,"loc":{"start":{"line":1,"column":42},"end":{"line":1,"column":43},"identifierName":"A"}, "name": "A" - } - }, - { - "type": "ImportSpecifier", - "start":57,"end":75,"loc":{"start":{"line":1,"column":57},"end":{"line":1,"column":75}}, - "leadingComments": [ + }, + "trailingComments": [ { "type": "CommentBlock", - "value": "7", - "start":51,"end":56,"loc":{"start":{"line":1,"column":51},"end":{"line":1,"column":56}} + "value": "6", + "start":44,"end":49,"loc":{"start":{"line":1,"column":44},"end":{"line":1,"column":49}} } ], - "trailingComments": [ + "leadingComments": [ { "type": "CommentBlock", - "value": "10", - "start":76,"end":82,"loc":{"start":{"line":1,"column":76},"end":{"line":1,"column":82}} + "value": "5", + "start":36,"end":41,"loc":{"start":{"line":1,"column":36},"end":{"line":1,"column":41}} } - ], + ] + }, + { + "type": "ImportSpecifier", + "start":57,"end":75,"loc":{"start":{"line":1,"column":57},"end":{"line":1,"column":75}}, "imported": { "type": "Identifier", "start":57,"end":58,"loc":{"start":{"line":1,"column":57},"end":{"line":1,"column":58},"identifierName":"B"}, + "name": "B", "trailingComments": [ { "type": "CommentBlock", "value": "8", "start":59,"end":64,"loc":{"start":{"line":1,"column":59},"end":{"line":1,"column":64}} } - ], - "name": "B" + ] }, + "importKind": "value", "local": { "type": "Identifier", "start":74,"end":75,"loc":{"start":{"line":1,"column":74},"end":{"line":1,"column":75},"identifierName":"C"}, + "name": "C", "leadingComments": [ { "type": "CommentBlock", "value": "9", "start":68,"end":73,"loc":{"start":{"line":1,"column":68},"end":{"line":1,"column":73}} } - ], - "name": "C" - } + ] + }, + "trailingComments": [ + { + "type": "CommentBlock", + "value": "10", + "start":76,"end":82,"loc":{"start":{"line":1,"column":76},"end":{"line":1,"column":82}} + } + ], + "leadingComments": [ + { + "type": "CommentBlock", + "value": "7", + "start":51,"end":56,"loc":{"start":{"line":1,"column":51},"end":{"line":1,"column":56}} + } + ] } ], "source": { "type": "StringLiteral", "start":104,"end":109,"loc":{"start":{"line":1,"column":104},"end":{"line":1,"column":109}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo", "leadingComments": [ { "type": "CommentBlock", "value": "12", "start":97,"end":103,"loc":{"start":{"line":1,"column":97},"end":{"line":1,"column":103}} } - ], - "extra": { - "rawValue": "foo", - "raw": "\"foo\"" + ] + }, + "innerComments": [ + { + "type": "CommentBlock", + "value": "4", + "start":28,"end":33,"loc":{"start":{"line":1,"column":28},"end":{"line":1,"column":33}} }, - "value": "foo" - } - }, - { - "type": "ImportDeclaration", - "start":117,"end":174,"loc":{"start":{"line":2,"column":6},"end":{"line":2,"column":63}}, - "leadingComments": [ + { + "type": "CommentBlock", + "value": "11", + "start":85,"end":91,"loc":{"start":{"line":1,"column":85},"end":{"line":1,"column":91}} + } + ], + "trailingComments": [ { "type": "CommentBlock", "value": "1", "start":111,"end":116,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}} } ], + "leadingComments": [ + { + "type": "CommentBlock", + "value": "1", + "start":0,"end":5,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":5}} + } + ] + }, + { + "type": "ImportDeclaration", + "start":117,"end":174,"loc":{"start":{"line":2,"column":6},"end":{"line":2,"column":63}}, "importKind": "value", "specifiers": [ { "type": "ImportNamespaceSpecifier", "start":130,"end":150,"loc":{"start":{"line":2,"column":19},"end":{"line":2,"column":39}}, - "leadingComments": [ + "local": { + "type": "Identifier", + "start":147,"end":150,"loc":{"start":{"line":2,"column":36},"end":{"line":2,"column":39},"identifierName":"foo"}, + "name": "foo", + "leadingComments": [ + { + "type": "CommentBlock", + "value": "4", + "start":141,"end":146,"loc":{"start":{"line":2,"column":30},"end":{"line":2,"column":35}} + } + ] + }, + "innerComments": [ { "type": "CommentBlock", - "value": "2", - "start":124,"end":129,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18}} + "value": "3", + "start":132,"end":137,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}} } ], "trailingComments": [ @@ -178,43 +185,38 @@ "start":151,"end":156,"loc":{"start":{"line":2,"column":40},"end":{"line":2,"column":45}} } ], - "innerComments": [ + "leadingComments": [ { "type": "CommentBlock", - "value": "3", - "start":132,"end":137,"loc":{"start":{"line":2,"column":21},"end":{"line":2,"column":26}} + "value": "2", + "start":124,"end":129,"loc":{"start":{"line":2,"column":13},"end":{"line":2,"column":18}} } - ], - "local": { - "type": "Identifier", - "start":147,"end":150,"loc":{"start":{"line":2,"column":36},"end":{"line":2,"column":39},"identifierName":"foo"}, - "leadingComments": [ - { - "type": "CommentBlock", - "value": "4", - "start":141,"end":146,"loc":{"start":{"line":2,"column":30},"end":{"line":2,"column":35}} - } - ], - "name": "foo" - } + ] } ], "source": { "type": "StringLiteral", "start":168,"end":173,"loc":{"start":{"line":2,"column":57},"end":{"line":2,"column":62}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo", "leadingComments": [ { "type": "CommentBlock", "value": "6", "start":162,"end":167,"loc":{"start":{"line":2,"column":51},"end":{"line":2,"column":56}} } - ], - "extra": { - "rawValue": "foo", - "raw": "\"foo\"" - }, - "value": "foo" - } + ] + }, + "leadingComments": [ + { + "type": "CommentBlock", + "value": "1", + "start":111,"end":116,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":5}} + } + ] } ], "directives": [] @@ -311,4 +313,4 @@ "start":162,"end":167,"loc":{"start":{"line":2,"column":51},"end":{"line":2,"column":56}} } ] -} +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-declare-function-after/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-declare-function-after/output.json index 72f0f6635a4b..382d4fcac08b 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-declare-function-after/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-declare-function-after/output.json @@ -10,6 +10,7 @@ { "type": "TSDeclareFunction", "start":0,"end":29,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":29}}, + "declare": true, "id": { "type": "Identifier", "start":17,"end":20,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":20},"identifierName":"foo"}, @@ -25,8 +26,7 @@ "type": "TSVoidKeyword", "start":24,"end":28,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":28}} } - }, - "declare": true + } }, { "type": "ExportNamedDeclaration", @@ -41,6 +41,7 @@ "start":40,"end":43,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":12},"identifierName":"foo"}, "name": "foo" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":40,"end":43,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":12},"identifierName":"foo"}, diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-declare-function-before/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-declare-function-before/output.json index 2dc87e91b68e..0058fdfd1c63 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-declare-function-before/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-declare-function-before/output.json @@ -20,6 +20,7 @@ "start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12},"identifierName":"foo"}, "name": "foo" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12},"identifierName":"foo"}, @@ -33,6 +34,7 @@ { "type": "TSDeclareFunction", "start":17,"end":46,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":29}}, + "declare": true, "id": { "type": "Identifier", "start":34,"end":37,"loc":{"start":{"line":3,"column":17},"end":{"line":3,"column":20},"identifierName":"foo"}, @@ -48,8 +50,7 @@ "type": "TSVoidKeyword", "start":41,"end":45,"loc":{"start":{"line":3,"column":24},"end":{"line":3,"column":28}} } - }, - "declare": true + } } ], "directives": [] diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-enum-after/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-enum-after/output.json index 46d31f603be2..69654c84b6ec 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-enum-after/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-enum-after/output.json @@ -37,6 +37,7 @@ "start":70,"end":74,"loc":{"start":{"line":5,"column":9},"end":{"line":5,"column":13},"identifierName":"Test"}, "name": "Test" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":78,"end":85,"loc":{"start":{"line":5,"column":17},"end":{"line":5,"column":24},"identifierName":"default"}, diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-enum-before/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-enum-before/output.json index 1ee9c0bf6b56..66814a6eb848 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-enum-before/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-enum-before/output.json @@ -20,6 +20,7 @@ "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13},"identifierName":"Test"}, "name": "Test" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":17,"end":24,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":24},"identifierName":"default"}, diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-interface-after/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-interface-after/output.json index b24638468861..13baab57ae9f 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-interface-after/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-interface-after/output.json @@ -34,6 +34,7 @@ "start":24,"end":25,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":10},"identifierName":"A"}, "name": "A" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":24,"end":25,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":10},"identifierName":"A"}, diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-interface-before/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-interface-before/output.json index 1d0567998efb..091f98022cb1 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-interface-before/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-interface-before/output.json @@ -20,6 +20,7 @@ "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"A"}, "name": "A" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"A"}, diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-namespace/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-namespace/output.json index 80c8faf0bbb8..0e29a80b2993 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-namespace/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-namespace/output.json @@ -34,6 +34,7 @@ "start":25,"end":26,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":10},"identifierName":"N"}, "name": "N" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":25,"end":26,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":10},"identifierName":"N"}, diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-type-after/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-type-after/output.json index b5f2fb8ebd93..d4e0ffde50d6 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-type-after/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-type-after/output.json @@ -33,6 +33,7 @@ "start":26,"end":27,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":10},"identifierName":"A"}, "name": "A" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":26,"end":27,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":10},"identifierName":"A"}, diff --git a/packages/babel-parser/test/fixtures/typescript/scope/export-type-before/output.json b/packages/babel-parser/test/fixtures/typescript/scope/export-type-before/output.json index 3da921f98898..90abe10759e4 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/export-type-before/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/export-type-before/output.json @@ -20,6 +20,7 @@ "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"A"}, "name": "A" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":9,"end":10,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":10},"identifierName":"A"}, diff --git a/packages/babel-parser/test/fixtures/typescript/scope/function-type-before-declaration/output.json b/packages/babel-parser/test/fixtures/typescript/scope/function-type-before-declaration/output.json index 54e57b9a6d14..0d4eb7d84b85 100644 --- a/packages/babel-parser/test/fixtures/typescript/scope/function-type-before-declaration/output.json +++ b/packages/babel-parser/test/fixtures/typescript/scope/function-type-before-declaration/output.json @@ -58,6 +58,7 @@ "start":46,"end":47,"loc":{"start":{"line":4,"column":9},"end":{"line":4,"column":10},"identifierName":"f"}, "name": "f" }, + "exportKind": "value", "exported": { "type": "Identifier", "start":46,"end":47,"loc":{"start":{"line":4,"column":9},"end":{"line":4,"column":10},"identifierName":"f"}, diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-basic/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-basic/input.ts new file mode 100644 index 000000000000..1b15744ea1ca --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-basic/input.ts @@ -0,0 +1 @@ +export { type A, type B, type C } from "foo"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-basic/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-basic/output.json new file mode 100644 index 000000000000..4c88696f67c6 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-basic/output.json @@ -0,0 +1,75 @@ +{ + "type": "File", + "start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}}, + "program": { + "type": "Program", + "start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExportNamedDeclaration", + "start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":9,"end":15,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":15}}, + "local": { + "type": "Identifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15},"identifierName":"A"}, + "name": "A" + }, + "exportKind": "type", + "exported": { + "type": "Identifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15},"identifierName":"A"}, + "name": "A" + } + }, + { + "type": "ExportSpecifier", + "start":17,"end":23,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":23}}, + "local": { + "type": "Identifier", + "start":22,"end":23,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":23},"identifierName":"B"}, + "name": "B" + }, + "exportKind": "type", + "exported": { + "type": "Identifier", + "start":22,"end":23,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":23},"identifierName":"B"}, + "name": "B" + } + }, + { + "type": "ExportSpecifier", + "start":25,"end":31,"loc":{"start":{"line":1,"column":25},"end":{"line":1,"column":31}}, + "local": { + "type": "Identifier", + "start":30,"end":31,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":31},"identifierName":"C"}, + "name": "C" + }, + "exportKind": "type", + "exported": { + "type": "Identifier", + "start":30,"end":31,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":31},"identifierName":"C"}, + "name": "C" + } + } + ], + "source": { + "type": "StringLiteral", + "start":39,"end":44,"loc":{"start":{"line":1,"column":39},"end":{"line":1,"column":44}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + }, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/input.ts new file mode 100644 index 000000000000..31c6395162cb --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/input.ts @@ -0,0 +1 @@ +export type { type foo } from "foo"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/output.json new file mode 100644 index 000000000000..ad97aea5407b --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-in-type/output.json @@ -0,0 +1,48 @@ +{ + "type": "File", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "errors": [ + "SyntaxError: The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement. (1:14)" + ], + "program": { + "type": "Program", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExportNamedDeclaration", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "exportKind": "type", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":14,"end":22,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":22}}, + "local": { + "type": "Identifier", + "start":19,"end":22,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":22},"identifierName":"foo"}, + "name": "foo" + }, + "exported": { + "type": "Identifier", + "start":19,"end":22,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":22},"identifierName":"foo"}, + "name": "foo" + }, + "exportKind": "type" + } + ], + "source": { + "type": "StringLiteral", + "start":30,"end":35,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":35}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + }, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-and-named-type/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-and-named-type/input.ts new file mode 100644 index 000000000000..704b11597e95 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-and-named-type/input.ts @@ -0,0 +1 @@ +export { Component, type ComponentProps } from "./exports.js"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-and-named-type/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-and-named-type/output.json new file mode 100644 index 000000000000..7488dd1ac788 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-and-named-type/output.json @@ -0,0 +1,60 @@ +{ + "type": "File", + "start":0,"end":62,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":62}}, + "program": { + "type": "Program", + "start":0,"end":62,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":62}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExportNamedDeclaration", + "start":0,"end":62,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":62}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18}}, + "local": { + "type": "Identifier", + "start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18},"identifierName":"Component"}, + "name": "Component" + }, + "exported": { + "type": "Identifier", + "start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18},"identifierName":"Component"}, + "name": "Component" + }, + "exportKind": "value" + }, + { + "type": "ExportSpecifier", + "start":20,"end":39,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":39}}, + "local": { + "type": "Identifier", + "start":25,"end":39,"loc":{"start":{"line":1,"column":25},"end":{"line":1,"column":39},"identifierName":"ComponentProps"}, + "name": "ComponentProps" + }, + "exported": { + "type": "Identifier", + "start":25,"end":39,"loc":{"start":{"line":1,"column":25},"end":{"line":1,"column":39},"identifierName":"ComponentProps"}, + "name": "ComponentProps" + }, + "exportKind": "type" + } + ], + "source": { + "type": "StringLiteral", + "start":47,"end":61,"loc":{"start":{"line":1,"column":47},"end":{"line":1,"column":61}}, + "extra": { + "rawValue": "./exports.js", + "raw": "\"./exports.js\"" + }, + "value": "./exports.js" + }, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-as/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-as/input.ts new file mode 100644 index 000000000000..37f938f792d0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-as/input.ts @@ -0,0 +1 @@ +export { type as as } from "./mod.js"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-as/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-as/output.json new file mode 100644 index 000000000000..22c7de50fd03 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-as/output.json @@ -0,0 +1,45 @@ +{ + "type": "File", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}}, + "program": { + "type": "Program", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExportNamedDeclaration", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":9,"end":19,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":19}}, + "local": { + "type": "Identifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13},"identifierName":"type"}, + "name": "type" + }, + "exported": { + "type": "Identifier", + "start":17,"end":19,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":19},"identifierName":"as"}, + "name": "as" + }, + "exportKind": "value" + } + ], + "source": { + "type": "StringLiteral", + "start":27,"end":37,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":37}}, + "extra": { + "rawValue": "./mod.js", + "raw": "\"./mod.js\"" + }, + "value": "./mod.js" + }, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type/input.ts new file mode 100644 index 000000000000..4c458f9f5763 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type/input.ts @@ -0,0 +1 @@ +export { type } from "./mod.js"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type/output.json new file mode 100644 index 000000000000..fa9cde2a7750 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type/output.json @@ -0,0 +1,45 @@ +{ + "type": "File", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "program": { + "type": "Program", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExportNamedDeclaration", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13}}, + "local": { + "type": "Identifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13},"identifierName":"type"}, + "name": "type" + }, + "exportKind": "value", + "exported": { + "type": "Identifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13},"identifierName":"type"}, + "name": "type" + } + } + ], + "source": { + "type": "StringLiteral", + "start":21,"end":31,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":31}}, + "extra": { + "rawValue": "./mod.js", + "raw": "\"./mod.js\"" + }, + "value": "./mod.js" + }, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-named-as/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-named-as/input.ts new file mode 100644 index 000000000000..b3bbea7354c1 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-named-as/input.ts @@ -0,0 +1 @@ +export { type as } from "./mod.js"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-named-as/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-named-as/output.json new file mode 100644 index 000000000000..e2ca3b3f46df --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-named-as/output.json @@ -0,0 +1,45 @@ +{ + "type": "File", + "start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}}, + "program": { + "type": "Program", + "start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExportNamedDeclaration", + "start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":9,"end":16,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":16}}, + "local": { + "type": "Identifier", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":16},"identifierName":"as"}, + "name": "as" + }, + "exportKind": "type", + "exported": { + "type": "Identifier", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":16},"identifierName":"as"}, + "name": "as" + } + } + ], + "source": { + "type": "StringLiteral", + "start":24,"end":34,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":34}}, + "extra": { + "rawValue": "./mod.js", + "raw": "\"./mod.js\"" + }, + "value": "./mod.js" + }, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-basic/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-basic/input.ts new file mode 100644 index 000000000000..97eff0907bee --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-basic/input.ts @@ -0,0 +1 @@ +import { type A, type B, type C } from "foo"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-basic/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-basic/output.json new file mode 100644 index 000000000000..7799fb70dc3d --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-basic/output.json @@ -0,0 +1,74 @@ +{ + "type": "File", + "start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}}, + "program": { + "type": "Program", + "start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":45,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":45}}, + "importKind": "value", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":9,"end":15,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":15}}, + "imported": { + "type": "Identifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15},"identifierName":"A"}, + "name": "A" + }, + "importKind": "type", + "local": { + "type": "Identifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":15},"identifierName":"A"}, + "name": "A" + } + }, + { + "type": "ImportSpecifier", + "start":17,"end":23,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":23}}, + "imported": { + "type": "Identifier", + "start":22,"end":23,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":23},"identifierName":"B"}, + "name": "B" + }, + "importKind": "type", + "local": { + "type": "Identifier", + "start":22,"end":23,"loc":{"start":{"line":1,"column":22},"end":{"line":1,"column":23},"identifierName":"B"}, + "name": "B" + } + }, + { + "type": "ImportSpecifier", + "start":25,"end":31,"loc":{"start":{"line":1,"column":25},"end":{"line":1,"column":31}}, + "imported": { + "type": "Identifier", + "start":30,"end":31,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":31},"identifierName":"C"}, + "name": "C" + }, + "importKind": "type", + "local": { + "type": "Identifier", + "start":30,"end":31,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":31},"identifierName":"C"}, + "name": "C" + } + } + ], + "source": { + "type": "StringLiteral", + "start":39,"end":44,"loc":{"start":{"line":1,"column":39},"end":{"line":1,"column":44}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/input.ts new file mode 100644 index 000000000000..b15216bc9806 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/input.ts @@ -0,0 +1 @@ +import type { type foo } from "foo"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/output.json new file mode 100644 index 000000000000..65c51671af8b --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/output.json @@ -0,0 +1,47 @@ +{ + "type": "File", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "errors": [ + "SyntaxError: The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement. (1:14)" + ], + "program": { + "type": "Program", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "importKind": "type", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":14,"end":22,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":22}}, + "imported": { + "type": "Identifier", + "start":19,"end":22,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":22},"identifierName":"foo"}, + "name": "foo" + }, + "local": { + "type": "Identifier", + "start":19,"end":22,"loc":{"start":{"line":1,"column":19},"end":{"line":1,"column":22},"identifierName":"foo"}, + "name": "foo" + }, + "importKind": "type" + } + ], + "source": { + "type": "StringLiteral", + "start":30,"end":35,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":35}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-and-named-type/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-and-named-type/input.ts new file mode 100644 index 000000000000..52e743608261 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-and-named-type/input.ts @@ -0,0 +1 @@ +import { Component, type ComponentProps } from "./exports.js"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-and-named-type/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-and-named-type/output.json new file mode 100644 index 000000000000..ee7a3a58078a --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-and-named-type/output.json @@ -0,0 +1,59 @@ +{ + "type": "File", + "start":0,"end":62,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":62}}, + "program": { + "type": "Program", + "start":0,"end":62,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":62}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":62,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":62}}, + "importKind": "value", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18}}, + "imported": { + "type": "Identifier", + "start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18},"identifierName":"Component"}, + "name": "Component" + }, + "local": { + "type": "Identifier", + "start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18},"identifierName":"Component"}, + "name": "Component" + }, + "importKind": "value" + }, + { + "type": "ImportSpecifier", + "start":20,"end":39,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":39}}, + "imported": { + "type": "Identifier", + "start":25,"end":39,"loc":{"start":{"line":1,"column":25},"end":{"line":1,"column":39},"identifierName":"ComponentProps"}, + "name": "ComponentProps" + }, + "local": { + "type": "Identifier", + "start":25,"end":39,"loc":{"start":{"line":1,"column":25},"end":{"line":1,"column":39},"identifierName":"ComponentProps"}, + "name": "ComponentProps" + }, + "importKind": "type" + } + ], + "source": { + "type": "StringLiteral", + "start":47,"end":61,"loc":{"start":{"line":1,"column":47},"end":{"line":1,"column":61}}, + "extra": { + "rawValue": "./exports.js", + "raw": "\"./exports.js\"" + }, + "value": "./exports.js" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type-as-as/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type-as-as/input.ts new file mode 100644 index 000000000000..bc2ee3b22029 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type-as-as/input.ts @@ -0,0 +1 @@ +import { type as as } from "./mod.js"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type-as-as/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type-as-as/output.json new file mode 100644 index 000000000000..16645aae9765 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type-as-as/output.json @@ -0,0 +1,44 @@ +{ + "type": "File", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}}, + "program": { + "type": "Program", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":38,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":38}}, + "importKind": "value", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":9,"end":19,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":19}}, + "imported": { + "type": "Identifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13},"identifierName":"type"}, + "name": "type" + }, + "local": { + "type": "Identifier", + "start":17,"end":19,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":19},"identifierName":"as"}, + "name": "as" + }, + "importKind": "value" + } + ], + "source": { + "type": "StringLiteral", + "start":27,"end":37,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":37}}, + "extra": { + "rawValue": "./mod.js", + "raw": "\"./mod.js\"" + }, + "value": "./mod.js" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type/input.ts new file mode 100644 index 000000000000..33f4c30dd0dd --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type/input.ts @@ -0,0 +1 @@ +import { type } from "./mod.js"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type/output.json new file mode 100644 index 000000000000..341d6b506293 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-named-type/output.json @@ -0,0 +1,44 @@ +{ + "type": "File", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "program": { + "type": "Program", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "importKind": "value", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13}}, + "imported": { + "type": "Identifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13},"identifierName":"type"}, + "name": "type" + }, + "importKind": "value", + "local": { + "type": "Identifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13},"identifierName":"type"}, + "name": "type" + } + } + ], + "source": { + "type": "StringLiteral", + "start":21,"end":31,"loc":{"start":{"line":1,"column":21},"end":{"line":1,"column":31}}, + "extra": { + "rawValue": "./mod.js", + "raw": "\"./mod.js\"" + }, + "value": "./mod.js" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-named-as/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-named-as/input.ts new file mode 100644 index 000000000000..e6fbba5e5919 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-named-as/input.ts @@ -0,0 +1 @@ +import { type as } from "./mod.js"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-named-as/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-named-as/output.json new file mode 100644 index 000000000000..92edacf58ae8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-named-as/output.json @@ -0,0 +1,44 @@ +{ + "type": "File", + "start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}}, + "program": { + "type": "Program", + "start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":35,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":35}}, + "importKind": "value", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":9,"end":16,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":16}}, + "imported": { + "type": "Identifier", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":16},"identifierName":"as"}, + "name": "as" + }, + "importKind": "type", + "local": { + "type": "Identifier", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":16},"identifierName":"as"}, + "name": "as" + } + } + ], + "source": { + "type": "StringLiteral", + "start":24,"end":34,"loc":{"start":{"line":1,"column":24},"end":{"line":1,"column":34}}, + "extra": { + "rawValue": "./mod.js", + "raw": "\"./mod.js\"" + }, + "value": "./mod.js" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration-error/output.json b/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration-error/output.json index 3df1338cbc58..5e09de512341 100644 --- a/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration-error/output.json +++ b/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration-error/output.json @@ -32,6 +32,7 @@ "start":26,"end":29,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":29},"identifierName":"Bar"}, "name": "Bar" }, + "importKind": "value", "local": { "type": "Identifier", "start":26,"end":29,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":29},"identifierName":"Bar"}, @@ -46,6 +47,7 @@ "start":31,"end":34,"loc":{"start":{"line":1,"column":31},"end":{"line":1,"column":34},"identifierName":"Baz"}, "name": "Baz" }, + "importKind": "value", "local": { "type": "Identifier", "start":31,"end":34,"loc":{"start":{"line":1,"column":31},"end":{"line":1,"column":34},"identifierName":"Baz"}, diff --git a/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration/output.json b/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration/output.json index b0f9cac40033..62b9afdd1083 100644 --- a/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration/output.json +++ b/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration/output.json @@ -45,6 +45,7 @@ "start":42,"end":43,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":15},"identifierName":"A"}, "name": "A" }, + "importKind": "value", "local": { "type": "Identifier", "start":42,"end":43,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":15},"identifierName":"A"}, @@ -59,6 +60,7 @@ "start":45,"end":46,"loc":{"start":{"line":2,"column":17},"end":{"line":2,"column":18},"identifierName":"B"}, "name": "B" }, + "importKind": "value", "local": { "type": "Identifier", "start":45,"end":46,"loc":{"start":{"line":2,"column":17},"end":{"line":2,"column":18},"identifierName":"B"}, From 8daf3bc8416e61bb66a443ae0a4d5689714492b7 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 1 Oct 2021 07:37:56 +0900 Subject: [PATCH 03/25] Implement generator --- packages/babel-generator/src/generators/modules.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/babel-generator/src/generators/modules.ts b/packages/babel-generator/src/generators/modules.ts index f95533a38acc..06f8e3925cb2 100644 --- a/packages/babel-generator/src/generators/modules.ts +++ b/packages/babel-generator/src/generators/modules.ts @@ -40,6 +40,11 @@ export function ExportDefaultSpecifier( } export function ExportSpecifier(this: Printer, node: t.ExportSpecifier) { + if (node.exportKind === "type") { + this.word(node.exportKind); + this.space(); + } + this.print(node.local, node); // @ts-expect-error todo(flow-ts) maybe check node type instead of relying on name to be undefined on t.StringLiteral if (node.exported && node.local.name !== node.exported.name) { From 16b4bc1f46d26c928547a726e23f171f6f7e616e Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 1 Oct 2021 07:38:19 +0900 Subject: [PATCH 04/25] Add generator tests --- .../test/fixtures/typescript/type-only-export-specifier/input.js | 1 + .../fixtures/typescript/type-only-export-specifier/output.js | 1 + .../test/fixtures/typescript/type-only-import-specifier/input.js | 1 + .../fixtures/typescript/type-only-import-specifier/output.js | 1 + 4 files changed, 4 insertions(+) create mode 100644 packages/babel-generator/test/fixtures/typescript/type-only-export-specifier/input.js create mode 100644 packages/babel-generator/test/fixtures/typescript/type-only-export-specifier/output.js create mode 100644 packages/babel-generator/test/fixtures/typescript/type-only-import-specifier/input.js create mode 100644 packages/babel-generator/test/fixtures/typescript/type-only-import-specifier/output.js diff --git a/packages/babel-generator/test/fixtures/typescript/type-only-export-specifier/input.js b/packages/babel-generator/test/fixtures/typescript/type-only-export-specifier/input.js new file mode 100644 index 000000000000..96fce1593f9e --- /dev/null +++ b/packages/babel-generator/test/fixtures/typescript/type-only-export-specifier/input.js @@ -0,0 +1 @@ +export { type foo } from "foo"; diff --git a/packages/babel-generator/test/fixtures/typescript/type-only-export-specifier/output.js b/packages/babel-generator/test/fixtures/typescript/type-only-export-specifier/output.js new file mode 100644 index 000000000000..fd80c0978c69 --- /dev/null +++ b/packages/babel-generator/test/fixtures/typescript/type-only-export-specifier/output.js @@ -0,0 +1 @@ +export { type foo } from "foo"; \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/typescript/type-only-import-specifier/input.js b/packages/babel-generator/test/fixtures/typescript/type-only-import-specifier/input.js new file mode 100644 index 000000000000..46ea4b7e00c5 --- /dev/null +++ b/packages/babel-generator/test/fixtures/typescript/type-only-import-specifier/input.js @@ -0,0 +1 @@ +import { type foo } from "foo"; diff --git a/packages/babel-generator/test/fixtures/typescript/type-only-import-specifier/output.js b/packages/babel-generator/test/fixtures/typescript/type-only-import-specifier/output.js new file mode 100644 index 000000000000..5805ddf6cba4 --- /dev/null +++ b/packages/babel-generator/test/fixtures/typescript/type-only-import-specifier/output.js @@ -0,0 +1 @@ +import { type foo } from "foo"; \ No newline at end of file From a2edbe38954e9cde77d5f74136330255bf690326 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 1 Oct 2021 07:38:46 +0900 Subject: [PATCH 05/25] Implement types --- packages/babel-types/src/ast-types/generated/index.ts | 3 ++- packages/babel-types/src/definitions/core.ts | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/babel-types/src/ast-types/generated/index.ts b/packages/babel-types/src/ast-types/generated/index.ts index f4def136c3c2..9d4c4eb33d2b 100755 --- a/packages/babel-types/src/ast-types/generated/index.ts +++ b/packages/babel-types/src/ast-types/generated/index.ts @@ -857,6 +857,7 @@ export interface ExportSpecifier extends BaseNode { type: "ExportSpecifier"; local: Identifier; exported: Identifier | StringLiteral; + exportKind?: "type" | "value" | null; } export interface ForOfStatement extends BaseNode { @@ -891,7 +892,7 @@ export interface ImportSpecifier extends BaseNode { type: "ImportSpecifier"; local: Identifier; imported: Identifier | StringLiteral; - importKind?: "type" | "typeof" | null; + importKind?: "type" | "typeof" | "value" | null; } export interface MetaProperty extends BaseNode { diff --git a/packages/babel-types/src/definitions/core.ts b/packages/babel-types/src/definitions/core.ts index fbf7e1859342..18ea0d274868 100644 --- a/packages/babel-types/src/definitions/core.ts +++ b/packages/babel-types/src/definitions/core.ts @@ -1546,6 +1546,11 @@ defineType("ExportSpecifier", { exported: { validate: assertNodeType("Identifier", "StringLiteral"), }, + exportKind: { + // And TypeScript's "export { type foo } from" + validate: assertOneOf("type", "value"), + optional: true, + }, }, }); @@ -1663,7 +1668,8 @@ defineType("ImportSpecifier", { }, importKind: { // Handle Flowtype's extension "import {typeof foo} from" - validate: assertOneOf("type", "typeof"), + // And TypeScript's "import { type foo } from" + validate: assertOneOf("type", "typeof", "value"), optional: true, }, }, From 642100833aa0b572ab2289bfe92246e811cb76dc Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 1 Oct 2021 07:39:26 +0900 Subject: [PATCH 06/25] Implement plugin-transform-typescript --- .../src/index.ts | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index b79d6deb96ad..0c79397a04a6 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -233,9 +233,23 @@ export default declare((api, opts) => { continue; } + const importsToRemove: NodePath[] = []; + const specifiersLength = stmt.node.specifiers.length; + const isAllSpecifiersElided = () => + specifiersLength > 0 && + specifiersLength === importsToRemove.length; + // If onlyRemoveTypeImports is `true`, only remove type-only imports // and exports introduced in TypeScript 3.8. if (onlyRemoveTypeImports) { + for (const specifier of stmt.node.specifiers) { + if (specifier.importKind === "type") { + const binding = stmt.scope.getBinding(specifier.local.name); + if (binding) { + importsToRemove.push(binding.path); + } + } + } NEEDS_EXPLICIT_ESM.set(path.node, false); } else { // Note: this will allow both `import { } from "m"` and `import "m";`. @@ -245,9 +259,6 @@ export default declare((api, opts) => { continue; } - let allElided = true; - const importsToRemove: NodePath[] = []; - for (const specifier of stmt.node.specifiers) { const binding = stmt.scope.getBinding(specifier.local.name); @@ -268,17 +279,16 @@ export default declare((api, opts) => { ) { importsToRemove.push(binding.path); } else { - allElided = false; NEEDS_EXPLICIT_ESM.set(path.node, false); } } + } - if (allElided) { - stmt.remove(); - } else { - for (const importPath of importsToRemove) { - importPath.remove(); - } + if (isAllSpecifiersElided()) { + stmt.remove(); + } else { + for (const importPath of importsToRemove) { + importPath.remove(); } } @@ -329,6 +339,17 @@ export default declare((api, opts) => { return; } + // remove export declaration that is filled with type-only specifiers + // export { type A1, type A2 } from "a"; + if ( + path.node.source && + path.node.specifiers.length > 0 && + path.node.specifiers.every(({ exportKind }) => exportKind === "type") + ) { + path.remove(); + return; + } + // remove export declaration if it's exporting only types // This logic is needed when exportKind is "value", because // currently the "type" keyword is optional. @@ -352,7 +373,10 @@ export default declare((api, opts) => { ExportSpecifier(path) { // remove type exports - if (!path.parent.source && isGlobalType(path, path.node.local.name)) { + if ( + (!path.parent.source && isGlobalType(path, path.node.local.name)) || + path.node.exportKind === "type" + ) { path.remove(); } }, From 6a596b97b9582f69d5985b7b4c149ffe326d08a8 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 1 Oct 2021 07:39:56 +0900 Subject: [PATCH 07/25] Add plugin-transform-typescript tests --- .../fixtures/exports/type-only-export-specifier-1/input.ts | 2 ++ .../fixtures/exports/type-only-export-specifier-1/output.mjs | 3 +++ .../fixtures/exports/type-only-export-specifier-2/input.ts | 1 + .../fixtures/exports/type-only-export-specifier-2/output.mjs | 1 + .../fixtures/exports/type-only-export-specifier-3/input.ts | 1 + .../fixtures/exports/type-only-export-specifier-3/output.mjs | 1 + .../test/fixtures/imports/only-remove-type-imports/input.ts | 2 ++ .../test/fixtures/imports/only-remove-type-imports/output.mjs | 1 + .../fixtures/imports/type-only-import-specifier-1/input.ts | 2 ++ .../fixtures/imports/type-only-import-specifier-1/output.mjs | 2 ++ .../fixtures/imports/type-only-import-specifier-2/input.ts | 1 + .../fixtures/imports/type-only-import-specifier-2/output.mjs | 1 + 12 files changed, 18 insertions(+) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-1/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-1/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-2/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-2/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-3/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-3/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-1/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-1/output.mjs create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-2/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-2/output.mjs diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-1/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-1/input.ts new file mode 100644 index 000000000000..d268127794d9 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-1/input.ts @@ -0,0 +1,2 @@ +class Foo {} +export { type Foo }; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-1/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-1/output.mjs new file mode 100644 index 000000000000..736b92725dc2 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-1/output.mjs @@ -0,0 +1,3 @@ +class Foo {} + +export {}; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-2/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-2/input.ts new file mode 100644 index 000000000000..b7724a25247d --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-2/input.ts @@ -0,0 +1 @@ +export { type A1, type A2 } from "a" diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-2/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-2/output.mjs new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-2/output.mjs @@ -0,0 +1 @@ +export {}; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-3/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-3/input.ts new file mode 100644 index 000000000000..a6431530d3ac --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-3/input.ts @@ -0,0 +1 @@ +export { type A1, type A2, A3 } from "a" diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-3/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-3/output.mjs new file mode 100644 index 000000000000..da23041dfb5d --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/exports/type-only-export-specifier-3/output.mjs @@ -0,0 +1 @@ +export { A3 } from "a"; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/input.ts index 744ae808eaa9..6b2b75d9f454 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/input.ts +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/input.ts @@ -10,4 +10,6 @@ import "g"; import type H from "h"; import type { I, I2 } from "i"; import type * as J from "j"; +import { type K1, type K2 } from "k"; +import { type L1, L2, type L3 } from "l"; ; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs index d86475cacac8..78a426b356b2 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs @@ -5,4 +5,5 @@ import d, { d2 } from "d"; import e, { e3 as e4 } from "e"; import "f"; import "g"; +import { L2 } from "l"; ; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-1/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-1/input.ts new file mode 100644 index 000000000000..e4fde7f90cb3 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-1/input.ts @@ -0,0 +1,2 @@ +import { Foo1, type Foo2 } from "Foo"; +Foo1; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-1/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-1/output.mjs new file mode 100644 index 000000000000..b960b41f8c81 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-1/output.mjs @@ -0,0 +1,2 @@ +import { Foo1 } from "Foo"; +Foo1; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-2/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-2/input.ts new file mode 100644 index 000000000000..d40d26bca8f4 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-2/input.ts @@ -0,0 +1 @@ +import { type Foo1, type Foo2 } from "Foo"; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-2/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-2/output.mjs new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-2/output.mjs @@ -0,0 +1 @@ +export {}; From 4929209940007b1cab49c3f62b6376ad514f6d19 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 2 Oct 2021 16:25:58 +0900 Subject: [PATCH 08/25] Remove imported-type from exports --- .../src/index.ts | 44 ++++++++++--------- .../type-only-import-specifier-3/input.ts | 2 + .../type-only-import-specifier-3/output.mjs | 1 + 3 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-3/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-3/output.mjs diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index 0c79397a04a6..3ca1349814ad 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -239,17 +239,19 @@ export default declare((api, opts) => { specifiersLength > 0 && specifiersLength === importsToRemove.length; + for (const specifier of stmt.node.specifiers) { + if (specifier.importKind === "type") { + registerGlobalType(programNode, specifier.local.name); + const binding = stmt.scope.getBinding(specifier.local.name); + if (binding) { + importsToRemove.push(binding.path); + } + } + } + // If onlyRemoveTypeImports is `true`, only remove type-only imports // and exports introduced in TypeScript 3.8. if (onlyRemoveTypeImports) { - for (const specifier of stmt.node.specifiers) { - if (specifier.importKind === "type") { - const binding = stmt.scope.getBinding(specifier.local.name); - if (binding) { - importsToRemove.push(binding.path); - } - } - } NEEDS_EXPLICIT_ESM.set(path.node, false); } else { // Note: this will allow both `import { } from "m"` and `import "m";`. @@ -268,18 +270,20 @@ export default declare((api, opts) => { // just bail if there is no binding, since chances are good that if // the import statement was injected then it wasn't a typescript type // import anyway. - if ( - binding && - isImportTypeOnly({ - binding, - programPath: path, - pragmaImportName, - pragmaFragImportName, - }) - ) { - importsToRemove.push(binding.path); - } else { - NEEDS_EXPLICIT_ESM.set(path.node, false); + if (!importsToRemove.includes(binding.path)) { + if ( + binding && + isImportTypeOnly({ + binding, + programPath: path, + pragmaImportName, + pragmaFragImportName, + }) + ) { + importsToRemove.push(binding.path); + } else { + NEEDS_EXPLICIT_ESM.set(path.node, false); + } } } } diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-3/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-3/input.ts new file mode 100644 index 000000000000..d98c1b3715be --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-3/input.ts @@ -0,0 +1,2 @@ +import { type Foo1, type Foo2 } from "Foo"; +export { Foo1, Foo2 }; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-3/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-3/output.mjs new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-3/output.mjs @@ -0,0 +1 @@ +export {}; From d337f69e35637b947021ed8161eddab2d3f52829 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Mon, 11 Oct 2021 19:22:44 +0900 Subject: [PATCH 09/25] Use string literal instead of variable --- packages/babel-generator/src/generators/modules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-generator/src/generators/modules.ts b/packages/babel-generator/src/generators/modules.ts index 06f8e3925cb2..16cd4bd08aef 100644 --- a/packages/babel-generator/src/generators/modules.ts +++ b/packages/babel-generator/src/generators/modules.ts @@ -41,7 +41,7 @@ export function ExportDefaultSpecifier( export function ExportSpecifier(this: Printer, node: t.ExportSpecifier) { if (node.exportKind === "type") { - this.word(node.exportKind); + this.word("type"); this.space(); } From 8cb6dc067d5cc78c9a6fe77582c1602efad43644 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Mon, 11 Oct 2021 19:28:14 +0900 Subject: [PATCH 10/25] Use set instead of array --- .../babel-plugin-transform-typescript/src/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index 3ca1349814ad..1c5fcc2d792b 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -233,18 +233,18 @@ export default declare((api, opts) => { continue; } - const importsToRemove: NodePath[] = []; + const importsToRemove: Set> = new Set(); const specifiersLength = stmt.node.specifiers.length; const isAllSpecifiersElided = () => specifiersLength > 0 && - specifiersLength === importsToRemove.length; + specifiersLength === importsToRemove.size; for (const specifier of stmt.node.specifiers) { if (specifier.importKind === "type") { registerGlobalType(programNode, specifier.local.name); const binding = stmt.scope.getBinding(specifier.local.name); if (binding) { - importsToRemove.push(binding.path); + importsToRemove.add(binding.path); } } } @@ -270,7 +270,7 @@ export default declare((api, opts) => { // just bail if there is no binding, since chances are good that if // the import statement was injected then it wasn't a typescript type // import anyway. - if (!importsToRemove.includes(binding.path)) { + if (!importsToRemove.has(binding.path)) { if ( binding && isImportTypeOnly({ @@ -280,7 +280,7 @@ export default declare((api, opts) => { pragmaFragImportName, }) ) { - importsToRemove.push(binding.path); + importsToRemove.add(binding.path); } else { NEEDS_EXPLICIT_ESM.set(path.node, false); } From 4d1cde6ae2cfdbe1b3ec2a090d75c91264bbdae8 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Mon, 11 Oct 2021 19:35:41 +0900 Subject: [PATCH 11/25] Keep import statement if all of specifiers are type-only one --- .../babel-plugin-transform-typescript/src/index.ts | 2 +- .../imports/only-remove-type-imports/output.mjs | 1 + .../imports/type-only-import-specifier-4/input.ts | 1 + .../imports/type-only-import-specifier-4/options.json | 10 ++++++++++ .../imports/type-only-import-specifier-4/output.mjs | 1 + 5 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/input.ts create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/options.json create mode 100644 packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/output.mjs diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index 1c5fcc2d792b..78098192aba2 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -288,7 +288,7 @@ export default declare((api, opts) => { } } - if (isAllSpecifiersElided()) { + if (!onlyRemoveTypeImports && isAllSpecifiersElided()) { stmt.remove(); } else { for (const importPath of importsToRemove) { diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs index 78a426b356b2..169883383851 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs @@ -5,5 +5,6 @@ import d, { d2 } from "d"; import e, { e3 as e4 } from "e"; import "f"; import "g"; +import "k"; import { L2 } from "l"; ; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/input.ts b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/input.ts new file mode 100644 index 000000000000..c8a5c01958be --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/input.ts @@ -0,0 +1 @@ +import { type A } from "x"; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/options.json b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/options.json new file mode 100644 index 000000000000..0ec61d2b5e55 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/options.json @@ -0,0 +1,10 @@ +{ + "plugins": [ + [ + "transform-typescript", + { + "onlyRemoveTypeImports": true + } + ] + ] +} diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/output.mjs new file mode 100644 index 000000000000..856f26b34c30 --- /dev/null +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/output.mjs @@ -0,0 +1 @@ +import "x"; From 05d51834489e1d4f2638d641bde981ace9264eda Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Wed, 13 Oct 2021 16:09:39 +0900 Subject: [PATCH 12/25] Rename isTypeOnly to hasTypeSpecifier --- .../src/plugins/typescript/index.js | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index f709162f4a00..fafc17f7984b 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3328,17 +3328,17 @@ export default (superClass: Class): Class => let leftOfAs = node[leftOfAsKey]; let rightOfAs; - let isTypeOnly = false; + let hasTypeSpecifier = false; let canParseAsKeyword = true; const pos = leftOfAs.start; // https://github.com/microsoft/TypeScript/blob/fc4f9d83d5939047aa6bb2a43965c6e9bbfbc35b/src/compiler/parser.ts#L7411-L7456 if (!isStringSpecifier && leftOfAs.name === "type") { - // import { type } from "mod"; - isTypeOnly: false, leftOfAs: type - // import { type as } from "mod"; - isTypeOnly: true, leftOfAs: as - // import { type as as } from "mod"; - isTypeOnly: false, leftOfAs: type, rightOfAs: as - // import { type as as as } from "mod"; - isTypeOnly: true, leftOfAs: as, rightOfAs: as + // import { type } from "mod"; - hasTypeSpecifier: false, leftOfAs: type + // import { type as } from "mod"; - hasTypeSpecifier: true, leftOfAs: as + // import { type as as } from "mod"; - hasTypeSpecifier: false, leftOfAs: type, rightOfAs: as + // import { type as as as } from "mod"; - hasTypeSpecifier: true, leftOfAs: as, rightOfAs: as if (this.isContextual(tt._as)) { // { type as ...? } const firstAs = this.parseIdentifier(); @@ -3347,7 +3347,7 @@ export default (superClass: Class): Class => const secondAs = this.parseIdentifier(); if (this.match(tt.name)) { // { type as as something } - isTypeOnly = true; + hasTypeSpecifier = true; leftOfAs = firstAs; rightOfAs = this.parseIdentifier(); canParseAsKeyword = false; @@ -3362,12 +3362,12 @@ export default (superClass: Class): Class => rightOfAs = this.parseIdentifier(); } else { // { type as } - isTypeOnly = true; + hasTypeSpecifier = true; leftOfAs = firstAs; } } else if (this.match(tt.name)) { // { type something ...? } - isTypeOnly = true; + hasTypeSpecifier = true; leftOfAs = this.parseIdentifier(); } } @@ -3375,9 +3375,9 @@ export default (superClass: Class): Class => node[rightOfAsKey] = rightOfAs; const kindKey = isImport ? "importKind" : "exportKind"; - node[kindKey] = isTypeOnly ? "type" : "value"; + node[kindKey] = hasTypeSpecifier ? "type" : "value"; - if (isTypeOnly && isInTypeOnlyImportExport) { + if (hasTypeSpecifier && isInTypeOnlyImportExport) { this.raise( pos, isImport From 31b273cf58012ec5092b80bd1250e83f232b04b3 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 15 Oct 2021 13:49:06 +0900 Subject: [PATCH 13/25] Use isContextual instead of string comparison --- packages/babel-parser/src/parser/statement.js | 9 +++++++-- packages/babel-parser/src/plugins/typescript/index.js | 3 ++- .../expport-invalid-escaped-type-only/input.ts | 1 + .../expport-invalid-escaped-type-only/options.json | 7 +++++++ .../import-invalid-escaped-type-only/input.ts | 1 + .../import-invalid-escaped-type-only/options.json | 7 +++++++ 6 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/expport-invalid-escaped-type-only/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/expport-invalid-escaped-type-only/options.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-escaped-type-only/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-escaped-type-only/options.json diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index bdb5e82f6847..0a3d117f40f6 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2169,6 +2169,7 @@ export default class StatementParser extends ExpressionParser { } const node = this.startNode(); + const isMaybeTypeOnly = this.isContextual(tt._type); const isString = this.match(tt.string); node.local = this.parseModuleExportName(); const canParseAsKeyword = this.parseTypeOnlyImportExportSpecifier( @@ -2176,6 +2177,7 @@ export default class StatementParser extends ExpressionParser { /* isImport */ false, isString, isInTypeExport, + isMaybeTypeOnly, ); if (canParseAsKeyword && this.eatContextual(tt._as)) { node.exported = this.parseModuleExportName(); @@ -2448,6 +2450,7 @@ export default class StatementParser extends ExpressionParser { isImport: boolean, isStringSpecifier: boolean, isInTypeOnlyImportExport: boolean, + isMaybeTypeOnly: boolean, /* eslint-enable no-unused-vars */ ): boolean { return true; @@ -2457,14 +2460,16 @@ export default class StatementParser extends ExpressionParser { parseImportSpecifier(node: N.ImportDeclaration): void { const specifier = this.startNode(); const importedIsString = this.match(tt.string); + const isMaybeTypeOnly = this.isContextual(tt._type); specifier.imported = this.parseModuleExportName(); - const canParseAsKeyworkd = this.parseTypeOnlyImportExportSpecifier( + const canParseAsKeyword = this.parseTypeOnlyImportExportSpecifier( specifier, /* isImport */ true, importedIsString, /* isInTypeOnlyImportExport */ node.importKind === "type", + isMaybeTypeOnly, ); - if (canParseAsKeyworkd && this.eatContextual(tt._as)) { + if (canParseAsKeyword && this.eatContextual(tt._as)) { specifier.local = this.parseIdentifier(); } else { const { imported } = specifier; diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index fafc17f7984b..bcfc016c439c 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3321,6 +3321,7 @@ export default (superClass: Class): Class => isImport: boolean, isStringSpecifier: boolean, isInTypeOnlyImportExport: boolean, + isMaybeTypeOnly: boolean, ): boolean { const leftOfAsKey = isImport ? "imported" : "local"; const rightOfAsKey = isImport ? "local" : "exported"; @@ -3334,7 +3335,7 @@ export default (superClass: Class): Class => const pos = leftOfAs.start; // https://github.com/microsoft/TypeScript/blob/fc4f9d83d5939047aa6bb2a43965c6e9bbfbc35b/src/compiler/parser.ts#L7411-L7456 - if (!isStringSpecifier && leftOfAs.name === "type") { + if (!isStringSpecifier && isMaybeTypeOnly) { // import { type } from "mod"; - hasTypeSpecifier: false, leftOfAs: type // import { type as } from "mod"; - hasTypeSpecifier: true, leftOfAs: as // import { type as as } from "mod"; - hasTypeSpecifier: false, leftOfAs: type, rightOfAs: as diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/expport-invalid-escaped-type-only/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/expport-invalid-escaped-type-only/input.ts new file mode 100644 index 000000000000..3e7cfa0ce5e4 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/expport-invalid-escaped-type-only/input.ts @@ -0,0 +1 @@ +export { typ\u0065 as } from "x"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/expport-invalid-escaped-type-only/options.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/expport-invalid-escaped-type-only/options.json new file mode 100644 index 000000000000..2a7af925f7ae --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/expport-invalid-escaped-type-only/options.json @@ -0,0 +1,7 @@ +{ + "sourceType": "module", + "plugins": [ + "typescript" + ], + "throws": "Unexpected token (1:22)" +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-escaped-type-only/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-escaped-type-only/input.ts new file mode 100644 index 000000000000..313c9cfc2370 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-escaped-type-only/input.ts @@ -0,0 +1 @@ +import { typ\u0065 as } from "x"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-escaped-type-only/options.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-escaped-type-only/options.json new file mode 100644 index 000000000000..2a7af925f7ae --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-escaped-type-only/options.json @@ -0,0 +1,7 @@ +{ + "sourceType": "module", + "plugins": [ + "typescript" + ], + "throws": "Unexpected token (1:22)" +} \ No newline at end of file From ec6398e7f793cf7d8e42bc730575845790a23e91 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 15 Oct 2021 14:16:58 +0900 Subject: [PATCH 14/25] Use tokenIsKeywordOrIdentifier instead of match --- .../src/plugins/typescript/index.js | 7 ++- .../export-named-type-as-keyword/input.ts | 2 + .../export-named-type-as-keyword/output.json | 58 +++++++++++++++++++ .../export-type-only-as-as-keyword/input.ts | 2 + .../output.json | 58 +++++++++++++++++++ .../export-type-only-keyword/input.ts | 2 + .../export-type-only-keyword/output.json | 58 +++++++++++++++++++ 7 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-keyword/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-keyword/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-as-as-keyword/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-as-as-keyword/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index bcfc016c439c..3cac2611bbb2 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -11,6 +11,7 @@ import { tokenIsTSDeclarationStart, tokenIsTSTypeOperator, tokenOperatorPrecedence, + tokenIsKeywordOrIdentifier, tt, type TokenType, } from "../../tokenizer/types"; @@ -3346,7 +3347,7 @@ export default (superClass: Class): Class => if (this.isContextual(tt._as)) { // { type as as ...? } const secondAs = this.parseIdentifier(); - if (this.match(tt.name)) { + if (tokenIsKeywordOrIdentifier(this.state.type)) { // { type as as something } hasTypeSpecifier = true; leftOfAs = firstAs; @@ -3357,7 +3358,7 @@ export default (superClass: Class): Class => rightOfAs = secondAs; canParseAsKeyword = false; } - } else if (this.match(tt.name)) { + } else if (tokenIsKeywordOrIdentifier(this.state.type)) { // { type as something } canParseAsKeyword = false; rightOfAs = this.parseIdentifier(); @@ -3366,7 +3367,7 @@ export default (superClass: Class): Class => hasTypeSpecifier = true; leftOfAs = firstAs; } - } else if (this.match(tt.name)) { + } else if (tokenIsKeywordOrIdentifier(this.state.type)) { // { type something ...? } hasTypeSpecifier = true; leftOfAs = this.parseIdentifier(); diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-keyword/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-keyword/input.ts new file mode 100644 index 000000000000..ca6b2b9131a2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-keyword/input.ts @@ -0,0 +1,2 @@ +const type = {}; +export { type as if }; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-keyword/output.json new file mode 100644 index 000000000000..0e29350fb3d6 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-named-type-as-keyword/output.json @@ -0,0 +1,58 @@ +{ + "type": "File", + "start":0,"end":39,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":22}}, + "program": { + "type": "Program", + "start":0,"end":39,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":22}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":16,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":16}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":15,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":15}}, + "id": { + "type": "Identifier", + "start":6,"end":10,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":10},"identifierName":"type"}, + "name": "type" + }, + "init": { + "type": "ObjectExpression", + "start":13,"end":15,"loc":{"start":{"line":1,"column":13},"end":{"line":1,"column":15}}, + "properties": [] + } + } + ], + "kind": "const" + }, + { + "type": "ExportNamedDeclaration", + "start":17,"end":39,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":22}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":26,"end":36,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":19}}, + "local": { + "type": "Identifier", + "start":26,"end":30,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":13},"identifierName":"type"}, + "name": "type" + }, + "exported": { + "type": "Identifier", + "start":34,"end":36,"loc":{"start":{"line":2,"column":17},"end":{"line":2,"column":19},"identifierName":"if"}, + "name": "if" + }, + "exportKind": "value" + } + ], + "source": null, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-as-as-keyword/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-as-as-keyword/input.ts new file mode 100644 index 000000000000..7c9932a83a46 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-as-as-keyword/input.ts @@ -0,0 +1,2 @@ +const as = {}; +export { type as as if }; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-as-as-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-as-as-keyword/output.json new file mode 100644 index 000000000000..f9271906199e --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-as-as-keyword/output.json @@ -0,0 +1,58 @@ +{ + "type": "File", + "start":0,"end":40,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":25}}, + "program": { + "type": "Program", + "start":0,"end":40,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":25}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":14,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":14}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":13,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":13}}, + "id": { + "type": "Identifier", + "start":6,"end":8,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":8},"identifierName":"as"}, + "name": "as" + }, + "init": { + "type": "ObjectExpression", + "start":11,"end":13,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":13}}, + "properties": [] + } + } + ], + "kind": "const" + }, + { + "type": "ExportNamedDeclaration", + "start":15,"end":40,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":25}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":24,"end":37,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":22}}, + "local": { + "type": "Identifier", + "start":29,"end":31,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":16},"identifierName":"as"}, + "name": "as" + }, + "exported": { + "type": "Identifier", + "start":35,"end":37,"loc":{"start":{"line":2,"column":20},"end":{"line":2,"column":22},"identifierName":"if"}, + "name": "if" + }, + "exportKind": "type" + } + ], + "source": null, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/input.ts new file mode 100644 index 000000000000..caba0b5d2314 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/input.ts @@ -0,0 +1,2 @@ +const if = {}; +export { type if }; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json new file mode 100644 index 000000000000..510f321617d5 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-type-only-keyword/output.json @@ -0,0 +1,58 @@ +{ + "type": "File", + "start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":19}}, + "program": { + "type": "Program", + "start":0,"end":34,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":19}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":14,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":14}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":13,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":13}}, + "id": { + "type": "Identifier", + "start":6,"end":8,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":8},"identifierName":"if"}, + "name": "if" + }, + "init": { + "type": "ObjectExpression", + "start":11,"end":13,"loc":{"start":{"line":1,"column":11},"end":{"line":1,"column":13}}, + "properties": [] + } + } + ], + "kind": "const" + }, + { + "type": "ExportNamedDeclaration", + "start":15,"end":34,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":19}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":24,"end":31,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":16}}, + "local": { + "type": "Identifier", + "start":29,"end":31,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":16},"identifierName":"if"}, + "name": "if" + }, + "exported": { + "type": "Identifier", + "start":29,"end":31,"loc":{"start":{"line":2,"column":14},"end":{"line":2,"column":16},"identifierName":"if"}, + "name": "if" + }, + "exportKind": "type" + } + ], + "source": null, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file From 6b79fd111f81b08d44a08f6188f57ad65aff6f0e Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sun, 17 Oct 2021 01:01:55 +0900 Subject: [PATCH 15/25] Remove import statement if all of specifiers are type-only one --- packages/babel-plugin-transform-typescript/src/index.ts | 2 +- .../test/fixtures/imports/only-remove-type-imports/output.mjs | 1 - .../fixtures/imports/type-only-import-specifier-4/output.mjs | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index 78098192aba2..1c5fcc2d792b 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -288,7 +288,7 @@ export default declare((api, opts) => { } } - if (!onlyRemoveTypeImports && isAllSpecifiersElided()) { + if (isAllSpecifiersElided()) { stmt.remove(); } else { for (const importPath of importsToRemove) { diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs index 169883383851..78a426b356b2 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/only-remove-type-imports/output.mjs @@ -5,6 +5,5 @@ import d, { d2 } from "d"; import e, { e3 as e4 } from "e"; import "f"; import "g"; -import "k"; import { L2 } from "l"; ; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/output.mjs index 856f26b34c30..e69de29bb2d1 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/output.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/imports/type-only-import-specifier-4/output.mjs @@ -1 +0,0 @@ -import "x"; From ebf0a5837fb0f24071a2aaea75dd8f3cdf67b2d7 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 23 Oct 2021 00:56:21 +0900 Subject: [PATCH 16/25] Fix type errors --- .../babel-plugin-transform-typescript/src/index.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-typescript/src/index.ts b/packages/babel-plugin-transform-typescript/src/index.ts index a3b189fe70e7..2975ae471c3c 100644 --- a/packages/babel-plugin-transform-typescript/src/index.ts +++ b/packages/babel-plugin-transform-typescript/src/index.ts @@ -272,7 +272,10 @@ export default declare((api: ConfigAPI, opts: Options): Plugin => { specifiersLength === importsToRemove.size; for (const specifier of stmt.node.specifiers) { - if (specifier.importKind === "type") { + if ( + specifier.type === "ImportSpecifier" && + specifier.importKind === "type" + ) { registerGlobalType(programNode, specifier.local.name); const binding = stmt.scope.getBinding(specifier.local.name); if (binding) { @@ -384,7 +387,11 @@ export default declare((api: ConfigAPI, opts: Options): Plugin => { if ( path.node.source && path.node.specifiers.length > 0 && - path.node.specifiers.every(({ exportKind }) => exportKind === "type") + path.node.specifiers.every( + specifier => + specifier.type === "ExportSpecifier" && + specifier.exportKind === "type", + ) ) { path.remove(); return; From d459c8b35bcdfc33e4db87d5a9154367b5215841 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Tue, 26 Oct 2021 21:20:27 +0900 Subject: [PATCH 17/25] Extract parseExportSpecifier --- packages/babel-parser/src/parser/statement.js | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 0a3d117f40f6..6bf9891fc559 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2167,31 +2167,35 @@ export default class StatementParser extends ExpressionParser { this.expect(tt.comma); if (this.eat(tt.braceR)) break; } - - const node = this.startNode(); - const isMaybeTypeOnly = this.isContextual(tt._type); - const isString = this.match(tt.string); - node.local = this.parseModuleExportName(); - const canParseAsKeyword = this.parseTypeOnlyImportExportSpecifier( - node, - /* isImport */ false, - isString, - isInTypeExport, - isMaybeTypeOnly, - ); - if (canParseAsKeyword && this.eatContextual(tt._as)) { - node.exported = this.parseModuleExportName(); - } else if (isString) { - node.exported = cloneStringLiteral(node.local); - } else if (!node.exported) { - node.exported = cloneIdentifier(node.local); - } - nodes.push(this.finishNode(node, "ExportSpecifier")); + const node = this.parseExportSpecifier(isInTypeExport); + nodes.push(node); } return nodes; } + parseExportSpecifier(isInTypeExport: boolean): N.ExportSpecifier { + const node = this.startNode(); + const isMaybeTypeOnly = this.isContextual(tt._type); + const isString = this.match(tt.string); + node.local = this.parseModuleExportName(); + const canParseAsKeyword = this.parseTypeOnlyImportExportSpecifier( + node, + /* isImport */ false, + isString, + isInTypeExport, + isMaybeTypeOnly, + ); + if (canParseAsKeyword && this.eatContextual(tt._as)) { + node.exported = this.parseModuleExportName(); + } else if (isString) { + node.exported = cloneStringLiteral(node.local); + } else if (!node.exported) { + node.exported = cloneIdentifier(node.local); + } + return this.finishNode(node, "ExportSpecifier"); + } + // https://tc39.es/ecma262/#prod-ModuleExportName parseModuleExportName(): N.StringLiteral | N.Identifier { if (this.match(tt.string)) { From 519f97702e5c1325bd18239b8f2242c987d28fa4 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Thu, 28 Oct 2021 16:55:49 +0900 Subject: [PATCH 18/25] Extract parameters --- packages/babel-parser/src/parser/statement.js | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 6bf9891fc559..bb8361b11562 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2167,18 +2167,29 @@ export default class StatementParser extends ExpressionParser { this.expect(tt.comma); if (this.eat(tt.braceR)) break; } - const node = this.parseExportSpecifier(isInTypeExport); + const isMaybeTypeOnly = this.isContextual(tt._type); + const isString = this.match(tt.string); + const local = this.parseModuleExportName(); + const node = this.parseExportSpecifier( + local, + isString, + isInTypeExport, + isMaybeTypeOnly, + ); nodes.push(node); } return nodes; } - parseExportSpecifier(isInTypeExport: boolean): N.ExportSpecifier { - const node = this.startNode(); - const isMaybeTypeOnly = this.isContextual(tt._type); - const isString = this.match(tt.string); - node.local = this.parseModuleExportName(); + parseExportSpecifier( + local: N.StringLiteral | N.Identifier, + isString: boolean, + isInTypeExport: boolean, + isMaybeTypeOnly: boolean, + ): N.ExportSpecifier { + const node = this.startNodeAtNode(local); + node.local = local; const canParseAsKeyword = this.parseTypeOnlyImportExportSpecifier( node, /* isImport */ false, From cb0f943225d579e17a9358104e8d451d67fd8bd3 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Thu, 28 Oct 2021 17:01:57 +0900 Subject: [PATCH 19/25] Move the logic for error --- .../src/plugins/typescript/index.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 3cac2611bbb2..37bfef5b2df4 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3372,22 +3372,22 @@ export default (superClass: Class): Class => hasTypeSpecifier = true; leftOfAs = this.parseIdentifier(); } + if (hasTypeSpecifier && isInTypeOnlyImportExport) { + this.raise( + pos, + isImport + ? TSErrors.TypeModifierIsUsedInTypeImports + : TSErrors.TypeModifierIsUsedInTypeExports, + ); + } } + node[leftOfAsKey] = leftOfAs; node[rightOfAsKey] = rightOfAs; const kindKey = isImport ? "importKind" : "exportKind"; node[kindKey] = hasTypeSpecifier ? "type" : "value"; - if (hasTypeSpecifier && isInTypeOnlyImportExport) { - this.raise( - pos, - isImport - ? TSErrors.TypeModifierIsUsedInTypeImports - : TSErrors.TypeModifierIsUsedInTypeExports, - ); - } - return canParseAsKeyword; } }; From 91918b728a81e18a210af1f648a07d7cd6e3a50d Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Thu, 28 Oct 2021 17:46:13 +0900 Subject: [PATCH 20/25] Refactor exports --- packages/babel-parser/src/parser/statement.js | 31 +++++++---------- .../src/plugins/typescript/index.js | 34 +++++++++++++++++++ 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index bb8361b11562..1b7cbb58052a 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2169,35 +2169,30 @@ export default class StatementParser extends ExpressionParser { } const isMaybeTypeOnly = this.isContextual(tt._type); const isString = this.match(tt.string); - const local = this.parseModuleExportName(); - const node = this.parseExportSpecifier( - local, - isString, - isInTypeExport, - isMaybeTypeOnly, + const node = this.startNode(); + node.local = this.parseModuleExportName(); + nodes.push( + this.parseExportSpecifier( + node, + isString, + isInTypeExport, + isMaybeTypeOnly, + ), ); - nodes.push(node); } return nodes; } parseExportSpecifier( - local: N.StringLiteral | N.Identifier, + node: any, isString: boolean, + // eslint-disable-next-line no-unused-vars isInTypeExport: boolean, + // eslint-disable-next-line no-unused-vars isMaybeTypeOnly: boolean, ): N.ExportSpecifier { - const node = this.startNodeAtNode(local); - node.local = local; - const canParseAsKeyword = this.parseTypeOnlyImportExportSpecifier( - node, - /* isImport */ false, - isString, - isInTypeExport, - isMaybeTypeOnly, - ); - if (canParseAsKeyword && this.eatContextual(tt._as)) { + if (this.eatContextual(tt._as)) { node.exported = this.parseModuleExportName(); } else if (isString) { node.exported = cloneStringLiteral(node.local); diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 37bfef5b2df4..69df953388e4 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -42,6 +42,7 @@ import { type ErrorTemplate, ErrorCodes, } from "../../parser/error"; +import { cloneIdentifier } from "../../parser/node"; type TsModifier = | "readonly" @@ -3317,6 +3318,31 @@ export default (superClass: Class): Class => return super.getExpression(); } + parseExportSpecifier( + node: any, + isString: boolean, + isInTypeExport: boolean, + isMaybeTypeOnly: boolean, + ) { + if (!isString && isMaybeTypeOnly) { + this.parseTypeOnlyImportExportSpecifier( + node, + /* isImport */ false, + isString, + isInTypeExport, + isMaybeTypeOnly, + ); + return this.finishNode(node, "ExportSpecifier"); + } + node.exportKind = "value"; + return super.parseExportSpecifier( + node, + isString, + isInTypeExport, + isMaybeTypeOnly, + ); + } + parseTypeOnlyImportExportSpecifier( node: any, isImport: boolean, @@ -3388,6 +3414,14 @@ export default (superClass: Class): Class => const kindKey = isImport ? "importKind" : "exportKind"; node[kindKey] = hasTypeSpecifier ? "type" : "value"; + if (!isImport) { + if (canParseAsKeyword && this.eatContextual(tt._as)) { + node[rightOfAsKey] = this.parseModuleExportName(); + } else if (!node[rightOfAsKey]) { + node[rightOfAsKey] = cloneIdentifier(node[leftOfAsKey]); + } + } + return canParseAsKeyword; } }; From d07401030b18e2dc5c7295b3e2b4be3ade10d68a Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 29 Oct 2021 01:43:06 +0900 Subject: [PATCH 21/25] Refactor imports --- packages/babel-parser/src/parser/statement.js | 46 ++++--- .../babel-parser/src/plugins/flow/index.js | 23 ++-- .../src/plugins/typescript/index.js | 113 ++++++++++-------- 3 files changed, 96 insertions(+), 86 deletions(-) diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 1b7cbb58052a..4a52f060a4d6 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2450,36 +2450,30 @@ export default class StatementParser extends ExpressionParser { if (this.eat(tt.braceR)) break; } - this.parseImportSpecifier(node); + const specifier = this.startNode(); + const importedIsString = this.match(tt.string); + const isMaybeTypeOnly = this.isContextual(tt._type); + specifier.imported = this.parseModuleExportName(); + const importSpecifier = this.parseImportSpecifier( + specifier, + importedIsString, + node.importKind === "type" || node.importKind === "typeof", + isMaybeTypeOnly, + ); + node.specifiers.push(importSpecifier); } } - parseTypeOnlyImportExportSpecifier( - /* eslint-disable no-unused-vars -- used in typescript plugin */ - node: any, - isImport: boolean, - isStringSpecifier: boolean, - isInTypeOnlyImportExport: boolean, + // https://tc39.es/ecma262/#prod-ImportSpecifier + parseImportSpecifier( + specifier: any, + importedIsString: boolean, + /* eslint-disable no-unused-vars -- used in TypeScript and Flow parser */ + isInTypeOnlyImport: boolean, isMaybeTypeOnly: boolean, /* eslint-enable no-unused-vars */ - ): boolean { - return true; - } - - // https://tc39.es/ecma262/#prod-ImportSpecifier - parseImportSpecifier(node: N.ImportDeclaration): void { - const specifier = this.startNode(); - const importedIsString = this.match(tt.string); - const isMaybeTypeOnly = this.isContextual(tt._type); - specifier.imported = this.parseModuleExportName(); - const canParseAsKeyword = this.parseTypeOnlyImportExportSpecifier( - specifier, - /* isImport */ true, - importedIsString, - /* isInTypeOnlyImportExport */ node.importKind === "type", - isMaybeTypeOnly, - ); - if (canParseAsKeyword && this.eatContextual(tt._as)) { + ): N.ImportSpecifier { + if (this.eatContextual(tt._as)) { specifier.local = this.parseIdentifier(); } else { const { imported } = specifier; @@ -2496,7 +2490,7 @@ export default class StatementParser extends ExpressionParser { } } this.checkLVal(specifier.local, "import specifier", BIND_LEXICAL); - node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); + return this.finishNode(specifier, "ImportSpecifier"); } // This is used in flow and typescript plugin diff --git a/packages/babel-parser/src/plugins/flow/index.js b/packages/babel-parser/src/plugins/flow/index.js index e7fc375a4bb6..fc915fe1dfe6 100644 --- a/packages/babel-parser/src/plugins/flow/index.js +++ b/packages/babel-parser/src/plugins/flow/index.js @@ -2631,10 +2631,14 @@ export default (superClass: Class): Class => } // parse import-type/typeof shorthand - parseImportSpecifier(node: N.ImportDeclaration): void { - const specifier = this.startNode(); - const firstIdentIsString = this.match(tt.string); - const firstIdent = this.parseModuleExportName(); + parseImportSpecifier( + specifier: any, + importedIsString: boolean, + isInTypeOnlyImport: boolean, + // eslint-disable-next-line no-unused-vars + isMaybeTypeOnly: boolean, + ): N.ImportSpecifier { + const firstIdent = specifier.imported; let specifierTypeKind = null; if (firstIdent.type === "Identifier") { @@ -2671,7 +2675,7 @@ export default (superClass: Class): Class => specifier.imported = this.parseIdentifier(true); specifier.importKind = specifierTypeKind; } else { - if (firstIdentIsString) { + if (importedIsString) { /*:: invariant(firstIdent instanceof N.StringLiteral) */ throw this.raise( specifier.start, @@ -2692,17 +2696,16 @@ export default (superClass: Class): Class => } } - const nodeIsTypeImport = hasTypeImportKind(node); const specifierIsTypeImport = hasTypeImportKind(specifier); - if (nodeIsTypeImport && specifierIsTypeImport) { + if (isInTypeOnlyImport && specifierIsTypeImport) { this.raise( specifier.start, FlowErrors.ImportTypeShorthandOnlyInPureImport, ); } - if (nodeIsTypeImport || specifierIsTypeImport) { + if (isInTypeOnlyImport || specifierIsTypeImport) { this.checkReservedType( specifier.local.name, specifier.local.start, @@ -2710,7 +2713,7 @@ export default (superClass: Class): Class => ); } - if (isBinding && !nodeIsTypeImport && !specifierIsTypeImport) { + if (isBinding && !isInTypeOnlyImport && !specifierIsTypeImport) { this.checkReservedWord( specifier.local.name, specifier.start, @@ -2720,7 +2723,7 @@ export default (superClass: Class): Class => } this.checkLVal(specifier.local, "import specifier", BIND_LEXICAL); - node.specifiers.push(this.finishNode(specifier, "ImportSpecifier")); + return this.finishNode(specifier, "ImportSpecifier"); } parseBindingAtom(): N.Pattern { diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 69df953388e4..626bbd527c53 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3328,9 +3328,7 @@ export default (superClass: Class): Class => this.parseTypeOnlyImportExportSpecifier( node, /* isImport */ false, - isString, isInTypeExport, - isMaybeTypeOnly, ); return this.finishNode(node, "ExportSpecifier"); } @@ -3343,13 +3341,34 @@ export default (superClass: Class): Class => ); } + parseImportSpecifier( + specifier: any, + importedIsString: boolean, + isInTypeOnlyImport: boolean, + isMaybeTypeOnly: boolean, + ): N.ImportSpecifier { + if (!importedIsString && isMaybeTypeOnly) { + this.parseTypeOnlyImportExportSpecifier( + specifier, + /* isImport */ true, + isInTypeOnlyImport, + ); + return this.finishNode(specifier, "ImportSpecifier"); + } + specifier.importKind = "value"; + return super.parseImportSpecifier( + specifier, + importedIsString, + isInTypeOnlyImport, + isMaybeTypeOnly, + ); + } + parseTypeOnlyImportExportSpecifier( node: any, isImport: boolean, - isStringSpecifier: boolean, isInTypeOnlyImportExport: boolean, - isMaybeTypeOnly: boolean, - ): boolean { + ): void { const leftOfAsKey = isImport ? "imported" : "local"; const rightOfAsKey = isImport ? "local" : "exported"; @@ -3362,50 +3381,48 @@ export default (superClass: Class): Class => const pos = leftOfAs.start; // https://github.com/microsoft/TypeScript/blob/fc4f9d83d5939047aa6bb2a43965c6e9bbfbc35b/src/compiler/parser.ts#L7411-L7456 - if (!isStringSpecifier && isMaybeTypeOnly) { - // import { type } from "mod"; - hasTypeSpecifier: false, leftOfAs: type - // import { type as } from "mod"; - hasTypeSpecifier: true, leftOfAs: as - // import { type as as } from "mod"; - hasTypeSpecifier: false, leftOfAs: type, rightOfAs: as - // import { type as as as } from "mod"; - hasTypeSpecifier: true, leftOfAs: as, rightOfAs: as + // import { type } from "mod"; - hasTypeSpecifier: false, leftOfAs: type + // import { type as } from "mod"; - hasTypeSpecifier: true, leftOfAs: as + // import { type as as } from "mod"; - hasTypeSpecifier: false, leftOfAs: type, rightOfAs: as + // import { type as as as } from "mod"; - hasTypeSpecifier: true, leftOfAs: as, rightOfAs: as + if (this.isContextual(tt._as)) { + // { type as ...? } + const firstAs = this.parseIdentifier(); if (this.isContextual(tt._as)) { - // { type as ...? } - const firstAs = this.parseIdentifier(); - if (this.isContextual(tt._as)) { - // { type as as ...? } - const secondAs = this.parseIdentifier(); - if (tokenIsKeywordOrIdentifier(this.state.type)) { - // { type as as something } - hasTypeSpecifier = true; - leftOfAs = firstAs; - rightOfAs = this.parseIdentifier(); - canParseAsKeyword = false; - } else { - // { type as as } - rightOfAs = secondAs; - canParseAsKeyword = false; - } - } else if (tokenIsKeywordOrIdentifier(this.state.type)) { - // { type as something } - canParseAsKeyword = false; - rightOfAs = this.parseIdentifier(); - } else { - // { type as } + // { type as as ...? } + const secondAs = this.parseIdentifier(); + if (tokenIsKeywordOrIdentifier(this.state.type)) { + // { type as as something } hasTypeSpecifier = true; leftOfAs = firstAs; + rightOfAs = this.parseIdentifier(); + canParseAsKeyword = false; + } else { + // { type as as } + rightOfAs = secondAs; + canParseAsKeyword = false; } } else if (tokenIsKeywordOrIdentifier(this.state.type)) { - // { type something ...? } + // { type as something } + canParseAsKeyword = false; + rightOfAs = this.parseIdentifier(); + } else { + // { type as } hasTypeSpecifier = true; - leftOfAs = this.parseIdentifier(); - } - if (hasTypeSpecifier && isInTypeOnlyImportExport) { - this.raise( - pos, - isImport - ? TSErrors.TypeModifierIsUsedInTypeImports - : TSErrors.TypeModifierIsUsedInTypeExports, - ); + leftOfAs = firstAs; } + } else if (tokenIsKeywordOrIdentifier(this.state.type)) { + // { type something ...? } + hasTypeSpecifier = true; + leftOfAs = this.parseIdentifier(); + } + if (hasTypeSpecifier && isInTypeOnlyImportExport) { + this.raise( + pos, + isImport + ? TSErrors.TypeModifierIsUsedInTypeImports + : TSErrors.TypeModifierIsUsedInTypeExports, + ); } node[leftOfAsKey] = leftOfAs; @@ -3414,14 +3431,10 @@ export default (superClass: Class): Class => const kindKey = isImport ? "importKind" : "exportKind"; node[kindKey] = hasTypeSpecifier ? "type" : "value"; - if (!isImport) { - if (canParseAsKeyword && this.eatContextual(tt._as)) { - node[rightOfAsKey] = this.parseModuleExportName(); - } else if (!node[rightOfAsKey]) { - node[rightOfAsKey] = cloneIdentifier(node[leftOfAsKey]); - } + if (canParseAsKeyword && this.eatContextual(tt._as)) { + node[rightOfAsKey] = this.parseModuleExportName(); + } else if (!node[rightOfAsKey]) { + node[rightOfAsKey] = cloneIdentifier(node[leftOfAsKey]); } - - return canParseAsKeyword; } }; From ab1f5c2b6e4d71f991bf17850c27892bbc250bd0 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 29 Oct 2021 01:47:44 +0900 Subject: [PATCH 22/25] Fix lint comments --- packages/babel-parser/src/parser/statement.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index 4a52f060a4d6..bc513b98081d 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -2187,10 +2187,10 @@ export default class StatementParser extends ExpressionParser { parseExportSpecifier( node: any, isString: boolean, - // eslint-disable-next-line no-unused-vars + /* eslint-disable no-unused-vars -- used in TypeScript parser */ isInTypeExport: boolean, - // eslint-disable-next-line no-unused-vars isMaybeTypeOnly: boolean, + /* eslint-enable no-unused-vars */ ): N.ExportSpecifier { if (this.eatContextual(tt._as)) { node.exported = this.parseModuleExportName(); From c44171aadcad7f643a3618c23e14d0bb6a84781d Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 29 Oct 2021 01:51:56 +0900 Subject: [PATCH 23/25] Fix babel-parser/types.js --- packages/babel-parser/src/types.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/babel-parser/src/types.js b/packages/babel-parser/src/types.js index e747abc6b5f5..5362caf18d3e 100644 --- a/packages/babel-parser/src/types.js +++ b/packages/babel-parser/src/types.js @@ -903,6 +903,7 @@ export type ImportDeclaration = NodeBase & { export type ImportSpecifier = ModuleSpecifier & { type: "ImportSpecifier", imported: Identifier | StringLiteral, + importKind?: "type" | "value", }; export type ImportDefaultSpecifier = ModuleSpecifier & { @@ -930,6 +931,7 @@ export type ExportSpecifier = NodeBase & { type: "ExportSpecifier", exported: Identifier | StringLiteral, local: Identifier, + exportKind?: "type" | "value", }; export type ExportDefaultSpecifier = NodeBase & { From bfd0f9ddc50284a0ab90d6459fa5eabb29c1be77 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 29 Oct 2021 02:32:20 +0900 Subject: [PATCH 24/25] Throw error for string local for import --- .../src/plugins/typescript/index.js | 4 +- .../input.ts | 1 + .../output.json | 44 +++++++++++++++++++ .../input.ts | 1 + .../output.json | 44 +++++++++++++++++++ .../input.ts | 1 + .../options.json | 7 +++ 7 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/output.json create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-string/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-string/options.json diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 626bbd527c53..fa499a521cf6 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3432,7 +3432,9 @@ export default (superClass: Class): Class => node[kindKey] = hasTypeSpecifier ? "type" : "value"; if (canParseAsKeyword && this.eatContextual(tt._as)) { - node[rightOfAsKey] = this.parseModuleExportName(); + node[rightOfAsKey] = isImport + ? this.parseIdentifier() + : this.parseModuleExportName(); } else if (!node[rightOfAsKey]) { node[rightOfAsKey] = cloneIdentifier(node[leftOfAsKey]); } diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/input.ts new file mode 100644 index 000000000000..061fadb8439d --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/input.ts @@ -0,0 +1 @@ +import { type as if } from "mod"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/output.json new file mode 100644 index 000000000000..7a37d3e1e7af --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/output.json @@ -0,0 +1,44 @@ +{ + "type": "File", + "start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":33}}, + "program": { + "type": "Program", + "start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":33}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":33,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":33}}, + "importKind": "value", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":9,"end":19,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":19}}, + "imported": { + "type": "Identifier", + "start":9,"end":13,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":13},"identifierName":"type"}, + "name": "type" + }, + "local": { + "type": "Identifier", + "start":17,"end":19,"loc":{"start":{"line":1,"column":17},"end":{"line":1,"column":19},"identifierName":"if"}, + "name": "if" + }, + "importKind": "value" + } + ], + "source": { + "type": "StringLiteral", + "start":27,"end":32,"loc":{"start":{"line":1,"column":27},"end":{"line":1,"column":32}}, + "extra": { + "rawValue": "mod", + "raw": "\"mod\"" + }, + "value": "mod" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/input.ts new file mode 100644 index 000000000000..3ef90464a510 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/input.ts @@ -0,0 +1 @@ +import { type as as if } from "mod"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/output.json new file mode 100644 index 000000000000..8b80e58814ff --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/output.json @@ -0,0 +1,44 @@ +{ + "type": "File", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "program": { + "type": "Program", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":36}}, + "importKind": "value", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":9,"end":22,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":22}}, + "imported": { + "type": "Identifier", + "start":14,"end":16,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":16},"identifierName":"as"}, + "name": "as" + }, + "local": { + "type": "Identifier", + "start":20,"end":22,"loc":{"start":{"line":1,"column":20},"end":{"line":1,"column":22},"identifierName":"if"}, + "name": "if" + }, + "importKind": "type" + } + ], + "source": { + "type": "StringLiteral", + "start":30,"end":35,"loc":{"start":{"line":1,"column":30},"end":{"line":1,"column":35}}, + "extra": { + "rawValue": "mod", + "raw": "\"mod\"" + }, + "value": "mod" + } + } + ], + "directives": [] + } +} \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-string/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-string/input.ts new file mode 100644 index 000000000000..05b291a2093a --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-string/input.ts @@ -0,0 +1 @@ +import { type foo as "bar" } from "mod"; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-string/options.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-string/options.json new file mode 100644 index 000000000000..786efcf1eab4 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-string/options.json @@ -0,0 +1,7 @@ +{ + "sourceType": "module", + "plugins": [ + "typescript" + ], + "throws": "Unexpected token (1:21)" +} \ No newline at end of file From 1f933af93dc4ceba243c5e272586119b02facfc9 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Fri, 29 Oct 2021 02:50:24 +0900 Subject: [PATCH 25/25] check lval --- .../src/plugins/typescript/index.js | 6 +- .../import-type-only-and-export/input.ts | 2 + .../import-type-only-and-export/output.json | 68 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-and-export/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-and-export/output.json diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index fa499a521cf6..b6b789a799b1 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -3435,8 +3435,12 @@ export default (superClass: Class): Class => node[rightOfAsKey] = isImport ? this.parseIdentifier() : this.parseModuleExportName(); - } else if (!node[rightOfAsKey]) { + } + if (!node[rightOfAsKey]) { node[rightOfAsKey] = cloneIdentifier(node[leftOfAsKey]); } + if (isImport) { + this.checkLVal(node[rightOfAsKey], "import specifier", BIND_LEXICAL); + } } }; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-and-export/input.ts b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-and-export/input.ts new file mode 100644 index 000000000000..0b84733b65e0 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-and-export/input.ts @@ -0,0 +1,2 @@ +import { type Foo1 } from "mod"; +export { Foo1 }; diff --git a/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-and-export/output.json b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-and-export/output.json new file mode 100644 index 000000000000..ff0c51d3b3ef --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-type-only-and-export/output.json @@ -0,0 +1,68 @@ +{ + "type": "File", + "start":0,"end":49,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":16}}, + "program": { + "type": "Program", + "start":0,"end":49,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":16}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":32,"loc":{"start":{"line":1,"column":0},"end":{"line":1,"column":32}}, + "importKind": "value", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":9,"end":18,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":18}}, + "imported": { + "type": "Identifier", + "start":14,"end":18,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":18},"identifierName":"Foo1"}, + "name": "Foo1" + }, + "local": { + "type": "Identifier", + "start":14,"end":18,"loc":{"start":{"line":1,"column":14},"end":{"line":1,"column":18},"identifierName":"Foo1"}, + "name": "Foo1" + }, + "importKind": "type" + } + ], + "source": { + "type": "StringLiteral", + "start":26,"end":31,"loc":{"start":{"line":1,"column":26},"end":{"line":1,"column":31}}, + "extra": { + "rawValue": "mod", + "raw": "\"mod\"" + }, + "value": "mod" + } + }, + { + "type": "ExportNamedDeclaration", + "start":33,"end":49,"loc":{"start":{"line":2,"column":0},"end":{"line":2,"column":16}}, + "exportKind": "value", + "specifiers": [ + { + "type": "ExportSpecifier", + "start":42,"end":46,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":13}}, + "local": { + "type": "Identifier", + "start":42,"end":46,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":13},"identifierName":"Foo1"}, + "name": "Foo1" + }, + "exportKind": "value", + "exported": { + "type": "Identifier", + "start":42,"end":46,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":13},"identifierName":"Foo1"}, + "name": "Foo1" + } + } + ], + "source": null, + "declaration": null + } + ], + "directives": [] + } +} \ No newline at end of file