eslint 8.57.0 → 9.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -28
- package/bin/eslint.js +4 -3
- package/conf/ecma-version.js +16 -0
- package/conf/globals.js +1 -0
- package/conf/rule-type-list.json +3 -1
- package/lib/api.js +7 -11
- package/lib/cli-engine/cli-engine.js +14 -3
- package/lib/cli-engine/formatters/formatters-meta.json +1 -29
- package/lib/cli-engine/lint-result-cache.js +2 -2
- package/lib/cli.js +115 -36
- package/lib/config/default-config.js +3 -0
- package/lib/config/flat-config-array.js +110 -24
- package/lib/config/flat-config-helpers.js +41 -20
- package/lib/config/flat-config-schema.js +1 -7
- package/lib/config/rule-validator.js +42 -6
- package/lib/eslint/eslint-helpers.js +116 -58
- package/lib/eslint/eslint.js +892 -377
- package/lib/eslint/index.js +2 -2
- package/lib/eslint/legacy-eslint.js +728 -0
- package/lib/linter/apply-disable-directives.js +59 -31
- package/lib/linter/code-path-analysis/code-path-analyzer.js +0 -1
- package/lib/linter/code-path-analysis/code-path.js +32 -30
- package/lib/linter/code-path-analysis/fork-context.js +1 -1
- package/lib/linter/config-comment-parser.js +8 -11
- package/lib/linter/index.js +1 -3
- package/lib/linter/interpolate.js +24 -2
- package/lib/linter/linter.js +428 -207
- package/lib/linter/report-translator.js +3 -3
- package/lib/linter/rules.js +6 -15
- package/lib/linter/source-code-fixer.js +1 -1
- package/lib/linter/timing.js +16 -8
- package/lib/options.js +35 -3
- package/lib/rule-tester/index.js +3 -1
- package/lib/rule-tester/rule-tester.js +424 -347
- package/lib/rules/array-bracket-newline.js +1 -1
- package/lib/rules/array-bracket-spacing.js +1 -1
- package/lib/rules/block-scoped-var.js +1 -1
- package/lib/rules/callback-return.js +2 -2
- package/lib/rules/camelcase.js +3 -5
- package/lib/rules/capitalized-comments.js +10 -7
- package/lib/rules/comma-dangle.js +1 -1
- package/lib/rules/comma-style.js +2 -2
- package/lib/rules/complexity.js +14 -1
- package/lib/rules/constructor-super.js +99 -100
- package/lib/rules/default-case.js +1 -1
- package/lib/rules/eol-last.js +2 -2
- package/lib/rules/function-paren-newline.js +2 -2
- package/lib/rules/indent-legacy.js +5 -5
- package/lib/rules/indent.js +5 -5
- package/lib/rules/index.js +1 -2
- package/lib/rules/key-spacing.js +2 -2
- package/lib/rules/line-comment-position.js +1 -1
- package/lib/rules/lines-around-directive.js +2 -2
- package/lib/rules/max-depth.js +1 -1
- package/lib/rules/max-len.js +3 -3
- package/lib/rules/max-lines.js +3 -3
- package/lib/rules/max-nested-callbacks.js +1 -1
- package/lib/rules/max-params.js +1 -1
- package/lib/rules/max-statements.js +1 -1
- package/lib/rules/multiline-comment-style.js +7 -7
- package/lib/rules/new-cap.js +1 -1
- package/lib/rules/newline-after-var.js +1 -1
- package/lib/rules/newline-before-return.js +1 -1
- package/lib/rules/no-case-declarations.js +13 -1
- package/lib/rules/no-constant-binary-expression.js +7 -8
- package/lib/rules/no-constant-condition.js +18 -7
- package/lib/rules/no-constructor-return.js +2 -2
- package/lib/rules/no-dupe-class-members.js +2 -2
- package/lib/rules/no-else-return.js +1 -1
- package/lib/rules/no-empty-function.js +2 -2
- package/lib/rules/no-empty-static-block.js +1 -1
- package/lib/rules/no-extend-native.js +1 -2
- package/lib/rules/no-extra-semi.js +1 -1
- package/lib/rules/no-fallthrough.js +41 -16
- package/lib/rules/no-implicit-coercion.js +66 -24
- package/lib/rules/no-inner-declarations.js +23 -2
- package/lib/rules/no-invalid-regexp.js +1 -1
- package/lib/rules/no-invalid-this.js +1 -1
- package/lib/rules/no-lone-blocks.js +3 -3
- package/lib/rules/no-loss-of-precision.js +1 -1
- package/lib/rules/no-misleading-character-class.js +225 -69
- package/lib/rules/no-mixed-spaces-and-tabs.js +1 -1
- package/lib/rules/no-multiple-empty-lines.js +1 -1
- package/lib/rules/no-new-native-nonconstructor.js +1 -1
- package/lib/rules/no-new-symbol.js +8 -1
- package/lib/rules/no-restricted-globals.js +1 -1
- package/lib/rules/no-restricted-imports.js +186 -40
- package/lib/rules/no-restricted-modules.js +2 -2
- package/lib/rules/no-return-await.js +1 -1
- package/lib/rules/no-sequences.js +1 -0
- package/lib/rules/no-this-before-super.js +45 -13
- package/lib/rules/no-trailing-spaces.js +2 -3
- package/lib/rules/no-unneeded-ternary.js +1 -1
- package/lib/rules/no-unsafe-optional-chaining.js +1 -1
- package/lib/rules/no-unused-private-class-members.js +1 -1
- package/lib/rules/no-unused-vars.js +197 -36
- package/lib/rules/no-useless-assignment.js +566 -0
- package/lib/rules/no-useless-backreference.js +1 -1
- package/lib/rules/no-useless-computed-key.js +2 -2
- package/lib/rules/no-useless-return.js +7 -2
- package/lib/rules/object-curly-spacing.js +3 -3
- package/lib/rules/object-property-newline.js +1 -1
- package/lib/rules/one-var.js +5 -5
- package/lib/rules/padded-blocks.js +7 -7
- package/lib/rules/prefer-arrow-callback.js +3 -3
- package/lib/rules/prefer-reflect.js +1 -1
- package/lib/rules/prefer-regex-literals.js +1 -1
- package/lib/rules/prefer-template.js +1 -1
- package/lib/rules/radix.js +2 -2
- package/lib/rules/semi-style.js +1 -1
- package/lib/rules/sort-imports.js +1 -1
- package/lib/rules/sort-keys.js +1 -1
- package/lib/rules/sort-vars.js +1 -1
- package/lib/rules/space-unary-ops.js +1 -1
- package/lib/rules/strict.js +1 -1
- package/lib/rules/use-isnan.js +101 -7
- package/lib/rules/utils/ast-utils.js +16 -7
- package/lib/rules/utils/char-source.js +240 -0
- package/lib/rules/utils/lazy-loading-rule-map.js +1 -1
- package/lib/rules/utils/unicode/index.js +9 -4
- package/lib/rules/yield-star-spacing.js +1 -1
- package/lib/shared/runtime-info.js +1 -0
- package/lib/shared/serialization.js +55 -0
- package/lib/shared/stats.js +30 -0
- package/lib/shared/string-utils.js +9 -11
- package/lib/shared/types.js +35 -1
- package/lib/source-code/index.js +3 -1
- package/lib/source-code/source-code.js +299 -85
- package/lib/source-code/token-store/backward-token-cursor.js +3 -3
- package/lib/source-code/token-store/cursors.js +4 -2
- package/lib/source-code/token-store/forward-token-comment-cursor.js +3 -3
- package/lib/source-code/token-store/forward-token-cursor.js +3 -3
- package/lib/source-code/token-store/index.js +2 -2
- package/lib/unsupported-api.js +3 -5
- package/messages/no-config-found.js +1 -1
- package/messages/plugin-conflict.js +1 -1
- package/messages/plugin-invalid.js +1 -1
- package/messages/plugin-missing.js +1 -1
- package/package.json +32 -29
- package/conf/config-schema.js +0 -93
- package/lib/cli-engine/formatters/checkstyle.js +0 -60
- package/lib/cli-engine/formatters/compact.js +0 -60
- package/lib/cli-engine/formatters/jslint-xml.js +0 -41
- package/lib/cli-engine/formatters/junit.js +0 -82
- package/lib/cli-engine/formatters/tap.js +0 -95
- package/lib/cli-engine/formatters/unix.js +0 -58
- package/lib/cli-engine/formatters/visualstudio.js +0 -63
- package/lib/cli-engine/xml-escape.js +0 -34
- package/lib/eslint/flat-eslint.js +0 -1155
- package/lib/rule-tester/flat-rule-tester.js +0 -1131
- package/lib/rules/require-jsdoc.js +0 -122
- package/lib/rules/utils/patterns/letters.js +0 -36
- package/lib/rules/valid-jsdoc.js +0 -516
- package/lib/shared/config-validator.js +0 -347
- package/lib/shared/deprecation-warnings.js +0 -58
- package/lib/shared/relative-module-resolver.js +0 -50
package/lib/rules/key-spacing.js
CHANGED
@@ -28,7 +28,7 @@ function containsLineTerminator(str) {
|
|
28
28
|
* @returns {any} Last element of arr.
|
29
29
|
*/
|
30
30
|
function last(arr) {
|
31
|
-
return arr
|
31
|
+
return arr.at(-1);
|
32
32
|
}
|
33
33
|
|
34
34
|
/**
|
@@ -489,7 +489,7 @@ module.exports = {
|
|
489
489
|
}
|
490
490
|
}
|
491
491
|
|
492
|
-
let messageId
|
492
|
+
let messageId;
|
493
493
|
|
494
494
|
if (isExtra) {
|
495
495
|
messageId = side === "key" ? "extraKey" : "extraValue";
|
@@ -68,7 +68,7 @@ module.exports = {
|
|
68
68
|
above = !options.position || options.position === "above";
|
69
69
|
ignorePattern = options.ignorePattern;
|
70
70
|
|
71
|
-
if (Object.
|
71
|
+
if (Object.hasOwn(options, "applyDefaultIgnorePatterns")) {
|
72
72
|
applyDefaultIgnorePatterns = options.applyDefaultIgnorePatterns;
|
73
73
|
} else {
|
74
74
|
applyDefaultIgnorePatterns = options.applyDefaultPatterns !== false;
|
@@ -166,7 +166,7 @@ module.exports = {
|
|
166
166
|
reportError(firstDirective, "before", false);
|
167
167
|
}
|
168
168
|
|
169
|
-
const lastDirective = directives
|
169
|
+
const lastDirective = directives.at(-1);
|
170
170
|
const statements = node.type === "Program" ? node.body : node.body.body;
|
171
171
|
|
172
172
|
/*
|
@@ -174,7 +174,7 @@ module.exports = {
|
|
174
174
|
* contains a directive prologue and isn't followed by a comment to ensure
|
175
175
|
* this rule behaves well with padded-blocks.
|
176
176
|
*/
|
177
|
-
if (lastDirective === statements
|
177
|
+
if (lastDirective === statements.at(-1) && !lastDirective.trailingComments) {
|
178
178
|
return;
|
179
179
|
}
|
180
180
|
|
package/lib/rules/max-depth.js
CHANGED
@@ -61,7 +61,7 @@ module.exports = {
|
|
61
61
|
|
62
62
|
if (
|
63
63
|
typeof option === "object" &&
|
64
|
-
(Object.
|
64
|
+
(Object.hasOwn(option, "maximum") || Object.hasOwn(option, "max"))
|
65
65
|
) {
|
66
66
|
maxDepth = option.maximum || option.max;
|
67
67
|
}
|
package/lib/rules/max-len.js
CHANGED
@@ -124,7 +124,7 @@ module.exports = {
|
|
124
124
|
}
|
125
125
|
|
126
126
|
// The options object must be the last option specified…
|
127
|
-
const options = Object.assign({}, context.options
|
127
|
+
const options = Object.assign({}, context.options.at(-1));
|
128
128
|
|
129
129
|
// …but max code length…
|
130
130
|
if (typeof context.options[0] === "number") {
|
@@ -290,7 +290,7 @@ module.exports = {
|
|
290
290
|
if (isJSXEmptyExpressionInSingleLineContainer(containingNode)) {
|
291
291
|
|
292
292
|
// push a unique node only
|
293
|
-
if (comments
|
293
|
+
if (comments.at(-1) !== containingNode.parent) {
|
294
294
|
comments.push(containingNode.parent);
|
295
295
|
}
|
296
296
|
} else {
|
@@ -344,7 +344,7 @@ module.exports = {
|
|
344
344
|
* comments to check.
|
345
345
|
*/
|
346
346
|
if (commentsIndex < comments.length) {
|
347
|
-
let comment
|
347
|
+
let comment;
|
348
348
|
|
349
349
|
// iterate over comments until we find one past the current line
|
350
350
|
do {
|
package/lib/rules/max-lines.js
CHANGED
@@ -77,7 +77,7 @@ module.exports = {
|
|
77
77
|
|
78
78
|
if (
|
79
79
|
typeof option === "object" &&
|
80
|
-
Object.
|
80
|
+
Object.hasOwn(option, "max")
|
81
81
|
) {
|
82
82
|
max = option.max;
|
83
83
|
} else if (typeof option === "number") {
|
@@ -148,7 +148,7 @@ module.exports = {
|
|
148
148
|
* If file ends with a linebreak, `sourceCode.lines` will have one extra empty line at the end.
|
149
149
|
* That isn't a real line, so we shouldn't count it.
|
150
150
|
*/
|
151
|
-
if (lines.length > 1 && lines
|
151
|
+
if (lines.length > 1 && lines.at(-1).text === "") {
|
152
152
|
lines.pop();
|
153
153
|
}
|
154
154
|
|
@@ -174,7 +174,7 @@ module.exports = {
|
|
174
174
|
},
|
175
175
|
end: {
|
176
176
|
line: sourceCode.lines.length,
|
177
|
-
column: sourceCode.lines
|
177
|
+
column: sourceCode.lines.at(-1).length
|
178
178
|
}
|
179
179
|
};
|
180
180
|
|
@@ -59,7 +59,7 @@ module.exports = {
|
|
59
59
|
|
60
60
|
if (
|
61
61
|
typeof option === "object" &&
|
62
|
-
(Object.
|
62
|
+
(Object.hasOwn(option, "maximum") || Object.hasOwn(option, "max"))
|
63
63
|
) {
|
64
64
|
THRESHOLD = option.maximum || option.max;
|
65
65
|
} else if (typeof option === "number") {
|
package/lib/rules/max-params.js
CHANGED
@@ -63,7 +63,7 @@ module.exports = {
|
|
63
63
|
|
64
64
|
if (
|
65
65
|
typeof option === "object" &&
|
66
|
-
(Object.
|
66
|
+
(Object.hasOwn(option, "maximum") || Object.hasOwn(option, "max"))
|
67
67
|
) {
|
68
68
|
numParams = option.maximum || option.max;
|
69
69
|
}
|
@@ -79,7 +79,7 @@ module.exports = {
|
|
79
79
|
|
80
80
|
if (
|
81
81
|
typeof option === "object" &&
|
82
|
-
(Object.
|
82
|
+
(Object.hasOwn(option, "maximum") || Object.hasOwn(option, "max"))
|
83
83
|
) {
|
84
84
|
maxStatements = option.maximum || option.max;
|
85
85
|
} else if (typeof option === "number") {
|
@@ -113,7 +113,7 @@ module.exports = {
|
|
113
113
|
|
114
114
|
return /^\*\s*$/u.test(lines[0]) &&
|
115
115
|
lines.slice(1, -1).every(line => /^\s* /u.test(line)) &&
|
116
|
-
/^\s*$/u.test(lines
|
116
|
+
/^\s*$/u.test(lines.at(-1));
|
117
117
|
}
|
118
118
|
|
119
119
|
/**
|
@@ -272,11 +272,11 @@ module.exports = {
|
|
272
272
|
context.report({
|
273
273
|
loc: {
|
274
274
|
start: firstComment.loc.start,
|
275
|
-
end: commentGroup
|
275
|
+
end: commentGroup.at(-1).loc.end
|
276
276
|
},
|
277
277
|
messageId: "expectedBlock",
|
278
278
|
fix(fixer) {
|
279
|
-
const range = [firstComment.range[0], commentGroup
|
279
|
+
const range = [firstComment.range[0], commentGroup.at(-1).range[1]];
|
280
280
|
|
281
281
|
return commentLines.some(value => value.startsWith("/"))
|
282
282
|
? null
|
@@ -301,7 +301,7 @@ module.exports = {
|
|
301
301
|
});
|
302
302
|
}
|
303
303
|
|
304
|
-
if (!/^\s*$/u.test(lines
|
304
|
+
if (!/^\s*$/u.test(lines.at(-1))) {
|
305
305
|
context.report({
|
306
306
|
loc: {
|
307
307
|
start: { line: firstComment.loc.end.line, column: firstComment.loc.end.column - 2 },
|
@@ -408,12 +408,12 @@ module.exports = {
|
|
408
408
|
context.report({
|
409
409
|
loc: {
|
410
410
|
start: firstComment.loc.start,
|
411
|
-
end: commentGroup
|
411
|
+
end: commentGroup.at(-1).loc.end
|
412
412
|
},
|
413
413
|
messageId: "expectedBlock",
|
414
414
|
fix(fixer) {
|
415
415
|
return fixer.replaceTextRange(
|
416
|
-
[firstComment.range[0], commentGroup
|
416
|
+
[firstComment.range[0], commentGroup.at(-1).range[1]],
|
417
417
|
convertToBlock(firstComment, commentLines)
|
418
418
|
);
|
419
419
|
}
|
@@ -459,7 +459,7 @@ module.exports = {
|
|
459
459
|
tokenBefore && tokenBefore.loc.end.line === comment.loc.start.line - 1 &&
|
460
460
|
tokenBefore === commentList[index - 1]
|
461
461
|
) {
|
462
|
-
commentGroups
|
462
|
+
commentGroups.at(-1).push(comment);
|
463
463
|
} else {
|
464
464
|
commentGroups.push([comment]);
|
465
465
|
}
|
package/lib/rules/new-cap.js
CHANGED
@@ -40,7 +40,7 @@ const CAPS_ALLOWED = [
|
|
40
40
|
function checkArray(obj, key, fallback) {
|
41
41
|
|
42
42
|
/* c8 ignore start */
|
43
|
-
if (Object.
|
43
|
+
if (Object.hasOwn(obj, key) && !Array.isArray(obj[key])) {
|
44
44
|
throw new TypeError(`${key}, if provided, must be an Array`);
|
45
45
|
}/* c8 ignore stop */
|
46
46
|
return obj[key] || fallback;
|
@@ -215,7 +215,7 @@ module.exports = {
|
|
215
215
|
fix(fixer) {
|
216
216
|
const linesBetween = sourceCode.getText().slice(lastToken.range[1], nextToken.range[0]).split(astUtils.LINEBREAK_MATCHER);
|
217
217
|
|
218
|
-
return fixer.replaceTextRange([lastToken.range[1], nextToken.range[0]], `${linesBetween.slice(0, -1).join("")}\n${linesBetween
|
218
|
+
return fixer.replaceTextRange([lastToken.range[1], nextToken.range[0]], `${linesBetween.slice(0, -1).join("")}\n${linesBetween.at(-1)}`);
|
219
219
|
}
|
220
220
|
});
|
221
221
|
}
|
@@ -166,7 +166,7 @@ module.exports = {
|
|
166
166
|
*/
|
167
167
|
function canFix(node) {
|
168
168
|
const leadingComments = sourceCode.getCommentsBefore(node);
|
169
|
-
const lastLeadingComment = leadingComments
|
169
|
+
const lastLeadingComment = leadingComments.at(-1);
|
170
170
|
const tokenBefore = sourceCode.getTokenBefore(node);
|
171
171
|
|
172
172
|
if (leadingComments.length === 0) {
|
@@ -19,9 +19,12 @@ module.exports = {
|
|
19
19
|
url: "https://eslint.org/docs/latest/rules/no-case-declarations"
|
20
20
|
},
|
21
21
|
|
22
|
+
hasSuggestions: true,
|
23
|
+
|
22
24
|
schema: [],
|
23
25
|
|
24
26
|
messages: {
|
27
|
+
addBrackets: "Add {} brackets around the case block.",
|
25
28
|
unexpected: "Unexpected lexical declaration in case block."
|
26
29
|
}
|
27
30
|
},
|
@@ -53,7 +56,16 @@ module.exports = {
|
|
53
56
|
if (isLexicalDeclaration(statement)) {
|
54
57
|
context.report({
|
55
58
|
node: statement,
|
56
|
-
messageId: "unexpected"
|
59
|
+
messageId: "unexpected",
|
60
|
+
suggest: [
|
61
|
+
{
|
62
|
+
messageId: "addBrackets",
|
63
|
+
fix: fixer => [
|
64
|
+
fixer.insertTextBefore(node.consequent[0], "{ "),
|
65
|
+
fixer.insertTextAfter(node.consequent.at(-1), " }")
|
66
|
+
]
|
67
|
+
}
|
68
|
+
]
|
57
69
|
});
|
58
70
|
}
|
59
71
|
}
|
@@ -5,8 +5,7 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
-
const
|
9
|
-
const { isNullLiteral, isConstant, isReferenceToGlobalVariable, isLogicalAssignmentOperator } = require("./utils/ast-utils");
|
8
|
+
const { isNullLiteral, isConstant, isReferenceToGlobalVariable, isLogicalAssignmentOperator, ECMASCRIPT_GLOBALS } = require("./utils/ast-utils");
|
10
9
|
|
11
10
|
const NUMERIC_OR_STRING_BINARY_OPERATORS = new Set(["+", "-", "*", "/", "%", "|", "^", "&", "**", "<<", ">>", ">>>"]);
|
12
11
|
|
@@ -103,7 +102,7 @@ function hasConstantNullishness(scope, node, nonNullish) {
|
|
103
102
|
|
104
103
|
return true;
|
105
104
|
case "SequenceExpression": {
|
106
|
-
const last = node.expressions
|
105
|
+
const last = node.expressions.at(-1);
|
107
106
|
|
108
107
|
return hasConstantNullishness(scope, last, nonNullish);
|
109
108
|
}
|
@@ -248,7 +247,7 @@ function hasConstantLooseBooleanComparison(scope, node) {
|
|
248
247
|
*/
|
249
248
|
return false;
|
250
249
|
case "SequenceExpression": {
|
251
|
-
const last = node.expressions
|
250
|
+
const last = node.expressions.at(-1);
|
252
251
|
|
253
252
|
return hasConstantLooseBooleanComparison(scope, last);
|
254
253
|
}
|
@@ -299,7 +298,7 @@ function hasConstantStrictBooleanComparison(scope, node) {
|
|
299
298
|
return true;
|
300
299
|
}
|
301
300
|
case "SequenceExpression": {
|
302
|
-
const last = node.expressions
|
301
|
+
const last = node.expressions.at(-1);
|
303
302
|
|
304
303
|
return hasConstantStrictBooleanComparison(scope, last);
|
305
304
|
}
|
@@ -376,7 +375,7 @@ function isAlwaysNew(scope, node) {
|
|
376
375
|
* Catching these is especially useful for primitive constructors
|
377
376
|
* which return boxed values, a surprising gotcha' in JavaScript.
|
378
377
|
*/
|
379
|
-
return Object.
|
378
|
+
return Object.hasOwn(ECMASCRIPT_GLOBALS, node.callee.name) &&
|
380
379
|
isReferenceToGlobalVariable(scope, node.callee);
|
381
380
|
}
|
382
381
|
case "Literal":
|
@@ -384,7 +383,7 @@ function isAlwaysNew(scope, node) {
|
|
384
383
|
// Regular expressions are objects, and thus always new
|
385
384
|
return typeof node.regex === "object";
|
386
385
|
case "SequenceExpression": {
|
387
|
-
const last = node.expressions
|
386
|
+
const last = node.expressions.at(-1);
|
388
387
|
|
389
388
|
return isAlwaysNew(scope, last);
|
390
389
|
}
|
@@ -440,7 +439,7 @@ module.exports = {
|
|
440
439
|
type: "problem",
|
441
440
|
docs: {
|
442
441
|
description: "Disallow expressions where the operation doesn't affect the value",
|
443
|
-
recommended:
|
442
|
+
recommended: true,
|
444
443
|
url: "https://eslint.org/docs/latest/rules/no-constant-binary-expression"
|
445
444
|
},
|
446
445
|
schema: [],
|
@@ -31,8 +31,7 @@ module.exports = {
|
|
31
31
|
type: "object",
|
32
32
|
properties: {
|
33
33
|
checkLoops: {
|
34
|
-
|
35
|
-
default: true
|
34
|
+
enum: ["all", "allExceptWhileTrue", "none", true, false]
|
36
35
|
}
|
37
36
|
},
|
38
37
|
additionalProperties: false
|
@@ -45,11 +44,17 @@ module.exports = {
|
|
45
44
|
},
|
46
45
|
|
47
46
|
create(context) {
|
48
|
-
const options = context.options[0] || {}
|
49
|
-
|
50
|
-
|
47
|
+
const options = context.options[0] || {};
|
48
|
+
let checkLoops = options.checkLoops ?? "allExceptWhileTrue";
|
49
|
+
const loopSetStack = [];
|
51
50
|
const sourceCode = context.sourceCode;
|
52
51
|
|
52
|
+
if (options.checkLoops === true) {
|
53
|
+
checkLoops = "all";
|
54
|
+
} else if (options.checkLoops === false) {
|
55
|
+
checkLoops = "none";
|
56
|
+
}
|
57
|
+
|
53
58
|
let loopsInCurrentScope = new Set();
|
54
59
|
|
55
60
|
//--------------------------------------------------------------------------
|
@@ -120,7 +125,7 @@ module.exports = {
|
|
120
125
|
* @private
|
121
126
|
*/
|
122
127
|
function checkLoop(node) {
|
123
|
-
if (checkLoops) {
|
128
|
+
if (checkLoops === "all" || checkLoops === "allExceptWhileTrue") {
|
124
129
|
trackConstantConditionLoop(node);
|
125
130
|
}
|
126
131
|
}
|
@@ -132,7 +137,13 @@ module.exports = {
|
|
132
137
|
return {
|
133
138
|
ConditionalExpression: reportIfConstant,
|
134
139
|
IfStatement: reportIfConstant,
|
135
|
-
WhileStatement
|
140
|
+
WhileStatement(node) {
|
141
|
+
if (node.test.type === "Literal" && node.test.value === true && checkLoops === "allExceptWhileTrue") {
|
142
|
+
return;
|
143
|
+
}
|
144
|
+
|
145
|
+
checkLoop(node);
|
146
|
+
},
|
136
147
|
"WhileStatement:exit": checkConstantConditionLoopInSet,
|
137
148
|
DoWhileStatement: checkLoop,
|
138
149
|
"DoWhileStatement:exit": checkConstantConditionLoopInSet,
|
@@ -20,7 +20,7 @@ module.exports = {
|
|
20
20
|
url: "https://eslint.org/docs/latest/rules/no-constructor-return"
|
21
21
|
},
|
22
22
|
|
23
|
-
schema:
|
23
|
+
schema: [],
|
24
24
|
|
25
25
|
fixable: null,
|
26
26
|
|
@@ -40,7 +40,7 @@ module.exports = {
|
|
40
40
|
stack.pop();
|
41
41
|
},
|
42
42
|
ReturnStatement(node) {
|
43
|
-
const last = stack
|
43
|
+
const last = stack.at(-1);
|
44
44
|
|
45
45
|
if (!last.parent) {
|
46
46
|
return;
|
@@ -42,7 +42,7 @@ module.exports = {
|
|
42
42
|
* - retv.set {boolean} A flag which shows the name is declared as setter.
|
43
43
|
*/
|
44
44
|
function getState(name, isStatic) {
|
45
|
-
const stateMap = stack
|
45
|
+
const stateMap = stack.at(-1);
|
46
46
|
const key = `$${name}`; // to avoid "__proto__".
|
47
47
|
|
48
48
|
if (!stateMap[key]) {
|
@@ -82,7 +82,7 @@ module.exports = {
|
|
82
82
|
}
|
83
83
|
|
84
84
|
const state = getState(name, node.static);
|
85
|
-
let isDuplicate
|
85
|
+
let isDuplicate;
|
86
86
|
|
87
87
|
if (kind === "get") {
|
88
88
|
isDuplicate = (state.init || state.get);
|
@@ -270,7 +270,7 @@ module.exports = {
|
|
270
270
|
function naiveHasReturn(node) {
|
271
271
|
if (node.type === "BlockStatement") {
|
272
272
|
const body = node.body,
|
273
|
-
lastChildNode = body
|
273
|
+
lastChildNode = body.at(-1);
|
274
274
|
|
275
275
|
return lastChildNode && checkForReturn(lastChildNode);
|
276
276
|
}
|
@@ -40,7 +40,7 @@ const ALLOW_OPTIONS = Object.freeze([
|
|
40
40
|
*/
|
41
41
|
function getKind(node) {
|
42
42
|
const parent = node.parent;
|
43
|
-
let kind
|
43
|
+
let kind;
|
44
44
|
|
45
45
|
if (node.type === "ArrowFunctionExpression") {
|
46
46
|
return "arrowFunctions";
|
@@ -73,7 +73,7 @@ function getKind(node) {
|
|
73
73
|
}
|
74
74
|
|
75
75
|
// Detects prefix.
|
76
|
-
let prefix
|
76
|
+
let prefix;
|
77
77
|
|
78
78
|
if (node.generator) {
|
79
79
|
prefix = "generator";
|
@@ -10,7 +10,6 @@
|
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
12
|
const astUtils = require("./utils/ast-utils");
|
13
|
-
const globals = require("globals");
|
14
13
|
|
15
14
|
//------------------------------------------------------------------------------
|
16
15
|
// Rule Definition
|
@@ -54,7 +53,7 @@ module.exports = {
|
|
54
53
|
const sourceCode = context.sourceCode;
|
55
54
|
const exceptions = new Set(config.exceptions || []);
|
56
55
|
const modifiedBuiltins = new Set(
|
57
|
-
Object.keys(
|
56
|
+
Object.keys(astUtils.ECMASCRIPT_GLOBALS)
|
58
57
|
.filter(builtin => builtin[0].toUpperCase() === builtin[0])
|
59
58
|
.filter(builtin => !exceptions.has(builtin))
|
60
59
|
);
|
@@ -48,9 +48,9 @@ function isFallThroughComment(comment, fallthroughCommentPattern) {
|
|
48
48
|
* @param {ASTNode} subsequentCase The case after caseWhichFallsThrough.
|
49
49
|
* @param {RuleContext} context A rule context which stores comments.
|
50
50
|
* @param {RegExp} fallthroughCommentPattern A pattern to match comment to.
|
51
|
-
* @returns {
|
51
|
+
* @returns {null | object} the comment if the case has a valid fallthrough comment, otherwise null
|
52
52
|
*/
|
53
|
-
function
|
53
|
+
function getFallthroughComment(caseWhichFallsThrough, subsequentCase, context, fallthroughCommentPattern) {
|
54
54
|
const sourceCode = context.sourceCode;
|
55
55
|
|
56
56
|
if (caseWhichFallsThrough.consequent.length === 1 && caseWhichFallsThrough.consequent[0].type === "BlockStatement") {
|
@@ -58,13 +58,17 @@ function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, f
|
|
58
58
|
const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
|
59
59
|
|
60
60
|
if (commentInBlock && isFallThroughComment(commentInBlock.value, fallthroughCommentPattern)) {
|
61
|
-
return
|
61
|
+
return commentInBlock;
|
62
62
|
}
|
63
63
|
}
|
64
64
|
|
65
65
|
const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
|
66
66
|
|
67
|
-
|
67
|
+
if (comment && isFallThroughComment(comment.value, fallthroughCommentPattern)) {
|
68
|
+
return comment;
|
69
|
+
}
|
70
|
+
|
71
|
+
return null;
|
68
72
|
}
|
69
73
|
|
70
74
|
/**
|
@@ -103,12 +107,17 @@ module.exports = {
|
|
103
107
|
allowEmptyCase: {
|
104
108
|
type: "boolean",
|
105
109
|
default: false
|
110
|
+
},
|
111
|
+
reportUnusedFallthroughComment: {
|
112
|
+
type: "boolean",
|
113
|
+
default: false
|
106
114
|
}
|
107
115
|
},
|
108
116
|
additionalProperties: false
|
109
117
|
}
|
110
118
|
],
|
111
119
|
messages: {
|
120
|
+
unusedFallthroughComment: "Found a comment that would permit fallthrough, but case cannot fall through.",
|
112
121
|
case: "Expected a 'break' statement before 'case'.",
|
113
122
|
default: "Expected a 'break' statement before 'default'."
|
114
123
|
}
|
@@ -120,12 +129,13 @@ module.exports = {
|
|
120
129
|
let currentCodePathSegments = new Set();
|
121
130
|
const sourceCode = context.sourceCode;
|
122
131
|
const allowEmptyCase = options.allowEmptyCase || false;
|
132
|
+
const reportUnusedFallthroughComment = options.reportUnusedFallthroughComment || false;
|
123
133
|
|
124
134
|
/*
|
125
135
|
* We need to use leading comments of the next SwitchCase node because
|
126
136
|
* trailing comments is wrong if semicolons are omitted.
|
127
137
|
*/
|
128
|
-
let
|
138
|
+
let previousCase = null;
|
129
139
|
let fallthroughCommentPattern = null;
|
130
140
|
|
131
141
|
if (options.commentPattern) {
|
@@ -168,13 +178,23 @@ module.exports = {
|
|
168
178
|
* And reports the previous fallthrough node if that does not exist.
|
169
179
|
*/
|
170
180
|
|
171
|
-
if (
|
172
|
-
context
|
173
|
-
|
174
|
-
|
175
|
-
|
181
|
+
if (previousCase && previousCase.node.parent === node.parent) {
|
182
|
+
const previousCaseFallthroughComment = getFallthroughComment(previousCase.node, node, context, fallthroughCommentPattern);
|
183
|
+
|
184
|
+
if (previousCase.isFallthrough && !(previousCaseFallthroughComment)) {
|
185
|
+
context.report({
|
186
|
+
messageId: node.test ? "case" : "default",
|
187
|
+
node
|
188
|
+
});
|
189
|
+
} else if (reportUnusedFallthroughComment && !previousCase.isSwitchExitReachable && previousCaseFallthroughComment) {
|
190
|
+
context.report({
|
191
|
+
messageId: "unusedFallthroughComment",
|
192
|
+
node: previousCaseFallthroughComment
|
193
|
+
});
|
194
|
+
}
|
195
|
+
|
176
196
|
}
|
177
|
-
|
197
|
+
previousCase = null;
|
178
198
|
},
|
179
199
|
|
180
200
|
"SwitchCase:exit"(node) {
|
@@ -185,11 +205,16 @@ module.exports = {
|
|
185
205
|
* `break`, `return`, or `throw` are unreachable.
|
186
206
|
* And allows empty cases and the last case.
|
187
207
|
*/
|
188
|
-
|
189
|
-
|
190
|
-
node.parent.cases
|
191
|
-
|
192
|
-
|
208
|
+
const isSwitchExitReachable = isAnySegmentReachable(currentCodePathSegments);
|
209
|
+
const isFallthrough = isSwitchExitReachable && (node.consequent.length > 0 || (!allowEmptyCase && hasBlankLinesBetween(node, nextToken))) &&
|
210
|
+
node.parent.cases.at(-1) !== node;
|
211
|
+
|
212
|
+
previousCase = {
|
213
|
+
node,
|
214
|
+
isSwitchExitReachable,
|
215
|
+
isFallthrough
|
216
|
+
};
|
217
|
+
|
193
218
|
}
|
194
219
|
};
|
195
220
|
}
|