eslint 9.6.0 → 9.8.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
@@ -39,7 +39,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
39
39
  10. [License](#license)
40
40
  11. [Team](#team)
41
41
  12. [Sponsors](#sponsors)
42
- 13. [Technology Sponsors](#technology-sponsors)
42
+ 13. [Technology Sponsors](#technology-sponsors) <!-- markdownlint-disable-line MD051 -->
43
43
 
44
44
  ## Installation and Usage
45
45
 
@@ -294,14 +294,13 @@ The following companies, organizations, and individuals support ESLint's ongoing
294
294
  <!-- NOTE: This section is autogenerated. Do not manually edit.-->
295
295
  <!--sponsorsstart-->
296
296
  <h3>Platinum Sponsors</h3>
297
- <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="undefined"></a></p><h3>Gold Sponsors</h3>
297
+ <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
298
298
  <p><a href="#"><img src="https://images.opencollective.com/guest-bf377e88/avatar.png" alt="Eli Schleifer" height="96"></a> <a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a></p><h3>Silver Sponsors</h3>
299
299
  <p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" 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> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
300
300
  <p><a href="https://www.notion.so"><img src="https://images.opencollective.com/notion/bf3b117/logo.png" alt="notion" 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://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" 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://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
301
301
  <!--sponsorsend-->
302
302
 
303
- ## Technology Sponsors
304
-
305
- * Site search ([eslint.org](https://eslint.org)) is sponsored by [Algolia](https://www.algolia.com)
306
- * Hosting for ([eslint.org](https://eslint.org)) is sponsored by [Netlify](https://www.netlify.com)
307
- * Password management is sponsored by [1Password](https://www.1password.com)
303
+ <!--techsponsorsstart-->
304
+ <h2>Technology Sponsors</h2>
305
+ <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>
306
+ <!--techsponsorsend-->
package/lib/cli.js CHANGED
@@ -26,7 +26,6 @@ const fs = require("node:fs"),
26
26
  { normalizeSeverityToString } = require("./shared/severity");
27
27
  const { Legacy: { naming } } = require("@eslint/eslintrc");
28
28
  const { ModuleImporter } = require("@humanwhocodes/module-importer");
29
- const { inactiveFlags, activeFlags } = require("./shared/flags");
30
29
  const debug = require("debug")("eslint:cli");
31
30
 
32
31
  //------------------------------------------------------------------------------
@@ -488,25 +487,6 @@ const cli = {
488
487
 
489
488
  const ActiveESLint = usingFlatConfig ? ESLint : LegacyESLint;
490
489
  const eslintOptions = await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc");
491
-
492
- if (eslintOptions.flags) {
493
- debug("Checking for inactive flags");
494
-
495
- for (const flag of eslintOptions.flags) {
496
- if (inactiveFlags.has(flag)) {
497
- log.warn(`InactiveFlag: The '${flag}' flag is no longer active: ${inactiveFlags.get(flag)}`);
498
- continue;
499
- }
500
-
501
- if (activeFlags.has(flag)) {
502
- continue;
503
- }
504
-
505
- log.error(`InvalidFlag: The '${flag}' flag is invalid.`);
506
- return 2;
507
- }
508
- }
509
-
510
490
  const engine = new ActiveESLint(eslintOptions);
511
491
  let results;
512
492
 
@@ -20,7 +20,9 @@ const { validateLanguageOptions } = require("./validate-language-options");
20
20
  // Type Definitions
21
21
  //-----------------------------------------------------------------------------
22
22
 
23
- /** @typedef {import("../../linter/vfile").VFile} VFile */
23
+ /** @typedef {import("@eslint/core").File} File */
24
+ /** @typedef {import("@eslint/core").Language} Language */
25
+ /** @typedef {import("@eslint/core").OkParseResult} OkParseResult */
24
26
 
25
27
  //-----------------------------------------------------------------------------
26
28
  // Helpers
@@ -56,6 +58,9 @@ function analyzeScope(ast, languageOptions, visitorKeys) {
56
58
  // Exports
57
59
  //-----------------------------------------------------------------------------
58
60
 
61
+ /**
62
+ * @type {Language}
63
+ */
59
64
  module.exports = {
60
65
 
61
66
  fileType: "text",
@@ -143,7 +148,7 @@ module.exports = {
143
148
 
144
149
  /**
145
150
  * Parses the given file into an AST.
146
- * @param {VFile} file The virtual file to parse.
151
+ * @param {File} file The virtual file to parse.
147
152
  * @param {Object} options Additional options passed from ESLint.
148
153
  * @param {LanguageOptions} options.languageOptions The language options.
149
154
  * @returns {Object} The result of parsing.
@@ -200,7 +205,7 @@ module.exports = {
200
205
  } catch (ex) {
201
206
 
202
207
  // If the message includes a leading line number, strip it:
203
- const message = `Parsing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
208
+ const message = ex.message.replace(/^line \d+:/iu, "").trim();
204
209
 
205
210
  debug("%s\n%s", message, ex.stack);
206
211
 
@@ -218,8 +223,8 @@ module.exports = {
218
223
 
219
224
  /**
220
225
  * Creates a new `SourceCode` object from the given information.
221
- * @param {VFile} file The virtual file to create a `SourceCode` object from.
222
- * @param {Object} parseResult The result returned from `parse()`.
226
+ * @param {File} file The virtual file to create a `SourceCode` object from.
227
+ * @param {OkParseResult} parseResult The result returned from `parse()`.
223
228
  * @param {Object} options Additional options passed from ESLint.
224
229
  * @param {LanguageOptions} options.languageOptions The language options.
225
230
  * @returns {SourceCode} The new `SourceCode` object.
@@ -29,6 +29,10 @@ const
29
29
  //------------------------------------------------------------------------------
30
30
 
31
31
  /** @typedef {import("eslint-scope").Variable} Variable */
32
+ /** @typedef {import("eslint-scope").Scope} Scope */
33
+ /** @typedef {import("@eslint/core").SourceCode} ISourceCode */
34
+ /** @typedef {import("@eslint/core").Directive} IDirective */
35
+ /** @typedef {import("@eslint/core").TraversalStep} ITraversalStep */
32
36
 
33
37
  //------------------------------------------------------------------------------
34
38
  // Private
@@ -373,6 +377,7 @@ class TraversalStep {
373
377
 
374
378
  /**
375
379
  * A class to represent a directive comment.
380
+ * @implements {IDirective}
376
381
  */
377
382
  class Directive {
378
383
 
@@ -429,12 +434,13 @@ const caches = Symbol("caches");
429
434
 
430
435
  /**
431
436
  * Represents parsed source code.
437
+ * @implements {ISourceCode}
432
438
  */
433
439
  class SourceCode extends TokenStore {
434
440
 
435
441
  /**
436
442
  * The cache of steps that were taken while traversing the source code.
437
- * @type {Array<TraversalStep>}
443
+ * @type {Array<ITraversalStep>}
438
444
  */
439
445
  #steps;
440
446
 
@@ -838,7 +844,7 @@ class SourceCode extends TokenStore {
838
844
  /**
839
845
  * Gets the scope for the given node
840
846
  * @param {ASTNode} currentNode The node to get the scope of
841
- * @returns {eslint-scope.Scope} The scope information for this node
847
+ * @returns {Scope} The scope information for this node
842
848
  * @throws {TypeError} If the `currentNode` argument is missing.
843
849
  */
844
850
  getScope(currentNode) {
@@ -910,6 +916,25 @@ class SourceCode extends TokenStore {
910
916
 
911
917
  return ancestorsStartingAtParent.reverse();
912
918
  }
919
+
920
+ /**
921
+ * Returns the locatin of the given node or token.
922
+ * @param {ASTNode|Token} nodeOrToken The node or token to get the location of.
923
+ * @returns {SourceLocation} The location of the node or token.
924
+ */
925
+ getLoc(nodeOrToken) {
926
+ return nodeOrToken.loc;
927
+ }
928
+
929
+ /**
930
+ * Returns the range of the given node or token.
931
+ * @param {ASTNode|Token} nodeOrToken The node or token to get the range of.
932
+ * @returns {[number, number]} The range of the node or token.
933
+ */
934
+ getRange(nodeOrToken) {
935
+ return nodeOrToken.range;
936
+ }
937
+
913
938
  /* eslint-enable class-methods-use-this -- node is owned by SourceCode */
914
939
 
915
940
  /**
@@ -10,6 +10,9 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  /** @typedef {import("../shared/types").LintMessage} LintMessage */
13
+ /** @typedef {import("@eslint/core").Language} Language */
14
+ /** @typedef {import("@eslint/core").Position} Position */
15
+ /** @typedef {import("@eslint/core").RulesConfig} RulesConfig */
13
16
 
14
17
  //------------------------------------------------------------------------------
15
18
  // Module Definition
@@ -24,8 +27,8 @@ const {
24
27
 
25
28
  /**
26
29
  * Compares the locations of two objects in a source file
27
- * @param {{line: number, column: number}} itemA The first object
28
- * @param {{line: number, column: number}} itemB The second object
30
+ * @param {Position} itemA The first object
31
+ * @param {Position} itemB The second object
29
32
  * @returns {number} A value less than 1 if itemA appears before itemB in the source file, greater than 1 if
30
33
  * itemA appears after itemB in the source file, or 0 if itemA and itemB have the same location.
31
34
  */
@@ -58,15 +61,18 @@ function groupByParentDirective(directives) {
58
61
  * Creates removal details for a set of directives within the same comment.
59
62
  * @param {Directive[]} directives Unused directives to be removed.
60
63
  * @param {Token} node The backing Comment token.
64
+ * @param {SourceCode} sourceCode The source code object for the file being linted.
61
65
  * @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
62
66
  */
63
- function createIndividualDirectivesRemoval(directives, node) {
67
+ function createIndividualDirectivesRemoval(directives, node, sourceCode) {
68
+
69
+ const range = sourceCode.getRange(node);
64
70
 
65
71
  /*
66
72
  * `node.value` starts right after `//` or `/*`.
67
73
  * All calculated offsets will be relative to this index.
68
74
  */
69
- const commentValueStart = node.range[0] + "//".length;
75
+ const commentValueStart = range[0] + "//".length;
70
76
 
71
77
  // Find where the list of rules starts. `\S+` matches with the directive name (e.g. `eslint-disable-line`)
72
78
  const listStartOffset = /^\s*\S+\s+/u.exec(node.value)[0].length;
@@ -162,10 +168,11 @@ function createIndividualDirectivesRemoval(directives, node) {
162
168
  * Creates a description of deleting an entire unused disable directive.
163
169
  * @param {Directive[]} directives Unused directives to be removed.
164
170
  * @param {Token} node The backing Comment token.
171
+ * @param {SourceCode} sourceCode The source code object for the file being linted.
165
172
  * @returns {{ description, fix, unprocessedDirective }} Details for later creation of an output problem.
166
173
  */
167
- function createDirectiveRemoval(directives, node) {
168
- const { range } = node;
174
+ function createDirectiveRemoval(directives, node, sourceCode) {
175
+ const range = sourceCode.getRange(node);
169
176
  const ruleIds = directives.filter(directive => directive.ruleId).map(directive => `'${directive.ruleId}'`);
170
177
 
171
178
  return {
@@ -183,9 +190,10 @@ function createDirectiveRemoval(directives, node) {
183
190
  /**
184
191
  * Parses details from directives to create output Problems.
185
192
  * @param {Iterable<Directive>} allDirectives Unused directives to be removed.
193
+ * @param {SourceCode} sourceCode The source code object for the file being linted.
186
194
  * @returns {{ description, fix, unprocessedDirective }[]} Details for later creation of output Problems.
187
195
  */
188
- function processUnusedDirectives(allDirectives) {
196
+ function processUnusedDirectives(allDirectives, sourceCode) {
189
197
  const directiveGroups = groupByParentDirective(allDirectives);
190
198
 
191
199
  return directiveGroups.flatMap(
@@ -198,8 +206,8 @@ function processUnusedDirectives(allDirectives) {
198
206
  }
199
207
 
200
208
  return remainingRuleIds.size
201
- ? createIndividualDirectivesRemoval(directives, parentDirective.node)
202
- : [createDirectiveRemoval(directives, parentDirective.node)];
209
+ ? createIndividualDirectivesRemoval(directives, parentDirective.node, sourceCode)
210
+ : [createDirectiveRemoval(directives, parentDirective.node, sourceCode)];
203
211
  }
204
212
  );
205
213
  }
@@ -306,6 +314,7 @@ function collectUsedEnableDirectives(directives) {
306
314
  function applyDirectives(options) {
307
315
  const problems = [];
308
316
  const usedDisableDirectives = new Set();
317
+ const { sourceCode } = options;
309
318
 
310
319
  for (const problem of options.problems) {
311
320
  let disableDirectivesForProblem = [];
@@ -367,8 +376,8 @@ function applyDirectives(options) {
367
376
  }
368
377
  }
369
378
 
370
- const processed = processUnusedDirectives(unusedDisableDirectivesToReport)
371
- .concat(processUnusedDirectives(unusedEnableDirectivesToReport));
379
+ const processed = processUnusedDirectives(unusedDisableDirectivesToReport, sourceCode)
380
+ .concat(processUnusedDirectives(unusedEnableDirectivesToReport, sourceCode));
372
381
  const columnOffset = options.language.columnStart === 1 ? 0 : 1;
373
382
  const lineOffset = options.language.lineStart === 1 ? 0 : 1;
374
383
 
@@ -387,11 +396,14 @@ function applyDirectives(options) {
387
396
  ? `Unused eslint-disable directive (no problems were reported from ${description}).`
388
397
  : "Unused eslint-disable directive (no problems were reported).";
389
398
  }
399
+
400
+ const loc = sourceCode.getLoc(parentDirective.node);
401
+
390
402
  return {
391
403
  ruleId: null,
392
404
  message,
393
- line: type === "disable-next-line" ? parentDirective.node.loc.start.line + lineOffset : line,
394
- column: type === "disable-next-line" ? parentDirective.node.loc.start.column + columnOffset : column,
405
+ line: type === "disable-next-line" ? loc.start.line + lineOffset : line,
406
+ column: type === "disable-next-line" ? loc.start.column + columnOffset : column,
395
407
  severity: options.reportUnusedDisableDirectives === "warn" ? 1 : 2,
396
408
  nodeType: null,
397
409
  ...options.disableFixes ? {} : { fix }
@@ -406,6 +418,7 @@ function applyDirectives(options) {
406
418
  * of reported problems, adds the suppression information to the problems.
407
419
  * @param {Object} options Information about directives and problems
408
420
  * @param {Language} options.language The language being linted.
421
+ * @param {SourceCode} options.sourceCode The source code object for the file being linted.
409
422
  * @param {{
410
423
  * type: ("disable"|"enable"|"disable-line"|"disable-next-line"),
411
424
  * ruleId: (string|null),
@@ -418,13 +431,13 @@ function applyDirectives(options) {
418
431
  * @param {{ruleId: (string|null), line: number, column: number}[]} options.problems
419
432
  * A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
420
433
  * @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
421
- * @param {Object} options.configuredRules The rules configuration.
434
+ * @param {RulesConfig} options.configuredRules The rules configuration.
422
435
  * @param {Function} options.ruleFilter A predicate function to filter which rules should be executed.
423
436
  * @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
424
437
  * @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
425
438
  * An object with a list of reported problems, the suppressed of which contain the suppression information.
426
439
  */
427
- module.exports = ({ language, directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
440
+ module.exports = ({ language, sourceCode, directives, disableFixes, problems, configuredRules, ruleFilter, reportUnusedDisableDirectives = "off" }) => {
428
441
  const blockDirectives = directives
429
442
  .filter(directive => directive.type === "disable" || directive.type === "enable")
430
443
  .map(directive => Object.assign({}, directive, { unprocessedDirective: directive }))
@@ -474,6 +487,7 @@ module.exports = ({ language, directives, disableFixes, problems, configuredRule
474
487
 
475
488
  const blockDirectivesResult = applyDirectives({
476
489
  language,
490
+ sourceCode,
477
491
  problems,
478
492
  directives: blockDirectives,
479
493
  disableFixes,
@@ -482,6 +496,7 @@ module.exports = ({ language, directives, disableFixes, problems, configuredRule
482
496
  });
483
497
  const lineDirectivesResult = applyDirectives({
484
498
  language,
499
+ sourceCode,
485
500
  problems: blockDirectivesResult.problems,
486
501
  directives: lineDirectives,
487
502
  disableFixes,
@@ -45,7 +45,7 @@ const { RuleValidator } = require("../config/rule-validator");
45
45
  const { assertIsRuleSeverity } = require("../config/flat-config-schema");
46
46
  const { normalizeSeverityToString } = require("../shared/severity");
47
47
  const jslang = require("../languages/js");
48
- const { activeFlags } = require("../shared/flags");
48
+ const { activeFlags, inactiveFlags } = require("../shared/flags");
49
49
  const debug = require("debug")("eslint:linter");
50
50
  const MAX_AUTOFIX_PASSES = 10;
51
51
  const DEFAULT_PARSER_NAME = "espree";
@@ -72,6 +72,10 @@ const STEP_KIND_CALL = 2;
72
72
  /** @typedef {import("../shared/types").Processor} Processor */
73
73
  /** @typedef {import("../shared/types").Rule} Rule */
74
74
  /** @typedef {import("../shared/types").Times} Times */
75
+ /** @typedef {import("@eslint/core").Language} Language */
76
+ /** @typedef {import("@eslint/core").RuleSeverity} RuleSeverity */
77
+ /** @typedef {import("@eslint/core").RuleConfig} RuleConfig */
78
+
75
79
 
76
80
  /* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
77
81
  /**
@@ -276,7 +280,7 @@ function updateLocationInformation({ line, column, endLine, endColumn }, languag
276
280
  * @param {string} [options.ruleId] the ruleId to report
277
281
  * @param {Object} [options.loc] the loc to report
278
282
  * @param {string} [options.message] the error message to report
279
- * @param {string} [options.severity] the error message to report
283
+ * @param {RuleSeverity} [options.severity] the error message to report
280
284
  * @param {Language} [options.language] the language to use to adjust the location information
281
285
  * @returns {LintMessage} created problem, returns a missing-rule problem if only provided ruleId.
282
286
  * @private
@@ -319,9 +323,10 @@ function createLintingProblem(options) {
319
323
  * @param {ASTNode|token} options.node The Comment node/token.
320
324
  * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
321
325
  * @param {Language} language The language to use to adjust the location information.
326
+ * @param {SourceCode} sourceCode The SourceCode object to get comments from.
322
327
  * @returns {Object} Directives and problems from the comment
323
328
  */
324
- function createDisableDirectives({ type, value, justification, node }, ruleMapper, language) {
329
+ function createDisableDirectives({ type, value, justification, node }, ruleMapper, language, sourceCode) {
325
330
  const ruleIds = Object.keys(commentParser.parseListConfig(value));
326
331
  const directiveRules = ruleIds.length ? ruleIds : [null];
327
332
  const result = {
@@ -332,11 +337,15 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
332
337
 
333
338
  for (const ruleId of directiveRules) {
334
339
 
340
+ const loc = sourceCode.getLoc(node);
341
+
335
342
  // push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
336
343
  if (ruleId === null || !!ruleMapper(ruleId)) {
344
+
345
+
337
346
  if (type === "disable-next-line") {
338
347
  const { line, column } = updateLocationInformation(
339
- node.loc.end,
348
+ loc.end,
340
349
  language
341
350
  );
342
351
 
@@ -350,7 +359,7 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
350
359
  });
351
360
  } else {
352
361
  const { line, column } = updateLocationInformation(
353
- node.loc.start,
362
+ loc.start,
354
363
  language
355
364
  );
356
365
 
@@ -364,7 +373,7 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
364
373
  });
365
374
  }
366
375
  } else {
367
- result.directiveProblems.push(createLintingProblem({ ruleId, loc: node.loc, language }));
376
+ result.directiveProblems.push(createLintingProblem({ ruleId, loc, language }));
368
377
  }
369
378
  }
370
379
  return result;
@@ -406,25 +415,27 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
406
415
  return;
407
416
  }
408
417
 
418
+ const loc = sourceCode.getLoc(comment);
419
+
409
420
  if (warnInlineConfig) {
410
421
  const kind = comment.type === "Block" ? `/*${directiveText}*/` : `//${directiveText}`;
411
422
 
412
423
  problems.push(createLintingProblem({
413
424
  ruleId: null,
414
425
  message: `'${kind}' has no effect because you have 'noInlineConfig' setting in ${warnInlineConfig}.`,
415
- loc: comment.loc,
426
+ loc,
416
427
  severity: 1
417
428
  }));
418
429
  return;
419
430
  }
420
431
 
421
- if (directiveText === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
432
+ if (directiveText === "eslint-disable-line" && loc.start.line !== loc.end.line) {
422
433
  const message = `${directiveText} comment should not span multiple lines.`;
423
434
 
424
435
  problems.push(createLintingProblem({
425
436
  ruleId: null,
426
437
  message,
427
- loc: comment.loc
438
+ loc
428
439
  }));
429
440
  return;
430
441
  }
@@ -442,7 +453,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
442
453
  value: directiveValue,
443
454
  justification: justificationPart,
444
455
  node: comment
445
- }, ruleMapper, jslang);
456
+ }, ruleMapper, jslang, sourceCode);
446
457
 
447
458
  disableDirectives.push(...directives);
448
459
  problems.push(...directiveProblems);
@@ -463,7 +474,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
463
474
  } catch (err) {
464
475
  problems.push(createLintingProblem({
465
476
  ruleId: null,
466
- loc: comment.loc,
477
+ loc,
467
478
  message: err.message
468
479
  }));
469
480
  continue;
@@ -490,14 +501,14 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
490
501
  const ruleValue = parseResult.config[name];
491
502
 
492
503
  if (!rule) {
493
- problems.push(createLintingProblem({ ruleId: name, loc: comment.loc }));
504
+ problems.push(createLintingProblem({ ruleId: name, loc }));
494
505
  return;
495
506
  }
496
507
 
497
508
  if (Object.hasOwn(configuredRules, name)) {
498
509
  problems.push(createLintingProblem({
499
510
  message: `Rule "${name}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
500
- loc: comment.loc
511
+ loc
501
512
  }));
502
513
  return;
503
514
  }
@@ -559,7 +570,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
559
570
  problems.push(createLintingProblem({
560
571
  ruleId: name,
561
572
  message: err.message,
562
- loc: comment.loc
573
+ loc
563
574
  }));
564
575
 
565
576
  // do not apply the config, if found invalid options.
@@ -571,7 +582,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
571
582
  } else {
572
583
  const problem = createLintingProblem({
573
584
  ruleId: null,
574
- loc: comment.loc,
585
+ loc,
575
586
  message: parseResult.error.message
576
587
  });
577
588
 
@@ -619,7 +630,7 @@ function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper, language) {
619
630
  })));
620
631
 
621
632
  directivesSources.forEach(directive => {
622
- const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper, language);
633
+ const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper, language, sourceCode);
623
634
 
624
635
  disableDirectives.push(...directives);
625
636
  problems.push(...directiveProblems);
@@ -877,7 +888,7 @@ function storeTime(time, timeOpts, slots) {
877
888
 
878
889
  /**
879
890
  * Get the options for a rule (not including severity), if any
880
- * @param {Array|number} ruleConfig rule configuration
891
+ * @param {RuleConfig} ruleConfig rule configuration
881
892
  * @returns {Array} of rule options, empty Array if none
882
893
  */
883
894
  function getRuleOptions(ruleConfig) {
@@ -941,7 +952,7 @@ function parse(file, language, languageOptions) {
941
952
  nodeType: null,
942
953
  fatal: true,
943
954
  severity: 2,
944
- message: error.message,
955
+ message: `Parsing error: ${error.message}`,
945
956
  line: error.line,
946
957
  column: error.column
947
958
  }))
@@ -1278,9 +1289,20 @@ class Linter {
1278
1289
  * @param {"flat"|"eslintrc"} [config.configType="flat"] the type of config used.
1279
1290
  */
1280
1291
  constructor({ cwd, configType = "flat", flags = [] } = {}) {
1292
+
1293
+ flags.forEach(flag => {
1294
+ if (inactiveFlags.has(flag)) {
1295
+ throw new Error(`The flag '${flag}' is inactive: ${inactiveFlags.get(flag)}`);
1296
+ }
1297
+
1298
+ if (!activeFlags.has(flag)) {
1299
+ throw new Error(`Unknown flag '${flag}'.`);
1300
+ }
1301
+ });
1302
+
1281
1303
  internalSlotsMap.set(this, {
1282
1304
  cwd: normalizeCwd(cwd),
1283
- flags: flags.filter(flag => activeFlags.has(flag)),
1305
+ flags,
1284
1306
  lastConfigArray: null,
1285
1307
  lastSourceCode: null,
1286
1308
  lastSuppressedMessages: [],
@@ -1458,7 +1480,7 @@ class Linter {
1458
1480
  debug("An error occurred while traversing");
1459
1481
  debug("Filename:", options.filename);
1460
1482
  if (err.currentNode) {
1461
- const { line } = err.currentNode.loc.start;
1483
+ const { line } = sourceCode.getLoc(err.currentNode).start;
1462
1484
 
1463
1485
  debug("Line:", line);
1464
1486
  err.message += `:${line}`;
@@ -1476,6 +1498,7 @@ class Linter {
1476
1498
 
1477
1499
  return applyDisableDirectives({
1478
1500
  language: jslang,
1501
+ sourceCode,
1479
1502
  directives: commentDirectives.disableDirectives,
1480
1503
  disableFixes: options.disableFixes,
1481
1504
  problems: lintingProblems
@@ -1755,10 +1778,14 @@ class Linter {
1755
1778
  if (options.warnInlineConfig) {
1756
1779
  if (sourceCode.getInlineConfigNodes) {
1757
1780
  sourceCode.getInlineConfigNodes().forEach(node => {
1781
+
1782
+ const loc = sourceCode.getLoc(node);
1783
+ const range = sourceCode.getRange(node);
1784
+
1758
1785
  inlineConfigProblems.push(createLintingProblem({
1759
1786
  ruleId: null,
1760
- message: `'${sourceCode.text.slice(node.range[0], node.range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
1761
- loc: node.loc,
1787
+ message: `'${sourceCode.text.slice(range[0], range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
1788
+ loc,
1762
1789
  severity: 1,
1763
1790
  language: config.language
1764
1791
  }));
@@ -1938,7 +1965,7 @@ class Linter {
1938
1965
  debug("An error occurred while traversing");
1939
1966
  debug("Filename:", options.filename);
1940
1967
  if (err.currentNode) {
1941
- const { line } = err.currentNode.loc.start;
1968
+ const { line } = sourceCode.getLoc(err.currentNode).start;
1942
1969
 
1943
1970
  debug("Line:", line);
1944
1971
  err.message += `:${line}`;
@@ -1957,6 +1984,7 @@ class Linter {
1957
1984
 
1958
1985
  return applyDisableDirectives({
1959
1986
  language: config.language,
1987
+ sourceCode,
1960
1988
  directives: commentDirectives.disableDirectives,
1961
1989
  disableFixes: options.disableFixes,
1962
1990
  problems: lintingProblems
@@ -334,10 +334,8 @@ class NodeEventGenerator {
334
334
  * @returns {void}
335
335
  */
336
336
  enterNode(node) {
337
- if (node.parent) {
338
- this.currentAncestry.unshift(node.parent);
339
- }
340
337
  this.applySelectors(node, false);
338
+ this.currentAncestry.unshift(node);
341
339
  }
342
340
 
343
341
  /**
@@ -346,8 +344,8 @@ class NodeEventGenerator {
346
344
  * @returns {void}
347
345
  */
348
346
  leaveNode(node) {
349
- this.applySelectors(node, true);
350
347
  this.currentAncestry.shift();
348
+ this.applySelectors(node, true);
351
349
  }
352
350
  }
353
351
 
@@ -10,7 +10,7 @@
10
10
  //------------------------------------------------------------------------------
11
11
 
12
12
  const assert = require("node:assert");
13
- const ruleFixer = require("./rule-fixer");
13
+ const { RuleFixer } = require("./rule-fixer");
14
14
  const { interpolate } = require("./interpolate");
15
15
 
16
16
  //------------------------------------------------------------------------------
@@ -91,13 +91,10 @@ function assertValidNodeInfo(descriptor) {
91
91
  * from the `node` of the original descriptor, or infers the `start` from the `loc` of the original descriptor.
92
92
  */
93
93
  function normalizeReportLoc(descriptor) {
94
- if (descriptor.loc) {
95
- if (descriptor.loc.start) {
96
- return descriptor.loc;
97
- }
98
- return { start: descriptor.loc, end: null };
94
+ if (descriptor.loc.start) {
95
+ return descriptor.loc;
99
96
  }
100
- return descriptor.node.loc;
97
+ return { start: descriptor.loc, end: null };
101
98
  }
102
99
 
103
100
  /**
@@ -190,6 +187,8 @@ function normalizeFixes(descriptor, sourceCode) {
190
187
  return null;
191
188
  }
192
189
 
190
+ const ruleFixer = new RuleFixer({ sourceCode });
191
+
193
192
  // @type {null | Fix | Fix[] | IterableIterator<Fix>}
194
193
  const fix = descriptor.fix(ruleFixer);
195
194
 
@@ -335,6 +334,7 @@ module.exports = function createReportTranslator(metadata) {
335
334
  return (...args) => {
336
335
  const descriptor = normalizeMultiArgReportCall(...args);
337
336
  const messages = metadata.messageIds;
337
+ const { sourceCode } = metadata;
338
338
 
339
339
  assertValidNodeInfo(descriptor);
340
340
 
@@ -367,9 +367,9 @@ module.exports = function createReportTranslator(metadata) {
367
367
  node: descriptor.node,
368
368
  message: interpolate(computedMessage, descriptor.data),
369
369
  messageId: descriptor.messageId,
370
- loc: normalizeReportLoc(descriptor),
371
- fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode),
372
- suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor, metadata.sourceCode, messages),
370
+ loc: descriptor.loc ? normalizeReportLoc(descriptor) : sourceCode.getLoc(descriptor.node),
371
+ fix: metadata.disableFixes ? null : normalizeFixes(descriptor, sourceCode),
372
+ suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor, sourceCode, messages),
373
373
  language: metadata.language
374
374
  });
375
375
  };
@@ -4,6 +4,8 @@
4
4
  */
5
5
  "use strict";
6
6
 
7
+ /* eslint class-methods-use-this: off -- Methods desired on instance */
8
+
7
9
  //------------------------------------------------------------------------------
8
10
  // Requirements
9
11
  //------------------------------------------------------------------------------
@@ -35,8 +37,22 @@ function insertTextAt(index, text) {
35
37
  /**
36
38
  * Creates code fixing commands for rules.
37
39
  */
40
+ class RuleFixer {
38
41
 
39
- const ruleFixer = Object.freeze({
42
+ /**
43
+ * The source code object representing the text to be fixed.
44
+ * @type {SourceCode}
45
+ */
46
+ #sourceCode;
47
+
48
+ /**
49
+ * Creates a new instance.
50
+ * @param {Object} options The options for the fixer.
51
+ * @param {SourceCode} options.sourceCode The source code object representing the text to be fixed.
52
+ */
53
+ constructor({ sourceCode }) {
54
+ this.#sourceCode = sourceCode;
55
+ }
40
56
 
41
57
  /**
42
58
  * Creates a fix command that inserts text after the given node or token.
@@ -46,8 +62,10 @@ const ruleFixer = Object.freeze({
46
62
  * @returns {Object} The fix command.
47
63
  */
48
64
  insertTextAfter(nodeOrToken, text) {
49
- return this.insertTextAfterRange(nodeOrToken.range, text);
50
- },
65
+ const range = this.#sourceCode.getRange(nodeOrToken);
66
+
67
+ return this.insertTextAfterRange(range, text);
68
+ }
51
69
 
52
70
  /**
53
71
  * Creates a fix command that inserts text after the specified range in the source text.
@@ -59,7 +77,7 @@ const ruleFixer = Object.freeze({
59
77
  */
60
78
  insertTextAfterRange(range, text) {
61
79
  return insertTextAt(range[1], text);
62
- },
80
+ }
63
81
 
64
82
  /**
65
83
  * Creates a fix command that inserts text before the given node or token.
@@ -69,8 +87,10 @@ const ruleFixer = Object.freeze({
69
87
  * @returns {Object} The fix command.
70
88
  */
71
89
  insertTextBefore(nodeOrToken, text) {
72
- return this.insertTextBeforeRange(nodeOrToken.range, text);
73
- },
90
+ const range = this.#sourceCode.getRange(nodeOrToken);
91
+
92
+ return this.insertTextBeforeRange(range, text);
93
+ }
74
94
 
75
95
  /**
76
96
  * Creates a fix command that inserts text before the specified range in the source text.
@@ -82,7 +102,7 @@ const ruleFixer = Object.freeze({
82
102
  */
83
103
  insertTextBeforeRange(range, text) {
84
104
  return insertTextAt(range[0], text);
85
- },
105
+ }
86
106
 
87
107
  /**
88
108
  * Creates a fix command that replaces text at the node or token.
@@ -92,8 +112,10 @@ const ruleFixer = Object.freeze({
92
112
  * @returns {Object} The fix command.
93
113
  */
94
114
  replaceText(nodeOrToken, text) {
95
- return this.replaceTextRange(nodeOrToken.range, text);
96
- },
115
+ const range = this.#sourceCode.getRange(nodeOrToken);
116
+
117
+ return this.replaceTextRange(range, text);
118
+ }
97
119
 
98
120
  /**
99
121
  * Creates a fix command that replaces text at the specified range in the source text.
@@ -108,7 +130,7 @@ const ruleFixer = Object.freeze({
108
130
  range,
109
131
  text
110
132
  };
111
- },
133
+ }
112
134
 
113
135
  /**
114
136
  * Creates a fix command that removes the node or token from the source.
@@ -117,8 +139,10 @@ const ruleFixer = Object.freeze({
117
139
  * @returns {Object} The fix command.
118
140
  */
119
141
  remove(nodeOrToken) {
120
- return this.removeRange(nodeOrToken.range);
121
- },
142
+ const range = this.#sourceCode.getRange(nodeOrToken);
143
+
144
+ return this.removeRange(range);
145
+ }
122
146
 
123
147
  /**
124
148
  * Creates a fix command that removes the specified range of text from the source.
@@ -133,8 +157,7 @@ const ruleFixer = Object.freeze({
133
157
  text: ""
134
158
  };
135
159
  }
136
-
137
- });
160
+ }
138
161
 
139
162
 
140
- module.exports = ruleFixer;
163
+ module.exports = { RuleFixer };
@@ -5,6 +5,12 @@
5
5
 
6
6
  "use strict";
7
7
 
8
+ //-----------------------------------------------------------------------------
9
+ // Type Definitions
10
+ //-----------------------------------------------------------------------------
11
+
12
+ /** @typedef {import("@eslint/core").File} File */
13
+
8
14
  //------------------------------------------------------------------------------
9
15
  // Helpers
10
16
  //------------------------------------------------------------------------------
@@ -54,6 +60,7 @@ function stripUnicodeBOM(value) {
54
60
 
55
61
  /**
56
62
  * Represents a virtual file inside of ESLint.
63
+ * @implements {File}
57
64
  */
58
65
  class VFile {
59
66
 
@@ -89,6 +89,9 @@ const arrayOfStringsOrObjectPatterns = {
89
89
  minItems: 1,
90
90
  uniqueItems: true
91
91
  },
92
+ regex: {
93
+ type: "string"
94
+ },
92
95
  importNamePattern: {
93
96
  type: "string"
94
97
  },
@@ -104,7 +107,6 @@ const arrayOfStringsOrObjectPatterns = {
104
107
  }
105
108
  },
106
109
  additionalProperties: false,
107
- required: ["group"],
108
110
  not: {
109
111
  anyOf: [
110
112
  { required: ["importNames", "allowImportNames"] },
@@ -113,7 +115,11 @@ const arrayOfStringsOrObjectPatterns = {
113
115
  { required: ["importNamePattern", "allowImportNames"] },
114
116
  { required: ["allowImportNames", "allowImportNamePattern"] }
115
117
  ]
116
- }
118
+ },
119
+ oneOf: [
120
+ { required: ["group"] },
121
+ { required: ["regex"] }
122
+ ]
117
123
  },
118
124
  uniqueItems: true
119
125
  }
@@ -235,9 +241,10 @@ module.exports = {
235
241
 
236
242
  // relative paths are supported for this rule
237
243
  const restrictedPatternGroups = restrictedPatterns.map(
238
- ({ group, message, caseSensitive, importNames, importNamePattern, allowImportNames, allowImportNamePattern }) => (
244
+ ({ group, regex, message, caseSensitive, importNames, importNamePattern, allowImportNames, allowImportNamePattern }) => (
239
245
  {
240
- matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group),
246
+ ...(group ? { matcher: ignore({ allowRelativePaths: true, ignorecase: !caseSensitive }).add(group) } : {}),
247
+ ...(typeof regex === "string" ? { regexMatcher: new RegExp(regex, caseSensitive ? "u" : "iu") } : {}),
241
248
  customMessage: message,
242
249
  importNames,
243
250
  importNamePattern,
@@ -493,7 +500,7 @@ module.exports = {
493
500
  * @private
494
501
  */
495
502
  function isRestrictedPattern(importSource, group) {
496
- return group.matcher.ignores(importSource);
503
+ return group.regexMatcher ? group.regexMatcher.test(importSource) : group.matcher.ignores(importSource);
497
504
  }
498
505
 
499
506
  /**
@@ -340,7 +340,7 @@ module.exports = {
340
340
  /**
341
341
  * Determines if a variable has a sibling rest property
342
342
  * @param {Variable} variable eslint-scope variable object.
343
- * @returns {boolean} True if the variable is exported, false if not.
343
+ * @returns {boolean} True if the variable has a sibling rest property, false if not.
344
344
  * @private
345
345
  */
346
346
  function hasRestSpreadSibling(variable) {
@@ -72,11 +72,11 @@ module.exports = {
72
72
  schema: [],
73
73
 
74
74
  messages: {
75
- nested: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' from within that group.",
76
- forward: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which appears later in the pattern.",
77
- backward: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which appears before in the same lookbehind.",
78
- disjunctive: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which is in another alternative.",
79
- intoNegativeLookaround: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which is in a negative lookaround."
75
+ nested: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}'{{ otherGroups }} from within that group.",
76
+ forward: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}'{{ otherGroups }} which appears later in the pattern.",
77
+ backward: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}'{{ otherGroups }} which appears before in the same lookbehind.",
78
+ disjunctive: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}'{{ otherGroups }} which is in another alternative.",
79
+ intoNegativeLookaround: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}'{{ otherGroups }} which is in a negative lookaround."
80
80
  }
81
81
  },
82
82
 
@@ -104,16 +104,21 @@ module.exports = {
104
104
 
105
105
  visitRegExpAST(regExpAST, {
106
106
  onBackreferenceEnter(bref) {
107
- const group = bref.resolved,
108
- brefPath = getPathToRoot(bref),
109
- groupPath = getPathToRoot(group);
110
- let messageId = null;
107
+ const groups = [bref.resolved].flat(),
108
+ brefPath = getPathToRoot(bref);
111
109
 
112
- if (brefPath.includes(group)) {
110
+ const problems = groups.map(group => {
111
+ const groupPath = getPathToRoot(group);
112
+
113
+ if (brefPath.includes(group)) {
114
+
115
+ // group is bref's ancestor => bref is nested ('nested reference') => group hasn't matched yet when bref starts to match.
116
+ return {
117
+ messageId: "nested",
118
+ group
119
+ };
120
+ }
113
121
 
114
- // group is bref's ancestor => bref is nested ('nested reference') => group hasn't matched yet when bref starts to match.
115
- messageId = "nested";
116
- } else {
117
122
 
118
123
  // Start from the root to find the lowest common ancestor.
119
124
  let i = brefPath.length - 1,
@@ -130,35 +135,80 @@ module.exports = {
130
135
  lowestCommonLookaround = commonPath.find(isLookaround),
131
136
  isMatchingBackward = lowestCommonLookaround && lowestCommonLookaround.kind === "lookbehind";
132
137
 
138
+ if (groupCut.at(-1).type === "Alternative") {
139
+
140
+ // group's and bref's ancestor nodes below the lowest common ancestor are sibling alternatives => they're disjunctive.
141
+ return {
142
+ messageId: "disjunctive",
143
+ group
144
+ };
145
+ }
133
146
  if (!isMatchingBackward && bref.end <= group.start) {
134
147
 
135
148
  // bref is left, group is right ('forward reference') => group hasn't matched yet when bref starts to match.
136
- messageId = "forward";
137
- } else if (isMatchingBackward && group.end <= bref.start) {
149
+ return {
150
+ messageId: "forward",
151
+ group
152
+ };
153
+ }
154
+ if (isMatchingBackward && group.end <= bref.start) {
138
155
 
139
156
  // the opposite of the previous when the regex is matching backward in a lookbehind context.
140
- messageId = "backward";
141
- } else if (groupCut.at(-1).type === "Alternative") {
142
-
143
- // group's and bref's ancestor nodes below the lowest common ancestor are sibling alternatives => they're disjunctive.
144
- messageId = "disjunctive";
145
- } else if (groupCut.some(isNegativeLookaround)) {
157
+ return {
158
+ messageId: "backward",
159
+ group
160
+ };
161
+ }
162
+ if (groupCut.some(isNegativeLookaround)) {
146
163
 
147
164
  // group is in a negative lookaround which isn't bref's ancestor => group has already failed when bref starts to match.
148
- messageId = "intoNegativeLookaround";
165
+ return {
166
+ messageId: "intoNegativeLookaround",
167
+ group
168
+ };
149
169
  }
170
+
171
+ return null;
172
+ });
173
+
174
+ if (problems.length === 0 || problems.some(problem => !problem)) {
175
+
176
+ // If there are no problems or no problems with any group then do not report it.
177
+ return;
150
178
  }
151
179
 
152
- if (messageId) {
153
- context.report({
154
- node,
155
- messageId,
156
- data: {
157
- bref: bref.raw,
158
- group: group.raw
159
- }
160
- });
180
+ let problemsToReport;
181
+
182
+ // Gets problems that appear in the same disjunction.
183
+ const problemsInSameDisjunction = problems.filter(problem => problem.messageId !== "disjunctive");
184
+
185
+ if (problemsInSameDisjunction.length) {
186
+
187
+ // Only report problems that appear in the same disjunction.
188
+ problemsToReport = problemsInSameDisjunction;
189
+ } else {
190
+
191
+ // If all groups appear in different disjunctions, report it.
192
+ problemsToReport = problems;
161
193
  }
194
+
195
+ const [{ messageId, group }, ...other] = problemsToReport;
196
+ let otherGroups = "";
197
+
198
+ if (other.length === 1) {
199
+ otherGroups = " and another group";
200
+ } else if (other.length > 1) {
201
+ otherGroups = ` and other ${other.length} groups`;
202
+ }
203
+ context.report({
204
+ node,
205
+ messageId,
206
+ data: {
207
+ bref: bref.raw,
208
+ group: group.raw,
209
+ otherGroups
210
+ }
211
+ });
162
212
  }
163
213
  });
164
214
  }
@@ -8,7 +8,7 @@
8
8
 
9
9
  const { RegExpValidator } = require("@eslint-community/regexpp");
10
10
 
11
- const REGEXPP_LATEST_ECMA_VERSION = 2024;
11
+ const REGEXPP_LATEST_ECMA_VERSION = 2025;
12
12
 
13
13
  /**
14
14
  * Checks if the given regular expression pattern would be valid with the `u` flag.
@@ -9,7 +9,7 @@
9
9
  * @type {Map<string, string>}
10
10
  */
11
11
  const activeFlags = new Map([
12
- ["test_only", "This flag is only used for testing."]
12
+ ["test_only", "Used only for testing."]
13
13
  ]);
14
14
 
15
15
  /**
@@ -17,7 +17,7 @@ const activeFlags = new Map([
17
17
  * @type {Map<string, string>}
18
18
  */
19
19
  const inactiveFlags = new Map([
20
- ["test_only_old", "This flag is no longer used for testing."]
20
+ ["test_only_old", "Used only for testing."]
21
21
  ]);
22
22
 
23
23
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.6.0",
3
+ "version": "9.8.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -67,10 +67,10 @@
67
67
  "bugs": "https://github.com/eslint/eslint/issues/",
68
68
  "dependencies": {
69
69
  "@eslint-community/eslint-utils": "^4.2.0",
70
- "@eslint-community/regexpp": "^4.6.1",
71
- "@eslint/config-array": "^0.17.0",
70
+ "@eslint-community/regexpp": "^4.11.0",
71
+ "@eslint/config-array": "^0.17.1",
72
72
  "@eslint/eslintrc": "^3.1.0",
73
- "@eslint/js": "9.6.0",
73
+ "@eslint/js": "9.8.0",
74
74
  "@humanwhocodes/module-importer": "^1.0.1",
75
75
  "@humanwhocodes/retry": "^0.3.0",
76
76
  "@nodelib/fs.walk": "^1.2.8",
@@ -79,7 +79,7 @@
79
79
  "cross-spawn": "^7.0.2",
80
80
  "debug": "^4.3.2",
81
81
  "escape-string-regexp": "^4.0.0",
82
- "eslint-scope": "^8.0.1",
82
+ "eslint-scope": "^8.0.2",
83
83
  "eslint-visitor-keys": "^4.0.0",
84
84
  "espree": "^10.1.0",
85
85
  "esquery": "^1.5.0",
@@ -104,6 +104,8 @@
104
104
  "devDependencies": {
105
105
  "@babel/core": "^7.4.3",
106
106
  "@babel/preset-env": "^7.4.3",
107
+ "@eslint/core": "^0.2.0",
108
+ "@eslint/json": "^0.2.0",
107
109
  "@types/estree": "^1.0.5",
108
110
  "@types/node": "^20.11.5",
109
111
  "@wdio/browser-runner": "^8.38.3",