Skip to content

Commit

Permalink
Add ESLint 8 support to @babel/eslint-parser (#13782)
Browse files Browse the repository at this point in the history
* Add ESLint 8 support to `@babel/eslint-parser`

* Only read eslint version dynamically during dev

* Fix tests

* Update ESLint

* Use ESLint 8 stable

* Review

* Check

* Fix babel config
  • Loading branch information
nicolo-ribaudo committed Oct 28, 2021
1 parent ad59a2c commit 381277a
Show file tree
Hide file tree
Showing 8 changed files with 518 additions and 57 deletions.
36 changes: 36 additions & 0 deletions babel.config.js
Expand Up @@ -58,6 +58,7 @@ module.exports = function (api) {
let ignoreLib = true;
let includeRegeneratorRuntime = false;
let needsPolyfillsForOldNode = false;
let dynamicESLintVersionCheck = false;

let transformRuntimeOptions;

Expand Down Expand Up @@ -99,6 +100,8 @@ module.exports = function (api) {
needsPolyfillsForOldNode = true;
break;
case "test-legacy": // In test-legacy environment, we build babel on latest node but test on minimum supported legacy versions
dynamicESLintVersionCheck = true;
// fall through
case "production":
// Config during builds before publish.
targets = { node: nodeVersion };
Expand All @@ -107,10 +110,12 @@ module.exports = function (api) {
case "test":
targets = { node: "current" };
needsPolyfillsForOldNode = true;
dynamicESLintVersionCheck = true;
break;
case "development":
envOpts.debug = true;
targets = { node: "current" };
dynamicESLintVersionCheck = true;
break;
}

Expand Down Expand Up @@ -218,6 +223,10 @@ module.exports = function (api) {
exclude: /regenerator-runtime/,
plugins: [["@babel/transform-runtime", transformRuntimeOptions]],
},
dynamicESLintVersionCheck && {
test: ["./eslint/*/src"].map(normalize),
plugins: [pluginDynamicESLintVersionCheck],
},
].filter(Boolean),
};

Expand Down Expand Up @@ -654,3 +663,30 @@ function pluginBabelParserTokenType({
tokenTypesMapping.set(tokenTypesDefinition[i].key.name, i);
}
})();

// Transforms
// ESLINT_VERSION
// to
// process.env.ESLINT_VERSION_FOR_BABEL
// ? parseInt(process.env.ESLINT_VERSION_FOR_BABEL, 10)
// : ESLINT_VERSION
function pluginDynamicESLintVersionCheck({ template }) {
const transformed = new WeakSet();

return {
visitor: {
ReferencedIdentifier(path) {
if (path.node.name !== "ESLINT_VERSION") return;

if (transformed.has(path.node)) return;
transformed.add(path.node);

path.replaceWith(template.expression.ast`
process.env.ESLINT_VERSION_FOR_BABEL
? parseInt(process.env.ESLINT_VERSION_FOR_BABEL, 10)
: ${path.node}
`);
},
},
};
}
5 changes: 3 additions & 2 deletions eslint/babel-eslint-parser/package.json
Expand Up @@ -28,7 +28,7 @@
},
"peerDependencies": {
"@babel/core": ">=7.11.0",
"eslint": ">=7.5.0"
"eslint": "^7.5.0 || ^8.0.0"
},
"dependencies": {
"eslint-scope": "^5.1.1",
Expand All @@ -38,6 +38,7 @@
"devDependencies": {
"@babel/core": "workspace:^",
"dedent": "^0.7.0",
"eslint": "^7.27.0"
"eslint": "^7.27.0",
"eslint-8": "npm:eslint@^8.0.0"
}
}
21 changes: 20 additions & 1 deletion eslint/babel-eslint-parser/src/convert/convertAST.cjs
@@ -1,3 +1,5 @@
const ESLINT_VERSION = require("../utils/eslint-version.cjs");

function* it(children) {
if (Array.isArray(children)) yield* children;
else yield children;
Expand Down Expand Up @@ -40,7 +42,7 @@ const convertNodesVisitor = {
delete node.extra;
}

if (node?.loc.identifierName) {
if (node.loc.identifierName) {
delete node.loc.identifierName;
}

Expand Down Expand Up @@ -89,6 +91,15 @@ const convertNodesVisitor = {
} else {
q.loc.end.column += 2;
}

if (ESLINT_VERSION >= 8) {
q.start -= 1;
if (q.tail) {
q.end += 1;
} else {
q.end += 2;
}
}
}
}
},
Expand Down Expand Up @@ -117,6 +128,10 @@ function convertProgramNode(ast) {
ast.range[1] = lastToken.end;
ast.loc.end.line = lastToken.loc.end.line;
ast.loc.end.column = lastToken.loc.end.column;

if (ESLINT_VERSION >= 8) {
ast.end = lastToken.end;
}
}
}
} else {
Expand All @@ -129,6 +144,10 @@ function convertProgramNode(ast) {
if (ast.body && ast.body.length > 0) {
ast.loc.start.line = ast.body[0].loc.start.line;
ast.range[0] = ast.body[0].start;

if (ESLINT_VERSION >= 8) {
ast.start = ast.body[0].start;
}
}
}

Expand Down
45 changes: 40 additions & 5 deletions eslint/babel-eslint-parser/src/convert/convertTokens.cjs
@@ -1,3 +1,5 @@
const ESLINT_VERSION = require("../utils/eslint-version.cjs");

function convertTemplateType(tokens, tl) {
let curlyBrace = null;
let templateTokens = [];
Expand Down Expand Up @@ -190,12 +192,45 @@ function convertToken(token, source, tl) {
// Acorn does not have rightAssociative
delete token.type.rightAssociative;
}

return token;
}

module.exports = function convertTokens(tokens, code, tl) {
return convertTemplateType(tokens, tl)
.filter(t => t.type !== "CommentLine" && t.type !== "CommentBlock")
.map(t => convertToken(t, code, tl));
const result = [];

const withoutComments = convertTemplateType(tokens, tl).filter(
t => t.type !== "CommentLine" && t.type !== "CommentBlock",
);
for (let i = 0, { length } = withoutComments; i < length; i++) {
const token = withoutComments[i];

if (!process.env.BABEL_8_BREAKING) {
// Babel 8 already produces a single token

if (
ESLINT_VERSION >= 8 &&
i + 1 < length &&
token.type.label === tl.hash
) {
const nextToken = withoutComments[i + 1];

// We must disambiguate private identifier from the hack pipes topic token
if (nextToken.type.label === tl.name && token.end === nextToken.start) {
i++;

nextToken.type = "PrivateIdentifier";
nextToken.start -= 1;
nextToken.loc.start.column -= 1;
nextToken.range = [nextToken.start, nextToken.end];

result.push(nextToken);
continue;
}
}
}

convertToken(token, code, tl);
result.push(token);
}

return result;
};
1 change: 1 addition & 0 deletions eslint/babel-eslint-parser/src/utils/eslint-version.cjs
@@ -0,0 +1 @@
module.exports = parseInt(require("eslint/package.json").version, 10);
4 changes: 2 additions & 2 deletions eslint/babel-eslint-parser/src/worker/configuration.cjs
@@ -1,4 +1,5 @@
const babel = require("./babel-core.cjs");
const ESLINT_VERSION = require("../utils/eslint-version.cjs");

/**
* Merge user supplied estree plugin options to default estree plugin options
Expand All @@ -8,8 +9,7 @@ const babel = require("./babel-core.cjs");
*/
function getParserPlugins(babelOptions) {
const babelParserPlugins = babelOptions.parserOpts?.plugins ?? [];
// todo: enable classFeatures when it is supported by ESLint
const estreeOptions = { classFeatures: false };
const estreeOptions = { classFeatures: ESLINT_VERSION >= 8 };
for (const plugin of babelParserPlugins) {
if (Array.isArray(plugin) && plugin[0] === "estree") {
Object.assign(estreeOptions, plugin[1]);
Expand Down

0 comments on commit 381277a

Please sign in to comment.