eslint 9.5.0 → 9.6.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
@@ -294,8 +294,8 @@ 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></p><h3>Gold Sponsors</h3>
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> <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>
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>
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-->
@@ -9,7 +9,7 @@
9
9
  * The latest ECMAScript version supported by ESLint.
10
10
  * @type {number} year-based ECMAScript version
11
11
  */
12
- const LATEST_ECMA_VERSION = 2024;
12
+ const LATEST_ECMA_VERSION = 2025;
13
13
 
14
14
  module.exports = {
15
15
  LATEST_ECMA_VERSION
package/conf/globals.js CHANGED
@@ -133,6 +133,10 @@ const es2024 = {
133
133
  ...es2023
134
134
  };
135
135
 
136
+ const es2025 = {
137
+ ...es2024
138
+ };
139
+
136
140
 
137
141
  //-----------------------------------------------------------------------------
138
142
  // Exports
@@ -151,5 +155,6 @@ module.exports = {
151
155
  es2021,
152
156
  es2022,
153
157
  es2023,
154
- es2024
158
+ es2024,
159
+ es2025
155
160
  };
package/lib/cli.js CHANGED
@@ -26,7 +26,7 @@ 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
-
29
+ const { inactiveFlags, activeFlags } = require("./shared/flags");
30
30
  const debug = require("debug")("eslint:cli");
31
31
 
32
32
  //------------------------------------------------------------------------------
@@ -117,6 +117,7 @@ async function translateOptions({
117
117
  fix,
118
118
  fixDryRun,
119
119
  fixType,
120
+ flag,
120
121
  global,
121
122
  ignore,
122
123
  ignorePath,
@@ -225,6 +226,7 @@ async function translateOptions({
225
226
  options.ignorePatterns = ignorePattern;
226
227
  options.stats = stats;
227
228
  options.warnIgnored = warnIgnored;
229
+ options.flags = flag;
228
230
 
229
231
  /*
230
232
  * For performance reasons rules not marked as 'error' are filtered out in quiet mode. As maxWarnings
@@ -485,8 +487,27 @@ const cli = {
485
487
  }
486
488
 
487
489
  const ActiveESLint = usingFlatConfig ? ESLint : LegacyESLint;
490
+ 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
+ }
488
509
 
489
- const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
510
+ const engine = new ActiveESLint(eslintOptions);
490
511
  let results;
491
512
 
492
513
  if (useStdin) {
@@ -723,6 +723,7 @@ function processOptions({
723
723
  errorOnUnmatchedPattern = true,
724
724
  fix = false,
725
725
  fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
726
+ flags = [],
726
727
  globInputPaths = true,
727
728
  ignore = true,
728
729
  ignorePatterns = null,
@@ -810,6 +811,9 @@ function processOptions({
810
811
  if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
811
812
  errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
812
813
  }
814
+ if (!isEmptyArrayOrArrayOfNonEmptyString(flags)) {
815
+ errors.push("'flags' must be an array of non-empty strings.");
816
+ }
813
817
  if (typeof globInputPaths !== "boolean") {
814
818
  errors.push("'globInputPaths' must be a boolean.");
815
819
  }
@@ -863,6 +867,7 @@ function processOptions({
863
867
  errorOnUnmatchedPattern,
864
868
  fix,
865
869
  fixTypes,
870
+ flags: [...flags],
866
871
  globInputPaths,
867
872
  ignore,
868
873
  ignorePatterns,
@@ -76,6 +76,7 @@ const { Retrier } = require("@humanwhocodes/retry");
76
76
  * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
77
77
  * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
78
78
  * @property {string[]} [fixTypes] Array of rule types to apply fixes for.
79
+ * @property {string[]} [flags] Array of feature flags to enable.
79
80
  * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
80
81
  * @property {boolean} [ignore] False disables all ignore patterns except for the default ones.
81
82
  * @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores. These patterns are relative to `cwd`.
@@ -421,7 +422,8 @@ async function calculateConfigArray(eslint, {
421
422
  relativeIgnorePatterns = ignorePatterns;
422
423
  } else {
423
424
 
424
- const relativeIgnorePath = path.relative(basePath, cwd);
425
+ // In minimatch patterns, only `/` can be used as path separator
426
+ const relativeIgnorePath = path.relative(basePath, cwd).replaceAll(path.sep, "/");
425
427
 
426
428
  relativeIgnorePatterns = ignorePatterns.map(pattern => {
427
429
  const negated = pattern.startsWith("!");
@@ -593,7 +595,8 @@ class ESLint {
593
595
  const processedOptions = processOptions(options);
594
596
  const linter = new Linter({
595
597
  cwd: processedOptions.cwd,
596
- configType: "flat"
598
+ configType: "flat",
599
+ flags: processedOptions.flags
597
600
  });
598
601
 
599
602
  const cacheFilePath = getCacheFile(
@@ -766,6 +769,17 @@ class ESLint {
766
769
  return createRulesMeta(resultRules);
767
770
  }
768
771
 
772
+ /**
773
+ * Indicates if the given feature flag is enabled for this instance.
774
+ * @param {string} flag The feature flag to check.
775
+ * @returns {boolean} `true` if the feature flag is enabled, `false` if not.
776
+ */
777
+ hasFlag(flag) {
778
+
779
+ // note: Linter does validation of the flags
780
+ return privateMembers.get(this).linter.hasFlag(flag);
781
+ }
782
+
769
783
  /**
770
784
  * Executes the current configuration on an array of file and directory names.
771
785
  * @param {string|string[]} patterns An array of file and directory names.
@@ -171,6 +171,7 @@ function processOptions({
171
171
  extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature.
172
172
  fix = false,
173
173
  fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
174
+ flags, /* eslint-disable-line no-unused-vars -- leaving for compatibility with ESLint#hasFlag */
174
175
  globInputPaths = true,
175
176
  ignore = true,
176
177
  ignorePath = null, // ← should be null by default because if it's a string then it may throw ENOENT.
@@ -310,6 +311,7 @@ function processOptions({
310
311
  extensions,
311
312
  fix,
312
313
  fixTypes,
314
+ flags: [], // LegacyESLint does not support flags, so just ignore them.
313
315
  globInputPaths,
314
316
  ignore,
315
317
  ignorePath,
@@ -558,6 +560,18 @@ class LegacyESLint {
558
560
 
559
561
  }
560
562
 
563
+ /* eslint-disable no-unused-vars, class-methods-use-this -- leaving for compatibility with ESLint#hasFlag */
564
+ /**
565
+ * Indicates if the given feature flag is enabled for this instance. For this
566
+ * class, this always returns `false` because it does not support feature flags.
567
+ * @param {string} flag The feature flag to check.
568
+ * @returns {boolean} Always false.
569
+ */
570
+ hasFlag(flag) {
571
+ return false;
572
+ }
573
+ /* eslint-enable no-unused-vars, class-methods-use-this -- reenable rules for the rest of the file */
574
+
561
575
  /**
562
576
  * Executes the current configuration on an array of file and directory names.
563
577
  * @param {string[]} patterns An array of file and directory names.
@@ -1099,7 +1099,7 @@ class SourceCode extends TokenStore {
1099
1099
  /**
1100
1100
  * Applies configuration found inside of the source code. This method is only
1101
1101
  * called when ESLint is running with inline configuration allowed.
1102
- * @returns {{problems:Array<Problem>,configs:{config:FlatConfigArray,node:ASTNode}}} Information
1102
+ * @returns {{problems:Array<Problem>,configs:{config:FlatConfigArray,loc:Location}}} Information
1103
1103
  * that ESLint needs to further process the inline configuration.
1104
1104
  */
1105
1105
  applyInlineConfig() {
@@ -1147,17 +1147,21 @@ class SourceCode extends TokenStore {
1147
1147
  break;
1148
1148
 
1149
1149
  case "eslint": {
1150
- const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc);
1150
+ const parseResult = commentParser.parseJsonConfig(directiveValue);
1151
1151
 
1152
1152
  if (parseResult.success) {
1153
1153
  configs.push({
1154
1154
  config: {
1155
1155
  rules: parseResult.config
1156
1156
  },
1157
- node: comment
1157
+ loc: comment.loc
1158
1158
  });
1159
1159
  } else {
1160
- problems.push(parseResult.error);
1160
+ problems.push({
1161
+ ruleId: null,
1162
+ loc: comment.loc,
1163
+ message: parseResult.error.message
1164
+ });
1161
1165
  }
1162
1166
 
1163
1167
  break;
@@ -22,12 +22,6 @@ const levn = require("levn"),
22
22
 
23
23
  const debug = require("debug")("eslint:config-comment-parser");
24
24
 
25
- //------------------------------------------------------------------------------
26
- // Typedefs
27
- //------------------------------------------------------------------------------
28
-
29
- /** @typedef {import("../shared/types").LintMessage} LintMessage */
30
-
31
25
  //------------------------------------------------------------------------------
32
26
  // Public Interface
33
27
  //------------------------------------------------------------------------------
@@ -69,10 +63,9 @@ module.exports = class ConfigCommentParser {
69
63
  /**
70
64
  * Parses a JSON-like config.
71
65
  * @param {string} string The string to parse.
72
- * @param {Object} location Start line and column of comments for potential error message.
73
- * @returns {({success: true, config: Object}|{success: false, error: LintMessage})} Result map object
66
+ * @returns {({success: true, config: Object}|{success: false, error: {message: string}})} Result map object
74
67
  */
75
- parseJsonConfig(string, location) {
68
+ parseJsonConfig(string) {
76
69
  debug("Parsing JSON config");
77
70
 
78
71
  // Parses a JSON-like comment by the same way as parsing CLI option.
@@ -115,13 +108,7 @@ module.exports = class ConfigCommentParser {
115
108
  return {
116
109
  success: false,
117
110
  error: {
118
- ruleId: null,
119
- fatal: true,
120
- severity: 2,
121
- message: `Failed to parse JSON from '${normalizedString}': ${ex.message}`,
122
- line: location.start.line,
123
- column: location.start.column + 1,
124
- nodeType: null
111
+ message: `Failed to parse JSON from '${normalizedString}': ${ex.message}`
125
112
  }
126
113
  };
127
114
 
@@ -45,6 +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
49
  const debug = require("debug")("eslint:linter");
49
50
  const MAX_AUTOFIX_PASSES = 10;
50
51
  const DEFAULT_PARSER_NAME = "espree";
@@ -317,9 +318,10 @@ function createLintingProblem(options) {
317
318
  * @param {string} options.justification The justification of the directive
318
319
  * @param {ASTNode|token} options.node The Comment node/token.
319
320
  * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules
321
+ * @param {Language} language The language to use to adjust the location information.
320
322
  * @returns {Object} Directives and problems from the comment
321
323
  */
322
- function createDisableDirectives({ type, value, justification, node }, ruleMapper) {
324
+ function createDisableDirectives({ type, value, justification, node }, ruleMapper, language) {
323
325
  const ruleIds = Object.keys(commentParser.parseListConfig(value));
324
326
  const directiveRules = ruleIds.length ? ruleIds : [null];
325
327
  const result = {
@@ -333,26 +335,36 @@ function createDisableDirectives({ type, value, justification, node }, ruleMappe
333
335
  // push to directives, if the rule is defined(including null, e.g. /*eslint enable*/)
334
336
  if (ruleId === null || !!ruleMapper(ruleId)) {
335
337
  if (type === "disable-next-line") {
338
+ const { line, column } = updateLocationInformation(
339
+ node.loc.end,
340
+ language
341
+ );
342
+
336
343
  result.directives.push({
337
344
  parentDirective,
338
345
  type,
339
- line: node.loc.end.line,
340
- column: node.loc.end.column + 1,
346
+ line,
347
+ column,
341
348
  ruleId,
342
349
  justification
343
350
  });
344
351
  } else {
352
+ const { line, column } = updateLocationInformation(
353
+ node.loc.start,
354
+ language
355
+ );
356
+
345
357
  result.directives.push({
346
358
  parentDirective,
347
359
  type,
348
- line: node.loc.start.line,
349
- column: node.loc.start.column + 1,
360
+ line,
361
+ column,
350
362
  ruleId,
351
363
  justification
352
364
  });
353
365
  }
354
366
  } else {
355
- result.directiveProblems.push(createLintingProblem({ ruleId, loc: node.loc }));
367
+ result.directiveProblems.push(createLintingProblem({ ruleId, loc: node.loc, language }));
356
368
  }
357
369
  }
358
370
  return result;
@@ -430,7 +442,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
430
442
  value: directiveValue,
431
443
  justification: justificationPart,
432
444
  node: comment
433
- }, ruleMapper);
445
+ }, ruleMapper, jslang);
434
446
 
435
447
  disableDirectives.push(...directives);
436
448
  problems.push(...directiveProblems);
@@ -470,7 +482,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
470
482
  break;
471
483
 
472
484
  case "eslint": {
473
- const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc);
485
+ const parseResult = commentParser.parseJsonConfig(directiveValue);
474
486
 
475
487
  if (parseResult.success) {
476
488
  Object.keys(parseResult.config).forEach(name => {
@@ -557,7 +569,14 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
557
569
  configuredRules[name] = ruleOptions;
558
570
  });
559
571
  } else {
560
- problems.push(parseResult.error);
572
+ const problem = createLintingProblem({
573
+ ruleId: null,
574
+ loc: comment.loc,
575
+ message: parseResult.error.message
576
+ });
577
+
578
+ problem.fatal = true;
579
+ problems.push(problem);
561
580
  }
562
581
 
563
582
  break;
@@ -588,22 +607,24 @@ function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper, language) {
588
607
  const disableDirectives = [];
589
608
  const problems = [];
590
609
 
591
- const {
592
- directives: directivesSources,
593
- problems: directivesProblems
594
- } = sourceCode.getDisableDirectives();
610
+ if (sourceCode.getDisableDirectives) {
611
+ const {
612
+ directives: directivesSources,
613
+ problems: directivesProblems
614
+ } = sourceCode.getDisableDirectives();
595
615
 
596
- problems.push(...directivesProblems.map(directiveProblem => createLintingProblem({
597
- ...directiveProblem,
598
- language
599
- })));
616
+ problems.push(...directivesProblems.map(directiveProblem => createLintingProblem({
617
+ ...directiveProblem,
618
+ language
619
+ })));
600
620
 
601
- directivesSources.forEach(directive => {
602
- const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper);
621
+ directivesSources.forEach(directive => {
622
+ const { directives, directiveProblems } = createDisableDirectives(directive, ruleMapper, language);
603
623
 
604
- disableDirectives.push(...directives);
605
- problems.push(...directiveProblems);
606
- });
624
+ disableDirectives.push(...directives);
625
+ problems.push(...directiveProblems);
626
+ });
627
+ }
607
628
 
608
629
  return {
609
630
  problems,
@@ -1122,9 +1143,9 @@ function runRules(
1122
1143
  });
1123
1144
 
1124
1145
  const eventGenerator = new NodeEventGenerator(emitter, {
1125
- visitorKeys: sourceCode.visitorKeys,
1146
+ visitorKeys: sourceCode.visitorKeys ?? language.visitorKeys,
1126
1147
  fallback: Traverser.getKeys,
1127
- matchClass: language.matchesSelectorClass,
1148
+ matchClass: language.matchesSelectorClass ?? (() => false),
1128
1149
  nodeTypeKey: language.nodeTypeKey
1129
1150
  });
1130
1151
 
@@ -1253,11 +1274,13 @@ class Linter {
1253
1274
  * Initialize the Linter.
1254
1275
  * @param {Object} [config] the config object
1255
1276
  * @param {string} [config.cwd] path to a directory that should be considered as the current working directory, can be undefined.
1277
+ * @param {Array<string>} [config.flags] the feature flags to enable.
1256
1278
  * @param {"flat"|"eslintrc"} [config.configType="flat"] the type of config used.
1257
1279
  */
1258
- constructor({ cwd, configType = "flat" } = {}) {
1280
+ constructor({ cwd, configType = "flat", flags = [] } = {}) {
1259
1281
  internalSlotsMap.set(this, {
1260
1282
  cwd: normalizeCwd(cwd),
1283
+ flags: flags.filter(flag => activeFlags.has(flag)),
1261
1284
  lastConfigArray: null,
1262
1285
  lastSourceCode: null,
1263
1286
  lastSuppressedMessages: [],
@@ -1278,6 +1301,15 @@ class Linter {
1278
1301
  return pkg.version;
1279
1302
  }
1280
1303
 
1304
+ /**
1305
+ * Indicates if the given feature flag is enabled for this instance.
1306
+ * @param {string} flag The feature flag to check.
1307
+ * @returns {boolean} `true` if the feature flag is enabled, `false` if not.
1308
+ */
1309
+ hasFlag(flag) {
1310
+ return internalSlotsMap.get(this).flags.includes(flag);
1311
+ }
1312
+
1281
1313
  /**
1282
1314
  * Same as linter.verify, except without support for processors.
1283
1315
  * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
@@ -1679,8 +1711,13 @@ class Linter {
1679
1711
  /*
1680
1712
  * If the given source code object as the first argument does not have scopeManager, analyze the scope.
1681
1713
  * This is for backward compatibility (SourceCode is frozen so it cannot rebind).
1714
+ *
1715
+ * We check explicitly for `null` to ensure that this is a JS-flavored language.
1716
+ * For non-JS languages we don't want to do this.
1717
+ *
1718
+ * TODO: Remove this check when we stop exporting the `SourceCode` object.
1682
1719
  */
1683
- if (!slots.lastSourceCode.scopeManager) {
1720
+ if (slots.lastSourceCode.scopeManager === null) {
1684
1721
  slots.lastSourceCode = new SourceCode({
1685
1722
  text: slots.lastSourceCode.text,
1686
1723
  ast: slots.lastSourceCode.ast,
@@ -1699,7 +1736,7 @@ class Linter {
1699
1736
  * this is primarily about adding variables into the global scope
1700
1737
  * to account for ecmaVersion and configured globals.
1701
1738
  */
1702
- sourceCode.applyLanguageOptions(languageOptions);
1739
+ sourceCode.applyLanguageOptions?.(languageOptions);
1703
1740
 
1704
1741
  const mergedInlineConfig = {
1705
1742
  rules: {}
@@ -1716,147 +1753,151 @@ class Linter {
1716
1753
 
1717
1754
  // if inline config should warn then add the warnings
1718
1755
  if (options.warnInlineConfig) {
1719
- sourceCode.getInlineConfigNodes().forEach(node => {
1720
- inlineConfigProblems.push(createLintingProblem({
1721
- ruleId: null,
1722
- message: `'${sourceCode.text.slice(node.range[0], node.range[1])}' has no effect because you have 'noInlineConfig' setting in ${options.warnInlineConfig}.`,
1723
- loc: node.loc,
1724
- severity: 1,
1725
- language: config.language
1726
- }));
1756
+ if (sourceCode.getInlineConfigNodes) {
1757
+ sourceCode.getInlineConfigNodes().forEach(node => {
1758
+ inlineConfigProblems.push(createLintingProblem({
1759
+ 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,
1762
+ severity: 1,
1763
+ language: config.language
1764
+ }));
1727
1765
 
1728
- });
1766
+ });
1767
+ }
1729
1768
  } else {
1730
- const inlineConfigResult = sourceCode.applyInlineConfig();
1731
-
1732
- inlineConfigProblems.push(
1733
- ...inlineConfigResult.problems
1734
- .map(problem => createLintingProblem({ ...problem, language: config.language }))
1735
- .map(problem => {
1736
- problem.fatal = true;
1737
- return problem;
1738
- })
1739
- );
1769
+ const inlineConfigResult = sourceCode.applyInlineConfig?.();
1770
+
1771
+ if (inlineConfigResult) {
1772
+ inlineConfigProblems.push(
1773
+ ...inlineConfigResult.problems
1774
+ .map(problem => createLintingProblem({ ...problem, language: config.language }))
1775
+ .map(problem => {
1776
+ problem.fatal = true;
1777
+ return problem;
1778
+ })
1779
+ );
1780
+
1781
+ // next we need to verify information about the specified rules
1782
+ const ruleValidator = new RuleValidator();
1783
+
1784
+ for (const { config: inlineConfig, loc } of inlineConfigResult.configs) {
1785
+
1786
+ Object.keys(inlineConfig.rules).forEach(ruleId => {
1787
+ const rule = getRuleFromConfig(ruleId, config);
1788
+ const ruleValue = inlineConfig.rules[ruleId];
1789
+
1790
+ if (!rule) {
1791
+ inlineConfigProblems.push(createLintingProblem({
1792
+ ruleId,
1793
+ loc,
1794
+ language: config.language
1795
+ }));
1796
+ return;
1797
+ }
1740
1798
 
1741
- // next we need to verify information about the specified rules
1742
- const ruleValidator = new RuleValidator();
1799
+ if (Object.hasOwn(mergedInlineConfig.rules, ruleId)) {
1800
+ inlineConfigProblems.push(createLintingProblem({
1801
+ message: `Rule "${ruleId}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
1802
+ loc,
1803
+ language: config.language
1804
+ }));
1805
+ return;
1806
+ }
1743
1807
 
1744
- for (const { config: inlineConfig, node } of inlineConfigResult.configs) {
1808
+ try {
1745
1809
 
1746
- Object.keys(inlineConfig.rules).forEach(ruleId => {
1747
- const rule = getRuleFromConfig(ruleId, config);
1748
- const ruleValue = inlineConfig.rules[ruleId];
1810
+ let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
1749
1811
 
1750
- if (!rule) {
1751
- inlineConfigProblems.push(createLintingProblem({
1752
- ruleId,
1753
- loc: node.loc,
1754
- language: config.language
1755
- }));
1756
- return;
1757
- }
1812
+ assertIsRuleSeverity(ruleId, ruleOptions[0]);
1758
1813
 
1759
- if (Object.hasOwn(mergedInlineConfig.rules, ruleId)) {
1760
- inlineConfigProblems.push(createLintingProblem({
1761
- message: `Rule "${ruleId}" is already configured by another configuration comment in the preceding code. This configuration is ignored.`,
1762
- loc: node.loc,
1763
- language: config.language
1764
- }));
1765
- return;
1766
- }
1814
+ /*
1815
+ * If the rule was already configured, inline rule configuration that
1816
+ * only has severity should retain options from the config and just override the severity.
1817
+ *
1818
+ * Example:
1819
+ *
1820
+ * {
1821
+ * rules: {
1822
+ * curly: ["error", "multi"]
1823
+ * }
1824
+ * }
1825
+ *
1826
+ * /* eslint curly: ["warn"] * /
1827
+ *
1828
+ * Results in:
1829
+ *
1830
+ * curly: ["warn", "multi"]
1831
+ */
1767
1832
 
1768
- try {
1833
+ let shouldValidateOptions = true;
1769
1834
 
1770
- let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
1835
+ if (
1771
1836
 
1772
- assertIsRuleSeverity(ruleId, ruleOptions[0]);
1837
+ /*
1838
+ * If inline config for the rule has only severity
1839
+ */
1840
+ ruleOptions.length === 1 &&
1773
1841
 
1774
- /*
1775
- * If the rule was already configured, inline rule configuration that
1776
- * only has severity should retain options from the config and just override the severity.
1777
- *
1778
- * Example:
1779
- *
1780
- * {
1781
- * rules: {
1782
- * curly: ["error", "multi"]
1783
- * }
1784
- * }
1785
- *
1786
- * /* eslint curly: ["warn"] * /
1787
- *
1788
- * Results in:
1789
- *
1790
- * curly: ["warn", "multi"]
1791
- */
1842
+ /*
1843
+ * And the rule was already configured
1844
+ */
1845
+ config.rules && Object.hasOwn(config.rules, ruleId)
1846
+ ) {
1792
1847
 
1793
- let shouldValidateOptions = true;
1848
+ /*
1849
+ * Then use severity from the inline config and options from the provided config
1850
+ */
1851
+ ruleOptions = [
1852
+ ruleOptions[0], // severity from the inline config
1853
+ ...config.rules[ruleId].slice(1) // options from the provided config
1854
+ ];
1794
1855
 
1795
- if (
1856
+ // if the rule was enabled, the options have already been validated
1857
+ if (config.rules[ruleId][0] > 0) {
1858
+ shouldValidateOptions = false;
1859
+ }
1860
+ }
1796
1861
 
1797
- /*
1798
- * If inline config for the rule has only severity
1799
- */
1800
- ruleOptions.length === 1 &&
1862
+ if (shouldValidateOptions) {
1863
+ ruleValidator.validate({
1864
+ plugins: config.plugins,
1865
+ rules: {
1866
+ [ruleId]: ruleOptions
1867
+ }
1868
+ });
1869
+ }
1801
1870
 
1802
- /*
1803
- * And the rule was already configured
1804
- */
1805
- config.rules && Object.hasOwn(config.rules, ruleId)
1806
- ) {
1871
+ mergedInlineConfig.rules[ruleId] = ruleOptions;
1872
+ } catch (err) {
1807
1873
 
1808
1874
  /*
1809
- * Then use severity from the inline config and options from the provided config
1875
+ * If the rule has invalid `meta.schema`, throw the error because
1876
+ * this is not an invalid inline configuration but an invalid rule.
1810
1877
  */
1811
- ruleOptions = [
1812
- ruleOptions[0], // severity from the inline config
1813
- ...config.rules[ruleId].slice(1) // options from the provided config
1814
- ];
1815
-
1816
- // if the rule was enabled, the options have already been validated
1817
- if (config.rules[ruleId][0] > 0) {
1818
- shouldValidateOptions = false;
1878
+ if (err.code === "ESLINT_INVALID_RULE_OPTIONS_SCHEMA") {
1879
+ throw err;
1819
1880
  }
1820
- }
1821
1881
 
1822
- if (shouldValidateOptions) {
1823
- ruleValidator.validate({
1824
- plugins: config.plugins,
1825
- rules: {
1826
- [ruleId]: ruleOptions
1827
- }
1828
- });
1829
- }
1882
+ let baseMessage = err.message.slice(
1883
+ err.message.startsWith("Key \"rules\":")
1884
+ ? err.message.indexOf(":", 12) + 1
1885
+ : err.message.indexOf(":") + 1
1886
+ ).trim();
1830
1887
 
1831
- mergedInlineConfig.rules[ruleId] = ruleOptions;
1832
- } catch (err) {
1833
-
1834
- /*
1835
- * If the rule has invalid `meta.schema`, throw the error because
1836
- * this is not an invalid inline configuration but an invalid rule.
1837
- */
1838
- if (err.code === "ESLINT_INVALID_RULE_OPTIONS_SCHEMA") {
1839
- throw err;
1840
- }
1841
-
1842
- let baseMessage = err.message.slice(
1843
- err.message.startsWith("Key \"rules\":")
1844
- ? err.message.indexOf(":", 12) + 1
1845
- : err.message.indexOf(":") + 1
1846
- ).trim();
1888
+ if (err.messageTemplate) {
1889
+ baseMessage += ` You passed "${ruleValue}".`;
1890
+ }
1847
1891
 
1848
- if (err.messageTemplate) {
1849
- baseMessage += ` You passed "${ruleValue}".`;
1892
+ inlineConfigProblems.push(createLintingProblem({
1893
+ ruleId,
1894
+ message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
1895
+ loc,
1896
+ language: config.language
1897
+ }));
1850
1898
  }
1851
-
1852
- inlineConfigProblems.push(createLintingProblem({
1853
- ruleId,
1854
- message: `Inline configuration for rule "${ruleId}" is invalid:\n\t${baseMessage}\n`,
1855
- loc: node.loc,
1856
- language: config.language
1857
- }));
1858
- }
1859
- });
1899
+ });
1900
+ }
1860
1901
  }
1861
1902
  }
1862
1903
  }
@@ -1873,7 +1914,7 @@ class Linter {
1873
1914
 
1874
1915
  let lintingProblems;
1875
1916
 
1876
- sourceCode.finalize();
1917
+ sourceCode.finalize?.();
1877
1918
 
1878
1919
  try {
1879
1920
  lintingProblems = runRules(
package/lib/options.js CHANGED
@@ -30,6 +30,7 @@ const optionator = require("optionator");
30
30
  * @property {boolean} errorOnUnmatchedPattern Prevent errors when pattern is unmatched
31
31
  * @property {boolean} eslintrc Disable use of configuration from .eslintrc.*
32
32
  * @property {string[]} [ext] Specify JavaScript file extensions
33
+ * @property {string[]} [flag] Feature flags
33
34
  * @property {boolean} fix Automatically fix problems
34
35
  * @property {boolean} fixDryRun Automatically fix problems without saving the changes to the file system
35
36
  * @property {("directive" | "problem" | "suggestion" | "layout")[]} [fixType] Specify the types of fixes to apply (directive, problem, suggestion, layout)
@@ -176,6 +177,16 @@ module.exports = function(usingFlatConfig) {
176
177
  };
177
178
  }
178
179
 
180
+ let flagFlag;
181
+
182
+ if (usingFlatConfig) {
183
+ flagFlag = {
184
+ option: "flag",
185
+ type: "[String]",
186
+ description: "Enable a feature flag"
187
+ };
188
+ }
189
+
179
190
  return optionator({
180
191
  prepend: "eslint [options] file.js [file.js] [dir]",
181
192
  defaults: {
@@ -424,7 +435,8 @@ module.exports = function(usingFlatConfig) {
424
435
  type: "path::String",
425
436
  description: "Print the configuration for the given file"
426
437
  },
427
- statsFlag
438
+ statsFlag,
439
+ flagFlag
428
440
  ].filter(value => !!value)
429
441
  });
430
442
  };
@@ -147,6 +147,36 @@ module.exports = {
147
147
  }
148
148
  }
149
149
 
150
+ /**
151
+ * Determines what variable type a def is.
152
+ * @param {Object} def the declaration to check
153
+ * @returns {VariableType} a simple name for the types of variables that this rule supports
154
+ */
155
+ function defToVariableType(def) {
156
+
157
+ /*
158
+ * This `destructuredArrayIgnorePattern` error report works differently from the catch
159
+ * clause and parameter error reports. _Both_ the `varsIgnorePattern` and the
160
+ * `destructuredArrayIgnorePattern` will be checked for array destructuring. However,
161
+ * for the purposes of the report, the currently defined behavior is to only inform the
162
+ * user of the `destructuredArrayIgnorePattern` if it's present (regardless of the fact
163
+ * that the `varsIgnorePattern` would also apply). If it's not present, the user will be
164
+ * informed of the `varsIgnorePattern`, assuming that's present.
165
+ */
166
+ if (config.destructuredArrayIgnorePattern && def.name.parent.type === "ArrayPattern") {
167
+ return "array-destructure";
168
+ }
169
+
170
+ switch (def.type) {
171
+ case "CatchClause":
172
+ return "catch-clause";
173
+ case "Parameter":
174
+ return "parameter";
175
+ default:
176
+ return "variable";
177
+ }
178
+ }
179
+
150
180
  /**
151
181
  * Gets a given variable's description and configured ignore pattern
152
182
  * based on the provided variableType
@@ -167,7 +197,7 @@ module.exports = {
167
197
 
168
198
  case "catch-clause":
169
199
  pattern = config.caughtErrorsIgnorePattern;
170
- variableDescription = "args";
200
+ variableDescription = "caught errors";
171
201
  break;
172
202
 
173
203
  case "parameter":
@@ -202,28 +232,7 @@ module.exports = {
202
232
  let additionalMessageData = "";
203
233
 
204
234
  if (def) {
205
- let pattern;
206
- let variableDescription;
207
-
208
- switch (def.type) {
209
- case "CatchClause":
210
- if (config.caughtErrorsIgnorePattern) {
211
- [variableDescription, pattern] = getVariableDescription("catch-clause");
212
- }
213
- break;
214
-
215
- case "Parameter":
216
- if (config.argsIgnorePattern) {
217
- [variableDescription, pattern] = getVariableDescription("parameter");
218
- }
219
- break;
220
-
221
- default:
222
- if (config.varsIgnorePattern) {
223
- [variableDescription, pattern] = getVariableDescription("variable");
224
- }
225
- break;
226
- }
235
+ const [variableDescription, pattern] = getVariableDescription(defToVariableType(def));
227
236
 
228
237
  if (pattern && variableDescription) {
229
238
  additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`;
@@ -248,14 +257,7 @@ module.exports = {
248
257
  let additionalMessageData = "";
249
258
 
250
259
  if (def) {
251
- let pattern;
252
- let variableDescription;
253
-
254
- if (def.name.parent.type === "ArrayPattern" && config.destructuredArrayIgnorePattern) {
255
- [variableDescription, pattern] = getVariableDescription("array-destructure");
256
- } else if (config.varsIgnorePattern) {
257
- [variableDescription, pattern] = getVariableDescription("variable");
258
- }
260
+ const [variableDescription, pattern] = getVariableDescription(defToVariableType(def));
259
261
 
260
262
  if (pattern && variableDescription) {
261
263
  additionalMessageData = `. Allowed unused ${variableDescription} must match ${pattern}`;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @fileoverview Shared flags for ESLint.
3
+ */
4
+
5
+ "use strict";
6
+
7
+ /**
8
+ * The set of flags that change ESLint behavior with a description.
9
+ * @type {Map<string, string>}
10
+ */
11
+ const activeFlags = new Map([
12
+ ["test_only", "This flag is only used for testing."]
13
+ ]);
14
+
15
+ /**
16
+ * The set of flags that used to be active but no longer have an effect.
17
+ * @type {Map<string, string>}
18
+ */
19
+ const inactiveFlags = new Map([
20
+ ["test_only_old", "This flag is no longer used for testing."]
21
+ ]);
22
+
23
+ module.exports = {
24
+ activeFlags,
25
+ inactiveFlags
26
+ };
@@ -11,7 +11,7 @@
11
11
  module.exports = {
12
12
 
13
13
  /**
14
- * Cover for console.log
14
+ * Cover for console.info
15
15
  * @param {...any} args The elements to log.
16
16
  * @returns {void}
17
17
  */
@@ -19,6 +19,15 @@ module.exports = {
19
19
  console.log(...args);
20
20
  },
21
21
 
22
+ /**
23
+ * Cover for console.warn
24
+ * @param {...any} args The elements to log.
25
+ * @returns {void}
26
+ */
27
+ warn(...args) {
28
+ console.warn(...args);
29
+ },
30
+
22
31
  /**
23
32
  * Cover for console.error
24
33
  * @param {...any} args The elements to log.
@@ -21,7 +21,7 @@ module.exports = {};
21
21
  /**
22
22
  * @typedef {Object} ParserOptions
23
23
  * @property {EcmaFeatures} [ecmaFeatures] The optional features.
24
- * @property {3|5|6|7|8|9|10|11|12|13|14|15|2015|2016|2017|2018|2019|2020|2021|2022|2023|2024} [ecmaVersion] The ECMAScript version (or revision number).
24
+ * @property {3|5|6|7|8|9|10|11|12|13|14|15|16|2015|2016|2017|2018|2019|2020|2021|2022|2023|2024|2025} [ecmaVersion] The ECMAScript version (or revision number).
25
25
  * @property {"script"|"module"} [sourceType] The source code type.
26
26
  * @property {boolean} [allowReserved] Allowing the use of reserved words as identifiers in ES3.
27
27
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "9.5.0",
3
+ "version": "9.6.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -68,9 +68,9 @@
68
68
  "dependencies": {
69
69
  "@eslint-community/eslint-utils": "^4.2.0",
70
70
  "@eslint-community/regexpp": "^4.6.1",
71
- "@eslint/config-array": "^0.16.0",
71
+ "@eslint/config-array": "^0.17.0",
72
72
  "@eslint/eslintrc": "^3.1.0",
73
- "@eslint/js": "9.5.0",
73
+ "@eslint/js": "9.6.0",
74
74
  "@humanwhocodes/module-importer": "^1.0.1",
75
75
  "@humanwhocodes/retry": "^0.3.0",
76
76
  "@nodelib/fs.walk": "^1.2.8",
@@ -81,7 +81,7 @@
81
81
  "escape-string-regexp": "^4.0.0",
82
82
  "eslint-scope": "^8.0.1",
83
83
  "eslint-visitor-keys": "^4.0.0",
84
- "espree": "^10.0.1",
84
+ "espree": "^10.1.0",
85
85
  "esquery": "^1.5.0",
86
86
  "esutils": "^2.0.2",
87
87
  "fast-deep-equal": "^3.1.3",
@@ -104,13 +104,11 @@
104
104
  "devDependencies": {
105
105
  "@babel/core": "^7.4.3",
106
106
  "@babel/preset-env": "^7.4.3",
107
- "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0",
108
107
  "@types/estree": "^1.0.5",
109
108
  "@types/node": "^20.11.5",
110
109
  "@wdio/browser-runner": "^8.38.3",
111
110
  "@wdio/cli": "^8.38.2",
112
111
  "@wdio/concise-reporter": "^8.38.2",
113
- "@wdio/globals": "^8.38.2",
114
112
  "@wdio/mocha-framework": "^8.38.2",
115
113
  "babel-loader": "^8.0.5",
116
114
  "c8": "^7.12.0",
@@ -122,11 +120,8 @@
122
120
  "eslint": "file:.",
123
121
  "eslint-config-eslint": "file:packages/eslint-config-eslint",
124
122
  "eslint-plugin-eslint-plugin": "^6.0.0",
125
- "eslint-plugin-internal-rules": "file:tools/internal-rules",
126
- "eslint-plugin-jsdoc": "^48.2.3",
127
- "eslint-plugin-n": "^17.2.0",
128
- "eslint-plugin-unicorn": "^52.0.0",
129
123
  "eslint-release": "^3.2.2",
124
+ "eslint-rule-composer": "^0.3.0",
130
125
  "eslump": "^3.0.0",
131
126
  "esprima": "^4.0.1",
132
127
  "fast-glob": "^3.2.11",
@@ -136,7 +131,7 @@
136
131
  "got": "^11.8.3",
137
132
  "gray-matter": "^4.0.3",
138
133
  "js-yaml": "^4.1.0",
139
- "knip": "^5.8.0",
134
+ "knip": "^5.21.0",
140
135
  "lint-staged": "^11.0.0",
141
136
  "load-perf": "^0.2.0",
142
137
  "markdown-it": "^12.2.0",