@rushstack/eslint-patch
Advanced tools
Comparing version
// This is a workaround for https://github.com/eslint/eslint/issues/3458 | ||
require('@rushstack/heft-node-rig/profiles/default/includes/eslint/patch/modern-module-resolution'); | ||
require('decoupled-local-node-rig/profiles/default/includes/eslint/patch/modern-module-resolution'); | ||
// This is a workaround for https://github.com/microsoft/rushstack/issues/3021 | ||
require('@rushstack/heft-node-rig/profiles/default/includes/eslint/patch/custom-config-package-names'); | ||
require('decoupled-local-node-rig/profiles/default/includes/eslint/patch/custom-config-package-names'); | ||
module.exports = { | ||
extends: [ | ||
'@rushstack/heft-node-rig/profiles/default/includes/eslint/profile/node', | ||
'@rushstack/heft-node-rig/profiles/default/includes/eslint/mixins/friendly-locals', | ||
'@rushstack/heft-node-rig/profiles/default/includes/eslint/mixins/tsdoc' | ||
'decoupled-local-node-rig/profiles/default/includes/eslint/profile/node', | ||
'decoupled-local-node-rig/profiles/default/includes/eslint/mixins/friendly-locals', | ||
'decoupled-local-node-rig/profiles/default/includes/eslint/mixins/tsdoc' | ||
], | ||
parserOptions: { tsconfigRootDir: __dirname }, | ||
plugins: ['eslint-plugin-header'], | ||
overrides: [ | ||
@@ -18,10 +18,3 @@ { | ||
rules: { | ||
'header/header': [ | ||
'warn', | ||
'line', | ||
[ | ||
' Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.', | ||
' See LICENSE in the project root for license information.' | ||
] | ||
] | ||
'no-console': 'off' | ||
} | ||
@@ -28,0 +21,0 @@ } |
@@ -5,2 +5,14 @@ { | ||
{ | ||
"version": "1.12.0", | ||
"tag": "@rushstack/eslint-patch_v1.12.0", | ||
"date": "Thu, 26 Jun 2025 18:57:04 GMT", | ||
"comments": { | ||
"minor": [ | ||
{ | ||
"comment": "Update for compatibility with ESLint 9" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"version": "1.11.0", | ||
@@ -7,0 +19,0 @@ "tag": "@rushstack/eslint-patch_v1.11.0", |
# Change Log - @rushstack/eslint-patch | ||
This log was last generated on Tue, 11 Mar 2025 02:12:33 GMT and should not be manually modified. | ||
This log was last generated on Thu, 26 Jun 2025 18:57:04 GMT and should not be manually modified. | ||
## 1.12.0 | ||
Thu, 26 Jun 2025 18:57:04 GMT | ||
### Minor changes | ||
- Update for compatibility with ESLint 9 | ||
## 1.11.0 | ||
@@ -6,0 +13,0 @@ Tue, 11 Mar 2025 02:12:33 GMT |
@@ -18,2 +18,38 @@ "use strict"; | ||
exports.isModuleResolutionError = isModuleResolutionError; | ||
const FLAT_CONFIG_REGEX = /eslint\.config\.(cjs|mjs|js)$/i; | ||
// Ex: | ||
// at async ESLint.lintFiles (C:\\path\\to\\\\eslint\\lib\\eslint\\eslint.js:720:21) | ||
const NODE_STACK_REGEX = /^\s*at (?:((?:\[object object\])?[^\\/]+(?: \[as \S+\])?) )?\(?(.*?)(?::(\d+)| (\d+))(?::(\d+))?\)?\s*$/i; | ||
function parseNodeStack(stack) { | ||
const stackTraceMatch = NODE_STACK_REGEX.exec(stack); | ||
if (!stackTraceMatch) { | ||
return undefined; | ||
} | ||
return { | ||
file: stackTraceMatch[2], | ||
method: stackTraceMatch[1], | ||
lineNumber: parseInt(stackTraceMatch[3], 10), | ||
column: stackTraceMatch[4] ? parseInt(stackTraceMatch[4], 10) : undefined | ||
}; | ||
} | ||
function getStackTrace() { | ||
const stackObj = {}; | ||
const originalStackTraceLimit = Error.stackTraceLimit; | ||
Error.stackTraceLimit = Infinity; | ||
Error.captureStackTrace(stackObj, getStackTrace); | ||
Error.stackTraceLimit = originalStackTraceLimit; | ||
if (!stackObj.stack) { | ||
throw new Error('Unable to capture stack trace'); | ||
} | ||
const { stack } = stackObj; | ||
const stackLines = stack.split('\n'); | ||
const frames = []; | ||
for (const line of stackLines) { | ||
const frame = parseNodeStack(line); | ||
if (frame) { | ||
frames.push(frame); | ||
} | ||
} | ||
return frames; | ||
} | ||
// Module path for eslintrc.cjs | ||
@@ -35,15 +71,79 @@ // Example: ".../@eslint/eslintrc/dist/eslintrc.cjs" | ||
exports.eslintFolder = eslintFolder; | ||
// Probe for the ESLint >=8.0.0 layout: | ||
// Probe for the ESLint >=9.0.0 flat config layout: | ||
for (let currentModule = module;;) { | ||
if (!eslintrcBundlePath) { | ||
if (currentModule.filename.endsWith('eslintrc.cjs')) { | ||
// For ESLint >=8.0.0, all @eslint/eslintrc code is bundled at this path: | ||
// .../@eslint/eslintrc/dist/eslintrc.cjs | ||
if (FLAT_CONFIG_REGEX.test(currentModule.filename)) { | ||
// Obtain the stack trace of the current module, since the | ||
// parent module of a flat config is undefined. From the | ||
// stack trace, we can find the ESLint folder. | ||
const stackTrace = getStackTrace(); | ||
const targetFrame = stackTrace.find((frame) => frame.file && frame.file.endsWith('eslint.js')); | ||
if (targetFrame) { | ||
// Walk up the path and continuously attempt to resolve the ESLint folder | ||
let currentPath = targetFrame.file; | ||
while (currentPath) { | ||
const potentialPath = path_1.default.dirname(currentPath); | ||
if (potentialPath === currentPath) { | ||
break; | ||
} | ||
currentPath = potentialPath; | ||
try { | ||
exports.eslintFolder = eslintFolder = path_1.default.dirname(require.resolve('eslint/package.json', { paths: [currentPath] })); | ||
break; | ||
} | ||
catch (ex) { | ||
if (!isModuleResolutionError(ex)) { | ||
throw ex; | ||
} | ||
} | ||
} | ||
} | ||
if (eslintFolder) { | ||
const eslintrcFolderPath = path_1.default.dirname(require.resolve('@eslint/eslintrc/package.json', { paths: [eslintFolder] })); | ||
eslintrcBundlePath = path_1.default.join(eslintrcFolderPath, 'dist/eslintrc.cjs'); | ||
} | ||
break; | ||
} | ||
if (!currentModule.parent) { | ||
break; | ||
} | ||
currentModule = currentModule.parent; | ||
} | ||
if (!eslintFolder) { | ||
// Probe for the ESLint >=8.0.0 layout: | ||
for (let currentModule = module;;) { | ||
if (!eslintrcBundlePath) { | ||
if (currentModule.filename.endsWith('eslintrc.cjs')) { | ||
// For ESLint >=8.0.0, all @eslint/eslintrc code is bundled at this path: | ||
// .../@eslint/eslintrc/dist/eslintrc.cjs | ||
try { | ||
const eslintrcFolderPath = path_1.default.dirname(require.resolve('@eslint/eslintrc/package.json', { paths: [currentModule.path] })); | ||
// Make sure we actually resolved the module in our call path | ||
// and not some other spurious dependency. | ||
const resolvedEslintrcBundlePath = path_1.default.join(eslintrcFolderPath, 'dist/eslintrc.cjs'); | ||
if (resolvedEslintrcBundlePath === currentModule.filename) { | ||
eslintrcBundlePath = resolvedEslintrcBundlePath; | ||
} | ||
} | ||
catch (ex) { | ||
// Module resolution failures are expected, as we're walking | ||
// up our require stack to look for eslint. All other errors | ||
// are re-thrown. | ||
if (!isModuleResolutionError(ex)) { | ||
throw ex; | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
// Next look for a file in ESLint's folder | ||
// .../eslint/lib/cli-engine/cli-engine.js | ||
try { | ||
const eslintrcFolderPath = path_1.default.dirname(require.resolve('@eslint/eslintrc/package.json', { paths: [currentModule.path] })); | ||
const eslintCandidateFolder = path_1.default.dirname(require.resolve('eslint/package.json', { | ||
paths: [currentModule.path] | ||
})); | ||
// Make sure we actually resolved the module in our call path | ||
// and not some other spurious dependency. | ||
const resolvedEslintrcBundlePath = path_1.default.join(eslintrcFolderPath, 'dist/eslintrc.cjs'); | ||
if (resolvedEslintrcBundlePath === currentModule.filename) { | ||
eslintrcBundlePath = resolvedEslintrcBundlePath; | ||
if (currentModule.filename.startsWith(eslintCandidateFolder + path_1.default.sep)) { | ||
exports.eslintFolder = eslintFolder = eslintCandidateFolder; | ||
break; | ||
} | ||
@@ -60,30 +160,7 @@ } | ||
} | ||
} | ||
else { | ||
// Next look for a file in ESLint's folder | ||
// .../eslint/lib/cli-engine/cli-engine.js | ||
try { | ||
const eslintCandidateFolder = path_1.default.dirname(require.resolve('eslint/package.json', { | ||
paths: [currentModule.path] | ||
})); | ||
// Make sure we actually resolved the module in our call path | ||
// and not some other spurious dependency. | ||
if (currentModule.filename.startsWith(eslintCandidateFolder + path_1.default.sep)) { | ||
exports.eslintFolder = eslintFolder = eslintCandidateFolder; | ||
break; | ||
} | ||
if (!currentModule.parent) { | ||
break; | ||
} | ||
catch (ex) { | ||
// Module resolution failures are expected, as we're walking | ||
// up our require stack to look for eslint. All other errors | ||
// are re-thrown. | ||
if (!isModuleResolutionError(ex)) { | ||
throw ex; | ||
} | ||
} | ||
currentModule = currentModule.parent; | ||
} | ||
if (!currentModule.parent) { | ||
break; | ||
} | ||
currentModule = currentModule.parent; | ||
} | ||
@@ -194,6 +271,6 @@ if (!eslintFolder) { | ||
let configArrayFactory; | ||
if (ESLINT_MAJOR_VERSION >= 8) { | ||
if (ESLINT_MAJOR_VERSION >= 8 && eslintrcBundlePath) { | ||
exports.configArrayFactory = configArrayFactory = require(eslintrcBundlePath).Legacy.ConfigArrayFactory; | ||
} | ||
else { | ||
else if (configArrayFactoryPath) { | ||
exports.configArrayFactory = configArrayFactory = require(configArrayFactoryPath).ConfigArrayFactory; | ||
@@ -205,7 +282,7 @@ } | ||
let Naming; | ||
if (ESLINT_MAJOR_VERSION >= 8) { | ||
if (ESLINT_MAJOR_VERSION >= 8 && eslintrcBundlePath) { | ||
exports.ModuleResolver = ModuleResolver = require(eslintrcBundlePath).Legacy.ModuleResolver; | ||
exports.Naming = Naming = require(eslintrcBundlePath).Legacy.naming; | ||
} | ||
else { | ||
else if (moduleResolverPath && namingPath) { | ||
exports.ModuleResolver = ModuleResolver = require(moduleResolverPath); | ||
@@ -212,0 +289,0 @@ exports.Naming = Naming = require(namingPath); |
@@ -15,7 +15,7 @@ export interface ISuppression { | ||
} | ||
export declare function getSuppressionsConfigForEslintrcFolderPath(eslintrcFolderPath: string): IBulkSuppressionsConfig; | ||
export declare function getAllBulkSuppressionsConfigsByEslintrcFolderPath(): [string, IBulkSuppressionsConfig][]; | ||
export declare function writeSuppressionsJsonToFile(eslintrcFolderPath: string, suppressionsConfig: IBulkSuppressionsConfig): void; | ||
export declare function deleteBulkSuppressionsFileInEslintrcFolder(eslintrcFolderPath: string): void; | ||
export declare function getSuppressionsConfigForEslintConfigFolderPath(eslintConfigFolderPath: string): IBulkSuppressionsConfig; | ||
export declare function getAllBulkSuppressionsConfigsByEslintConfigFolderPath(): [string, IBulkSuppressionsConfig][]; | ||
export declare function writeSuppressionsJsonToFile(eslintConfigFolderPath: string, suppressionsConfig: IBulkSuppressionsConfig): void; | ||
export declare function deleteBulkSuppressionsFileInEslintConfigFolder(eslintConfigFolderPath: string): void; | ||
export declare function serializeSuppression({ file, scopeId, rule }: ISuppression): string; | ||
//# sourceMappingURL=bulk-suppressions-file.d.ts.map |
@@ -8,6 +8,6 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getSuppressionsConfigForEslintrcFolderPath = getSuppressionsConfigForEslintrcFolderPath; | ||
exports.getAllBulkSuppressionsConfigsByEslintrcFolderPath = getAllBulkSuppressionsConfigsByEslintrcFolderPath; | ||
exports.getSuppressionsConfigForEslintConfigFolderPath = getSuppressionsConfigForEslintConfigFolderPath; | ||
exports.getAllBulkSuppressionsConfigsByEslintConfigFolderPath = getAllBulkSuppressionsConfigsByEslintConfigFolderPath; | ||
exports.writeSuppressionsJsonToFile = writeSuppressionsJsonToFile; | ||
exports.deleteBulkSuppressionsFileInEslintrcFolder = deleteBulkSuppressionsFileInEslintrcFolder; | ||
exports.deleteBulkSuppressionsFileInEslintConfigFolder = deleteBulkSuppressionsFileInEslintConfigFolder; | ||
exports.serializeSuppression = serializeSuppression; | ||
@@ -26,4 +26,4 @@ const fs_1 = __importDefault(require("fs")); | ||
const suppressionsJsonByFolderPath = new Map(); | ||
function getSuppressionsConfigForEslintrcFolderPath(eslintrcFolderPath) { | ||
const cachedSuppressionsConfig = suppressionsJsonByFolderPath.get(eslintrcFolderPath); | ||
function getSuppressionsConfigForEslintConfigFolderPath(eslintConfigFolderPath) { | ||
const cachedSuppressionsConfig = suppressionsJsonByFolderPath.get(eslintConfigFolderPath); | ||
let shouldLoad; | ||
@@ -39,3 +39,3 @@ let suppressionsConfig; | ||
if (shouldLoad) { | ||
const suppressionsPath = `${eslintrcFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; | ||
const suppressionsPath = `${eslintConfigFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; | ||
let rawJsonFile; | ||
@@ -70,16 +70,16 @@ try { | ||
} | ||
suppressionsJsonByFolderPath.set(eslintrcFolderPath, { readTime: Date.now(), suppressionsConfig }); | ||
suppressionsJsonByFolderPath.set(eslintConfigFolderPath, { readTime: Date.now(), suppressionsConfig }); | ||
} | ||
return suppressionsConfig; | ||
} | ||
function getAllBulkSuppressionsConfigsByEslintrcFolderPath() { | ||
function getAllBulkSuppressionsConfigsByEslintConfigFolderPath() { | ||
const result = []; | ||
for (const [eslintrcFolderPath, { suppressionsConfig }] of suppressionsJsonByFolderPath) { | ||
result.push([eslintrcFolderPath, suppressionsConfig]); | ||
for (const [eslintConfigFolderPath, { suppressionsConfig }] of suppressionsJsonByFolderPath) { | ||
result.push([eslintConfigFolderPath, suppressionsConfig]); | ||
} | ||
return result; | ||
} | ||
function writeSuppressionsJsonToFile(eslintrcFolderPath, suppressionsConfig) { | ||
suppressionsJsonByFolderPath.set(eslintrcFolderPath, { readTime: Date.now(), suppressionsConfig }); | ||
const suppressionsPath = `${eslintrcFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; | ||
function writeSuppressionsJsonToFile(eslintConfigFolderPath, suppressionsConfig) { | ||
suppressionsJsonByFolderPath.set(eslintConfigFolderPath, { readTime: Date.now(), suppressionsConfig }); | ||
const suppressionsPath = `${eslintConfigFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; | ||
if (suppressionsConfig.jsonObject.suppressions.length === 0) { | ||
@@ -93,4 +93,4 @@ deleteFile(suppressionsPath); | ||
} | ||
function deleteBulkSuppressionsFileInEslintrcFolder(eslintrcFolderPath) { | ||
const suppressionsPath = `${eslintrcFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; | ||
function deleteBulkSuppressionsFileInEslintConfigFolder(eslintConfigFolderPath) { | ||
const suppressionsPath = `${eslintConfigFolderPath}/${SUPPRESSIONS_JSON_FILENAME}`; | ||
deleteFile(suppressionsPath); | ||
@@ -97,0 +97,0 @@ } |
@@ -19,3 +19,3 @@ import type { TSESTree } from '@typescript-eslint/types'; | ||
export declare function write(): void; | ||
export declare function requireFromPathToLinterJS(importPath: string): import('eslint').Linter; | ||
export declare function requireFromPathToLinterJS(importPath: string): import('eslint-9').Linter | import('eslint-8').Linter; | ||
export declare function patchClass<T, U extends T>(originalClass: new () => T, patchedClass: new () => U): void; | ||
@@ -22,0 +22,0 @@ /** |
@@ -52,3 +52,6 @@ "use strict"; | ||
const bulk_suppressions_file_1 = require("./bulk-suppressions-file"); | ||
const ESLINTRC_FILENAMES = [ | ||
const ESLINT_CONFIG_FILENAMES = [ | ||
'eslint.config.js', | ||
'eslint.config.cjs', | ||
'eslint.config.mjs', | ||
'.eslintrc.js', | ||
@@ -118,5 +121,5 @@ '.eslintrc.cjs' | ||
} | ||
const eslintrcPathByFileOrFolderPath = new Map(); | ||
function findEslintrcFolderPathForNormalizedFileAbsolutePath(normalizedFilePath) { | ||
const cachedFolderPathForFilePath = eslintrcPathByFileOrFolderPath.get(normalizedFilePath); | ||
const eslintConfigPathByFileOrFolderPath = new Map(); | ||
function findEslintConfigFolderPathForNormalizedFileAbsolutePath(normalizedFilePath) { | ||
const cachedFolderPathForFilePath = eslintConfigPathByFileOrFolderPath.get(normalizedFilePath); | ||
if (cachedFolderPathForFilePath) { | ||
@@ -127,24 +130,24 @@ return cachedFolderPathForFilePath; | ||
const pathsToCache = [normalizedFilePath]; | ||
let eslintrcFolderPath; | ||
findEslintrcFileLoop: for (let currentFolder = normalizedFileFolderPath; currentFolder; // 'something'.substring(0, -1) is '' | ||
let eslintConfigFolderPath; | ||
findEslintConfigFileLoop: for (let currentFolder = normalizedFileFolderPath; currentFolder; // 'something'.substring(0, -1) is '' | ||
currentFolder = currentFolder.substring(0, currentFolder.lastIndexOf('/'))) { | ||
const cachedEslintrcFolderPath = eslintrcPathByFileOrFolderPath.get(currentFolder); | ||
const cachedEslintrcFolderPath = eslintConfigPathByFileOrFolderPath.get(currentFolder); | ||
if (cachedEslintrcFolderPath) { | ||
// Need to cache this result into the intermediate paths | ||
eslintrcFolderPath = cachedEslintrcFolderPath; | ||
eslintConfigFolderPath = cachedEslintrcFolderPath; | ||
break; | ||
} | ||
pathsToCache.push(currentFolder); | ||
for (const eslintrcFilename of ESLINTRC_FILENAMES) { | ||
if (fs_1.default.existsSync(`${currentFolder}/${eslintrcFilename}`)) { | ||
eslintrcFolderPath = currentFolder; | ||
break findEslintrcFileLoop; | ||
for (const eslintConfigFilename of ESLINT_CONFIG_FILENAMES) { | ||
if (fs_1.default.existsSync(`${currentFolder}/${eslintConfigFilename}`)) { | ||
eslintConfigFolderPath = currentFolder; | ||
break findEslintConfigFileLoop; | ||
} | ||
} | ||
} | ||
if (eslintrcFolderPath) { | ||
if (eslintConfigFolderPath) { | ||
for (const checkedFolder of pathsToCache) { | ||
eslintrcPathByFileOrFolderPath.set(checkedFolder, eslintrcFolderPath); | ||
eslintConfigPathByFileOrFolderPath.set(checkedFolder, eslintConfigFolderPath); | ||
} | ||
return eslintrcFolderPath; | ||
return eslintConfigFolderPath; | ||
} | ||
@@ -163,7 +166,7 @@ else { | ||
const normalizedFileAbsolutePath = fileAbsolutePath.replace(/\\/g, '/'); | ||
const eslintrcDirectory = findEslintrcFolderPathForNormalizedFileAbsolutePath(normalizedFileAbsolutePath); | ||
const fileRelativePath = normalizedFileAbsolutePath.substring(eslintrcDirectory.length + 1); | ||
const eslintConfigDirectory = findEslintConfigFolderPathForNormalizedFileAbsolutePath(normalizedFileAbsolutePath); | ||
const fileRelativePath = normalizedFileAbsolutePath.substring(eslintConfigDirectory.length + 1); | ||
const scopeId = calculateScopeId(currentNode); | ||
const suppression = { file: fileRelativePath, scopeId, rule }; | ||
const config = (0, bulk_suppressions_file_1.getSuppressionsConfigForEslintrcFolderPath)(eslintrcDirectory); | ||
const config = (0, bulk_suppressions_file_1.getSuppressionsConfigForEslintConfigFolderPath)(eslintConfigDirectory); | ||
const serializedSuppression = (0, bulk_suppressions_file_1.serializeSuppression)(suppression); | ||
@@ -181,3 +184,3 @@ const currentNodeIsSuppressed = config.serializedSuppressions.has(serializedSuppression); | ||
function prune() { | ||
for (const [eslintrcFolderPath, suppressionsConfig] of (0, bulk_suppressions_file_1.getAllBulkSuppressionsConfigsByEslintrcFolderPath)()) { | ||
for (const [eslintConfigFolderPath, suppressionsConfig] of (0, bulk_suppressions_file_1.getAllBulkSuppressionsConfigsByEslintConfigFolderPath)()) { | ||
if (suppressionsConfig) { | ||
@@ -191,3 +194,3 @@ const { newSerializedSuppressions, newJsonObject } = suppressionsConfig; | ||
}; | ||
(0, bulk_suppressions_file_1.writeSuppressionsJsonToFile)(eslintrcFolderPath, newSuppressionsConfig); | ||
(0, bulk_suppressions_file_1.writeSuppressionsJsonToFile)(eslintConfigFolderPath, newSuppressionsConfig); | ||
} | ||
@@ -197,3 +200,3 @@ } | ||
function write() { | ||
for (const [eslintrcFolderPath, suppressionsConfig] of (0, bulk_suppressions_file_1.getAllBulkSuppressionsConfigsByEslintrcFolderPath)()) { | ||
for (const [eslintrcFolderPath, suppressionsConfig] of (0, bulk_suppressions_file_1.getAllBulkSuppressionsConfigsByEslintConfigFolderPath)()) { | ||
if (suppressionsConfig) { | ||
@@ -200,0 +203,0 @@ (0, bulk_suppressions_file_1.writeSuppressionsJsonToFile)(eslintrcFolderPath, suppressionsConfig); |
@@ -32,7 +32,7 @@ "use strict"; | ||
console.log('No files with existing suppressions found.'); | ||
(0, bulk_suppressions_file_1.deleteBulkSuppressionsFileInEslintrcFolder)(normalizedCwd); | ||
(0, bulk_suppressions_file_1.deleteBulkSuppressionsFileInEslintConfigFolder)(normalizedCwd); | ||
} | ||
} | ||
async function getAllFilesWithExistingSuppressionsForCwdAsync(normalizedCwd) { | ||
const { jsonObject: bulkSuppressionsConfigJson } = (0, bulk_suppressions_file_1.getSuppressionsConfigForEslintrcFolderPath)(normalizedCwd); | ||
const { jsonObject: bulkSuppressionsConfigJson } = (0, bulk_suppressions_file_1.getSuppressionsConfigForEslintConfigFolderPath)(normalizedCwd); | ||
const allFiles = new Set(); | ||
@@ -39,0 +39,0 @@ for (const { file: filePath } of bulkSuppressionsConfigJson.suppressions) { |
@@ -42,8 +42,12 @@ "use strict"; | ||
const cwd = process.cwd(); | ||
const eslintPath = (0, get_eslint_cli_1.getEslintPath)(cwd); | ||
const [eslintPath, eslintVersion] = (0, get_eslint_cli_1.getEslintPathAndVersion)(cwd); | ||
const { ESLint } = require(eslintPath); | ||
const eslint = new ESLint({ | ||
useEslintrc: true, | ||
cwd | ||
}); | ||
let eslint; | ||
const majorVersion = parseInt(eslintVersion, 10); | ||
if (majorVersion < 9) { | ||
eslint = new ESLint({ cwd, useEslintrc: true }); | ||
} | ||
else { | ||
eslint = new ESLint({ cwd }); | ||
} | ||
let results; | ||
@@ -69,2 +73,3 @@ try { | ||
const stylishFormatter = await eslint.loadFormatter(); | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const formattedResults = await Promise.resolve(stylishFormatter.format(results)); | ||
@@ -71,0 +76,0 @@ console.log(formattedResults); |
@@ -1,2 +0,2 @@ | ||
export declare function getEslintPath(packagePath: string): string; | ||
export declare function getEslintPathAndVersion(packagePath: string): [string, string]; | ||
//# sourceMappingURL=get-eslint-cli.d.ts.map |
@@ -8,3 +8,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getEslintPath = getEslintPath; | ||
exports.getEslintPathAndVersion = getEslintPathAndVersion; | ||
const path_1 = __importDefault(require("path")); | ||
@@ -21,5 +21,6 @@ const constants_1 = require("../../constants"); | ||
'8.23.1', | ||
'8.57.0' | ||
'8.57.0', | ||
'9.25.1' | ||
]); | ||
function getEslintPath(packagePath) { | ||
function getEslintPathAndVersion(packagePath) { | ||
// Try to find a local ESLint installation, the one that should be listed as a dev dependency in package.json | ||
@@ -36,5 +37,5 @@ // and installed in node_modules | ||
} | ||
return localEslintApiPath; | ||
return [localEslintApiPath, localEslintVersion]; | ||
} | ||
catch (e) { | ||
catch (e1) { | ||
try { | ||
@@ -54,3 +55,3 @@ const { dependencies, devDependencies } = require(`${packagePath}/package.json`); | ||
} | ||
catch (e) { | ||
catch (e2) { | ||
throw new Error("@rushstack/eslint-bulk: This command must be run in the same folder as a project's package.json file."); | ||
@@ -57,0 +58,0 @@ } |
@@ -11,4 +11,8 @@ "use strict"; | ||
function isCorrectCwd(cwd) { | ||
return fs_1.default.existsSync(`${cwd}/.eslintrc.js`) || fs_1.default.existsSync(`${cwd}/.eslintrc.cjs`); | ||
return (fs_1.default.existsSync(`${cwd}/eslint.config.js`) || | ||
fs_1.default.existsSync(`${cwd}/eslint.config.cjs`) || | ||
fs_1.default.existsSync(`${cwd}/eslint.config.mjs`) || | ||
fs_1.default.existsSync(`${cwd}/.eslintrc.js`) || | ||
fs_1.default.existsSync(`${cwd}/.eslintrc.cjs`)); | ||
} | ||
//# sourceMappingURL=is-correct-cwd.js.map |
@@ -6,3 +6,3 @@ /** | ||
*/ | ||
export declare function generatePatchedLinterJsFileIfDoesNotExist(inputFilePath: string, outputFilePath: string): void; | ||
export declare function generatePatchedLinterJsFileIfDoesNotExist(inputFilePath: string, outputFilePath: string, eslintPackageVersion: string): void; | ||
//# sourceMappingURL=generate-patched-file.d.ts.map |
@@ -16,3 +16,3 @@ "use strict"; | ||
*/ | ||
function generatePatchedLinterJsFileIfDoesNotExist(inputFilePath, outputFilePath) { | ||
function generatePatchedLinterJsFileIfDoesNotExist(inputFilePath, outputFilePath, eslintPackageVersion) { | ||
const generateEnvVarValue = process.env[constants_1.ESLINT_BULK_FORCE_REGENERATE_PATCH_ENV_VAR_NAME]; | ||
@@ -22,2 +22,3 @@ if (generateEnvVarValue !== 'true' && generateEnvVarValue !== '1' && fs_1.default.existsSync(outputFilePath)) { | ||
} | ||
const majorVersion = parseInt(eslintPackageVersion, 10); | ||
const inputFile = fs_1.default.readFileSync(inputFilePath).toString(); | ||
@@ -61,2 +62,15 @@ let inputIndex = 0; | ||
} | ||
const markerForStartOfClassMethodSpaces = '\n */\n '; | ||
const markerForStartOfClassMethodTabs = '\n\t */\n\t'; | ||
function indexOfStartOfClassMethod(input, position) { | ||
let startOfClassMethodIndex = input.indexOf(markerForStartOfClassMethodSpaces, position); | ||
if (startOfClassMethodIndex === -1) { | ||
startOfClassMethodIndex = input.indexOf(markerForStartOfClassMethodTabs, position); | ||
if (startOfClassMethodIndex === -1) { | ||
return { index: startOfClassMethodIndex }; | ||
} | ||
return { index: startOfClassMethodIndex, marker: markerForStartOfClassMethodTabs }; | ||
} | ||
return { index: startOfClassMethodIndex, marker: markerForStartOfClassMethodSpaces }; | ||
} | ||
/** | ||
@@ -67,11 +81,12 @@ * Returns index of next public method | ||
*/ | ||
function getIndexOfNextPublicMethod(fromIndex) { | ||
function getIndexOfNextMethod(fromIndex) { | ||
const rest = inputFile.substring(fromIndex); | ||
const endOfClassIndex = rest.indexOf('\n}'); | ||
const markerForStartOfClassMethod = '\n */\n '; | ||
const startOfClassMethodIndex = rest.indexOf(markerForStartOfClassMethod); | ||
if (startOfClassMethodIndex === -1 || startOfClassMethodIndex > endOfClassIndex) { | ||
return -1; | ||
const { index: startOfClassMethodIndex, marker: startOfClassMethodMarker } = indexOfStartOfClassMethod(rest); | ||
if (startOfClassMethodIndex === -1 || | ||
!startOfClassMethodMarker || | ||
startOfClassMethodIndex > endOfClassIndex) { | ||
return { index: -1 }; | ||
} | ||
const afterMarkerIndex = rest.indexOf(markerForStartOfClassMethod) + markerForStartOfClassMethod.length; | ||
const afterMarkerIndex = startOfClassMethodIndex + startOfClassMethodMarker.length; | ||
const isPublicMethod = rest[afterMarkerIndex] !== '_' && | ||
@@ -81,6 +96,3 @@ rest[afterMarkerIndex] !== '#' && | ||
!rest.substring(afterMarkerIndex, rest.indexOf('\n', afterMarkerIndex)).includes('constructor'); | ||
if (isPublicMethod) { | ||
return fromIndex + afterMarkerIndex; | ||
} | ||
return getIndexOfNextPublicMethod(fromIndex + afterMarkerIndex); | ||
return { index: fromIndex + afterMarkerIndex, isPublic: isPublicMethod }; | ||
} | ||
@@ -138,2 +150,9 @@ function scanUntilIndex(indexToScanTo) { | ||
`; | ||
if (majorVersion >= 9) { | ||
outputFile += scanUntilMarker('const emitter = createEmitter();'); | ||
outputFile += ` | ||
// --- BEGIN MONKEY PATCH --- | ||
let currentNode = undefined; | ||
// --- END MONKEY PATCH ---`; | ||
} | ||
// Match this: | ||
@@ -170,3 +189,3 @@ // ``` | ||
// // --- BEGIN MONKEY PATCH --- | ||
// if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode, ruleId })) return; | ||
// if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) return; | ||
// // --- END MONKEY PATCH --- | ||
@@ -180,9 +199,29 @@ // | ||
outputFile += ` | ||
// --- BEGIN MONKEY PATCH --- | ||
if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode, ruleId, problem })) return; | ||
// --- END MONKEY PATCH --- | ||
`; | ||
outputFile += scanUntilMarker('nodeQueue.forEach(traversalInfo => {'); | ||
outputFile += scanUntilMarker('});'); | ||
outputFile += scanUntilNewline(); | ||
// --- BEGIN MONKEY PATCH --- | ||
if (bulkSuppressionsPatch.shouldBulkSuppress({ filename, currentNode: args[0]?.node ?? currentNode, ruleId, problem })) return; | ||
// --- END MONKEY PATCH ---`; | ||
// | ||
// Match this: | ||
// ``` | ||
// Object.keys(ruleListeners).forEach(selector => { | ||
// ... | ||
// }); | ||
// ``` | ||
// | ||
// Convert to something like this: | ||
// ``` | ||
// Object.keys(ruleListeners).forEach(selector => { | ||
// // --- BEGIN MONKEY PATCH --- | ||
// emitter.on(selector, (...args) => { currentNode = args[args.length - 1]; }); | ||
// // --- END MONKEY PATCH --- | ||
// ... | ||
// }); | ||
// ``` | ||
if (majorVersion >= 9) { | ||
outputFile += scanUntilMarker('Object.keys(ruleListeners).forEach(selector => {'); | ||
outputFile += ` | ||
// --- BEGIN MONKEY PATCH --- | ||
emitter.on(selector, (...args) => { currentNode = args[args.length - 1]; }); | ||
// --- END MONKEY PATCH ---`; | ||
} | ||
outputFile += scanUntilMarker('class Linter {'); | ||
@@ -215,15 +254,39 @@ outputFile += scanUntilNewline(); | ||
`; | ||
let indexOfNextPublicMethod = getIndexOfNextPublicMethod(inputIndex); | ||
while (indexOfNextPublicMethod !== -1) { | ||
outputFile += scanUntilIndex(indexOfNextPublicMethod); | ||
outputFile += scanUntilNewline(); | ||
outputFile += ` // --- BEGIN MONKEY PATCH --- | ||
const privateMethodNames = []; | ||
let { index: indexOfNextMethod, isPublic } = getIndexOfNextMethod(inputIndex); | ||
while (indexOfNextMethod !== -1) { | ||
outputFile += scanUntilIndex(indexOfNextMethod); | ||
if (isPublic) { | ||
// Inject the monkey patch at the start of the public method | ||
outputFile += scanUntilNewline(); | ||
outputFile += ` // --- BEGIN MONKEY PATCH --- | ||
this._conditionallyReinitialize(); | ||
// --- END MONKEY PATCH --- | ||
`; | ||
indexOfNextPublicMethod = getIndexOfNextPublicMethod(inputIndex); | ||
} | ||
else if (inputFile[inputIndex] === '#') { | ||
// Replace the '#' private method with a '_' private method, so that our monkey patch | ||
// can still call it. Otherwise, we get the following error during execution: | ||
// TypeError: Receiver must be an instance of class Linter | ||
const privateMethodName = scanUntilMarker('('); | ||
// Remove the '(' at the end and stash it, since we need to escape it for the regex later | ||
privateMethodNames.push(privateMethodName.slice(0, -1)); | ||
outputFile += `_${privateMethodName.slice(1)}`; | ||
} | ||
const indexResult = getIndexOfNextMethod(inputIndex); | ||
indexOfNextMethod = indexResult.index; | ||
isPublic = indexResult.isPublic; | ||
} | ||
outputFile += scanUntilEnd(); | ||
// Do a second pass to find and replace all calls to private methods with the patched versions. | ||
if (privateMethodNames.length) { | ||
// eslint-disable-next-line @rushstack/security/no-unsafe-regexp | ||
const privateMethodCallRegex = new RegExp(`\.(${privateMethodNames.join('|')})\\(`, 'g'); | ||
outputFile = outputFile.replace(privateMethodCallRegex, (match, privateMethodName) => { | ||
// Replace the leading '#' with a leading '_' | ||
return `._${privateMethodName.slice(1)}(`; | ||
}); | ||
} | ||
fs_1.default.writeFileSync(outputFilePath, outputFile); | ||
} | ||
//# sourceMappingURL=generate-patched-file.js.map |
@@ -22,3 +22,3 @@ "use strict"; | ||
const pathToGeneratedPatch = (0, path_utils_1.ensurePathToGeneratedPatch)(); | ||
(0, generate_patched_file_1.generatePatchedLinterJsFileIfDoesNotExist)(pathToLinterJS, pathToGeneratedPatch); | ||
(0, generate_patched_file_1.generatePatchedLinterJsFileIfDoesNotExist)(pathToLinterJS, pathToGeneratedPatch, _patch_base_1.eslintPackageVersion); | ||
const { Linter: LinterPatch } = require(pathToGeneratedPatch); | ||
@@ -25,0 +25,0 @@ LinterPatch.prototype.verify = (0, bulk_suppressions_patch_1.extendVerifyFunction)(LinterPatch.prototype.verify); |
{ | ||
"name": "@rushstack/eslint-patch", | ||
"version": "1.11.0", | ||
"version": "1.12.0", | ||
"description": "Enhance ESLint with better support for large scale monorepos", | ||
@@ -29,10 +29,10 @@ "main": "lib/usage.js", | ||
"devDependencies": { | ||
"@rushstack/heft": "0.69.2", | ||
"@rushstack/heft-node-rig": "2.7.0", | ||
"@types/eslint": "8.56.10", | ||
"@types/node": "20.17.19", | ||
"@typescript-eslint/types": "~8.26.1", | ||
"eslint": "~8.57.0", | ||
"eslint-plugin-header": "~3.1.1", | ||
"typescript": "~5.8.2" | ||
"@rushstack/heft": "0.73.2", | ||
"@types/eslint-8": "npm:@types/eslint@8.56.10", | ||
"@types/eslint-9": "npm:@types/eslint@9.6.1", | ||
"@typescript-eslint/types": "~8.31.0", | ||
"eslint-9": "npm:eslint@~9.25.1", | ||
"eslint-8": "npm:eslint@~8.57.0", | ||
"typescript": "~5.8.2", | ||
"decoupled-local-node-rig": "1.0.0" | ||
}, | ||
@@ -39,0 +39,0 @@ "scripts": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
274356
7.7%2513
6.66%