Skip to content

Commit

Permalink
Add createImportExpressions parser option (#15682)
Browse files Browse the repository at this point in the history
Co-authored-by: Nicol貌 Ribaudo <hello@nicr.dev>
Co-authored-by: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com>
  • Loading branch information
3 people committed Sep 25, 2023
1 parent b06dab5 commit 0f333da
Show file tree
Hide file tree
Showing 260 changed files with 3,990 additions and 393 deletions.
12 changes: 12 additions & 0 deletions packages/babel-generator/src/generators/modules.ts
Expand Up @@ -312,3 +312,15 @@ export function ImportNamespaceSpecifier(
this.space();
this.print(node.local, node);
}

export function ImportExpression(this: Printer, node: t.ImportExpression) {
this.word("import");
this.token("(");
this.print(node.source, node);
if (node.options != null) {
this.token(",");
this.space();
this.print(node.options, node);
}
this.token(")");
}
@@ -0,0 +1 @@
/*0*/import/*1*/(/*2*/"foo"/*3*/)/*4*/
@@ -0,0 +1,5 @@
{
"parserOpts": {
"createImportExpressions": true
}
}
@@ -0,0 +1 @@
/*0*/import /*1*/( /*2*/"foo" /*3*/); /*4*/
@@ -0,0 +1 @@
import("module.js");
@@ -0,0 +1 @@
{ "parserOpts": { "createImportExpressions": false } }
@@ -0,0 +1 @@
import("module.js");
@@ -1 +1 @@
{ "plugins": ["dynamicImport"] }
{ "parserOpts": { "createImportExpressions": true } }
@@ -0,0 +1 @@
import("foo.json", { with: { type: "json" } });
@@ -0,0 +1,5 @@
{
"plugins": [["importAttributes", { "importAttributesKeyword": "with" }]],
"sourceType": "module",
"parserOpts": { "createImportExpressions": false }
}
@@ -0,0 +1,5 @@
import("foo.json", {
with: {
type: "json"
}
});
@@ -0,0 +1 @@
import("foo.json", { with: { type: "json" } });
@@ -0,0 +1,5 @@
{
"plugins": [["importAttributes", { "importAttributesKeyword": "with" }]],
"sourceType": "module",
"parserOpts": { "createImportExpressions": true }
}
@@ -0,0 +1,5 @@
import("foo.json", {
with: {
type: "json"
}
});
4 changes: 2 additions & 2 deletions packages/babel-helper-module-transforms/src/dynamic-import.ts
Expand Up @@ -17,12 +17,12 @@ if (!process.env.BABEL_8_BREAKING && !USE_ESM && !IS_STANDALONE) {
}

export function buildDynamicImport(
node: t.CallExpression,
node: t.CallExpression | t.ImportExpression,
deferToThen: boolean,
wrapWithPromise: boolean,
builder: (specifier: t.Expression) => t.Expression,
): t.Expression {
const [specifier] = node.arguments;
const specifier = t.isCallExpression(node) ? node.arguments[0] : node.source;

if (
t.isStringLiteral(specifier) ||
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-helper-module-transforms/src/index.ts
Expand Up @@ -165,7 +165,7 @@ export function ensureStatementsHoisted(statements: t.Statement[]) {
* wrap it in a call to the interop helpers based on the type.
*/
export function wrapInterop(
programPath: NodePath,
programPath: NodePath<t.Program>,
expr: t.Expression,
type: InteropType,
): t.CallExpression {
Expand Down
5 changes: 5 additions & 0 deletions packages/babel-parser/data/schema.json
Expand Up @@ -68,6 +68,11 @@
"description": "By default, exported identifiers must refer to a declared variable.\nSet this to true to allow export statements to reference undeclared variables.",
"type": "boolean"
},
"createImportExpressions": {
"description": "By default, `import(foo)` is parsed as `CallExpression(Import, [Identifier(foo)])`.\nSet this to true to parse it as an `ImportExpression` node.",
"type": "boolean",
"default": false
},
"createParenthesizedExpressions": {
"description": "By default, the parser adds information about parentheses by setting\n`extra.parenthesized` to `true` as needed.\nWhen this option is `true` the parser creates `ParenthesizedExpression`\nAST nodes instead of using the `extra` property.",
"type": "boolean"
Expand Down
4 changes: 4 additions & 0 deletions packages/babel-parser/src/options.ts
Expand Up @@ -20,6 +20,7 @@ export type Options = {
strictMode: boolean | undefined | null;
ranges: boolean;
tokens: boolean;
createImportExpressions: boolean;
createParenthesizedExpressions: boolean;
errorRecovery: boolean;
attachComment: boolean;
Expand Down Expand Up @@ -68,6 +69,9 @@ export const defaultOptions: Options = {
ranges: false,
// Adds all parsed tokens to a `tokens` property on the `File` node
tokens: false,
// Whether to create ImportExpression AST nodes (if false
// `import(foo)` will be parsed as CallExpression(Import, [Identifier(foo)])
createImportExpressions: false,
// Whether to create ParenthesizedExpression AST nodes (if false
// the parser sets extra.parenthesized on the expression nodes instead).
createParenthesizedExpressions: false,
Expand Down
48 changes: 42 additions & 6 deletions packages/babel-parser/src/parser/expression.ts
Expand Up @@ -1110,19 +1110,26 @@ export default abstract class ExpressionParser extends LValParser {
return this.parseSuper();

case tt._import:
node = this.startNode<N.MetaProperty | N.Import>();
node = this.startNode<N.MetaProperty | N.Import | N.ImportExpression>();
this.next();

if (this.match(tt.dot)) {
return this.parseImportMetaProperty(node as Undone<N.MetaProperty>);
}

if (!this.match(tt.parenL)) {
if (this.match(tt.parenL)) {
if (this.options.createImportExpressions) {
return this.parseImportCall(node as Undone<N.ImportExpression>);
} else {
return this.finishNode(node, "Import");
}
} else {
this.raise(Errors.UnsupportedImport, {
at: this.state.lastTokStartLoc,
});
return this.finishNode(node, "Import");
}
return this.finishNode(node, "Import");

case tt._this:
node = this.startNode();
this.next();
Expand Down Expand Up @@ -1920,9 +1927,14 @@ export default abstract class ExpressionParser extends LValParser {
}

parseNewCallee(this: Parser, node: Undone<N.NewExpression>): void {
node.callee = this.parseNoCallExpr();
if (node.callee.type === "Import") {
this.raise(Errors.ImportCallNotNewExpression, { at: node.callee });
const isImport = this.match(tt._import);
const callee = this.parseNoCallExpr();
node.callee = callee;
if (
isImport &&
(callee.type === "Import" || callee.type === "ImportExpression")
) {
this.raise(Errors.ImportCallNotNewExpression, { at: callee });
}
}

Expand Down Expand Up @@ -2939,6 +2951,30 @@ export default abstract class ExpressionParser extends LValParser {
return this.finishNode(node, "YieldExpression");
}

// https://tc39.es/ecma262/#prod-ImportCall
parseImportCall(
this: Parser,
node: Undone<N.ImportExpression>,
): N.ImportExpression {
this.next(); // eat tt.parenL
node.source = this.parseMaybeAssignAllowIn();
if (
this.hasPlugin("importAttributes") ||
this.hasPlugin("importAssertions")
) {
node.options = null;
}
if (this.eat(tt.comma)) {
this.expectImportAttributesPlugin();
if (!this.match(tt.parenR)) {
node.options = this.parseMaybeAssignAllowIn();
this.eat(tt.comma);
}
}
this.expect(tt.parenR);
return this.finishNode(node, "ImportExpression");
}

// Validates a pipeline (for any of the pipeline Babylon plugins) at the point
// of the infix operator `|>`.

Expand Down
3 changes: 3 additions & 0 deletions packages/babel-parser/src/plugins/estree.ts
Expand Up @@ -424,6 +424,9 @@ export default (superClass: typeof Parser) =>
this.hasPlugin("importAttributes") ||
this.hasPlugin("importAssertions")
) {
(node as N.Node as N.EstreeImportExpression).options =
node.arguments[1] ?? null;
// compatibility with previous ESTree AST
(node as N.Node as N.EstreeImportExpression).attributes =
node.arguments[1] ?? null;
}
Expand Down
10 changes: 10 additions & 0 deletions packages/babel-parser/src/types.d.ts
Expand Up @@ -624,6 +624,12 @@ export interface NewExpression extends CallOrNewBase {
optional?: boolean; // TODO: Not in spec
}

export interface ImportExpression extends NodeBase {
type: "ImportExpression";
source: Expression;
options: Expression | null;
}

export interface SequenceExpression extends NodeBase {
type: "SequenceExpression";
expressions: Expression[];
Expand Down Expand Up @@ -1213,6 +1219,10 @@ export interface EstreeMethodDefinition extends NodeBase {
export interface EstreeImportExpression extends NodeBase {
type: "ImportExpression";
source: Expression;
options?: Expression | null;
/**
* @deprecated Use options instead
*/
attributes?: Expression | null;
}

Expand Down
@@ -0,0 +1,3 @@
function failsParse() {
return import.then();
}
@@ -0,0 +1,58 @@
{
"type": "File",
"start":0,"end":49,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":49}},
"errors": [
"SyntaxError: The only valid meta property for import is import.meta. (2:16)"
],
"program": {
"type": "Program",
"start":0,"end":49,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":49}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "FunctionDeclaration",
"start":0,"end":49,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":49}},
"id": {
"type": "Identifier",
"start":9,"end":19,"loc":{"start":{"line":1,"column":9,"index":9},"end":{"line":1,"column":19,"index":19},"identifierName":"failsParse"},
"name": "failsParse"
},
"generator": false,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":22,"end":49,"loc":{"start":{"line":1,"column":22,"index":22},"end":{"line":3,"column":1,"index":49}},
"body": [
{
"type": "ReturnStatement",
"start":26,"end":47,"loc":{"start":{"line":2,"column":2,"index":26},"end":{"line":2,"column":23,"index":47}},
"argument": {
"type": "CallExpression",
"start":33,"end":46,"loc":{"start":{"line":2,"column":9,"index":33},"end":{"line":2,"column":22,"index":46}},
"callee": {
"type": "MetaProperty",
"start":33,"end":44,"loc":{"start":{"line":2,"column":9,"index":33},"end":{"line":2,"column":20,"index":44}},
"meta": {
"type": "Identifier",
"start":33,"end":39,"loc":{"start":{"line":2,"column":9,"index":33},"end":{"line":2,"column":15,"index":39},"identifierName":"import"},
"name": "import"
},
"property": {
"type": "Identifier",
"start":40,"end":44,"loc":{"start":{"line":2,"column":16,"index":40},"end":{"line":2,"column":20,"index":44},"identifierName":"then"},
"name": "then"
}
},
"arguments": []
}
}
],
"directives": []
}
}
],
"directives": []
}
}
@@ -0,0 +1,3 @@
function* a() {
yield import('http');
}
@@ -0,0 +1,60 @@
{
"type": "File",
"start":0,"end":41,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":41}},
"program": {
"type": "Program",
"start":0,"end":41,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":41}},
"sourceType": "script",
"interpreter": null,
"body": [
{
"type": "FunctionDeclaration",
"start":0,"end":41,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":41}},
"id": {
"type": "Identifier",
"start":10,"end":11,"loc":{"start":{"line":1,"column":10,"index":10},"end":{"line":1,"column":11,"index":11},"identifierName":"a"},
"name": "a"
},
"generator": true,
"async": false,
"params": [],
"body": {
"type": "BlockStatement",
"start":14,"end":41,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":3,"column":1,"index":41}},
"body": [
{
"type": "ExpressionStatement",
"start":18,"end":39,"loc":{"start":{"line":2,"column":2,"index":18},"end":{"line":2,"column":23,"index":39}},
"expression": {
"type": "YieldExpression",
"start":18,"end":38,"loc":{"start":{"line":2,"column":2,"index":18},"end":{"line":2,"column":22,"index":38}},
"delegate": false,
"argument": {
"type": "CallExpression",
"start":24,"end":38,"loc":{"start":{"line":2,"column":8,"index":24},"end":{"line":2,"column":22,"index":38}},
"callee": {
"type": "Import",
"start":24,"end":30,"loc":{"start":{"line":2,"column":8,"index":24},"end":{"line":2,"column":14,"index":30}}
},
"arguments": [
{
"type": "StringLiteral",
"start":31,"end":37,"loc":{"start":{"line":2,"column":15,"index":31},"end":{"line":2,"column":21,"index":37}},
"extra": {
"rawValue": "http",
"raw": "'http'"
},
"value": "http"
}
]
}
}
}
],
"directives": []
}
}
],
"directives": []
}
}
@@ -0,0 +1,3 @@
function loadImport(file) {
return import(`test/${file}.js`);
}

0 comments on commit 0f333da

Please sign in to comment.