Skip to content

Commit

Permalink
Add support for import.meta
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Apr 2, 2020
1 parent f1c3592 commit b4a6e52
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 16 deletions.
11 changes: 10 additions & 1 deletion acorn-loose/src/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,13 @@ lp.parseExprAtom = function() {

lp.parseExprImport = function() {
const node = this.startNode()
this.next() // skip `import`
const meta = this.parseIdent(true)
switch (this.tok.type) {
case tt.parenL:
return this.parseDynamicImport(node)
case tt.dot:
node.meta = meta
return this.parseImportMeta(node)
default:
node.name = "import"
return this.finishNode(node, "Identifier")
Expand All @@ -325,6 +328,12 @@ lp.parseDynamicImport = function(node) {
return this.finishNode(node, "ImportExpression")
}

lp.parseImportMeta = function(node) {
this.next() // skip '.'
node.property = this.parseIdent(true)
return this.finishNode(node, "MetaProperty")
}

lp.parseNew = function() {
let node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart
let meta = this.parseIdent(true)
Expand Down
11 changes: 7 additions & 4 deletions acorn-loose/src/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,13 @@ lp.parseStatement = function() {
return this.parseClass(true)

case tt._import:
if (this.options.ecmaVersion > 10 && this.lookAhead(1).type === tt.parenL) {
node.expression = this.parseExpression()
this.semicolon()
return this.finishNode(node, "ExpressionStatement")
if (this.options.ecmaVersion > 10) {
const nextType = this.lookAhead(1).type
if (nextType === tt.parenL || nextType === tt.dot) {
node.expression = this.parseExpression()
this.semicolon()
return this.finishNode(node, "ExpressionStatement")
}
}

return this.parseImport()
Expand Down
34 changes: 30 additions & 4 deletions acorn/src/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,10 +427,18 @@ pp.parseExprAtom = function(refDestructuringErrors) {

pp.parseExprImport = function() {
const node = this.startNode()
this.next() // skip `import`

// Consume `import` as an identifier for `import.meta`.
// Because `this.parseIdent(true)` doesn't check escape sequences, it needs the check of `this.containsEsc`.
if (this.containsEsc) this.raiseRecoverable(this.start, "Escape sequence in keyword import")
const meta = this.parseIdent(true)

switch (this.type) {
case tt.parenL:
return this.parseDynamicImport(node)
case tt.dot:
node.meta = meta
return this.parseImportMeta(node)
default:
this.unexpected()
}
Expand All @@ -455,6 +463,22 @@ pp.parseDynamicImport = function(node) {
return this.finishNode(node, "ImportExpression")
}

pp.parseImportMeta = function(node) {
this.next() // skip `.`

const containsEsc = this.containsEsc
node.property = this.parseIdent(true)

if (node.property.name !== "meta")
this.raiseRecoverable(node.property.start, "The only valid meta property for import is 'import.meta'")
if (containsEsc)
this.raiseRecoverable(node.start, "'import.meta' must not contain escaped characters")
if (this.options.sourceType !== "module")
this.raiseRecoverable(node.start, "Cannot use 'import.meta' outside a module")

return this.finishNode(node, "MetaProperty")
}

pp.parseLiteral = function(value) {
let node = this.startNode()
node.value = value
Expand Down Expand Up @@ -557,10 +581,12 @@ pp.parseNew = function() {
node.meta = meta
let containsEsc = this.containsEsc
node.property = this.parseIdent(true)
if (node.property.name !== "target" || containsEsc)
this.raiseRecoverable(node.property.start, "The only valid meta property for new is new.target")
if (node.property.name !== "target")
this.raiseRecoverable(node.property.start, "The only valid meta property for new is 'new.target'")
if (containsEsc)
this.raiseRecoverable(node.start, "'new.target' must not contain escaped characters")
if (!this.inNonArrowFunction())
this.raiseRecoverable(node.start, "new.target can only be used in functions")
this.raiseRecoverable(node.start, "'new.target' can only be used in functions")
return this.finishNode(node, "MetaProperty")
}
let startPos = this.start, startLoc = this.startLoc, isImport = this.type === tt._import
Expand Down
2 changes: 1 addition & 1 deletion acorn/src/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ pp.parseStatement = function(context, topLevel, exports) {
skipWhiteSpace.lastIndex = this.pos
let skip = skipWhiteSpace.exec(this.input)
let next = this.pos + skip[0].length, nextCh = this.input.charCodeAt(next)
if (nextCh === 40) // '('
if (nextCh === 40 || nextCh === 46) // '(' or '.'
return this.parseExpressionStatement(node, this.parseExpression())
}

Expand Down
1 change: 0 additions & 1 deletion bin/run_test262.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const unsupportedFeatures = [
"class-static-fields-public",
"class-static-methods-private",
"coalesce-expression",
"import.meta",
"numeric-separator-literal",
"optional-chaining",
"top-level-await"
Expand Down
1 change: 1 addition & 0 deletions test/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
require("./tests-bigint.js");
require("./tests-dynamic-import.js");
require("./tests-export-all-as-ns-from-source.js");
require("./tests-import-meta.js");
var acorn = require("../acorn")
var acorn_loose = require("../acorn-loose")

Expand Down
2 changes: 1 addition & 1 deletion test/tests-dynamic-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ test(
{ ecmaVersion: 11 }
);

testFail('function failsParse() { return import.then(); }', 'Unexpected token (1:37)', {
testFail('function failsParse() { return import.then(); }', 'The only valid meta property for import is \'import.meta\' (1:38)', {
ecmaVersion: 11,
loose: false
});
Expand Down
6 changes: 3 additions & 3 deletions test/tests-harmony.js
Original file line number Diff line number Diff line change
Expand Up @@ -14781,10 +14781,10 @@ test("function foo() { new.target; }", {
sourceType: "script"
}, {ecmaVersion: 6});

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

test("export default function foo() {} false", {
body: [
Expand Down
97 changes: 97 additions & 0 deletions test/tests-import-meta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Tests for ECMAScript 2020 `import.meta`

if (typeof exports != 'undefined') {
var test = require('./driver.js').test;
var testFail = require('./driver.js').testFail;
}

test(
"import.meta",
{
"type": "Program",
"start": 0,
"end": 11,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 11,
"expression": {
"type": "MetaProperty",
"start": 0,
"end": 11,
"meta": {
"type": "Identifier",
"start": 0,
"end": 6,
"name": "import"
},
"property": {
"type": "Identifier",
"start": 7,
"end": 11,
"name": "meta"
}
}
}
],
"sourceType": "module"
},
{ ecmaVersion: 11, sourceType: "module" }
);

test(
"import.meta.url",
{
"type": "Program",
"start": 0,
"end": 15,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 15,
"expression": {
"type": "MemberExpression",
"start": 0,
"end": 15,
"object": {
"type": "MetaProperty",
"start": 0,
"end": 11,
"meta": {
"type": "Identifier",
"start": 0,
"end": 6,
"name": "import"
},
"property": {
"type": "Identifier",
"start": 7,
"end": 11,
"name": "meta"
}
},
"property": {
"type": "Identifier",
"start": 12,
"end": 15,
"name": "url"
},
"computed": false
}
}
],
"sourceType": "module"
},
{ ecmaVersion: 11, sourceType: "module" }
);

testFail("import.meta", "Unexpected token (1:6)", { ecmaVersion: 10, sourceType: "module" });
testFail("import.meta", "Cannot use 'import.meta' outside a module (1:0)", { ecmaVersion: 11, sourceType: "script" });
testFail("import['meta']", "Unexpected token (1:6)", { ecmaVersion: 11, sourceType: "module" });
testFail("a = import['meta']", "Unexpected token (1:10)", { ecmaVersion: 11, sourceType: "module" });
testFail("import.target", "The only valid meta property for import is 'import.meta' (1:7)", { ecmaVersion: 11, sourceType: "module" });
testFail("new.meta", "The only valid meta property for new is 'new.target' (1:4)", { ecmaVersion: 11, sourceType: "module" });
testFail("im\\u0070ort.meta", "Escape sequence in keyword import (1:0)", { ecmaVersion: 11, sourceType: "module" });
testFail("import.\\u006d\\u0065\\u0074\\u0061", "'import.meta' must not contain escaped characters (1:0)", { ecmaVersion: 11, sourceType: "module" });
2 changes: 1 addition & 1 deletion test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -29428,7 +29428,7 @@ test("(\\u0061sync ())", {
}, {ecmaVersion: 8})
testFail("({ \\u0061sync x() { await x } })", "Unexpected token (1:14)", {ecmaVersion: 8})
testFail("for (x \\u006ff y) {}", "Unexpected token (1:7)", {ecmaVersion: 6})
testFail("function x () { new.ta\\u0072get }", "The only valid meta property for new is new.target (1:20)", {ecmaVersion: 6})
testFail("function x () { new.ta\\u0072get }", "'new.target' must not contain escaped characters (1:16)", {ecmaVersion: 6})
testFail("class X { st\\u0061tic y() {} }", "Unexpected token (1:22)", {ecmaVersion: 6})

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

0 comments on commit b4a6e52

Please sign in to comment.