Comparing version
@@ -151,14 +151,2 @@ #!/usr/bin/env node | ||
// Call the config inspector if `--inspect-config` is present. | ||
if (process.argv.includes("--inspect-config")) { | ||
console.warn("You can also run this command directly using 'npx @eslint/config-inspector' in the same directory as your configuration file."); | ||
const spawn = require("cross-spawn"); | ||
spawn.sync("npx", ["@eslint/config-inspector"], { encoding: "utf8", stdio: "inherit" }); | ||
return; | ||
} | ||
// Otherwise, call the CLI. | ||
@@ -165,0 +153,0 @@ const cli = require("../lib/cli"); |
@@ -73,2 +73,3 @@ /** | ||
Int8Array: false, | ||
Intl: false, | ||
Map: false, | ||
@@ -75,0 +76,0 @@ Promise: false, |
@@ -22,3 +22,3 @@ /** | ||
{ LegacyESLint } = require("./eslint"), | ||
{ ESLint, shouldUseFlatConfig } = require("./eslint/eslint"), | ||
{ ESLint, shouldUseFlatConfig, locateConfigFileToUse } = require("./eslint/eslint"), | ||
createCLIOptions = require("./options"), | ||
@@ -341,2 +341,23 @@ log = require("./shared/logging"), | ||
/** | ||
* Calculates the command string for the --inspect-config operation. | ||
* @param {string} configFile The path to the config file to inspect. | ||
* @returns {Promise<string>} The command string to execute. | ||
*/ | ||
async calculateInspectConfigFlags(configFile) { | ||
// find the config file | ||
const { | ||
configFilePath, | ||
basePath, | ||
error | ||
} = await locateConfigFileToUse({ cwd: process.cwd(), configFile }); | ||
if (error) { | ||
throw error; | ||
} | ||
return ["--config", configFilePath, "--basePath", basePath]; | ||
}, | ||
/** | ||
* Executes the CLI based on an array of arguments that is passed in. | ||
@@ -430,2 +451,20 @@ * @param {string|Array|Object} args The arguments to process. | ||
if (options.inspectConfig) { | ||
log.info("You can also run this command directly using 'npx @eslint/config-inspector' in the same directory as your configuration file."); | ||
try { | ||
const flatOptions = await translateOptions(options, "flat"); | ||
const spawn = require("cross-spawn"); | ||
const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile); | ||
spawn.sync("npx", ["@eslint/config-inspector", ...flags], { encoding: "utf8", stdio: "inherit" }); | ||
} catch (error) { | ||
log.error(error); | ||
return 2; | ||
} | ||
return 0; | ||
} | ||
debug(`Running on ${useStdin ? "text" : "files"}`); | ||
@@ -432,0 +471,0 @@ |
@@ -21,2 +21,7 @@ /** | ||
/** | ||
* Fields that are considered metadata and not part of the config object. | ||
*/ | ||
const META_FIELDS = new Set(["name"]); | ||
const ruleValidator = new RuleValidator(); | ||
@@ -78,3 +83,49 @@ | ||
/** | ||
* Wraps a config error with details about where the error occurred. | ||
* @param {Error} error The original error. | ||
* @param {number} originalLength The original length of the config array. | ||
* @param {number} baseLength The length of the base config. | ||
* @returns {TypeError} The new error with details. | ||
*/ | ||
function wrapConfigErrorWithDetails(error, originalLength, baseLength) { | ||
let location = "user-defined"; | ||
let configIndex = error.index; | ||
/* | ||
* A config array is set up in this order: | ||
* 1. Base config | ||
* 2. Original configs | ||
* 3. User-defined configs | ||
* 4. CLI-defined configs | ||
* | ||
* So we need to adjust the index to account for the base config. | ||
* | ||
* - If the index is less than the base length, it's in the base config | ||
* (as specified by `baseConfig` argument to `FlatConfigArray` constructor). | ||
* - If the index is greater than the base length but less than the original | ||
* length + base length, it's in the original config. The original config | ||
* is passed to the `FlatConfigArray` constructor as the first argument. | ||
* - Otherwise, it's in the user-defined config, which is loaded from the | ||
* config file and merged with any command-line options. | ||
*/ | ||
if (error.index < baseLength) { | ||
location = "base"; | ||
} else if (error.index < originalLength + baseLength) { | ||
location = "original"; | ||
configIndex = error.index - baseLength; | ||
} else { | ||
configIndex = error.index - originalLength - baseLength; | ||
} | ||
return new TypeError( | ||
`${error.message.slice(0, -1)} at ${location} index ${configIndex}.`, | ||
{ cause: error } | ||
); | ||
} | ||
const originalBaseConfig = Symbol("originalBaseConfig"); | ||
const originalLength = Symbol("originalLength"); | ||
const baseLength = Symbol("baseLength"); | ||
@@ -106,2 +157,8 @@ //----------------------------------------------------------------------------- | ||
/** | ||
* The original length of the array before any modifications. | ||
* @type {number} | ||
*/ | ||
this[originalLength] = this.length; | ||
if (baseConfig[Symbol.iterator]) { | ||
@@ -114,2 +171,8 @@ this.unshift(...baseConfig); | ||
/** | ||
* The length of the array after applying the base config. | ||
* @type {number} | ||
*/ | ||
this[baseLength] = this.length - this[originalLength]; | ||
/** | ||
* The base config used to build the config array. | ||
@@ -131,2 +194,45 @@ * @type {Array<FlatConfig>} | ||
/** | ||
* Normalizes the array by calling the superclass method and catching/rethrowing | ||
* any ConfigError exceptions with additional details. | ||
* @param {any} [context] The context to use to normalize the array. | ||
* @returns {Promise<FlatConfigArray>} A promise that resolves when the array is normalized. | ||
*/ | ||
normalize(context) { | ||
return super.normalize(context) | ||
.catch(error => { | ||
if (error.name === "ConfigError") { | ||
throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]); | ||
} | ||
throw error; | ||
}); | ||
} | ||
/** | ||
* Normalizes the array by calling the superclass method and catching/rethrowing | ||
* any ConfigError exceptions with additional details. | ||
* @param {any} [context] The context to use to normalize the array. | ||
* @returns {FlatConfigArray} The current instance. | ||
* @throws {TypeError} If the config is invalid. | ||
*/ | ||
normalizeSync(context) { | ||
try { | ||
return super.normalizeSync(context); | ||
} catch (error) { | ||
if (error.name === "ConfigError") { | ||
throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]); | ||
} | ||
throw error; | ||
} | ||
} | ||
/* eslint-disable class-methods-use-this -- Desired as instance method */ | ||
@@ -143,5 +249,5 @@ /** | ||
/* | ||
* If `shouldIgnore` is false, we remove any ignore patterns specified | ||
* in the config so long as it's not a default config and it doesn't | ||
* have a `files` entry. | ||
* If a config object has `ignores` and no other non-meta fields, then it's an object | ||
* for global ignores. If `shouldIgnore` is false, that object shouldn't apply, | ||
* so we'll remove its `ignores`. | ||
*/ | ||
@@ -152,3 +258,3 @@ if ( | ||
config.ignores && | ||
!config.files | ||
Object.keys(config).filter(key => !META_FIELDS.has(key)).length === 1 | ||
) { | ||
@@ -155,0 +261,0 @@ /* eslint-disable-next-line no-unused-vars -- need to strip off other keys */ |
@@ -18,3 +18,2 @@ /** | ||
const minimatch = require("minimatch"); | ||
const util = require("util"); | ||
const fswalk = require("@nodelib/fs.walk"); | ||
@@ -28,3 +27,2 @@ const globParent = require("glob-parent"); | ||
const doFsWalk = util.promisify(fswalk.walk); | ||
const Minimatch = minimatch.Minimatch; | ||
@@ -285,52 +283,88 @@ const MINIMATCH_OPTIONS = { dot: true }; | ||
const filePaths = (await doFsWalk(basePath, { | ||
const filePaths = (await new Promise((resolve, reject) => { | ||
deepFilter(entry) { | ||
const relativePath = normalizeToPosix(path.relative(basePath, entry.path)); | ||
const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true)); | ||
let promiseRejected = false; | ||
return matchesPattern && !configs.isDirectoryIgnored(entry.path); | ||
}, | ||
entryFilter(entry) { | ||
const relativePath = normalizeToPosix(path.relative(basePath, entry.path)); | ||
/** | ||
* Wraps a boolean-returning filter function. The wrapped function will reject the promise if an error occurs. | ||
* @param {Function} filter A filter function to wrap. | ||
* @returns {Function} A function similar to the wrapped filter that rejects the promise if an error occurs. | ||
*/ | ||
function wrapFilter(filter) { | ||
return (...args) => { | ||
// entries may be directories or files so filter out directories | ||
if (entry.dirent.isDirectory()) { | ||
// No need to run the filter if an error has been thrown. | ||
if (!promiseRejected) { | ||
try { | ||
return filter(...args); | ||
} catch (error) { | ||
promiseRejected = true; | ||
reject(error); | ||
} | ||
} | ||
return false; | ||
} | ||
}; | ||
} | ||
/* | ||
* Optimization: We need to track when patterns are left unmatched | ||
* and so we use `unmatchedPatterns` to do that. There is a bit of | ||
* complexity here because the same file can be matched by more than | ||
* one pattern. So, when we start, we actually need to test every | ||
* pattern against every file. Once we know there are no remaining | ||
* unmatched patterns, then we can switch to just looking for the | ||
* first matching pattern for improved speed. | ||
*/ | ||
const matchesPattern = unmatchedPatterns.size > 0 | ||
? matchers.reduce((previousValue, matcher) => { | ||
const pathMatches = matcher.match(relativePath); | ||
fswalk.walk( | ||
basePath, | ||
{ | ||
deepFilter: wrapFilter(entry => { | ||
const relativePath = normalizeToPosix(path.relative(basePath, entry.path)); | ||
const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true)); | ||
return matchesPattern && !configs.isDirectoryIgnored(entry.path); | ||
}), | ||
entryFilter: wrapFilter(entry => { | ||
const relativePath = normalizeToPosix(path.relative(basePath, entry.path)); | ||
// entries may be directories or files so filter out directories | ||
if (entry.dirent.isDirectory()) { | ||
return false; | ||
} | ||
/* | ||
* We updated the unmatched patterns set only if the path | ||
* matches and the file isn't ignored. If the file is | ||
* ignored, that means there wasn't a match for the | ||
* pattern so it should not be removed. | ||
* | ||
* Performance note: isFileIgnored() aggressively caches | ||
* results so there is no performance penalty for calling | ||
* it twice with the same argument. | ||
* Optimization: We need to track when patterns are left unmatched | ||
* and so we use `unmatchedPatterns` to do that. There is a bit of | ||
* complexity here because the same file can be matched by more than | ||
* one pattern. So, when we start, we actually need to test every | ||
* pattern against every file. Once we know there are no remaining | ||
* unmatched patterns, then we can switch to just looking for the | ||
* first matching pattern for improved speed. | ||
*/ | ||
if (pathMatches && !configs.isFileIgnored(entry.path)) { | ||
unmatchedPatterns.delete(matcher.pattern); | ||
} | ||
const matchesPattern = unmatchedPatterns.size > 0 | ||
? matchers.reduce((previousValue, matcher) => { | ||
const pathMatches = matcher.match(relativePath); | ||
return pathMatches || previousValue; | ||
}, false) | ||
: matchers.some(matcher => matcher.match(relativePath)); | ||
/* | ||
* We updated the unmatched patterns set only if the path | ||
* matches and the file isn't ignored. If the file is | ||
* ignored, that means there wasn't a match for the | ||
* pattern so it should not be removed. | ||
* | ||
* Performance note: isFileIgnored() aggressively caches | ||
* results so there is no performance penalty for calling | ||
* it twice with the same argument. | ||
*/ | ||
if (pathMatches && !configs.isFileIgnored(entry.path)) { | ||
unmatchedPatterns.delete(matcher.pattern); | ||
} | ||
return matchesPattern && !configs.isFileIgnored(entry.path); | ||
} | ||
return pathMatches || previousValue; | ||
}, false) | ||
: matchers.some(matcher => matcher.match(relativePath)); | ||
return matchesPattern && !configs.isFileIgnored(entry.path); | ||
}) | ||
}, | ||
(error, entries) => { | ||
// If the promise is already rejected, calling `resolve` or `reject` will do nothing. | ||
if (error) { | ||
reject(error); | ||
} else { | ||
resolve(entries); | ||
} | ||
} | ||
); | ||
})).map(entry => entry.path); | ||
@@ -337,0 +371,0 @@ |
@@ -45,2 +45,3 @@ /** | ||
const LintResultCache = require("../cli-engine/lint-result-cache"); | ||
const { Retrier } = require("@humanwhocodes/retry"); | ||
@@ -855,2 +856,4 @@ /* | ||
const controller = new AbortController(); | ||
const retryCodes = new Set(["ENFILE", "EMFILE"]); | ||
const retrier = new Retrier(error => retryCodes.has(error.code)); | ||
@@ -924,3 +927,3 @@ debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`); | ||
return fs.readFile(filePath, { encoding: "utf8", signal: controller.signal }) | ||
return retrier.retry(() => fs.readFile(filePath, { encoding: "utf8", signal: controller.signal }) | ||
.then(text => { | ||
@@ -955,7 +958,7 @@ | ||
return result; | ||
}).catch(error => { | ||
})) | ||
.catch(error => { | ||
controller.abort(error); | ||
throw error; | ||
}); | ||
}) | ||
@@ -1221,3 +1224,4 @@ ); | ||
ESLint, | ||
shouldUseFlatConfig | ||
shouldUseFlatConfig, | ||
locateConfigFileToUse | ||
}; |
@@ -41,12 +41,12 @@ /** | ||
*/ | ||
function groupByParentComment(directives) { | ||
function groupByParentDirective(directives) { | ||
const groups = new Map(); | ||
for (const directive of directives) { | ||
const { unprocessedDirective: { parentComment } } = directive; | ||
const { unprocessedDirective: { parentDirective } } = directive; | ||
if (groups.has(parentComment)) { | ||
groups.get(parentComment).push(directive); | ||
if (groups.has(parentDirective)) { | ||
groups.get(parentDirective).push(directive); | ||
} else { | ||
groups.set(parentComment, [directive]); | ||
groups.set(parentDirective, [directive]); | ||
} | ||
@@ -61,15 +61,15 @@ } | ||
* @param {Directive[]} directives Unused directives to be removed. | ||
* @param {Token} commentToken The backing Comment token. | ||
* @param {Token} node The backing Comment token. | ||
* @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems. | ||
*/ | ||
function createIndividualDirectivesRemoval(directives, commentToken) { | ||
function createIndividualDirectivesRemoval(directives, node) { | ||
/* | ||
* `commentToken.value` starts right after `//` or `/*`. | ||
* `node.value` starts right after `//` or `/*`. | ||
* All calculated offsets will be relative to this index. | ||
*/ | ||
const commentValueStart = commentToken.range[0] + "//".length; | ||
const commentValueStart = node.range[0] + "//".length; | ||
// Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`) | ||
const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length; | ||
const listStartOffset = /^\s*\S+\s+/u.exec(node.value)[0].length; | ||
@@ -83,3 +83,3 @@ /* | ||
*/ | ||
const listText = commentToken.value | ||
const listText = node.value | ||
.slice(listStartOffset) // remove directive name and all whitespace before the list | ||
@@ -165,9 +165,9 @@ .split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists | ||
/** | ||
* Creates a description of deleting an entire unused disable comment. | ||
* Creates a description of deleting an entire unused disable directive. | ||
* @param {Directive[]} directives Unused directives to be removed. | ||
* @param {Token} commentToken The backing Comment token. | ||
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem. | ||
* @param {Token} node The backing Comment token. | ||
* @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output problem. | ||
*/ | ||
function createCommentRemoval(directives, commentToken) { | ||
const { range } = commentToken; | ||
function createDirectiveRemoval(directives, node) { | ||
const { range } = node; | ||
const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`); | ||
@@ -193,8 +193,8 @@ | ||
function processUnusedDirectives(allDirectives) { | ||
const directiveGroups = groupByParentComment(allDirectives); | ||
const directiveGroups = groupByParentDirective(allDirectives); | ||
return directiveGroups.flatMap( | ||
directives => { | ||
const { parentComment } = directives[0].unprocessedDirective; | ||
const remainingRuleIds = new Set(parentComment.ruleIds); | ||
const { parentDirective } = directives[0].unprocessedDirective; | ||
const remainingRuleIds = new Set(parentDirective.ruleIds); | ||
@@ -206,4 +206,4 @@ for (const directive of directives) { | ||
return remainingRuleIds.size | ||
? createIndividualDirectivesRemoval(directives, parentComment.commentToken) | ||
: [createCommentRemoval(directives, parentComment.commentToken)]; | ||
? createIndividualDirectivesRemoval(directives, parentDirective.node) | ||
: [createDirectiveRemoval(directives, parentDirective.node)]; | ||
} | ||
@@ -381,3 +381,3 @@ ); | ||
.map(({ description, fix, unprocessedDirective }) => { | ||
const { parentComment, type, line, column } = unprocessedDirective; | ||
const { parentDirective, type, line, column } = unprocessedDirective; | ||
@@ -398,4 +398,4 @@ let message; | ||
message, | ||
line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line, | ||
column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column, | ||
line: type === "disable-next-line" ? parentDirective.node.loc.start.line : line, | ||
column: type === "disable-next-line" ? parentDirective.node.loc.start.column + 1 : column, | ||
severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2, | ||
@@ -402,0 +402,0 @@ nodeType: null, |
@@ -41,3 +41,3 @@ /** | ||
* @property {string} [ignorePath] Specify path of ignore file | ||
* @property {string[]} [ignorePattern] Pattern of files to ignore (in addition to those in .eslintignore) | ||
* @property {string[]} [ignorePattern] Patterns of files to ignore. In eslintrc mode, these are in addition to `.eslintignore` | ||
* @property {boolean} init Run config initialization wizard | ||
@@ -265,3 +265,3 @@ * @property {boolean} inlineConfig Prevent comments from changing config or rules | ||
type: "[String]", | ||
description: "Pattern of files to ignore (in addition to those in .eslintignore)", | ||
description: `Patterns of files to ignore${usingFlatConfig ? "" : " (in addition to those in .eslintignore)"}`, | ||
concatRepeatedArrays: [true, { | ||
@@ -268,0 +268,0 @@ oneValuePerFlag: true |
@@ -34,4 +34,3 @@ /** | ||
checkLoops: { | ||
type: "boolean", | ||
default: true | ||
enum: ["all", "allExceptWhileTrue", "none", true, false] | ||
} | ||
@@ -49,7 +48,13 @@ }, | ||
create(context) { | ||
const options = context.options[0] || {}, | ||
checkLoops = options.checkLoops !== false, | ||
loopSetStack = []; | ||
const options = context.options[0] || {}; | ||
let checkLoops = options.checkLoops ?? "allExceptWhileTrue"; | ||
const loopSetStack = []; | ||
const sourceCode = context.sourceCode; | ||
if (options.checkLoops === true) { | ||
checkLoops = "all"; | ||
} else if (options.checkLoops === false) { | ||
checkLoops = "none"; | ||
} | ||
let loopsInCurrentScope = new Set(); | ||
@@ -125,3 +130,3 @@ | ||
function checkLoop(node) { | ||
if (checkLoops) { | ||
if (checkLoops === "all" || checkLoops === "allExceptWhileTrue") { | ||
trackConstantConditionLoop(node); | ||
@@ -138,3 +143,9 @@ } | ||
IfStatement: reportIfConstant, | ||
WhileStatement: checkLoop, | ||
WhileStatement(node) { | ||
if (node.test.type === "Literal" && node.test.value === true && checkLoops === "allExceptWhileTrue") { | ||
return; | ||
} | ||
checkLoop(node); | ||
}, | ||
"WhileStatement:exit": checkConstantConditionLoopInSet, | ||
@@ -141,0 +152,0 @@ DoWhileStatement: checkLoop, |
@@ -9,8 +9,2 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
const Graphemer = require("graphemer").default; | ||
//------------------------------------------------------------------------------ | ||
// Helpers | ||
@@ -22,4 +16,4 @@ //------------------------------------------------------------------------------ | ||
/** @type {Graphemer | undefined} */ | ||
let splitter; | ||
/** @type {Intl.Segmenter | undefined} */ | ||
let segmenter; | ||
@@ -52,7 +46,11 @@ //------------------------------------------------------------------------------ | ||
if (!splitter) { | ||
splitter = new Graphemer(); | ||
segmenter ??= new Intl.Segmenter("en-US"); // en-US locale should be supported everywhere | ||
let graphemeCount = 0; | ||
// eslint-disable-next-line no-unused-vars -- for-of needs a variable | ||
for (const unused of segmenter.segment(value)) { | ||
graphemeCount++; | ||
} | ||
return splitter.countGraphemes(value); | ||
return graphemeCount; | ||
} | ||
@@ -59,0 +57,0 @@ |
@@ -376,2 +376,52 @@ /** | ||
/** | ||
* A class to represent a directive comment. | ||
*/ | ||
class Directive { | ||
/** | ||
* The type of directive. | ||
* @type {"disable"|"enable"|"disable-next-line"|"disable-line"} | ||
* @readonly | ||
*/ | ||
type; | ||
/** | ||
* The node representing the directive. | ||
* @type {ASTNode|Comment} | ||
* @readonly | ||
*/ | ||
node; | ||
/** | ||
* Everything after the "eslint-disable" portion of the directive, | ||
* but before the "--" that indicates the justification. | ||
* @type {string} | ||
* @readonly | ||
*/ | ||
value; | ||
/** | ||
* The justification for the directive. | ||
* @type {string} | ||
* @readonly | ||
*/ | ||
justification; | ||
/** | ||
* Creates a new instance. | ||
* @param {Object} options The options for the directive. | ||
* @param {"disable"|"enable"|"disable-next-line"|"disable-line"} options.type The type of directive. | ||
* @param {ASTNode|Comment} options.node The node representing the directive. | ||
* @param {string} options.value The value of the directive. | ||
* @param {string} options.justification The justification for the directive. | ||
*/ | ||
constructor({ type, node, value, justification }) { | ||
this.type = type; | ||
this.node = node; | ||
this.value = value; | ||
this.justification = justification; | ||
} | ||
} | ||
//------------------------------------------------------------------------------ | ||
@@ -926,2 +976,80 @@ // Public Interface | ||
/** | ||
* Returns an all directive nodes that enable or disable rules along with any problems | ||
* encountered while parsing the directives. | ||
* @returns {{problems:Array<Problem>,directives:Array<Directive>}} Information | ||
* that ESLint needs to further process the directives. | ||
*/ | ||
getDisableDirectives() { | ||
// check the cache first | ||
const cachedDirectives = this[caches].get("disableDirectives"); | ||
if (cachedDirectives) { | ||
return cachedDirectives; | ||
} | ||
const problems = []; | ||
const directives = []; | ||
this.getInlineConfigNodes().forEach(comment => { | ||
const { directivePart, justificationPart } = commentParser.extractDirectiveComment(comment.value); | ||
// Step 1: Extract the directive text | ||
const match = directivesPattern.exec(directivePart); | ||
if (!match) { | ||
return; | ||
} | ||
const directiveText = match[1]; | ||
// Step 2: Extract the directive value | ||
const lineCommentSupported = /^eslint-disable-(next-)?line$/u.test(directiveText); | ||
if (comment.type === "Line" && !lineCommentSupported) { | ||
return; | ||
} | ||
// Step 3: Validate the directive does not span multiple lines | ||
if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) { | ||
const message = `${directiveText} comment should not span multiple lines.`; | ||
problems.push({ | ||
ruleId: null, | ||
message, | ||
loc: comment.loc | ||
}); | ||
return; | ||
} | ||
// Step 4: Extract the directive value and create the Directive object | ||
const directiveValue = directivePart.slice(match.index + directiveText.length); | ||
switch (directiveText) { | ||
case "eslint-disable": | ||
case "eslint-enable": | ||
case "eslint-disable-next-line": | ||
case "eslint-disable-line": { | ||
const directiveType = directiveText.slice("eslint-".length); | ||
directives.push(new Directive({ | ||
type: directiveType, | ||
node: comment, | ||
value: directiveValue, | ||
justification: justificationPart | ||
})); | ||
} | ||
// no default | ||
} | ||
}); | ||
const result = { problems, directives }; | ||
this[caches].set("disableDirectives", result); | ||
return result; | ||
} | ||
/** | ||
* Applies language options sent in from the core. | ||
@@ -928,0 +1056,0 @@ * @param {Object} languageOptions The language options for this run. |
{ | ||
"name": "eslint", | ||
"version": "9.0.0", | ||
"version": "9.1.0", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -36,3 +36,4 @@ "description": "An AST-based pattern checker for JavaScript.", | ||
"test:fuzz": "node Makefile.js fuzz", | ||
"test:performance": "node Makefile.js perf" | ||
"test:performance": "node Makefile.js perf", | ||
"test:emfile": "node tools/check-emfile-handling.js" | ||
}, | ||
@@ -72,5 +73,6 @@ "gitHooks": { | ||
"@eslint/eslintrc": "^3.0.2", | ||
"@eslint/js": "9.0.0", | ||
"@humanwhocodes/config-array": "^0.12.3", | ||
"@eslint/js": "9.1.1", | ||
"@humanwhocodes/config-array": "^0.13.0", | ||
"@humanwhocodes/module-importer": "^1.0.1", | ||
"@humanwhocodes/retry": "^0.2.3", | ||
"@nodelib/fs.walk": "^1.2.8", | ||
@@ -91,3 +93,2 @@ "ajv": "^6.12.4", | ||
"glob-parent": "^6.0.2", | ||
"graphemer": "^1.4.0", | ||
"ignore": "^5.2.0", | ||
@@ -126,3 +127,3 @@ "imurmurhash": "^0.1.4", | ||
"eslint-plugin-eslint-comments": "^3.2.0", | ||
"eslint-plugin-eslint-plugin": "^5.2.1", | ||
"eslint-plugin-eslint-plugin": "^6.0.0", | ||
"eslint-plugin-internal-rules": "file:tools/internal-rules", | ||
@@ -138,3 +139,3 @@ "eslint-plugin-jsdoc": "^46.9.0", | ||
"glob": "^10.0.0", | ||
"globals": "^14.0.0", | ||
"globals": "^15.0.0", | ||
"got": "^11.8.3", | ||
@@ -141,0 +142,0 @@ "gray-matter": "^4.0.3", |
@@ -62,3 +62,3 @@ [](https://www.npmjs.com/package/eslint) | ||
After running `npm init @eslint/config`, you'll have an `eslint.config.js` or `eslint.config.mjs` file in your directory. In it, you'll see some rules configured like this: | ||
After running `npm init @eslint/config`, you'll have an `eslint.config.js` (or `eslint.config.mjs`) file in your directory. In it, you'll see some rules configured like this: | ||
@@ -304,3 +304,3 @@ ```js | ||
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3> | ||
<p><a href="https://bitwarden.com"><img src="https://avatars.githubusercontent.com/u/15990069?v=4" alt="Bitwarden" height="96"></a> <a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3> | ||
<p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3> | ||
<p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/eb04ddc/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3> | ||
@@ -307,0 +307,0 @@ <p><a href="https://www.notion.so"><img src="https://images.opencollective.com/notion/bf3b117/logo.png" alt="notion" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p> |
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
3034432
0.31%71399
0.33%+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
Updated