diff --git a/packages/babel-generator/src/generators/modules.ts b/packages/babel-generator/src/generators/modules.ts index 8029c04ae9b9..870319ec5477 100644 --- a/packages/babel-generator/src/generators/modules.ts +++ b/packages/babel-generator/src/generators/modules.ts @@ -248,6 +248,10 @@ export function ImportDeclaration(this: Printer, node: t.ImportDeclaration) { this.noIndentInnerCommentsHere(); this.word("module"); this.space(); + } else if (node.phase) { + this.noIndentInnerCommentsHere(); + this.word(node.phase); + this.space(); } const specifiers = node.specifiers.slice(0); @@ -315,6 +319,10 @@ export function ImportNamespaceSpecifier( export function ImportExpression(this: Printer, node: t.ImportExpression) { this.word("import"); + if (node.phase) { + this.token("."); + this.word(node.phase); + } this.token("("); this.print(node.source, node); if (node.options != null) { diff --git a/packages/babel-generator/test/fixtures/import-phases/options.json b/packages/babel-generator/test/fixtures/import-phases/options.json new file mode 100644 index 000000000000..dd7da3eefb1a --- /dev/null +++ b/packages/babel-generator/test/fixtures/import-phases/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["sourcePhaseImports"], + "parserOpts": { "createImportExpressions": true } +} diff --git a/packages/babel-generator/test/fixtures/import-phases/source-declaration/input.js b/packages/babel-generator/test/fixtures/import-phases/source-declaration/input.js new file mode 100644 index 000000000000..0907982f2885 --- /dev/null +++ b/packages/babel-generator/test/fixtures/import-phases/source-declaration/input.js @@ -0,0 +1 @@ +import source x from "y"; diff --git a/packages/babel-generator/test/fixtures/import-phases/source-declaration/output.js b/packages/babel-generator/test/fixtures/import-phases/source-declaration/output.js new file mode 100644 index 000000000000..b7a8dea61a36 --- /dev/null +++ b/packages/babel-generator/test/fixtures/import-phases/source-declaration/output.js @@ -0,0 +1 @@ +import source x from "y"; \ No newline at end of file diff --git a/packages/babel-generator/test/fixtures/import-phases/source-expression/input.js b/packages/babel-generator/test/fixtures/import-phases/source-expression/input.js new file mode 100644 index 000000000000..1994954871e6 --- /dev/null +++ b/packages/babel-generator/test/fixtures/import-phases/source-expression/input.js @@ -0,0 +1 @@ +import.source("x"); diff --git a/packages/babel-generator/test/fixtures/import-phases/source-expression/output.js b/packages/babel-generator/test/fixtures/import-phases/source-expression/output.js new file mode 100644 index 000000000000..283642fdf663 --- /dev/null +++ b/packages/babel-generator/test/fixtures/import-phases/source-expression/output.js @@ -0,0 +1 @@ +import.source("x"); \ No newline at end of file diff --git a/packages/babel-parser/data/schema.json b/packages/babel-parser/data/schema.json index 25732221a273..ad149875ab72 100644 --- a/packages/babel-parser/data/schema.json +++ b/packages/babel-parser/data/schema.json @@ -192,6 +192,7 @@ "placeholders", "privateIn", "regexpUnicodeSets", + "sourcePhaseImports", "throwExpressions", "topLevelAwait", "typescript", diff --git a/packages/babel-parser/src/parse-error/standard-errors.ts b/packages/babel-parser/src/parse-error/standard-errors.ts index 0d43a53046af..92fe0348a874 100644 --- a/packages/babel-parser/src/parse-error/standard-errors.ts +++ b/packages/babel-parser/src/parse-error/standard-errors.ts @@ -233,6 +233,10 @@ export default { "In non-strict mode code, functions can only be declared at top level or inside a block.", SloppyFunctionAnnexB: "In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement.", + SourcePhaseImportRequiresDefault: + 'Only `import source x from "./module"` is valid.', + SourcePhaseDynamicImportRequiresImportExpressions: + "'import.source(...)' can only be parsed when using the 'createImportExpressions' option.", StaticPrototype: "Classes may not have static property named prototype.", SuperNotAllowed: "`super()` is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class?", diff --git a/packages/babel-parser/src/parser/expression.ts b/packages/babel-parser/src/parser/expression.ts index 0d073646b729..d672fde59b32 100644 --- a/packages/babel-parser/src/parser/expression.ts +++ b/packages/babel-parser/src/parser/expression.ts @@ -1652,7 +1652,10 @@ export default abstract class ExpressionParser extends LValParser { } // https://tc39.es/ecma262/#prod-ImportMeta - parseImportMetaProperty(node: Undone): N.MetaProperty { + parseImportMetaProperty( + this: Parser, + node: Undone, + ): N.MetaProperty | N.ImportExpression { const id = this.createIdentifier( this.startNodeAtNode(node), "import", @@ -1664,9 +1667,20 @@ export default abstract class ExpressionParser extends LValParser { this.raise(Errors.ImportMetaOutsideModule, { at: id }); } this.sawUnambiguousESM = true; + } else if (this.isContextual(tt._source)) { + this.expectPlugin("sourcePhaseImports"); + if (!this.options.createImportExpressions) { + throw this.raise( + Errors.SourcePhaseDynamicImportRequiresImportExpressions, + { at: this.state.startLoc }, + ); + } + this.next(); + (node as Undone).phase = "source"; + return this.parseImportCall(node as Undone); } - return this.parseMetaProperty(node, id, "meta"); + return this.parseMetaProperty(node as Undone, id, "meta"); } parseLiteralAtNode( diff --git a/packages/babel-parser/src/parser/statement.ts b/packages/babel-parser/src/parser/statement.ts index a84981fe5bec..29fd8c2afa47 100644 --- a/packages/babel-parser/src/parser/statement.ts +++ b/packages/babel-parser/src/parser/statement.ts @@ -2870,13 +2870,21 @@ export default abstract class StatementParser extends ExpressionParser { } checkImportReflection(node: Undone) { - if (node.module) { - if ( - node.specifiers.length !== 1 || - node.specifiers[0].type !== "ImportDefaultSpecifier" - ) { + const { specifiers } = node; + const isSingleDefaultBinding = + specifiers.length === 1 && + specifiers[0].type === "ImportDefaultSpecifier"; + + if (node.phase === "source") { + if (!isSingleDefaultBinding) { + this.raise(Errors.SourcePhaseImportRequiresDefault, { + at: specifiers[0].loc.start, + }); + } + } else if (node.module) { + if (!isSingleDefaultBinding) { this.raise(Errors.ImportReflectionNotBinding, { - at: node.specifiers[0].loc.start, + at: specifiers[0].loc.start, }); } if (node.assertions?.length > 0) { @@ -2921,7 +2929,8 @@ export default abstract class StatementParser extends ExpressionParser { } isPotentialImportPhase(isExport: boolean): boolean { - return !isExport && this.isContextual(tt._module); + if (isExport) return false; + return this.isContextual(tt._source) || this.isContextual(tt._module); } applyImportPhase( @@ -2932,20 +2941,28 @@ export default abstract class StatementParser extends ExpressionParser { ): void { if (isExport) { if (!process.env.IS_PUBLISH) { - if (phase === "module") { + if (phase === "module" || phase === "source") { throw new Error( - "Assertion failure: export declarations do not support the 'module' phase.", + `Assertion failure: export declarations do not support the '${phase}' phase.`, ); } } return; } + if (phase === "module") { this.expectPlugin("importReflection", loc); (node as N.ImportDeclaration).module = true; } else if (this.hasPlugin("importReflection")) { (node as N.ImportDeclaration).module = false; } + + if (phase === "source") { + this.expectPlugin("sourcePhaseImports", loc); + (node as N.ImportDeclaration).phase = "source"; + } else if (this.hasPlugin("sourcePhaseImports")) { + (node as N.ImportDeclaration).phase = null; + } } /* diff --git a/packages/babel-parser/src/tokenizer/types.ts b/packages/babel-parser/src/tokenizer/types.ts index 385bab7e71fb..daa10d39e511 100644 --- a/packages/babel-parser/src/tokenizer/types.ts +++ b/packages/babel-parser/src/tokenizer/types.ts @@ -291,6 +291,7 @@ export const tt = { _of: createKeywordLike("of", { startsExpr }), _sent: createKeywordLike("sent", { startsExpr }), _set: createKeywordLike("set", { startsExpr }), + _source: createKeywordLike("source", { startsExpr }), _static: createKeywordLike("static", { startsExpr }), _using: createKeywordLike("using", { startsExpr }), _yield: createKeywordLike("yield", { startsExpr }), diff --git a/packages/babel-parser/src/types.d.ts b/packages/babel-parser/src/types.d.ts index 1c21761bafff..5799756091a1 100644 --- a/packages/babel-parser/src/types.d.ts +++ b/packages/babel-parser/src/types.d.ts @@ -627,6 +627,7 @@ export interface NewExpression extends CallOrNewBase { export interface ImportExpression extends NodeBase { type: "ImportExpression"; source: Expression; + phase?: null | "source"; options: Expression | null; } @@ -927,6 +928,7 @@ export interface ImportDeclaration extends NodeBase { >; source: Literal; importKind?: "type" | "typeof" | "value"; // TODO: Not in spec, + phase?: null | "source"; attributes?: ImportAttribute[]; // @deprecated assertions?: ImportAttribute[]; diff --git a/packages/babel-parser/src/typings.d.ts b/packages/babel-parser/src/typings.d.ts index dbbaf4f76163..126a6c1286d7 100644 --- a/packages/babel-parser/src/typings.d.ts +++ b/packages/babel-parser/src/typings.d.ts @@ -36,6 +36,7 @@ export type Plugin = | "placeholders" | "privateIn" // Enabled by default | "regexpUnicodeSets" // Enabled by default + | "sourcePhaseImports" | "throwExpressions" | "topLevelAwait" | "v8intrinsic" diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/input.js new file mode 100644 index 000000000000..b3ab4945e1d3 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/input.js @@ -0,0 +1 @@ +import source s from "x" with { attr: "val" }; \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/options.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/options.json new file mode 100644 index 000000000000..c4a65d77444d --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/options.json @@ -0,0 +1,4 @@ +{ + "sourceType": "module", + "plugins": ["sourcePhaseImports", "importAttributes"] +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/output.json new file mode 100644 index 000000000000..93232efb0709 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-declaration/output.json @@ -0,0 +1,58 @@ +{ + "type": "File", + "start":0,"end":46,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":46,"index":46}}, + "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}}, + "phase": "source", + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":15,"index":15}}, + "local": { + "type": "Identifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":15,"index":15},"identifierName":"s"}, + "name": "s" + } + } + ], + "source": { + "type": "StringLiteral", + "start":21,"end":24,"loc":{"start":{"line":1,"column":21,"index":21},"end":{"line":1,"column":24,"index":24}}, + "extra": { + "rawValue": "x", + "raw": "\"x\"" + }, + "value": "x" + }, + "attributes": [ + { + "type": "ImportAttribute", + "start":32,"end":43,"loc":{"start":{"line":1,"column":32,"index":32},"end":{"line":1,"column":43,"index":43}}, + "key": { + "type": "Identifier", + "start":32,"end":36,"loc":{"start":{"line":1,"column":32,"index":32},"end":{"line":1,"column":36,"index":36},"identifierName":"attr"}, + "name": "attr" + }, + "value": { + "type": "StringLiteral", + "start":38,"end":43,"loc":{"start":{"line":1,"column":38,"index":38},"end":{"line":1,"column":43,"index":43}}, + "extra": { + "rawValue": "val", + "raw": "\"val\"" + }, + "value": "val" + } + } + ] + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/input.js new file mode 100644 index 000000000000..329aa1d6c1d9 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/input.js @@ -0,0 +1 @@ +import.source("x", { with: { attr: "val" } }); diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/options.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/options.json new file mode 100644 index 000000000000..01b7ae8a55b8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["sourcePhaseImports", "importAttributes"], + "createImportExpressions": true +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/output.json new file mode 100644 index 000000000000..35f3fbdc146e --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/attributes-expression/output.json @@ -0,0 +1,76 @@ +{ + "type": "File", + "start":0,"end":46,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":46,"index":46}}, + "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": "ExpressionStatement", + "start":0,"end":46,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":46,"index":46}}, + "expression": { + "type": "ImportExpression", + "start":0,"end":45,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":45,"index":45}}, + "phase": "source", + "source": { + "type": "StringLiteral", + "start":14,"end":17,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":17,"index":17}}, + "extra": { + "rawValue": "x", + "raw": "\"x\"" + }, + "value": "x" + }, + "options": { + "type": "ObjectExpression", + "start":19,"end":44,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":44,"index":44}}, + "properties": [ + { + "type": "ObjectProperty", + "start":21,"end":42,"loc":{"start":{"line":1,"column":21,"index":21},"end":{"line":1,"column":42,"index":42}}, + "method": false, + "key": { + "type": "Identifier", + "start":21,"end":25,"loc":{"start":{"line":1,"column":21,"index":21},"end":{"line":1,"column":25,"index":25},"identifierName":"with"}, + "name": "with" + }, + "computed": false, + "shorthand": false, + "value": { + "type": "ObjectExpression", + "start":27,"end":42,"loc":{"start":{"line":1,"column":27,"index":27},"end":{"line":1,"column":42,"index":42}}, + "properties": [ + { + "type": "ObjectProperty", + "start":29,"end":40,"loc":{"start":{"line":1,"column":29,"index":29},"end":{"line":1,"column":40,"index":40}}, + "method": false, + "key": { + "type": "Identifier", + "start":29,"end":33,"loc":{"start":{"line":1,"column":29,"index":29},"end":{"line":1,"column":33,"index":33},"identifierName":"attr"}, + "name": "attr" + }, + "computed": false, + "shorthand": false, + "value": { + "type": "StringLiteral", + "start":35,"end":40,"loc":{"start":{"line":1,"column":35,"index":35},"end":{"line":1,"column":40,"index":40}}, + "extra": { + "rawValue": "val", + "raw": "\"val\"" + }, + "value": "val" + } + } + ] + } + } + ] + } + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import-no-createImportExpressions/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import-no-createImportExpressions/input.js new file mode 100644 index 000000000000..80f2a209f3bb --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import-no-createImportExpressions/input.js @@ -0,0 +1 @@ +import.source("foo"); \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import-no-createImportExpressions/options.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import-no-createImportExpressions/options.json new file mode 100644 index 000000000000..82867fc88bbb --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import-no-createImportExpressions/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["sourcePhaseImports"], + "throws": "'import.source(...)' can only be parsed when using the 'createImportExpressions' option. (1:7)" +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/input.js new file mode 100644 index 000000000000..80f2a209f3bb --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/input.js @@ -0,0 +1 @@ +import.source("foo"); \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/options.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/options.json new file mode 100644 index 000000000000..10b0764f64b7 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["sourcePhaseImports"], + "createImportExpressions": true +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/output.json new file mode 100644 index 000000000000..d77b2e5a5e71 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/dynamic-import/output.json @@ -0,0 +1,31 @@ +{ + "type": "File", + "start":0,"end":21,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":21,"index":21}}, + "program": { + "type": "Program", + "start":0,"end":21,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":21,"index":21}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ExpressionStatement", + "start":0,"end":21,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":21,"index":21}}, + "expression": { + "type": "ImportExpression", + "start":0,"end":20,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":20,"index":20}}, + "phase": "source", + "source": { + "type": "StringLiteral", + "start":14,"end":19,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":19,"index":19}}, + "extra": { + "rawValue": "foo", + "raw": "\"foo\"" + }, + "value": "foo" + } + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-default-binding-source/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-default-binding-source/input.js new file mode 100644 index 000000000000..82164c0f91b2 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-default-binding-source/input.js @@ -0,0 +1 @@ +import source from "x"; \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-default-binding-source/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-default-binding-source/output.json new file mode 100644 index 000000000000..64a65c083939 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-default-binding-source/output.json @@ -0,0 +1,38 @@ +{ + "type": "File", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":23,"index":23}}, + "program": { + "type": "Program", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":23,"index":23}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":23,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":23,"index":23}}, + "phase": null, + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":7,"end":13,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":13,"index":13}}, + "local": { + "type": "Identifier", + "start":7,"end":13,"loc":{"start":{"line":1,"column":7,"index":7},"end":{"line":1,"column":13,"index":13},"identifierName":"source"}, + "name": "source" + } + } + ], + "source": { + "type": "StringLiteral", + "start":19,"end":22,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":22,"index":22}}, + "extra": { + "rawValue": "x", + "raw": "\"x\"" + }, + "value": "x" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-from/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-from/input.js new file mode 100644 index 000000000000..a5c6af299fd3 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-from/input.js @@ -0,0 +1 @@ +import source from from "x"; diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-from/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-from/output.json new file mode 100644 index 000000000000..9280e87e471d --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-from/output.json @@ -0,0 +1,38 @@ +{ + "type": "File", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":28,"index":28}}, + "program": { + "type": "Program", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":28,"index":28}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":28,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":28,"index":28}}, + "phase": "source", + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":14,"end":18,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":18,"index":18}}, + "local": { + "type": "Identifier", + "start":14,"end":18,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":18,"index":18},"identifierName":"from"}, + "name": "from" + } + } + ], + "source": { + "type": "StringLiteral", + "start":24,"end":27,"loc":{"start":{"line":1,"column":24,"index":24},"end":{"line":1,"column":27,"index":27}}, + "extra": { + "rawValue": "x", + "raw": "\"x\"" + }, + "value": "x" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-source/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-source/input.js new file mode 100644 index 000000000000..a21321d210f8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-source/input.js @@ -0,0 +1 @@ +import source source from "x"; \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-source/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-source/output.json new file mode 100644 index 000000000000..ca1a5118448f --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source-binding-source/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}}, + "phase": "source", + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":14,"end":20,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":20,"index":20}}, + "local": { + "type": "Identifier", + "start":14,"end":20,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":20,"index":20},"identifierName":"source"}, + "name": "source" + } + } + ], + "source": { + "type": "StringLiteral", + "start":26,"end":29,"loc":{"start":{"line":1,"column":26,"index":26},"end":{"line":1,"column":29,"index":29}}, + "extra": { + "rawValue": "x", + "raw": "\"x\"" + }, + "value": "x" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source/input.js new file mode 100644 index 000000000000..6cfa837a5768 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source/input.js @@ -0,0 +1 @@ +import source x from "x"; diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source/output.json new file mode 100644 index 000000000000..84970bf7a9ad --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/import-source/output.json @@ -0,0 +1,38 @@ +{ + "type": "File", + "start":0,"end":25,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":25,"index":25}}, + "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}}, + "phase": "source", + "specifiers": [ + { + "type": "ImportDefaultSpecifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":15,"index":15}}, + "local": { + "type": "Identifier", + "start":14,"end":15,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":15,"index":15},"identifierName":"x"}, + "name": "x" + } + } + ], + "source": { + "type": "StringLiteral", + "start":21,"end":24,"loc":{"start":{"line":1,"column":21,"index":21},"end":{"line":1,"column":24,"index":24}}, + "extra": { + "rawValue": "x", + "raw": "\"x\"" + }, + "value": "x" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-named/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-named/input.js new file mode 100644 index 000000000000..3a5f9a7c45d8 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-named/input.js @@ -0,0 +1 @@ +import source { x } from "x"; \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-named/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-named/output.json new file mode 100644 index 000000000000..082eaec22323 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-named/output.json @@ -0,0 +1,46 @@ +{ + "type": "File", + "start":0,"end":29,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":29,"index":29}}, + "errors": [ + "SyntaxError: Only `import source x from \"./module\"` is valid. (1:16)" + ], + "program": { + "type": "Program", + "start":0,"end":29,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":29,"index":29}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "ImportDeclaration", + "start":0,"end":29,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":29,"index":29}}, + "phase": "source", + "specifiers": [ + { + "type": "ImportSpecifier", + "start":16,"end":17,"loc":{"start":{"line":1,"column":16,"index":16},"end":{"line":1,"column":17,"index":17}}, + "imported": { + "type": "Identifier", + "start":16,"end":17,"loc":{"start":{"line":1,"column":16,"index":16},"end":{"line":1,"column":17,"index":17},"identifierName":"x"}, + "name": "x" + }, + "local": { + "type": "Identifier", + "start":16,"end":17,"loc":{"start":{"line":1,"column":16,"index":16},"end":{"line":1,"column":17,"index":17},"identifierName":"x"}, + "name": "x" + } + } + ], + "source": { + "type": "StringLiteral", + "start":25,"end":28,"loc":{"start":{"line":1,"column":25,"index":25},"end":{"line":1,"column":28,"index":28}}, + "extra": { + "rawValue": "x", + "raw": "\"x\"" + }, + "value": "x" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-namespace/input.js b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-namespace/input.js new file mode 100644 index 000000000000..b991e826da8d --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-namespace/input.js @@ -0,0 +1 @@ +import source * as x from "x"; \ No newline at end of file diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-namespace/output.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-namespace/output.json new file mode 100644 index 000000000000..296ee9fea1f1 --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/no-namespace/output.json @@ -0,0 +1,41 @@ +{ + "type": "File", + "start":0,"end":30,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":1,"column":30,"index":30}}, + "errors": [ + "SyntaxError: Only `import source x from \"./module\"` is valid. (1:14)" + ], + "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}}, + "phase": "source", + "specifiers": [ + { + "type": "ImportNamespaceSpecifier", + "start":14,"end":20,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":1,"column":20,"index":20}}, + "local": { + "type": "Identifier", + "start":19,"end":20,"loc":{"start":{"line":1,"column":19,"index":19},"end":{"line":1,"column":20,"index":20},"identifierName":"x"}, + "name": "x" + } + } + ], + "source": { + "type": "StringLiteral", + "start":26,"end":29,"loc":{"start":{"line":1,"column":26,"index":26},"end":{"line":1,"column":29,"index":29}}, + "extra": { + "rawValue": "x", + "raw": "\"x\"" + }, + "value": "x" + } + } + ], + "directives": [] + } +} diff --git a/packages/babel-parser/test/fixtures/experimental/source-phase-imports/options.json b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/options.json new file mode 100644 index 000000000000..2d987408711b --- /dev/null +++ b/packages/babel-parser/test/fixtures/experimental/source-phase-imports/options.json @@ -0,0 +1,4 @@ +{ + "sourceType": "module", + "plugins": ["sourcePhaseImports"] +} diff --git a/packages/babel-parser/typings/babel-parser.d.ts b/packages/babel-parser/typings/babel-parser.d.ts index aa021814ce76..54743d7aa382 100644 --- a/packages/babel-parser/typings/babel-parser.d.ts +++ b/packages/babel-parser/typings/babel-parser.d.ts @@ -40,6 +40,7 @@ type Plugin = | "placeholders" | "privateIn" // Enabled by default | "regexpUnicodeSets" // Enabled by default + | "sourcePhaseImports" | "throwExpressions" | "topLevelAwait" | "v8intrinsic" diff --git a/packages/babel-types/src/ast-types/generated/index.ts b/packages/babel-types/src/ast-types/generated/index.ts index 2281fb58abed..e2007191e38c 100644 --- a/packages/babel-types/src/ast-types/generated/index.ts +++ b/packages/babel-types/src/ast-types/generated/index.ts @@ -864,6 +864,7 @@ export interface ImportDeclaration extends BaseNode { attributes?: Array | null; importKind?: "type" | "typeof" | "value" | null; module?: boolean | null; + phase?: "source" | null; } export interface ImportDefaultSpecifier extends BaseNode { @@ -887,6 +888,7 @@ export interface ImportExpression extends BaseNode { type: "ImportExpression"; source: Expression; options?: Expression | null; + phase?: "source" | 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 c6a656c40212..5c249569d3f8 100644 --- a/packages/babel-types/src/definitions/core.ts +++ b/packages/babel-types/src/definitions/core.ts @@ -1743,6 +1743,10 @@ defineType("ImportDeclaration", { optional: true, validate: assertValueType("boolean"), }, + phase: { + default: null, + validate: assertOneOf("source"), + }, specifiers: { validate: chain( assertValueType("array"), @@ -1810,6 +1814,10 @@ defineType("ImportExpression", { visitor: ["source", "options"], aliases: ["Expression"], fields: { + phase: { + default: null, + validate: assertOneOf("source"), + }, source: { validate: assertNodeType("Expression"), },