diff --git a/eslint/babel-eslint-parser/src/convert/convertAST.js b/eslint/babel-eslint-parser/src/convert/convertAST.js index 7dfcd9a6df6f..b76cde36ea7e 100644 --- a/eslint/babel-eslint-parser/src/convert/convertAST.js +++ b/eslint/babel-eslint-parser/src/convert/convertAST.js @@ -1,14 +1,10 @@ import { types as t, traverse } from "@babel/core"; function convertNodes(ast, code) { - const state = { source: code }; const astTransformVisitor = { noScope: true, enter(path) { - const node = path.node; - - // private var to track original node type - node._babelType = node.type; + const { node } = path; if (node.innerComments) { delete node.innerComments; @@ -23,7 +19,15 @@ function convertNodes(ast, code) { } }, exit(path) { - const node = path.node; + const { node } = path; + + if (Object.hasOwnProperty.call(node, "extra")) { + delete node.extra; + } + + if (node.loc && Object.hasOwnProperty.call(node.loc, "identifierName")) { + delete node.loc.identifierName; + } if (path.isTypeParameter()) { node.type = "Identifier"; @@ -74,6 +78,7 @@ function convertNodes(ast, code) { } }, }; + const state = { source: code }; // Monkey patch visitor keys in order to be able to traverse the estree nodes t.VISITOR_KEYS.Property = t.VISITOR_KEYS.ObjectProperty; @@ -95,9 +100,9 @@ function convertNodes(ast, code) { function convertProgramNode(ast) { ast.type = "Program"; ast.sourceType = ast.program.sourceType; - ast.directives = ast.program.directives; ast.body = ast.program.body; delete ast.program; + delete ast.errors; if (ast.comments.length) { const lastComment = ast.comments[ast.comments.length - 1]; diff --git a/eslint/babel-eslint-parser/test/helpers/assert-implements-ast.js b/eslint/babel-eslint-parser/test/helpers/assert-implements-ast.js deleted file mode 100644 index 3c435940a4cf..000000000000 --- a/eslint/babel-eslint-parser/test/helpers/assert-implements-ast.js +++ /dev/null @@ -1,40 +0,0 @@ -// Checks if the source ast implements the target ast. Ignores extra keys on source ast -export default function assertImplementsAST(target, source, path) { - if (!path) { - path = []; - } - - function error(text) { - const err = new Error(`At ${path.join(".")}: ${text}:`); - err.depth = path.length + 1; - throw err; - } - - const typeA = target === null ? "null" : typeof target; - const typeB = source === null ? "null" : typeof source; - if (typeA !== typeB) { - error( - `have different types (${typeA} !== ${typeB}) (${target} !== ${source})`, - ); - } else if ( - typeA === "object" && - ["RegExp"].indexOf(target.constructor.name) !== -1 && - target.constructor.name !== source.constructor.name - ) { - error( - `object have different constructors (${target.constructor.name} !== ${source.constructor.name}`, - ); - } else if (typeA === "object") { - const keysTarget = Object.keys(target); - for (const i in keysTarget) { - const key = keysTarget[i]; - path.push(key); - assertImplementsAST(target[key], source[key], path); - path.pop(); - } - } else if (target !== source) { - error( - `are different (${JSON.stringify(target)} !== ${JSON.stringify(source)})`, - ); - } -} diff --git a/eslint/babel-eslint-parser/test/index.js b/eslint/babel-eslint-parser/test/index.js index a5158b4aa640..8d9c3880f18e 100644 --- a/eslint/babel-eslint-parser/test/index.js +++ b/eslint/babel-eslint-parser/test/index.js @@ -1,19 +1,41 @@ -import assert from "assert"; import espree from "espree"; import escope from "eslint-scope"; import unpad from "dedent"; import { parseForESLint } from "../src"; -import assertImplementsAST from "./helpers/assert-implements-ast"; -const babelOptions = { +const BABEL_OPTIONS = { configFile: require.resolve( "@babel/eslint-shared-fixtures/config/babel.config.js", ), }; +const ALLOWED_PROPERTIES = [ + "importKind", + "exportKind", + "variance", + "typeArguments", +]; + +function deeplyRemoveProperties(obj, props) { + for (const [k, v] of Object.entries(obj)) { + if (typeof v === "object") { + if (Array.isArray(v)) { + for (const el of v) { + if (el != null) deeplyRemoveProperties(el, props); + } + } + + if (props.includes(k)) delete obj[k]; + else v != null && deeplyRemoveProperties(v, props); + continue; + } + + if (props.includes(k)) delete obj[k]; + } +} function parseAndAssertSame(code) { code = unpad(code); - const esAST = espree.parse(code, { + const espreeAST = espree.parse(code, { ecmaFeatures: { // enable JSX parsing jsx: true, @@ -31,21 +53,22 @@ function parseAndAssertSame(code) { ecmaVersion: 2020, sourceType: "module", }); - const babylonAST = parseForESLint(code, { + const babelAST = parseForESLint(code, { eslintVisitorKeys: true, eslintScopeManager: true, - babelOptions, + babelOptions: BABEL_OPTIONS, }).ast; - assertImplementsAST(esAST, babylonAST); + deeplyRemoveProperties(babelAST, ALLOWED_PROPERTIES); + expect(babelAST).toEqual(espreeAST); } -describe("babylon-to-espree", () => { +describe("Babel and Espree", () => { describe("compatibility", () => { it("should allow ast.analyze to be called without options", function() { const esAST = parseForESLint("`test`", { eslintScopeManager: true, eslintVisitorKeys: true, - babelOptions, + babelOptions: BABEL_OPTIONS, }).ast; expect(() => { escope.analyze(esAST); @@ -244,9 +267,9 @@ describe("babylon-to-espree", () => { const babylonAST = parseForESLint(code, { eslintVisitorKeys: true, eslintScopeManager: true, - babelOptions, + babelOptions: BABEL_OPTIONS, }).ast; - assert.strictEqual(babylonAST.tokens[1].type, "Punctuator"); + expect(babylonAST.tokens[1].type).toEqual("Punctuator"); }); // Espree doesn't support the nullish coalescing operator yet @@ -255,9 +278,9 @@ describe("babylon-to-espree", () => { const babylonAST = parseForESLint(code, { eslintVisitorKeys: true, eslintScopeManager: true, - babelOptions, + babelOptions: BABEL_OPTIONS, }).ast; - assert.strictEqual(babylonAST.tokens[1].type, "Punctuator"); + expect(babylonAST.tokens[1].type).toEqual("Punctuator"); }); // Espree doesn't support the pipeline operator yet @@ -266,9 +289,9 @@ describe("babylon-to-espree", () => { const babylonAST = parseForESLint(code, { eslintVisitorKeys: true, eslintScopeManager: true, - babelOptions, + babelOptions: BABEL_OPTIONS, }).ast; - assert.strictEqual(babylonAST.tokens[1].type, "Punctuator"); + expect(babylonAST.tokens[1].type).toEqual("Punctuator"); }); // Espree doesn't support private fields yet @@ -277,10 +300,10 @@ describe("babylon-to-espree", () => { const babylonAST = parseForESLint(code, { eslintVisitorKeys: true, eslintScopeManager: true, - babelOptions, + babelOptions: BABEL_OPTIONS, }).ast; - assert.strictEqual(babylonAST.tokens[3].type, "Punctuator"); - assert.strictEqual(babylonAST.tokens[3].value, "#"); + expect(babylonAST.tokens[3].type).toEqual("Punctuator"); + expect(babylonAST.tokens[3].value).toEqual("#"); }); // eslint-disable-next-line jest/no-disabled-tests @@ -448,9 +471,13 @@ describe("babylon-to-espree", () => { }); it("do not allow import export everywhere", () => { - assert.throws(() => { + expect(() => { parseAndAssertSame('function F() { import a from "a"; }'); - }, /SyntaxError: 'import' and 'export' may only appear at the top level/); + }).toThrow( + new SyntaxError( + "'import' and 'export' may only appear at the top level", + ), + ); }); it("return outside function", () => { @@ -458,9 +485,9 @@ describe("babylon-to-espree", () => { }); it("super outside method", () => { - assert.throws(() => { + expect(() => { parseAndAssertSame("function F() { super(); }"); - }, /SyntaxError: 'super' keyword outside a method/); + }).toThrow(new SyntaxError("'super' keyword outside a method")); }); it("StringLiteral", () => {