eslint 10.4.0 → 10.5.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 +2 -2
- package/bin/eslint.js +28 -12
- package/lib/linter/code-path-analysis/code-path-analyzer.js +2 -2
- package/lib/linter/code-path-analysis/code-path-state.js +10 -6
- package/lib/rules/array-callback-return.js +1 -15
- package/lib/rules/consistent-return.js +2 -16
- package/lib/rules/getter-return.js +1 -15
- package/lib/rules/max-depth.js +21 -2
- package/lib/rules/max-lines-per-function.js +1 -0
- package/lib/rules/max-nested-callbacks.js +19 -3
- package/lib/rules/max-statements.js +1 -0
- package/lib/rules/no-fallthrough.js +1 -15
- package/lib/rules/no-unreachable-loop.js +2 -15
- package/lib/rules/no-unreachable.js +7 -16
- package/lib/rules/no-useless-return.js +1 -15
- package/lib/rules/no-with.js +7 -1
- package/lib/rules/prefer-arrow-callback.js +18 -8
- package/lib/rules/utils/code-path-utils.js +22 -0
- package/lib/types/index.d.ts +23 -0
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
|
|
|
45
45
|
|
|
46
46
|
### Prerequisites
|
|
47
47
|
|
|
48
|
-
To use ESLint, you must have [Node.js](https://nodejs.org/) (`^20.19.0`, `^22.13.0`, or `>=24`) installed and built with SSL support. (If you are using an official Node.js distribution, SSL
|
|
48
|
+
To use ESLint, you must have [Node.js](https://nodejs.org/) (`^20.19.0`, `^22.13.0`, or `>=24`) installed and built with SSL and ICU support. (If you are using an official Node.js distribution, both SSL and ICU are always built in.)
|
|
49
49
|
|
|
50
50
|
If you use ESLint's TypeScript type definitions, TypeScript 5.3 or later is required.
|
|
51
51
|
|
|
@@ -360,7 +360,7 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
|
|
360
360
|
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
|
|
361
361
|
<p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a></p><h3>Silver Sponsors</h3>
|
|
362
362
|
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
|
|
363
|
-
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" 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://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://citadel.co.jp"><img src="https://avatars.githubusercontent.com/u/75781367" alt="Citadel AI" height="32"></a
|
|
363
|
+
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" 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://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://citadel.co.jp"><img src="https://avatars.githubusercontent.com/u/75781367" alt="Citadel AI" height="32"></a></p>
|
|
364
364
|
<h3>Technology Sponsors</h3>
|
|
365
365
|
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
|
|
366
366
|
<p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
|
package/bin/eslint.js
CHANGED
|
@@ -61,6 +61,32 @@ function readStdin() {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Spawns an external command and propagates its exit status.
|
|
66
|
+
* @param {string} command The command to run.
|
|
67
|
+
* @param {string[]} args The command arguments.
|
|
68
|
+
* @throws {Error} If the command cannot be spawned.
|
|
69
|
+
* @returns {void}
|
|
70
|
+
*/
|
|
71
|
+
function spawnExternalCommand(command, args) {
|
|
72
|
+
const spawn = require("cross-spawn");
|
|
73
|
+
const result = spawn.sync(command, args, {
|
|
74
|
+
encoding: "utf8",
|
|
75
|
+
stdio: "inherit",
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (result.error) {
|
|
79
|
+
throw result.error;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (result.signal) {
|
|
83
|
+
process.kill(process.pid, result.signal);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
process.exitCode = result.status ?? 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
64
90
|
/**
|
|
65
91
|
* Get the error message of a given value.
|
|
66
92
|
* @param {any} error The value to get.
|
|
@@ -146,12 +172,7 @@ ${getErrorMessage(error)}`;
|
|
|
146
172
|
"You can also run this command directly using 'npm init @eslint/config@latest'.",
|
|
147
173
|
);
|
|
148
174
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
spawn.sync("npm", ["init", "@eslint/config@latest"], {
|
|
152
|
-
encoding: "utf8",
|
|
153
|
-
stdio: "inherit",
|
|
154
|
-
});
|
|
175
|
+
spawnExternalCommand("npm", ["init", "@eslint/config@latest"]);
|
|
155
176
|
return;
|
|
156
177
|
}
|
|
157
178
|
|
|
@@ -161,12 +182,7 @@ ${getErrorMessage(error)}`;
|
|
|
161
182
|
"You can also run this command directly using 'npx @eslint/mcp@latest'.",
|
|
162
183
|
);
|
|
163
184
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
spawn.sync("npx", ["@eslint/mcp@latest"], {
|
|
167
|
-
encoding: "utf8",
|
|
168
|
-
stdio: "inherit",
|
|
169
|
-
});
|
|
185
|
+
spawnExternalCommand("npx", ["@eslint/mcp@latest"]);
|
|
170
186
|
return;
|
|
171
187
|
}
|
|
172
188
|
|
|
@@ -617,7 +617,7 @@ function processCodePathToExit(analyzer, node) {
|
|
|
617
617
|
|
|
618
618
|
case "Identifier":
|
|
619
619
|
if (isIdentifierReference(node)) {
|
|
620
|
-
state.
|
|
620
|
+
state.makeFirstThrowablePathInTryOrCatchBlock();
|
|
621
621
|
dontForward = true;
|
|
622
622
|
}
|
|
623
623
|
break;
|
|
@@ -626,7 +626,7 @@ function processCodePathToExit(analyzer, node) {
|
|
|
626
626
|
case "ImportExpression":
|
|
627
627
|
case "MemberExpression":
|
|
628
628
|
case "NewExpression":
|
|
629
|
-
state.
|
|
629
|
+
state.makeFirstThrowablePathInTryOrCatchBlock();
|
|
630
630
|
break;
|
|
631
631
|
|
|
632
632
|
case "YieldExpression":
|
|
@@ -1772,11 +1772,12 @@ class CodePathState {
|
|
|
1772
1772
|
}
|
|
1773
1773
|
|
|
1774
1774
|
/**
|
|
1775
|
-
* Makes a code path segment from the first throwable node to the `catch`
|
|
1776
|
-
* block or the `finally` block
|
|
1775
|
+
* Makes a code path segment from the first throwable node in a `try` block to the `catch`
|
|
1776
|
+
* block or the `finally` block or from the first throwable node in a `catch` block
|
|
1777
|
+
* to the `finally` block.
|
|
1777
1778
|
* @returns {void}
|
|
1778
1779
|
*/
|
|
1779
|
-
|
|
1780
|
+
makeFirstThrowablePathInTryOrCatchBlock() {
|
|
1780
1781
|
const forkContext = this.forkContext;
|
|
1781
1782
|
|
|
1782
1783
|
if (!forkContext.reachable) {
|
|
@@ -1785,10 +1786,13 @@ class CodePathState {
|
|
|
1785
1786
|
|
|
1786
1787
|
const context = getThrowContext(this);
|
|
1787
1788
|
|
|
1789
|
+
if (context === this || !context.thrownForkContext.empty) {
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1788
1793
|
if (
|
|
1789
|
-
context
|
|
1790
|
-
context.position !== "
|
|
1791
|
-
!context.thrownForkContext.empty
|
|
1794
|
+
context.position !== "try" &&
|
|
1795
|
+
(context.position !== "catch" || !context.hasFinalizer)
|
|
1792
1796
|
) {
|
|
1793
1797
|
return;
|
|
1794
1798
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
//------------------------------------------------------------------------------
|
|
11
11
|
|
|
12
12
|
const astUtils = require("./utils/ast-utils");
|
|
13
|
+
const { isAnySegmentReachable } = require("./utils/code-path-utils");
|
|
13
14
|
|
|
14
15
|
//------------------------------------------------------------------------------
|
|
15
16
|
// Helpers
|
|
@@ -30,21 +31,6 @@ function isTargetMethod(node) {
|
|
|
30
31
|
return astUtils.isSpecificMemberAccess(node, null, TARGET_METHODS);
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
/**
|
|
34
|
-
* Checks all segments in a set and returns true if any are reachable.
|
|
35
|
-
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
36
|
-
* @returns {boolean} True if any segment is reachable; false otherwise.
|
|
37
|
-
*/
|
|
38
|
-
function isAnySegmentReachable(segments) {
|
|
39
|
-
for (const segment of segments) {
|
|
40
|
-
if (segment.reachable) {
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
34
|
/**
|
|
49
35
|
* Returns a human-legible description of an array method
|
|
50
36
|
* @param {string} arrayMethodName A method name to fully qualify
|
|
@@ -10,26 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
const astUtils = require("./utils/ast-utils");
|
|
12
12
|
const { upperCaseFirst } = require("../shared/string-utils");
|
|
13
|
+
const { isAnySegmentReachable } = require("./utils/code-path-utils");
|
|
13
14
|
|
|
14
15
|
//------------------------------------------------------------------------------
|
|
15
16
|
// Helpers
|
|
16
17
|
//------------------------------------------------------------------------------
|
|
17
18
|
|
|
18
|
-
/**
|
|
19
|
-
* Checks all segments in a set and returns true if all are unreachable.
|
|
20
|
-
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
21
|
-
* @returns {boolean} True if all segments are unreachable; false otherwise.
|
|
22
|
-
*/
|
|
23
|
-
function areAllSegmentsUnreachable(segments) {
|
|
24
|
-
for (const segment of segments) {
|
|
25
|
-
if (segment.reachable) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
19
|
/**
|
|
34
20
|
* Checks whether a given node is a `constructor` method in an ES6 class
|
|
35
21
|
* @param {ASTNode} node A node to check
|
|
@@ -100,7 +86,7 @@ module.exports = {
|
|
|
100
86
|
*/
|
|
101
87
|
if (
|
|
102
88
|
!funcInfo.hasReturnValue ||
|
|
103
|
-
|
|
89
|
+
!isAnySegmentReachable(funcInfo.currentSegments) ||
|
|
104
90
|
astUtils.isES5Constructor(node) ||
|
|
105
91
|
isClassConstructor(node)
|
|
106
92
|
) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
//------------------------------------------------------------------------------
|
|
11
11
|
|
|
12
12
|
const astUtils = require("./utils/ast-utils");
|
|
13
|
+
const { isAnySegmentReachable } = require("./utils/code-path-utils");
|
|
13
14
|
|
|
14
15
|
//------------------------------------------------------------------------------
|
|
15
16
|
// Helpers
|
|
@@ -17,21 +18,6 @@ const astUtils = require("./utils/ast-utils");
|
|
|
17
18
|
|
|
18
19
|
const TARGET_NODE_TYPE = /^(?:Arrow)?FunctionExpression$/u;
|
|
19
20
|
|
|
20
|
-
/**
|
|
21
|
-
* Checks all segments in a set and returns true if any are reachable.
|
|
22
|
-
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
23
|
-
* @returns {boolean} True if any segment is reachable; false otherwise.
|
|
24
|
-
*/
|
|
25
|
-
function isAnySegmentReachable(segments) {
|
|
26
|
-
for (const segment of segments) {
|
|
27
|
-
if (segment.reachable) {
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
21
|
//------------------------------------------------------------------------------
|
|
36
22
|
// Rule Definition
|
|
37
23
|
//------------------------------------------------------------------------------
|
package/lib/rules/max-depth.js
CHANGED
|
@@ -54,6 +54,8 @@ module.exports = {
|
|
|
54
54
|
},
|
|
55
55
|
|
|
56
56
|
create(context) {
|
|
57
|
+
const sourceCode = context.sourceCode;
|
|
58
|
+
|
|
57
59
|
//--------------------------------------------------------------------------
|
|
58
60
|
// Helpers
|
|
59
61
|
//--------------------------------------------------------------------------
|
|
@@ -102,6 +104,7 @@ module.exports = {
|
|
|
102
104
|
if (len > maxDepth) {
|
|
103
105
|
context.report({
|
|
104
106
|
node,
|
|
107
|
+
loc: sourceCode.getFirstToken(node).loc,
|
|
105
108
|
messageId: "tooDeeply",
|
|
106
109
|
data: { depth: len, maxDepth },
|
|
107
110
|
});
|
|
@@ -117,6 +120,18 @@ module.exports = {
|
|
|
117
120
|
functionStack[functionStack.length - 1]--;
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Checks whether a node is an else-if statement.
|
|
125
|
+
* @param {ASTNode} node node to evaluate
|
|
126
|
+
* @returns {boolean} Whether the node is an else-if statement
|
|
127
|
+
*/
|
|
128
|
+
function isElseIf(node) {
|
|
129
|
+
return (
|
|
130
|
+
node.parent.type === "IfStatement" &&
|
|
131
|
+
node.parent.alternate === node
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
120
135
|
//--------------------------------------------------------------------------
|
|
121
136
|
// Public API
|
|
122
137
|
//--------------------------------------------------------------------------
|
|
@@ -129,7 +144,7 @@ module.exports = {
|
|
|
129
144
|
StaticBlock: startFunction,
|
|
130
145
|
|
|
131
146
|
IfStatement(node) {
|
|
132
|
-
if (node
|
|
147
|
+
if (!isElseIf(node)) {
|
|
133
148
|
pushBlock(node);
|
|
134
149
|
}
|
|
135
150
|
},
|
|
@@ -142,7 +157,11 @@ module.exports = {
|
|
|
142
157
|
ForInStatement: pushBlock,
|
|
143
158
|
ForOfStatement: pushBlock,
|
|
144
159
|
|
|
145
|
-
"IfStatement:exit"
|
|
160
|
+
"IfStatement:exit"(node) {
|
|
161
|
+
if (!isElseIf(node)) {
|
|
162
|
+
popBlock();
|
|
163
|
+
}
|
|
164
|
+
},
|
|
146
165
|
"SwitchStatement:exit": popBlock,
|
|
147
166
|
"TryStatement:exit": popBlock,
|
|
148
167
|
"DoWhileStatement:exit": popBlock,
|
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
+
//------------------------------------------------------------------------------
|
|
9
|
+
// Requirements
|
|
10
|
+
//------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
|
13
|
+
|
|
8
14
|
//------------------------------------------------------------------------------
|
|
9
15
|
// Rule Definition
|
|
10
16
|
//------------------------------------------------------------------------------
|
|
@@ -53,6 +59,8 @@ module.exports = {
|
|
|
53
59
|
},
|
|
54
60
|
|
|
55
61
|
create(context) {
|
|
62
|
+
const sourceCode = context.sourceCode;
|
|
63
|
+
|
|
56
64
|
//--------------------------------------------------------------------------
|
|
57
65
|
// Constants
|
|
58
66
|
//--------------------------------------------------------------------------
|
|
@@ -90,17 +98,25 @@ module.exports = {
|
|
|
90
98
|
if (callbackStack.length > THRESHOLD) {
|
|
91
99
|
const opts = { num: callbackStack.length, max: THRESHOLD };
|
|
92
100
|
|
|
93
|
-
context.report({
|
|
101
|
+
context.report({
|
|
102
|
+
node,
|
|
103
|
+
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
|
104
|
+
messageId: "exceed",
|
|
105
|
+
data: opts,
|
|
106
|
+
});
|
|
94
107
|
}
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
/**
|
|
98
111
|
* Pops the call stack.
|
|
112
|
+
* @param {ASTNode} node The node to check.
|
|
99
113
|
* @returns {void}
|
|
100
114
|
* @private
|
|
101
115
|
*/
|
|
102
|
-
function popStack() {
|
|
103
|
-
callbackStack.
|
|
116
|
+
function popStack(node) {
|
|
117
|
+
if (callbackStack.at(-1) === node) {
|
|
118
|
+
callbackStack.pop();
|
|
119
|
+
}
|
|
104
120
|
}
|
|
105
121
|
|
|
106
122
|
//--------------------------------------------------------------------------
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
//------------------------------------------------------------------------------
|
|
10
10
|
|
|
11
11
|
const { directivesPattern } = require("../shared/directives");
|
|
12
|
+
const { isAnySegmentReachable } = require("./utils/code-path-utils");
|
|
12
13
|
|
|
13
14
|
//------------------------------------------------------------------------------
|
|
14
15
|
// Helpers
|
|
@@ -16,21 +17,6 @@ const { directivesPattern } = require("../shared/directives");
|
|
|
16
17
|
|
|
17
18
|
const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/iu;
|
|
18
19
|
|
|
19
|
-
/**
|
|
20
|
-
* Checks all segments in a set and returns true if any are reachable.
|
|
21
|
-
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
22
|
-
* @returns {boolean} True if any segment is reachable; false otherwise.
|
|
23
|
-
*/
|
|
24
|
-
function isAnySegmentReachable(segments) {
|
|
25
|
-
for (const segment of segments) {
|
|
26
|
-
if (segment.reachable) {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
20
|
/**
|
|
35
21
|
* Checks whether or not a given comment string is really a fallthrough comment and not an ESLint directive.
|
|
36
22
|
* @param {string} comment The comment string to check.
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
"use strict";
|
|
7
7
|
|
|
8
|
+
const { isAnySegmentReachable } = require("./utils/code-path-utils");
|
|
9
|
+
|
|
8
10
|
//------------------------------------------------------------------------------
|
|
9
11
|
// Helpers
|
|
10
12
|
//------------------------------------------------------------------------------
|
|
@@ -17,21 +19,6 @@ const allLoopTypes = [
|
|
|
17
19
|
"ForOfStatement",
|
|
18
20
|
];
|
|
19
21
|
|
|
20
|
-
/**
|
|
21
|
-
* Checks all segments in a set and returns true if any are reachable.
|
|
22
|
-
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
23
|
-
* @returns {boolean} True if any segment is reachable; false otherwise.
|
|
24
|
-
*/
|
|
25
|
-
function isAnySegmentReachable(segments) {
|
|
26
|
-
for (const segment of segments) {
|
|
27
|
-
if (segment.reachable) {
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
22
|
/**
|
|
36
23
|
* Determines whether the given node is the first node in the code path to which a loop statement
|
|
37
24
|
* 'loops' for the next iteration.
|
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
"use strict";
|
|
6
6
|
|
|
7
|
+
//------------------------------------------------------------------------------
|
|
8
|
+
// Requirements
|
|
9
|
+
//------------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
const { isAnySegmentReachable } = require("./utils/code-path-utils");
|
|
12
|
+
|
|
7
13
|
//------------------------------------------------------------------------------
|
|
8
14
|
// Helpers
|
|
9
15
|
//------------------------------------------------------------------------------
|
|
@@ -23,21 +29,6 @@ function isInitialized(node) {
|
|
|
23
29
|
return Boolean(node.init);
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
/**
|
|
27
|
-
* Checks all segments in a set and returns true if all are unreachable.
|
|
28
|
-
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
29
|
-
* @returns {boolean} True if all segments are unreachable; false otherwise.
|
|
30
|
-
*/
|
|
31
|
-
function areAllSegmentsUnreachable(segments) {
|
|
32
|
-
for (const segment of segments) {
|
|
33
|
-
if (segment.reachable) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
32
|
/**
|
|
42
33
|
* The class to distinguish consecutive unreachable statements.
|
|
43
34
|
*/
|
|
@@ -154,7 +145,7 @@ module.exports = {
|
|
|
154
145
|
if (
|
|
155
146
|
node &&
|
|
156
147
|
(node.type === "PropertyDefinition" ||
|
|
157
|
-
|
|
148
|
+
!isAnySegmentReachable(currentCodePathSegments))
|
|
158
149
|
) {
|
|
159
150
|
// Store this statement to distinguish consecutive statements.
|
|
160
151
|
if (range.isEmpty) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
const astUtils = require("./utils/ast-utils"),
|
|
12
12
|
FixTracker = require("./utils/fix-tracker");
|
|
13
|
+
const { isAnySegmentReachable } = require("./utils/code-path-utils");
|
|
13
14
|
|
|
14
15
|
//------------------------------------------------------------------------------
|
|
15
16
|
// Helpers
|
|
@@ -60,21 +61,6 @@ function isInFinally(node) {
|
|
|
60
61
|
return false;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
/**
|
|
64
|
-
* Checks all segments in a set and returns true if any are reachable.
|
|
65
|
-
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
66
|
-
* @returns {boolean} True if any segment is reachable; false otherwise.
|
|
67
|
-
*/
|
|
68
|
-
function isAnySegmentReachable(segments) {
|
|
69
|
-
for (const segment of segments) {
|
|
70
|
-
if (segment.reachable) {
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
64
|
//------------------------------------------------------------------------------
|
|
79
65
|
// Rule Definition
|
|
80
66
|
//------------------------------------------------------------------------------
|
package/lib/rules/no-with.js
CHANGED
|
@@ -28,9 +28,15 @@ module.exports = {
|
|
|
28
28
|
},
|
|
29
29
|
|
|
30
30
|
create(context) {
|
|
31
|
+
const sourceCode = context.sourceCode;
|
|
32
|
+
|
|
31
33
|
return {
|
|
32
34
|
WithStatement(node) {
|
|
33
|
-
context.report({
|
|
35
|
+
context.report({
|
|
36
|
+
node,
|
|
37
|
+
loc: sourceCode.getFirstToken(node).loc,
|
|
38
|
+
messageId: "unexpectedWith",
|
|
39
|
+
});
|
|
34
40
|
},
|
|
35
41
|
};
|
|
36
42
|
},
|
|
@@ -323,6 +323,24 @@ module.exports = {
|
|
|
323
323
|
return;
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
const functionToken = sourceCode.getFirstToken(
|
|
327
|
+
node,
|
|
328
|
+
node.async ? 1 : 0,
|
|
329
|
+
);
|
|
330
|
+
const leftParenToken = sourceCode.getTokenAfter(
|
|
331
|
+
functionToken,
|
|
332
|
+
astUtils.isOpeningParenToken,
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
if (node.async) {
|
|
336
|
+
if (
|
|
337
|
+
functionToken.loc.end.line <
|
|
338
|
+
leftParenToken.loc.start.line
|
|
339
|
+
) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
326
344
|
// Remove `.bind(this)` if exists.
|
|
327
345
|
if (callbackInfo.isLexicalThis) {
|
|
328
346
|
const memberNode = node.parent;
|
|
@@ -375,14 +393,6 @@ module.exports = {
|
|
|
375
393
|
}
|
|
376
394
|
|
|
377
395
|
// Convert the function expression to an arrow function.
|
|
378
|
-
const functionToken = sourceCode.getFirstToken(
|
|
379
|
-
node,
|
|
380
|
-
node.async ? 1 : 0,
|
|
381
|
-
);
|
|
382
|
-
const leftParenToken = sourceCode.getTokenAfter(
|
|
383
|
-
functionToken,
|
|
384
|
-
astUtils.isOpeningParenToken,
|
|
385
|
-
);
|
|
386
396
|
const tokenBeforeBody = sourceCode.getTokenBefore(
|
|
387
397
|
node.body,
|
|
388
398
|
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Code path related utilities.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Checks all segments in a set and returns true if any are reachable.
|
|
9
|
+
* @param {Set<CodePathSegment>} segments The segments to check.
|
|
10
|
+
* @returns {boolean} `true` if any segment is reachable; `false` otherwise.
|
|
11
|
+
*/
|
|
12
|
+
function isAnySegmentReachable(segments) {
|
|
13
|
+
for (const segment of segments) {
|
|
14
|
+
if (segment.reachable) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = { isAnySegmentReachable };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -723,6 +723,22 @@ export namespace Rule {
|
|
|
723
723
|
| "class-field-initializer"
|
|
724
724
|
| "class-static-block";
|
|
725
725
|
|
|
726
|
+
interface CodePathSegmentTraversalController {
|
|
727
|
+
skip(): void;
|
|
728
|
+
break(): void;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
type CodePathSegmentTraversalCallback = (
|
|
732
|
+
this: CodePath,
|
|
733
|
+
segment: CodePathSegment,
|
|
734
|
+
controller: CodePathSegmentTraversalController,
|
|
735
|
+
) => void;
|
|
736
|
+
|
|
737
|
+
interface CodePathTraversalOptions {
|
|
738
|
+
first?: CodePathSegment | undefined;
|
|
739
|
+
last?: CodePathSegment | undefined;
|
|
740
|
+
}
|
|
741
|
+
|
|
726
742
|
interface CodePath {
|
|
727
743
|
id: string;
|
|
728
744
|
origin: CodePathOrigin;
|
|
@@ -732,12 +748,19 @@ export namespace Rule {
|
|
|
732
748
|
thrownSegments: CodePathSegment[];
|
|
733
749
|
upper: CodePath | null;
|
|
734
750
|
childCodePaths: CodePath[];
|
|
751
|
+
traverseSegments(callback: CodePathSegmentTraversalCallback): void;
|
|
752
|
+
traverseSegments(
|
|
753
|
+
options: CodePathTraversalOptions,
|
|
754
|
+
callback: CodePathSegmentTraversalCallback,
|
|
755
|
+
): void;
|
|
735
756
|
}
|
|
736
757
|
|
|
737
758
|
interface CodePathSegment {
|
|
738
759
|
id: string;
|
|
739
760
|
nextSegments: CodePathSegment[];
|
|
740
761
|
prevSegments: CodePathSegment[];
|
|
762
|
+
allNextSegments: CodePathSegment[];
|
|
763
|
+
allPrevSegments: CodePathSegment[];
|
|
741
764
|
reachable: boolean;
|
|
742
765
|
}
|
|
743
766
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.5.0",
|
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -78,8 +78,15 @@
|
|
|
78
78
|
"test:fuzz": "node Makefile.js fuzz",
|
|
79
79
|
"test:performance": "node Makefile.js perf",
|
|
80
80
|
"test:pnpm": "cd tests/pnpm && node check.js && pnpm install && pnpm exec tsc",
|
|
81
|
-
"test:types": "tsc -p tests/lib/types/tsconfig.json"
|
|
81
|
+
"test:types": "tsc -p tests/lib/types/tsconfig.json && npm run test:types --workspaces --if-present",
|
|
82
|
+
"test:types:5.3": "npx -p typescript@5.3 -y -- tsc -p tsconfig.types-legacy.json",
|
|
83
|
+
"test:types:5.x": "npx -p typescript@5.x -y -- tsc -p tsconfig.types.json",
|
|
84
|
+
"test:types:7.x": "npx -p @typescript/native-preview@latest -y -- tsgo -p tsconfig.types.json",
|
|
85
|
+
"test:types:all": "npm run test:types && npm run test:types:5.3 && npm run test:types:5.x && npm run test:types:7.x"
|
|
82
86
|
},
|
|
87
|
+
"workspaces": [
|
|
88
|
+
"packages/*"
|
|
89
|
+
],
|
|
83
90
|
"gitHooks": {
|
|
84
91
|
"pre-commit": "lint-staged"
|
|
85
92
|
},
|
|
@@ -124,7 +131,7 @@
|
|
|
124
131
|
"@eslint/config-array": "^0.23.5",
|
|
125
132
|
"@eslint/config-helpers": "^0.6.0",
|
|
126
133
|
"@eslint/core": "^1.2.1",
|
|
127
|
-
"@eslint/plugin-kit": "^0.7.
|
|
134
|
+
"@eslint/plugin-kit": "^0.7.2",
|
|
128
135
|
"@humanfs/node": "^0.16.6",
|
|
129
136
|
"@humanwhocodes/module-importer": "^1.0.1",
|
|
130
137
|
"@humanwhocodes/retry": "^0.4.2",
|
|
@@ -151,12 +158,12 @@
|
|
|
151
158
|
"optionator": "^0.9.3"
|
|
152
159
|
},
|
|
153
160
|
"devDependencies": {
|
|
154
|
-
"@arethetypeswrong/cli": "^0.18.
|
|
161
|
+
"@arethetypeswrong/cli": "^0.18.3",
|
|
155
162
|
"@babel/core": "^7.4.3",
|
|
156
163
|
"@babel/preset-env": "^7.4.3",
|
|
157
164
|
"@cypress/webpack-preprocessor": "^6.0.2",
|
|
158
165
|
"@eslint/eslintrc": "^3.3.5",
|
|
159
|
-
"@eslint/json": "^
|
|
166
|
+
"@eslint/json": "^2.0.0",
|
|
160
167
|
"@types/esquery": "^1.5.4",
|
|
161
168
|
"@types/node": "^22.13.14",
|
|
162
169
|
"@typescript-eslint/parser": "^8.58.2",
|