From 86a524250dcc7c32225f2880ec66767a06c6258d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=AF=E7=84=B6?= Date: Tue, 24 Dec 2024 17:03:32 +0800 Subject: [PATCH] refactor: eslint v8 compat (#397) --- lib/rules/callback-return.js | 3 +- lib/rules/exports-style.js | 7 +-- lib/rules/global-require.js | 18 +++----- lib/rules/handle-callback-err.js | 5 ++- lib/rules/hashbang.js | 3 +- lib/rules/no-deprecated-api.js | 6 +-- lib/rules/no-exports-assign.js | 5 +-- lib/rules/no-path-concat.js | 3 +- .../no-unsupported-features/es-syntax.js | 4 +- lib/rules/prefer-promises/dns.js | 6 +-- lib/rules/prefer-promises/fs.js | 6 +-- lib/util/check-prefer-global.js | 10 ++--- lib/util/check-unsupported-builtins.js | 4 +- lib/util/eslint-compat.js | 43 +++++++++++++++++++ lib/util/get-tsconfig.js | 8 +--- lib/util/is-typescript.js | 6 +-- lib/util/visit-import.js | 6 +-- lib/util/visit-require.js | 12 ++---- 18 files changed, 90 insertions(+), 65 deletions(-) create mode 100644 lib/util/eslint-compat.js diff --git a/lib/rules/callback-return.js b/lib/rules/callback-return.js index 5ae1c65a..2c9e3819 100644 --- a/lib/rules/callback-return.js +++ b/lib/rules/callback-return.js @@ -3,6 +3,7 @@ * See LICENSE file in root directory for full license. */ "use strict" +const { getSourceCode } = require("../util/eslint-compat") /** @type {import('eslint').Rule.RuleModule} */ module.exports = { @@ -27,7 +28,7 @@ module.exports = { create(context) { const callbacks = context.options[0] || ["callback", "cb", "next"] - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 + const sourceCode = getSourceCode(context) /** * Find the closest parent matching a list of types. diff --git a/lib/rules/exports-style.js b/lib/rules/exports-style.js index 49809db9..5a71b573 100644 --- a/lib/rules/exports-style.js +++ b/lib/rules/exports-style.js @@ -5,6 +5,7 @@ "use strict" const { hasParentNode } = require("../util/has-parent-node.js") +const { getSourceCode, getScope } = require("../util/eslint-compat") /*istanbul ignore next */ /** @@ -302,7 +303,7 @@ module.exports = { const batchAssignAllowed = Boolean( context.options[1] != null && context.options[1].allowBatchAssign ) - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 + const sourceCode = getSourceCode(context) /** * Gets the location info of reports. @@ -426,8 +427,8 @@ module.exports = { } return { - "Program:exit"(node) { - const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + "Program:exit"() { + const scope = getScope(context) switch (mode) { case "module.exports": diff --git a/lib/rules/global-require.js b/lib/rules/global-require.js index 988ddac7..ed4a7758 100644 --- a/lib/rules/global-require.js +++ b/lib/rules/global-require.js @@ -4,6 +4,8 @@ */ "use strict" +const { getScope, getAncestors } = require("../util/eslint-compat") + const ACCEPTABLE_PARENTS = [ "AssignmentExpression", "VariableDeclarator", @@ -59,26 +61,18 @@ module.exports = { }, create(context) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - return { CallExpression(node) { - const currentScope = - sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + const currentScope = getScope(context, node) if ( node.callee.type === "Identifier" && node.callee.name === "require" && !isShadowed(currentScope, node.callee) ) { - const isGoodRequire = ( - sourceCode.getAncestors?.(node) ?? - context.getAncestors() - ) // TODO: remove context.getAncestors() when dropping support for ESLint < v9 - .every( - parent => - ACCEPTABLE_PARENTS.indexOf(parent.type) > -1 - ) + const isGoodRequire = getAncestors(context, node).every( + parent => ACCEPTABLE_PARENTS.indexOf(parent.type) > -1 + ) if (!isGoodRequire) { context.report({ node, messageId: "unexpected" }) diff --git a/lib/rules/handle-callback-err.js b/lib/rules/handle-callback-err.js index 313659ed..6f7127a5 100644 --- a/lib/rules/handle-callback-err.js +++ b/lib/rules/handle-callback-err.js @@ -4,6 +4,8 @@ */ "use strict" +const { getScope } = require("../util/eslint-compat") + /** @type {import('eslint').Rule.RuleModule} */ module.exports = { meta: { @@ -25,7 +27,6 @@ module.exports = { }, create(context) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 const errorArgument = context.options[0] || "err" /** @@ -71,7 +72,7 @@ module.exports = { * @returns {void} */ function checkForError(node) { - const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + const scope = getScope(context, node) const parameters = getParameters(scope) const firstParameter = parameters[0] diff --git a/lib/rules/hashbang.js b/lib/rules/hashbang.js index 3b9a0fa1..786287dc 100644 --- a/lib/rules/hashbang.js +++ b/lib/rules/hashbang.js @@ -11,6 +11,7 @@ const getConvertPath = require("../util/get-convert-path") const { getPackageJson } = require("../util/get-package-json") const getNpmignore = require("../util/get-npmignore") const { isBinFile } = require("../util/is-bin-file") +const { getSourceCode } = require("../util/eslint-compat") const ENV_SHEBANG = "#!/usr/bin/env" const NODE_SHEBANG = `${ENV_SHEBANG} {{executableName}}\n` @@ -119,7 +120,7 @@ module.exports = { }, }, create(context) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 + const sourceCode = getSourceCode(context) const filePath = context.filename ?? context.getFilename() if (filePath === "") { return {} diff --git a/lib/rules/no-deprecated-api.js b/lib/rules/no-deprecated-api.js index 53a5d78e..a875c8d2 100644 --- a/lib/rules/no-deprecated-api.js +++ b/lib/rules/no-deprecated-api.js @@ -15,6 +15,7 @@ const getConfiguredNodeVersion = require("../util/get-configured-node-version") const getSemverRange = require("../util/get-semver-range") const extendTrackmapWithNodePrefix = require("../util/extend-trackmap-with-node-prefix") const unprefixNodeColon = require("../util/unprefix-node-colon") +const { getScope } = require("../util/eslint-compat") /** @typedef {import('../unsupported-features/types.js').DeprecatedInfo} DeprecatedInfo */ /** @@ -820,10 +821,9 @@ module.exports = { }) } - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 return { - "Program:exit"(node) { - const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + "Program:exit"() { + const scope = getScope(context) const tracker = new ReferenceTracker(scope, { mode: "legacy", diff --git a/lib/rules/no-exports-assign.js b/lib/rules/no-exports-assign.js index fcfa9453..6811f704 100644 --- a/lib/rules/no-exports-assign.js +++ b/lib/rules/no-exports-assign.js @@ -5,6 +5,7 @@ "use strict" const { findVariable } = require("@eslint-community/eslint-utils") +const { getScope } = require("../util/eslint-compat") /** * @param {import('estree').Node} node @@ -61,11 +62,9 @@ module.exports = { type: "problem", }, create(context) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - return { AssignmentExpression(node) { - const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + const scope = getScope(context) if ( !isExports(node.left, scope) || diff --git a/lib/rules/no-path-concat.js b/lib/rules/no-path-concat.js index da90aa68..0fb08561 100644 --- a/lib/rules/no-path-concat.js +++ b/lib/rules/no-path-concat.js @@ -11,6 +11,7 @@ const { getStringIfConstant, } = require("@eslint-community/eslint-utils") const { hasParentNode } = require("../util/has-parent-node.js") +const { getSourceCode } = require("../util/eslint-compat") /** * Get the first char of the specified template element. @@ -195,7 +196,7 @@ module.exports = { create(context) { return { "Program:exit"(programNode) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 + const sourceCode = getSourceCode(context) const globalScope = sourceCode.getScope?.(programNode) ?? context.getScope() const tracker = new ReferenceTracker(globalScope) diff --git a/lib/rules/no-unsupported-features/es-syntax.js b/lib/rules/no-unsupported-features/es-syntax.js index b5987cfb..bc994e1e 100644 --- a/lib/rules/no-unsupported-features/es-syntax.js +++ b/lib/rules/no-unsupported-features/es-syntax.js @@ -10,6 +10,7 @@ const rangeSubset = require("semver/ranges/subset") const getConfiguredNodeVersion = require("../../util/get-configured-node-version") const getSemverRange = require("../../util/get-semver-range") const mergeVisitorsInPlace = require("../../util/merge-visitors-in-place") +const { getScope } = require("../../util/eslint-compat") /** @type {Record} */ const features = require("./es-syntax.json") @@ -113,8 +114,7 @@ function normalizeScope(initialScope, node) { * @returns {boolean} */ function isStrict(context, node) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + const scope = getScope(context) return normalizeScope(scope, node).isStrict } diff --git a/lib/rules/prefer-promises/dns.js b/lib/rules/prefer-promises/dns.js index 9b151720..abdf937b 100644 --- a/lib/rules/prefer-promises/dns.js +++ b/lib/rules/prefer-promises/dns.js @@ -9,6 +9,7 @@ const { CONSTRUCT, ReferenceTracker, } = require("@eslint-community/eslint-utils") +const { getScope } = require("../../util/eslint-compat") /** @type {import('@eslint-community/eslint-utils').TraceMap} */ const dns = { @@ -57,9 +58,8 @@ module.exports = { create(context) { return { - "Program:exit"(node) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + "Program:exit"() { + const scope = getScope(context) const tracker = new ReferenceTracker(scope, { mode: "legacy" }) const references = [ ...tracker.iterateCjsReferences(traceMap), diff --git a/lib/rules/prefer-promises/fs.js b/lib/rules/prefer-promises/fs.js index 0a005470..e9e135b1 100644 --- a/lib/rules/prefer-promises/fs.js +++ b/lib/rules/prefer-promises/fs.js @@ -5,6 +5,7 @@ "use strict" const { CALL, ReferenceTracker } = require("@eslint-community/eslint-utils") +const { getScope } = require("../../util/eslint-compat") /** @type {import('@eslint-community/eslint-utils').TraceMap} */ const traceMap = { @@ -55,9 +56,8 @@ module.exports = { create(context) { return { - "Program:exit"(node) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - const scope = sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + "Program:exit"() { + const scope = getScope(context) const tracker = new ReferenceTracker(scope, { mode: "legacy" }) const references = [ ...tracker.iterateCjsReferences(traceMap), diff --git a/lib/util/check-prefer-global.js b/lib/util/check-prefer-global.js index ca685de8..fa058469 100644 --- a/lib/util/check-prefer-global.js +++ b/lib/util/check-prefer-global.js @@ -5,7 +5,7 @@ "use strict" const { ReferenceTracker } = require("@eslint-community/eslint-utils") - +const { getScope } = require("../util/eslint-compat") /** * @typedef TraceMap * @property {import('@eslint-community/eslint-utils').TraceMap} globals @@ -36,9 +36,7 @@ class Verifier { */ verifyToPreferGlobals() { const { context, traceMap } = this - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - const scope = - sourceCode.getScope?.(sourceCode.ast) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + const scope = getScope(context) const tracker = new ReferenceTracker(scope, { mode: "legacy", }) @@ -57,9 +55,7 @@ class Verifier { */ verifyToPreferModules() { const { context, traceMap } = this - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - const scope = - sourceCode.getScope?.(sourceCode.ast) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + const scope = getScope(context) const tracker = new ReferenceTracker(scope) for (const { node } of tracker.iterateGlobalReferences( diff --git a/lib/util/check-unsupported-builtins.js b/lib/util/check-unsupported-builtins.js index 0412fd03..8096edb5 100644 --- a/lib/util/check-unsupported-builtins.js +++ b/lib/util/check-unsupported-builtins.js @@ -10,6 +10,7 @@ const getConfiguredNodeVersion = require("./get-configured-node-version") const getSemverRange = require("./get-semver-range") const unprefixNodeColon = require("./unprefix-node-colon") const semverRangeSubset = require("semver/ranges/subset") +const { getScope } = require("../util/eslint-compat") /** * Parses the options. @@ -86,8 +87,7 @@ module.exports.checkUnsupportedBuiltins = function checkUnsupportedBuiltins( traceMap ) { const options = parseOptions(context) - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - const scope = sourceCode.getScope?.(sourceCode.ast) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 + const scope = getScope(context) const tracker = new ReferenceTracker(scope, { mode: "legacy" }) const references = [ ...tracker.iterateCjsReferences(traceMap.modules ?? {}), diff --git a/lib/util/eslint-compat.js b/lib/util/eslint-compat.js new file mode 100644 index 00000000..cbba37a4 --- /dev/null +++ b/lib/util/eslint-compat.js @@ -0,0 +1,43 @@ +/** + * @fileoverview Utilities for eslint compatibility. + * @see https://eslint.org/docs/latest/use/migrate-to-9.0.0#removed-context-methods + * @author aladdin-add + */ +"use strict" + +/** @import { Rule } from 'eslint' */ +/** @typedef {import('estree').Node} Node */ + +exports.getSourceCode = function (/** @type Rule.RuleContext */ context) { + return context.sourceCode || context.getSourceCode() +} + +exports.getScope = function ( + /** @type {Rule.RuleContext} */ context, + /** @type {Node} */ node +) { + const sourceCode = exports.getSourceCode(context) + return sourceCode.getScope?.(node || sourceCode.ast) || context.getScope() +} + +exports.getAncestors = function ( + /** @type {Rule.RuleContext} */ context, + /** @type {Node} */ node +) { + const sourceCode = exports.getSourceCode(context) + return sourceCode.getAncestors?.(node) || context.getAncestors() +} + +exports.getCwd = function (/** @type {Rule.RuleContext} */ context) { + return context.cwd || context.getCwd() +} + +exports.getPhysicalFilename = function ( + /** @type {Rule.RuleContext} */ context +) { + return context.physicalFilename || context.getPhysicalFilename?.() +} + +exports.getFilename = function (/** @type {Rule.RuleContext} */ context) { + return context.filename || context.getFilename?.() +} diff --git a/lib/util/get-tsconfig.js b/lib/util/get-tsconfig.js index 8bb6b5f2..802470e6 100644 --- a/lib/util/get-tsconfig.js +++ b/lib/util/get-tsconfig.js @@ -1,6 +1,7 @@ "use strict" const { getTsconfig, parseTsconfig } = require("get-tsconfig") +const { getPhysicalFilename, getFilename } = require("./eslint-compat") const fsCache = new Map() /** @@ -30,12 +31,7 @@ function getTSConfigForFile(filename) { * @returns {import("get-tsconfig").TsConfigResult | null} */ function getTSConfigForContext(context) { - // TODO: remove context.get(PhysicalFilename|Filename) when dropping eslint < v10 - const filename = - context.physicalFilename ?? - context.getPhysicalFilename?.() ?? - context.filename ?? - context.getFilename?.() + const filename = getPhysicalFilename(context) ?? getFilename(context) return getTSConfigForFile(filename) } diff --git a/lib/util/is-typescript.js b/lib/util/is-typescript.js index 80163066..3c7065fe 100644 --- a/lib/util/is-typescript.js +++ b/lib/util/is-typescript.js @@ -1,6 +1,7 @@ "use strict" const path = require("path") +const { getPhysicalFilename, getFilename } = require("./eslint-compat") const typescriptExtensions = [".ts", ".tsx", ".cts", ".mts"] @@ -12,10 +13,7 @@ const typescriptExtensions = [".ts", ".tsx", ".cts", ".mts"] */ module.exports = function isTypescript(context) { const sourceFileExt = path.extname( - context.physicalFilename ?? - context.getPhysicalFilename?.() ?? - context.filename ?? - context.getFilename?.() + getPhysicalFilename(context) ?? getFilename(context) ) return typescriptExtensions.includes(sourceFileExt) } diff --git a/lib/util/visit-import.js b/lib/util/visit-import.js index aee0a194..3b76072f 100644 --- a/lib/util/visit-import.js +++ b/lib/util/visit-import.js @@ -11,7 +11,7 @@ const getResolverConfig = require("./get-resolver-config") const getTryExtensions = require("./get-try-extensions") const ImportTarget = require("./import-target") const stripImportPathParams = require("./strip-import-path-params") - +const { getFilename } = require("./eslint-compat") /** @typedef {import('@typescript-eslint/typescript-estree').TSESTree.ImportDeclaration} ImportDeclaration */ /** @@ -38,9 +38,7 @@ module.exports = function visitImport( ) { /** @type {import('./import-target.js')[]} */ const targets = [] - const basedir = path.dirname( - path.resolve(context.filename ?? context.getFilename()) - ) + const basedir = path.dirname(path.resolve(getFilename(context))) const paths = getResolvePaths(context, optionIndex) const resolverConfig = getResolverConfig(context, optionIndex) const extensions = getTryExtensions(context, optionIndex) diff --git a/lib/util/visit-require.js b/lib/util/visit-require.js index ba4026f4..e5711885 100644 --- a/lib/util/visit-require.js +++ b/lib/util/visit-require.js @@ -16,6 +16,7 @@ const getResolverConfig = require("./get-resolver-config") const getTryExtensions = require("./get-try-extensions") const ImportTarget = require("./import-target") const stripImportPathParams = require("./strip-import-path-params") +const { getScope, getFilename } = require("../util/eslint-compat") /** * @typedef VisitRequireOptions @@ -39,20 +40,15 @@ module.exports = function visitRequire( ) { /** @type {import('./import-target.js')[]} */ const targets = [] - const basedir = path.dirname( - path.resolve(context.filename ?? context.getFilename()) - ) + const basedir = path.dirname(path.resolve(getFilename(context))) const paths = getResolvePaths(context) const resolverConfig = getResolverConfig(context) const extensions = getTryExtensions(context) const options = { basedir, paths, extensions, resolverConfig } return { - "Program:exit"(node) { - const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 - const tracker = new ReferenceTracker( - sourceCode.getScope?.(node) ?? context.getScope() //TODO: remove context.getScope() when dropping support for ESLint < v9 - ) + "Program:exit"() { + const tracker = new ReferenceTracker(getScope(context)) const references = tracker.iterateGlobalReferences({ require: { [CALL]: true,