eslint 9.17.0 → 9.19.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 (75) hide show
  1. package/README.md +27 -8
  2. package/lib/cli.js +11 -4
  3. package/lib/config/config-loader.js +9 -22
  4. package/lib/config/flat-config-schema.js +16 -1
  5. package/lib/eslint/eslint.js +2 -5
  6. package/lib/linter/linter.js +74 -5
  7. package/lib/options.js +13 -0
  8. package/lib/rules/arrow-body-style.js +1 -0
  9. package/lib/rules/camelcase.js +1 -0
  10. package/lib/rules/capitalized-comments.js +1 -0
  11. package/lib/rules/consistent-this.js +1 -0
  12. package/lib/rules/curly.js +1 -0
  13. package/lib/rules/default-case-last.js +2 -2
  14. package/lib/rules/default-param-last.js +1 -0
  15. package/lib/rules/dot-notation.js +1 -0
  16. package/lib/rules/func-name-matching.js +1 -0
  17. package/lib/rules/func-style.js +1 -0
  18. package/lib/rules/id-denylist.js +1 -0
  19. package/lib/rules/id-length.js +1 -0
  20. package/lib/rules/id-match.js +1 -0
  21. package/lib/rules/init-declarations.js +1 -0
  22. package/lib/rules/logical-assignment-operators.js +1 -0
  23. package/lib/rules/no-console.js +3 -1
  24. package/lib/rules/no-continue.js +1 -0
  25. package/lib/rules/no-div-regex.js +1 -0
  26. package/lib/rules/no-else-return.js +1 -0
  27. package/lib/rules/no-extra-boolean-cast.js +1 -0
  28. package/lib/rules/no-extra-label.js +1 -0
  29. package/lib/rules/no-implicit-coercion.js +1 -0
  30. package/lib/rules/no-inline-comments.js +1 -0
  31. package/lib/rules/no-label-var.js +1 -0
  32. package/lib/rules/no-labels.js +1 -0
  33. package/lib/rules/no-lonely-if.js +1 -0
  34. package/lib/rules/no-magic-numbers.js +1 -0
  35. package/lib/rules/no-multi-str.js +1 -0
  36. package/lib/rules/no-negated-condition.js +1 -0
  37. package/lib/rules/no-nested-ternary.js +1 -0
  38. package/lib/rules/no-plusplus.js +1 -0
  39. package/lib/rules/no-shadow-restricted-names.js +17 -7
  40. package/lib/rules/no-ternary.js +1 -0
  41. package/lib/rules/no-undef-init.js +1 -0
  42. package/lib/rules/no-undefined.js +1 -0
  43. package/lib/rules/no-underscore-dangle.js +1 -0
  44. package/lib/rules/no-unneeded-ternary.js +1 -0
  45. package/lib/rules/no-useless-computed-key.js +1 -0
  46. package/lib/rules/no-useless-concat.js +1 -0
  47. package/lib/rules/no-void.js +1 -0
  48. package/lib/rules/no-warning-comments.js +1 -0
  49. package/lib/rules/object-shorthand.js +1 -0
  50. package/lib/rules/one-var.js +1 -0
  51. package/lib/rules/operator-assignment.js +1 -0
  52. package/lib/rules/prefer-arrow-callback.js +1 -0
  53. package/lib/rules/prefer-destructuring.js +1 -0
  54. package/lib/rules/prefer-exponentiation-operator.js +1 -0
  55. package/lib/rules/prefer-numeric-literals.js +1 -0
  56. package/lib/rules/prefer-object-spread.js +1 -0
  57. package/lib/rules/prefer-spread.js +1 -0
  58. package/lib/rules/prefer-template.js +1 -0
  59. package/lib/rules/sort-imports.js +3 -2
  60. package/lib/rules/sort-keys.js +1 -0
  61. package/lib/rules/sort-vars.js +1 -0
  62. package/lib/rules/vars-on-top.js +1 -0
  63. package/lib/rules/yoda.js +1 -0
  64. package/lib/shared/flags.js +3 -3
  65. package/lib/shared/option-utils.js +56 -0
  66. package/lib/types/index.d.ts +7 -1
  67. package/lib/types/rules/best-practices.d.ts +109 -95
  68. package/lib/types/rules/deprecated.d.ts +32 -15
  69. package/lib/types/rules/ecmascript-6.d.ts +39 -39
  70. package/lib/types/rules/node-commonjs.d.ts +23 -12
  71. package/lib/types/rules/possible-errors.d.ts +86 -54
  72. package/lib/types/rules/strict-mode.d.ts +1 -1
  73. package/lib/types/rules/stylistic-issues.d.ts +105 -99
  74. package/lib/types/rules/variables.d.ts +12 -12
  75. package/package.json +8 -6
package/README.md CHANGED
@@ -1,11 +1,9 @@
1
1
  [![npm version](https://img.shields.io/npm/v/eslint.svg)](https://www.npmjs.com/package/eslint)
2
2
  [![Downloads](https://img.shields.io/npm/dm/eslint.svg)](https://www.npmjs.com/package/eslint)
3
3
  [![Build Status](https://github.com/eslint/eslint/workflows/CI/badge.svg)](https://github.com/eslint/eslint/actions)
4
- [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint?ref=badge_shield)
5
- <br />
4
+ <br>
6
5
  [![Open Collective Backers](https://img.shields.io/opencollective/backers/eslint)](https://opencollective.com/eslint)
7
6
  [![Open Collective Sponsors](https://img.shields.io/opencollective/sponsors/eslint)](https://opencollective.com/eslint)
8
- [![Follow us on Twitter](https://img.shields.io/twitter/follow/geteslint?label=Follow&style=social)](https://twitter.com/intent/user?screen_name=geteslint)
9
7
 
10
8
  # ESLint
11
9
 
@@ -17,7 +15,8 @@
17
15
  [Code of Conduct](https://eslint.org/conduct) |
18
16
  [Twitter](https://twitter.com/geteslint) |
19
17
  [Discord](https://eslint.org/chat) |
20
- [Mastodon](https://fosstodon.org/@eslint)
18
+ [Mastodon](https://fosstodon.org/@eslint) |
19
+ [Bluesky](https://bsky.app/profile/eslint.org)
21
20
 
22
21
  ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. In many ways, it is similar to JSLint and JSHint with a few exceptions:
23
22
 
@@ -203,7 +202,27 @@ This means:
203
202
 
204
203
  ## License
205
204
 
206
- [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Feslint%2Feslint?ref=badge_large)
205
+ MIT License
206
+
207
+ Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
208
+
209
+ Permission is hereby granted, free of charge, to any person obtaining a copy
210
+ of this software and associated documentation files (the "Software"), to deal
211
+ in the Software without restriction, including without limitation the rights
212
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
213
+ copies of the Software, and to permit persons to whom the Software is
214
+ furnished to do so, subject to the following conditions:
215
+
216
+ The above copyright notice and this permission notice shall be included in
217
+ all copies or substantial portions of the Software.
218
+
219
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
220
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
221
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
222
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
223
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
224
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
225
+ THE SOFTWARE.
207
226
 
208
227
  ## Team
209
228
 
@@ -298,9 +317,9 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
298
317
 
299
318
  <h3>Platinum Sponsors</h3>
300
319
  <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>
301
- <p><a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a></p><h3>Silver Sponsors</h3>
302
- <p><a href="https://www.serptriumph.com/"><img src="https://images.opencollective.com/serp-triumph5/fea3074/logo.png" alt="SERP Triumph" height="64"></a> <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>
303
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://www.wordhint.net/"><img src="https://images.opencollective.com/wordhint/be86813/avatar.png" alt="WordHint" 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.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340?v=4" alt="GitBook" 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://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465?v=4" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a></p>
320
+ <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a></p><h3>Silver Sponsors</h3>
321
+ <p><a href="https://www.serptriumph.com/"><img src="https://images.opencollective.com/serp-triumph5/fea3074/logo.png" alt="SERP Triumph" height="64"></a> <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" alt="American Express" height="64"></a></p><h3>Bronze Sponsors</h3>
322
+ <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" 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.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nolebase.ayaka.io"><img src="https://avatars.githubusercontent.com/u/11081491" alt="Neko" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a></p>
304
323
  <h3>Technology Sponsors</h3>
305
324
  Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
306
325
  <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>
package/lib/cli.js CHANGED
@@ -128,6 +128,7 @@ async function translateOptions({
128
128
  quiet,
129
129
  reportUnusedDisableDirectives,
130
130
  reportUnusedDisableDirectivesSeverity,
131
+ reportUnusedInlineConfigs,
131
132
  resolvePluginsRelativeTo,
132
133
  rule,
133
134
  rulesdir,
@@ -180,6 +181,13 @@ async function translateOptions({
180
181
  };
181
182
  }
182
183
 
184
+ if (reportUnusedInlineConfigs !== void 0) {
185
+ overrideConfig[0].linterOptions = {
186
+ ...overrideConfig[0].linterOptions,
187
+ reportUnusedInlineConfigs: normalizeSeverityToString(reportUnusedInlineConfigs)
188
+ };
189
+ }
190
+
183
191
  if (plugin) {
184
192
  overrideConfig[0].plugins = await loadPlugins(importer, plugin);
185
193
  }
@@ -341,16 +349,15 @@ const cli = {
341
349
  /**
342
350
  * Calculates the command string for the --inspect-config operation.
343
351
  * @param {string} configFile The path to the config file to inspect.
344
- * @param {boolean} hasUnstableTSConfigFlag `true` if the `unstable_ts_config` flag is enabled, `false` if it's not.
345
352
  * @returns {Promise<string>} The command string to execute.
346
353
  */
347
- async calculateInspectConfigFlags(configFile, hasUnstableTSConfigFlag) {
354
+ async calculateInspectConfigFlags(configFile) {
348
355
 
349
356
  // find the config file
350
357
  const {
351
358
  configFilePath,
352
359
  basePath
353
- } = await locateConfigFileToUse({ cwd: process.cwd(), configFile }, hasUnstableTSConfigFlag);
360
+ } = await locateConfigFileToUse({ cwd: process.cwd(), configFile });
354
361
 
355
362
  return ["--config", configFilePath, "--basePath", basePath];
356
363
  },
@@ -451,7 +458,7 @@ const cli = {
451
458
  try {
452
459
  const flatOptions = await translateOptions(options, "flat");
453
460
  const spawn = require("cross-spawn");
454
- const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile, flatOptions.flags ? flatOptions.flags.includes("unstable_ts_config") : false);
461
+ const flags = await cli.calculateInspectConfigFlags(flatOptions.overrideConfigFile);
455
462
 
456
463
  spawn.sync("npx", ["@eslint/config-inspector@latest", ...flags], { encoding: "utf8", stdio: "inherit" });
457
464
  } catch (error) {
@@ -31,7 +31,6 @@ const { FlatConfigArray } = require("./flat-config-array");
31
31
  * @property {Array<FlatConfigObject>} [defaultConfigs] The default configs to use.
32
32
  * @property {Array<string>} [ignorePatterns] The ignore patterns to use.
33
33
  * @property {FlatConfigObject|Array<FlatConfigObject>} overrideConfig The override config to use.
34
- * @property {boolean} allowTS Indicates if TypeScript configuration files are allowed.
35
34
  */
36
35
 
37
36
  //------------------------------------------------------------------------------
@@ -41,10 +40,7 @@ const { FlatConfigArray } = require("./flat-config-array");
41
40
  const FLAT_CONFIG_FILENAMES = [
42
41
  "eslint.config.js",
43
42
  "eslint.config.mjs",
44
- "eslint.config.cjs"
45
- ];
46
-
47
- const TS_FLAT_CONFIG_FILENAMES = [
43
+ "eslint.config.cjs",
48
44
  "eslint.config.ts",
49
45
  "eslint.config.mts",
50
46
  "eslint.config.cts"
@@ -119,10 +115,9 @@ function isRunningInDeno() {
119
115
  /**
120
116
  * Load the config array from the given filename.
121
117
  * @param {string} filePath The filename to load from.
122
- * @param {boolean} allowTS Indicates if TypeScript configuration files are allowed.
123
118
  * @returns {Promise<any>} The config loaded from the config file.
124
119
  */
125
- async function loadConfigFile(filePath, allowTS) {
120
+ async function loadConfigFile(filePath) {
126
121
 
127
122
  debug(`Loading config from ${filePath}`);
128
123
 
@@ -171,7 +166,7 @@ async function loadConfigFile(filePath, allowTS) {
171
166
  *
172
167
  * When Node.js supports native TypeScript imports, we can remove this check.
173
168
  */
174
- if (allowTS && isTS && !isDeno && !isBun) {
169
+ if (isTS && !isDeno && !isBun) {
175
170
 
176
171
  // eslint-disable-next-line no-use-before-define -- `ConfigLoader.loadJiti` can be overwritten for testing
177
172
  const { createJiti } = await ConfigLoader.loadJiti().catch(() => {
@@ -261,8 +256,7 @@ class ConfigLoader {
261
256
  const resultPromise = ConfigLoader.locateConfigFileToUse({
262
257
  useConfigFile: this.#options.configFile,
263
258
  cwd: this.#options.cwd,
264
- fromDirectory,
265
- allowTS: this.#options.allowTS
259
+ fromDirectory
266
260
  });
267
261
 
268
262
  // ensure `ConfigLoader.locateConfigFileToUse` is called only once for `fromDirectory`
@@ -443,15 +437,10 @@ class ConfigLoader {
443
437
  * @param {string|false|undefined} options.useConfigFile The path to the config file to use.
444
438
  * @param {string} options.cwd Path to a directory that should be considered as the current working directory.
445
439
  * @param {string} [options.fromDirectory] The directory from which to start searching. Defaults to `cwd`.
446
- * @param {boolean} options.allowTS Indicates if TypeScript configuration files are allowed.
447
440
  * @returns {Promise<{configFilePath:string|undefined,basePath:string}>} Location information for
448
441
  * the config file.
449
442
  */
450
- static async locateConfigFileToUse({ useConfigFile, cwd, fromDirectory = cwd, allowTS }) {
451
-
452
- const configFilenames = allowTS
453
- ? [...FLAT_CONFIG_FILENAMES, ...TS_FLAT_CONFIG_FILENAMES]
454
- : FLAT_CONFIG_FILENAMES;
443
+ static async locateConfigFileToUse({ useConfigFile, cwd, fromDirectory = cwd }) {
455
444
 
456
445
  // determine where to load config file from
457
446
  let configFilePath;
@@ -464,7 +453,7 @@ class ConfigLoader {
464
453
  } else if (useConfigFile !== false) {
465
454
  debug("Searching for eslint.config.js");
466
455
  configFilePath = await findUp(
467
- configFilenames,
456
+ FLAT_CONFIG_FILENAMES,
468
457
  { cwd: fromDirectory }
469
458
  );
470
459
 
@@ -497,8 +486,7 @@ class ConfigLoader {
497
486
  ignoreEnabled,
498
487
  ignorePatterns,
499
488
  overrideConfig,
500
- defaultConfigs = [],
501
- allowTS
489
+ defaultConfigs = []
502
490
  } = options;
503
491
 
504
492
  debug(`Calculating config array from config file ${configFilePath} and base path ${basePath}`);
@@ -509,7 +497,7 @@ class ConfigLoader {
509
497
  if (configFilePath) {
510
498
 
511
499
  debug(`Loading config file ${configFilePath}`);
512
- const fileConfig = await loadConfigFile(configFilePath, allowTS);
500
+ const fileConfig = await loadConfigFile(configFilePath);
513
501
 
514
502
  if (Array.isArray(fileConfig)) {
515
503
  configs.push(...fileConfig);
@@ -618,8 +606,7 @@ class LegacyConfigLoader extends ConfigLoader {
618
606
  if (!this.#configFilePath) {
619
607
  this.#configFilePath = ConfigLoader.locateConfigFileToUse({
620
608
  useConfigFile: this.#options.configFile,
621
- cwd: this.#options.cwd,
622
- allowTS: this.#options.allowTS
609
+ cwd: this.#options.cwd
623
610
  });
624
611
  }
625
612
 
@@ -304,6 +304,20 @@ const disableDirectiveSeveritySchema = {
304
304
  }
305
305
  };
306
306
 
307
+ /** @type {ObjectPropertySchema} */
308
+ const unusedInlineConfigsSeveritySchema = {
309
+ merge(first, second) {
310
+ const value = second === void 0 ? first : second;
311
+
312
+ return normalizeSeverityToNumber(value);
313
+ },
314
+ validate(value) {
315
+ if (!ALLOWED_SEVERITIES.has(value)) {
316
+ throw new TypeError("Expected one of: \"error\", \"warn\", \"off\", 0, 1, or 2.");
317
+ }
318
+ }
319
+ };
320
+
307
321
  /** @type {ObjectPropertySchema} */
308
322
  const deepObjectAssignSchema = {
309
323
  merge(first = {}, second = {}) {
@@ -555,7 +569,8 @@ const flatConfigSchema = {
555
569
  linterOptions: {
556
570
  schema: {
557
571
  noInlineConfig: booleanSchema,
558
- reportUnusedDisableDirectives: disableDirectiveSeveritySchema
572
+ reportUnusedDisableDirectives: disableDirectiveSeveritySchema,
573
+ reportUnusedInlineConfigs: unusedInlineConfigsSeveritySchema
559
574
  }
560
575
  },
561
576
  language: languageSchema,
@@ -266,15 +266,13 @@ function compareResultsByFilePath(a, b) {
266
266
  * This function is used primarily by the `--inspect-config` option. For now,
267
267
  * we will maintain the existing behavior, which is to search up from the cwd.
268
268
  * @param {ESLintOptions} options The ESLint instance options.
269
- * @param {boolean} allowTS `true` if the `unstable_ts_config` flag is enabled, `false` if it's not.
270
269
  * @returns {Promise<{configFilePath:string|undefined;basePath:string}>} Location information for
271
270
  * the config file.
272
271
  */
273
- async function locateConfigFileToUse({ configFile, cwd }, allowTS) {
272
+ async function locateConfigFileToUse({ configFile, cwd }) {
274
273
 
275
274
  const configLoader = new ConfigLoader({
276
275
  cwd,
277
- allowTS,
278
276
  configFile
279
277
  });
280
278
 
@@ -469,8 +467,7 @@ class ESLint {
469
467
  configFile: processedOptions.configFile,
470
468
  ignoreEnabled: processedOptions.ignore,
471
469
  ignorePatterns: processedOptions.ignorePatterns,
472
- defaultConfigs,
473
- allowTS: processedOptions.flags.includes("unstable_ts_config")
470
+ defaultConfigs
474
471
  };
475
472
 
476
473
  this.#configLoader = processedOptions.flags.includes("unstable_config_lookup_from_file")
@@ -40,7 +40,7 @@ const { FlatConfigArray } = require("../config/flat-config-array");
40
40
  const { startTime, endTime } = require("../shared/stats");
41
41
  const { RuleValidator } = require("../config/rule-validator");
42
42
  const { assertIsRuleSeverity } = require("../config/flat-config-schema");
43
- const { normalizeSeverityToString } = require("../shared/severity");
43
+ const { normalizeSeverityToString, normalizeSeverityToNumber } = require("../shared/severity");
44
44
  const { deepMergeArrays } = require("../shared/deep-merge-arrays");
45
45
  const jslang = require("../languages/js");
46
46
  const { activeFlags, inactiveFlags } = require("../shared/flags");
@@ -56,6 +56,7 @@ const { VFile } = require("./vfile");
56
56
  const { ParserService } = require("../services/parser-service");
57
57
  const { FileContext } = require("./file-context");
58
58
  const { ProcessorService } = require("../services/processor-service");
59
+ const { containsDifferentProperty } = require("../shared/option-utils");
59
60
  const STEP_KIND_VISIT = 1;
60
61
  const STEP_KIND_CALL = 2;
61
62
 
@@ -76,6 +77,7 @@ const STEP_KIND_CALL = 2;
76
77
  /** @typedef {import("@eslint/core").Language} Language */
77
78
  /** @typedef {import("@eslint/core").RuleSeverity} RuleSeverity */
78
79
  /** @typedef {import("@eslint/core").RuleConfig} RuleConfig */
80
+ /** @typedef {import("../types").Linter.StringSeverity} StringSeverity */
79
81
 
80
82
 
81
83
  /* eslint-disable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/4#issuecomment-778805577 */
@@ -141,7 +143,8 @@ const STEP_KIND_CALL = 2;
141
143
  /**
142
144
  * @typedef {Object} InternalOptions
143
145
  * @property {string | null} warnInlineConfig The config name what `noInlineConfig` setting came from. If `noInlineConfig` setting didn't exist, this is null. If this is a config name, then the linter warns directive comments.
144
- * @property {"off" | "warn" | "error"} reportUnusedDisableDirectives (boolean values were normalized)
146
+ * @property {StringSeverity} reportUnusedDisableDirectives Severity to report unused disable directives, if not "off" (boolean values were normalized).
147
+ * @property {StringSeverity} reportUnusedInlineConfigs Severity to report unused inline configs, if not "off".
145
148
  */
146
149
 
147
150
  //------------------------------------------------------------------------------
@@ -314,6 +317,62 @@ function createLintingProblem(options) {
314
317
  };
315
318
  }
316
319
 
320
+ /**
321
+ * Wraps the value in an Array if it isn't already one.
322
+ * @template T
323
+ * @param {T|T[]} value Value to be wrapped.
324
+ * @returns {Array} The value as an array.
325
+ */
326
+ function asArray(value) {
327
+ return Array.isArray(value) ? value : [value];
328
+ }
329
+
330
+ /**
331
+ * Pushes a problem to inlineConfigProblems if ruleOptions are redundant.
332
+ * @param {ConfigData} config Provided config.
333
+ * @param {Object} loc A line/column location
334
+ * @param {Array} problems Problems that may be added to.
335
+ * @param {string} ruleId The rule ID.
336
+ * @param {Array} ruleOptions The rule options, merged with the config's.
337
+ * @param {Array} ruleOptionsInline The rule options from the comment.
338
+ * @param {"error"|"warn"} severity The severity to report.
339
+ * @returns {void}
340
+ */
341
+ function addProblemIfSameSeverityAndOptions(config, loc, problems, ruleId, ruleOptions, ruleOptionsInline, severity) {
342
+ const existingConfigRaw = config.rules?.[ruleId];
343
+ const existingConfig = existingConfigRaw ? asArray(existingConfigRaw) : ["off"];
344
+ const existingSeverity = normalizeSeverityToString(existingConfig[0]);
345
+ const inlineSeverity = normalizeSeverityToString(ruleOptions[0]);
346
+ const sameSeverity = existingSeverity === inlineSeverity;
347
+
348
+ if (!sameSeverity) {
349
+ return;
350
+ }
351
+
352
+ const alreadyConfigured = existingConfigRaw
353
+ ? `is already configured to '${existingSeverity}'`
354
+ : "is not enabled so can't be turned off";
355
+ let message;
356
+
357
+ if ((existingConfig.length === 1 && ruleOptions.length === 1) || existingSeverity === "off") {
358
+ message = `Unused inline config ('${ruleId}' ${alreadyConfigured}).`;
359
+ } else if (!containsDifferentProperty(ruleOptions.slice(1), existingConfig.slice(1))) {
360
+ message = ruleOptionsInline.length === 1
361
+ ? `Unused inline config ('${ruleId}' ${alreadyConfigured}).`
362
+ : `Unused inline config ('${ruleId}' ${alreadyConfigured} with the same options).`;
363
+ }
364
+
365
+ if (message) {
366
+ problems.push(createLintingProblem({
367
+ ruleId: null,
368
+ message,
369
+ loc,
370
+ language: config.language,
371
+ severity: normalizeSeverityToNumber(severity)
372
+ }));
373
+ }
374
+ }
375
+
317
376
  /**
318
377
  * Creates a collection of disable directives from a comment
319
378
  * @param {Object} options to create disable directives
@@ -517,7 +576,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
517
576
  return;
518
577
  }
519
578
 
520
- let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
579
+ let ruleOptions = asArray(ruleValue);
521
580
 
522
581
  /*
523
582
  * If the rule was already configured, inline rule configuration that
@@ -555,7 +614,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig, config)
555
614
  */
556
615
  ruleOptions = [
557
616
  ruleOptions[0], // severity from the inline config
558
- ...Array.isArray(config.rules[name]) ? config.rules[name].slice(1) : [] // options from the provided config
617
+ ...asArray(config.rules[name]).slice(1) // options from the provided config
559
618
  ];
560
619
  }
561
620
 
@@ -774,6 +833,8 @@ function normalizeVerifyOptions(providedOptions, config) {
774
833
  }
775
834
  }
776
835
 
836
+ const reportUnusedInlineConfigs = linterOptions.reportUnusedInlineConfigs === void 0 ? "off" : normalizeSeverityToString(linterOptions.reportUnusedInlineConfigs);
837
+
777
838
  let ruleFilter = providedOptions.ruleFilter;
778
839
 
779
840
  if (typeof ruleFilter !== "function") {
@@ -787,6 +848,7 @@ function normalizeVerifyOptions(providedOptions, config) {
787
848
  ? `your config${configNameOfNoInlineConfig}`
788
849
  : null,
789
850
  reportUnusedDisableDirectives,
851
+ reportUnusedInlineConfigs,
790
852
  disableFixes: Boolean(providedOptions.disableFixes),
791
853
  stats: providedOptions.stats,
792
854
  ruleFilter
@@ -1787,7 +1849,8 @@ class Linter {
1787
1849
 
1788
1850
  try {
1789
1851
 
1790
- let ruleOptions = Array.isArray(ruleValue) ? ruleValue : [ruleValue];
1852
+ const ruleOptionsInline = asArray(ruleValue);
1853
+ let ruleOptions = ruleOptionsInline;
1791
1854
 
1792
1855
  assertIsRuleSeverity(ruleId, ruleOptions[0]);
1793
1856
 
@@ -1850,6 +1913,12 @@ class Linter {
1850
1913
  }
1851
1914
  }
1852
1915
 
1916
+ if (options.reportUnusedInlineConfigs !== "off") {
1917
+ addProblemIfSameSeverityAndOptions(
1918
+ config, loc, inlineConfigProblems, ruleId, ruleOptions, ruleOptionsInline, options.reportUnusedInlineConfigs
1919
+ );
1920
+ }
1921
+
1853
1922
  if (shouldValidateOptions) {
1854
1923
  ruleValidator.validate({
1855
1924
  plugins: config.plugins,
package/lib/options.js CHANGED
@@ -187,6 +187,18 @@ module.exports = function(usingFlatConfig) {
187
187
  };
188
188
  }
189
189
 
190
+ let reportUnusedInlineConfigsFlag;
191
+
192
+ if (usingFlatConfig) {
193
+ reportUnusedInlineConfigsFlag = {
194
+ option: "report-unused-inline-configs",
195
+ type: "String",
196
+ default: void 0,
197
+ description: "Adds reported errors for unused eslint inline config comments",
198
+ enum: ["off", "warn", "error", "0", "1", "2"]
199
+ };
200
+ }
201
+
190
202
  return optionator({
191
203
  prepend: "eslint [options] file.js [file.js] [dir]",
192
204
  defaults: {
@@ -350,6 +362,7 @@ module.exports = function(usingFlatConfig) {
350
362
  description: "Chooses severity level for reporting unused eslint-disable and eslint-enable directives",
351
363
  enum: ["off", "warn", "error", "0", "1", "2"]
352
364
  },
365
+ reportUnusedInlineConfigsFlag,
353
366
  {
354
367
  heading: "Caching"
355
368
  },
@@ -24,6 +24,7 @@ module.exports = {
24
24
  docs: {
25
25
  description: "Require braces around arrow function bodies",
26
26
  recommended: false,
27
+ frozen: true,
27
28
  url: "https://eslint.org/docs/latest/rules/arrow-body-style"
28
29
  },
29
30
 
@@ -31,6 +31,7 @@ module.exports = {
31
31
  docs: {
32
32
  description: "Enforce camelcase naming convention",
33
33
  recommended: false,
34
+ frozen: true,
34
35
  url: "https://eslint.org/docs/latest/rules/camelcase"
35
36
  },
36
37
 
@@ -107,6 +107,7 @@ module.exports = {
107
107
  docs: {
108
108
  description: "Enforce or disallow capitalization of the first letter of a comment",
109
109
  recommended: false,
110
+ frozen: true,
110
111
  url: "https://eslint.org/docs/latest/rules/capitalized-comments"
111
112
  },
112
113
 
@@ -16,6 +16,7 @@ module.exports = {
16
16
  docs: {
17
17
  description: "Enforce consistent naming when capturing the current execution context",
18
18
  recommended: false,
19
+ frozen: true,
19
20
  url: "https://eslint.org/docs/latest/rules/consistent-this"
20
21
  },
21
22
 
@@ -22,6 +22,7 @@ module.exports = {
22
22
  docs: {
23
23
  description: "Enforce consistent brace style for all control statements",
24
24
  recommended: false,
25
+ frozen: true,
25
26
  url: "https://eslint.org/docs/latest/rules/curly"
26
27
  },
27
28
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @fileoverview Rule to enforce `default` clauses in switch statements to be last
2
+ * @fileoverview Rule to enforce `default` clauses in `switch` statements to be last
3
3
  * @author Milos Djermanovic
4
4
  */
5
5
 
@@ -15,7 +15,7 @@ module.exports = {
15
15
  type: "suggestion",
16
16
 
17
17
  docs: {
18
- description: "Enforce `default` clauses in switch statements to be last",
18
+ description: "Enforce `default` clauses in `switch` statements to be last",
19
19
  recommended: false,
20
20
  url: "https://eslint.org/docs/latest/rules/default-case-last"
21
21
  },
@@ -13,6 +13,7 @@ module.exports = {
13
13
  docs: {
14
14
  description: "Enforce default parameters to be last",
15
15
  recommended: false,
16
+ frozen: true,
16
17
  url: "https://eslint.org/docs/latest/rules/default-param-last"
17
18
  },
18
19
 
@@ -33,6 +33,7 @@ module.exports = {
33
33
  docs: {
34
34
  description: "Enforce dot notation whenever possible",
35
35
  recommended: false,
36
+ frozen: true,
36
37
  url: "https://eslint.org/docs/latest/rules/dot-notation"
37
38
  },
38
39
 
@@ -76,6 +76,7 @@ module.exports = {
76
76
  docs: {
77
77
  description: "Require function names to match the name of the variable or property to which they are assigned",
78
78
  recommended: false,
79
+ frozen: true,
79
80
  url: "https://eslint.org/docs/latest/rules/func-name-matching"
80
81
  },
81
82
 
@@ -21,6 +21,7 @@ module.exports = {
21
21
  docs: {
22
22
  description: "Enforce the consistent use of either `function` declarations or expressions assigned to variables",
23
23
  recommended: false,
24
+ frozen: true,
24
25
  url: "https://eslint.org/docs/latest/rules/func-style"
25
26
  },
26
27
 
@@ -109,6 +109,7 @@ module.exports = {
109
109
  docs: {
110
110
  description: "Disallow specified identifiers",
111
111
  recommended: false,
112
+ frozen: true,
112
113
  url: "https://eslint.org/docs/latest/rules/id-denylist"
113
114
  },
114
115
 
@@ -32,6 +32,7 @@ module.exports = {
32
32
  docs: {
33
33
  description: "Enforce minimum and maximum identifier lengths",
34
34
  recommended: false,
35
+ frozen: true,
35
36
  url: "https://eslint.org/docs/latest/rules/id-length"
36
37
  },
37
38
 
@@ -30,6 +30,7 @@ module.exports = {
30
30
  docs: {
31
31
  description: "Require identifiers to match a specified regular expression",
32
32
  recommended: false,
33
+ frozen: true,
33
34
  url: "https://eslint.org/docs/latest/rules/id-match"
34
35
  },
35
36
 
@@ -50,6 +50,7 @@ module.exports = {
50
50
  docs: {
51
51
  description: "Require or disallow initialization in variable declarations",
52
52
  recommended: false,
53
+ frozen: true,
53
54
  url: "https://eslint.org/docs/latest/rules/init-declarations"
54
55
  },
55
56
 
@@ -186,6 +186,7 @@ module.exports = {
186
186
  docs: {
187
187
  description: "Require or disallow logical assignment operator shorthand",
188
188
  recommended: false,
189
+ frozen: true,
189
190
  url: "https://eslint.org/docs/latest/rules/logical-assignment-operators"
190
191
  },
191
192
 
@@ -49,6 +49,7 @@ module.exports = {
49
49
 
50
50
  messages: {
51
51
  unexpected: "Unexpected console statement.",
52
+ limited: "Unexpected console statement. Only these console methods are allowed: {{ allowed }}.",
52
53
  removeConsole: "Remove the console.{{ propertyName }}()."
53
54
  }
54
55
  },
@@ -169,7 +170,8 @@ module.exports = {
169
170
  context.report({
170
171
  node,
171
172
  loc: node.loc,
172
- messageId: "unexpected",
173
+ messageId: allowed.length ? "limited" : "unexpected",
174
+ data: { allowed: allowed.join(", ") },
173
175
  suggest: canProvideSuggestions(node)
174
176
  ? [{
175
177
  messageId: "removeConsole",
@@ -17,6 +17,7 @@ module.exports = {
17
17
  docs: {
18
18
  description: "Disallow `continue` statements",
19
19
  recommended: false,
20
+ frozen: true,
20
21
  url: "https://eslint.org/docs/latest/rules/no-continue"
21
22
  },
22
23