Skip to content

Commit 0f333da

Browse files
JLHwungnicolo-ribaudoliuxingbaoyu
committed
Add createImportExpressions parser option (#15682)
Co-authored-by: Nicolò Ribaudo <[email protected]> Co-authored-by: liuxingbaoyu <[email protected]>
1 parent b06dab5 commit 0f333da

File tree

260 files changed

+3990
-393
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

260 files changed

+3990
-393
lines changed

packages/babel-generator/src/generators/modules.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,15 @@ export function ImportNamespaceSpecifier(
312312
this.space();
313313
this.print(node.local, node);
314314
}
315+
316+
export function ImportExpression(this: Printer, node: t.ImportExpression) {
317+
this.word("import");
318+
this.token("(");
319+
this.print(node.source, node);
320+
if (node.options != null) {
321+
this.token(",");
322+
this.space();
323+
this.print(node.options, node);
324+
}
325+
this.token(")");
326+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/*0*/import/*1*/(/*2*/"foo"/*3*/)/*4*/
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"parserOpts": {
3+
"createImportExpressions": true
4+
}
5+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/*0*/import /*1*/( /*2*/"foo" /*3*/); /*4*/
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import("module.js");
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "parserOpts": { "createImportExpressions": false } }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import("module.js");
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "plugins": ["dynamicImport"] }
1+
{ "parserOpts": { "createImportExpressions": true } }
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import("foo.json", { with: { type: "json" } });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"plugins": [["importAttributes", { "importAttributesKeyword": "with" }]],
3+
"sourceType": "module",
4+
"parserOpts": { "createImportExpressions": false }
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import("foo.json", {
2+
with: {
3+
type: "json"
4+
}
5+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import("foo.json", { with: { type: "json" } });
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"plugins": [["importAttributes", { "importAttributesKeyword": "with" }]],
3+
"sourceType": "module",
4+
"parserOpts": { "createImportExpressions": true }
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import("foo.json", {
2+
with: {
3+
type: "json"
4+
}
5+
});

packages/babel-helper-module-transforms/src/dynamic-import.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ if (!process.env.BABEL_8_BREAKING && !USE_ESM && !IS_STANDALONE) {
1717
}
1818

1919
export function buildDynamicImport(
20-
node: t.CallExpression,
20+
node: t.CallExpression | t.ImportExpression,
2121
deferToThen: boolean,
2222
wrapWithPromise: boolean,
2323
builder: (specifier: t.Expression) => t.Expression,
2424
): t.Expression {
25-
const [specifier] = node.arguments;
25+
const specifier = t.isCallExpression(node) ? node.arguments[0] : node.source;
2626

2727
if (
2828
t.isStringLiteral(specifier) ||

packages/babel-helper-module-transforms/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ export function ensureStatementsHoisted(statements: t.Statement[]) {
165165
* wrap it in a call to the interop helpers based on the type.
166166
*/
167167
export function wrapInterop(
168-
programPath: NodePath,
168+
programPath: NodePath<t.Program>,
169169
expr: t.Expression,
170170
type: InteropType,
171171
): t.CallExpression {

packages/babel-parser/data/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@
6868
"description": "By default, exported identifiers must refer to a declared variable.\nSet this to true to allow export statements to reference undeclared variables.",
6969
"type": "boolean"
7070
},
71+
"createImportExpressions": {
72+
"description": "By default, `import(foo)` is parsed as `CallExpression(Import, [Identifier(foo)])`.\nSet this to true to parse it as an `ImportExpression` node.",
73+
"type": "boolean",
74+
"default": false
75+
},
7176
"createParenthesizedExpressions": {
7277
"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.",
7378
"type": "boolean"

packages/babel-parser/src/options.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type Options = {
2020
strictMode: boolean | undefined | null;
2121
ranges: boolean;
2222
tokens: boolean;
23+
createImportExpressions: boolean;
2324
createParenthesizedExpressions: boolean;
2425
errorRecovery: boolean;
2526
attachComment: boolean;
@@ -68,6 +69,9 @@ export const defaultOptions: Options = {
6869
ranges: false,
6970
// Adds all parsed tokens to a `tokens` property on the `File` node
7071
tokens: false,
72+
// Whether to create ImportExpression AST nodes (if false
73+
// `import(foo)` will be parsed as CallExpression(Import, [Identifier(foo)])
74+
createImportExpressions: false,
7175
// Whether to create ParenthesizedExpression AST nodes (if false
7276
// the parser sets extra.parenthesized on the expression nodes instead).
7377
createParenthesizedExpressions: false,

packages/babel-parser/src/parser/expression.ts

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,19 +1110,26 @@ export default abstract class ExpressionParser extends LValParser {
11101110
return this.parseSuper();
11111111

11121112
case tt._import:
1113-
node = this.startNode<N.MetaProperty | N.Import>();
1113+
node = this.startNode<N.MetaProperty | N.Import | N.ImportExpression>();
11141114
this.next();
11151115

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

1120-
if (!this.match(tt.parenL)) {
1120+
if (this.match(tt.parenL)) {
1121+
if (this.options.createImportExpressions) {
1122+
return this.parseImportCall(node as Undone<N.ImportExpression>);
1123+
} else {
1124+
return this.finishNode(node, "Import");
1125+
}
1126+
} else {
11211127
this.raise(Errors.UnsupportedImport, {
11221128
at: this.state.lastTokStartLoc,
11231129
});
1130+
return this.finishNode(node, "Import");
11241131
}
1125-
return this.finishNode(node, "Import");
1132+
11261133
case tt._this:
11271134
node = this.startNode();
11281135
this.next();
@@ -1920,9 +1927,14 @@ export default abstract class ExpressionParser extends LValParser {
19201927
}
19211928

19221929
parseNewCallee(this: Parser, node: Undone<N.NewExpression>): void {
1923-
node.callee = this.parseNoCallExpr();
1924-
if (node.callee.type === "Import") {
1925-
this.raise(Errors.ImportCallNotNewExpression, { at: node.callee });
1930+
const isImport = this.match(tt._import);
1931+
const callee = this.parseNoCallExpr();
1932+
node.callee = callee;
1933+
if (
1934+
isImport &&
1935+
(callee.type === "Import" || callee.type === "ImportExpression")
1936+
) {
1937+
this.raise(Errors.ImportCallNotNewExpression, { at: callee });
19261938
}
19271939
}
19281940

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

2954+
// https://tc39.es/ecma262/#prod-ImportCall
2955+
parseImportCall(
2956+
this: Parser,
2957+
node: Undone<N.ImportExpression>,
2958+
): N.ImportExpression {
2959+
this.next(); // eat tt.parenL
2960+
node.source = this.parseMaybeAssignAllowIn();
2961+
if (
2962+
this.hasPlugin("importAttributes") ||
2963+
this.hasPlugin("importAssertions")
2964+
) {
2965+
node.options = null;
2966+
}
2967+
if (this.eat(tt.comma)) {
2968+
this.expectImportAttributesPlugin();
2969+
if (!this.match(tt.parenR)) {
2970+
node.options = this.parseMaybeAssignAllowIn();
2971+
this.eat(tt.comma);
2972+
}
2973+
}
2974+
this.expect(tt.parenR);
2975+
return this.finishNode(node, "ImportExpression");
2976+
}
2977+
29422978
// Validates a pipeline (for any of the pipeline Babylon plugins) at the point
29432979
// of the infix operator `|>`.
29442980

packages/babel-parser/src/plugins/estree.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,9 @@ export default (superClass: typeof Parser) =>
424424
this.hasPlugin("importAttributes") ||
425425
this.hasPlugin("importAssertions")
426426
) {
427+
(node as N.Node as N.EstreeImportExpression).options =
428+
node.arguments[1] ?? null;
429+
// compatibility with previous ESTree AST
427430
(node as N.Node as N.EstreeImportExpression).attributes =
428431
node.arguments[1] ?? null;
429432
}

packages/babel-parser/src/types.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,12 @@ export interface NewExpression extends CallOrNewBase {
624624
optional?: boolean; // TODO: Not in spec
625625
}
626626

627+
export interface ImportExpression extends NodeBase {
628+
type: "ImportExpression";
629+
source: Expression;
630+
options: Expression | null;
631+
}
632+
627633
export interface SequenceExpression extends NodeBase {
628634
type: "SequenceExpression";
629635
expressions: Expression[];
@@ -1213,6 +1219,10 @@ export interface EstreeMethodDefinition extends NodeBase {
12131219
export interface EstreeImportExpression extends NodeBase {
12141220
type: "ImportExpression";
12151221
source: Expression;
1222+
options?: Expression | null;
1223+
/**
1224+
* @deprecated Use options instead
1225+
*/
12161226
attributes?: Expression | null;
12171227
}
12181228

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function failsParse() {
2+
return import.then();
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":49,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":49}},
4+
"errors": [
5+
"SyntaxError: The only valid meta property for import is import.meta. (2:16)"
6+
],
7+
"program": {
8+
"type": "Program",
9+
"start":0,"end":49,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":49}},
10+
"sourceType": "script",
11+
"interpreter": null,
12+
"body": [
13+
{
14+
"type": "FunctionDeclaration",
15+
"start":0,"end":49,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":49}},
16+
"id": {
17+
"type": "Identifier",
18+
"start":9,"end":19,"loc":{"start":{"line":1,"column":9,"index":9},"end":{"line":1,"column":19,"index":19},"identifierName":"failsParse"},
19+
"name": "failsParse"
20+
},
21+
"generator": false,
22+
"async": false,
23+
"params": [],
24+
"body": {
25+
"type": "BlockStatement",
26+
"start":22,"end":49,"loc":{"start":{"line":1,"column":22,"index":22},"end":{"line":3,"column":1,"index":49}},
27+
"body": [
28+
{
29+
"type": "ReturnStatement",
30+
"start":26,"end":47,"loc":{"start":{"line":2,"column":2,"index":26},"end":{"line":2,"column":23,"index":47}},
31+
"argument": {
32+
"type": "CallExpression",
33+
"start":33,"end":46,"loc":{"start":{"line":2,"column":9,"index":33},"end":{"line":2,"column":22,"index":46}},
34+
"callee": {
35+
"type": "MetaProperty",
36+
"start":33,"end":44,"loc":{"start":{"line":2,"column":9,"index":33},"end":{"line":2,"column":20,"index":44}},
37+
"meta": {
38+
"type": "Identifier",
39+
"start":33,"end":39,"loc":{"start":{"line":2,"column":9,"index":33},"end":{"line":2,"column":15,"index":39},"identifierName":"import"},
40+
"name": "import"
41+
},
42+
"property": {
43+
"type": "Identifier",
44+
"start":40,"end":44,"loc":{"start":{"line":2,"column":16,"index":40},"end":{"line":2,"column":20,"index":44},"identifierName":"then"},
45+
"name": "then"
46+
}
47+
},
48+
"arguments": []
49+
}
50+
}
51+
],
52+
"directives": []
53+
}
54+
}
55+
],
56+
"directives": []
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function* a() {
2+
yield import('http');
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"type": "File",
3+
"start":0,"end":41,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":41}},
4+
"program": {
5+
"type": "Program",
6+
"start":0,"end":41,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":41}},
7+
"sourceType": "script",
8+
"interpreter": null,
9+
"body": [
10+
{
11+
"type": "FunctionDeclaration",
12+
"start":0,"end":41,"loc":{"start":{"line":1,"column":0,"index":0},"end":{"line":3,"column":1,"index":41}},
13+
"id": {
14+
"type": "Identifier",
15+
"start":10,"end":11,"loc":{"start":{"line":1,"column":10,"index":10},"end":{"line":1,"column":11,"index":11},"identifierName":"a"},
16+
"name": "a"
17+
},
18+
"generator": true,
19+
"async": false,
20+
"params": [],
21+
"body": {
22+
"type": "BlockStatement",
23+
"start":14,"end":41,"loc":{"start":{"line":1,"column":14,"index":14},"end":{"line":3,"column":1,"index":41}},
24+
"body": [
25+
{
26+
"type": "ExpressionStatement",
27+
"start":18,"end":39,"loc":{"start":{"line":2,"column":2,"index":18},"end":{"line":2,"column":23,"index":39}},
28+
"expression": {
29+
"type": "YieldExpression",
30+
"start":18,"end":38,"loc":{"start":{"line":2,"column":2,"index":18},"end":{"line":2,"column":22,"index":38}},
31+
"delegate": false,
32+
"argument": {
33+
"type": "CallExpression",
34+
"start":24,"end":38,"loc":{"start":{"line":2,"column":8,"index":24},"end":{"line":2,"column":22,"index":38}},
35+
"callee": {
36+
"type": "Import",
37+
"start":24,"end":30,"loc":{"start":{"line":2,"column":8,"index":24},"end":{"line":2,"column":14,"index":30}}
38+
},
39+
"arguments": [
40+
{
41+
"type": "StringLiteral",
42+
"start":31,"end":37,"loc":{"start":{"line":2,"column":15,"index":31},"end":{"line":2,"column":21,"index":37}},
43+
"extra": {
44+
"rawValue": "http",
45+
"raw": "'http'"
46+
},
47+
"value": "http"
48+
}
49+
]
50+
}
51+
}
52+
}
53+
],
54+
"directives": []
55+
}
56+
}
57+
],
58+
"directives": []
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function loadImport(file) {
2+
return import(`test/${file}.js`);
3+
}

0 commit comments

Comments
 (0)