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.
Files changed (156) hide show
  1. package/README.md +31 -28
  2. package/bin/eslint.js +4 -3
  3. package/conf/ecma-version.js +16 -0
  4. package/conf/globals.js +1 -0
  5. package/conf/rule-type-list.json +3 -1
  6. package/lib/api.js +7 -11
  7. package/lib/cli-engine/cli-engine.js +14 -3
  8. package/lib/cli-engine/formatters/formatters-meta.json +1 -29
  9. package/lib/cli-engine/lint-result-cache.js +2 -2
  10. package/lib/cli.js +115 -36
  11. package/lib/config/default-config.js +3 -0
  12. package/lib/config/flat-config-array.js +110 -24
  13. package/lib/config/flat-config-helpers.js +41 -20
  14. package/lib/config/flat-config-schema.js +1 -7
  15. package/lib/config/rule-validator.js +42 -6
  16. package/lib/eslint/eslint-helpers.js +116 -58
  17. package/lib/eslint/eslint.js +892 -377
  18. package/lib/eslint/index.js +2 -2
  19. package/lib/eslint/legacy-eslint.js +728 -0
  20. package/lib/linter/apply-disable-directives.js +59 -31
  21. package/lib/linter/code-path-analysis/code-path-analyzer.js +0 -1
  22. package/lib/linter/code-path-analysis/code-path.js +32 -30
  23. package/lib/linter/code-path-analysis/fork-context.js +1 -1
  24. package/lib/linter/config-comment-parser.js +8 -11
  25. package/lib/linter/index.js +1 -3
  26. package/lib/linter/interpolate.js +24 -2
  27. package/lib/linter/linter.js +428 -207
  28. package/lib/linter/report-translator.js +3 -3
  29. package/lib/linter/rules.js +6 -15
  30. package/lib/linter/source-code-fixer.js +1 -1
  31. package/lib/linter/timing.js +16 -8
  32. package/lib/options.js +35 -3
  33. package/lib/rule-tester/index.js +3 -1
  34. package/lib/rule-tester/rule-tester.js +424 -347
  35. package/lib/rules/array-bracket-newline.js +1 -1
  36. package/lib/rules/array-bracket-spacing.js +1 -1
  37. package/lib/rules/block-scoped-var.js +1 -1
  38. package/lib/rules/callback-return.js +2 -2
  39. package/lib/rules/camelcase.js +3 -5
  40. package/lib/rules/capitalized-comments.js +10 -7
  41. package/lib/rules/comma-dangle.js +1 -1
  42. package/lib/rules/comma-style.js +2 -2
  43. package/lib/rules/complexity.js +14 -1
  44. package/lib/rules/constructor-super.js +99 -100
  45. package/lib/rules/default-case.js +1 -1
  46. package/lib/rules/eol-last.js +2 -2
  47. package/lib/rules/function-paren-newline.js +2 -2
  48. package/lib/rules/indent-legacy.js +5 -5
  49. package/lib/rules/indent.js +5 -5
  50. package/lib/rules/index.js +1 -2
  51. package/lib/rules/key-spacing.js +2 -2
  52. package/lib/rules/line-comment-position.js +1 -1
  53. package/lib/rules/lines-around-directive.js +2 -2
  54. package/lib/rules/max-depth.js +1 -1
  55. package/lib/rules/max-len.js +3 -3
  56. package/lib/rules/max-lines.js +3 -3
  57. package/lib/rules/max-nested-callbacks.js +1 -1
  58. package/lib/rules/max-params.js +1 -1
  59. package/lib/rules/max-statements.js +1 -1
  60. package/lib/rules/multiline-comment-style.js +7 -7
  61. package/lib/rules/new-cap.js +1 -1
  62. package/lib/rules/newline-after-var.js +1 -1
  63. package/lib/rules/newline-before-return.js +1 -1
  64. package/lib/rules/no-case-declarations.js +13 -1
  65. package/lib/rules/no-constant-binary-expression.js +7 -8
  66. package/lib/rules/no-constant-condition.js +18 -7
  67. package/lib/rules/no-constructor-return.js +2 -2
  68. package/lib/rules/no-dupe-class-members.js +2 -2
  69. package/lib/rules/no-else-return.js +1 -1
  70. package/lib/rules/no-empty-function.js +2 -2
  71. package/lib/rules/no-empty-static-block.js +1 -1
  72. package/lib/rules/no-extend-native.js +1 -2
  73. package/lib/rules/no-extra-semi.js +1 -1
  74. package/lib/rules/no-fallthrough.js +41 -16
  75. package/lib/rules/no-implicit-coercion.js +66 -24
  76. package/lib/rules/no-inner-declarations.js +23 -2
  77. package/lib/rules/no-invalid-regexp.js +1 -1
  78. package/lib/rules/no-invalid-this.js +1 -1
  79. package/lib/rules/no-lone-blocks.js +3 -3
  80. package/lib/rules/no-loss-of-precision.js +1 -1
  81. package/lib/rules/no-misleading-character-class.js +225 -69
  82. package/lib/rules/no-mixed-spaces-and-tabs.js +1 -1
  83. package/lib/rules/no-multiple-empty-lines.js +1 -1
  84. package/lib/rules/no-new-native-nonconstructor.js +1 -1
  85. package/lib/rules/no-new-symbol.js +8 -1
  86. package/lib/rules/no-restricted-globals.js +1 -1
  87. package/lib/rules/no-restricted-imports.js +186 -40
  88. package/lib/rules/no-restricted-modules.js +2 -2
  89. package/lib/rules/no-return-await.js +1 -1
  90. package/lib/rules/no-sequences.js +1 -0
  91. package/lib/rules/no-this-before-super.js +45 -13
  92. package/lib/rules/no-trailing-spaces.js +2 -3
  93. package/lib/rules/no-unneeded-ternary.js +1 -1
  94. package/lib/rules/no-unsafe-optional-chaining.js +1 -1
  95. package/lib/rules/no-unused-private-class-members.js +1 -1
  96. package/lib/rules/no-unused-vars.js +197 -36
  97. package/lib/rules/no-useless-assignment.js +566 -0
  98. package/lib/rules/no-useless-backreference.js +1 -1
  99. package/lib/rules/no-useless-computed-key.js +2 -2
  100. package/lib/rules/no-useless-return.js +7 -2
  101. package/lib/rules/object-curly-spacing.js +3 -3
  102. package/lib/rules/object-property-newline.js +1 -1
  103. package/lib/rules/one-var.js +5 -5
  104. package/lib/rules/padded-blocks.js +7 -7
  105. package/lib/rules/prefer-arrow-callback.js +3 -3
  106. package/lib/rules/prefer-reflect.js +1 -1
  107. package/lib/rules/prefer-regex-literals.js +1 -1
  108. package/lib/rules/prefer-template.js +1 -1
  109. package/lib/rules/radix.js +2 -2
  110. package/lib/rules/semi-style.js +1 -1
  111. package/lib/rules/sort-imports.js +1 -1
  112. package/lib/rules/sort-keys.js +1 -1
  113. package/lib/rules/sort-vars.js +1 -1
  114. package/lib/rules/space-unary-ops.js +1 -1
  115. package/lib/rules/strict.js +1 -1
  116. package/lib/rules/use-isnan.js +101 -7
  117. package/lib/rules/utils/ast-utils.js +16 -7
  118. package/lib/rules/utils/char-source.js +240 -0
  119. package/lib/rules/utils/lazy-loading-rule-map.js +1 -1
  120. package/lib/rules/utils/unicode/index.js +9 -4
  121. package/lib/rules/yield-star-spacing.js +1 -1
  122. package/lib/shared/runtime-info.js +1 -0
  123. package/lib/shared/serialization.js +55 -0
  124. package/lib/shared/stats.js +30 -0
  125. package/lib/shared/string-utils.js +9 -11
  126. package/lib/shared/types.js +35 -1
  127. package/lib/source-code/index.js +3 -1
  128. package/lib/source-code/source-code.js +299 -85
  129. package/lib/source-code/token-store/backward-token-cursor.js +3 -3
  130. package/lib/source-code/token-store/cursors.js +4 -2
  131. package/lib/source-code/token-store/forward-token-comment-cursor.js +3 -3
  132. package/lib/source-code/token-store/forward-token-cursor.js +3 -3
  133. package/lib/source-code/token-store/index.js +2 -2
  134. package/lib/unsupported-api.js +3 -5
  135. package/messages/no-config-found.js +1 -1
  136. package/messages/plugin-conflict.js +1 -1
  137. package/messages/plugin-invalid.js +1 -1
  138. package/messages/plugin-missing.js +1 -1
  139. package/package.json +32 -29
  140. package/conf/config-schema.js +0 -93
  141. package/lib/cli-engine/formatters/checkstyle.js +0 -60
  142. package/lib/cli-engine/formatters/compact.js +0 -60
  143. package/lib/cli-engine/formatters/jslint-xml.js +0 -41
  144. package/lib/cli-engine/formatters/junit.js +0 -82
  145. package/lib/cli-engine/formatters/tap.js +0 -95
  146. package/lib/cli-engine/formatters/unix.js +0 -58
  147. package/lib/cli-engine/formatters/visualstudio.js +0 -63
  148. package/lib/cli-engine/xml-escape.js +0 -34
  149. package/lib/eslint/flat-eslint.js +0 -1155
  150. package/lib/rule-tester/flat-rule-tester.js +0 -1131
  151. package/lib/rules/require-jsdoc.js +0 -122
  152. package/lib/rules/utils/patterns/letters.js +0 -36
  153. package/lib/rules/valid-jsdoc.js +0 -516
  154. package/lib/shared/config-validator.js +0 -347
  155. package/lib/shared/deprecation-warnings.js +0 -58
  156. package/lib/shared/relative-module-resolver.js +0 -50
@@ -16,6 +16,11 @@
16
16
  //------------------------------------------------------------------------------
17
17
 
18
18
  const escapeRegExp = require("escape-string-regexp");
19
+ const {
20
+ Legacy: {
21
+ ConfigOps
22
+ }
23
+ } = require("@eslint/eslintrc/universal");
19
24
 
20
25
  /**
21
26
  * Compares the locations of two objects in a source file
@@ -33,16 +38,16 @@ function compareLocations(itemA, itemB) {
33
38
  * @param {Iterable<Directive>} directives Unused directives to be removed.
34
39
  * @returns {Directive[][]} Directives grouped by their parent comment.
35
40
  */
36
- function groupByParentComment(directives) {
41
+ function groupByParentDirective(directives) {
37
42
  const groups = new Map();
38
43
 
39
44
  for (const directive of directives) {
40
- const { unprocessedDirective: { parentComment } } = directive;
45
+ const { unprocessedDirective: { parentDirective } } = directive;
41
46
 
42
- if (groups.has(parentComment)) {
43
- groups.get(parentComment).push(directive);
47
+ if (groups.has(parentDirective)) {
48
+ groups.get(parentDirective).push(directive);
44
49
  } else {
45
- groups.set(parentComment, [directive]);
50
+ groups.set(parentDirective, [directive]);
46
51
  }
47
52
  }
48
53
 
@@ -52,19 +57,19 @@ function groupByParentComment(directives) {
52
57
  /**
53
58
  * Creates removal details for a set of directives within the same comment.
54
59
  * @param {Directive[]} directives Unused directives to be removed.
55
- * @param {Token} commentToken The backing Comment token.
60
+ * @param {Token} node The backing Comment token.
56
61
  * @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
57
62
  */
58
- function createIndividualDirectivesRemoval(directives, commentToken) {
63
+ function createIndividualDirectivesRemoval(directives, node) {
59
64
 
60
65
  /*
61
- * `commentToken.value` starts right after `//` or `/*`.
66
+ * `node.value` starts right after `//` or `/*`.
62
67
  * All calculated offsets will be relative to this index.
63
68
  */
64
- const commentValueStart = commentToken.range[0] + "//".length;
69
+ const commentValueStart = node.range[0] + "//".length;
65
70
 
66
71
  // Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
67
- const listStartOffset = /^\s*\S+\s+/u.exec(commentToken.value)[0].length;
72
+ const listStartOffset = /^\s*\S+\s+/u.exec(node.value)[0].length;
68
73
 
69
74
  /*
70
75
  * Get the list text without any surrounding whitespace. In order to preserve the original
@@ -73,7 +78,7 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
73
78
  * // eslint-disable-line rule-one , rule-two , rule-three -- comment
74
79
  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
75
80
  */
76
- const listText = commentToken.value
81
+ const listText = node.value
77
82
  .slice(listStartOffset) // remove directive name and all whitespace before the list
78
83
  .split(/\s-{2,}\s/u)[0] // remove `-- comment`, if it exists
79
84
  .trimEnd(); // remove all whitespace after the list
@@ -154,19 +159,19 @@ function createIndividualDirectivesRemoval(directives, commentToken) {
154
159
  }
155
160
 
156
161
  /**
157
- * Creates a description of deleting an entire unused disable comment.
162
+ * Creates a description of deleting an entire unused disable directive.
158
163
  * @param {Directive[]} directives Unused directives to be removed.
159
- * @param {Token} commentToken The backing Comment token.
160
- * @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output Problem.
164
+ * @param {Token} node The backing Comment token.
165
+ * @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output problem.
161
166
  */
162
- function createCommentRemoval(directives, commentToken) {
163
- const { range } = commentToken;
167
+ function createDirectiveRemoval(directives, node) {
168
+ const { range } = node;
164
169
  const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
165
170
 
166
171
  return {
167
172
  description: ruleIds.length <= 2
168
173
  ? ruleIds.join(" or ")
169
- : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds[ruleIds.length - 1]}`,
174
+ : `${ruleIds.slice(0, ruleIds.length - 1).join(", ")}, or ${ruleIds.at(-1)}`,
170
175
  fix: {
171
176
  range,
172
177
  text: " "
@@ -181,20 +186,20 @@ function createCommentRemoval(directives, commentToken) {
181
186
  * @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
182
187
  */
183
188
  function processUnusedDirectives(allDirectives) {
184
- const directiveGroups = groupByParentComment(allDirectives);
189
+ const directiveGroups = groupByParentDirective(allDirectives);
185
190
 
186
191
  return directiveGroups.flatMap(
187
192
  directives => {
188
- const { parentComment } = directives[0].unprocessedDirective;
189
- const remainingRuleIds = new Set(parentComment.ruleIds);
193
+ const { parentDirective } = directives[0].unprocessedDirective;
194
+ const remainingRuleIds = new Set(parentDirective.ruleIds);
190
195
 
191
196
  for (const directive of directives) {
192
197
  remainingRuleIds.delete(directive.ruleId);
193
198
  }
194
199
 
195
200
  return remainingRuleIds.size
196
- ? createIndividualDirectivesRemoval(directives, parentComment.commentToken)
197
- : [createCommentRemoval(directives, parentComment.commentToken)];
201
+ ? createIndividualDirectivesRemoval(directives, parentDirective.node)
202
+ : [createDirectiveRemoval(directives, parentDirective.node)];
198
203
  }
199
204
  );
200
205
  }
@@ -337,7 +342,7 @@ function applyDirectives(options) {
337
342
  problem.suppressions = problem.suppressions.concat(suppressions);
338
343
  } else {
339
344
  problem.suppressions = suppressions;
340
- usedDisableDirectives.add(disableDirectivesForProblem[disableDirectivesForProblem.length - 1]);
345
+ usedDisableDirectives.add(disableDirectivesForProblem.at(-1));
341
346
  }
342
347
  }
343
348
 
@@ -345,11 +350,11 @@ function applyDirectives(options) {
345
350
  }
346
351
 
347
352
  const unusedDisableDirectivesToReport = options.directives
348
- .filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive));
353
+ .filter(directive => directive.type === "disable" && !usedDisableDirectives.has(directive) && !options.rulesToIgnore.has(directive.ruleId));
349
354
 
350
355
 
351
356
  const unusedEnableDirectivesToReport = new Set(
352
- options.directives.filter(directive => directive.unprocessedDirective.type === "enable")
357
+ options.directives.filter(directive => directive.unprocessedDirective.type === "enable" && !options.rulesToIgnore.has(directive.ruleId))
353
358
  );
354
359
 
355
360
  /*
@@ -367,7 +372,7 @@ function applyDirectives(options) {
367
372
 
368
373
  const unusedDirectives = processed
369
374
  .map(({ description, fix, unprocessedDirective }) => {
370
- const { parentComment, type, line, column } = unprocessedDirective;
375
+ const { parentDirective, type, line, column } = unprocessedDirective;
371
376
 
372
377
  let message;
373
378
 
@@ -383,8 +388,8 @@ function applyDirectives(options) {
383
388
  return {
384
389
  ruleId: null,
385
390
  message,
386
- line: type === "disable-next-line" ? parentComment.commentToken.loc.start.line : line,
387
- column: type === "disable-next-line" ? parentComment.commentToken.loc.start.column + 1 : column,
391
+ line: type === "disable-next-line" ? parentDirective.node.loc.start.line : line,
392
+ column: type === "disable-next-line" ? parentDirective.node.loc.start.column + 1 : column,
388
393
  severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
389
394
  nodeType: null,
390
395
  ...options.disableFixes ? {} : { fix }
@@ -410,11 +415,13 @@ function applyDirectives(options) {
410
415
  * @param {{ruleId: (string|null), line: number, column: number}[]} options.problems
411
416
  * A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
412
417
  * @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
418
+ * @param {Object} options.configuredRules The rules configuration.
419
+ * @param {Function} options.ruleFilter A predicate function to filter which rules should be executed.
413
420
  * @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
414
421
  * @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
415
422
  * An object with a list of reported problems, the suppressed of which contain the suppression information.
416
423
  */
417
- module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirectives = "off" }) => {
424
+ module.exports = ({ directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
418
425
  const blockDirectives = directives
419
426
  .filter(directive => directive.type === "disable" || directive.type === "enable")
420
427
  .map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
@@ -443,17 +450,38 @@ module.exports = ({ directives, disableFixes, problems, reportUnusedDisableDirec
443
450
  }
444
451
  }).sort(compareLocations);
445
452
 
453
+ // This determines a list of rules that are not being run by the given ruleFilter, if present.
454
+ const rulesToIgnore = configuredRules && ruleFilter
455
+ ? new Set(Object.keys(configuredRules).filter(ruleId => {
456
+ const severity = ConfigOps.getRuleSeverity(configuredRules[ruleId]);
457
+
458
+ // Ignore for disabled rules.
459
+ if (severity === 0) {
460
+ return false;
461
+ }
462
+
463
+ return !ruleFilter({ severity, ruleId });
464
+ }))
465
+ : new Set();
466
+
467
+ // If no ruleId is supplied that means this directive is applied to all rules, so we can't determine if it's unused if any rules are filtered out.
468
+ if (rulesToIgnore.size > 0) {
469
+ rulesToIgnore.add(null);
470
+ }
471
+
446
472
  const blockDirectivesResult = applyDirectives({
447
473
  problems,
448
474
  directives: blockDirectives,
449
475
  disableFixes,
450
- reportUnusedDisableDirectives
476
+ reportUnusedDisableDirectives,
477
+ rulesToIgnore
451
478
  });
452
479
  const lineDirectivesResult = applyDirectives({
453
480
  problems: blockDirectivesResult.problems,
454
481
  directives: lineDirectives,
455
482
  disableFixes,
456
- reportUnusedDisableDirectives
483
+ reportUnusedDisableDirectives,
484
+ rulesToIgnore
457
485
  });
458
486
 
459
487
  return reportUnusedDisableDirectives !== "off"
@@ -222,7 +222,6 @@ function forwardCurrentToHead(analyzer, node) {
222
222
  : "onUnreachableCodePathSegmentStart";
223
223
 
224
224
  debug.dump(`${eventName} ${headSegment.id}`);
225
-
226
225
  CodePathSegment.markUsed(headSegment);
227
226
  analyzer.emitter.emit(
228
227
  eventName,
@@ -125,20 +125,6 @@ class CodePath {
125
125
  return this.internal.thrownForkContext;
126
126
  }
127
127
 
128
- /**
129
- * Tracks the traversal of the code path through each segment. This array
130
- * starts empty and segments are added or removed as the code path is
131
- * traversed. This array always ends up empty at the end of a code path
132
- * traversal. The `CodePathState` uses this to track its progress through
133
- * the code path.
134
- * This is a passthrough to the underlying `CodePathState`.
135
- * @type {CodePathSegment[]}
136
- * @deprecated
137
- */
138
- get currentSegments() {
139
- return this.internal.currentSegments;
140
- }
141
-
142
128
  /**
143
129
  * Traverses all segments in this code path.
144
130
  *
@@ -180,9 +166,9 @@ class CodePath {
180
166
  const lastSegment = resolvedOptions.last;
181
167
 
182
168
  // set up initial location information
183
- let record = null;
184
- let index = 0;
185
- let end = 0;
169
+ let record;
170
+ let index;
171
+ let end;
186
172
  let segment = null;
187
173
 
188
174
  // segments that have already been visited during traversal
@@ -191,8 +177,8 @@ class CodePath {
191
177
  // tracks the traversal steps
192
178
  const stack = [[startSegment, 0]];
193
179
 
194
- // tracks the last skipped segment during traversal
195
- let skippedSegment = null;
180
+ // segments that have been skipped during traversal
181
+ const skipped = new Set();
196
182
 
197
183
  // indicates if we exited early from the traversal
198
184
  let broken = false;
@@ -207,11 +193,7 @@ class CodePath {
207
193
  * @returns {void}
208
194
  */
209
195
  skip() {
210
- if (stack.length <= 1) {
211
- broken = true;
212
- } else {
213
- skippedSegment = stack[stack.length - 2][0];
214
- }
196
+ skipped.add(segment);
215
197
  },
216
198
 
217
199
  /**
@@ -236,6 +218,18 @@ class CodePath {
236
218
  );
237
219
  }
238
220
 
221
+ /**
222
+ * Checks if a given previous segment has been skipped.
223
+ * @param {CodePathSegment} prevSegment A previous segment to check.
224
+ * @returns {boolean} `true` if the segment has been skipped.
225
+ */
226
+ function isSkipped(prevSegment) {
227
+ return (
228
+ skipped.has(prevSegment) ||
229
+ segment.isLoopedPrevSegment(prevSegment)
230
+ );
231
+ }
232
+
239
233
  // the traversal
240
234
  while (stack.length > 0) {
241
235
 
@@ -251,7 +245,7 @@ class CodePath {
251
245
  * Otherwise, we just read the value and sometimes modify the
252
246
  * record as we traverse.
253
247
  */
254
- record = stack[stack.length - 1];
248
+ record = stack.at(-1);
255
249
  segment = record[0];
256
250
  index = record[1];
257
251
 
@@ -272,17 +266,21 @@ class CodePath {
272
266
  continue;
273
267
  }
274
268
 
275
- // Reset the skipping flag if all branches have been skipped.
276
- if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
277
- skippedSegment = null;
278
- }
279
269
  visited.add(segment);
280
270
 
271
+
272
+ // Skips the segment if all previous segments have been skipped.
273
+ const shouldSkip = (
274
+ skipped.size > 0 &&
275
+ segment.prevSegments.length > 0 &&
276
+ segment.prevSegments.every(isSkipped)
277
+ );
278
+
281
279
  /*
282
280
  * If the most recent segment hasn't been skipped, then we call
283
281
  * the callback, passing in the segment and the controller.
284
282
  */
285
- if (!skippedSegment) {
283
+ if (!shouldSkip) {
286
284
  resolvedCallback.call(this, segment, controller);
287
285
 
288
286
  // exit if we're at the last segment
@@ -298,6 +296,10 @@ class CodePath {
298
296
  if (broken) {
299
297
  break;
300
298
  }
299
+ } else {
300
+
301
+ // If the most recent segment has been skipped, then mark it as skipped.
302
+ skipped.add(segment);
301
303
  }
302
304
  }
303
305
 
@@ -207,7 +207,7 @@ class ForkContext {
207
207
  get head() {
208
208
  const list = this.segmentsList;
209
209
 
210
- return list.length === 0 ? [] : list[list.length - 1];
210
+ return list.length === 0 ? [] : list.at(-1);
211
211
  }
212
212
 
213
213
  /**
@@ -40,7 +40,7 @@ module.exports = class ConfigCommentParser {
40
40
 
41
41
  /**
42
42
  * Parses a list of "name:string_value" or/and "name" options divided by comma or
43
- * whitespace. Used for "global" and "exported" comments.
43
+ * whitespace. Used for "global" comments.
44
44
  * @param {string} string The string to parse.
45
45
  * @param {Comment} comment The comment node which has the string.
46
46
  * @returns {Object} Result map object of names and string values, or null values if no value was provided
@@ -75,11 +75,9 @@ module.exports = class ConfigCommentParser {
75
75
  parseJsonConfig(string, location) {
76
76
  debug("Parsing JSON config");
77
77
 
78
- let items = {};
79
-
80
78
  // Parses a JSON-like comment by the same way as parsing CLI option.
81
79
  try {
82
- items = levn.parse("Object", string) || {};
80
+ const items = levn.parse("Object", string) || {};
83
81
 
84
82
  // Some tests say that it should ignore invalid comments such as `/*eslint no-alert:abc*/`.
85
83
  // Also, commaless notations have invalid severity:
@@ -102,11 +100,15 @@ module.exports = class ConfigCommentParser {
102
100
  * Optionator cannot parse commaless notations.
103
101
  * But we are supporting that. So this is a fallback for that.
104
102
  */
105
- items = {};
106
103
  const normalizedString = string.replace(/([-a-zA-Z0-9/]+):/gu, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/u, "$1,");
107
104
 
108
105
  try {
109
- items = JSON.parse(`{${normalizedString}}`);
106
+ const items = JSON.parse(`{${normalizedString}}`);
107
+
108
+ return {
109
+ success: true,
110
+ config: items
111
+ };
110
112
  } catch (ex) {
111
113
  debug("Manual parsing failed.");
112
114
 
@@ -124,11 +126,6 @@ module.exports = class ConfigCommentParser {
124
126
  };
125
127
 
126
128
  }
127
-
128
- return {
129
- success: true,
130
- config: items
131
- };
132
129
  }
133
130
 
134
131
  /**
@@ -1,13 +1,11 @@
1
1
  "use strict";
2
2
 
3
3
  const { Linter } = require("./linter");
4
- const interpolate = require("./interpolate");
5
4
  const SourceCodeFixer = require("./source-code-fixer");
6
5
 
7
6
  module.exports = {
8
7
  Linter,
9
8
 
10
9
  // For testers.
11
- SourceCodeFixer,
12
- interpolate
10
+ SourceCodeFixer
13
11
  };
@@ -9,13 +9,30 @@
9
9
  // Public Interface
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- module.exports = (text, data) => {
12
+ /**
13
+ * Returns a global expression matching placeholders in messages.
14
+ * @returns {RegExp} Global regular expression matching placeholders
15
+ */
16
+ function getPlaceholderMatcher() {
17
+ return /\{\{([^{}]+?)\}\}/gu;
18
+ }
19
+
20
+ /**
21
+ * Replaces {{ placeholders }} in the message with the provided data.
22
+ * Does not replace placeholders not available in the data.
23
+ * @param {string} text Original message with potential placeholders
24
+ * @param {Record<string, string>} data Map of placeholder name to its value
25
+ * @returns {string} Message with replaced placeholders
26
+ */
27
+ function interpolate(text, data) {
13
28
  if (!data) {
14
29
  return text;
15
30
  }
16
31
 
32
+ const matcher = getPlaceholderMatcher();
33
+
17
34
  // Substitution content for any {{ }} markers.
18
- return text.replace(/\{\{([^{}]+?)\}\}/gu, (fullMatch, termWithWhitespace) => {
35
+ return text.replace(matcher, (fullMatch, termWithWhitespace) => {
19
36
  const term = termWithWhitespace.trim();
20
37
 
21
38
  if (term in data) {
@@ -25,4 +42,9 @@ module.exports = (text, data) => {
25
42
  // Preserve old behavior: If parameter name not provided, don't replace it.
26
43
  return fullMatch;
27
44
  });
45
+ }
46
+
47
+ module.exports = {
48
+ getPlaceholderMatcher,
49
+ interpolate
28
50
  };