eslint 9.4.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.
Files changed (42) hide show
  1. package/README.md +3 -3
  2. package/conf/ecma-version.js +1 -1
  3. package/conf/globals.js +6 -1
  4. package/lib/api.js +1 -1
  5. package/lib/cli.js +23 -2
  6. package/lib/config/default-config.js +5 -0
  7. package/lib/config/flat-config-array.js +71 -8
  8. package/lib/config/flat-config-schema.js +46 -62
  9. package/lib/eslint/eslint-helpers.js +32 -17
  10. package/lib/eslint/eslint.js +30 -12
  11. package/lib/eslint/legacy-eslint.js +14 -0
  12. package/lib/languages/js/index.js +247 -0
  13. package/lib/{source-code → languages/js/source-code}/source-code.js +46 -22
  14. package/lib/languages/js/validate-language-options.js +181 -0
  15. package/lib/linter/apply-disable-directives.js +8 -3
  16. package/lib/linter/config-comment-parser.js +3 -16
  17. package/lib/linter/linter.js +291 -249
  18. package/lib/linter/report-translator.js +14 -7
  19. package/lib/linter/vfile.js +104 -0
  20. package/lib/options.js +13 -1
  21. package/lib/rule-tester/rule-tester.js +5 -2
  22. package/lib/rules/no-sparse-arrays.js +26 -3
  23. package/lib/rules/no-unused-vars.js +33 -31
  24. package/lib/shared/flags.js +26 -0
  25. package/lib/shared/logging.js +10 -1
  26. package/lib/shared/types.js +1 -1
  27. package/messages/all-matched-files-ignored.js +21 -0
  28. package/package.json +13 -18
  29. /package/lib/{source-code → languages/js/source-code}/index.js +0 -0
  30. /package/lib/{source-code → languages/js/source-code}/token-store/backward-token-comment-cursor.js +0 -0
  31. /package/lib/{source-code → languages/js/source-code}/token-store/backward-token-cursor.js +0 -0
  32. /package/lib/{source-code → languages/js/source-code}/token-store/cursor.js +0 -0
  33. /package/lib/{source-code → languages/js/source-code}/token-store/cursors.js +0 -0
  34. /package/lib/{source-code → languages/js/source-code}/token-store/decorative-cursor.js +0 -0
  35. /package/lib/{source-code → languages/js/source-code}/token-store/filter-cursor.js +0 -0
  36. /package/lib/{source-code → languages/js/source-code}/token-store/forward-token-comment-cursor.js +0 -0
  37. /package/lib/{source-code → languages/js/source-code}/token-store/forward-token-cursor.js +0 -0
  38. /package/lib/{source-code → languages/js/source-code}/token-store/index.js +0 -0
  39. /package/lib/{source-code → languages/js/source-code}/token-store/limit-cursor.js +0 -0
  40. /package/lib/{source-code → languages/js/source-code}/token-store/padded-token-cursor.js +0 -0
  41. /package/lib/{source-code → languages/js/source-code}/token-store/skip-cursor.js +0 -0
  42. /package/lib/{source-code → languages/js/source-code}/token-store/utils.js +0 -0
package/README.md CHANGED
@@ -289,13 +289,13 @@ Percy Ma
289
289
 
290
290
  ## Sponsors
291
291
 
292
- The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://opencollective.com/eslint) to get your logo on our README and website.
292
+ The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate) to get your logo on our README and website.
293
293
 
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="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/api.js CHANGED
@@ -13,7 +13,7 @@ const { ESLint, shouldUseFlatConfig } = require("./eslint/eslint");
13
13
  const { LegacyESLint } = require("./eslint/legacy-eslint");
14
14
  const { Linter } = require("./linter");
15
15
  const { RuleTester } = require("./rule-tester");
16
- const { SourceCode } = require("./source-code");
16
+ const { SourceCode } = require("./languages/js/source-code");
17
17
 
18
18
  //-----------------------------------------------------------------------------
19
19
  // Functions
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) {
@@ -20,6 +20,10 @@ exports.defaultConfig = [
20
20
  plugins: {
21
21
  "@": {
22
22
 
23
+ languages: {
24
+ js: require("../languages/js")
25
+ },
26
+
23
27
  /*
24
28
  * Because we try to delay loading rules until absolutely
25
29
  * necessary, a proxy allows us to hook into the lazy-loading
@@ -37,6 +41,7 @@ exports.defaultConfig = [
37
41
  })
38
42
  }
39
43
  },
44
+ language: "@/js",
40
45
  languageOptions: {
41
46
  sourceType: "module",
42
47
  ecmaVersion: "latest",
@@ -10,7 +10,7 @@
10
10
  //-----------------------------------------------------------------------------
11
11
 
12
12
  const { ConfigArray, ConfigArraySymbol } = require("@eslint/config-array");
13
- const { flatConfigSchema } = require("./flat-config-schema");
13
+ const { flatConfigSchema, hasMethod } = require("./flat-config-schema");
14
14
  const { RuleValidator } = require("./rule-validator");
15
15
  const { defaultConfig } = require("./default-config");
16
16
 
@@ -123,6 +123,43 @@ function wrapConfigErrorWithDetails(error, originalLength, baseLength) {
123
123
  );
124
124
  }
125
125
 
126
+ /**
127
+ * Converts a languageOptions object to a JSON representation.
128
+ * @param {Record<string, any>} languageOptions The options to create a JSON
129
+ * representation of.
130
+ * @param {string} objectKey The key of the object being converted.
131
+ * @returns {Record<string, any>} The JSON representation of the languageOptions.
132
+ * @throws {TypeError} If a function is found in the languageOptions.
133
+ */
134
+ function languageOptionsToJSON(languageOptions, objectKey = "languageOptions") {
135
+
136
+ const result = {};
137
+
138
+ for (const [key, value] of Object.entries(languageOptions)) {
139
+ if (value) {
140
+ if (typeof value === "object") {
141
+ const name = getObjectId(value);
142
+
143
+ if (name && hasMethod(value)) {
144
+ result[key] = name;
145
+ } else {
146
+ result[key] = languageOptionsToJSON(value, key);
147
+ }
148
+ continue;
149
+ }
150
+
151
+ if (typeof value === "function") {
152
+ throw new TypeError(`Cannot serialize key "${key}" in ${objectKey}: Function values are not supported.`);
153
+ }
154
+
155
+ }
156
+
157
+ result[key] = value;
158
+ }
159
+
160
+ return result;
161
+ }
162
+
126
163
  const originalBaseConfig = Symbol("originalBaseConfig");
127
164
  const originalLength = Symbol("originalLength");
128
165
  const baseLength = Symbol("baseLength");
@@ -269,10 +306,11 @@ class FlatConfigArray extends ConfigArray {
269
306
  */
270
307
  [ConfigArraySymbol.finalizeConfig](config) {
271
308
 
272
- const { plugins, languageOptions, processor } = config;
273
- let parserName, processorName;
309
+ const { plugins, language, languageOptions, processor } = config;
310
+ let parserName, processorName, languageName;
274
311
  let invalidParser = false,
275
- invalidProcessor = false;
312
+ invalidProcessor = false,
313
+ invalidLanguage = false;
276
314
 
277
315
  // Check parser value
278
316
  if (languageOptions && languageOptions.parser) {
@@ -290,6 +328,29 @@ class FlatConfigArray extends ConfigArray {
290
328
  }
291
329
  }
292
330
 
331
+ // Check language value
332
+ if (language) {
333
+ if (typeof language === "string") {
334
+ const { pluginName, objectName: localLanguageName } = splitPluginIdentifier(language);
335
+
336
+ languageName = language;
337
+
338
+ if (!plugins || !plugins[pluginName] || !plugins[pluginName].languages || !plugins[pluginName].languages[localLanguageName]) {
339
+ throw new TypeError(`Key "language": Could not find "${localLanguageName}" in plugin "${pluginName}".`);
340
+ }
341
+
342
+ config.language = plugins[pluginName].languages[localLanguageName];
343
+ } else {
344
+ invalidLanguage = true;
345
+ }
346
+
347
+ try {
348
+ config.language.validateLanguageOptions(config.languageOptions);
349
+ } catch (error) {
350
+ throw new TypeError(`Key "languageOptions": ${error.message}`, { cause: error });
351
+ }
352
+ }
353
+
293
354
  // Check processor value
294
355
  if (processor) {
295
356
  if (typeof processor === "string") {
@@ -329,6 +390,10 @@ class FlatConfigArray extends ConfigArray {
329
390
  throw new Error("Could not serialize processor object (missing 'meta' object).");
330
391
  }
331
392
 
393
+ if (invalidLanguage) {
394
+ throw new Error("Caching is not supported when language is an object.");
395
+ }
396
+
332
397
  return {
333
398
  ...this,
334
399
  plugins: Object.entries(plugins).map(([namespace, plugin]) => {
@@ -341,10 +406,8 @@ class FlatConfigArray extends ConfigArray {
341
406
 
342
407
  return `${namespace}:${pluginId}`;
343
408
  }),
344
- languageOptions: {
345
- ...languageOptions,
346
- parser: parserName
347
- },
409
+ language: languageName,
410
+ languageOptions: languageOptionsToJSON(languageOptions),
348
411
  processor: processorName
349
412
  };
350
413
  }
@@ -33,12 +33,6 @@ const ruleSeverities = new Map([
33
33
  [2, 2], ["error", 2]
34
34
  ]);
35
35
 
36
- const globalVariablesValues = new Set([
37
- true, "true", "writable", "writeable",
38
- false, "false", "readonly", "readable", null,
39
- "off"
40
- ]);
41
-
42
36
  /**
43
37
  * Check if a value is a non-null object.
44
38
  * @param {any} value The value to check.
@@ -143,6 +137,23 @@ function normalizeRuleOptions(ruleOptions) {
143
137
  return structuredClone(finalOptions);
144
138
  }
145
139
 
140
+ /**
141
+ * Determines if an object has any methods.
142
+ * @param {Object} object The object to check.
143
+ * @returns {boolean} `true` if the object has any methods.
144
+ */
145
+ function hasMethod(object) {
146
+
147
+ for (const key of Object.keys(object)) {
148
+
149
+ if (typeof object[key] === "function") {
150
+ return true;
151
+ }
152
+ }
153
+
154
+ return false;
155
+ }
156
+
146
157
  //-----------------------------------------------------------------------------
147
158
  // Assertions
148
159
  //-----------------------------------------------------------------------------
@@ -301,47 +312,48 @@ const deepObjectAssignSchema = {
301
312
  validate: "object"
302
313
  };
303
314
 
315
+
304
316
  //-----------------------------------------------------------------------------
305
317
  // High-Level Schemas
306
318
  //-----------------------------------------------------------------------------
307
319
 
308
320
  /** @type {ObjectPropertySchema} */
309
- const globalsSchema = {
310
- merge: "assign",
311
- validate(value) {
321
+ const languageOptionsSchema = {
322
+ merge(first = {}, second = {}) {
312
323
 
313
- assertIsObject(value);
324
+ const result = deepMerge(first, second);
314
325
 
315
- for (const key of Object.keys(value)) {
326
+ for (const [key, value] of Object.entries(result)) {
316
327
 
317
- // avoid hairy edge case
318
- if (key === "__proto__") {
319
- continue;
320
- }
328
+ /*
329
+ * Special case: Because the `parser` property is an object, it should
330
+ * not be deep merged. Instead, it should be replaced if it exists in
331
+ * the second object. To make this more generic, we just check for
332
+ * objects with methods and replace them if they exist in the second
333
+ * object.
334
+ */
335
+ if (isNonArrayObject(value)) {
336
+ if (hasMethod(value)) {
337
+ result[key] = second[key] ?? first[key];
338
+ continue;
339
+ }
321
340
 
322
- if (key !== key.trim()) {
323
- throw new TypeError(`Global "${key}" has leading or trailing whitespace.`);
341
+ // for other objects, make sure we aren't reusing the same object
342
+ result[key] = { ...result[key] };
343
+ continue;
324
344
  }
325
345
 
326
- if (!globalVariablesValues.has(value[key])) {
327
- throw new TypeError(`Key "${key}": Expected "readonly", "writable", or "off".`);
328
- }
329
346
  }
330
- }
347
+
348
+ return result;
349
+ },
350
+ validate: "object"
331
351
  };
332
352
 
333
353
  /** @type {ObjectPropertySchema} */
334
- const parserSchema = {
354
+ const languageSchema = {
335
355
  merge: "replace",
336
- validate(value) {
337
-
338
- if (!value || typeof value !== "object" ||
339
- (typeof value.parse !== "function" && typeof value.parseForESLint !== "function")
340
- ) {
341
- throw new TypeError("Expected object with parse() or parseForESLint() method.");
342
- }
343
-
344
- }
356
+ validate: assertIsPluginMemberName
345
357
  };
346
358
 
347
359
  /** @type {ObjectPropertySchema} */
@@ -501,28 +513,6 @@ const rulesSchema = {
501
513
  }
502
514
  };
503
515
 
504
- /** @type {ObjectPropertySchema} */
505
- const ecmaVersionSchema = {
506
- merge: "replace",
507
- validate(value) {
508
- if (typeof value === "number" || value === "latest") {
509
- return;
510
- }
511
-
512
- throw new TypeError("Expected a number or \"latest\".");
513
- }
514
- };
515
-
516
- /** @type {ObjectPropertySchema} */
517
- const sourceTypeSchema = {
518
- merge: "replace",
519
- validate(value) {
520
- if (typeof value !== "string" || !/^(?:script|module|commonjs)$/u.test(value)) {
521
- throw new TypeError("Expected \"script\", \"module\", or \"commonjs\".");
522
- }
523
- }
524
- };
525
-
526
516
  /**
527
517
  * Creates a schema that always throws an error. Useful for warning
528
518
  * about eslintrc-style keys.
@@ -568,15 +558,8 @@ const flatConfigSchema = {
568
558
  reportUnusedDisableDirectives: disableDirectiveSeveritySchema
569
559
  }
570
560
  },
571
- languageOptions: {
572
- schema: {
573
- ecmaVersion: ecmaVersionSchema,
574
- sourceType: sourceTypeSchema,
575
- globals: globalsSchema,
576
- parser: parserSchema,
577
- parserOptions: deepObjectAssignSchema
578
- }
579
- },
561
+ language: languageSchema,
562
+ languageOptions: languageOptionsSchema,
580
563
  processor: processorSchema,
581
564
  plugins: pluginsSchema,
582
565
  rules: rulesSchema
@@ -588,5 +571,6 @@ const flatConfigSchema = {
588
571
 
589
572
  module.exports = {
590
573
  flatConfigSchema,
574
+ hasMethod,
591
575
  assertIsRuleSeverity
592
576
  };
@@ -91,7 +91,7 @@ class AllFilesIgnoredError extends Error {
91
91
  */
92
92
  constructor(pattern) {
93
93
  super(`All files matched by '${pattern}' are ignored.`);
94
- this.messageTemplate = "all-files-ignored";
94
+ this.messageTemplate = "all-matched-files-ignored";
95
95
  this.messageData = { pattern };
96
96
  }
97
97
  }
@@ -494,7 +494,7 @@ async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
494
494
 
495
495
  }
496
496
 
497
- return [...new Set(filePaths)];
497
+ return filePaths;
498
498
 
499
499
  }
500
500
 
@@ -543,10 +543,7 @@ async function findFiles({
543
543
 
544
544
  // files are added directly to the list
545
545
  if (stat.isFile()) {
546
- results.push({
547
- filePath,
548
- ignored: !configs.getConfig(filePath)
549
- });
546
+ results.push(filePath);
550
547
  }
551
548
 
552
549
  // directories need extensions attached
@@ -604,11 +601,10 @@ async function findFiles({
604
601
  });
605
602
 
606
603
  return [
607
- ...results,
608
- ...globbyResults.map(filePath => ({
609
- filePath: path.resolve(filePath),
610
- ignored: false
611
- }))
604
+ ...new Set([
605
+ ...results,
606
+ ...globbyResults.map(filePath => path.resolve(filePath))
607
+ ])
612
608
  ];
613
609
  }
614
610
 
@@ -630,17 +626,31 @@ function isErrorMessage(message) {
630
626
  * Returns result with warning by ignore settings
631
627
  * @param {string} filePath File path of checked code
632
628
  * @param {string} baseDir Absolute path of base directory
629
+ * @param {"ignored"|"external"|"unconfigured"} configStatus A status that determines why the file is ignored
633
630
  * @returns {LintResult} Result with single warning
634
631
  * @private
635
632
  */
636
- function createIgnoreResult(filePath, baseDir) {
633
+ function createIgnoreResult(filePath, baseDir, configStatus) {
637
634
  let message;
638
- const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
639
635
 
640
- if (isInNodeModules) {
641
- message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
642
- } else {
643
- message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
636
+ switch (configStatus) {
637
+ case "external":
638
+ message = "File ignored because outside of base path.";
639
+ break;
640
+ case "unconfigured":
641
+ message = "File ignored because no matching configuration was supplied.";
642
+ break;
643
+ default:
644
+ {
645
+ const isInNodeModules = baseDir && path.dirname(path.relative(baseDir, filePath)).split(path.sep).includes("node_modules");
646
+
647
+ if (isInNodeModules) {
648
+ message = "File ignored by default because it is located under the node_modules directory. Use ignore pattern \"!**/node_modules/\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
649
+ } else {
650
+ message = "File ignored because of a matching ignore pattern. Use \"--no-ignore\" to disable file ignore settings or use \"--no-warn-ignored\" to suppress this warning.";
651
+ }
652
+ }
653
+ break;
644
654
  }
645
655
 
646
656
  return {
@@ -713,6 +723,7 @@ function processOptions({
713
723
  errorOnUnmatchedPattern = true,
714
724
  fix = false,
715
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 = [],
716
727
  globInputPaths = true,
717
728
  ignore = true,
718
729
  ignorePatterns = null,
@@ -800,6 +811,9 @@ function processOptions({
800
811
  if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
801
812
  errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
802
813
  }
814
+ if (!isEmptyArrayOrArrayOfNonEmptyString(flags)) {
815
+ errors.push("'flags' must be an array of non-empty strings.");
816
+ }
803
817
  if (typeof globInputPaths !== "boolean") {
804
818
  errors.push("'globInputPaths' must be a boolean.");
805
819
  }
@@ -853,6 +867,7 @@ function processOptions({
853
867
  errorOnUnmatchedPattern,
854
868
  fix,
855
869
  fixTypes,
870
+ flags: [...flags],
856
871
  globInputPaths,
857
872
  ignore,
858
873
  ignorePatterns,
@@ -68,7 +68,7 @@ const { Retrier } = require("@humanwhocodes/retry");
68
68
  * The options with which to configure the ESLint instance.
69
69
  * @typedef {Object} ESLintOptions
70
70
  * @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
71
- * @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this instance
71
+ * @property {ConfigData|Array<ConfigData>} [baseConfig] Base config, extended by all configs used with this instance
72
72
  * @property {boolean} [cache] Enable result caching.
73
73
  * @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
74
74
  * @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
@@ -76,10 +76,11 @@ 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`.
82
- * @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
83
+ * @property {ConfigData|Array<ConfigData>} [overrideConfig] Override config, overrides all configs used with this instance
83
84
  * @property {boolean|string} [overrideConfigFile] Searches for default config file when falsy;
84
85
  * doesn't do any config file lookup when `true`; considered to be a config filename
85
86
  * when a string.
@@ -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.
@@ -865,22 +879,24 @@ class ESLint {
865
879
  */
866
880
  const results = await Promise.all(
867
881
 
868
- filePaths.map(({ filePath, ignored }) => {
882
+ filePaths.map(filePath => {
883
+
884
+ const config = configs.getConfig(filePath);
869
885
 
870
886
  /*
871
- * If a filename was entered that matches an ignore
872
- * pattern, then notify the user.
887
+ * If a filename was entered that cannot be matched
888
+ * to a config, then notify the user.
873
889
  */
874
- if (ignored) {
890
+ if (!config) {
875
891
  if (warnIgnored) {
876
- return createIgnoreResult(filePath, cwd);
892
+ const configStatus = configs.getConfigStatus(filePath);
893
+
894
+ return createIgnoreResult(filePath, cwd, configStatus);
877
895
  }
878
896
 
879
897
  return void 0;
880
898
  }
881
899
 
882
- const config = configs.getConfig(filePath);
883
-
884
900
  // Skip if there is cached result.
885
901
  if (lintResultCache) {
886
902
  const cachedResult =
@@ -1029,7 +1045,9 @@ class ESLint {
1029
1045
  const shouldWarnIgnored = typeof warnIgnored === "boolean" ? warnIgnored : constructorWarnIgnored;
1030
1046
 
1031
1047
  if (shouldWarnIgnored) {
1032
- results.push(createIgnoreResult(resolvedFilename, cwd));
1048
+ const configStatus = configs.getConfigStatus(resolvedFilename);
1049
+
1050
+ results.push(createIgnoreResult(resolvedFilename, cwd, configStatus));
1033
1051
  }
1034
1052
  } else {
1035
1053
 
@@ -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.