eslint 9.31.0 → 9.37.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 (126) hide show
  1. package/README.md +8 -4
  2. package/lib/cli-engine/file-enumerator.js +1 -1
  3. package/lib/cli-engine/lint-result-cache.js +47 -29
  4. package/lib/cli.js +62 -266
  5. package/lib/config/flat-config-schema.js +1 -1
  6. package/lib/eslint/eslint-helpers.js +429 -6
  7. package/lib/eslint/eslint.js +509 -327
  8. package/lib/eslint/worker.js +162 -0
  9. package/lib/languages/js/source-code/source-code.js +3 -4
  10. package/lib/linter/esquery.js +4 -1
  11. package/lib/linter/{report-translator.js → file-report.js} +255 -61
  12. package/lib/linter/interpolate.js +1 -1
  13. package/lib/linter/linter.js +143 -297
  14. package/lib/options.js +23 -9
  15. package/lib/rule-tester/rule-tester.js +3 -0
  16. package/lib/rules/accessor-pairs.js +35 -0
  17. package/lib/rules/array-bracket-newline.js +1 -1
  18. package/lib/rules/array-bracket-spacing.js +1 -1
  19. package/lib/rules/array-callback-return.js +0 -1
  20. package/lib/rules/array-element-newline.js +1 -1
  21. package/lib/rules/arrow-parens.js +1 -1
  22. package/lib/rules/arrow-spacing.js +1 -1
  23. package/lib/rules/block-spacing.js +1 -1
  24. package/lib/rules/brace-style.js +1 -1
  25. package/lib/rules/comma-dangle.js +1 -1
  26. package/lib/rules/comma-spacing.js +1 -1
  27. package/lib/rules/comma-style.js +1 -1
  28. package/lib/rules/computed-property-spacing.js +1 -1
  29. package/lib/rules/dot-location.js +1 -1
  30. package/lib/rules/dot-notation.js +1 -1
  31. package/lib/rules/eol-last.js +1 -1
  32. package/lib/rules/func-call-spacing.js +2 -2
  33. package/lib/rules/function-call-argument-newline.js +1 -1
  34. package/lib/rules/function-paren-newline.js +1 -1
  35. package/lib/rules/generator-star-spacing.js +1 -1
  36. package/lib/rules/grouped-accessor-pairs.js +38 -7
  37. package/lib/rules/implicit-arrow-linebreak.js +1 -1
  38. package/lib/rules/indent-legacy.js +2 -1
  39. package/lib/rules/indent.js +1 -1
  40. package/lib/rules/index.js +1 -0
  41. package/lib/rules/jsx-quotes.js +1 -1
  42. package/lib/rules/key-spacing.js +1 -1
  43. package/lib/rules/keyword-spacing.js +1 -1
  44. package/lib/rules/line-comment-position.js +1 -1
  45. package/lib/rules/linebreak-style.js +1 -1
  46. package/lib/rules/lines-around-comment.js +1 -1
  47. package/lib/rules/lines-around-directive.js +1 -1
  48. package/lib/rules/lines-between-class-members.js +1 -1
  49. package/lib/rules/max-len.js +1 -1
  50. package/lib/rules/max-statements-per-line.js +1 -1
  51. package/lib/rules/multiline-comment-style.js +1 -1
  52. package/lib/rules/multiline-ternary.js +1 -1
  53. package/lib/rules/new-parens.js +1 -1
  54. package/lib/rules/newline-after-var.js +1 -1
  55. package/lib/rules/newline-before-return.js +1 -1
  56. package/lib/rules/newline-per-chained-call.js +1 -1
  57. package/lib/rules/no-alert.js +1 -1
  58. package/lib/rules/no-await-in-loop.js +12 -1
  59. package/lib/rules/no-confusing-arrow.js +1 -1
  60. package/lib/rules/no-empty-function.js +20 -1
  61. package/lib/rules/no-empty-static-block.js +25 -1
  62. package/lib/rules/no-empty.js +37 -0
  63. package/lib/rules/no-eval.js +3 -1
  64. package/lib/rules/no-extra-parens.js +1 -1
  65. package/lib/rules/no-extra-semi.js +1 -1
  66. package/lib/rules/no-floating-decimal.js +1 -1
  67. package/lib/rules/no-implied-eval.js +12 -2
  68. package/lib/rules/no-irregular-whitespace.js +2 -2
  69. package/lib/rules/no-loss-of-precision.js +31 -11
  70. package/lib/rules/no-misleading-character-class.js +7 -2
  71. package/lib/rules/no-mixed-operators.js +1 -1
  72. package/lib/rules/no-mixed-spaces-and-tabs.js +2 -1
  73. package/lib/rules/no-multi-spaces.js +1 -1
  74. package/lib/rules/no-multiple-empty-lines.js +1 -1
  75. package/lib/rules/no-octal.js +1 -4
  76. package/lib/rules/no-restricted-globals.js +131 -21
  77. package/lib/rules/no-restricted-imports.js +171 -4
  78. package/lib/rules/no-spaced-func.js +1 -1
  79. package/lib/rules/no-tabs.js +1 -1
  80. package/lib/rules/no-trailing-spaces.js +3 -2
  81. package/lib/rules/no-unused-vars.js +27 -0
  82. package/lib/rules/no-useless-escape.js +1 -1
  83. package/lib/rules/no-whitespace-before-property.js +1 -1
  84. package/lib/rules/nonblock-statement-body-position.js +1 -1
  85. package/lib/rules/object-curly-newline.js +1 -1
  86. package/lib/rules/object-curly-spacing.js +1 -1
  87. package/lib/rules/object-property-newline.js +1 -1
  88. package/lib/rules/one-var-declaration-per-line.js +1 -1
  89. package/lib/rules/one-var.js +63 -30
  90. package/lib/rules/operator-linebreak.js +1 -1
  91. package/lib/rules/padded-blocks.js +1 -1
  92. package/lib/rules/padding-line-between-statements.js +1 -1
  93. package/lib/rules/prefer-destructuring.js +8 -0
  94. package/lib/rules/prefer-regex-literals.js +1 -1
  95. package/lib/rules/preserve-caught-error.js +535 -0
  96. package/lib/rules/quote-props.js +1 -1
  97. package/lib/rules/quotes.js +1 -1
  98. package/lib/rules/require-await.js +9 -0
  99. package/lib/rules/require-unicode-regexp.js +3 -1
  100. package/lib/rules/rest-spread-spacing.js +1 -1
  101. package/lib/rules/semi-spacing.js +1 -1
  102. package/lib/rules/semi-style.js +1 -1
  103. package/lib/rules/semi.js +1 -1
  104. package/lib/rules/space-before-blocks.js +1 -1
  105. package/lib/rules/space-before-function-paren.js +1 -1
  106. package/lib/rules/space-in-parens.js +1 -1
  107. package/lib/rules/space-infix-ops.js +1 -1
  108. package/lib/rules/space-unary-ops.js +1 -1
  109. package/lib/rules/spaced-comment.js +1 -1
  110. package/lib/rules/strict.js +2 -1
  111. package/lib/rules/switch-colon-spacing.js +1 -1
  112. package/lib/rules/template-curly-spacing.js +1 -1
  113. package/lib/rules/template-tag-spacing.js +1 -1
  114. package/lib/rules/utils/ast-utils.js +28 -8
  115. package/lib/rules/utils/char-source.js +1 -1
  116. package/lib/rules/wrap-iife.js +1 -1
  117. package/lib/rules/wrap-regex.js +1 -1
  118. package/lib/rules/yield-star-spacing.js +1 -1
  119. package/lib/rules/yoda.js +2 -2
  120. package/lib/services/suppressions-service.js +3 -0
  121. package/lib/services/warning-service.js +13 -0
  122. package/lib/shared/naming.js +1 -1
  123. package/lib/shared/translate-cli-options.js +281 -0
  124. package/lib/types/index.d.ts +29 -25
  125. package/lib/types/rules.d.ts +266 -88
  126. package/package.json +7 -8
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  [Contribute to ESLint](https://eslint.org/docs/latest/contribute) |
14
14
  [Report Bugs](https://eslint.org/docs/latest/contribute/report-bugs) |
15
15
  [Code of Conduct](https://eslint.org/conduct) |
16
- [Twitter](https://twitter.com/geteslint) |
16
+ [X](https://x.com/geteslint) |
17
17
  [Discord](https://eslint.org/chat) |
18
18
  [Mastodon](https://fosstodon.org/@eslint) |
19
19
  [Bluesky](https://bsky.app/profile/eslint.org)
@@ -277,6 +277,11 @@ Nitin Kumar
277
277
  The people who review and fix bugs and help triage issues.
278
278
 
279
279
  <table><tbody><tr><td align="center" valign="top" width="11%">
280
+ <a href="https://github.com/DMartens">
281
+ <img src="https://github.com/DMartens.png?s=75" width="75" height="75" alt="fnx's Avatar"><br />
282
+ fnx
283
+ </a>
284
+ </td><td align="center" valign="top" width="11%">
280
285
  <a href="https://github.com/JoshuaKGoldberg">
281
286
  <img src="https://github.com/JoshuaKGoldberg.png?s=75" width="75" height="75" alt="Josh Goldberg ✨'s Avatar"><br />
282
287
  Josh Goldberg ✨
@@ -324,12 +329,11 @@ Percy Ma
324
329
  The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
325
330
  to get your logo on our READMEs and [website](https://eslint.org/sponsors).
326
331
 
327
- <h3>Diamond Sponsors</h3>
328
- <p><a href="https://www.ag-grid.com/"><img src="https://images.opencollective.com/ag-grid/bec0580/logo.png" alt="AG Grid" height="128"></a></p><h3>Platinum Sponsors</h3>
332
+ <h3>Platinum Sponsors</h3>
329
333
  <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>
330
334
  <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> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
331
335
  <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" 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> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
332
- <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://sentry.io"><img src="https://github.com/getsentry.png" alt="Sentry" 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://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> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
336
+ <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://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://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> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
333
337
  <h3>Technology Sponsors</h3>
334
338
  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.
335
339
  <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>
@@ -51,7 +51,7 @@ const debug = require("debug")("eslint:file-enumerator");
51
51
  //------------------------------------------------------------------------------
52
52
 
53
53
  const minimatchOpts = { dot: true, matchBase: true };
54
- const dotfilesPattern = /(?:(?:^\.)|(?:[/\\]\.))[^/\\.].*/u;
54
+ const dotfilesPattern = /(?:^\.|[/\\]\.)[^/\\.].*/u;
55
55
  const NONE = 0;
56
56
  const IGNORED_SILENTLY = 1;
57
57
  const IGNORED = 2;
@@ -17,6 +17,12 @@ const hash = require("./hash");
17
17
 
18
18
  const debug = require("debug")("eslint:lint-result-cache");
19
19
 
20
+ //------------------------------------------------------------------------------
21
+ // Typedefs
22
+ //------------------------------------------------------------------------------
23
+
24
+ /** @typedef {import("../types").Linter.Config} Config */
25
+
20
26
  //-----------------------------------------------------------------------------
21
27
  // Helpers
22
28
  //-----------------------------------------------------------------------------
@@ -40,7 +46,7 @@ function isValidCacheStrategy(cacheStrategy) {
40
46
 
41
47
  /**
42
48
  * Calculates the hash of the config
43
- * @param {ConfigArray} config The config.
49
+ * @param {Config} config The config.
44
50
  * @returns {string} The hash of the config
45
51
  */
46
52
  function hashOfConfigFor(config) {
@@ -96,38 +102,13 @@ class LintResultCache {
96
102
  * cache. If the file is present and has not been changed, rebuild any
97
103
  * missing result information.
98
104
  * @param {string} filePath The file for which to retrieve lint results.
99
- * @param {ConfigArray} config The config of the file.
105
+ * @param {Config} config The config of the file.
100
106
  * @returns {Object|null} The rebuilt lint results, or null if the file is
101
107
  * changed or not in the filesystem.
102
108
  */
103
109
  getCachedLintResults(filePath, config) {
104
- /*
105
- * Cached lint results are valid if and only if:
106
- * 1. The file is present in the filesystem
107
- * 2. The file has not changed since the time it was previously linted
108
- * 3. The ESLint configuration has not changed since the time the file
109
- * was previously linted
110
- * If any of these are not true, we will not reuse the lint results.
111
- */
112
- const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
113
- const hashOfConfig = hashOfConfigFor(config);
114
- const changed =
115
- fileDescriptor.changed ||
116
- fileDescriptor.meta.hashOfConfig !== hashOfConfig;
110
+ const cachedResults = this.getValidCachedLintResults(filePath, config);
117
111
 
118
- if (fileDescriptor.notFound) {
119
- debug(`File not found on the file system: ${filePath}`);
120
- return null;
121
- }
122
-
123
- if (changed) {
124
- debug(`Cache entry not found or no longer valid: ${filePath}`);
125
- return null;
126
- }
127
-
128
- const cachedResults = fileDescriptor.meta.results;
129
-
130
- // Just in case, not sure if this can ever happen.
131
112
  if (!cachedResults) {
132
113
  return cachedResults;
133
114
  }
@@ -151,6 +132,43 @@ class LintResultCache {
151
132
  return results;
152
133
  }
153
134
 
135
+ /**
136
+ * Retrieve cached lint results for a given file path, if present in the
137
+ * cache and still valid.
138
+ * @param {string} filePath The file for which to retrieve lint results.
139
+ * @param {Config} config The config of the file.
140
+ * @returns {Object|null} The cached lint results if present in the cache
141
+ * and still valid; null otherwise.
142
+ */
143
+ getValidCachedLintResults(filePath, config) {
144
+ /*
145
+ * Cached lint results are valid if and only if:
146
+ * 1. The file is present in the filesystem
147
+ * 2. The file has not changed since the time it was previously linted
148
+ * 3. The ESLint configuration has not changed since the time the file
149
+ * was previously linted
150
+ * If any of these are not true, we will not reuse the lint results.
151
+ */
152
+ const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
153
+
154
+ if (fileDescriptor.notFound) {
155
+ debug(`File not found on the file system: ${filePath}`);
156
+ return null;
157
+ }
158
+
159
+ const hashOfConfig = hashOfConfigFor(config);
160
+ const changed =
161
+ fileDescriptor.changed ||
162
+ fileDescriptor.meta.hashOfConfig !== hashOfConfig;
163
+
164
+ if (changed) {
165
+ debug(`Cache entry not found or no longer valid: ${filePath}`);
166
+ return null;
167
+ }
168
+
169
+ return fileDescriptor.meta.results;
170
+ }
171
+
154
172
  /**
155
173
  * Set the cached lint results for a given file path, after removing any
156
174
  * information that will be both unnecessary and difficult to serialize.
@@ -158,7 +176,7 @@ class LintResultCache {
158
176
  * applied), to prevent potentially incorrect results if fixes are not
159
177
  * written to disk.
160
178
  * @param {string} filePath The file for which to set lint results.
161
- * @param {ConfigArray} config The config of the file.
179
+ * @param {Config} config The config of the file.
162
180
  * @param {Object} result The lint result to be set for the file.
163
181
  * @returns {void}
164
182
  */
package/lib/cli.js CHANGED
@@ -16,8 +16,9 @@
16
16
  //------------------------------------------------------------------------------
17
17
 
18
18
  const fs = require("node:fs"),
19
+ { mkdir, stat, writeFile } = require("node:fs/promises"),
19
20
  path = require("node:path"),
20
- { promisify } = require("node:util"),
21
+ { pathToFileURL } = require("node:url"),
21
22
  { LegacyESLint } = require("./eslint"),
22
23
  {
23
24
  ESLint,
@@ -27,282 +28,23 @@ const fs = require("node:fs"),
27
28
  createCLIOptions = require("./options"),
28
29
  log = require("./shared/logging"),
29
30
  RuntimeInfo = require("./shared/runtime-info"),
30
- { normalizeSeverityToString } = require("./shared/severity");
31
- const { ModuleImporter } = require("@humanwhocodes/module-importer");
31
+ translateOptions = require("./shared/translate-cli-options");
32
32
  const { getCacheFile } = require("./eslint/eslint-helpers");
33
33
  const { SuppressionsService } = require("./services/suppressions-service");
34
34
  const debug = require("debug")("eslint:cli");
35
- const {
36
- normalizePackageName,
37
- getShorthandName,
38
- } = require("./shared/naming.js");
39
35
 
40
36
  //------------------------------------------------------------------------------
41
37
  // Types
42
38
  //------------------------------------------------------------------------------
43
39
 
44
- /** @import { ESLintOptions } from "./eslint/eslint.js" */
45
-
46
40
  /** @typedef {import("./options").ParsedCLIOptions} ParsedCLIOptions */
47
- /** @typedef {import("./types").Linter.LintMessage} LintMessage */
48
41
  /** @typedef {import("./types").ESLint.LintResult} LintResult */
49
- /** @typedef {import("./types").ESLint.Plugin} Plugin */
50
42
  /** @typedef {import("./types").ESLint.ResultsMeta} ResultsMeta */
51
43
 
52
44
  //------------------------------------------------------------------------------
53
45
  // Helpers
54
46
  //------------------------------------------------------------------------------
55
47
 
56
- const mkdir = promisify(fs.mkdir);
57
- const stat = promisify(fs.stat);
58
- const writeFile = promisify(fs.writeFile);
59
-
60
- /**
61
- * Loads plugins with the specified names.
62
- * @param {{ "import": (name: string) => Promise<any> }} importer An object with an `import` method called once for each plugin.
63
- * @param {string[]} pluginNames The names of the plugins to be loaded, with or without the "eslint-plugin-" prefix.
64
- * @returns {Promise<Record<string, Plugin>>} A mapping of plugin short names to implementations.
65
- */
66
- async function loadPlugins(importer, pluginNames) {
67
- const plugins = {};
68
-
69
- await Promise.all(
70
- pluginNames.map(async pluginName => {
71
- const longName = normalizePackageName(pluginName, "eslint-plugin");
72
- const module = await importer.import(longName);
73
-
74
- if (!("default" in module)) {
75
- throw new Error(
76
- `"${longName}" cannot be used with the \`--plugin\` option because its default module does not provide a \`default\` export`,
77
- );
78
- }
79
-
80
- const shortName = getShorthandName(pluginName, "eslint-plugin");
81
-
82
- plugins[shortName] = module.default;
83
- }),
84
- );
85
-
86
- return plugins;
87
- }
88
-
89
- /**
90
- * Predicate function for whether or not to apply fixes in quiet mode.
91
- * If a message is a warning, do not apply a fix.
92
- * @param {LintMessage} message The lint result.
93
- * @returns {boolean} True if the lint message is an error (and thus should be
94
- * autofixed), false otherwise.
95
- */
96
- function quietFixPredicate(message) {
97
- return message.severity === 2;
98
- }
99
-
100
- /**
101
- * Predicate function for whether or not to run a rule in quiet mode.
102
- * If a rule is set to warning, do not run it.
103
- * @param {{ ruleId: string; severity: number; }} rule The rule id and severity.
104
- * @returns {boolean} True if the lint rule should run, false otherwise.
105
- */
106
- function quietRuleFilter(rule) {
107
- return rule.severity === 2;
108
- }
109
-
110
- /**
111
- * Translates the CLI options into the options expected by the ESLint constructor.
112
- * @param {ParsedCLIOptions} cliOptions The CLI options to translate.
113
- * @param {"flat"|"eslintrc"} [configType="eslintrc"] The format of the
114
- * config to generate.
115
- * @returns {Promise<ESLintOptions>} The options object for the ESLint constructor.
116
- * @private
117
- */
118
- async function translateOptions(
119
- {
120
- cache,
121
- cacheFile,
122
- cacheLocation,
123
- cacheStrategy,
124
- config,
125
- configLookup,
126
- env,
127
- errorOnUnmatchedPattern,
128
- eslintrc,
129
- ext,
130
- fix,
131
- fixDryRun,
132
- fixType,
133
- flag,
134
- global,
135
- ignore,
136
- ignorePath,
137
- ignorePattern,
138
- inlineConfig,
139
- parser,
140
- parserOptions,
141
- plugin,
142
- quiet,
143
- reportUnusedDisableDirectives,
144
- reportUnusedDisableDirectivesSeverity,
145
- reportUnusedInlineConfigs,
146
- resolvePluginsRelativeTo,
147
- rule,
148
- rulesdir,
149
- stats,
150
- warnIgnored,
151
- passOnNoPatterns,
152
- maxWarnings,
153
- },
154
- configType,
155
- ) {
156
- let overrideConfig, overrideConfigFile;
157
- const importer = new ModuleImporter();
158
-
159
- if (configType === "flat") {
160
- overrideConfigFile =
161
- typeof config === "string" ? config : !configLookup;
162
- if (overrideConfigFile === false) {
163
- overrideConfigFile = void 0;
164
- }
165
-
166
- const languageOptions = {};
167
-
168
- if (global) {
169
- languageOptions.globals = global.reduce((obj, name) => {
170
- if (name.endsWith(":true")) {
171
- obj[name.slice(0, -5)] = "writable";
172
- } else {
173
- obj[name] = "readonly";
174
- }
175
- return obj;
176
- }, {});
177
- }
178
-
179
- if (parserOptions) {
180
- languageOptions.parserOptions = parserOptions;
181
- }
182
-
183
- if (parser) {
184
- languageOptions.parser = await importer.import(parser);
185
- }
186
-
187
- overrideConfig = [
188
- {
189
- ...(Object.keys(languageOptions).length > 0
190
- ? { languageOptions }
191
- : {}),
192
- rules: rule ? rule : {},
193
- },
194
- ];
195
-
196
- if (
197
- reportUnusedDisableDirectives ||
198
- reportUnusedDisableDirectivesSeverity !== void 0
199
- ) {
200
- overrideConfig[0].linterOptions = {
201
- reportUnusedDisableDirectives: reportUnusedDisableDirectives
202
- ? "error"
203
- : normalizeSeverityToString(
204
- reportUnusedDisableDirectivesSeverity,
205
- ),
206
- };
207
- }
208
-
209
- if (reportUnusedInlineConfigs !== void 0) {
210
- overrideConfig[0].linterOptions = {
211
- ...overrideConfig[0].linterOptions,
212
- reportUnusedInlineConfigs: normalizeSeverityToString(
213
- reportUnusedInlineConfigs,
214
- ),
215
- };
216
- }
217
-
218
- if (plugin) {
219
- overrideConfig[0].plugins = await loadPlugins(importer, plugin);
220
- }
221
-
222
- if (ext) {
223
- overrideConfig.push({
224
- files: ext.map(
225
- extension =>
226
- `**/*${extension.startsWith(".") ? "" : "."}${extension}`,
227
- ),
228
- });
229
- }
230
- } else {
231
- overrideConfigFile = config;
232
-
233
- overrideConfig = {
234
- env:
235
- env &&
236
- env.reduce((obj, name) => {
237
- obj[name] = true;
238
- return obj;
239
- }, {}),
240
- globals:
241
- global &&
242
- global.reduce((obj, name) => {
243
- if (name.endsWith(":true")) {
244
- obj[name.slice(0, -5)] = "writable";
245
- } else {
246
- obj[name] = "readonly";
247
- }
248
- return obj;
249
- }, {}),
250
- ignorePatterns: ignorePattern,
251
- parser,
252
- parserOptions,
253
- plugins: plugin,
254
- rules: rule,
255
- };
256
- }
257
-
258
- const options = {
259
- allowInlineConfig: inlineConfig,
260
- cache,
261
- cacheLocation: cacheLocation || cacheFile,
262
- cacheStrategy,
263
- errorOnUnmatchedPattern,
264
- fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
265
- fixTypes: fixType,
266
- ignore,
267
- overrideConfig,
268
- overrideConfigFile,
269
- passOnNoPatterns,
270
- };
271
-
272
- if (configType === "flat") {
273
- options.ignorePatterns = ignorePattern;
274
- options.stats = stats;
275
- options.warnIgnored = warnIgnored;
276
- options.flags = flag;
277
-
278
- /*
279
- * For performance reasons rules not marked as 'error' are filtered out in quiet mode. As maxWarnings
280
- * requires rules set to 'warn' to be run, we only filter out 'warn' rules if maxWarnings is not specified.
281
- */
282
- options.ruleFilter =
283
- quiet && maxWarnings === -1 ? quietRuleFilter : () => true;
284
- } else {
285
- options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
286
- options.rulePaths = rulesdir;
287
- options.useEslintrc = eslintrc;
288
- options.extensions = ext;
289
- options.ignorePath = ignorePath;
290
- if (
291
- reportUnusedDisableDirectives ||
292
- reportUnusedDisableDirectivesSeverity !== void 0
293
- ) {
294
- options.reportUnusedDisableDirectives =
295
- reportUnusedDisableDirectives
296
- ? "error"
297
- : normalizeSeverityToString(
298
- reportUnusedDisableDirectivesSeverity,
299
- );
300
- }
301
- }
302
-
303
- return options;
304
- }
305
-
306
48
  /**
307
49
  * Count error messages.
308
50
  * @param {LintResult[]} results The lint results.
@@ -322,6 +64,26 @@ function countErrors(results) {
322
64
  return { errorCount, fatalErrorCount, warningCount };
323
65
  }
324
66
 
67
+ /**
68
+ * Creates an options module from the provided CLI options and encodes it as a data URL.
69
+ * @param {ParsedCLIOptions} options The CLI options.
70
+ * @returns {URL} The URL of the options module.
71
+ */
72
+ function createOptionsModule(options) {
73
+ const translateOptionsFileURL = new URL(
74
+ "./shared/translate-cli-options.js",
75
+ pathToFileURL(__filename),
76
+ ).href;
77
+ const optionsSrc =
78
+ `import translateOptions from ${JSON.stringify(translateOptionsFileURL)};\n` +
79
+ `export default await translateOptions(${JSON.stringify(options)}, "flat");\n`;
80
+
81
+ // Base64 encoding is typically shorter than URL encoding
82
+ return new URL(
83
+ `data:text/javascript;base64,${Buffer.from(optionsSrc).toString("base64")}`,
84
+ );
85
+ }
86
+
325
87
  /**
326
88
  * Check if a given file path is a directory or not.
327
89
  * @param {string} filePath The path to a file to check.
@@ -385,6 +147,30 @@ async function printResults(engine, results, format, outputFile, resultsMeta) {
385
147
  return true;
386
148
  }
387
149
 
150
+ /**
151
+ * Validates the `--concurrency` flag value.
152
+ * @param {string} concurrency The `--concurrency` flag value to validate.
153
+ * @returns {void}
154
+ * @throws {Error} If the `--concurrency` flag value is invalid.
155
+ */
156
+ function validateConcurrency(concurrency) {
157
+ if (
158
+ concurrency === void 0 ||
159
+ concurrency === "auto" ||
160
+ concurrency === "off"
161
+ ) {
162
+ return;
163
+ }
164
+
165
+ const concurrencyValue = Number(concurrency);
166
+
167
+ if (!Number.isInteger(concurrencyValue) || concurrencyValue < 1) {
168
+ throw new Error(
169
+ `Option concurrency: '${concurrency}' is not a positive integer, 'auto' or 'off'.`,
170
+ );
171
+ }
172
+ }
173
+
388
174
  //------------------------------------------------------------------------------
389
175
  // Public Interface
390
176
  //------------------------------------------------------------------------------
@@ -445,6 +231,7 @@ const cli = {
445
231
 
446
232
  try {
447
233
  options = CLIOptions.parse(args);
234
+ validateConcurrency(options.concurrency);
448
235
  } catch (error) {
449
236
  debug("Error parsing CLI options:", error.message);
450
237
 
@@ -614,11 +401,20 @@ const cli = {
614
401
  }
615
402
 
616
403
  const ActiveESLint = usingFlatConfig ? ESLint : LegacyESLint;
617
- const eslintOptions = await translateOptions(
618
- options,
619
- usingFlatConfig ? "flat" : "eslintrc",
620
- );
621
- const engine = new ActiveESLint(eslintOptions);
404
+
405
+ /** @type {ESLint|LegacyESLint} */
406
+ let engine;
407
+
408
+ if (options.concurrency && options.concurrency !== "off") {
409
+ const optionsURL = createOptionsModule(options);
410
+ engine = await ESLint.fromOptionsModule(optionsURL);
411
+ } else {
412
+ const eslintOptions = await translateOptions(
413
+ options,
414
+ usingFlatConfig ? "flat" : "eslintrc",
415
+ );
416
+ engine = new ActiveESLint(eslintOptions);
417
+ }
622
418
  let results;
623
419
 
624
420
  if (useStdin) {
@@ -230,7 +230,7 @@ function assertIsRuleSeverity(ruleId, value) {
230
230
  * @throws {TypeError} If the string isn't in the correct format.
231
231
  */
232
232
  function assertIsPluginMemberName(value) {
233
- if (!/[@a-z0-9-_$]+(?:\/(?:[a-z0-9-_$]+))+$/iu.test(value)) {
233
+ if (!/[\w\-@$]+(?:\/[\w\-$]+)+$/iu.test(value)) {
234
234
  throw new TypeError(
235
235
  `Expected string in the form "pluginName/objectName" but found "${value}".`,
236
236
  );