Skip to content

Commit b4a6e52

Browse files
authored
Add support for import.meta
1 parent f1c3592 commit b4a6e52

10 files changed

+151
-16
lines changed

acorn-loose/src/expression.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -310,10 +310,13 @@ lp.parseExprAtom = function() {
310310

311311
lp.parseExprImport = function() {
312312
const node = this.startNode()
313-
this.next() // skip `import`
313+
const meta = this.parseIdent(true)
314314
switch (this.tok.type) {
315315
case tt.parenL:
316316
return this.parseDynamicImport(node)
317+
case tt.dot:
318+
node.meta = meta
319+
return this.parseImportMeta(node)
317320
default:
318321
node.name = "import"
319322
return this.finishNode(node, "Identifier")
@@ -325,6 +328,12 @@ lp.parseDynamicImport = function(node) {
325328
return this.finishNode(node, "ImportExpression")
326329
}
327330

331+
lp.parseImportMeta = function(node) {
332+
this.next() // skip '.'
333+
node.property = this.parseIdent(true)
334+
return this.finishNode(node, "MetaProperty")
335+
}
336+
328337
lp.parseNew = function() {
329338
let node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart
330339
let meta = this.parseIdent(true)

acorn-loose/src/statement.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,13 @@ lp.parseStatement = function() {
176176
return this.parseClass(true)
177177

178178
case tt._import:
179-
if (this.options.ecmaVersion > 10 && this.lookAhead(1).type === tt.parenL) {
180-
node.expression = this.parseExpression()
181-
this.semicolon()
182-
return this.finishNode(node, "ExpressionStatement")
179+
if (this.options.ecmaVersion > 10) {
180+
const nextType = this.lookAhead(1).type
181+
if (nextType === tt.parenL || nextType === tt.dot) {
182+
node.expression = this.parseExpression()
183+
this.semicolon()
184+
return this.finishNode(node, "ExpressionStatement")
185+
}
183186
}
184187

185188
return this.parseImport()

acorn/src/expression.js

+30-4
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,18 @@ pp.parseExprAtom = function(refDestructuringErrors) {
427427

428428
pp.parseExprImport = function() {
429429
const node = this.startNode()
430-
this.next() // skip `import`
430+
431+
// Consume `import` as an identifier for `import.meta`.
432+
// Because `this.parseIdent(true)` doesn't check escape sequences, it needs the check of `this.containsEsc`.
433+
if (this.containsEsc) this.raiseRecoverable(this.start, "Escape sequence in keyword import")
434+
const meta = this.parseIdent(true)
435+
431436
switch (this.type) {
432437
case tt.parenL:
433438
return this.parseDynamicImport(node)
439+
case tt.dot:
440+
node.meta = meta
441+
return this.parseImportMeta(node)
434442
default:
435443
this.unexpected()
436444
}
@@ -455,6 +463,22 @@ pp.parseDynamicImport = function(node) {
455463
return this.finishNode(node, "ImportExpression")
456464
}
457465

466+
pp.parseImportMeta = function(node) {
467+
this.next() // skip `.`
468+
469+
const containsEsc = this.containsEsc
470+
node.property = this.parseIdent(true)
471+
472+
if (node.property.name !== "meta")
473+
this.raiseRecoverable(node.property.start, "The only valid meta property for import is 'import.meta'")
474+
if (containsEsc)
475+
this.raiseRecoverable(node.start, "'import.meta' must not contain escaped characters")
476+
if (this.options.sourceType !== "module")
477+
this.raiseRecoverable(node.start, "Cannot use 'import.meta' outside a module")
478+
479+
return this.finishNode(node, "MetaProperty")
480+
}
481+
458482
pp.parseLiteral = function(value) {
459483
let node = this.startNode()
460484
node.value = value
@@ -557,10 +581,12 @@ pp.parseNew = function() {
557581
node.meta = meta
558582
let containsEsc = this.containsEsc
559583
node.property = this.parseIdent(true)
560-
if (node.property.name !== "target" || containsEsc)
561-
this.raiseRecoverable(node.property.start, "The only valid meta property for new is new.target")
584+
if (node.property.name !== "target")
585+
this.raiseRecoverable(node.property.start, "The only valid meta property for new is 'new.target'")
586+
if (containsEsc)
587+
this.raiseRecoverable(node.start, "'new.target' must not contain escaped characters")
562588
if (!this.inNonArrowFunction())
563-
this.raiseRecoverable(node.start, "new.target can only be used in functions")
589+
this.raiseRecoverable(node.start, "'new.target' can only be used in functions")
564590
return this.finishNode(node, "MetaProperty")
565591
}
566592
let startPos = this.start, startLoc = this.startLoc, isImport = this.type === tt._import

acorn/src/statement.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ pp.parseStatement = function(context, topLevel, exports) {
122122
skipWhiteSpace.lastIndex = this.pos
123123
let skip = skipWhiteSpace.exec(this.input)
124124
let next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next)
125-
if (nextCh === 40) // '('
125+
if (nextCh === 40 || nextCh === 46) // '(' or '.'
126126
return this.parseExpressionStatement(node, this.parseExpression())
127127
}
128128

bin/run_test262.js

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const unsupportedFeatures = [
1111
"class-static-fields-public",
1212
"class-static-methods-private",
1313
"coalesce-expression",
14-
"import.meta",
1514
"numeric-separator-literal",
1615
"optional-chaining",
1716
"top-level-await"

test/run.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
require("./tests-bigint.js");
1919
require("./tests-dynamic-import.js");
2020
require("./tests-export-all-as-ns-from-source.js");
21+
require("./tests-import-meta.js");
2122
var acorn = require("../acorn")
2223
var acorn_loose = require("../acorn-loose")
2324

test/tests-dynamic-import.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ test(
204204
{ ecmaVersion: 11 }
205205
);
206206

207-
testFail('function failsParse() { return import.then(); }', 'Unexpected token (1:37)', {
207+
testFail('function failsParse() { return import.then(); }', 'The only valid meta property for import is \'import.meta\' (1:38)', {
208208
ecmaVersion: 11,
209209
loose: false
210210
});

test/tests-harmony.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -14781,10 +14781,10 @@ test("function foo() { new.target; }", {
1478114781
sourceType: "script"
1478214782
}, {ecmaVersion: 6});
1478314783

14784-
testFail("new.prop", "The only valid meta property for new is new.target (1:4)", {ecmaVersion: 6});
14785-
testFail("new.target", "new.target can only be used in functions (1:0)", {ecmaVersion: 6});
14784+
testFail("new.prop", "The only valid meta property for new is 'new.target' (1:4)", {ecmaVersion: 6});
14785+
testFail("new.target", "'new.target' can only be used in functions (1:0)", {ecmaVersion: 6});
1478614786
test("function x() { return () => new.target }", {}, {ecmaVersion: 6});
14787-
testFail("let y = () => new.target", "new.target can only be used in functions (1:14)", {ecmaVersion: 6});
14787+
testFail("let y = () => new.target", "'new.target' can only be used in functions (1:14)", {ecmaVersion: 6});
1478814788

1478914789
test("export default function foo() {} false", {
1479014790
body: [

test/tests-import-meta.js

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Tests for ECMAScript 2020 `import.meta`
2+
3+
if (typeof exports != 'undefined') {
4+
var test = require('./driver.js').test;
5+
var testFail = require('./driver.js').testFail;
6+
}
7+
8+
test(
9+
"import.meta",
10+
{
11+
"type": "Program",
12+
"start": 0,
13+
"end": 11,
14+
"body": [
15+
{
16+
"type": "ExpressionStatement",
17+
"start": 0,
18+
"end": 11,
19+
"expression": {
20+
"type": "MetaProperty",
21+
"start": 0,
22+
"end": 11,
23+
"meta": {
24+
"type": "Identifier",
25+
"start": 0,
26+
"end": 6,
27+
"name": "import"
28+
},
29+
"property": {
30+
"type": "Identifier",
31+
"start": 7,
32+
"end": 11,
33+
"name": "meta"
34+
}
35+
}
36+
}
37+
],
38+
"sourceType": "module"
39+
},
40+
{ ecmaVersion: 11, sourceType: "module" }
41+
);
42+
43+
test(
44+
"import.meta.url",
45+
{
46+
"type": "Program",
47+
"start": 0,
48+
"end": 15,
49+
"body": [
50+
{
51+
"type": "ExpressionStatement",
52+
"start": 0,
53+
"end": 15,
54+
"expression": {
55+
"type": "MemberExpression",
56+
"start": 0,
57+
"end": 15,
58+
"object": {
59+
"type": "MetaProperty",
60+
"start": 0,
61+
"end": 11,
62+
"meta": {
63+
"type": "Identifier",
64+
"start": 0,
65+
"end": 6,
66+
"name": "import"
67+
},
68+
"property": {
69+
"type": "Identifier",
70+
"start": 7,
71+
"end": 11,
72+
"name": "meta"
73+
}
74+
},
75+
"property": {
76+
"type": "Identifier",
77+
"start": 12,
78+
"end": 15,
79+
"name": "url"
80+
},
81+
"computed": false
82+
}
83+
}
84+
],
85+
"sourceType": "module"
86+
},
87+
{ ecmaVersion: 11, sourceType: "module" }
88+
);
89+
90+
testFail("import.meta", "Unexpected token (1:6)", { ecmaVersion: 10, sourceType: "module" });
91+
testFail("import.meta", "Cannot use 'import.meta' outside a module (1:0)", { ecmaVersion: 11, sourceType: "script" });
92+
testFail("import['meta']", "Unexpected token (1:6)", { ecmaVersion: 11, sourceType: "module" });
93+
testFail("a = import['meta']", "Unexpected token (1:10)", { ecmaVersion: 11, sourceType: "module" });
94+
testFail("import.target", "The only valid meta property for import is 'import.meta' (1:7)", { ecmaVersion: 11, sourceType: "module" });
95+
testFail("new.meta", "The only valid meta property for new is 'new.target' (1:4)", { ecmaVersion: 11, sourceType: "module" });
96+
testFail("im\\u0070ort.meta", "Escape sequence in keyword import (1:0)", { ecmaVersion: 11, sourceType: "module" });
97+
testFail("import.\\u006d\\u0065\\u0074\\u0061", "'import.meta' must not contain escaped characters (1:0)", { ecmaVersion: 11, sourceType: "module" });

test/tests.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -29428,7 +29428,7 @@ test("(\\u0061sync ())", {
2942829428
}, {ecmaVersion: 8})
2942929429
testFail("({ \\u0061sync x() { await x } })", "Unexpected token (1:14)", {ecmaVersion: 8})
2943029430
testFail("for (x \\u006ff y) {}", "Unexpected token (1:7)", {ecmaVersion: 6})
29431-
testFail("function x () { new.ta\\u0072get }", "The only valid meta property for new is new.target (1:20)", {ecmaVersion: 6})
29431+
testFail("function x () { new.ta\\u0072get }", "'new.target' must not contain escaped characters (1:16)", {ecmaVersion: 6})
2943229432
testFail("class X { st\\u0061tic y() {} }", "Unexpected token (1:22)", {ecmaVersion: 6})
2943329433

2943429434
testFail("(x=1)=2", "Parenthesized pattern (1:0)")

0 commit comments

Comments
 (0)