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 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 is always built in.)
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> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="TestMu AI Open Source Office (Formerly LambdaTest)" height="32"></a></p>
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
- const spawn = require("cross-spawn");
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
- const spawn = require("cross-spawn");
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.makeFirstThrowablePathInTryBlock();
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.makeFirstThrowablePathInTryBlock();
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
- makeFirstThrowablePathInTryBlock() {
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 === this ||
1790
- context.position !== "try" ||
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
- areAllSegmentsUnreachable(funcInfo.currentSegments) ||
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
  //------------------------------------------------------------------------------
@@ -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.parent.type !== "IfStatement") {
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": popBlock,
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,
@@ -222,6 +222,7 @@ module.exports = {
222
222
 
223
223
  context.report({
224
224
  node,
225
+ loc: astUtils.getFunctionHeadLoc(funcNode, sourceCode),
225
226
  messageId: "exceed",
226
227
  data: { name, lineCount, maxLines },
227
228
  });
@@ -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({ node, messageId: "exceed", data: opts });
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.pop();
116
+ function popStack(node) {
117
+ if (callbackStack.at(-1) === node) {
118
+ callbackStack.pop();
119
+ }
104
120
  }
105
121
 
106
122
  //--------------------------------------------------------------------------
@@ -108,6 +108,7 @@ module.exports = {
108
108
 
109
109
  context.report({
110
110
  node,
111
+ loc: astUtils.getFunctionHeadLoc(node, context.sourceCode),
111
112
  messageId: "exceed",
112
113
  data: { name, count, max },
113
114
  });
@@ -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
- areAllSegmentsUnreachable(currentCodePathSegments))
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
  //------------------------------------------------------------------------------
@@ -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({ node, messageId: "unexpectedWith" });
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 };
@@ -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.4.0",
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.1",
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.0",
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": "^1.2.0",
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",