eslint 8.47.0 → 8.49.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
@@ -288,8 +288,8 @@ The following companies, organizations, and individuals support ESLint's ongoing
288
288
  <h3>Platinum Sponsors</h3>
289
289
  <p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
290
290
  <p><a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
291
- <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>
292
- <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://github.com/about"><img src="https://avatars.githubusercontent.com/u/9919?v=4" alt="GitHub" 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>
291
+ <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://opensource.siemens.com"><img src="https://avatars.githubusercontent.com/u/624020?v=4" alt="Siemens" 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>
292
+ <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" 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://github.com/about"><img src="https://avatars.githubusercontent.com/u/9919?v=4" alt="GitHub" 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>
293
293
  <!--sponsorsend-->
294
294
 
295
295
  ## Technology Sponsors
@@ -9,7 +9,8 @@
9
9
  // Requirements
10
10
  //-----------------------------------------------------------------------------
11
11
 
12
- const ajv = require("../shared/ajv")();
12
+ const ajvImport = require("../shared/ajv");
13
+ const ajv = ajvImport();
13
14
  const {
14
15
  parseRuleId,
15
16
  getRuleFromConfig,
@@ -192,15 +192,18 @@ function forwardCurrentToHead(analyzer, node) {
192
192
  headSegment = headSegments[i];
193
193
 
194
194
  if (currentSegment !== headSegment && currentSegment) {
195
- debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
196
195
 
197
- if (currentSegment.reachable) {
198
- analyzer.emitter.emit(
199
- "onCodePathSegmentEnd",
200
- currentSegment,
201
- node
202
- );
203
- }
196
+ const eventName = currentSegment.reachable
197
+ ? "onCodePathSegmentEnd"
198
+ : "onUnreachableCodePathSegmentEnd";
199
+
200
+ debug.dump(`${eventName} ${currentSegment.id}`);
201
+
202
+ analyzer.emitter.emit(
203
+ eventName,
204
+ currentSegment,
205
+ node
206
+ );
204
207
  }
205
208
  }
206
209
 
@@ -213,16 +216,19 @@ function forwardCurrentToHead(analyzer, node) {
213
216
  headSegment = headSegments[i];
214
217
 
215
218
  if (currentSegment !== headSegment && headSegment) {
216
- debug.dump(`onCodePathSegmentStart ${headSegment.id}`);
219
+
220
+ const eventName = headSegment.reachable
221
+ ? "onCodePathSegmentStart"
222
+ : "onUnreachableCodePathSegmentStart";
223
+
224
+ debug.dump(`${eventName} ${headSegment.id}`);
217
225
 
218
226
  CodePathSegment.markUsed(headSegment);
219
- if (headSegment.reachable) {
220
- analyzer.emitter.emit(
221
- "onCodePathSegmentStart",
222
- headSegment,
223
- node
224
- );
225
- }
227
+ analyzer.emitter.emit(
228
+ eventName,
229
+ headSegment,
230
+ node
231
+ );
226
232
  }
227
233
  }
228
234
 
@@ -241,15 +247,17 @@ function leaveFromCurrentSegment(analyzer, node) {
241
247
 
242
248
  for (let i = 0; i < currentSegments.length; ++i) {
243
249
  const currentSegment = currentSegments[i];
250
+ const eventName = currentSegment.reachable
251
+ ? "onCodePathSegmentEnd"
252
+ : "onUnreachableCodePathSegmentEnd";
244
253
 
245
- debug.dump(`onCodePathSegmentEnd ${currentSegment.id}`);
246
- if (currentSegment.reachable) {
247
- analyzer.emitter.emit(
248
- "onCodePathSegmentEnd",
249
- currentSegment,
250
- node
251
- );
252
- }
254
+ debug.dump(`${eventName} ${currentSegment.id}`);
255
+
256
+ analyzer.emitter.emit(
257
+ eventName,
258
+ currentSegment,
259
+ node
260
+ );
253
261
  }
254
262
 
255
263
  state.currentSegments = [];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview A class of the code path segment.
2
+ * @fileoverview The CodePathSegment class.
3
3
  * @author Toru Nagashima
4
4
  */
5
5
 
@@ -30,10 +30,22 @@ function isReachable(segment) {
30
30
 
31
31
  /**
32
32
  * A code path segment.
33
+ *
34
+ * Each segment is arranged in a series of linked lists (implemented by arrays)
35
+ * that keep track of the previous and next segments in a code path. In this way,
36
+ * you can navigate between all segments in any code path so long as you have a
37
+ * reference to any segment in that code path.
38
+ *
39
+ * When first created, the segment is in a detached state, meaning that it knows the
40
+ * segments that came before it but those segments don't know that this new segment
41
+ * follows it. Only when `CodePathSegment#markUsed()` is called on a segment does it
42
+ * officially become part of the code path by updating the previous segments to know
43
+ * that this new segment follows.
33
44
  */
34
45
  class CodePathSegment {
35
46
 
36
47
  /**
48
+ * Creates a new instance.
37
49
  * @param {string} id An identifier.
38
50
  * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
39
51
  * This array includes unreachable segments.
@@ -49,27 +61,25 @@ class CodePathSegment {
49
61
  this.id = id;
50
62
 
51
63
  /**
52
- * An array of the next segments.
64
+ * An array of the next reachable segments.
53
65
  * @type {CodePathSegment[]}
54
66
  */
55
67
  this.nextSegments = [];
56
68
 
57
69
  /**
58
- * An array of the previous segments.
70
+ * An array of the previous reachable segments.
59
71
  * @type {CodePathSegment[]}
60
72
  */
61
73
  this.prevSegments = allPrevSegments.filter(isReachable);
62
74
 
63
75
  /**
64
- * An array of the next segments.
65
- * This array includes unreachable segments.
76
+ * An array of all next segments including reachable and unreachable.
66
77
  * @type {CodePathSegment[]}
67
78
  */
68
79
  this.allNextSegments = [];
69
80
 
70
81
  /**
71
- * An array of the previous segments.
72
- * This array includes unreachable segments.
82
+ * An array of all previous segments including reachable and unreachable.
73
83
  * @type {CodePathSegment[]}
74
84
  */
75
85
  this.allPrevSegments = allPrevSegments;
@@ -83,7 +93,11 @@ class CodePathSegment {
83
93
  // Internal data.
84
94
  Object.defineProperty(this, "internal", {
85
95
  value: {
96
+
97
+ // determines if the segment has been attached to the code path
86
98
  used: false,
99
+
100
+ // array of previous segments coming from the end of a loop
87
101
  loopedPrevSegments: []
88
102
  }
89
103
  });
@@ -113,9 +127,10 @@ class CodePathSegment {
113
127
  }
114
128
 
115
129
  /**
116
- * Creates a segment that follows given segments.
130
+ * Creates a new segment and appends it after the given segments.
117
131
  * @param {string} id An identifier.
118
- * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
132
+ * @param {CodePathSegment[]} allPrevSegments An array of the previous segments
133
+ * to append to.
119
134
  * @returns {CodePathSegment} The created segment.
120
135
  */
121
136
  static newNext(id, allPrevSegments) {
@@ -127,7 +142,7 @@ class CodePathSegment {
127
142
  }
128
143
 
129
144
  /**
130
- * Creates an unreachable segment that follows given segments.
145
+ * Creates an unreachable segment and appends it after the given segments.
131
146
  * @param {string} id An identifier.
132
147
  * @param {CodePathSegment[]} allPrevSegments An array of the previous segments.
133
148
  * @returns {CodePathSegment} The created segment.
@@ -137,7 +152,7 @@ class CodePathSegment {
137
152
 
138
153
  /*
139
154
  * In `if (a) return a; foo();` case, the unreachable segment preceded by
140
- * the return statement is not used but must not be remove.
155
+ * the return statement is not used but must not be removed.
141
156
  */
142
157
  CodePathSegment.markUsed(segment);
143
158
 
@@ -157,7 +172,7 @@ class CodePathSegment {
157
172
  }
158
173
 
159
174
  /**
160
- * Makes a given segment being used.
175
+ * Marks a given segment as used.
161
176
  *
162
177
  * And this function registers the segment into the previous segments as a next.
163
178
  * @param {CodePathSegment} segment A segment to mark.
@@ -172,6 +187,13 @@ class CodePathSegment {
172
187
  let i;
173
188
 
174
189
  if (segment.reachable) {
190
+
191
+ /*
192
+ * If the segment is reachable, then it's officially part of the
193
+ * code path. This loops through all previous segments to update
194
+ * their list of next segments. Because the segment is reachable,
195
+ * it's added to both `nextSegments` and `allNextSegments`.
196
+ */
175
197
  for (i = 0; i < segment.allPrevSegments.length; ++i) {
176
198
  const prevSegment = segment.allPrevSegments[i];
177
199
 
@@ -179,6 +201,13 @@ class CodePathSegment {
179
201
  prevSegment.nextSegments.push(segment);
180
202
  }
181
203
  } else {
204
+
205
+ /*
206
+ * If the segment is not reachable, then it's not officially part of the
207
+ * code path. This loops through all previous segments to update
208
+ * their list of next segments. Because the segment is not reachable,
209
+ * it's added only to `allNextSegments`.
210
+ */
182
211
  for (i = 0; i < segment.allPrevSegments.length; ++i) {
183
212
  segment.allPrevSegments[i].allNextSegments.push(segment);
184
213
  }
@@ -196,19 +225,20 @@ class CodePathSegment {
196
225
  }
197
226
 
198
227
  /**
199
- * Replaces unused segments with the previous segments of each unused segment.
200
- * @param {CodePathSegment[]} segments An array of segments to replace.
201
- * @returns {CodePathSegment[]} The replaced array.
228
+ * Creates a new array based on an array of segments. If any segment in the
229
+ * array is unused, then it is replaced by all of its previous segments.
230
+ * All used segments are returned as-is without replacement.
231
+ * @param {CodePathSegment[]} segments The array of segments to flatten.
232
+ * @returns {CodePathSegment[]} The flattened array.
202
233
  */
203
234
  static flattenUnusedSegments(segments) {
204
- const done = Object.create(null);
205
- const retv = [];
235
+ const done = new Set();
206
236
 
207
237
  for (let i = 0; i < segments.length; ++i) {
208
238
  const segment = segments[i];
209
239
 
210
240
  // Ignores duplicated.
211
- if (done[segment.id]) {
241
+ if (done.has(segment)) {
212
242
  continue;
213
243
  }
214
244
 
@@ -217,18 +247,16 @@ class CodePathSegment {
217
247
  for (let j = 0; j < segment.allPrevSegments.length; ++j) {
218
248
  const prevSegment = segment.allPrevSegments[j];
219
249
 
220
- if (!done[prevSegment.id]) {
221
- done[prevSegment.id] = true;
222
- retv.push(prevSegment);
250
+ if (!done.has(prevSegment)) {
251
+ done.add(prevSegment);
223
252
  }
224
253
  }
225
254
  } else {
226
- done[segment.id] = true;
227
- retv.push(segment);
255
+ done.add(segment);
228
256
  }
229
257
  }
230
258
 
231
- return retv;
259
+ return [...done];
232
260
  }
233
261
  }
234
262
 
@@ -117,6 +117,7 @@ class CodePath {
117
117
  /**
118
118
  * Current code path segments.
119
119
  * @type {CodePathSegment[]}
120
+ * @deprecated
120
121
  */
121
122
  get currentSegments() {
122
123
  return this.internal.currentSegments;
@@ -898,6 +898,7 @@ const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
898
898
  getTokensBetween: "getTokensBetween"
899
899
  };
900
900
 
901
+
901
902
  const BASE_TRAVERSAL_CONTEXT = Object.freeze(
902
903
  Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).reduce(
903
904
  (contextInfo, methodName) =>
@@ -16,7 +16,9 @@ const
16
16
  equal = require("fast-deep-equal"),
17
17
  Traverser = require("../shared/traverser"),
18
18
  { getRuleOptionsSchema } = require("../config/flat-config-helpers"),
19
- { Linter, SourceCodeFixer, interpolate } = require("../linter");
19
+ { Linter, SourceCodeFixer, interpolate } = require("../linter"),
20
+ CodePath = require("../linter/code-path-analysis/code-path");
21
+
20
22
  const { FlatConfigArray } = require("../config/flat-config-array");
21
23
  const { defaultConfig } = require("../config/default-config");
22
24
 
@@ -32,6 +34,7 @@ const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
32
34
 
33
35
  /** @typedef {import("../shared/types").Parser} Parser */
34
36
  /** @typedef {import("../shared/types").LanguageOptions} LanguageOptions */
37
+ /** @typedef {import("../shared/types").Rule} Rule */
35
38
 
36
39
 
37
40
  /**
@@ -273,6 +276,21 @@ function getCommentsDeprecation() {
273
276
  );
274
277
  }
275
278
 
279
+ /**
280
+ * Emit a deprecation warning if rule uses CodePath#currentSegments.
281
+ * @param {string} ruleName Name of the rule.
282
+ * @returns {void}
283
+ */
284
+ function emitCodePathCurrentSegmentsWarning(ruleName) {
285
+ if (!emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`]) {
286
+ emitCodePathCurrentSegmentsWarning[`warned-${ruleName}`] = true;
287
+ process.emitWarning(
288
+ `"${ruleName}" rule uses CodePath#currentSegments and will stop working in ESLint v9. Please read the documentation for how to update your code: https://eslint.org/docs/latest/extend/code-path-analysis#usage-examples`,
289
+ "DeprecationWarning"
290
+ );
291
+ }
292
+ }
293
+
276
294
  //------------------------------------------------------------------------------
277
295
  // Public Interface
278
296
  //------------------------------------------------------------------------------
@@ -446,7 +464,7 @@ class FlatRuleTester {
446
464
  /**
447
465
  * Adds a new rule test to execute.
448
466
  * @param {string} ruleName The name of the rule to run.
449
- * @param {Function} rule The rule to test.
467
+ * @param {Function | Rule} rule The rule to test.
450
468
  * @param {{
451
469
  * valid: (ValidTestCase | string)[],
452
470
  * invalid: InvalidTestCase[]
@@ -663,6 +681,7 @@ class FlatRuleTester {
663
681
 
664
682
  // Verify the code.
665
683
  const { getComments } = SourceCode.prototype;
684
+ const originalCurrentSegments = Object.getOwnPropertyDescriptor(CodePath.prototype, "currentSegments");
666
685
  let messages;
667
686
 
668
687
  // check for validation errors
@@ -676,11 +695,20 @@ class FlatRuleTester {
676
695
 
677
696
  try {
678
697
  SourceCode.prototype.getComments = getCommentsDeprecation;
698
+ Object.defineProperty(CodePath.prototype, "currentSegments", {
699
+ get() {
700
+ emitCodePathCurrentSegmentsWarning(ruleName);
701
+ return originalCurrentSegments.get.call(this);
702
+ }
703
+ });
704
+
679
705
  messages = linter.verify(code, configs, filename);
680
706
  } finally {
681
707
  SourceCode.prototype.getComments = getComments;
708
+ Object.defineProperty(CodePath.prototype, "currentSegments", originalCurrentSegments);
682
709
  }
683
710
 
711
+
684
712
  const fatalErrorMessage = messages.find(m => m.fatal);
685
713
 
686
714
  assert(!fatalErrorMessage, `A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`);
@@ -1011,29 +1039,35 @@ class FlatRuleTester {
1011
1039
  /*
1012
1040
  * This creates a mocha test suite and pipes all supplied info through
1013
1041
  * one of the templates above.
1042
+ * The test suites for valid/invalid are created conditionally as
1043
+ * test runners (eg. vitest) fail for empty test suites.
1014
1044
  */
1015
1045
  this.constructor.describe(ruleName, () => {
1016
- this.constructor.describe("valid", () => {
1017
- test.valid.forEach(valid => {
1018
- this.constructor[valid.only ? "itOnly" : "it"](
1019
- sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
1020
- () => {
1021
- testValidTemplate(valid);
1022
- }
1023
- );
1046
+ if (test.valid.length > 0) {
1047
+ this.constructor.describe("valid", () => {
1048
+ test.valid.forEach(valid => {
1049
+ this.constructor[valid.only ? "itOnly" : "it"](
1050
+ sanitize(typeof valid === "object" ? valid.name || valid.code : valid),
1051
+ () => {
1052
+ testValidTemplate(valid);
1053
+ }
1054
+ );
1055
+ });
1024
1056
  });
1025
- });
1057
+ }
1026
1058
 
1027
- this.constructor.describe("invalid", () => {
1028
- test.invalid.forEach(invalid => {
1029
- this.constructor[invalid.only ? "itOnly" : "it"](
1030
- sanitize(invalid.name || invalid.code),
1031
- () => {
1032
- testInvalidTemplate(invalid);
1033
- }
1034
- );
1059
+ if (test.invalid.length > 0) {
1060
+ this.constructor.describe("invalid", () => {
1061
+ test.invalid.forEach(invalid => {
1062
+ this.constructor[invalid.only ? "itOnly" : "it"](
1063
+ sanitize(invalid.name || invalid.code),
1064
+ () => {
1065
+ testInvalidTemplate(invalid);
1066
+ }
1067
+ );
1068
+ });
1035
1069
  });
1036
- });
1070
+ }
1037
1071
  });
1038
1072
  }
1039
1073
  }