eslint
Advanced tools
Comparing version 8.43.0 to 8.44.0
@@ -131,3 +131,7 @@ /** | ||
const es2024 = { | ||
...es2023 | ||
}; | ||
//----------------------------------------------------------------------------- | ||
@@ -149,3 +153,4 @@ // Exports | ||
es2022, | ||
es2023 | ||
es2023, | ||
es2024 | ||
}; |
@@ -161,3 +161,13 @@ /** | ||
function calculateStatsPerFile(messages) { | ||
return messages.reduce((stat, message) => { | ||
const stat = { | ||
errorCount: 0, | ||
fatalErrorCount: 0, | ||
warningCount: 0, | ||
fixableErrorCount: 0, | ||
fixableWarningCount: 0 | ||
}; | ||
for (let i = 0; i < messages.length; i++) { | ||
const message = messages[i]; | ||
if (message.fatal || message.severity === 2) { | ||
@@ -177,10 +187,4 @@ stat.errorCount++; | ||
} | ||
return stat; | ||
}, { | ||
errorCount: 0, | ||
fatalErrorCount: 0, | ||
warningCount: 0, | ||
fixableErrorCount: 0, | ||
fixableWarningCount: 0 | ||
}); | ||
} | ||
return stat; | ||
} | ||
@@ -195,3 +199,13 @@ | ||
function calculateStatsPerRun(results) { | ||
return results.reduce((stat, result) => { | ||
const stat = { | ||
errorCount: 0, | ||
fatalErrorCount: 0, | ||
warningCount: 0, | ||
fixableErrorCount: 0, | ||
fixableWarningCount: 0 | ||
}; | ||
for (let i = 0; i < results.length; i++) { | ||
const result = results[i]; | ||
stat.errorCount += result.errorCount; | ||
@@ -202,10 +216,5 @@ stat.fatalErrorCount += result.fatalErrorCount; | ||
stat.fixableWarningCount += result.fixableWarningCount; | ||
return stat; | ||
}, { | ||
errorCount: 0, | ||
fatalErrorCount: 0, | ||
warningCount: 0, | ||
fixableErrorCount: 0, | ||
fixableWarningCount: 0 | ||
}); | ||
} | ||
return stat; | ||
} | ||
@@ -212,0 +221,0 @@ |
@@ -106,3 +106,13 @@ /** | ||
function calculateStatsPerFile(messages) { | ||
return messages.reduce((stat, message) => { | ||
const stat = { | ||
errorCount: 0, | ||
fatalErrorCount: 0, | ||
warningCount: 0, | ||
fixableErrorCount: 0, | ||
fixableWarningCount: 0 | ||
}; | ||
for (let i = 0; i < messages.length; i++) { | ||
const message = messages[i]; | ||
if (message.fatal || message.severity === 2) { | ||
@@ -122,36 +132,7 @@ stat.errorCount++; | ||
} | ||
return stat; | ||
}, { | ||
errorCount: 0, | ||
fatalErrorCount: 0, | ||
warningCount: 0, | ||
fixableErrorCount: 0, | ||
fixableWarningCount: 0 | ||
}); | ||
} | ||
return stat; | ||
} | ||
/** | ||
* It will calculate the error and warning count for collection of results from all files | ||
* @param {LintResult[]} results Collection of messages from all the files | ||
* @returns {Object} Contains the stats | ||
* @private | ||
*/ | ||
function calculateStatsPerRun(results) { | ||
return results.reduce((stat, result) => { | ||
stat.errorCount += result.errorCount; | ||
stat.fatalErrorCount += result.fatalErrorCount; | ||
stat.warningCount += result.warningCount; | ||
stat.fixableErrorCount += result.fixableErrorCount; | ||
stat.fixableWarningCount += result.fixableWarningCount; | ||
return stat; | ||
}, { | ||
errorCount: 0, | ||
fatalErrorCount: 0, | ||
warningCount: 0, | ||
fixableErrorCount: 0, | ||
fixableWarningCount: 0 | ||
}); | ||
} | ||
/** | ||
* Create rulesMeta object. | ||
@@ -556,39 +537,2 @@ * @param {Map<string,Rule>} rules a map of rules from which to generate the object. | ||
/** | ||
* Collect used deprecated rules. | ||
* @param {Array<FlatConfig>} configs The configs to evaluate. | ||
* @returns {IterableIterator<DeprecatedRuleInfo>} Used deprecated rules. | ||
*/ | ||
function *iterateRuleDeprecationWarnings(configs) { | ||
const processedRuleIds = new Set(); | ||
for (const config of configs) { | ||
for (const [ruleId, ruleConfig] of Object.entries(config.rules)) { | ||
// Skip if it was processed. | ||
if (processedRuleIds.has(ruleId)) { | ||
continue; | ||
} | ||
processedRuleIds.add(ruleId); | ||
// Skip if it's not used. | ||
if (!getRuleSeverity(ruleConfig)) { | ||
continue; | ||
} | ||
const rule = getRuleFromConfig(ruleId, config); | ||
// Skip if it's not deprecated. | ||
if (!(rule && rule.meta && rule.meta.deprecated)) { | ||
continue; | ||
} | ||
// This rule was used and deprecated. | ||
yield { | ||
ruleId, | ||
replacedBy: rule.meta.replacedBy || [] | ||
}; | ||
} | ||
} | ||
} | ||
/** | ||
* Creates an error to be thrown when an array of results passed to `getRulesMetaForResults` was not created by the current engine. | ||
@@ -817,3 +761,2 @@ * @returns {TypeError} An error object. | ||
const startTime = Date.now(); | ||
const usedConfigs = []; | ||
const fixTypesSet = fixTypes ? new Set(fixTypes) : null; | ||
@@ -876,11 +819,2 @@ | ||
/* | ||
* Store used configs for: | ||
* - this method uses to collect used deprecated rules. | ||
* - `--fix-type` option uses to get the loaded rule's meta data. | ||
*/ | ||
if (!usedConfigs.includes(config)) { | ||
usedConfigs.push(config); | ||
} | ||
// Skip if there is cached result. | ||
@@ -954,18 +888,6 @@ if (lintResultCache) { | ||
let usedDeprecatedRules; | ||
const finalResults = results.filter(result => !!result); | ||
return processLintReport(this, { | ||
results: finalResults, | ||
...calculateStatsPerRun(finalResults), | ||
// Initialize it lazily because CLI and `ESLint` API don't use it. | ||
get usedDeprecatedRules() { | ||
if (!usedDeprecatedRules) { | ||
usedDeprecatedRules = Array.from( | ||
iterateRuleDeprecationWarnings(usedConfigs) | ||
); | ||
} | ||
return usedDeprecatedRules; | ||
} | ||
results: finalResults | ||
}); | ||
@@ -1032,3 +954,2 @@ } | ||
const resolvedFilename = path.resolve(cwd, filePath || "__placeholder__.js"); | ||
let config; | ||
@@ -1042,5 +963,2 @@ // Clear the last used config arrays. | ||
// TODO: Needed? | ||
config = configs.getConfig(resolvedFilename); | ||
// Do lint. | ||
@@ -1060,17 +978,5 @@ results.push(verifyText({ | ||
debug(`Linting complete in: ${Date.now() - startTime}ms`); | ||
let usedDeprecatedRules; | ||
return processLintReport(this, { | ||
results, | ||
...calculateStatsPerRun(results), | ||
// Initialize it lazily because CLI and `ESLint` API don't use it. | ||
get usedDeprecatedRules() { | ||
if (!usedDeprecatedRules) { | ||
usedDeprecatedRules = Array.from( | ||
iterateRuleDeprecationWarnings(config) | ||
); | ||
} | ||
return usedDeprecatedRules; | ||
} | ||
results | ||
}); | ||
@@ -1077,0 +983,0 @@ |
@@ -36,3 +36,3 @@ /** | ||
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */ | ||
/** | ||
@@ -76,3 +76,2 @@ * A test case that is expected to pass lint. | ||
*/ | ||
/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */ | ||
@@ -79,0 +78,0 @@ //------------------------------------------------------------------------------ |
@@ -66,3 +66,3 @@ /** | ||
/* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */ | ||
/** | ||
@@ -112,3 +112,2 @@ * A test case that is expected to pass lint. | ||
*/ | ||
/* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */ | ||
@@ -115,0 +114,0 @@ //------------------------------------------------------------------------------ |
@@ -227,49 +227,41 @@ /** | ||
/** | ||
* Creates a new `AccessorData` object for the given getter or setter node. | ||
* @param {ASTNode} node A getter or setter node. | ||
* @returns {AccessorData} New `AccessorData` object that contains the given node. | ||
* Checks accessor pairs in the given list of nodes. | ||
* @param {ASTNode[]} nodes The list to check. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
function createAccessorData(node) { | ||
const name = astUtils.getStaticPropertyName(node); | ||
const key = (name !== null) ? name : sourceCode.getTokens(node.key); | ||
function checkList(nodes) { | ||
const accessors = []; | ||
let found = false; | ||
return { | ||
key, | ||
getters: node.kind === "get" ? [node] : [], | ||
setters: node.kind === "set" ? [node] : [] | ||
}; | ||
} | ||
for (let i = 0; i < nodes.length; i++) { | ||
const node = nodes[i]; | ||
/** | ||
* Merges the given `AccessorData` object into the given accessors list. | ||
* @param {AccessorData[]} accessors The list to merge into. | ||
* @param {AccessorData} accessorData The object to merge. | ||
* @returns {AccessorData[]} The same instance with the merged object. | ||
* @private | ||
*/ | ||
function mergeAccessorData(accessors, accessorData) { | ||
const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key)); | ||
if (isAccessorKind(node)) { | ||
if (equalKeyElement) { | ||
equalKeyElement.getters.push(...accessorData.getters); | ||
equalKeyElement.setters.push(...accessorData.setters); | ||
} else { | ||
accessors.push(accessorData); | ||
} | ||
// Creates a new `AccessorData` object for the given getter or setter node. | ||
const name = astUtils.getStaticPropertyName(node); | ||
const key = (name !== null) ? name : sourceCode.getTokens(node.key); | ||
return accessors; | ||
} | ||
// Merges the given `AccessorData` object into the given accessors list. | ||
for (let j = 0; j < accessors.length; j++) { | ||
const accessor = accessors[j]; | ||
/** | ||
* Checks accessor pairs in the given list of nodes. | ||
* @param {ASTNode[]} nodes The list to check. | ||
* @returns {void} | ||
* @private | ||
*/ | ||
function checkList(nodes) { | ||
const accessors = nodes | ||
.filter(isAccessorKind) | ||
.map(createAccessorData) | ||
.reduce(mergeAccessorData, []); | ||
if (areEqualKeys(accessor.key, key)) { | ||
accessor.getters.push(...node.kind === "get" ? [node] : []); | ||
accessor.setters.push(...node.kind === "set" ? [node] : []); | ||
found = true; | ||
break; | ||
} | ||
} | ||
if (!found) { | ||
accessors.push({ | ||
key, | ||
getters: node.kind === "get" ? [node] : [], | ||
setters: node.kind === "set" ? [node] : [] | ||
}); | ||
} | ||
found = false; | ||
} | ||
} | ||
@@ -276,0 +268,0 @@ for (const { getters, setters } of accessors) { |
@@ -243,7 +243,11 @@ /** | ||
const linebreaksCount = node.elements.map((element, i) => { | ||
let linebreaksCount = 0; | ||
for (let i = 0; i < node.elements.length; i++) { | ||
const element = node.elements[i]; | ||
const previousElement = elements[i - 1]; | ||
if (i === 0 || element === null || previousElement === null) { | ||
return false; | ||
continue; | ||
} | ||
@@ -255,4 +259,6 @@ | ||
return !astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement); | ||
}).filter(isBreak => isBreak === true).length; | ||
if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) { | ||
linebreaksCount++; | ||
} | ||
} | ||
@@ -259,0 +265,0 @@ const needsLinebreaks = ( |
@@ -136,4 +136,3 @@ /** | ||
node.computed && | ||
node.property.type === "TemplateLiteral" && | ||
node.property.expressions.length === 0 | ||
astUtils.isStaticTemplateLiteral(node.property) | ||
) { | ||
@@ -140,0 +139,0 @@ checkComputedProperty(node, node.property.quasis[0].value.cooked); |
@@ -141,39 +141,2 @@ /** | ||
/** | ||
* Creates a new `AccessorData` object for the given getter or setter node. | ||
* @param {ASTNode} node A getter or setter node. | ||
* @returns {AccessorData} New `AccessorData` object that contains the given node. | ||
* @private | ||
*/ | ||
function createAccessorData(node) { | ||
const name = astUtils.getStaticPropertyName(node); | ||
const key = (name !== null) ? name : sourceCode.getTokens(node.key); | ||
return { | ||
key, | ||
getters: node.kind === "get" ? [node] : [], | ||
setters: node.kind === "set" ? [node] : [] | ||
}; | ||
} | ||
/** | ||
* Merges the given `AccessorData` object into the given accessors list. | ||
* @param {AccessorData[]} accessors The list to merge into. | ||
* @param {AccessorData} accessorData The object to merge. | ||
* @returns {AccessorData[]} The same instance with the merged object. | ||
* @private | ||
*/ | ||
function mergeAccessorData(accessors, accessorData) { | ||
const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key)); | ||
if (equalKeyElement) { | ||
equalKeyElement.getters.push(...accessorData.getters); | ||
equalKeyElement.setters.push(...accessorData.setters); | ||
} else { | ||
accessors.push(accessorData); | ||
} | ||
return accessors; | ||
} | ||
/** | ||
* Checks accessor pairs in the given list of nodes. | ||
@@ -186,8 +149,36 @@ * @param {ASTNode[]} nodes The list to check. | ||
function checkList(nodes, shouldCheck) { | ||
const accessors = nodes | ||
.filter(shouldCheck) | ||
.filter(isAccessorKind) | ||
.map(createAccessorData) | ||
.reduce(mergeAccessorData, []); | ||
const accessors = []; | ||
let found = false; | ||
for (let i = 0; i < nodes.length; i++) { | ||
const node = nodes[i]; | ||
if (shouldCheck(node) && isAccessorKind(node)) { | ||
// Creates a new `AccessorData` object for the given getter or setter node. | ||
const name = astUtils.getStaticPropertyName(node); | ||
const key = (name !== null) ? name : sourceCode.getTokens(node.key); | ||
// Merges the given `AccessorData` object into the given accessors list. | ||
for (let j = 0; j < accessors.length; j++) { | ||
const accessor = accessors[j]; | ||
if (areEqualKeys(accessor.key, key)) { | ||
accessor.getters.push(...node.kind === "get" ? [node] : []); | ||
accessor.setters.push(...node.kind === "set" ? [node] : []); | ||
found = true; | ||
break; | ||
} | ||
} | ||
if (!found) { | ||
accessors.push({ | ||
key, | ||
getters: node.kind === "get" ? [node] : [], | ||
setters: node.kind === "set" ? [node] : [] | ||
}); | ||
} | ||
found = false; | ||
} | ||
} | ||
for (const { getters, setters } of accessors) { | ||
@@ -194,0 +185,0 @@ |
@@ -373,4 +373,7 @@ /** | ||
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" && | ||
(astUtils.getPrecedence({ type: "AssignmentExpression" }) < astUtils.getPrecedence(logical.parent)); | ||
const parentPrecedence = astUtils.getPrecedence(logical.parent); | ||
const requiresOuterParenthesis = logical.parent.type !== "ExpressionStatement" && ( | ||
parentPrecedence === -1 || | ||
astUtils.getPrecedence({ type: "AssignmentExpression" }) < parentPrecedence | ||
); | ||
@@ -377,0 +380,0 @@ if (!astUtils.isParenthesised(sourceCode, logical) && requiresOuterParenthesis) { |
@@ -255,15 +255,19 @@ /** | ||
/** | ||
* A reducer to group an AST node by line number, both start and end. | ||
* @param {Object} acc the accumulator | ||
* @param {ASTNode} node the AST node in question | ||
* @returns {Object} the modified accumulator | ||
* @private | ||
* | ||
* reduce an array of AST nodes by line number, both start and end. | ||
* @param {ASTNode[]} arr array of AST nodes | ||
* @returns {Object} accululated AST nodes | ||
*/ | ||
function groupByLineNumber(acc, node) { | ||
for (let i = node.loc.start.line; i <= node.loc.end.line; ++i) { | ||
ensureArrayAndPush(acc, i, node); | ||
function groupArrayByLineNumber(arr) { | ||
const obj = {}; | ||
for (let i = 0; i < arr.length; i++) { | ||
const node = arr[i]; | ||
for (let j = node.loc.start.line; j <= node.loc.end.line; ++j) { | ||
ensureArrayAndPush(obj, j, node); | ||
} | ||
} | ||
return acc; | ||
return obj; | ||
} | ||
@@ -316,9 +320,9 @@ | ||
const strings = getAllStrings(); | ||
const stringsByLine = strings.reduce(groupByLineNumber, {}); | ||
const stringsByLine = groupArrayByLineNumber(strings); | ||
const templateLiterals = getAllTemplateLiterals(); | ||
const templateLiteralsByLine = templateLiterals.reduce(groupByLineNumber, {}); | ||
const templateLiteralsByLine = groupArrayByLineNumber(templateLiterals); | ||
const regExpLiterals = getAllRegExpLiterals(); | ||
const regExpLiteralsByLine = regExpLiterals.reduce(groupByLineNumber, {}); | ||
const regExpLiteralsByLine = groupArrayByLineNumber(regExpLiterals); | ||
@@ -325,0 +329,0 @@ lines.forEach((line, i) => { |
@@ -49,2 +49,3 @@ /** | ||
conditionalAssign: { type: "boolean" }, | ||
ternaryOperandBinaryExpressions: { type: "boolean" }, | ||
nestedBinaryExpressions: { type: "boolean" }, | ||
@@ -80,2 +81,3 @@ returnAssign: { type: "boolean" }, | ||
const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false; | ||
const EXCEPT_COND_TERNARY = ALL_NODES && context.options[1] && context.options[1].ternaryOperandBinaryExpressions === false; | ||
const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false; | ||
@@ -891,3 +893,7 @@ const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false; | ||
} | ||
const availableTypes = new Set(["BinaryExpression", "LogicalExpression"]); | ||
if ( | ||
!(EXCEPT_COND_TERNARY && availableTypes.has(node.test.type)) && | ||
!isCondAssignException(node) && | ||
@@ -899,7 +905,11 @@ hasExcessParensWithPrecedence(node.test, precedence({ type: "LogicalExpression", operator: "||" })) | ||
if (hasExcessParensWithPrecedence(node.consequent, PRECEDENCE_OF_ASSIGNMENT_EXPR)) { | ||
if ( | ||
!(EXCEPT_COND_TERNARY && availableTypes.has(node.consequent.type)) && | ||
hasExcessParensWithPrecedence(node.consequent, PRECEDENCE_OF_ASSIGNMENT_EXPR)) { | ||
report(node.consequent); | ||
} | ||
if (hasExcessParensWithPrecedence(node.alternate, PRECEDENCE_OF_ASSIGNMENT_EXPR)) { | ||
if ( | ||
!(EXCEPT_COND_TERNARY && availableTypes.has(node.alternate.type)) && | ||
hasExcessParensWithPrecedence(node.alternate, PRECEDENCE_OF_ASSIGNMENT_EXPR)) { | ||
report(node.alternate); | ||
@@ -906,0 +916,0 @@ } |
@@ -42,2 +42,19 @@ /** | ||
/** | ||
* Checks if a node or token is fixable. | ||
* A node is fixable if it can be removed without turning a subsequent statement into a directive after fixing other nodes. | ||
* @param {Token} nodeOrToken The node or token to check. | ||
* @returns {boolean} Whether or not the node is fixable. | ||
*/ | ||
function isFixable(nodeOrToken) { | ||
const nextToken = sourceCode.getTokenAfter(nodeOrToken); | ||
if (!nextToken || nextToken.type !== "String") { | ||
return true; | ||
} | ||
const stringNode = sourceCode.getNodeByRangeIndex(nextToken.range[0]); | ||
return !astUtils.isTopLevelExpressionStatement(stringNode.parent); | ||
} | ||
/** | ||
* Reports an unnecessary semicolon error. | ||
@@ -51,13 +68,14 @@ * @param {Node|Token} nodeOrToken A node or a token to be reported. | ||
messageId: "unexpected", | ||
fix(fixer) { | ||
fix: isFixable(nodeOrToken) | ||
? fixer => | ||
/* | ||
* Expand the replacement range to include the surrounding | ||
* tokens to avoid conflicting with semi. | ||
* https://github.com/eslint/eslint/issues/7928 | ||
*/ | ||
return new FixTracker(fixer, context.sourceCode) | ||
.retainSurroundingTokens(nodeOrToken) | ||
.remove(nodeOrToken); | ||
} | ||
/* | ||
* Expand the replacement range to include the surrounding | ||
* tokens to avoid conflicting with semi. | ||
* https://github.com/eslint/eslint/issues/7928 | ||
*/ | ||
new FixTracker(fixer, context.sourceCode) | ||
.retainSurroundingTokens(nodeOrToken) | ||
.remove(nodeOrToken) | ||
: null | ||
}); | ||
@@ -64,0 +82,0 @@ } |
@@ -86,3 +86,3 @@ /** | ||
function addDecimalPointToNumber(stringNumber) { | ||
return `${stringNumber.slice(0, 1)}.${stringNumber.slice(1)}`; | ||
return `${stringNumber[0]}.${stringNumber.slice(1)}`; | ||
} | ||
@@ -96,3 +96,8 @@ | ||
function removeLeadingZeros(numberAsString) { | ||
return numberAsString.replace(/^0*/u, ""); | ||
for (let i = 0; i < numberAsString.length; i++) { | ||
if (numberAsString[i] !== "0") { | ||
return numberAsString.slice(i); | ||
} | ||
} | ||
return numberAsString; | ||
} | ||
@@ -106,3 +111,8 @@ | ||
function removeTrailingZeros(numberAsString) { | ||
return numberAsString.replace(/0*$/u, ""); | ||
for (let i = numberAsString.length - 1; i >= 0; i--) { | ||
if (numberAsString[i] !== "0") { | ||
return numberAsString.slice(0, i + 1); | ||
} | ||
} | ||
return numberAsString; | ||
} | ||
@@ -134,3 +144,3 @@ | ||
if (trimmedFloat.startsWith(".")) { | ||
const decimalDigits = trimmedFloat.split(".").pop(); | ||
const decimalDigits = trimmedFloat.slice(1); | ||
const significantDigits = removeLeadingZeros(decimalDigits); | ||
@@ -151,3 +161,2 @@ | ||
/** | ||
@@ -168,3 +177,2 @@ * Converts a base ten number to proper scientific notation | ||
return `${normalizedCoefficient}e${magnitude}`; | ||
} | ||
@@ -171,0 +179,0 @@ |
@@ -9,2 +9,8 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
const astUtils = require("./utils/ast-utils"); | ||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
@@ -121,11 +127,2 @@ //------------------------------------------------------------------------------ | ||
/** | ||
* Function to check if a node is a static string template literal. | ||
* @param {ASTNode} node The node to check. | ||
* @returns {boolean} If the node is a string template literal. | ||
*/ | ||
function isStaticTemplateLiteral(node) { | ||
return node && node.type === "TemplateLiteral" && node.expressions.length === 0; | ||
} | ||
/** | ||
* Function to check if a node is a require call. | ||
@@ -149,3 +146,3 @@ * @param {ASTNode} node The node to check. | ||
if (isStaticTemplateLiteral(node)) { | ||
if (astUtils.isStaticTemplateLiteral(node)) { | ||
return node.quasis[0].value.cooked.trim(); | ||
@@ -152,0 +149,0 @@ } |
@@ -9,2 +9,8 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
const astUtils = require("./utils/ast-utils"); | ||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
@@ -51,2 +57,41 @@ //------------------------------------------------------------------------------ | ||
/** | ||
* Checks if a `LabeledStatement` node is fixable. | ||
* For a node to be fixable, there must be no comments between the label and the body. | ||
* Furthermore, is must be possible to remove the label without turning the body statement into a | ||
* directive after other fixes are applied. | ||
* @param {ASTNode} node The node to evaluate. | ||
* @returns {boolean} Whether or not the node is fixable. | ||
*/ | ||
function isFixable(node) { | ||
/* | ||
* Only perform a fix if there are no comments between the label and the body. This will be the case | ||
* when there is exactly one token/comment (the ":") between the label and the body. | ||
*/ | ||
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) !== | ||
sourceCode.getTokenBefore(node.body, { includeComments: true })) { | ||
return false; | ||
} | ||
// Looking for the node's deepest ancestor which is not a `LabeledStatement`. | ||
let ancestor = node.parent; | ||
while (ancestor.type === "LabeledStatement") { | ||
ancestor = ancestor.parent; | ||
} | ||
if (ancestor.type === "Program" || | ||
(ancestor.type === "BlockStatement" && astUtils.isFunction(ancestor.parent))) { | ||
const { body } = node; | ||
if (body.type === "ExpressionStatement" && | ||
((body.expression.type === "Literal" && typeof body.expression.value === "string") || | ||
astUtils.isStaticTemplateLiteral(body.expression))) { | ||
return false; // potential directive | ||
} | ||
} | ||
return true; | ||
} | ||
/** | ||
* Removes the top of the stack. | ||
@@ -63,15 +108,3 @@ * At the same time, this reports the label if it's never used. | ||
data: node.label, | ||
fix(fixer) { | ||
/* | ||
* Only perform a fix if there are no comments between the label and the body. This will be the case | ||
* when there is exactly one token/comment (the ":") between the label and the body. | ||
*/ | ||
if (sourceCode.getTokenAfter(node.label, { includeComments: true }) === | ||
sourceCode.getTokenBefore(node.body, { includeComments: true })) { | ||
return fixer.removeRange([node.range[0], node.body.range[0]]); | ||
} | ||
return null; | ||
} | ||
fix: isFixable(node) ? fixer => fixer.removeRange([node.range[0], node.body.range[0]]) : null | ||
}); | ||
@@ -78,0 +111,0 @@ } |
@@ -469,3 +469,4 @@ /** | ||
parent.left === id && | ||
isUnusedExpression(parent) | ||
isUnusedExpression(parent) && | ||
!astUtils.isLogicalAssignmentOperator(parent.operator) | ||
) || | ||
@@ -472,0 +473,0 @@ ( |
@@ -58,2 +58,3 @@ /** | ||
const parentPrecedence = astUtils.getPrecedence(parent); | ||
const needsParens = ( | ||
@@ -63,3 +64,3 @@ parent.type === "ClassDeclaration" || | ||
parent.type.endsWith("Expression") && | ||
astUtils.getPrecedence(parent) >= PRECEDENCE_OF_EXPONENTIATION_EXPR && | ||
(parentPrecedence === -1 || parentPrecedence >= PRECEDENCE_OF_EXPONENTIATION_EXPR) && | ||
!(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) && | ||
@@ -66,0 +67,0 @@ !((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) && |
@@ -40,11 +40,2 @@ /** | ||
/** | ||
* Determines whether the given node is a template literal without expressions. | ||
* @param {ASTNode} node Node to check. | ||
* @returns {boolean} True if the node is a template literal without expressions. | ||
*/ | ||
function isStaticTemplateLiteral(node) { | ||
return node.type === "TemplateLiteral" && node.expressions.length === 0; | ||
} | ||
const validPrecedingTokens = new Set([ | ||
@@ -182,3 +173,3 @@ "(", | ||
isGlobalReference(astUtils.skipChainExpression(node.tag).object) && | ||
isStaticTemplateLiteral(node.quasi); | ||
astUtils.isStaticTemplateLiteral(node.quasi); | ||
} | ||
@@ -196,3 +187,3 @@ | ||
if (isStaticTemplateLiteral(node)) { | ||
if (astUtils.isStaticTemplateLiteral(node)) { | ||
return node.quasis[0].value.cooked; | ||
@@ -215,3 +206,3 @@ } | ||
return isStringLiteral(node) || | ||
isStaticTemplateLiteral(node) || | ||
astUtils.isStaticTemplateLiteral(node) || | ||
isStringRawTaggedStaticTemplateLiteral(node); | ||
@@ -218,0 +209,0 @@ } |
@@ -8,2 +8,8 @@ /** | ||
//------------------------------------------------------------------------------ | ||
// Requirements | ||
//------------------------------------------------------------------------------ | ||
const astUtils = require("./utils/ast-utils"); | ||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
@@ -92,3 +98,3 @@ //------------------------------------------------------------------------------ | ||
if (sibling.type === "Literal" || sibling.type === "TemplateLiteral" && !sibling.expressions.length) { | ||
if (sibling.type === "Literal" || astUtils.isStaticTemplateLiteral(sibling)) { | ||
const value = sibling.type === "Literal" ? sibling.value : sibling.quasis[0].value.cooked; | ||
@@ -95,0 +101,0 @@ |
@@ -62,11 +62,2 @@ /** | ||
/** | ||
* Determines whether a node is a Template Literal which can be determined statically. | ||
* @param {ASTNode} node Node to test | ||
* @returns {boolean} True if the node is a Template Literal without expression. | ||
*/ | ||
function isStaticTemplateLiteral(node) { | ||
return node.type === "TemplateLiteral" && node.expressions.length === 0; | ||
} | ||
/** | ||
* Determines whether a non-Literal node should be treated as a single Literal node. | ||
@@ -77,3 +68,3 @@ * @param {ASTNode} node Node to test | ||
function looksLikeLiteral(node) { | ||
return isNegativeNumericLiteral(node) || isStaticTemplateLiteral(node); | ||
return isNegativeNumericLiteral(node) || astUtils.isStaticTemplateLiteral(node); | ||
} | ||
@@ -105,3 +96,3 @@ | ||
if (isStaticTemplateLiteral(node)) { | ||
if (astUtils.isStaticTemplateLiteral(node)) { | ||
return { | ||
@@ -108,0 +99,0 @@ type: "Literal", |
@@ -24,3 +24,3 @@ /** | ||
* @property {EcmaFeatures} [ecmaFeatures] The optional features. | ||
* @property {3|5|6|7|8|9|10|11|12|13|14|2015|2016|2017|2018|2019|2020|2021|2022|2023} [ecmaVersion] The ECMAScript version (or revision number). | ||
* @property {3|5|6|7|8|9|10|11|12|13|14|15|2015|2016|2017|2018|2019|2020|2021|2022|2023|2024} [ecmaVersion] The ECMAScript version (or revision number). | ||
* @property {"script"|"module"} [sourceType] The source code type. | ||
@@ -27,0 +27,0 @@ * @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3. |
{ | ||
"name": "eslint", | ||
"version": "8.43.0", | ||
"version": "8.44.0", | ||
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", | ||
@@ -65,4 +65,4 @@ "description": "An AST-based pattern checker for JavaScript.", | ||
"@eslint-community/regexpp": "^4.4.0", | ||
"@eslint/eslintrc": "^2.0.3", | ||
"@eslint/js": "8.43.0", | ||
"@eslint/eslintrc": "^2.1.0", | ||
"@eslint/js": "8.44.0", | ||
"@humanwhocodes/config-array": "^0.11.10", | ||
@@ -79,3 +79,3 @@ "@humanwhocodes/module-importer": "^1.0.1", | ||
"eslint-visitor-keys": "^3.4.1", | ||
"espree": "^9.5.2", | ||
"espree": "^9.6.0", | ||
"esquery": "^1.4.2", | ||
@@ -100,3 +100,3 @@ "esutils": "^2.0.2", | ||
"natural-compare": "^1.4.0", | ||
"optionator": "^0.9.1", | ||
"optionator": "^0.9.3", | ||
"strip-ansi": "^6.0.1", | ||
@@ -121,4 +121,4 @@ "strip-json-comments": "^3.1.0", | ||
"eslint-plugin-internal-rules": "file:tools/internal-rules", | ||
"eslint-plugin-jsdoc": "^38.1.6", | ||
"eslint-plugin-n": "^15.2.4", | ||
"eslint-plugin-jsdoc": "^46.2.5", | ||
"eslint-plugin-n": "^16.0.0", | ||
"eslint-plugin-unicorn": "^42.0.0", | ||
@@ -160,3 +160,3 @@ "eslint-release": "^3.2.0", | ||
"regenerator-runtime": "^0.13.2", | ||
"semver": "^7.3.5", | ||
"semver": "^7.5.3", | ||
"shelljs": "^0.8.2", | ||
@@ -163,0 +163,0 @@ "sinon": "^11.0.0", |
@@ -120,3 +120,3 @@ [![npm version](https://img.shields.io/npm/v/eslint.svg)](https://www.npmjs.com/package/eslint) | ||
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021 and 2022. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/latest/use/configure). | ||
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, and 2023. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/latest/use/configure). | ||
@@ -288,3 +288,3 @@ ### What about experimental features? | ||
<p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" 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></p><h3>Bronze Sponsors</h3> | ||
<p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" 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: free icons, photos, illustrations, and music" 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://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p> | ||
<p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" 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: free icons, photos, illustrations, and music" 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://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465?v=4" alt="Mercedes-Benz Group" 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://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p> | ||
<!--sponsorsend--> | ||
@@ -291,0 +291,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
2887145
68283
+ Added@eslint/js@8.44.0(transitive)
- Removed@eslint/js@8.43.0(transitive)
Updated@eslint/eslintrc@^2.1.0
Updated@eslint/js@8.44.0
Updatedespree@^9.6.0
Updatedoptionator@^0.9.3