eslint
Advanced tools
Comparing version 9.0.0-rc.0 to 9.0.0
@@ -151,2 +151,14 @@ #!/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. | ||
@@ -153,0 +165,0 @@ const cli = require("../lib/cli"); |
@@ -134,2 +134,3 @@ /** | ||
rulesdir, | ||
stats, | ||
warnIgnored, | ||
@@ -226,2 +227,3 @@ passOnNoPatterns, | ||
options.ignorePatterns = ignorePattern; | ||
options.stats = stats; | ||
options.warnIgnored = warnIgnored; | ||
@@ -228,0 +230,0 @@ |
@@ -688,2 +688,3 @@ /** | ||
plugins = {}, | ||
stats = false, | ||
warnIgnored = true, | ||
@@ -795,2 +796,5 @@ passOnNoPatterns = false, | ||
} | ||
if (typeof stats !== "boolean") { | ||
errors.push("'stats' must be a boolean."); | ||
} | ||
if (typeof warnIgnored !== "boolean") { | ||
@@ -823,2 +827,3 @@ errors.push("'warnIgnored' must be a boolean."); | ||
ignorePatterns, | ||
stats, | ||
passOnNoPatterns, | ||
@@ -825,0 +830,0 @@ warnIgnored, |
@@ -87,2 +87,3 @@ /** | ||
* @property {Record<string,Plugin>} [plugins] An array of plugin implementations. | ||
* @property {boolean} [stats] True enables added statistics on lint results. | ||
* @property {boolean} warnIgnored Show warnings when the file list includes ignored files | ||
@@ -469,2 +470,3 @@ * @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause | ||
* @param {Function} config.ruleFilter A predicate function to filter which rules should be run. | ||
* @param {boolean} config.stats If `true`, then if reports extra statistics with the lint results. | ||
* @param {Linter} config.linter The linter instance to verify. | ||
@@ -482,2 +484,3 @@ * @returns {LintResult} The result of linting. | ||
ruleFilter, | ||
stats, | ||
linter | ||
@@ -503,2 +506,3 @@ }) { | ||
ruleFilter, | ||
stats, | ||
@@ -535,2 +539,9 @@ /** | ||
if (stats) { | ||
result.stats = { | ||
times: linter.getTimes(), | ||
fixPasses: linter.getFixPassCount() | ||
}; | ||
} | ||
return result; | ||
@@ -816,2 +827,3 @@ } | ||
ruleFilter, | ||
stats, | ||
globInputPaths, | ||
@@ -931,2 +943,3 @@ errorOnUnmatchedPattern, | ||
ruleFilter, | ||
stats, | ||
linter | ||
@@ -1020,3 +1033,4 @@ }); | ||
warnIgnored: constructorWarnIgnored, | ||
ruleFilter | ||
ruleFilter, | ||
stats | ||
} = eslintOptions; | ||
@@ -1045,2 +1059,3 @@ const results = []; | ||
ruleFilter, | ||
stats, | ||
linter | ||
@@ -1047,0 +1062,0 @@ })); |
@@ -8,2 +8,4 @@ /** | ||
const { startTime, endTime } = require("../shared/stats"); | ||
//------------------------------------------------------------------------------ | ||
@@ -132,17 +134,23 @@ // Helpers | ||
* @param {Function} fn function to be called | ||
* @param {boolean} stats if 'stats' is true, return the result and the time difference | ||
* @returns {Function} function to be executed | ||
* @private | ||
*/ | ||
function time(key, fn) { | ||
if (typeof data[key] === "undefined") { | ||
data[key] = 0; | ||
} | ||
function time(key, fn, stats) { | ||
return function(...args) { | ||
let t = process.hrtime(); | ||
const t = startTime(); | ||
const result = fn(...args); | ||
const tdiff = endTime(t); | ||
t = process.hrtime(t); | ||
data[key] += t[0] * 1e3 + t[1] / 1e6; | ||
return result; | ||
if (enabled) { | ||
if (typeof data[key] === "undefined") { | ||
data[key] = 0; | ||
} | ||
data[key] += tdiff; | ||
} | ||
return stats ? { result, tdiff } : result; | ||
}; | ||
@@ -149,0 +157,0 @@ } |
@@ -63,2 +63,3 @@ /** | ||
* @property {string[]} _ Positional filenames or patterns | ||
* @property {boolean} [stats] Report additional statistics | ||
*/ | ||
@@ -107,2 +108,12 @@ | ||
let inspectConfigFlag; | ||
if (usingFlatConfig) { | ||
inspectConfigFlag = { | ||
option: "inspect-config", | ||
type: "Boolean", | ||
description: "Open the config inspector with the current configuration" | ||
}; | ||
} | ||
let extFlag; | ||
@@ -148,2 +159,13 @@ | ||
let statsFlag; | ||
if (usingFlatConfig) { | ||
statsFlag = { | ||
option: "stats", | ||
type: "Boolean", | ||
default: "false", | ||
description: "Add statistics to the lint report" | ||
}; | ||
} | ||
let warnIgnoredFlag; | ||
@@ -179,2 +201,3 @@ | ||
}, | ||
inspectConfigFlag, | ||
envFlag, | ||
@@ -407,5 +430,6 @@ extFlag, | ||
description: "Print the configuration for the given file" | ||
} | ||
}, | ||
statsFlag | ||
].filter(value => !!value) | ||
}); | ||
}; |
@@ -139,2 +139,11 @@ /** | ||
/* | ||
* Ignored test case properties when checking for test case duplicates. | ||
*/ | ||
const duplicationIgnoredParameters = new Set([ | ||
"name", | ||
"errors", | ||
"output" | ||
]); | ||
const forbiddenMethods = [ | ||
@@ -852,3 +861,3 @@ "applyInlineConfig", | ||
* Check if this test case is a duplicate of one we have seen before. | ||
* @param {Object} item test case object | ||
* @param {string|Object} item test case object | ||
* @param {Set<string>} seenTestCases set of serialized test cases we have seen so far (managed by this function) | ||
@@ -868,4 +877,11 @@ * @returns {void} | ||
const serializedTestCase = stringify(item); | ||
const normalizedItem = typeof item === "string" ? { code: item } : item; | ||
const serializedTestCase = stringify(normalizedItem, { | ||
replacer(key, value) { | ||
// "this" is the currently stringified object --> only ignore top-level properties | ||
return (normalizedItem !== this || !duplicationIgnoredParameters.has(key)) ? value : void 0; | ||
} | ||
}); | ||
assert( | ||
@@ -872,0 +888,0 @@ !seenTestCases.has(serializedTestCase), |
@@ -50,7 +50,5 @@ /** | ||
type: "array", | ||
items: [ | ||
{ | ||
type: "string" | ||
} | ||
], | ||
items: { | ||
type: "string" | ||
}, | ||
minItems: 0, | ||
@@ -57,0 +55,0 @@ uniqueItems: true |
@@ -13,18 +13,2 @@ /** | ||
/** | ||
* Checks all segments in a set and returns true if any are reachable. | ||
* @param {Set<CodePathSegment>} segments The segments to check. | ||
* @returns {boolean} True if any segment is reachable; false otherwise. | ||
*/ | ||
function isAnySegmentReachable(segments) { | ||
for (const segment of segments) { | ||
if (segment.reachable) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
/** | ||
* Checks whether or not a given node is a constructor. | ||
@@ -169,4 +153,3 @@ * @param {ASTNode} node A node to check. This node type is one of | ||
duplicate: "Unexpected duplicate 'super()'.", | ||
badSuper: "Unexpected 'super()' because 'super' is not a constructor.", | ||
unexpected: "Unexpected 'super()'." | ||
badSuper: "Unexpected 'super()' because 'super' is not a constructor." | ||
} | ||
@@ -191,3 +174,3 @@ }, | ||
*/ | ||
let segInfoMap = Object.create(null); | ||
const segInfoMap = Object.create(null); | ||
@@ -200,3 +183,3 @@ /** | ||
function isCalledInSomePath(segment) { | ||
return segment.reachable && segInfoMap[segment.id]?.calledInSomePaths; | ||
return segment.reachable && segInfoMap[segment.id].calledInSomePaths; | ||
} | ||
@@ -219,13 +202,2 @@ | ||
function isCalledInEveryPath(segment) { | ||
/* | ||
* If specific segment is the looped segment of the current segment, | ||
* skip the segment. | ||
* If not skipped, this never becomes true after a loop. | ||
*/ | ||
if (segment.nextSegments.length === 1 && | ||
segment.nextSegments[0]?.isLoopedPrevSegment(segment)) { | ||
return true; | ||
} | ||
return segment.reachable && segInfoMap[segment.id].calledInEveryPaths; | ||
@@ -287,5 +259,5 @@ } | ||
// Reports if `super()` lacked. | ||
const seenSegments = codePath.returnedSegments.filter(hasSegmentBeenSeen); | ||
const calledInEveryPaths = seenSegments.every(isCalledInEveryPath); | ||
const calledInSomePaths = seenSegments.some(isCalledInSomePath); | ||
const returnedSegments = codePath.returnedSegments; | ||
const calledInEveryPaths = returnedSegments.every(isCalledInEveryPath); | ||
const calledInSomePaths = returnedSegments.some(isCalledInSomePath); | ||
@@ -305,9 +277,10 @@ if (!calledInEveryPaths) { | ||
* @param {CodePathSegment} segment A code path segment to initialize. | ||
* @param {CodePathSegment} node Node that starts the segment. | ||
* @returns {void} | ||
*/ | ||
onCodePathSegmentStart(segment) { | ||
onCodePathSegmentStart(segment, node) { | ||
funcInfo.currentSegments.add(segment); | ||
if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) { | ||
if (!(funcInfo.isConstructor && funcInfo.hasExtends)) { | ||
return; | ||
@@ -319,11 +292,20 @@ } | ||
const seenPrevSegments = segment.prevSegments.filter(hasSegmentBeenSeen); | ||
// When there are previous segments, aggregates these. | ||
const prevSegments = segment.prevSegments; | ||
if (prevSegments.length > 0) { | ||
const seenPrevSegments = prevSegments.filter(hasSegmentBeenSeen); | ||
if (seenPrevSegments.length > 0) { | ||
info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath); | ||
info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath); | ||
} | ||
/* | ||
* ForStatement > *.update segments are a special case as they are created in advance, | ||
* without seen previous segments. Since they logically don't affect `calledInEveryPaths` | ||
* calculations, and they can never be a lone previous segment of another one, we'll set | ||
* their `calledInEveryPaths` to `true` to effectively ignore them in those calculations. | ||
* . | ||
*/ | ||
if (node.parent && node.parent.type === "ForStatement" && node.parent.update === node) { | ||
info.calledInEveryPaths = true; | ||
} | ||
}, | ||
@@ -354,21 +336,26 @@ | ||
onCodePathSegmentLoop(fromSegment, toSegment) { | ||
if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) { | ||
if (!(funcInfo.isConstructor && funcInfo.hasExtends)) { | ||
return; | ||
} | ||
// Update information inside of the loop. | ||
const isRealLoop = toSegment.prevSegments.length >= 2; | ||
funcInfo.codePath.traverseSegments( | ||
{ first: toSegment, last: fromSegment }, | ||
segment => { | ||
const info = segInfoMap[segment.id] ?? new SegmentInfo(); | ||
(segment, controller) => { | ||
const info = segInfoMap[segment.id]; | ||
// skip segments after the loop | ||
if (!info) { | ||
controller.skip(); | ||
return; | ||
} | ||
const seenPrevSegments = segment.prevSegments.filter(hasSegmentBeenSeen); | ||
const calledInSomePreviousPaths = seenPrevSegments.some(isCalledInSomePath); | ||
const calledInEveryPreviousPaths = seenPrevSegments.every(isCalledInEveryPath); | ||
// Updates flags. | ||
info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath); | ||
info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath); | ||
info.calledInSomePaths ||= calledInSomePreviousPaths; | ||
info.calledInEveryPaths ||= calledInEveryPreviousPaths; | ||
// If flags become true anew, reports the valid nodes. | ||
if (info.calledInSomePaths || isRealLoop) { | ||
if (calledInSomePreviousPaths) { | ||
const nodes = info.validNodes; | ||
@@ -387,5 +374,2 @@ | ||
} | ||
// save just in case we created a new SegmentInfo object | ||
segInfoMap[segment.id] = info; | ||
} | ||
@@ -401,3 +385,3 @@ ); | ||
"CallExpression:exit"(node) { | ||
if (!(funcInfo && funcInfo.isConstructor)) { | ||
if (!(funcInfo.isConstructor && funcInfo.hasExtends)) { | ||
return; | ||
@@ -412,37 +396,30 @@ } | ||
// Reports if needed. | ||
if (funcInfo.hasExtends) { | ||
const segments = funcInfo.currentSegments; | ||
let duplicate = false; | ||
let info = null; | ||
const segments = funcInfo.currentSegments; | ||
let duplicate = false; | ||
let info = null; | ||
for (const segment of segments) { | ||
for (const segment of segments) { | ||
if (segment.reachable) { | ||
info = segInfoMap[segment.id]; | ||
if (segment.reachable) { | ||
info = segInfoMap[segment.id]; | ||
duplicate = duplicate || info.calledInSomePaths; | ||
info.calledInSomePaths = info.calledInEveryPaths = true; | ||
} | ||
duplicate = duplicate || info.calledInSomePaths; | ||
info.calledInSomePaths = info.calledInEveryPaths = true; | ||
} | ||
} | ||
if (info) { | ||
if (duplicate) { | ||
context.report({ | ||
messageId: "duplicate", | ||
node | ||
}); | ||
} else if (!funcInfo.superIsConstructor) { | ||
context.report({ | ||
messageId: "badSuper", | ||
node | ||
}); | ||
} else { | ||
info.validNodes.push(node); | ||
} | ||
if (info) { | ||
if (duplicate) { | ||
context.report({ | ||
messageId: "duplicate", | ||
node | ||
}); | ||
} else if (!funcInfo.superIsConstructor) { | ||
context.report({ | ||
messageId: "badSuper", | ||
node | ||
}); | ||
} else { | ||
info.validNodes.push(node); | ||
} | ||
} else if (isAnySegmentReachable(funcInfo.currentSegments)) { | ||
context.report({ | ||
messageId: "unexpected", | ||
node | ||
}); | ||
} | ||
@@ -457,3 +434,3 @@ }, | ||
ReturnStatement(node) { | ||
if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) { | ||
if (!(funcInfo.isConstructor && funcInfo.hasExtends)) { | ||
return; | ||
@@ -478,10 +455,2 @@ } | ||
} | ||
}, | ||
/** | ||
* Resets state. | ||
* @returns {void} | ||
*/ | ||
"Program:exit"() { | ||
segInfoMap = Object.create(null); | ||
} | ||
@@ -488,0 +457,0 @@ }; |
@@ -120,3 +120,3 @@ /** | ||
ruleDef.VariableDeclaration = function(node) { | ||
if (node.kind === "let" || node.kind === "const") { | ||
if (node.kind !== "var") { | ||
markLoneBlock(node); | ||
@@ -123,0 +123,0 @@ } |
@@ -19,2 +19,7 @@ /** | ||
/** | ||
* A simple name for the types of variables that this rule supports | ||
* @typedef {'array-destructure'|'catch-clause'|'parameter'|'variable'} VariableType | ||
*/ | ||
/** | ||
* Bag of data used for formatting the `unusedVar` lint message. | ||
@@ -27,2 +32,9 @@ * @typedef {Object} UnusedVarMessageData | ||
/** | ||
* Bag of data used for formatting the `usedIgnoredVar` lint message. | ||
* @typedef {Object} UsedIgnoredVarMessageData | ||
* @property {string} varName The name of the unused var. | ||
* @property {string} additional Any additional info to be appended at the end. | ||
*/ | ||
//------------------------------------------------------------------------------ | ||
@@ -78,2 +90,5 @@ // Rule Definition | ||
type: "boolean" | ||
}, | ||
reportUsedIgnorePattern: { | ||
type: "boolean" | ||
} | ||
@@ -88,3 +103,4 @@ }, | ||
messages: { | ||
unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}." | ||
unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}.", | ||
usedIgnoredVar: "'{{varName}}' is marked as ignored but is used{{additional}}." | ||
} | ||
@@ -103,3 +119,4 @@ }, | ||
caughtErrors: "all", | ||
ignoreClassWithStaticInitBlock: false | ||
ignoreClassWithStaticInitBlock: false, | ||
reportUsedIgnorePattern: false | ||
}; | ||
@@ -118,2 +135,3 @@ | ||
config.ignoreClassWithStaticInitBlock = firstOption.ignoreClassWithStaticInitBlock || config.ignoreClassWithStaticInitBlock; | ||
config.reportUsedIgnorePattern = firstOption.reportUsedIgnorePattern || config.reportUsedIgnorePattern; | ||
@@ -139,2 +157,46 @@ if (firstOption.varsIgnorePattern) { | ||
/** | ||
* Gets a given variable's description and configured ignore pattern | ||
* based on the provided variableType | ||
* @param {VariableType} variableType a simple name for the types of variables that this rule supports | ||
* @throws {Error} (Unreachable) | ||
* @returns {[string | undefined, string | undefined]} the given variable's description and | ||
* ignore pattern | ||
*/ | ||
function getVariableDescription(variableType) { | ||
let pattern; | ||
let variableDescription; | ||
switch (variableType) { | ||
case "array-destructure": | ||
pattern = config.destructuredArrayIgnorePattern; | ||
variableDescription = "elements of array destructuring"; | ||
break; | ||
case "catch-clause": | ||
pattern = config.caughtErrorsIgnorePattern; | ||
variableDescription = "args"; | ||
break; | ||
case "parameter": | ||
pattern = config.argsIgnorePattern; | ||
variableDescription = "args"; | ||
break; | ||
case "variable": | ||
pattern = config.varsIgnorePattern; | ||
variableDescription = "vars"; | ||
break; | ||
default: | ||
throw new Error(`Unexpected variable type: ${variableType}`); | ||
} | ||
if (pattern) { | ||
pattern = pattern.toString(); | ||
} | ||
return [variableDescription, pattern]; | ||
} | ||
/** | ||
* Generates the message data about the variable being defined and unused, | ||
@@ -146,23 +208,38 @@ * including the ignore pattern if configured. | ||
function getDefinedMessageData(unusedVar) { | ||
const defType = unusedVar.defs && unusedVar.defs[0] && unusedVar.defs[0].type; | ||
let type; | ||
let pattern; | ||
const def = unusedVar.defs && unusedVar.defs[0]; | ||
let additionalMessageData = ""; | ||
if (defType === "CatchClause" && config.caughtErrorsIgnorePattern) { | ||
type = "args"; | ||
pattern = config.caughtErrorsIgnorePattern.toString(); | ||
} else if (defType === "Parameter" && config.argsIgnorePattern) { | ||
type = "args"; | ||
pattern = config.argsIgnorePattern.toString(); | ||
} else if (defType !== "Parameter" && defType !== "CatchClause" && config.varsIgnorePattern) { | ||
type = "vars"; | ||
pattern = config.varsIgnorePattern.toString(); | ||
if (def) { | ||
let pattern; | ||
let variableDescription; | ||
switch (def.type) { | ||
case "CatchClause": | ||
if (config.caughtErrorsIgnorePattern) { | ||
[variableDescription, pattern] = getVariableDescription("catch-clause"); | ||
} | ||
break; | ||
case "Parameter": | ||
if (config.argsIgnorePattern) { | ||
[variableDescription, pattern] = getVariableDescription("parameter"); | ||
} | ||
break; | ||
default: | ||
if (config.varsIgnorePattern) { | ||
[variableDescription, pattern] = getVariableDescription("variable"); | ||
} | ||
break; | ||
} | ||
if (pattern && variableDescription) { | ||
additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`; | ||
} | ||
} | ||
const additional = type ? `. Allowed unused ${type} must match ${pattern}` : ""; | ||
return { | ||
varName: unusedVar.name, | ||
action: "defined", | ||
additional | ||
additional: additionalMessageData | ||
}; | ||
@@ -178,9 +255,18 @@ } | ||
function getAssignedMessageData(unusedVar) { | ||
const def = unusedVar.defs[0]; | ||
let additional = ""; | ||
const def = unusedVar.defs && unusedVar.defs[0]; | ||
let additionalMessageData = ""; | ||
if (config.destructuredArrayIgnorePattern && def && def.name.parent.type === "ArrayPattern") { | ||
additional = `. Allowed unused elements of array destructuring patterns must match ${config.destructuredArrayIgnorePattern.toString()}`; | ||
} else if (config.varsIgnorePattern) { | ||
additional = `. Allowed unused vars must match ${config.varsIgnorePattern.toString()}`; | ||
if (def) { | ||
let pattern; | ||
let variableDescription; | ||
if (def.name.parent.type === "ArrayPattern" && config.destructuredArrayIgnorePattern) { | ||
[variableDescription, pattern] = getVariableDescription("array-destructure"); | ||
} else if (config.varsIgnorePattern) { | ||
[variableDescription, pattern] = getVariableDescription("variable"); | ||
} | ||
if (pattern && variableDescription) { | ||
additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`; | ||
} | ||
} | ||
@@ -191,6 +277,29 @@ | ||
action: "assigned a value", | ||
additional | ||
additional: additionalMessageData | ||
}; | ||
} | ||
/** | ||
* Generate the warning message about a variable being used even though | ||
* it is marked as being ignored. | ||
* @param {Variable} variable eslint-scope variable object | ||
* @param {VariableType} variableType a simple name for the types of variables that this rule supports | ||
* @returns {UsedIgnoredVarMessageData} The message data to be used with | ||
* this used ignored variable. | ||
*/ | ||
function getUsedIgnoredMessageData(variable, variableType) { | ||
const [variableDescription, pattern] = getVariableDescription(variableType); | ||
let additionalMessageData = ""; | ||
if (pattern && variableDescription) { | ||
additionalMessageData = `. Used ${variableDescription} must not match ${pattern}`; | ||
} | ||
return { | ||
varName: variable.name, | ||
additional: additionalMessageData | ||
}; | ||
} | ||
//-------------------------------------------------------------------------- | ||
@@ -545,4 +654,9 @@ // Helpers | ||
function isUsedVariable(variable) { | ||
const functionNodes = getFunctionDefinitions(variable), | ||
isFunctionDefinition = functionNodes.length > 0; | ||
if (variable.eslintUsed) { | ||
return true; | ||
} | ||
const functionNodes = getFunctionDefinitions(variable); | ||
const isFunctionDefinition = functionNodes.length > 0; | ||
let rhsNode = null; | ||
@@ -603,7 +717,12 @@ | ||
// skip function expression names and variables marked with markVariableAsUsed() | ||
if (scope.functionExpressionScope || variable.eslintUsed) { | ||
// skip function expression names | ||
if (scope.functionExpressionScope) { | ||
continue; | ||
} | ||
// skip variables marked with markVariableAsUsed() | ||
if (!config.reportUsedIgnorePattern && variable.eslintUsed) { | ||
continue; | ||
} | ||
// skip implicit "arguments" variable | ||
@@ -630,2 +749,10 @@ if (scope.type === "function" && variable.name === "arguments" && variable.identifiers.length === 0) { | ||
) { | ||
if (config.reportUsedIgnorePattern && isUsedVariable(variable)) { | ||
context.report({ | ||
node: def.name, | ||
messageId: "usedIgnoredVar", | ||
data: getUsedIgnoredMessageData(variable, "array-destructure") | ||
}); | ||
} | ||
continue; | ||
@@ -650,2 +777,10 @@ } | ||
if (config.caughtErrorsIgnorePattern && config.caughtErrorsIgnorePattern.test(def.name.name)) { | ||
if (config.reportUsedIgnorePattern && isUsedVariable(variable)) { | ||
context.report({ | ||
node: def.name, | ||
messageId: "usedIgnoredVar", | ||
data: getUsedIgnoredMessageData(variable, "catch-clause") | ||
}); | ||
} | ||
continue; | ||
@@ -667,2 +802,10 @@ } | ||
if (config.argsIgnorePattern && config.argsIgnorePattern.test(def.name.name)) { | ||
if (config.reportUsedIgnorePattern && isUsedVariable(variable)) { | ||
context.report({ | ||
node: def.name, | ||
messageId: "usedIgnoredVar", | ||
data: getUsedIgnoredMessageData(variable, "parameter") | ||
}); | ||
} | ||
continue; | ||
@@ -679,2 +822,10 @@ } | ||
if (config.varsIgnorePattern && config.varsIgnorePattern.test(def.name.name)) { | ||
if (config.reportUsedIgnorePattern && isUsedVariable(variable)) { | ||
context.report({ | ||
node: def.name, | ||
messageId: "usedIgnoredVar", | ||
data: getUsedIgnoredMessageData(variable, "variable") | ||
}); | ||
} | ||
continue; | ||
@@ -743,4 +894,3 @@ } | ||
}; | ||
} | ||
}; |
@@ -185,3 +185,3 @@ /** | ||
(methodName === "indexOf" || methodName === "lastIndexOf") && | ||
node.arguments.length === 1 && | ||
node.arguments.length <= 2 && | ||
isNaNIdentifier(node.arguments[0]) | ||
@@ -194,3 +194,3 @@ ) { | ||
*/ | ||
const isSuggestable = node.arguments[0].type !== "SequenceExpression"; | ||
const isSuggestable = node.arguments[0].type !== "SequenceExpression" && !node.arguments[1]; | ||
const suggestedFixes = []; | ||
@@ -197,0 +197,0 @@ |
@@ -192,2 +192,3 @@ /** | ||
* @property {number} fixableWarningCount Number of fixable warnings for the result. | ||
* @property {Stats} [stats] The performance statistics collected with the `stats` flag. | ||
* @property {string} [source] The source code of the file that was linted. | ||
@@ -199,2 +200,35 @@ * @property {string} [output] The source code of the file that was linted, with as many fixes applied as possible. | ||
/** | ||
* Performance statistics | ||
* @typedef {Object} Stats | ||
* @property {number} fixPasses The number of times ESLint has applied at least one fix after linting. | ||
* @property {Times} times The times spent on (parsing, fixing, linting) a file. | ||
*/ | ||
/** | ||
* Performance Times for each ESLint pass | ||
* @typedef {Object} Times | ||
* @property {TimePass[]} passes Time passes | ||
*/ | ||
/** | ||
* @typedef {Object} TimePass | ||
* @property {ParseTime} parse The parse object containing all parse time information. | ||
* @property {Record<string, RuleTime>} [rules] The rules object containing all lint time information for each rule. | ||
* @property {FixTime} fix The parse object containing all fix time information. | ||
* @property {number} total The total time that is spent on (parsing, fixing, linting) a file. | ||
*/ | ||
/** | ||
* @typedef {Object} ParseTime | ||
* @property {number} total The total time that is spent when parsing a file. | ||
*/ | ||
/** | ||
* @typedef {Object} RuleTime | ||
* @property {number} total The total time that is spent on a rule. | ||
*/ | ||
/** | ||
* @typedef {Object} FixTime | ||
* @property {number} total The total time that is spent on applying fixes to the code. | ||
*/ | ||
/** | ||
* Information provided when the maximum warning threshold is exceeded. | ||
@@ -201,0 +235,0 @@ * @typedef {Object} MaxWarningsExceeded |
@@ -12,3 +12,3 @@ /** | ||
const Cursor = require("./cursor"); | ||
const utils = require("./utils"); | ||
const { getLastIndex, getFirstIndex } = require("./utils"); | ||
@@ -35,4 +35,4 @@ //------------------------------------------------------------------------------ | ||
this.tokens = tokens; | ||
this.index = utils.getLastIndex(tokens, indexMap, endLoc); | ||
this.indexEnd = utils.getFirstIndex(tokens, indexMap, startLoc); | ||
this.index = getLastIndex(tokens, indexMap, endLoc); | ||
this.indexEnd = getFirstIndex(tokens, indexMap, startLoc); | ||
} | ||
@@ -39,0 +39,0 @@ |
@@ -89,3 +89,5 @@ /** | ||
exports.forward = new CursorFactory(ForwardTokenCursor, ForwardTokenCommentCursor); | ||
exports.backward = new CursorFactory(BackwardTokenCursor, BackwardTokenCommentCursor); | ||
module.exports = { | ||
forward: new CursorFactory(ForwardTokenCursor, ForwardTokenCommentCursor), | ||
backward: new CursorFactory(BackwardTokenCursor, BackwardTokenCommentCursor) | ||
}; |
@@ -12,3 +12,3 @@ /** | ||
const Cursor = require("./cursor"); | ||
const utils = require("./utils"); | ||
const { getFirstIndex, search } = require("./utils"); | ||
@@ -36,4 +36,4 @@ //------------------------------------------------------------------------------ | ||
this.comments = comments; | ||
this.tokenIndex = utils.getFirstIndex(tokens, indexMap, startLoc); | ||
this.commentIndex = utils.search(comments, startLoc); | ||
this.tokenIndex = getFirstIndex(tokens, indexMap, startLoc); | ||
this.commentIndex = search(comments, startLoc); | ||
this.border = endLoc; | ||
@@ -40,0 +40,0 @@ } |
@@ -12,3 +12,3 @@ /** | ||
const Cursor = require("./cursor"); | ||
const utils = require("./utils"); | ||
const { getFirstIndex, getLastIndex } = require("./utils"); | ||
@@ -35,4 +35,4 @@ //------------------------------------------------------------------------------ | ||
this.tokens = tokens; | ||
this.index = utils.getFirstIndex(tokens, indexMap, startLoc); | ||
this.indexEnd = utils.getLastIndex(tokens, indexMap, endLoc); | ||
this.index = getFirstIndex(tokens, indexMap, startLoc); | ||
this.indexEnd = getLastIndex(tokens, indexMap, endLoc); | ||
} | ||
@@ -39,0 +39,0 @@ |
@@ -18,3 +18,3 @@ "use strict"; | ||
If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team. | ||
If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting. | ||
`; | ||
@@ -21,0 +21,0 @@ |
@@ -14,4 +14,4 @@ "use strict"; | ||
If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team. | ||
If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting. | ||
`.trimStart(); | ||
}; |
@@ -17,4 +17,4 @@ "use strict"; | ||
If you still can't figure out the problem, please stop by https://eslint.org/chat/help to chat with the team. | ||
If you still can't figure out the problem, please see https://eslint.org/docs/latest/use/troubleshooting. | ||
`.trimStart(); | ||
}; |
{ | ||
"name": "eslint", | ||
"version": "9.0.0-rc.0", | ||
"version": "9.0.0", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -33,2 +33,3 @@ "description": "An AST-based pattern checker for JavaScript.", | ||
"test": "node Makefile.js test", | ||
"test:browser": "node Makefile.js wdio", | ||
"test:cli": "mocha", | ||
@@ -71,4 +72,4 @@ "test:fuzz": "node Makefile.js fuzz", | ||
"@eslint/eslintrc": "^3.0.2", | ||
"@eslint/js": "9.0.0-rc.0", | ||
"@humanwhocodes/config-array": "^0.11.14", | ||
"@eslint/js": "9.0.0", | ||
"@humanwhocodes/config-array": "^0.12.3", | ||
"@humanwhocodes/module-importer": "^1.0.1", | ||
@@ -139,3 +140,3 @@ "@nodelib/fs.walk": "^1.2.8", | ||
"js-yaml": "^4.1.0", | ||
"knip": "^5.0.1", | ||
"knip": "^5.8.0", | ||
"lint-staged": "^11.0.0", | ||
@@ -145,3 +146,3 @@ "load-perf": "^0.2.0", | ||
"markdown-it-container": "^3.0.0", | ||
"markdownlint": "^0.33.0", | ||
"markdownlint": "^0.34.0", | ||
"markdownlint-cli": "^0.39.0", | ||
@@ -148,0 +149,0 @@ "marked": "^4.0.8", |
@@ -62,11 +62,7 @@ [![npm version](https://img.shields.io/npm/v/eslint.svg)](https://www.npmjs.com/package/eslint) | ||
After running `npm init @eslint/config`, you'll have an `.eslintrc` 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: | ||
```json | ||
{ | ||
"rules": { | ||
"semi": ["error", "always"], | ||
"quotes": ["error", "double"] | ||
} | ||
} | ||
```js | ||
import pluginJs from "@eslint/js"; | ||
export default [ pluginJs.configs.recommended, ]; | ||
``` | ||
@@ -310,3 +306,3 @@ | ||
<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> | ||
<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://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" 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> | ||
<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> | ||
<!--sponsorsend--> | ||
@@ -313,0 +309,0 @@ |
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
3025150
398
71163
0
313
18
+ Added@eslint/js@9.0.0(transitive)
+ Added@humanwhocodes/config-array@0.12.3(transitive)
- Removed@eslint/js@9.0.0-rc.09.1.1(transitive)
- Removed@humanwhocodes/config-array@0.11.140.13.0(transitive)
- Removed@humanwhocodes/retry@0.2.3(transitive)
- Removedeslint@9.1.1(transitive)
Updated@eslint/js@9.0.0