diff --git a/packages/babel-parser/src/parser/comments.ts b/packages/babel-parser/src/parser/comments.ts index 03e2db053db1..44022899d492 100644 --- a/packages/babel-parser/src/parser/comments.ts +++ b/packages/babel-parser/src/parser/comments.ts @@ -1,7 +1,7 @@ /*:: declare var invariant; */ import BaseParser from "./base"; -import type { Comment, Node } from "../types"; +import type { Comment, Node, Identifier } from "../types"; import * as charCodes from "charcodes"; import type { Undone } from "./node"; @@ -246,6 +246,38 @@ export default class CommentsParser extends BaseParser { } } + /* eslint-disable no-irregular-whitespace */ + /** + * Reset previous node leading comments, assuming that `node` is a + * single-token node. Used in import phase modifiers parsing. We parse + * `module` in `import module foo from ...` as an identifier but may + * reinterpret it into a phase modifier later. In this case the identifier is + * not part of the AST and we should sync the knowledge to commentStacks + * + * For example, when parsing + * ``` + * import /* 1 *​/ module a from "a"; + * ``` + * the comment whitespace `/* 1 *​/` has trailing node Identifier(module). When + * we see that `module` is not a default import binding, we mark `/* 1 *​/` as + * inner comments of the ImportDeclaration. So `/* 1 *​/` should be detached from + * the Identifier node. + * + * @param node the last finished AST node _before_ current token + */ + /* eslint-enable no-irregular-whitespace */ + resetPreviousIdentifierLeadingComments(node: Identifier) { + const { commentStack } = this.state; + const { length } = commentStack; + if (length === 0) return; + + if (commentStack[length - 1].trailingNode === node) { + commentStack[length - 1].trailingNode = null; + } else if (length >= 2 && commentStack[length - 2].trailingNode === node) { + commentStack[length - 2].trailingNode = null; + } + } + /** * Attach a node to the comment whitespaces right before/after * the given range. diff --git a/packages/babel-parser/src/parser/statement.ts b/packages/babel-parser/src/parser/statement.ts index d5c2e0492345..5d760bde35a3 100644 --- a/packages/babel-parser/src/parser/statement.ts +++ b/packages/babel-parser/src/parser/statement.ts @@ -1,6 +1,7 @@ import type * as N from "../types"; import { tokenIsIdentifier, + tokenIsKeywordOrIdentifier, tokenIsLoop, tokenIsTemplate, tt, @@ -2345,9 +2346,13 @@ export default abstract class StatementParser extends ExpressionParser { >, decorators: N.Decorator[] | null, ): N.AnyExport { + const maybeDefaultIdentifier = this.parseMaybeImportPhase( + node, + /* isExport */ true, + ); const hasDefault = this.maybeParseExportDefaultSpecifier( - // @ts-expect-error todo(flow->ts) node, + maybeDefaultIdentifier, ); const parseAfterDefault = !hasDefault || this.eat(tt.comma); const hasStar = @@ -2441,13 +2446,23 @@ export default abstract class StatementParser extends ExpressionParser { return this.eat(tt.star); } - maybeParseExportDefaultSpecifier(node: N.Node): boolean { - if (this.isExportDefaultSpecifier()) { + maybeParseExportDefaultSpecifier( + node: Undone< + | N.ExportDefaultDeclaration + | N.ExportAllDeclaration + | N.ExportNamedDeclaration + >, + maybeDefaultIdentifier: N.Identifier | null, + ): node is Undone { + if (maybeDefaultIdentifier || this.isExportDefaultSpecifier()) { // export defaultObj ... - this.expectPlugin("exportDefaultFrom"); - const specifier = this.startNode(); - specifier.exported = this.parseIdentifier(true); - node.specifiers = [this.finishNode(specifier, "ExportDefaultSpecifier")]; + this.expectPlugin("exportDefaultFrom", maybeDefaultIdentifier?.loc.start); + const id = maybeDefaultIdentifier || this.parseIdentifier(true); + const specifier = this.startNodeAtNode(id); + specifier.exported = id; + (node as Undone).specifiers = [ + this.finishNode(specifier, "ExportDefaultSpecifier"), + ]; return true; } return false; @@ -2918,67 +2933,175 @@ export default abstract class StatementParser extends ExpressionParser { } } - parseMaybeImportReflection(node: Undone) { - let isImportReflection = false; - if (this.isContextual(tt._module)) { - const lookahead = this.lookahead(); - const nextType = lookahead.type; - if (tokenIsIdentifier(nextType)) { - if (nextType !== tt._from) { - // import module x - isImportReflection = true; - } else { - const nextNextTokenFirstChar = this.input.charCodeAt( - this.nextTokenStartSince(lookahead.end), + isPotentialImportPhase(isExport: boolean): boolean { + return !isExport && this.isContextual(tt._module); + } + + applyImportPhase( + node: Undone, + isExport: boolean, + phase: string | null, + loc?: Position, + ): void { + if (isExport) { + if (!process.env.IS_PUBLISH) { + if (phase === "module") { + throw new Error( + "Assertion failure: export declarations do not support the 'module' phase.", ); - if (nextNextTokenFirstChar === charCodes.lowercaseF) { - // import module from from ... - isImportReflection = true; - } } - } else if (nextType !== tt.comma) { - // import module { x } ... - // import module "foo" - // They are invalid, we will continue parsing and throw - // a recoverable error later - isImportReflection = true; - } - } - if (isImportReflection) { - this.expectPlugin("importReflection"); - this.next(); // eat tt._module; - node.module = true; + } + return; + } + if (phase === "module") { + this.expectPlugin("importReflection", loc); + (node as N.ImportDeclaration).module = true; } else if (this.hasPlugin("importReflection")) { - node.module = false; + (node as N.ImportDeclaration).module = false; } } + /* + * Parse `module` in `import module x fro "x"`, disambiguating + * `import module from "x"` and `import module from from "x"`. + * + * This function might return an identifier representing the `module` + * if it eats `module` and then discovers that it was the default import + * binding and not the import reflection. + * + * This function is also used to parse `import type` and `import typeof` + * in the TS and Flow plugins. + * + * Note: the proposal has been updated to use `source` instead of `module`, + * but it has not been implemented yet. + */ + parseMaybeImportPhase( + node: Undone, + isExport: boolean, + ): N.Identifier | null { + if (!this.isPotentialImportPhase(isExport)) { + this.applyImportPhase( + node as Undone, + isExport, + null, + ); + return null; + } + + const phaseIdentifier = this.parseIdentifier(true); + + const { type } = this.state; + const isImportPhase = tokenIsKeywordOrIdentifier(type) + ? // OK: import x from "foo"; + // OK: import from from "foo"; + // NO: import from "foo"; + // NO: import from 'foo'; + // With the module declarations proposals, we will need further disambiguation + // for `import module from from;`. + type !== tt._from || this.lookaheadCharCode() === charCodes.lowercaseF + : // OK: import { x } from "foo"; + // OK: import x from "foo"; + // OK: import * as T from "foo"; + // NO: import from "foo"; + // OK: import "foo"; + // The last one is invalid, we will continue parsing and throw + // an error later + type !== tt.comma; + + if (isImportPhase) { + this.resetPreviousIdentifierLeadingComments(phaseIdentifier); + this.applyImportPhase( + node as Undone, + isExport, + phaseIdentifier.name, + phaseIdentifier.loc.start, + ); + return null; + } else { + this.applyImportPhase( + node as Undone, + isExport, + null, + ); + // `` is a default binding, return it to the main import declaration parser + return phaseIdentifier; + } + } + + isPrecedingIdImportPhase( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + phase: string, + ) { + const { type } = this.state; + return tokenIsIdentifier(type) + ? // OK: import x from "foo"; + // OK: import from from "foo"; + // NO: import from "foo"; + // NO: import from 'foo'; + // With the module declarations proposals, we will need further disambiguation + // for `import module from from;`. + type !== tt._from || this.lookaheadCharCode() === charCodes.lowercaseF + : // OK: import { x } from "foo"; + // OK: import x from "foo"; + // OK: import * as T from "foo"; + // NO: import from "foo"; + // OK: import "foo"; + // The last one is invalid, we will continue parsing and throw + // an error later + type !== tt.comma; + } + // Parses import declaration. // https://tc39.es/ecma262/#prod-ImportDeclaration parseImport(this: Parser, node: Undone): N.AnyImport { - // import '...' - node.specifiers = []; - if (!this.match(tt.string)) { - this.parseMaybeImportReflection(node); - // check if we have a default import like - // import React from "react"; - const hasDefault = this.maybeParseDefaultImportSpecifier(node); - /* we are checking if we do not have a default import, then it is obvious that we need named imports - * import { get } from "axios"; - * but if we do have a default import - * we need to check if we have a comma after that and - * that is where this `|| this.eat` condition comes into play - */ - const parseNext = !hasDefault || this.eat(tt.comma); - // if we do have to parse the next set of specifiers, we first check for star imports - // import React, * from "react"; - const hasStar = parseNext && this.maybeParseStarImportSpecifier(node); - // now we check if we need to parse the next imports - // but only if they are not importing * (everything) - if (parseNext && !hasStar) this.parseNamedImportSpecifiers(node); - this.expectContextual(tt._from); + if (this.match(tt.string)) { + // import '...' + return this.parseImportSourceAndAttributes(node); } + + return this.parseImportSpecifiersAndAfter( + node, + this.parseMaybeImportPhase(node, /* isExport */ false), + ); + } + + parseImportSpecifiersAndAfter( + this: Parser, + node: Undone, + maybeDefaultIdentifier: N.Identifier | null, + ): N.AnyImport { + node.specifiers = []; + + // check if we have a default import like + // import React from "react"; + const hasDefault = this.maybeParseDefaultImportSpecifier( + node, + maybeDefaultIdentifier, + ); + /* we are checking if we do not have a default import, then it is obvious that we need named imports + * import { get } from "axios"; + * but if we do have a default import + * we need to check if we have a comma after that and + * that is where this `|| this.eat` condition comes into play + */ + const parseNext = !hasDefault || this.eat(tt.comma); + // if we do have to parse the next set of specifiers, we first check for star imports + // import React, * from "react"; + const hasStar = parseNext && this.maybeParseStarImportSpecifier(node); + // now we check if we need to parse the next imports + // but only if they are not importing * (everything) + if (parseNext && !hasStar) this.parseNamedImportSpecifiers(node); + this.expectContextual(tt._from); + + return this.parseImportSourceAndAttributes(node); + } + + parseImportSourceAndAttributes( + this: Parser, + node: Undone, + ): N.AnyImport { + node.specifiers ??= []; node.source = this.parseImportSource(); this.maybeParseImportAttributes(node); this.checkImportReflection(node); @@ -2993,11 +3116,6 @@ export default abstract class StatementParser extends ExpressionParser { return this.parseExprAtom() as N.StringLiteral; } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - shouldParseDefaultImport(node: Undone): boolean { - return tokenIsIdentifier(this.state.type); - } - parseImportSpecifierLocal< T extends | N.ImportSpecifier @@ -3177,9 +3295,24 @@ export default abstract class StatementParser extends ExpressionParser { } } - maybeParseDefaultImportSpecifier(node: Undone): boolean { - if (this.shouldParseDefaultImport(node)) { - // import defaultObj, { x, y as z } from '...' + maybeParseDefaultImportSpecifier( + node: Undone, + maybeDefaultIdentifier: N.Identifier | null, + ): boolean { + // import defaultObj, { x, y as z } from '...' + if (maybeDefaultIdentifier) { + const specifier = this.startNodeAtNode( + maybeDefaultIdentifier, + ); + specifier.local = maybeDefaultIdentifier; + node.specifiers.push( + this.finishImportSpecifier(specifier, "ImportDefaultSpecifier"), + ); + return true; + } else if ( + // We allow keywords, and parseImportSpecifierLocal will report a recoverable error + tokenIsKeywordOrIdentifier(this.state.type) + ) { this.parseImportSpecifierLocal( node, this.startNode(), diff --git a/packages/babel-parser/src/plugins/flow/index.ts b/packages/babel-parser/src/plugins/flow/index.ts index 5ff1a7f0d512..c912c0bc0277 100644 --- a/packages/babel-parser/src/plugins/flow/index.ts +++ b/packages/babel-parser/src/plugins/flow/index.ts @@ -247,10 +247,6 @@ function hasTypeImportKind(node: N.Node): boolean { return node.importKind === "type" || node.importKind === "typeof"; } -function isMaybeDefaultImport(type: TokenType): boolean { - return tokenIsKeywordOrIdentifier(type) && type !== tt._from; -} - const exportSuggestions = { const: "declare export var", let: "declare export var", @@ -2230,20 +2226,6 @@ export default (superClass: typeof Parser) => super.assertModuleNodeAllowed(node); } - parseExport( - node: Undone, - decorators: N.Decorator[] | null, - ): N.AnyExport { - const decl = super.parseExport(node, decorators); - if ( - decl.type === "ExportNamedDeclaration" || - decl.type === "ExportAllDeclaration" - ) { - decl.exportKind = decl.exportKind || "value"; - } - return decl; - } - parseExportDeclaration( node: N.ExportNamedDeclaration, ): N.Declaration | undefined | null { @@ -2713,14 +2695,6 @@ export default (superClass: typeof Parser) => return node; } - shouldParseDefaultImport(node: N.ImportDeclaration): boolean { - if (!hasTypeImportKind(node)) { - return super.shouldParseDefaultImport(node); - } - - return isMaybeDefaultImport(this.state.type); - } - checkImportReflection(node: Undone) { super.checkImportReflection(node); if (node.module && node.importKind !== "value") { @@ -2746,37 +2720,35 @@ export default (superClass: typeof Parser) => node.specifiers.push(this.finishImportSpecifier(specifier, type)); } - // parse typeof and type imports - maybeParseDefaultImportSpecifier(node: N.ImportDeclaration): boolean { - node.importKind = "value"; - - let kind = null; - if (this.match(tt._typeof)) { - kind = "typeof" as const; - } else if (this.isContextual(tt._type)) { - kind = "type" as const; + isPotentialImportPhase(isExport: boolean): boolean { + if (super.isPotentialImportPhase(isExport)) return true; + if (this.isContextual(tt._type)) { + if (!isExport) return true; + const ch = this.lookaheadCharCode(); + return ch === charCodes.leftCurlyBrace || ch === charCodes.asterisk; } - if (kind) { - const lh = this.lookahead(); - const { type } = lh; - - // import type * is not allowed - if (kind === "type" && type === tt.star) { - // FIXME: lh.start? - this.unexpected(null, lh.type); - } + return !isExport && this.isContextual(tt._typeof); + } - if ( - isMaybeDefaultImport(type) || - type === tt.braceL || - type === tt.star - ) { - this.next(); - node.importKind = kind; + applyImportPhase( + node: Undone, + isExport: boolean, + phase: string | null, + loc?: Position, + ): void { + super.applyImportPhase(node, isExport, phase, loc); + if (isExport) { + if (!phase && this.match(tt._default)) { + // TODO: Align with our TS AST and always add .exportKind + return; } + (node as N.ExportNamedDeclaration).exportKind = + phase === "type" ? phase : "value"; + } else { + if (phase === "type" && this.match(tt.star)) this.unexpected(); + (node as N.ImportDeclaration).importKind = + phase === "type" || phase === "typeof" ? phase : "value"; } - - return super.maybeParseDefaultImportSpecifier(node); } // parse import-type/typeof shorthand diff --git a/packages/babel-parser/src/plugins/placeholders.ts b/packages/babel-parser/src/plugins/placeholders.ts index c030877fffcd..f349bd32a9d7 100644 --- a/packages/babel-parser/src/plugins/placeholders.ts +++ b/packages/babel-parser/src/plugins/placeholders.ts @@ -302,12 +302,22 @@ export default (superClass: typeof Parser) => return super.isExportDefaultSpecifier(); } - maybeParseExportDefaultSpecifier(node: N.Node): boolean { - if (node.specifiers && node.specifiers.length > 0) { + maybeParseExportDefaultSpecifier( + node: Undone< + | N.ExportDefaultDeclaration + | N.ExportAllDeclaration + | N.ExportNamedDeclaration + >, + maybeDefaultIdentifier: N.Identifier | null, + ): node is Undone { + if ((node as N.ExportNamedDeclaration).specifiers?.length) { // "export %%NAME%%" has already been parsed by #parseExport. return true; } - return super.maybeParseExportDefaultSpecifier(node); + return super.maybeParseExportDefaultSpecifier( + node, + maybeDefaultIdentifier, + ); } checkExport(node: N.ExportNamedDeclaration): void { diff --git a/packages/babel-parser/src/plugins/typescript/index.ts b/packages/babel-parser/src/plugins/typescript/index.ts index 1a47e8c62762..66fe6e360459 100644 --- a/packages/babel-parser/src/plugins/typescript/index.ts +++ b/packages/babel-parser/src/plugins/typescript/index.ts @@ -1971,10 +1971,11 @@ export default (superClass: ClassWithMixin) => tsParseImportEqualsDeclaration( node: Undone, + maybeDefaultIdentifier?: N.Identifier | null, isExport?: boolean, ): N.TsImportEqualsDeclaration { node.isExport = isExport || false; - node.id = this.parseIdentifier(); + node.id = maybeDefaultIdentifier || this.parseIdentifier(); this.checkIdentifier(node.id, BIND_FLAGS_TS_IMPORT); this.expect(tt.eq); const moduleReference = this.tsParseModuleReference(); @@ -2701,41 +2702,70 @@ export default (superClass: ClassWithMixin) => */ checkDuplicateExports() {} + isPotentialImportPhase(isExport: boolean): boolean { + if (super.isPotentialImportPhase(isExport)) return true; + if (this.isContextual(tt._type)) { + const ch = this.lookaheadCharCode(); + return isExport + ? ch === charCodes.leftCurlyBrace || ch === charCodes.asterisk + : ch !== charCodes.equalsTo; + } + return !isExport && this.isContextual(tt._typeof); + } + + applyImportPhase( + node: Undone, + isExport: boolean, + phase: string | null, + loc?: Position, + ): void { + super.applyImportPhase(node, isExport, phase, loc); + if (isExport) { + (node as N.ExportNamedDeclaration).exportKind = + phase === "type" ? "type" : "value"; + } else { + (node as N.ImportDeclaration).importKind = + phase === "type" || phase === "typeof" ? phase : "value"; + } + } + parseImport( node: Undone, ): N.AnyImport { - node.importKind = "value"; + if (this.match(tt.string)) { + node.importKind = "value"; + return super.parseImport(node as Undone); + } + + let importNode; if ( - tokenIsIdentifier(this.state.type) || - this.match(tt.star) || - this.match(tt.braceL) + tokenIsIdentifier(this.state.type) && + this.lookaheadCharCode() === charCodes.equalsTo ) { - let ahead = this.lookahead(); - - if ( - this.isContextual(tt._type) && - // import type, { a } from "b"; - ahead.type !== tt.comma && - // import type from "a"; - ahead.type !== tt._from && - // import type = require("a"); - ahead.type !== tt.eq - ) { - node.importKind = "type"; - this.next(); - ahead = this.lookahead(); - } - - if (tokenIsIdentifier(this.state.type) && ahead.type === tt.eq) { + node.importKind = "value"; + return this.tsParseImportEqualsDeclaration( + node as Undone, + ); + } else if (this.isContextual(tt._type)) { + const maybeDefaultIdentifier = this.parseMaybeImportPhase( + node as Undone, + /* isExport */ false, + ); + if (this.lookaheadCharCode() === charCodes.equalsTo) { return this.tsParseImportEqualsDeclaration( node as Undone, + maybeDefaultIdentifier, + ); + } else { + importNode = super.parseImportSpecifiersAndAfter( + node as Undone, + maybeDefaultIdentifier, ); } + } else { + importNode = super.parseImport(node as Undone); } - const importNode = super.parseImport(node as Undone); - /*:: invariant(importNode.type !== "TSImportEqualsDeclaration") */ - // `import type` can only be used on imports with named imports or with a // default import - but not both if ( @@ -2760,17 +2790,22 @@ export default (superClass: ClassWithMixin) => if (this.match(tt._import)) { // `export import A = B;` this.next(); // eat `tt._import` + let maybeDefaultIdentifier: N.Identifier | null = null; if ( this.isContextual(tt._type) && - this.lookaheadCharCode() !== charCodes.equalsTo + // We pass false here, because we are parsing an `import ... =` + this.isPotentialImportPhase(/* isExport */ false) ) { - node.importKind = "type"; - this.next(); // eat "type" + maybeDefaultIdentifier = this.parseMaybeImportPhase( + node as Undone, + /* isExport */ false, + ); } else { node.importKind = "value"; } return this.tsParseImportEqualsDeclaration( node as Undone, + maybeDefaultIdentifier, /* isExport */ true, ); } else if (this.eat(tt.eq)) { @@ -2788,16 +2823,6 @@ export default (superClass: ClassWithMixin) => this.semicolon(); return this.finishNode(decl, "TSNamespaceExportDeclaration"); } else { - node.exportKind = "value"; - - if (this.isContextual(tt._type)) { - const ch = this.lookaheadCharCode(); - if (ch === charCodes.leftCurlyBrace || ch === charCodes.asterisk) { - this.next(); - node.exportKind = "type"; - } - } - return super.parseExport( node as Undone, decorators, diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/96/options.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/96/options.json index 9b76c337e1c3..2104ca43283f 100644 --- a/packages/babel-parser/test/fixtures/es2015/uncategorised/96/options.json +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/96/options.json @@ -1,4 +1,3 @@ { - "sourceType": "module", - "throws": "Unexpected token, expected \"{\" (1:7)" + "sourceType": "module" } diff --git a/packages/babel-parser/test/fixtures/es2015/uncategorised/96/output.json b/packages/babel-parser/test/fixtures/es2015/uncategorised/96/output.json new file mode 100644 index 000000000000..494c6112d903 --- /dev/null +++ b/packages/babel-parser/test/fixtures/es2015/uncategorised/96/output.json @@ -0,0 +1,40 @@ +{ + "type": "File", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}}, + "errors": [ + "SyntaxError: Unexpected keyword 'default'. (1:7)" + ], + "program": { + "type": "Program", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}}, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":7,"end":14,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":14,"index":14}}, + "local": { + "type": "Identifier", + "start":7,"end":14,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":14,"index":14},"identifierName":"default"}, + "name": "default" + } + } + ], + "source": { + "type": "StringLiteral", + "start":20,"end":25,"loc":{"start":{"line":1,"column":20,"index":20},"end":{"line":1,"column":25,"index":25}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default/options.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default/options.json deleted file mode 100644 index f2ecbd9741e7..000000000000 --- a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default/options.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "throws": "Unexpected token, expected \"{\" (1:7)" -} diff --git a/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default/output.json b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default/output.json new file mode 100644 index 000000000000..494c6112d903 --- /dev/null +++ b/packages/babel-parser/test/fixtures/esprima/es2015-import-declaration/invalid-import-default/output.json @@ -0,0 +1,40 @@ +{ + "type": "File", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}}, + "errors": [ + "SyntaxError: Unexpected keyword 'default'. (1:7)" + ], + "program": { + "type": "Program", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}}, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":7,"end":14,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":14,"index":14}}, + "local": { + "type": "Identifier", + "start":7,"end":14,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":14,"index":14},"identifierName":"default"}, + "name": "default" + } + } + ], + "source": { + "type": "StringLiteral", + "start":20,"end":25,"loc":{"start":{"line":1,"column":20,"index":20},"end":{"line":1,"column":25,"index":25}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/import-reflection/comments/input.mjs b/packages/babel-parser/test/fixtures/experimental/import-reflection/comments/input.mjs new file mode 100644 index 000000000000..9726c2258813 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-reflection/comments/input.mjs @@ -0,0 +1 @@ +/* 0 */ import /* 1 */ module /* 2 */ Foo /* 3 */ from /* 4 */ "foo" /* 5 */; \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/import-reflection/comments/output.json b/packages/babel-parser/test/fixtures/experimental/import-reflection/comments/output.json new file mode 100644 index 000000000000..d96d9c5124d9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/import-reflection/comments/output.json @@ -0,0 +1,112 @@ +{ + "type": "File", + "start":0,"end":77,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":77,"index":77}}, + "program": { + "type": "Program", + "start":0,"end":77,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":77,"index":77}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":8,"end":77,"loc":{"start":{"line":1,"column":8,"index":8},"end":{"line":1,"column":77,"index":77}}, + "module": true, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":38,"end":41,"loc":{"start":{"line":1,"column":38,"index":38},"end":{"line":1,"column":41,"index":41}}, + "local": { + "type": "Identifier", + "start":38,"end":41,"loc":{"start":{"line":1,"column":38,"index":38},"end":{"line":1,"column":41,"index":41},"identifierName":"Foo"}, + "name": "Foo" + }, + "trailingComments": [ + { + "type": "CommentBlock", + "value": " 3 ", + "start":42,"end":49,"loc":{"start":{"line":1,"column":42,"index":42},"end":{"line":1,"column":49,"index":49}} + } + ], + "leadingComments": [ + { + "type": "CommentBlock", + "value": " 2 ", + "start":30,"end":37,"loc":{"start":{"line":1,"column":30,"index":30},"end":{"line":1,"column":37,"index":37}} + } + ] + } + ], + "source": { + "type": "StringLiteral", + "start":63,"end":68,"loc":{"start":{"line":1,"column":63,"index":63},"end":{"line":1,"column":68,"index":68}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo", + "trailingComments": [ + { + "type": "CommentBlock", + "value": " 5 ", + "start":69,"end":76,"loc":{"start":{"line":1,"column":69,"index":69},"end":{"line":1,"column":76,"index":76}} + } + ], + "leadingComments": [ + { + "type": "CommentBlock", + "value": " 4 ", + "start":55,"end":62,"loc":{"start":{"line":1,"column":55,"index":55},"end":{"line":1,"column":62,"index":62}} + } + ] + }, + "innerComments": [ + { + "type": "CommentBlock", + "value": " 1 ", + "start":15,"end":22,"loc":{"start":{"line":1,"column":15,"index":15},"end":{"line":1,"column":22,"index":22}} + } + ], + "leadingComments": [ + { + "type": "CommentBlock", + "value": " 0 ", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}} + } + ] + } + ], + "directives": [] + }, + "comments": [ + { + "type": "CommentBlock", + "value": " 0 ", + "start":0,"end":7,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":7,"index":7}} + }, + { + "type": "CommentBlock", + "value": " 1 ", + "start":15,"end":22,"loc":{"start":{"line":1,"column":15,"index":15},"end":{"line":1,"column":22,"index":22}} + }, + { + "type": "CommentBlock", + "value": " 2 ", + "start":30,"end":37,"loc":{"start":{"line":1,"column":30,"index":30},"end":{"line":1,"column":37,"index":37}} + }, + { + "type": "CommentBlock", + "value": " 3 ", + "start":42,"end":49,"loc":{"start":{"line":1,"column":42,"index":42},"end":{"line":1,"column":49,"index":49}} + }, + { + "type": "CommentBlock", + "value": " 4 ", + "start":55,"end":62,"loc":{"start":{"line":1,"column":55,"index":55},"end":{"line":1,"column":62,"index":62}} + }, + { + "type": "CommentBlock", + "value": " 5 ", + "start":69,"end":76,"loc":{"start":{"line":1,"column":69,"index":69},"end":{"line":1,"column":76,"index":76}} + } + ] +} diff --git a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-type-import-2/options.json b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-type-import-2/options.json index 2dd1ff29f752..2360f761dd93 100644 --- a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-type-import-2/options.json +++ b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-type-import-2/options.json @@ -1,4 +1,8 @@ { - "plugins": ["importReflection", "flow"], - "sourceType": "module" + "plugins": [ + "importReflection", + "flow" + ], + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:19)" } diff --git a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-type-import-2/output.json b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-type-import-2/output.json deleted file mode 100644 index 1e45993a703d..000000000000 --- a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-type-import-2/output.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "type": "File", - "start":0,"end":44,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":44,"index":44}}, - "errors": [ - "SyntaxError: An `import module` declaration can not use `type` or `typeof` keyword. (1:19)" - ], - "program": { - "type": "Program", - "start":0,"end":44,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":44,"index":44}}, - "sourceType": "module", - "interpreter": null, - "body": [ - { - "type": "ImportDeclaration", - "start":0,"end":44,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":44,"index":44}}, - "specifiers": [ - { - "type": "ImportDefaultSpecifier", - "start":19,"end":22,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":22,"index":22}}, - "local": { - "type": "Identifier", - "start":19,"end":22,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":22,"index":22},"identifierName":"foo"}, - "name": "foo" - } - } - ], - "module": true, - "importKind": "type", - "source": { - "type": "StringLiteral", - "start":28,"end":43,"loc":{"start":{"line":1,"column":28,"index":28},"end":{"line":1,"column":43,"index":43}}, - "extra": { - "rawValue": "./module.wasm", - "raw": "\"./module.wasm\"" - }, - "value": "./module.wasm" - } - } - ], - "directives": [] - } -} diff --git a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-typeof-import-2/options.json b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-typeof-import-2/options.json index 2dd1ff29f752..bfd6a7a73ddd 100644 --- a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-typeof-import-2/options.json +++ b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-typeof-import-2/options.json @@ -1,4 +1,8 @@ { - "plugins": ["importReflection", "flow"], - "sourceType": "module" + "plugins": [ + "importReflection", + "flow" + ], + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:21)" } diff --git a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-typeof-import-2/output.json b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-typeof-import-2/output.json deleted file mode 100644 index fffe18921787..000000000000 --- a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-flow-typeof-import-2/output.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "type": "File", - "start":0,"end":46,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":46,"index":46}}, - "errors": [ - "SyntaxError: An `import module` declaration can not use `type` or `typeof` keyword. (1:21)" - ], - "program": { - "type": "Program", - "start":0,"end":46,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":46,"index":46}}, - "sourceType": "module", - "interpreter": null, - "body": [ - { - "type": "ImportDeclaration", - "start":0,"end":46,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":46,"index":46}}, - "specifiers": [ - { - "type": "ImportDefaultSpecifier", - "start":21,"end":24,"loc":{"start":{"line":1,"column":21,"index":21},"end":{"line":1,"column":24,"index":24}}, - "local": { - "type": "Identifier", - "start":21,"end":24,"loc":{"start":{"line":1,"column":21,"index":21},"end":{"line":1,"column":24,"index":24},"identifierName":"foo"}, - "name": "foo" - } - } - ], - "module": true, - "importKind": "typeof", - "source": { - "type": "StringLiteral", - "start":30,"end":45,"loc":{"start":{"line":1,"column":30,"index":30},"end":{"line":1,"column":45,"index":45}}, - "extra": { - "rawValue": "./module.wasm", - "raw": "\"./module.wasm\"" - }, - "value": "./module.wasm" - } - } - ], - "directives": [] - } -} diff --git a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-ts-type-import-1/options.json b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-ts-type-import-1/options.json index 43d760edb3d7..d2768d790303 100644 --- a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-ts-type-import-1/options.json +++ b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-ts-type-import-1/options.json @@ -1,4 +1,8 @@ { - "plugins": ["importReflection", "typescript"], - "sourceType": "module" + "plugins": [ + "importReflection", + "typescript" + ], + "sourceType": "module", + "throws": "Unexpected token, expected \"from\" (1:19)" } diff --git a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-ts-type-import-1/output.json b/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-ts-type-import-1/output.json deleted file mode 100644 index c443420a3c87..000000000000 --- a/packages/babel-parser/test/fixtures/experimental/import-reflection/invalid-ts-type-import-1/output.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "type": "File", - "start":0,"end":44,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":44,"index":44}}, - "errors": [ - "SyntaxError: An `import module` declaration can not use `type` modifier (1:19)" - ], - "program": { - "type": "Program", - "start":0,"end":44,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":44,"index":44}}, - "sourceType": "module", - "interpreter": null, - "body": [ - { - "type": "ImportDeclaration", - "start":0,"end":44,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":44,"index":44}}, - "importKind": "type", - "specifiers": [ - { - "type": "ImportDefaultSpecifier", - "start":19,"end":22,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":22,"index":22}}, - "local": { - "type": "Identifier", - "start":19,"end":22,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":22,"index":22},"identifierName":"foo"}, - "name": "foo" - } - } - ], - "module": true, - "source": { - "type": "StringLiteral", - "start":28,"end":43,"loc":{"start":{"line":1,"column":28,"index":28},"end":{"line":1,"column":43,"index":43}}, - "extra": { - "rawValue": "./module.wasm", - "raw": "\"./module.wasm\"" - }, - "value": "./module.wasm" - } - } - ], - "directives": [] - } -} diff --git a/packages/babel-parser/test/fixtures/flow/imports/import-type-keyword/input.js b/packages/babel-parser/test/fixtures/flow/imports/import-type-keyword/input.js new file mode 100644 index 000000000000..0da6d5f06a16 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/imports/import-type-keyword/input.js @@ -0,0 +1 @@ +import type switch from 'foo'; diff --git a/packages/babel-parser/test/fixtures/flow/imports/import-type-keyword/output.json b/packages/babel-parser/test/fixtures/flow/imports/import-type-keyword/output.json new file mode 100644 index 000000000000..17b2d427b043 --- /dev/null +++ b/packages/babel-parser/test/fixtures/flow/imports/import-type-keyword/output.json @@ -0,0 +1,38 @@ +{ + "type": "File", + "start":0,"end":30,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":30,"index":30}}, + "program": { + "type": "Program", + "start":0,"end":30,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":30,"index":30}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":30,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":30,"index":30}}, + "importKind": "type", + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":12,"end":18,"loc":{"start":{"line":1,"column":12,"index":12},"end":{"line":1,"column":18,"index":18}}, + "local": { + "type": "Identifier", + "start":12,"end":18,"loc":{"start":{"line":1,"column":12,"index":12},"end":{"line":1,"column":18,"index":18},"identifierName":"switch"}, + "name": "switch" + } + } + ], + "source": { + "type": "StringLiteral", + "start":24,"end":29,"loc":{"start":{"line":1,"column":24,"index":24},"end":{"line":1,"column":29,"index":29}}, + "extra": { + "rawValue": "foo", + "raw": "'foo'" + }, + "value": "foo" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/flow/type-imports/invalid-import-type-namespace/options.json b/packages/babel-parser/test/fixtures/flow/type-imports/invalid-import-type-namespace/options.json index ac0cbbd262eb..a740d0c03e4f 100644 --- a/packages/babel-parser/test/fixtures/flow/type-imports/invalid-import-type-namespace/options.json +++ b/packages/babel-parser/test/fixtures/flow/type-imports/invalid-import-type-namespace/options.json @@ -4,5 +4,5 @@ "jsx", "flow" ], - "throws": "Unexpected token, expected \"*\" (1:7)" -} \ No newline at end of file + "throws": "Unexpected token (1:12)" +}