Skip to content

Commit f2d0bd6

Browse files
committed
Support dynamic import call
Fix #1728
1 parent 6deba39 commit f2d0bd6

22 files changed

+3151
-6
lines changed

docs/syntax-tree-format.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ interface MetaProperty {
287287
```js
288288
interface CallExpression {
289289
type: 'CallExpression';
290-
callee: Expression;
290+
callee: Expression | Import;
291291
arguments: ArgumentListElement[];
292292
}
293293

@@ -301,6 +301,10 @@ interface NewExpression {
301301
with
302302
303303
```js
304+
interface Import {
305+
type: 'Import';
306+
}
307+
304308
type ArgumentListElement = Expression | SpreadElement;
305309

306310
interface SpreadElement {

src/messages.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Error messages should be identical to V8.
22
export const Messages = {
3+
BadImportCallArity: 'Unexpected token',
34
BadGetterArity: 'Getter must not have any formal parameters',
45
BadSetterArity: 'Setter must have exactly one formal parameter',
56
BadSetterRestParameter: 'Setter function argument must not be a rest parameter',

src/nodes.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ export class BreakStatement {
189189

190190
export class CallExpression {
191191
readonly type: string;
192-
readonly callee: Expression;
192+
readonly callee: Expression | Import;
193193
readonly arguments: ArgumentListElement[];
194-
constructor(callee, args) {
194+
constructor(callee: Expression | Import, args: ArgumentListElement[]) {
195195
this.type = Syntax.CallExpression;
196196
this.callee = callee;
197197
this.arguments = args;
@@ -469,6 +469,13 @@ export class IfStatement {
469469
}
470470
}
471471

472+
export class Import {
473+
readonly type: string;
474+
constructor() {
475+
this.type = Syntax.Import;
476+
}
477+
}
478+
472479
export class ImportDeclaration {
473480
readonly type: string;
474481
readonly specifiers: ImportDeclarationSpecifier[];

src/parser.ts

+31-3
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,8 @@ export class Parser {
680680
expr = this.finalize(node, new Node.ThisExpression());
681681
} else if (this.matchKeyword('class')) {
682682
expr = this.parseClassExpression();
683+
} else if (this.matchImportCall()) {
684+
expr = this.parseImportCall();
683685
} else {
684686
expr = this.throwUnexpectedToken(this.nextToken());
685687
}
@@ -1255,6 +1257,25 @@ export class Parser {
12551257
return args;
12561258
}
12571259

1260+
matchImportCall(): boolean {
1261+
let match = this.matchKeyword('import');
1262+
if (match) {
1263+
const state = this.scanner.saveState();
1264+
this.scanner.scanComments();
1265+
const next = this.scanner.lex();
1266+
this.scanner.restoreState(state);
1267+
match = (next.type === Token.Punctuator) && (next.value === '(');
1268+
}
1269+
1270+
return match;
1271+
}
1272+
1273+
parseImportCall(): Node.Import {
1274+
const node = this.createNode();
1275+
this.expectKeyword('import');
1276+
return this.finalize(node, new Node.Import());
1277+
}
1278+
12581279
parseLeftHandSideExpressionAllowCall(): Node.Expression {
12591280
const startToken = this.lookahead;
12601281
const maybeAsync = this.matchContextualKeyword('async');
@@ -1287,6 +1308,9 @@ export class Parser {
12871308
this.context.isBindingElement = false;
12881309
this.context.isAssignmentTarget = false;
12891310
const args = asyncArrow ? this.parseAsyncArguments() : this.parseArguments();
1311+
if (expr.type === Syntax.Import && args.length !== 1) {
1312+
this.tolerateError(Messages.BadImportCallArity);
1313+
}
12901314
expr = this.finalize(this.startNode(startToken), new Node.CallExpression(expr, args));
12911315
if (asyncArrow && this.match('=>')) {
12921316
expr = {
@@ -1789,10 +1813,14 @@ export class Parser {
17891813
statement = this.parseExportDeclaration();
17901814
break;
17911815
case 'import':
1792-
if (!this.context.isModule) {
1793-
this.tolerateUnexpectedToken(this.lookahead, Messages.IllegalImportDeclaration);
1816+
if (this.matchImportCall()) {
1817+
statement = this.parseExpressionStatement();
1818+
} else {
1819+
if (!this.context.isModule) {
1820+
this.tolerateUnexpectedToken(this.lookahead, Messages.IllegalImportDeclaration);
1821+
}
1822+
statement = this.parseImportDeclaration();
17941823
}
1795-
statement = this.parseImportDeclaration();
17961824
break;
17971825
case 'const':
17981826
statement = this.parseLexicalDeclaration({ inFor: false });

src/syntax.ts

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const Syntax = {
3030
FunctionExpression: 'FunctionExpression',
3131
Identifier: 'Identifier',
3232
IfStatement: 'IfStatement',
33+
Import: 'Import',
3334
ImportDeclaration: 'ImportDeclaration',
3435
ImportDefaultSpecifier: 'ImportDefaultSpecifier',
3536
ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',

test/api-tests.js

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ describe('esprima.Syntax', function () {
6060
FunctionExpression: 'FunctionExpression',
6161
Identifier: 'Identifier',
6262
IfStatement: 'IfStatement',
63+
Import: 'Import',
6364
ImportDeclaration: 'ImportDeclaration',
6465
ImportDefaultSpecifier: 'ImportDefaultSpecifier',
6566
ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import $ from "a";
2+
import("b").then(c);

0 commit comments

Comments
 (0)