eslint 8.55.0 → 8.57.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
@@ -103,7 +103,7 @@ We are now at or near 100% compatibility with JSCS. If you try ESLint and believ
103
103
 
104
104
  ### Does Prettier replace ESLint?
105
105
 
106
- No, ESLint does both traditional linting (looking for problematic patterns) and style checking (enforcement of conventions). You can use ESLint for everything, or you can combine both using Prettier to format your code and ESLint to catch possible errors.
106
+ No, ESLint and Prettier have diffent jobs: ESLint is a linter (looking for problematic patterns) and Prettier is a code formatter. Using both tools is common, refer to [Prettier's documentation](https://prettier.io/docs/en/install#eslint-and-other-linters) to learn how to configure them to work well with each other.
107
107
 
108
108
  ### Why can't ESLint find my plugins?
109
109
 
@@ -209,12 +209,12 @@ The people who manage releases, review feature requests, and meet regularly to e
209
209
 
210
210
  <table><tbody><tr><td align="center" valign="top" width="11%">
211
211
  <a href="https://github.com/nzakas">
212
- <img src="https://github.com/nzakas.png?s=75" width="75" height="75"><br />
212
+ <img src="https://github.com/nzakas.png?s=75" width="75" height="75" alt="Nicholas C. Zakas's Avatar"><br />
213
213
  Nicholas C. Zakas
214
214
  </a>
215
215
  </td><td align="center" valign="top" width="11%">
216
216
  <a href="https://github.com/mdjermanovic">
217
- <img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75"><br />
217
+ <img src="https://github.com/mdjermanovic.png?s=75" width="75" height="75" alt="Milos Djermanovic's Avatar"><br />
218
218
  Milos Djermanovic
219
219
  </a>
220
220
  </td></tr></tbody></table>
@@ -225,12 +225,12 @@ The people who review and implement new features.
225
225
 
226
226
  <table><tbody><tr><td align="center" valign="top" width="11%">
227
227
  <a href="https://github.com/aladdin-add">
228
- <img src="https://github.com/aladdin-add.png?s=75" width="75" height="75"><br />
228
+ <img src="https://github.com/aladdin-add.png?s=75" width="75" height="75" alt="唯然's Avatar"><br />
229
229
  唯然
230
230
  </a>
231
231
  </td><td align="center" valign="top" width="11%">
232
232
  <a href="https://github.com/snitin315">
233
- <img src="https://github.com/snitin315.png?s=75" width="75" height="75"><br />
233
+ <img src="https://github.com/snitin315.png?s=75" width="75" height="75" alt="Nitin Kumar's Avatar"><br />
234
234
  Nitin Kumar
235
235
  </a>
236
236
  </td></tr></tbody></table>
@@ -241,22 +241,22 @@ The people who review and fix bugs and help triage issues.
241
241
 
242
242
  <table><tbody><tr><td align="center" valign="top" width="11%">
243
243
  <a href="https://github.com/bmish">
244
- <img src="https://github.com/bmish.png?s=75" width="75" height="75"><br />
244
+ <img src="https://github.com/bmish.png?s=75" width="75" height="75" alt="Bryan Mishkin's Avatar"><br />
245
245
  Bryan Mishkin
246
246
  </a>
247
247
  </td><td align="center" valign="top" width="11%">
248
248
  <a href="https://github.com/fasttime">
249
- <img src="https://github.com/fasttime.png?s=75" width="75" height="75"><br />
249
+ <img src="https://github.com/fasttime.png?s=75" width="75" height="75" alt="Francesco Trotta's Avatar"><br />
250
250
  Francesco Trotta
251
251
  </a>
252
252
  </td><td align="center" valign="top" width="11%">
253
253
  <a href="https://github.com/ota-meshi">
254
- <img src="https://github.com/ota-meshi.png?s=75" width="75" height="75"><br />
254
+ <img src="https://github.com/ota-meshi.png?s=75" width="75" height="75" alt="Yosuke Ota's Avatar"><br />
255
255
  Yosuke Ota
256
256
  </a>
257
257
  </td><td align="center" valign="top" width="11%">
258
258
  <a href="https://github.com/Tanujkanti4441">
259
- <img src="https://github.com/Tanujkanti4441.png?s=75" width="75" height="75"><br />
259
+ <img src="https://github.com/Tanujkanti4441.png?s=75" width="75" height="75" alt="Tanuj Kanti's Avatar"><br />
260
260
  Tanuj Kanti
261
261
  </a>
262
262
  </td></tr></tbody></table>
@@ -267,17 +267,17 @@ Team members who focus specifically on eslint.org
267
267
 
268
268
  <table><tbody><tr><td align="center" valign="top" width="11%">
269
269
  <a href="https://github.com/amareshsm">
270
- <img src="https://github.com/amareshsm.png?s=75" width="75" height="75"><br />
270
+ <img src="https://github.com/amareshsm.png?s=75" width="75" height="75" alt="Amaresh S M's Avatar"><br />
271
271
  Amaresh S M
272
272
  </a>
273
273
  </td><td align="center" valign="top" width="11%">
274
274
  <a href="https://github.com/harish-sethuraman">
275
- <img src="https://github.com/harish-sethuraman.png?s=75" width="75" height="75"><br />
275
+ <img src="https://github.com/harish-sethuraman.png?s=75" width="75" height="75" alt="Strek's Avatar"><br />
276
276
  Strek
277
277
  </a>
278
278
  </td><td align="center" valign="top" width="11%">
279
279
  <a href="https://github.com/kecrily">
280
- <img src="https://github.com/kecrily.png?s=75" width="75" height="75"><br />
280
+ <img src="https://github.com/kecrily.png?s=75" width="75" height="75" alt="Percy Ma's Avatar"><br />
281
281
  Percy Ma
282
282
  </a>
283
283
  </td></tr></tbody></table>
@@ -293,8 +293,8 @@ The following companies, organizations, and individuals support ESLint's ongoing
293
293
  <h3>Platinum Sponsors</h3>
294
294
  <p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <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>
295
295
  <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>
296
- <p><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></p><h3>Bronze Sponsors</h3>
297
- <p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" 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://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" 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://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a></p>
296
+ <p><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>
297
+ <p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" 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://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" 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></p>
298
298
  <!--sponsorsend-->
299
299
 
300
300
  ## Technology Sponsors
package/lib/api.js CHANGED
@@ -9,17 +9,45 @@
9
9
  // Requirements
10
10
  //-----------------------------------------------------------------------------
11
11
 
12
- const { ESLint } = require("./eslint");
12
+ const { ESLint, FlatESLint } = require("./eslint");
13
+ const { shouldUseFlatConfig } = require("./eslint/flat-eslint");
13
14
  const { Linter } = require("./linter");
14
15
  const { RuleTester } = require("./rule-tester");
15
16
  const { SourceCode } = require("./source-code");
16
17
 
18
+ //-----------------------------------------------------------------------------
19
+ // Functions
20
+ //-----------------------------------------------------------------------------
21
+
22
+ /**
23
+ * Loads the correct ESLint constructor given the options.
24
+ * @param {Object} [options] The options object
25
+ * @param {boolean} [options.useFlatConfig] Whether or not to use a flat config
26
+ * @param {string} [options.cwd] The current working directory
27
+ * @returns {Promise<ESLint|LegacyESLint>} The ESLint constructor
28
+ */
29
+ async function loadESLint({ useFlatConfig, cwd = process.cwd() } = {}) {
30
+
31
+ /*
32
+ * Note: The v9.x version of this function doesn't have a cwd option
33
+ * because it's not used. It's only used in the v8.x version of this
34
+ * function.
35
+ */
36
+
37
+ const shouldESLintUseFlatConfig = typeof useFlatConfig === "boolean"
38
+ ? useFlatConfig
39
+ : await shouldUseFlatConfig({ cwd });
40
+
41
+ return shouldESLintUseFlatConfig ? FlatESLint : ESLint;
42
+ }
43
+
17
44
  //-----------------------------------------------------------------------------
18
45
  // Exports
19
46
  //-----------------------------------------------------------------------------
20
47
 
21
48
  module.exports = {
22
49
  Linter,
50
+ loadESLint,
23
51
  ESLint,
24
52
  RuleTester,
25
53
  SourceCode
@@ -83,7 +83,7 @@ const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
83
83
  * @property {string[]} [plugins] An array of plugins to load.
84
84
  * @property {Record<string,RuleConf>} [rules] An object of rules to use.
85
85
  * @property {string[]} [rulePaths] An array of directories to load custom rules from.
86
- * @property {boolean} [reportUnusedDisableDirectives] `true` adds reports for unused eslint-disable directives
86
+ * @property {boolean|string} [reportUnusedDisableDirectives] `true`, `"error"` or '"warn"' adds reports for unused eslint-disable directives
87
87
  * @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.
88
88
  * @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD
89
89
  */
@@ -224,7 +224,7 @@ function calculateStatsPerRun(results) {
224
224
  * @param {ConfigArray} config.config The config.
225
225
  * @param {boolean} config.fix If `true` then it does fix.
226
226
  * @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
227
- * @param {boolean} config.reportUnusedDisableDirectives If `true` then it reports unused `eslint-disable` comments.
227
+ * @param {boolean|string} config.reportUnusedDisableDirectives If `true`, `"error"` or '"warn"', then it reports unused `eslint-disable` comments.
228
228
  * @param {FileEnumerator} config.fileEnumerator The file enumerator to check if a path is a target or not.
229
229
  * @param {Linter} config.linter The linter instance to verify.
230
230
  * @returns {LintResult} The result of linting.
package/lib/cli.js CHANGED
@@ -22,7 +22,8 @@ const fs = require("fs"),
22
22
  { FlatESLint, shouldUseFlatConfig } = require("./eslint/flat-eslint"),
23
23
  createCLIOptions = require("./options"),
24
24
  log = require("./shared/logging"),
25
- RuntimeInfo = require("./shared/runtime-info");
25
+ RuntimeInfo = require("./shared/runtime-info"),
26
+ { normalizeSeverityToString } = require("./shared/severity");
26
27
  const { Legacy: { naming } } = require("@eslint/eslintrc");
27
28
  const { ModuleImporter } = require("@humanwhocodes/module-importer");
28
29
 
@@ -89,6 +90,7 @@ async function translateOptions({
89
90
  plugin,
90
91
  quiet,
91
92
  reportUnusedDisableDirectives,
93
+ reportUnusedDisableDirectivesSeverity,
92
94
  resolvePluginsRelativeTo,
93
95
  rule,
94
96
  rulesdir,
@@ -125,6 +127,14 @@ async function translateOptions({
125
127
  rules: rule ? rule : {}
126
128
  }];
127
129
 
130
+ if (reportUnusedDisableDirectives || reportUnusedDisableDirectivesSeverity !== void 0) {
131
+ overrideConfig[0].linterOptions = {
132
+ reportUnusedDisableDirectives: reportUnusedDisableDirectives
133
+ ? "error"
134
+ : normalizeSeverityToString(reportUnusedDisableDirectivesSeverity)
135
+ };
136
+ }
137
+
128
138
  if (parser) {
129
139
  overrideConfig[0].languageOptions.parser = await importer.import(parser);
130
140
  }
@@ -177,8 +187,7 @@ async function translateOptions({
177
187
  fixTypes: fixType,
178
188
  ignore,
179
189
  overrideConfig,
180
- overrideConfigFile,
181
- reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0
190
+ overrideConfigFile
182
191
  };
183
192
 
184
193
  if (configType === "flat") {
@@ -190,6 +199,11 @@ async function translateOptions({
190
199
  options.useEslintrc = eslintrc;
191
200
  options.extensions = ext;
192
201
  options.ignorePath = ignorePath;
202
+ if (reportUnusedDisableDirectives || reportUnusedDisableDirectivesSeverity !== void 0) {
203
+ options.reportUnusedDisableDirectives = reportUnusedDisableDirectives
204
+ ? "error"
205
+ : normalizeSeverityToString(reportUnusedDisableDirectivesSeverity);
206
+ }
193
207
  }
194
208
 
195
209
  return options;
@@ -386,6 +400,11 @@ const cli = {
386
400
  return 2;
387
401
  }
388
402
 
403
+ if (options.reportUnusedDisableDirectives && options.reportUnusedDisableDirectivesSeverity !== void 0) {
404
+ log.error("The --report-unused-disable-directives option and the --report-unused-disable-directives-severity option cannot be used together.");
405
+ return 2;
406
+ }
407
+
389
408
  const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint;
390
409
 
391
410
  const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
@@ -14,6 +14,7 @@
14
14
  * starting in Node.js v17.
15
15
  */
16
16
  const structuredClone = require("@ungap/structured-clone").default;
17
+ const { normalizeSeverityToNumber } = require("../shared/severity");
17
18
 
18
19
  //-----------------------------------------------------------------------------
19
20
  // Type Definitions
@@ -52,6 +53,15 @@ function isNonNullObject(value) {
52
53
  return typeof value === "object" && value !== null;
53
54
  }
54
55
 
56
+ /**
57
+ * Check if a value is a non-null non-array object.
58
+ * @param {any} value The value to check.
59
+ * @returns {boolean} `true` if the value is a non-null non-array object.
60
+ */
61
+ function isNonArrayObject(value) {
62
+ return isNonNullObject(value) && !Array.isArray(value);
63
+ }
64
+
55
65
  /**
56
66
  * Check if a value is undefined.
57
67
  * @param {any} value The value to check.
@@ -62,19 +72,27 @@ function isUndefined(value) {
62
72
  }
63
73
 
64
74
  /**
65
- * Deeply merges two objects.
75
+ * Deeply merges two non-array objects.
66
76
  * @param {Object} first The base object.
67
77
  * @param {Object} second The overrides object.
78
+ * @param {Map<string, Map<string, Object>>} [mergeMap] Maps the combination of first and second arguments to a merged result.
68
79
  * @returns {Object} An object with properties from both first and second.
69
80
  */
70
- function deepMerge(first = {}, second = {}) {
81
+ function deepMerge(first, second, mergeMap = new Map()) {
71
82
 
72
- /*
73
- * If the second value is an array, just return it. We don't merge
74
- * arrays because order matters and we can't know the correct order.
75
- */
76
- if (Array.isArray(second)) {
77
- return second;
83
+ let secondMergeMap = mergeMap.get(first);
84
+
85
+ if (secondMergeMap) {
86
+ const result = secondMergeMap.get(second);
87
+
88
+ if (result) {
89
+
90
+ // If this combination of first and second arguments has been already visited, return the previously created result.
91
+ return result;
92
+ }
93
+ } else {
94
+ secondMergeMap = new Map();
95
+ mergeMap.set(first, secondMergeMap);
78
96
  }
79
97
 
80
98
  /*
@@ -88,27 +106,25 @@ function deepMerge(first = {}, second = {}) {
88
106
  ...second
89
107
  };
90
108
 
109
+ delete result.__proto__; // eslint-disable-line no-proto -- don't merge own property "__proto__"
110
+
111
+ // Store the pending result for this combination of first and second arguments.
112
+ secondMergeMap.set(second, result);
113
+
91
114
  for (const key of Object.keys(second)) {
92
115
 
93
116
  // avoid hairy edge case
94
- if (key === "__proto__") {
117
+ if (key === "__proto__" || !Object.prototype.propertyIsEnumerable.call(first, key)) {
95
118
  continue;
96
119
  }
97
120
 
98
121
  const firstValue = first[key];
99
122
  const secondValue = second[key];
100
123
 
101
- if (isNonNullObject(firstValue)) {
102
- result[key] = deepMerge(firstValue, secondValue);
103
- } else if (isUndefined(firstValue)) {
104
- if (isNonNullObject(secondValue)) {
105
- result[key] = deepMerge(
106
- Array.isArray(secondValue) ? [] : {},
107
- secondValue
108
- );
109
- } else if (!isUndefined(secondValue)) {
110
- result[key] = secondValue;
111
- }
124
+ if (isNonArrayObject(firstValue) && isNonArrayObject(secondValue)) {
125
+ result[key] = deepMerge(firstValue, secondValue, mergeMap);
126
+ } else if (isUndefined(secondValue)) {
127
+ result[key] = firstValue;
112
128
  }
113
129
  }
114
130
 
@@ -262,6 +278,26 @@ const booleanSchema = {
262
278
  validate: "boolean"
263
279
  };
264
280
 
281
+ const ALLOWED_SEVERITIES = new Set(["error", "warn", "off", 2, 1, 0]);
282
+
283
+ /** @type {ObjectPropertySchema} */
284
+ const disableDirectiveSeveritySchema = {
285
+ merge(first, second) {
286
+ const value = second === void 0 ? first : second;
287
+
288
+ if (typeof value === "boolean") {
289
+ return value ? "warn" : "off";
290
+ }
291
+
292
+ return normalizeSeverityToNumber(value);
293
+ },
294
+ validate(value) {
295
+ if (!(ALLOWED_SEVERITIES.has(value) || typeof value === "boolean")) {
296
+ throw new TypeError("Expected one of: \"error\", \"warn\", \"off\", 0, 1, 2, or a boolean.");
297
+ }
298
+ }
299
+ };
300
+
265
301
  /** @type {ObjectPropertySchema} */
266
302
  const deepObjectAssignSchema = {
267
303
  merge(first = {}, second = {}) {
@@ -534,7 +570,7 @@ const flatConfigSchema = {
534
570
  linterOptions: {
535
571
  schema: {
536
572
  noInlineConfig: booleanSchema,
537
- reportUnusedDisableDirectives: booleanSchema
573
+ reportUnusedDisableDirectives: disableDirectiveSeveritySchema
538
574
  }
539
575
  },
540
576
  languageOptions: {
@@ -675,7 +675,6 @@ function processOptions({
675
675
  overrideConfig = null,
676
676
  overrideConfigFile = null,
677
677
  plugins = {},
678
- reportUnusedDisableDirectives = null, // ← should be null by default because if it's a string then it overrides the 'reportUnusedDisableDirectives' setting in config files. And we cannot use `overrideConfig.reportUnusedDisableDirectives` instead because we cannot configure the `error` severity with that.
679
678
  warnIgnored = true,
680
679
  ...unknownOptions
681
680
  }) {
@@ -720,6 +719,9 @@ function processOptions({
720
719
  if (unknownOptionKeys.includes("rulePaths")) {
721
720
  errors.push("'rulePaths' has been removed. Please define your rules using plugins.");
722
721
  }
722
+ if (unknownOptionKeys.includes("reportUnusedDisableDirectives")) {
723
+ errors.push("'reportUnusedDisableDirectives' has been removed. Please use the 'overrideConfig.linterOptions.reportUnusedDisableDirectives' option instead.");
724
+ }
723
725
  }
724
726
  if (typeof allowInlineConfig !== "boolean") {
725
727
  errors.push("'allowInlineConfig' must be a boolean.");
@@ -774,14 +776,6 @@ function processOptions({
774
776
  if (Array.isArray(plugins)) {
775
777
  errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.");
776
778
  }
777
- if (
778
- reportUnusedDisableDirectives !== "error" &&
779
- reportUnusedDisableDirectives !== "warn" &&
780
- reportUnusedDisableDirectives !== "off" &&
781
- reportUnusedDisableDirectives !== null
782
- ) {
783
- errors.push("'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.");
784
- }
785
779
  if (typeof warnIgnored !== "boolean") {
786
780
  errors.push("'warnIgnored' must be a boolean.");
787
781
  }
@@ -806,7 +800,6 @@ function processOptions({
806
800
  globInputPaths,
807
801
  ignore,
808
802
  ignorePatterns,
809
- reportUnusedDisableDirectives,
810
803
  warnIgnored
811
804
  };
812
805
  }
@@ -682,6 +682,13 @@ class ESLint {
682
682
  }
683
683
  }
684
684
 
685
+ /**
686
+ * The type of configuration used by this class.
687
+ * @type {string}
688
+ * @static
689
+ */
690
+ ESLint.configType = "eslintrc";
691
+
685
692
  //------------------------------------------------------------------------------
686
693
  // Public Interface
687
694
  //------------------------------------------------------------------------------
@@ -11,6 +11,7 @@
11
11
 
12
12
  // Note: Node.js 12 does not support fs/promises.
13
13
  const fs = require("fs").promises;
14
+ const { existsSync } = require("fs");
14
15
  const path = require("path");
15
16
  const findUp = require("find-up");
16
17
  const { version } = require("../../package.json");
@@ -83,7 +84,6 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
83
84
  * doesn't do any config file lookup when `true`; considered to be a config filename
84
85
  * when a string.
85
86
  * @property {Record<string,Plugin>} [plugins] An array of plugin implementations.
86
- * @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives.
87
87
  * @property {boolean} warnIgnored Show warnings when the file list includes ignored files
88
88
  */
89
89
 
@@ -91,7 +91,11 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
91
91
  // Helpers
92
92
  //------------------------------------------------------------------------------
93
93
 
94
- const FLAT_CONFIG_FILENAME = "eslint.config.js";
94
+ const FLAT_CONFIG_FILENAMES = [
95
+ "eslint.config.js",
96
+ "eslint.config.mjs",
97
+ "eslint.config.cjs"
98
+ ];
95
99
  const debug = require("debug")("eslint:flat-eslint");
96
100
  const removedFormatters = new Set(["table", "codeframe"]);
97
101
  const privateMembers = new WeakMap();
@@ -248,7 +252,7 @@ function compareResultsByFilePath(a, b) {
248
252
  */
249
253
  function findFlatConfigFile(cwd) {
250
254
  return findUp(
251
- FLAT_CONFIG_FILENAME,
255
+ FLAT_CONFIG_FILENAMES,
252
256
  { cwd }
253
257
  );
254
258
  }
@@ -448,7 +452,6 @@ async function calculateConfigArray(eslint, {
448
452
  * @param {FlatConfigArray} config.configs The config.
449
453
  * @param {boolean} config.fix If `true` then it does fix.
450
454
  * @param {boolean} config.allowInlineConfig If `true` then it uses directive comments.
451
- * @param {boolean} config.reportUnusedDisableDirectives If `true` then it reports unused `eslint-disable` comments.
452
455
  * @param {Linter} config.linter The linter instance to verify.
453
456
  * @returns {LintResult} The result of linting.
454
457
  * @private
@@ -460,7 +463,6 @@ function verifyText({
460
463
  configs,
461
464
  fix,
462
465
  allowInlineConfig,
463
- reportUnusedDisableDirectives,
464
466
  linter
465
467
  }) {
466
468
  const filePath = providedFilePath || "<text>";
@@ -480,7 +482,6 @@ function verifyText({
480
482
  allowInlineConfig,
481
483
  filename: filePathToVerify,
482
484
  fix,
483
- reportUnusedDisableDirectives,
484
485
 
485
486
  /**
486
487
  * Check if the linter should adopt a given code block or not.
@@ -748,7 +749,6 @@ class FlatESLint {
748
749
  cwd,
749
750
  fix,
750
751
  fixTypes,
751
- reportUnusedDisableDirectives,
752
752
  globInputPaths,
753
753
  errorOnUnmatchedPattern,
754
754
  warnIgnored
@@ -766,7 +766,7 @@ class FlatESLint {
766
766
  const errorCode = error && error.code;
767
767
 
768
768
  // Ignore errors when no such file exists or file system is read only (and cache file does not exist)
769
- if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !(await fs.exists(cacheFilePath)))) {
769
+ if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !existsSync(cacheFilePath))) {
770
770
  throw error;
771
771
  }
772
772
  }
@@ -858,7 +858,6 @@ class FlatESLint {
858
858
  cwd,
859
859
  fix: fixer,
860
860
  allowInlineConfig,
861
- reportUnusedDisableDirectives,
862
861
  linter
863
862
  });
864
863
 
@@ -943,7 +942,6 @@ class FlatESLint {
943
942
  allowInlineConfig,
944
943
  cwd,
945
944
  fix,
946
- reportUnusedDisableDirectives,
947
945
  warnIgnored: constructorWarnIgnored
948
946
  } = eslintOptions;
949
947
  const results = [];
@@ -967,7 +965,6 @@ class FlatESLint {
967
965
  cwd,
968
966
  fix,
969
967
  allowInlineConfig,
970
- reportUnusedDisableDirectives,
971
968
  linter
972
969
  }));
973
970
  }
@@ -1119,11 +1116,20 @@ class FlatESLint {
1119
1116
  }
1120
1117
  }
1121
1118
 
1119
+ /**
1120
+ * The type of configuration used by this class.
1121
+ * @type {string}
1122
+ * @static
1123
+ */
1124
+ FlatESLint.configType = "flat";
1125
+
1122
1126
  /**
1123
1127
  * Returns whether flat config should be used.
1128
+ * @param {Object} [options] The options for this function.
1129
+ * @param {string} [options.cwd] The current working directory.
1124
1130
  * @returns {Promise<boolean>} Whether flat config should be used.
1125
1131
  */
1126
- async function shouldUseFlatConfig() {
1132
+ async function shouldUseFlatConfig({ cwd = process.cwd() } = {}) {
1127
1133
  switch (process.env.ESLINT_USE_FLAT_CONFIG) {
1128
1134
  case "true":
1129
1135
  return true;
@@ -1135,7 +1141,7 @@ async function shouldUseFlatConfig() {
1135
1141
  * If neither explicitly enabled nor disabled, then use the presence
1136
1142
  * of a flat config file to determine enablement.
1137
1143
  */
1138
- return !!(await findFlatConfigFile(process.cwd()));
1144
+ return !!(await findFlatConfigFile(cwd));
1139
1145
  }
1140
1146
  }
1141
1147
 
@@ -15,7 +15,10 @@ const levn = require("levn"),
15
15
  Legacy: {
16
16
  ConfigOps
17
17
  }
18
- } = require("@eslint/eslintrc/universal");
18
+ } = require("@eslint/eslintrc/universal"),
19
+ {
20
+ directivesPattern
21
+ } = require("../shared/directives");
19
22
 
20
23
  const debug = require("debug")("eslint:config-comment-parser");
21
24
 
@@ -148,4 +151,35 @@ module.exports = class ConfigCommentParser {
148
151
  return items;
149
152
  }
150
153
 
154
+ /**
155
+ * Extract the directive and the justification from a given directive comment and trim them.
156
+ * @param {string} value The comment text to extract.
157
+ * @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
158
+ */
159
+ extractDirectiveComment(value) {
160
+ const match = /\s-{2,}\s/u.exec(value);
161
+
162
+ if (!match) {
163
+ return { directivePart: value.trim(), justificationPart: "" };
164
+ }
165
+
166
+ const directive = value.slice(0, match.index).trim();
167
+ const justification = value.slice(match.index + match[0].length).trim();
168
+
169
+ return { directivePart: directive, justificationPart: justification };
170
+ }
171
+
172
+ /**
173
+ * Parses a directive comment into directive text and value.
174
+ * @param {Comment} comment The comment node with the directive to be parsed.
175
+ * @returns {{directiveText: string, directiveValue: string}} The directive text and value.
176
+ */
177
+ parseDirective(comment) {
178
+ const { directivePart } = this.extractDirectiveComment(comment.value);
179
+ const match = directivesPattern.exec(directivePart);
180
+ const directiveText = match[1];
181
+ const directiveValue = directivePart.slice(match.index + directiveText.length);
182
+
183
+ return { directiveText, directiveValue };
184
+ }
151
185
  };
@@ -44,6 +44,7 @@ const { getRuleFromConfig } = require("../config/flat-config-helpers");
44
44
  const { FlatConfigArray } = require("../config/flat-config-array");
45
45
  const { RuleValidator } = require("../config/rule-validator");
46
46
  const { assertIsRuleOptions, assertIsRuleSeverity } = require("../config/flat-config-schema");
47
+ const { normalizeSeverityToString } = require("../shared/severity");
47
48
  const debug = require("debug")("eslint:linter");
48
49
  const MAX_AUTOFIX_PASSES = 10;
49
50
  const DEFAULT_PARSER_NAME = "espree";
@@ -316,24 +317,6 @@ function createDisableDirectives(options) {
316
317
  return result;
317
318
  }
318
319
 
319
- /**
320
- * Extract the directive and the justification from a given directive comment and trim them.
321
- * @param {string} value The comment text to extract.
322
- * @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
323
- */
324
- function extractDirectiveComment(value) {
325
- const match = /\s-{2,}\s/u.exec(value);
326
-
327
- if (!match) {
328
- return { directivePart: value.trim(), justificationPart: "" };
329
- }
330
-
331
- const directive = value.slice(0, match.index).trim();
332
- const justification = value.slice(match.index + match[0].length).trim();
333
-
334
- return { directivePart: directive, justificationPart: justification };
335
- }
336
-
337
320
  /**
338
321
  * Parses comments in file to extract file-specific config of rules, globals
339
322
  * and environments and merges them with global config; also code blocks
@@ -355,7 +338,7 @@ function getDirectiveComments(sourceCode, ruleMapper, warnInlineConfig) {
355
338
  });
356
339
 
357
340
  sourceCode.getInlineConfigNodes().filter(token => token.type !== "Shebang").forEach(comment => {
358
- const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
341
+ const { directivePart, justificationPart } = commentParser.extractDirectiveComment(comment.value);
359
342
 
360
343
  const match = directivesPattern.exec(directivePart);
361
344
 
@@ -500,7 +483,7 @@ function getDirectiveCommentsForFlatConfig(sourceCode, ruleMapper) {
500
483
  const disableDirectives = [];
501
484
 
502
485
  sourceCode.getInlineConfigNodes().filter(token => token.type !== "Shebang").forEach(comment => {
503
- const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
486
+ const { directivePart, justificationPart } = commentParser.extractDirectiveComment(comment.value);
504
487
 
505
488
  const match = directivesPattern.exec(directivePart);
506
489
 
@@ -620,7 +603,7 @@ function findEslintEnv(text) {
620
603
  if (match[0].endsWith("*/")) {
621
604
  retv = Object.assign(
622
605
  retv || {},
623
- commentParser.parseListConfig(extractDirectiveComment(match[1]).directivePart)
606
+ commentParser.parseListConfig(commentParser.extractDirectiveComment(match[1]).directivePart)
624
607
  );
625
608
  }
626
609
  }
@@ -671,9 +654,11 @@ function normalizeVerifyOptions(providedOptions, config) {
671
654
  reportUnusedDisableDirectives = reportUnusedDisableDirectives ? "error" : "off";
672
655
  }
673
656
  if (typeof reportUnusedDisableDirectives !== "string") {
674
- reportUnusedDisableDirectives =
675
- linterOptions.reportUnusedDisableDirectives
676
- ? "warn" : "off";
657
+ if (typeof linterOptions.reportUnusedDisableDirectives === "boolean") {
658
+ reportUnusedDisableDirectives = linterOptions.reportUnusedDisableDirectives ? "warn" : "off";
659
+ } else {
660
+ reportUnusedDisableDirectives = linterOptions.reportUnusedDisableDirectives === void 0 ? "off" : normalizeSeverityToString(linterOptions.reportUnusedDisableDirectives);
661
+ }
677
662
  }
678
663
 
679
664
  return {
package/lib/options.js CHANGED
@@ -48,6 +48,7 @@ const optionator = require("optionator");
48
48
  * @property {string[]} [plugin] Specify plugins
49
49
  * @property {string} [printConfig] Print the configuration for the given file
50
50
  * @property {boolean | undefined} reportUnusedDisableDirectives Adds reported errors for unused eslint-disable and eslint-enable directives
51
+ * @property {string | undefined} reportUnusedDisableDirectivesSeverity A severity string indicating if and how unused disable and enable directives should be tracked and reported.
51
52
  * @property {string} [resolvePluginsRelativeTo] A folder where plugins should be resolved from, CWD by default
52
53
  * @property {Object} [rule] Specify rules
53
54
  * @property {string[]} [rulesdir] Load additional rules from this directory. Deprecated: Use rules from plugins
@@ -167,7 +168,7 @@ module.exports = function(usingFlatConfig) {
167
168
  alias: "c",
168
169
  type: "path::String",
169
170
  description: usingFlatConfig
170
- ? "Use this configuration instead of eslint.config.js"
171
+ ? "Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs"
171
172
  : "Use this configuration, overriding .eslintrc.* config options if present"
172
173
  },
173
174
  envFlag,
@@ -306,6 +307,13 @@ module.exports = function(usingFlatConfig) {
306
307
  default: void 0,
307
308
  description: "Adds reported errors for unused eslint-disable and eslint-enable directives"
308
309
  },
310
+ {
311
+ option: "report-unused-disable-directives-severity",
312
+ type: "String",
313
+ default: void 0,
314
+ description: "Chooses severity level for reporting unused eslint-disable and eslint-enable directives",
315
+ enum: ["off", "warn", "error", "0", "1", "2"]
316
+ },
309
317
  {
310
318
  heading: "Caching"
311
319
  },
@@ -13,6 +13,7 @@
13
13
  const
14
14
  assert = require("assert"),
15
15
  util = require("util"),
16
+ path = require("path"),
16
17
  equal = require("fast-deep-equal"),
17
18
  Traverser = require("../shared/traverser"),
18
19
  { getRuleOptionsSchema } = require("../config/flat-config-helpers"),
@@ -592,7 +593,15 @@ class FlatRuleTester {
592
593
  * @private
593
594
  */
594
595
  function runRuleForItem(item) {
595
- const configs = new FlatConfigArray(testerConfig, { baseConfig });
596
+ const flatConfigArrayOptions = {
597
+ baseConfig
598
+ };
599
+
600
+ if (item.filename) {
601
+ flatConfigArrayOptions.basePath = path.parse(item.filename).root;
602
+ }
603
+
604
+ const configs = new FlatConfigArray(testerConfig, flatConfigArrayOptions);
596
605
 
597
606
  /*
598
607
  * Modify the returned config so that the parser is wrapped to catch
@@ -96,7 +96,7 @@ module.exports = {
96
96
 
97
97
  if (codePath.origin === "program") {
98
98
  const scope = sourceCode.getScope(node);
99
- const features = context.parserOptions.ecmaFeatures || {};
99
+ const features = context.languageOptions.parserOptions.ecmaFeatures || {};
100
100
 
101
101
  // `this` at the top level of scripts always refers to the global object
102
102
  stack.push({
@@ -209,12 +209,15 @@ module.exports = {
209
209
  });
210
210
  }
211
211
 
212
- suggest.push({
213
- messageId: "wrapBraces",
214
- fix(fixer) {
215
- return curlyWrapFixer(sourceCode, node, fixer);
216
- }
217
- });
212
+ // Do not suggest wrapping an unnamed FunctionExpression in braces as that would be invalid syntax.
213
+ if (!(node.body.type === "FunctionExpression" && !node.body.id)) {
214
+ suggest.push({
215
+ messageId: "wrapBraces",
216
+ fix(fixer) {
217
+ return curlyWrapFixer(sourceCode, node, fixer);
218
+ }
219
+ });
220
+ }
218
221
 
219
222
  context.report({
220
223
  node: node.body,
@@ -142,40 +142,27 @@ module.exports = {
142
142
  }
143
143
  }
144
144
 
145
- /**
146
- * Checks property accesses in a destructuring assignment expression, e.g. `var foo; ({foo} = bar);`
147
- * @param {ASTNode} node An AssignmentExpression or AssignmentPattern node
148
- * @returns {undefined}
149
- */
150
- function checkDestructuringAssignment(node) {
151
- if (node.right.type === "Identifier") {
152
- const objectName = node.right.name;
153
-
154
- if (node.left.type === "ObjectPattern") {
155
- node.left.properties.forEach(property => {
156
- checkPropertyAccess(node.left, objectName, astUtils.getStaticPropertyName(property));
157
- });
158
- }
159
- }
160
- }
161
-
162
145
  return {
163
146
  MemberExpression(node) {
164
147
  checkPropertyAccess(node, node.object && node.object.name, astUtils.getStaticPropertyName(node));
165
148
  },
166
- VariableDeclarator(node) {
167
- if (node.init && node.init.type === "Identifier") {
168
- const objectName = node.init.name;
169
-
170
- if (node.id.type === "ObjectPattern") {
171
- node.id.properties.forEach(property => {
172
- checkPropertyAccess(node.id, objectName, astUtils.getStaticPropertyName(property));
173
- });
149
+ ObjectPattern(node) {
150
+ let objectName = null;
151
+
152
+ if (node.parent.type === "VariableDeclarator") {
153
+ if (node.parent.init && node.parent.init.type === "Identifier") {
154
+ objectName = node.parent.init.name;
155
+ }
156
+ } else if (node.parent.type === "AssignmentExpression" || node.parent.type === "AssignmentPattern") {
157
+ if (node.parent.right.type === "Identifier") {
158
+ objectName = node.parent.right.name;
174
159
  }
175
160
  }
176
- },
177
- AssignmentExpression: checkDestructuringAssignment,
178
- AssignmentPattern: checkDestructuringAssignment
161
+
162
+ node.properties.forEach(property => {
163
+ checkPropertyAccess(node, objectName, astUtils.getStaticPropertyName(property));
164
+ });
165
+ }
179
166
  };
180
167
  }
181
168
  };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @fileoverview Helpers for severity values (e.g. normalizing different types).
3
+ * @author Bryan Mishkin
4
+ */
5
+
6
+ "use strict";
7
+
8
+ /**
9
+ * Convert severity value of different types to a string.
10
+ * @param {string|number} severity severity value
11
+ * @throws error if severity is invalid
12
+ * @returns {string} severity string
13
+ */
14
+ function normalizeSeverityToString(severity) {
15
+ if ([2, "2", "error"].includes(severity)) {
16
+ return "error";
17
+ }
18
+ if ([1, "1", "warn"].includes(severity)) {
19
+ return "warn";
20
+ }
21
+ if ([0, "0", "off"].includes(severity)) {
22
+ return "off";
23
+ }
24
+ throw new Error(`Invalid severity value: ${severity}`);
25
+ }
26
+
27
+ /**
28
+ * Convert severity value of different types to a number.
29
+ * @param {string|number} severity severity value
30
+ * @throws error if severity is invalid
31
+ * @returns {number} severity number
32
+ */
33
+ function normalizeSeverityToNumber(severity) {
34
+ if ([2, "2", "error"].includes(severity)) {
35
+ return 2;
36
+ }
37
+ if ([1, "1", "warn"].includes(severity)) {
38
+ return 1;
39
+ }
40
+ if ([0, "0", "off"].includes(severity)) {
41
+ return 0;
42
+ }
43
+ throw new Error(`Invalid severity value: ${severity}`);
44
+ }
45
+
46
+ module.exports = {
47
+ normalizeSeverityToString,
48
+ normalizeSeverityToNumber
49
+ };
@@ -212,24 +212,6 @@ function isSpaceBetween(sourceCode, first, second, checkInsideOfJSXText) {
212
212
  // Directive Comments
213
213
  //-----------------------------------------------------------------------------
214
214
 
215
- /**
216
- * Extract the directive and the justification from a given directive comment and trim them.
217
- * @param {string} value The comment text to extract.
218
- * @returns {{directivePart: string, justificationPart: string}} The extracted directive and justification.
219
- */
220
- function extractDirectiveComment(value) {
221
- const match = /\s-{2,}\s/u.exec(value);
222
-
223
- if (!match) {
224
- return { directivePart: value.trim(), justificationPart: "" };
225
- }
226
-
227
- const directive = value.slice(0, match.index).trim();
228
- const justification = value.slice(match.index + match[0].length).trim();
229
-
230
- return { directivePart: directive, justificationPart: justification };
231
- }
232
-
233
215
  /**
234
216
  * Ensures that variables representing built-in properties of the Global Object,
235
217
  * and any globals declared by special block comments, are present in the global
@@ -921,7 +903,7 @@ class SourceCode extends TokenStore {
921
903
  return false;
922
904
  }
923
905
 
924
- const { directivePart } = extractDirectiveComment(comment.value);
906
+ const { directivePart } = commentParser.extractDirectiveComment(comment.value);
925
907
 
926
908
  const directiveMatch = directivesPattern.exec(directivePart);
927
909
 
@@ -977,10 +959,7 @@ class SourceCode extends TokenStore {
977
959
 
978
960
  this.getInlineConfigNodes().forEach(comment => {
979
961
 
980
- const { directivePart } = extractDirectiveComment(comment.value);
981
- const match = directivesPattern.exec(directivePart);
982
- const directiveText = match[1];
983
- const directiveValue = directivePart.slice(match.index + directiveText.length);
962
+ const { directiveText, directiveValue } = commentParser.parseDirective(comment);
984
963
 
985
964
  switch (directiveText) {
986
965
  case "exported":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "8.55.0",
3
+ "version": "8.57.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -65,8 +65,8 @@
65
65
  "@eslint-community/eslint-utils": "^4.2.0",
66
66
  "@eslint-community/regexpp": "^4.6.1",
67
67
  "@eslint/eslintrc": "^2.1.4",
68
- "@eslint/js": "8.55.0",
69
- "@humanwhocodes/config-array": "^0.11.13",
68
+ "@eslint/js": "8.57.0",
69
+ "@humanwhocodes/config-array": "^0.11.14",
70
70
  "@humanwhocodes/module-importer": "^1.0.1",
71
71
  "@nodelib/fs.walk": "^1.2.8",
72
72
  "@ungap/structured-clone": "^1.2.0",
@@ -119,11 +119,11 @@
119
119
  "eslint": "file:.",
120
120
  "eslint-config-eslint": "file:packages/eslint-config-eslint",
121
121
  "eslint-plugin-eslint-comments": "^3.2.0",
122
- "eslint-plugin-eslint-plugin": "^5.1.0",
122
+ "eslint-plugin-eslint-plugin": "^5.2.1",
123
123
  "eslint-plugin-internal-rules": "file:tools/internal-rules",
124
124
  "eslint-plugin-jsdoc": "^46.2.5",
125
- "eslint-plugin-n": "^16.0.0",
126
- "eslint-plugin-unicorn": "^42.0.0",
125
+ "eslint-plugin-n": "^16.6.0",
126
+ "eslint-plugin-unicorn": "^49.0.0",
127
127
  "eslint-release": "^3.2.0",
128
128
  "eslump": "^3.0.0",
129
129
  "esprima": "^4.0.1",
@@ -136,7 +136,7 @@
136
136
  "load-perf": "^0.2.0",
137
137
  "markdown-it": "^12.2.0",
138
138
  "markdown-it-container": "^3.0.0",
139
- "markdownlint": "^0.31.1",
139
+ "markdownlint": "^0.32.0",
140
140
  "markdownlint-cli": "^0.37.0",
141
141
  "marked": "^4.0.8",
142
142
  "memfs": "^3.0.1",