Skip to content

Commit 11a2385

Browse files
committed
Support dynamic import call
Fix #1728 Closes gh-1740
1 parent 0c26790 commit 11a2385

26 files changed

+3577
-6
lines changed

docs/syntax-tree-format.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ interface MetaProperty {
289289
```js
290290
interface CallExpression {
291291
type: 'CallExpression';
292-
callee: Expression;
292+
callee: Expression | Import;
293293
arguments: ArgumentListElement[];
294294
}
295295

@@ -303,6 +303,10 @@ interface NewExpression {
303303
with
304304
305305
```js
306+
interface Import {
307+
type: 'Import';
308+
}
309+
306310
type ArgumentListElement = Expression | SpreadElement;
307311

308312
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

+33-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
}
@@ -1216,6 +1218,8 @@ export class Parser {
12161218
} else {
12171219
this.throwUnexpectedToken(this.lookahead);
12181220
}
1221+
} else if (this.matchKeyword('import')) {
1222+
this.throwUnexpectedToken(this.lookahead);
12191223
} else {
12201224
const callee = this.isolateCoverGrammar(this.parseLeftHandSideExpression);
12211225
const args = this.match('(') ? this.parseArguments() : [];
@@ -1255,6 +1259,25 @@ export class Parser {
12551259
return args;
12561260
}
12571261

1262+
matchImportCall(): boolean {
1263+
let match = this.matchKeyword('import');
1264+
if (match) {
1265+
const state = this.scanner.saveState();
1266+
this.scanner.scanComments();
1267+
const next = this.scanner.lex();
1268+
this.scanner.restoreState(state);
1269+
match = (next.type === Token.Punctuator) && (next.value === '(');
1270+
}
1271+
1272+
return match;
1273+
}
1274+
1275+
parseImportCall(): Node.Import {
1276+
const node = this.createNode();
1277+
this.expectKeyword('import');
1278+
return this.finalize(node, new Node.Import());
1279+
}
1280+
12581281
parseLeftHandSideExpressionAllowCall(): Node.Expression {
12591282
const startToken = this.lookahead;
12601283
const maybeAsync = this.matchContextualKeyword('async');
@@ -1287,6 +1310,9 @@ export class Parser {
12871310
this.context.isBindingElement = false;
12881311
this.context.isAssignmentTarget = false;
12891312
const args = asyncArrow ? this.parseAsyncArguments() : this.parseArguments();
1313+
if (expr.type === Syntax.Import && args.length !== 1) {
1314+
this.tolerateError(Messages.BadImportCallArity);
1315+
}
12901316
expr = this.finalize(this.startNode(startToken), new Node.CallExpression(expr, args));
12911317
if (asyncArrow && this.match('=>')) {
12921318
expr = {
@@ -1789,10 +1815,14 @@ export class Parser {
17891815
statement = this.parseExportDeclaration();
17901816
break;
17911817
case 'import':
1792-
if (!this.context.isModule) {
1793-
this.tolerateUnexpectedToken(this.lookahead, Messages.IllegalImportDeclaration);
1818+
if (this.matchImportCall()) {
1819+
statement = this.parseExpressionStatement();
1820+
} else {
1821+
if (!this.context.isModule) {
1822+
this.tolerateUnexpectedToken(this.lookahead, Messages.IllegalImportDeclaration);
1823+
}
1824+
statement = this.parseImportDeclaration();
17941825
}
1795-
statement = this.parseImportDeclaration();
17961826
break;
17971827
case 'const':
17981828
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 @@
1+
async function f(x) { await import(x) }

0 commit comments

Comments
 (0)