@babel/eslint-parser: fix BigIntLiteral node to match ESTree spec (#10827)
* @babel/eslint-parser: fix BigIntLiteral node to match ESTree spec * Move token conversion to @babel/eslint-parser * Add estree plugin tests * Update test helpers to handle BigInt serializing * Update Literal union type to include BigIntLiteral * Add FlowIgnore comment for BigInt
This commit is contained in:
committed by
Nicolò Ribaudo
parent
b2429fe203
commit
fb100eee41
@@ -34,6 +34,7 @@
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
|
||||
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
|
||||
"@babel/plugin-syntax-bigint": "^7.7.4",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/plugin-syntax-export-default-from": "^7.0.0",
|
||||
"@babel/plugin-syntax-export-namespace-from": "^7.0.0",
|
||||
|
||||
@@ -70,7 +70,6 @@ const astTransformVisitor = {
|
||||
}
|
||||
|
||||
// modules
|
||||
|
||||
if (path.isImportDeclaration()) {
|
||||
delete node.isType;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ export default function(token, tt, source) {
|
||||
flags: value.flags,
|
||||
};
|
||||
token.value = `/${value.pattern}/${value.flags}`;
|
||||
} else if (type === tt.bigint) {
|
||||
token.type = "Numeric";
|
||||
token.value = `${token.value}n`;
|
||||
}
|
||||
|
||||
return token;
|
||||
|
||||
@@ -27,7 +27,7 @@ function parseAndAssertSame(code) {
|
||||
loc: true,
|
||||
range: true,
|
||||
comment: true,
|
||||
ecmaVersion: 2018,
|
||||
ecmaVersion: 2020,
|
||||
sourceType: "module",
|
||||
});
|
||||
const babylonAST = parseForESLint(code, {
|
||||
@@ -518,5 +518,11 @@ describe("babylon-to-espree", () => {
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it("BigInt", () => {
|
||||
parseAndAssertSame(`
|
||||
const a = 1n;
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,5 +17,6 @@ module.exports = {
|
||||
"@babel/plugin-syntax-export-namespace-from",
|
||||
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: false }],
|
||||
["@babel/plugin-proposal-pipeline-operator", { proposal: "minimal" }],
|
||||
"@babel/plugin-syntax-bigint",
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
|
||||
/* global BigInt */
|
||||
|
||||
import { types as tt, TokenType } from "../tokenizer/types";
|
||||
import type Parser from "../parser";
|
||||
import * as N from "../types";
|
||||
@@ -31,6 +33,16 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
return node;
|
||||
}
|
||||
|
||||
estreeParseBigIntLiteral(value: any): N.Node {
|
||||
// https://github.com/estree/estree/blob/master/es2020.md#bigintliteral
|
||||
// $FlowIgnore
|
||||
const bigInt = typeof BigInt !== "undefined" ? BigInt(value) : null;
|
||||
const node = this.estreeParseLiteral(bigInt);
|
||||
node.bigint = String(node.value || value);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
estreeParseLiteral(value: any): N.Node {
|
||||
return this.parseLiteral(value, "Literal");
|
||||
}
|
||||
@@ -244,13 +256,16 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
|
||||
parseExprAtom(refShorthandDefaultPos?: ?Pos): N.Expression {
|
||||
switch (this.state.type) {
|
||||
case tt.regexp:
|
||||
return this.estreeParseRegExpLiteral(this.state.value);
|
||||
|
||||
case tt.num:
|
||||
case tt.string:
|
||||
return this.estreeParseLiteral(this.state.value);
|
||||
|
||||
case tt.regexp:
|
||||
return this.estreeParseRegExpLiteral(this.state.value);
|
||||
|
||||
case tt.bigint:
|
||||
return this.estreeParseBigIntLiteral(this.state.value);
|
||||
|
||||
case tt._null:
|
||||
return this.estreeParseLiteral(null);
|
||||
|
||||
|
||||
@@ -96,7 +96,8 @@ export type Literal =
|
||||
| NullLiteral
|
||||
| StringLiteral
|
||||
| BooleanLiteral
|
||||
| NumericLiteral;
|
||||
| NumericLiteral
|
||||
| BigIntLiteral;
|
||||
|
||||
export type RegExpLiteral = NodeBase & {
|
||||
type: "RegExpLiteral",
|
||||
|
||||
1
packages/babel-parser/test/fixtures/estree/bigInt/basic/input.js
vendored
Normal file
1
packages/babel-parser/test/fixtures/estree/bigInt/basic/input.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
const a = 1n;
|
||||
102
packages/babel-parser/test/fixtures/estree/bigInt/basic/output.json
vendored
Normal file
102
packages/babel-parser/test/fixtures/estree/bigInt/basic/output.json
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"type": "File",
|
||||
"start": 0,
|
||||
"end": 13,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 13
|
||||
}
|
||||
},
|
||||
"program": {
|
||||
"type": "Program",
|
||||
"start": 0,
|
||||
"end": 13,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 13
|
||||
}
|
||||
},
|
||||
"sourceType": "script",
|
||||
"interpreter": null,
|
||||
"body": [
|
||||
{
|
||||
"type": "VariableDeclaration",
|
||||
"start": 0,
|
||||
"end": 13,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 13
|
||||
}
|
||||
},
|
||||
"declarations": [
|
||||
{
|
||||
"type": "VariableDeclarator",
|
||||
"start": 6,
|
||||
"end": 12,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 12
|
||||
}
|
||||
},
|
||||
"id": {
|
||||
"type": "Identifier",
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 6
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 7
|
||||
},
|
||||
"identifierName": "a"
|
||||
},
|
||||
"name": "a"
|
||||
},
|
||||
"init": {
|
||||
"type": "Literal",
|
||||
"start": 10,
|
||||
"end": 12,
|
||||
"loc": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"column": 10
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"column": 12
|
||||
}
|
||||
},
|
||||
"value": "1",
|
||||
"raw": "1n",
|
||||
"bigint": "1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"kind": "const"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
6
packages/babel-parser/test/fixtures/estree/bigInt/options.json
vendored
Normal file
6
packages/babel-parser/test/fixtures/estree/bigInt/options.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
"estree",
|
||||
"bigInt"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"plugins": ["classProperties", ["decorators", { "decoratorsBeforeExport": false }]]
|
||||
"plugins": [
|
||||
"classProperties",
|
||||
["decorators", { "decoratorsBeforeExport": false }]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* global BigInt */
|
||||
|
||||
import { multiple as getFixtures } from "@babel/helper-fixtures";
|
||||
import { codeFrameColumns } from "@babel/code-frame";
|
||||
import fs from "fs";
|
||||
@@ -61,7 +63,7 @@ export function runFixtureTests(fixturesPath, parseFunction) {
|
||||
/^.*Got error message: /,
|
||||
"",
|
||||
);
|
||||
fs.writeFileSync(fn, JSON.stringify(task.options, null, " "));
|
||||
fs.writeFileSync(fn, JSON.stringify(task.options, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,19 +109,33 @@ export function runThrowTestsWithEstree(fixturesPath, parseFunction) {
|
||||
}
|
||||
|
||||
function save(test, ast) {
|
||||
// Ensure that RegExp and Errors are serialized as strings
|
||||
forceToString(RegExp, () =>
|
||||
forceToString(Error, () =>
|
||||
fs.writeFileSync(test.expect.loc, JSON.stringify(ast, null, " ")),
|
||||
),
|
||||
overrideToJSON(() =>
|
||||
fs.writeFileSync(test.expect.loc, JSON.stringify(ast, null, 2)),
|
||||
);
|
||||
}
|
||||
|
||||
function forceToString(obj, cb) {
|
||||
const { toJSON } = obj.prototype;
|
||||
obj.prototype.toJSON = obj.prototype.toString;
|
||||
// Ensure that RegExp, BigInt, and Errors are serialized as strings
|
||||
function overrideToJSON(cb) {
|
||||
const originalToJSONMap = new Map();
|
||||
const notJSONparseableObj = [RegExp, Error];
|
||||
|
||||
if (typeof BigInt !== "undefined") {
|
||||
notJSONparseableObj.push(BigInt);
|
||||
}
|
||||
|
||||
for (const obj of notJSONparseableObj) {
|
||||
const { toJSON } = obj.prototype;
|
||||
originalToJSONMap.set(obj, toJSON);
|
||||
obj.prototype.toJSON = function() {
|
||||
return this.toString();
|
||||
};
|
||||
}
|
||||
|
||||
cb();
|
||||
obj.prototype.toJSON = toJSON;
|
||||
|
||||
for (const obj of notJSONparseableObj) {
|
||||
obj.prototype.toJSON = originalToJSONMap.get(obj);
|
||||
}
|
||||
}
|
||||
|
||||
function runTest(test, parseFunction) {
|
||||
@@ -143,7 +159,7 @@ function runTest(test, parseFunction) {
|
||||
const fn = path.dirname(test.expect.loc) + "/options.json";
|
||||
test.options = test.options || {};
|
||||
test.options.throws = err.message;
|
||||
fs.writeFileSync(fn, JSON.stringify(test.options, null, " "));
|
||||
fs.writeFileSync(fn, JSON.stringify(test.options, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -172,11 +188,11 @@ function runTest(test, parseFunction) {
|
||||
const fn = path.dirname(test.expect.loc) + "/options.json";
|
||||
test.options = test.options || {};
|
||||
delete test.options.throws;
|
||||
const contents = JSON.stringify(test.options, null, " ");
|
||||
const contents = JSON.stringify(test.options, null, 2);
|
||||
if (contents === "{}") {
|
||||
fs.unlinkSync(fn);
|
||||
} else {
|
||||
fs.writeFileSync(fn, JSON.stringify(test.options, null, " "));
|
||||
fs.writeFileSync(fn, JSON.stringify(test.options, null, 2));
|
||||
}
|
||||
test.expect.loc += "on";
|
||||
return save(test, ast);
|
||||
@@ -198,7 +214,6 @@ function runTest(test, parseFunction) {
|
||||
}
|
||||
|
||||
function ppJSON(v) {
|
||||
v = v instanceof RegExp || v instanceof Error ? v.toString() : v;
|
||||
return JSON.stringify(v, null, 2);
|
||||
}
|
||||
|
||||
@@ -211,42 +226,49 @@ function addPath(str, pt) {
|
||||
}
|
||||
|
||||
function misMatch(exp, act) {
|
||||
if (
|
||||
exp instanceof RegExp ||
|
||||
act instanceof RegExp ||
|
||||
exp instanceof Error ||
|
||||
act instanceof Error
|
||||
) {
|
||||
const left = ppJSON(exp);
|
||||
const right = ppJSON(act);
|
||||
if (left !== right) return left + " !== " + right;
|
||||
} else if (Array.isArray(exp)) {
|
||||
if (!Array.isArray(act)) return ppJSON(exp) + " != " + ppJSON(act);
|
||||
if (act.length != exp.length) {
|
||||
return "array length mismatch " + exp.length + " != " + act.length;
|
||||
}
|
||||
for (let i = 0; i < act.length; ++i) {
|
||||
const mis = misMatch(exp[i], act[i]);
|
||||
if (mis) return addPath(mis, i);
|
||||
}
|
||||
} else if (!exp || !act || typeof exp != "object" || typeof act != "object") {
|
||||
if (exp !== act && typeof exp != "function") {
|
||||
return ppJSON(exp) + " !== " + ppJSON(act);
|
||||
}
|
||||
} else {
|
||||
for (const prop of Object.keys(exp)) {
|
||||
const mis = misMatch(exp[prop], act[prop]);
|
||||
if (mis) return addPath(mis, prop);
|
||||
}
|
||||
|
||||
for (const prop of Object.keys(act)) {
|
||||
if (typeof act[prop] === "function") {
|
||||
continue;
|
||||
overrideToJSON(() => {
|
||||
if (
|
||||
exp instanceof RegExp ||
|
||||
act instanceof RegExp ||
|
||||
exp instanceof Error ||
|
||||
act instanceof Error
|
||||
) {
|
||||
const left = ppJSON(exp);
|
||||
const right = ppJSON(act);
|
||||
if (left !== right) return left + " !== " + right;
|
||||
} else if (Array.isArray(exp)) {
|
||||
if (!Array.isArray(act)) return ppJSON(exp) + " != " + ppJSON(act);
|
||||
if (act.length != exp.length) {
|
||||
return "array length mismatch " + exp.length + " != " + act.length;
|
||||
}
|
||||
for (let i = 0; i < act.length; ++i) {
|
||||
const mis = misMatch(exp[i], act[i]);
|
||||
if (mis) return addPath(mis, i);
|
||||
}
|
||||
} else if (
|
||||
!exp ||
|
||||
!act ||
|
||||
typeof exp != "object" ||
|
||||
typeof act != "object"
|
||||
) {
|
||||
if (exp !== act && typeof exp != "function") {
|
||||
return ppJSON(exp) + " !== " + ppJSON(act);
|
||||
}
|
||||
} else {
|
||||
for (const prop of Object.keys(exp)) {
|
||||
const mis = misMatch(exp[prop], act[prop]);
|
||||
if (mis) return addPath(mis, prop);
|
||||
}
|
||||
|
||||
if (!(prop in exp) && act[prop] !== undefined) {
|
||||
return `Did not expect a property '${prop}'`;
|
||||
for (const prop of Object.keys(act)) {
|
||||
if (typeof act[prop] === "function") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(prop in exp) && act[prop] !== undefined) {
|
||||
return `Did not expect a property '${prop}'`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user