eslint 9.5.0 → 9.7.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></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="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
+ <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-->
@@ -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.
@@ -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) {
@@ -1099,7 +1105,7 @@ class SourceCode extends TokenStore {
1099
1105
  /**
1100
1106
  * Applies configuration found inside of the source code. This method is only
1101
1107
  * called when ESLint is running with inline configuration allowed.
1102
- * @returns {{problems:Array<Problem>,configs:{config:FlatConfigArray,node:ASTNode}}} Information
1108
+ * @returns {{problems:Array<Problem>,configs:{config:FlatConfigArray,loc:Location}}} Information
1103
1109
  * that ESLint needs to further process the inline configuration.
1104
1110
  */
1105
1111
  applyInlineConfig() {
@@ -1147,17 +1153,21 @@ class SourceCode extends TokenStore {
1147
1153
  break;
1148
1154
 
1149
1155
  case "eslint": {
1150
- const parseResult = commentParser.parseJsonConfig(directiveValue, comment.loc);
1156
+ const parseResult = commentParser.parseJsonConfig(directiveValue);
1151
1157
 
1152
1158
  if (parseResult.success) {
1153
1159
  configs.push({
1154
1160
  config: {
1155
1161
  rules: parseResult.config
1156
1162
  },
1157
- node: comment
1163
+ loc: comment.loc
1158
1164
  });
1159
1165
  } else {
1160
- problems.push(parseResult.error);
1166
+ problems.push({
1167
+ ruleId: null,
1168
+ loc: comment.loc,
1169
+ message: parseResult.error.message
1170
+ });
1161
1171
  }
1162
1172
 
1163
1173
  break;
@@ -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
  */
@@ -418,7 +421,7 @@ function applyDirectives(options) {
418
421
  * @param {{ruleId: (string|null), line: number, column: number}[]} options.problems
419
422
  * A list of problems reported by rules, sorted by increasing location in the file, with one-based columns.
420
423
  * @param {"off" | "warn" | "error"} options.reportUnusedDisableDirectives If `"warn"` or `"error"`, adds additional problems for unused directives
421
- * @param {Object} options.configuredRules The rules configuration.
424
+ * @param {RulesConfig} options.configuredRules The rules configuration.
422
425
  * @param {Function} options.ruleFilter A predicate function to filter which rules should be executed.
423
426
  * @param {boolean} options.disableFixes If true, it doesn't make `fix` properties.
424
427
  * @returns {{ruleId: (string|null), line: number, column: number, suppressions?: {kind: string, justification: string}}[]}
@@ -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