eslint 8.22.0 → 8.33.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 (80) hide show
  1. package/README.md +51 -45
  2. package/bin/eslint.js +2 -4
  3. package/conf/globals.js +6 -1
  4. package/conf/rule-type-list.json +2 -2
  5. package/lib/cli-engine/file-enumerator.js +4 -2
  6. package/lib/cli-engine/formatters/formatters-meta.json +46 -0
  7. package/lib/cli-engine/formatters/html.js +76 -51
  8. package/lib/cli.js +163 -40
  9. package/lib/config/default-config.js +2 -2
  10. package/lib/config/flat-config-array.js +1 -1
  11. package/lib/eslint/eslint-helpers.js +409 -87
  12. package/lib/eslint/eslint.js +5 -2
  13. package/lib/eslint/flat-eslint.js +113 -110
  14. package/lib/linter/code-path-analysis/code-path-segment.js +2 -2
  15. package/lib/linter/code-path-analysis/code-path-state.js +7 -7
  16. package/lib/linter/code-path-analysis/debug-helpers.js +3 -3
  17. package/lib/linter/code-path-analysis/id-generator.js +2 -2
  18. package/lib/linter/config-comment-parser.js +1 -2
  19. package/lib/linter/linter.js +17 -7
  20. package/lib/linter/timing.js +4 -4
  21. package/lib/options.js +293 -239
  22. package/lib/rule-tester/flat-rule-tester.js +13 -11
  23. package/lib/rule-tester/rule-tester.js +15 -11
  24. package/lib/rules/array-callback-return.js +2 -2
  25. package/lib/rules/comma-dangle.js +3 -3
  26. package/lib/rules/for-direction.js +1 -1
  27. package/lib/rules/func-name-matching.js +2 -2
  28. package/lib/rules/getter-return.js +14 -8
  29. package/lib/rules/global-require.js +2 -1
  30. package/lib/rules/id-length.js +43 -2
  31. package/lib/rules/indent-legacy.js +4 -4
  32. package/lib/rules/indent.js +23 -15
  33. package/lib/rules/index.js +3 -0
  34. package/lib/rules/key-spacing.js +50 -38
  35. package/lib/rules/lines-around-comment.js +2 -2
  36. package/lib/rules/logical-assignment-operators.js +474 -0
  37. package/lib/rules/multiline-ternary.js +2 -2
  38. package/lib/rules/new-cap.js +2 -2
  39. package/lib/rules/no-else-return.js +1 -1
  40. package/lib/rules/no-empty-static-block.js +47 -0
  41. package/lib/rules/no-empty.js +19 -2
  42. package/lib/rules/no-extra-boolean-cast.js +1 -1
  43. package/lib/rules/no-extra-parens.js +18 -3
  44. package/lib/rules/no-fallthrough.js +26 -5
  45. package/lib/rules/no-implicit-coercion.js +20 -1
  46. package/lib/rules/no-implicit-globals.js +5 -0
  47. package/lib/rules/no-invalid-regexp.js +40 -18
  48. package/lib/rules/no-labels.js +1 -1
  49. package/lib/rules/no-lone-blocks.js +1 -1
  50. package/lib/rules/no-loss-of-precision.js +2 -2
  51. package/lib/rules/no-magic-numbers.js +18 -1
  52. package/lib/rules/no-misleading-character-class.js +4 -4
  53. package/lib/rules/no-new-native-nonconstructor.js +64 -0
  54. package/lib/rules/no-obj-calls.js +1 -1
  55. package/lib/rules/no-restricted-exports.js +106 -10
  56. package/lib/rules/no-return-await.js +28 -1
  57. package/lib/rules/no-underscore-dangle.js +36 -11
  58. package/lib/rules/no-unneeded-ternary.js +1 -1
  59. package/lib/rules/no-use-before-define.js +1 -1
  60. package/lib/rules/no-useless-computed-key.js +1 -1
  61. package/lib/rules/no-var.js +2 -2
  62. package/lib/rules/no-warning-comments.js +24 -5
  63. package/lib/rules/padded-blocks.js +1 -1
  64. package/lib/rules/prefer-arrow-callback.js +4 -3
  65. package/lib/rules/prefer-const.js +13 -1
  66. package/lib/rules/prefer-named-capture-group.js +71 -6
  67. package/lib/rules/prefer-object-spread.js +1 -1
  68. package/lib/rules/prefer-regex-literals.js +147 -32
  69. package/lib/rules/prefer-rest-params.js +1 -1
  70. package/lib/rules/require-yield.js +0 -1
  71. package/lib/rules/strict.js +1 -1
  72. package/lib/rules/utils/ast-utils.js +10 -4
  73. package/lib/shared/directives.js +15 -0
  74. package/lib/shared/logging.js +1 -1
  75. package/lib/shared/runtime-info.js +1 -1
  76. package/lib/shared/traverser.js +1 -1
  77. package/lib/shared/types.js +15 -2
  78. package/lib/source-code/token-store/cursor.js +1 -1
  79. package/messages/print-config-with-directory-path.js +1 -1
  80. package/package.json +27 -27
package/lib/cli.js CHANGED
@@ -6,7 +6,7 @@
6
6
  "use strict";
7
7
 
8
8
  /*
9
- * The CLI object should *not* call process.exit() directly. It should only return
9
+ * NOTE: The CLI object should *not* call process.exit() directly. It should only return
10
10
  * exit codes. This allows other programs to use the CLI object and still control
11
11
  * when the program exits.
12
12
  */
@@ -19,9 +19,13 @@ const fs = require("fs"),
19
19
  path = require("path"),
20
20
  { promisify } = require("util"),
21
21
  { ESLint } = require("./eslint"),
22
- CLIOptions = require("./options"),
22
+ { FlatESLint } = require("./eslint/flat-eslint"),
23
+ createCLIOptions = require("./options"),
23
24
  log = require("./shared/logging"),
24
25
  RuntimeInfo = require("./shared/runtime-info");
26
+ const { Legacy: { naming } } = require("@eslint/eslintrc");
27
+ const { findFlatConfigFile } = require("./eslint/flat-eslint");
28
+ const { ModuleImporter } = require("@humanwhocodes/module-importer");
25
29
 
26
30
  const debug = require("debug")("eslint:cli");
27
31
 
@@ -33,6 +37,7 @@ const debug = require("debug")("eslint:cli");
33
37
  /** @typedef {import("./eslint/eslint").LintMessage} LintMessage */
34
38
  /** @typedef {import("./eslint/eslint").LintResult} LintResult */
35
39
  /** @typedef {import("./options").ParsedCLIOptions} ParsedCLIOptions */
40
+ /** @typedef {import("./shared/types").ResultsMeta} ResultsMeta */
36
41
 
37
42
  //------------------------------------------------------------------------------
38
43
  // Helpers
@@ -54,17 +59,20 @@ function quietFixPredicate(message) {
54
59
  }
55
60
 
56
61
  /**
57
- * Translates the CLI options into the options expected by the CLIEngine.
62
+ * Translates the CLI options into the options expected by the ESLint constructor.
58
63
  * @param {ParsedCLIOptions} cliOptions The CLI options to translate.
59
- * @returns {ESLintOptions} The options object for the CLIEngine.
64
+ * @param {"flat"|"eslintrc"} [configType="eslintrc"] The format of the
65
+ * config to generate.
66
+ * @returns {Promise<ESLintOptions>} The options object for the ESLint constructor.
60
67
  * @private
61
68
  */
62
- function translateOptions({
69
+ async function translateOptions({
63
70
  cache,
64
71
  cacheFile,
65
72
  cacheLocation,
66
73
  cacheStrategy,
67
74
  config,
75
+ configLookup,
68
76
  env,
69
77
  errorOnUnmatchedPattern,
70
78
  eslintrc,
@@ -85,19 +93,60 @@ function translateOptions({
85
93
  resolvePluginsRelativeTo,
86
94
  rule,
87
95
  rulesdir
88
- }) {
89
- return {
90
- allowInlineConfig: inlineConfig,
91
- cache,
92
- cacheLocation: cacheLocation || cacheFile,
93
- cacheStrategy,
94
- errorOnUnmatchedPattern,
95
- extensions: ext,
96
- fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
97
- fixTypes: fixType,
98
- ignore,
99
- ignorePath,
100
- overrideConfig: {
96
+ }, configType) {
97
+
98
+ let overrideConfig, overrideConfigFile;
99
+ const importer = new ModuleImporter();
100
+
101
+ if (configType === "flat") {
102
+ overrideConfigFile = (typeof config === "string") ? config : !configLookup;
103
+ if (overrideConfigFile === false) {
104
+ overrideConfigFile = void 0;
105
+ }
106
+
107
+ let globals = {};
108
+
109
+ if (global) {
110
+ globals = global.reduce((obj, name) => {
111
+ if (name.endsWith(":true")) {
112
+ obj[name.slice(0, -5)] = "writable";
113
+ } else {
114
+ obj[name] = "readonly";
115
+ }
116
+ return obj;
117
+ }, globals);
118
+ }
119
+
120
+ overrideConfig = [{
121
+ languageOptions: {
122
+ globals,
123
+ parserOptions: parserOptions || {}
124
+ },
125
+ rules: rule ? rule : {}
126
+ }];
127
+
128
+ if (parser) {
129
+ overrideConfig[0].languageOptions.parser = await importer.import(parser);
130
+ }
131
+
132
+ if (plugin) {
133
+ const plugins = {};
134
+
135
+ for (const pluginName of plugin) {
136
+
137
+ const shortName = naming.getShorthandName(pluginName, "eslint-plugin");
138
+ const longName = naming.normalizePackageName(pluginName, "eslint-plugin");
139
+
140
+ plugins[shortName] = await importer.import(longName);
141
+ }
142
+
143
+ overrideConfig[0].plugins = plugins;
144
+ }
145
+
146
+ } else {
147
+ overrideConfigFile = config;
148
+
149
+ overrideConfig = {
101
150
  env: env && env.reduce((obj, name) => {
102
151
  obj[name] = true;
103
152
  return obj;
@@ -115,19 +164,40 @@ function translateOptions({
115
164
  parserOptions,
116
165
  plugins: plugin,
117
166
  rules: rule
118
- },
119
- overrideConfigFile: config,
120
- reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0,
121
- resolvePluginsRelativeTo,
122
- rulePaths: rulesdir,
123
- useEslintrc: eslintrc
167
+ };
168
+ }
169
+
170
+ const options = {
171
+ allowInlineConfig: inlineConfig,
172
+ cache,
173
+ cacheLocation: cacheLocation || cacheFile,
174
+ cacheStrategy,
175
+ errorOnUnmatchedPattern,
176
+ fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
177
+ fixTypes: fixType,
178
+ ignore,
179
+ overrideConfig,
180
+ overrideConfigFile,
181
+ reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0
124
182
  };
183
+
184
+ if (configType === "flat") {
185
+ options.ignorePatterns = ignorePattern;
186
+ } else {
187
+ options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
188
+ options.rulePaths = rulesdir;
189
+ options.useEslintrc = eslintrc;
190
+ options.extensions = ext;
191
+ options.ignorePath = ignorePath;
192
+ }
193
+
194
+ return options;
125
195
  }
126
196
 
127
197
  /**
128
198
  * Count error messages.
129
199
  * @param {LintResult[]} results The lint results.
130
- * @returns {{errorCount:number;warningCount:number}} The number of error messages.
200
+ * @returns {{errorCount:number;fatalErrorCount:number,warningCount:number}} The number of error messages.
131
201
  */
132
202
  function countErrors(results) {
133
203
  let errorCount = 0;
@@ -165,10 +235,11 @@ async function isDirectory(filePath) {
165
235
  * @param {LintResult[]} results The results to print.
166
236
  * @param {string} format The name of the formatter to use or the path to the formatter.
167
237
  * @param {string} outputFile The path for the output file.
238
+ * @param {ResultsMeta} resultsMeta Warning count and max threshold.
168
239
  * @returns {Promise<boolean>} True if the printing succeeds, false if not.
169
240
  * @private
170
241
  */
171
- async function printResults(engine, results, format, outputFile) {
242
+ async function printResults(engine, results, format, outputFile, resultsMeta) {
172
243
  let formatter;
173
244
 
174
245
  try {
@@ -178,7 +249,7 @@ async function printResults(engine, results, format, outputFile) {
178
249
  return false;
179
250
  }
180
251
 
181
- const output = await formatter.format(results);
252
+ const output = await formatter.format(results, resultsMeta);
182
253
 
183
254
  if (output) {
184
255
  if (outputFile) {
@@ -204,6 +275,31 @@ async function printResults(engine, results, format, outputFile) {
204
275
  return true;
205
276
  }
206
277
 
278
+ /**
279
+ * Returns whether flat config should be used.
280
+ * @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
281
+ * @returns {Promise<boolean>} Where flat config should be used.
282
+ */
283
+ async function shouldUseFlatConfig(allowFlatConfig) {
284
+ if (!allowFlatConfig) {
285
+ return false;
286
+ }
287
+
288
+ switch (process.env.ESLINT_USE_FLAT_CONFIG) {
289
+ case "true":
290
+ return true;
291
+ case "false":
292
+ return false;
293
+ default:
294
+
295
+ /*
296
+ * If neither explicitly enabled nor disabled, then use the presence
297
+ * of a flat config file to determine enablement.
298
+ */
299
+ return !!(await findFlatConfigFile(process.cwd()));
300
+ }
301
+ }
302
+
207
303
  //------------------------------------------------------------------------------
208
304
  // Public Interface
209
305
  //------------------------------------------------------------------------------
@@ -218,19 +314,34 @@ const cli = {
218
314
  * Executes the CLI based on an array of arguments that is passed in.
219
315
  * @param {string|Array|Object} args The arguments to process.
220
316
  * @param {string} [text] The text to lint (used for TTY).
317
+ * @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
221
318
  * @returns {Promise<number>} The exit code for the operation.
222
319
  */
223
- async execute(args, text) {
320
+ async execute(args, text, allowFlatConfig) {
224
321
  if (Array.isArray(args)) {
225
322
  debug("CLI args: %o", args.slice(2));
226
323
  }
227
324
 
325
+ /*
326
+ * Before doing anything, we need to see if we are using a
327
+ * flat config file. If so, then we need to change the way command
328
+ * line args are parsed. This is temporary, and when we fully
329
+ * switch to flat config we can remove this logic.
330
+ */
331
+
332
+ const usingFlatConfig = await shouldUseFlatConfig(allowFlatConfig);
333
+
334
+ debug("Using flat config?", usingFlatConfig);
335
+
336
+ const CLIOptions = createCLIOptions(usingFlatConfig);
337
+
228
338
  /** @type {ParsedCLIOptions} */
229
339
  let options;
230
340
 
231
341
  try {
232
342
  options = CLIOptions.parse(args);
233
343
  } catch (error) {
344
+ debug("Error parsing CLI options:", error.message);
234
345
  log.error(error.message);
235
346
  return 2;
236
347
  }
@@ -251,6 +362,7 @@ const cli = {
251
362
  log.info(RuntimeInfo.environment());
252
363
  return 0;
253
364
  } catch (err) {
365
+ debug("Error retrieving environment info");
254
366
  log.error(err.message);
255
367
  return 2;
256
368
  }
@@ -266,7 +378,9 @@ const cli = {
266
378
  return 2;
267
379
  }
268
380
 
269
- const engine = new ESLint(translateOptions(options));
381
+ const engine = usingFlatConfig
382
+ ? new FlatESLint(await translateOptions(options, "flat"))
383
+ : new ESLint(await translateOptions(options));
270
384
  const fileConfig =
271
385
  await engine.calculateConfigForFile(options.printConfig);
272
386
 
@@ -289,7 +403,9 @@ const cli = {
289
403
  return 2;
290
404
  }
291
405
 
292
- const engine = new ESLint(translateOptions(options));
406
+ const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint;
407
+
408
+ const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
293
409
  let results;
294
410
 
295
411
  if (useStdin) {
@@ -303,27 +419,34 @@ const cli = {
303
419
 
304
420
  if (options.fix) {
305
421
  debug("Fix mode enabled - applying fixes");
306
- await ESLint.outputFixes(results);
422
+ await ActiveESLint.outputFixes(results);
307
423
  }
308
424
 
309
425
  let resultsToPrint = results;
310
426
 
311
427
  if (options.quiet) {
312
428
  debug("Quiet mode enabled - filtering out warnings");
313
- resultsToPrint = ESLint.getErrorResults(resultsToPrint);
429
+ resultsToPrint = ActiveESLint.getErrorResults(resultsToPrint);
314
430
  }
315
431
 
316
- if (await printResults(engine, resultsToPrint, options.format, options.outputFile)) {
432
+ const resultCounts = countErrors(results);
433
+ const tooManyWarnings = options.maxWarnings >= 0 && resultCounts.warningCount > options.maxWarnings;
434
+ const resultsMeta = tooManyWarnings
435
+ ? {
436
+ maxWarningsExceeded: {
437
+ maxWarnings: options.maxWarnings,
438
+ foundWarnings: resultCounts.warningCount
439
+ }
440
+ }
441
+ : {};
317
442
 
318
- // Errors and warnings from the original unfiltered results should determine the exit code
319
- const { errorCount, fatalErrorCount, warningCount } = countErrors(results);
443
+ if (await printResults(engine, resultsToPrint, options.format, options.outputFile, resultsMeta)) {
320
444
 
321
- const tooManyWarnings =
322
- options.maxWarnings >= 0 && warningCount > options.maxWarnings;
445
+ // Errors and warnings from the original unfiltered results should determine the exit code
323
446
  const shouldExitForFatalErrors =
324
- options.exitOnFatalError && fatalErrorCount > 0;
447
+ options.exitOnFatalError && resultCounts.fatalErrorCount > 0;
325
448
 
326
- if (!errorCount && tooManyWarnings) {
449
+ if (!resultCounts.errorCount && tooManyWarnings) {
327
450
  log.error(
328
451
  "ESLint found too many warnings (maximum: %s).",
329
452
  options.maxWarnings
@@ -334,7 +457,7 @@ const cli = {
334
457
  return 2;
335
458
  }
336
459
 
337
- return (errorCount || tooManyWarnings) ? 1 : 0;
460
+ return (resultCounts.errorCount || tooManyWarnings) ? 1 : 0;
338
461
  }
339
462
 
340
463
  return 2;
@@ -51,8 +51,8 @@ exports.defaultConfig = [
51
51
  // default ignores are listed here
52
52
  {
53
53
  ignores: [
54
- "**/node_modules/**",
55
- ".git/**"
54
+ "**/node_modules/*",
55
+ ".git/"
56
56
  ]
57
57
  },
58
58
 
@@ -70,7 +70,7 @@ class FlatConfigArray extends ConfigArray {
70
70
  }
71
71
 
72
72
  /**
73
- * The baes config used to build the config array.
73
+ * The base config used to build the config array.
74
74
  * @type {Array<FlatConfig>}
75
75
  */
76
76
  this[originalBaseConfig] = baseConfig;