eslint 8.56.0 → 9.0.0-alpha.1
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 +7 -2
- package/conf/ecma-version.js +16 -0
- package/conf/rule-type-list.json +3 -1
- package/lib/api.js +1 -1
- package/lib/cli-engine/cli-engine.js +14 -3
- package/lib/cli-engine/formatters/formatters-meta.json +1 -29
- package/lib/cli-engine/lint-result-cache.js +2 -2
- package/lib/cli.js +46 -25
- package/lib/config/default-config.js +3 -0
- package/lib/config/flat-config-array.js +0 -20
- package/lib/config/flat-config-helpers.js +41 -20
- package/lib/config/flat-config-schema.js +35 -25
- package/lib/config/rule-validator.js +27 -4
- package/lib/eslint/eslint-helpers.js +32 -12
- package/lib/eslint/eslint.js +856 -373
- package/lib/eslint/index.js +2 -2
- package/lib/eslint/legacy-eslint.js +722 -0
- package/lib/linter/apply-disable-directives.js +35 -7
- package/lib/linter/code-path-analysis/code-path.js +5 -19
- package/lib/linter/code-path-analysis/fork-context.js +1 -1
- package/lib/linter/config-comment-parser.js +8 -11
- package/lib/linter/linter.js +196 -100
- package/lib/linter/report-translator.js +2 -2
- package/lib/linter/rules.js +6 -15
- package/lib/linter/source-code-fixer.js +1 -1
- package/lib/options.js +9 -1
- package/lib/rule-tester/rule-tester.js +234 -291
- package/lib/rules/array-bracket-newline.js +1 -1
- package/lib/rules/array-bracket-spacing.js +1 -1
- package/lib/rules/block-scoped-var.js +1 -1
- package/lib/rules/callback-return.js +2 -2
- package/lib/rules/comma-dangle.js +1 -1
- package/lib/rules/comma-style.js +2 -2
- package/lib/rules/complexity.js +1 -1
- package/lib/rules/constructor-super.js +1 -1
- package/lib/rules/default-case.js +1 -1
- package/lib/rules/eol-last.js +2 -2
- package/lib/rules/function-paren-newline.js +2 -2
- package/lib/rules/indent-legacy.js +5 -5
- package/lib/rules/indent.js +5 -5
- package/lib/rules/index.js +1 -2
- package/lib/rules/key-spacing.js +2 -2
- package/lib/rules/line-comment-position.js +1 -1
- package/lib/rules/lines-around-directive.js +2 -2
- package/lib/rules/max-depth.js +1 -1
- package/lib/rules/max-len.js +3 -3
- package/lib/rules/max-lines.js +3 -3
- package/lib/rules/max-nested-callbacks.js +1 -1
- package/lib/rules/max-params.js +1 -1
- package/lib/rules/max-statements.js +1 -1
- package/lib/rules/multiline-comment-style.js +7 -7
- package/lib/rules/new-cap.js +1 -1
- package/lib/rules/newline-after-var.js +1 -1
- package/lib/rules/newline-before-return.js +1 -1
- package/lib/rules/no-constant-binary-expression.js +6 -6
- package/lib/rules/no-constructor-return.js +2 -2
- package/lib/rules/no-dupe-class-members.js +2 -2
- package/lib/rules/no-else-return.js +1 -1
- package/lib/rules/no-empty-function.js +2 -2
- package/lib/rules/no-empty-static-block.js +1 -1
- package/lib/rules/no-extra-semi.js +1 -1
- package/lib/rules/no-fallthrough.js +1 -1
- package/lib/rules/no-implicit-coercion.js +17 -1
- package/lib/rules/no-inner-declarations.js +23 -2
- package/lib/rules/no-invalid-regexp.js +1 -1
- package/lib/rules/no-invalid-this.js +1 -1
- package/lib/rules/no-lone-blocks.js +2 -2
- package/lib/rules/no-loss-of-precision.js +1 -1
- package/lib/rules/no-misleading-character-class.js +174 -65
- package/lib/rules/no-mixed-spaces-and-tabs.js +1 -1
- package/lib/rules/no-multiple-empty-lines.js +1 -1
- package/lib/rules/no-new-native-nonconstructor.js +1 -1
- package/lib/rules/no-new-symbol.js +8 -1
- package/lib/rules/no-restricted-globals.js +1 -1
- package/lib/rules/no-restricted-imports.js +2 -2
- package/lib/rules/no-restricted-modules.js +2 -2
- package/lib/rules/no-return-await.js +1 -1
- package/lib/rules/no-sequences.js +1 -0
- package/lib/rules/no-trailing-spaces.js +2 -3
- package/lib/rules/no-unneeded-ternary.js +1 -1
- package/lib/rules/no-unsafe-optional-chaining.js +1 -1
- package/lib/rules/no-unused-private-class-members.js +1 -1
- package/lib/rules/no-unused-vars.js +6 -8
- package/lib/rules/no-useless-assignment.js +566 -0
- package/lib/rules/no-useless-backreference.js +1 -1
- package/lib/rules/object-curly-spacing.js +3 -3
- package/lib/rules/object-property-newline.js +1 -1
- package/lib/rules/one-var.js +5 -5
- package/lib/rules/padded-blocks.js +7 -7
- package/lib/rules/prefer-arrow-callback.js +3 -3
- package/lib/rules/prefer-reflect.js +1 -1
- package/lib/rules/prefer-regex-literals.js +1 -1
- package/lib/rules/prefer-template.js +1 -1
- package/lib/rules/radix.js +2 -2
- package/lib/rules/semi-style.js +1 -1
- package/lib/rules/sort-imports.js +1 -1
- package/lib/rules/sort-keys.js +1 -1
- package/lib/rules/sort-vars.js +1 -1
- package/lib/rules/space-unary-ops.js +1 -1
- package/lib/rules/strict.js +1 -1
- package/lib/rules/utils/ast-utils.js +7 -7
- package/lib/rules/yield-star-spacing.js +1 -1
- package/lib/shared/types.js +1 -1
- package/lib/source-code/source-code.js +5 -83
- package/lib/source-code/token-store/index.js +2 -2
- package/lib/unsupported-api.js +3 -5
- package/package.json +12 -14
- package/conf/config-schema.js +0 -93
- package/lib/cli-engine/formatters/checkstyle.js +0 -60
- package/lib/cli-engine/formatters/compact.js +0 -60
- package/lib/cli-engine/formatters/jslint-xml.js +0 -41
- package/lib/cli-engine/formatters/junit.js +0 -82
- package/lib/cli-engine/formatters/tap.js +0 -95
- package/lib/cli-engine/formatters/unix.js +0 -58
- package/lib/cli-engine/formatters/visualstudio.js +0 -63
- package/lib/eslint/flat-eslint.js +0 -1142
- package/lib/rule-tester/flat-rule-tester.js +0 -1122
- package/lib/rules/require-jsdoc.js +0 -122
- package/lib/rules/valid-jsdoc.js +0 -516
- package/lib/shared/config-validator.js +0 -347
- package/lib/shared/relative-module-resolver.js +0 -50
package/README.md
CHANGED
@@ -43,7 +43,7 @@ ESLint is a tool for identifying and reporting on patterns found in ECMAScript/J
|
|
43
43
|
|
44
44
|
## Installation and Usage
|
45
45
|
|
46
|
-
Prerequisites: [Node.js](https://nodejs.org/) (`^
|
46
|
+
Prerequisites: [Node.js](https://nodejs.org/) (`^18.18.0`, `^20.9.0`, or `>=21.1.0`) built with SSL support. (If you are using an official Node.js distribution, SSL is always built in.)
|
47
47
|
|
48
48
|
You can install and configure ESLint using this command:
|
49
49
|
|
@@ -245,6 +245,11 @@ The people who review and fix bugs and help triage issues.
|
|
245
245
|
Bryan Mishkin
|
246
246
|
</a>
|
247
247
|
</td><td align="center" valign="top" width="11%">
|
248
|
+
<a href="https://github.com/JoshuaKGoldberg">
|
249
|
+
<img src="https://github.com/JoshuaKGoldberg.png?s=75" width="75" height="75" alt="Josh Goldberg ✨'s Avatar"><br />
|
250
|
+
Josh Goldberg ✨
|
251
|
+
</a>
|
252
|
+
</td><td align="center" valign="top" width="11%">
|
248
253
|
<a href="https://github.com/fasttime">
|
249
254
|
<img src="https://github.com/fasttime.png?s=75" width="75" height="75" alt="Francesco Trotta's Avatar"><br />
|
250
255
|
Francesco Trotta
|
@@ -293,7 +298,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
293
298
|
<h3>Platinum Sponsors</h3>
|
294
299
|
<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
300
|
<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> <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>
|
301
|
+
<p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/eb04ddc/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
|
297
302
|
<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
303
|
<!--sponsorsend-->
|
299
304
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Configuration related to ECMAScript versions
|
3
|
+
* @author Milos Djermanovic
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
/**
|
9
|
+
* The latest ECMAScript version supported by ESLint.
|
10
|
+
* @type {number} year-based ECMAScript version
|
11
|
+
*/
|
12
|
+
const LATEST_ECMA_VERSION = 2024;
|
13
|
+
|
14
|
+
module.exports = {
|
15
|
+
LATEST_ECMA_VERSION
|
16
|
+
};
|
package/conf/rule-type-list.json
CHANGED
@@ -23,6 +23,8 @@
|
|
23
23
|
{ "removed": "space-in-brackets", "replacedBy": ["object-curly-spacing", "array-bracket-spacing"] },
|
24
24
|
{ "removed": "space-return-throw-case", "replacedBy": ["keyword-spacing"] },
|
25
25
|
{ "removed": "space-unary-word-ops", "replacedBy": ["space-unary-ops"] },
|
26
|
-
{ "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] }
|
26
|
+
{ "removed": "spaced-line-comment", "replacedBy": ["spaced-comment"] },
|
27
|
+
{ "removed": "valid-jsdoc", "replacedBy": [] },
|
28
|
+
{ "removed": "require-jsdoc", "replacedBy": [] }
|
27
29
|
]
|
28
30
|
}
|
package/lib/api.js
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
// Requirements
|
10
10
|
//-----------------------------------------------------------------------------
|
11
11
|
|
12
|
-
const { ESLint } = require("./eslint");
|
12
|
+
const { ESLint } = require("./eslint/eslint");
|
13
13
|
const { Linter } = require("./linter");
|
14
14
|
const { RuleTester } = require("./rule-tester");
|
15
15
|
const { SourceCode } = require("./source-code");
|
@@ -41,6 +41,17 @@ const hash = require("./hash");
|
|
41
41
|
const LintResultCache = require("./lint-result-cache");
|
42
42
|
|
43
43
|
const debug = require("debug")("eslint:cli-engine");
|
44
|
+
const removedFormatters = new Set([
|
45
|
+
"checkstyle",
|
46
|
+
"codeframe",
|
47
|
+
"compact",
|
48
|
+
"jslint-xml",
|
49
|
+
"junit",
|
50
|
+
"table",
|
51
|
+
"tap",
|
52
|
+
"unix",
|
53
|
+
"visualstudio"
|
54
|
+
]);
|
44
55
|
const validFixTypes = new Set(["directive", "problem", "suggestion", "layout"]);
|
45
56
|
|
46
57
|
//------------------------------------------------------------------------------
|
@@ -639,7 +650,7 @@ class CLIEngine {
|
|
639
650
|
});
|
640
651
|
const lintResultCache =
|
641
652
|
options.cache ? new LintResultCache(cacheFilePath, options.cacheStrategy) : null;
|
642
|
-
const linter = new Linter({ cwd: options.cwd });
|
653
|
+
const linter = new Linter({ cwd: options.cwd, configType: "eslintrc" });
|
643
654
|
|
644
655
|
/** @type {ConfigArray[]} */
|
645
656
|
const lastConfigArrays = [configArrayFactory.getConfigArrayForFile()];
|
@@ -721,7 +732,7 @@ class CLIEngine {
|
|
721
732
|
* @returns {void}
|
722
733
|
*/
|
723
734
|
static outputFixes(report) {
|
724
|
-
report.results.filter(result => Object.
|
735
|
+
report.results.filter(result => Object.hasOwn(result, "output")).forEach(result => {
|
725
736
|
fs.writeFileSync(result.filePath, result.output);
|
726
737
|
});
|
727
738
|
}
|
@@ -1047,7 +1058,7 @@ class CLIEngine {
|
|
1047
1058
|
try {
|
1048
1059
|
return require(formatterPath);
|
1049
1060
|
} catch (ex) {
|
1050
|
-
if (format
|
1061
|
+
if (removedFormatters.has(format)) {
|
1051
1062
|
ex.message = `The ${format} formatter is no longer part of core ESLint. Install it manually with \`npm install -D eslint-formatter-${format}\``;
|
1052
1063
|
} else {
|
1053
1064
|
ex.message = `There was a problem loading formatter: ${formatterPath}\nError: ${ex.message}`;
|
@@ -1,20 +1,8 @@
|
|
1
1
|
[
|
2
|
-
{
|
3
|
-
"name": "checkstyle",
|
4
|
-
"description": "Outputs results to the [Checkstyle](https://checkstyle.sourceforge.io/) format."
|
5
|
-
},
|
6
|
-
{
|
7
|
-
"name": "compact",
|
8
|
-
"description": "Human-readable output format. Mimics the default output of JSHint."
|
9
|
-
},
|
10
2
|
{
|
11
3
|
"name": "html",
|
12
4
|
"description": "Outputs results to HTML. The `html` formatter is useful for visual presentation in the browser."
|
13
5
|
},
|
14
|
-
{
|
15
|
-
"name": "jslint-xml",
|
16
|
-
"description": "Outputs results to format compatible with the [JSLint Jenkins plugin](https://plugins.jenkins.io/jslint/)."
|
17
|
-
},
|
18
6
|
{
|
19
7
|
"name": "json-with-metadata",
|
20
8
|
"description": "Outputs JSON-serialized results. The `json-with-metadata` provides the same linting results as the [`json`](#json) formatter with additional metadata about the rules applied. The linting results are included in the `results` property and the rules metadata is included in the `metadata` property.\n\nAlternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint."
|
@@ -23,24 +11,8 @@
|
|
23
11
|
"name": "json",
|
24
12
|
"description": "Outputs JSON-serialized results. The `json` formatter is useful when you want to programmatically work with the CLI's linting results.\n\nAlternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint."
|
25
13
|
},
|
26
|
-
{
|
27
|
-
"name": "junit",
|
28
|
-
"description": "Outputs results to format compatible with the [JUnit Jenkins plugin](https://plugins.jenkins.io/junit/)."
|
29
|
-
},
|
30
14
|
{
|
31
15
|
"name": "stylish",
|
32
16
|
"description": "Human-readable output format. This is the default formatter."
|
33
|
-
},
|
34
|
-
{
|
35
|
-
"name": "tap",
|
36
|
-
"description": "Outputs results to the [Test Anything Protocol (TAP)](https://testanything.org/) specification format."
|
37
|
-
},
|
38
|
-
{
|
39
|
-
"name": "unix",
|
40
|
-
"description": "Outputs results to a format similar to many commands in UNIX-like systems. Parsable with tools such as [grep](https://www.gnu.org/software/grep/manual/grep.html), [sed](https://www.gnu.org/software/sed/manual/sed.html), and [awk](https://www.gnu.org/software/gawk/manual/gawk.html)."
|
41
|
-
},
|
42
|
-
{
|
43
|
-
"name": "visualstudio",
|
44
|
-
"description": "Outputs results to format compatible with the integrated terminal of the [Visual Studio](https://visualstudio.microsoft.com/) IDE. When using Visual Studio, you can click on the linting results in the integrated terminal to go to the issue in the source code."
|
45
17
|
}
|
46
|
-
]
|
18
|
+
]
|
@@ -164,7 +164,7 @@ class LintResultCache {
|
|
164
164
|
* @returns {void}
|
165
165
|
*/
|
166
166
|
setCachedLintResults(filePath, config, result) {
|
167
|
-
if (result && Object.
|
167
|
+
if (result && Object.hasOwn(result, "output")) {
|
168
168
|
return;
|
169
169
|
}
|
170
170
|
|
@@ -181,7 +181,7 @@ class LintResultCache {
|
|
181
181
|
* In `getCachedLintResults`, if source is explicitly null, we will
|
182
182
|
* read the file from the filesystem to set the value again.
|
183
183
|
*/
|
184
|
-
if (Object.
|
184
|
+
if (Object.hasOwn(resultToSerialize, "source")) {
|
185
185
|
resultToSerialize.source = null;
|
186
186
|
}
|
187
187
|
|
package/lib/cli.js
CHANGED
@@ -18,8 +18,8 @@
|
|
18
18
|
const fs = require("fs"),
|
19
19
|
path = require("path"),
|
20
20
|
{ promisify } = require("util"),
|
21
|
-
{
|
22
|
-
{
|
21
|
+
{ LegacyESLint } = require("./eslint"),
|
22
|
+
{ ESLint, shouldUseFlatConfig } = require("./eslint/eslint"),
|
23
23
|
createCLIOptions = require("./options"),
|
24
24
|
log = require("./shared/logging"),
|
25
25
|
RuntimeInfo = require("./shared/runtime-info"),
|
@@ -58,6 +58,16 @@ function quietFixPredicate(message) {
|
|
58
58
|
return message.severity === 2;
|
59
59
|
}
|
60
60
|
|
61
|
+
/**
|
62
|
+
* Predicate function for whether or not to run a rule in quiet mode.
|
63
|
+
* If a rule is set to warning, do not run it.
|
64
|
+
* @param {{ ruleId: string; severity: number; }} rule The rule id and severity.
|
65
|
+
* @returns {boolean} True if the lint rule should run, false otherwise.
|
66
|
+
*/
|
67
|
+
function quietRuleFilter(rule) {
|
68
|
+
return rule.severity === 2;
|
69
|
+
}
|
70
|
+
|
61
71
|
/**
|
62
72
|
* Translates the CLI options into the options expected by the ESLint constructor.
|
63
73
|
* @param {ParsedCLIOptions} cliOptions The CLI options to translate.
|
@@ -94,7 +104,9 @@ async function translateOptions({
|
|
94
104
|
resolvePluginsRelativeTo,
|
95
105
|
rule,
|
96
106
|
rulesdir,
|
97
|
-
warnIgnored
|
107
|
+
warnIgnored,
|
108
|
+
passOnNoPatterns,
|
109
|
+
maxWarnings
|
98
110
|
}, configType) {
|
99
111
|
|
100
112
|
let overrideConfig, overrideConfigFile;
|
@@ -187,12 +199,19 @@ async function translateOptions({
|
|
187
199
|
fixTypes: fixType,
|
188
200
|
ignore,
|
189
201
|
overrideConfig,
|
190
|
-
overrideConfigFile
|
202
|
+
overrideConfigFile,
|
203
|
+
passOnNoPatterns
|
191
204
|
};
|
192
205
|
|
193
206
|
if (configType === "flat") {
|
194
207
|
options.ignorePatterns = ignorePattern;
|
195
208
|
options.warnIgnored = warnIgnored;
|
209
|
+
|
210
|
+
/*
|
211
|
+
* For performance reasons rules not marked as 'error' are filtered out in quiet mode. As maxWarnings
|
212
|
+
* requires rules set to 'warn' to be run, we only filter out 'warn' rules if maxWarnings is not specified.
|
213
|
+
*/
|
214
|
+
options.ruleFilter = quiet && maxWarnings === -1 ? quietRuleFilter : () => true;
|
196
215
|
} else {
|
197
216
|
options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
|
198
217
|
options.rulePaths = rulesdir;
|
@@ -266,25 +285,23 @@ async function printResults(engine, results, format, outputFile, resultsMeta) {
|
|
266
285
|
|
267
286
|
const output = await formatter.format(results, resultsMeta);
|
268
287
|
|
269
|
-
if (
|
270
|
-
|
271
|
-
const filePath = path.resolve(process.cwd(), outputFile);
|
288
|
+
if (outputFile) {
|
289
|
+
const filePath = path.resolve(process.cwd(), outputFile);
|
272
290
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
291
|
+
if (await isDirectory(filePath)) {
|
292
|
+
log.error("Cannot write to output file path, it is a directory: %s", outputFile);
|
293
|
+
return false;
|
294
|
+
}
|
277
295
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
}
|
285
|
-
} else {
|
286
|
-
log.info(output);
|
296
|
+
try {
|
297
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
298
|
+
await writeFile(filePath, output);
|
299
|
+
} catch (ex) {
|
300
|
+
log.error("There was a problem writing the output file:\n%s", ex);
|
301
|
+
return false;
|
287
302
|
}
|
303
|
+
} else if (output) {
|
304
|
+
log.info(output);
|
288
305
|
}
|
289
306
|
|
290
307
|
return true;
|
@@ -304,10 +321,10 @@ const cli = {
|
|
304
321
|
* Executes the CLI based on an array of arguments that is passed in.
|
305
322
|
* @param {string|Array|Object} args The arguments to process.
|
306
323
|
* @param {string} [text] The text to lint (used for TTY).
|
307
|
-
* @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
|
324
|
+
* @param {boolean} [allowFlatConfig=true] Whether or not to allow flat config.
|
308
325
|
* @returns {Promise<number>} The exit code for the operation.
|
309
326
|
*/
|
310
|
-
async execute(args, text, allowFlatConfig) {
|
327
|
+
async execute(args, text, allowFlatConfig = true) {
|
311
328
|
if (Array.isArray(args)) {
|
312
329
|
debug("CLI args: %o", args.slice(2));
|
313
330
|
}
|
@@ -323,6 +340,10 @@ const cli = {
|
|
323
340
|
|
324
341
|
debug("Using flat config?", usingFlatConfig);
|
325
342
|
|
343
|
+
if (allowFlatConfig && !usingFlatConfig) {
|
344
|
+
process.emitWarning("You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details.", "ESLintRCWarning");
|
345
|
+
}
|
346
|
+
|
326
347
|
const CLIOptions = createCLIOptions(usingFlatConfig);
|
327
348
|
|
328
349
|
/** @type {ParsedCLIOptions} */
|
@@ -376,8 +397,8 @@ const cli = {
|
|
376
397
|
}
|
377
398
|
|
378
399
|
const engine = usingFlatConfig
|
379
|
-
? new
|
380
|
-
: new
|
400
|
+
? new ESLint(await translateOptions(options, "flat"))
|
401
|
+
: new LegacyESLint(await translateOptions(options));
|
381
402
|
const fileConfig =
|
382
403
|
await engine.calculateConfigForFile(options.printConfig);
|
383
404
|
|
@@ -405,7 +426,7 @@ const cli = {
|
|
405
426
|
return 2;
|
406
427
|
}
|
407
428
|
|
408
|
-
const ActiveESLint = usingFlatConfig ?
|
429
|
+
const ActiveESLint = usingFlatConfig ? ESLint : LegacyESLint;
|
409
430
|
|
410
431
|
const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
|
411
432
|
let results;
|
@@ -13,7 +13,6 @@ const { ConfigArray, ConfigArraySymbol } = require("@humanwhocodes/config-array"
|
|
13
13
|
const { flatConfigSchema } = require("./flat-config-schema");
|
14
14
|
const { RuleValidator } = require("./rule-validator");
|
15
15
|
const { defaultConfig } = require("./default-config");
|
16
|
-
const jsPlugin = require("@eslint/js");
|
17
16
|
|
18
17
|
//-----------------------------------------------------------------------------
|
19
18
|
// Helpers
|
@@ -134,25 +133,6 @@ class FlatConfigArray extends ConfigArray {
|
|
134
133
|
* @returns {Object} The preprocessed config.
|
135
134
|
*/
|
136
135
|
[ConfigArraySymbol.preprocessConfig](config) {
|
137
|
-
if (config === "eslint:recommended") {
|
138
|
-
|
139
|
-
// if we are in a Node.js environment warn the user
|
140
|
-
if (typeof process !== "undefined" && process.emitWarning) {
|
141
|
-
process.emitWarning("The 'eslint:recommended' string configuration is deprecated and will be replaced by the @eslint/js package's 'recommended' config.");
|
142
|
-
}
|
143
|
-
|
144
|
-
return jsPlugin.configs.recommended;
|
145
|
-
}
|
146
|
-
|
147
|
-
if (config === "eslint:all") {
|
148
|
-
|
149
|
-
// if we are in a Node.js environment warn the user
|
150
|
-
if (typeof process !== "undefined" && process.emitWarning) {
|
151
|
-
process.emitWarning("The 'eslint:all' string configuration is deprecated and will be replaced by the @eslint/js package's 'all' config.");
|
152
|
-
}
|
153
|
-
|
154
|
-
return jsPlugin.configs.all;
|
155
|
-
}
|
156
136
|
|
157
137
|
/*
|
158
138
|
* If `shouldIgnore` is false, we remove any ignore patterns specified
|
@@ -5,6 +5,23 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Typedefs
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
/** @typedef {import("../shared/types").Rule} Rule */
|
13
|
+
|
14
|
+
//------------------------------------------------------------------------------
|
15
|
+
// Private Members
|
16
|
+
//------------------------------------------------------------------------------
|
17
|
+
|
18
|
+
// JSON schema that disallows passing any options
|
19
|
+
const noOptionsSchema = Object.freeze({
|
20
|
+
type: "array",
|
21
|
+
minItems: 0,
|
22
|
+
maxItems: 0
|
23
|
+
});
|
24
|
+
|
8
25
|
//-----------------------------------------------------------------------------
|
9
26
|
// Functions
|
10
27
|
//-----------------------------------------------------------------------------
|
@@ -52,32 +69,39 @@ function getRuleFromConfig(ruleId, config) {
|
|
52
69
|
const { pluginName, ruleName } = parseRuleId(ruleId);
|
53
70
|
|
54
71
|
const plugin = config.plugins && config.plugins[pluginName];
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
// normalize function rules into objects
|
59
|
-
if (rule && typeof rule === "function") {
|
60
|
-
rule = {
|
61
|
-
create: rule
|
62
|
-
};
|
63
|
-
}
|
72
|
+
const rule = plugin && plugin.rules && plugin.rules[ruleName];
|
64
73
|
|
65
74
|
return rule;
|
66
75
|
}
|
67
76
|
|
68
77
|
/**
|
69
78
|
* Gets a complete options schema for a rule.
|
70
|
-
* @param {
|
71
|
-
* @
|
79
|
+
* @param {Rule} rule A rule object
|
80
|
+
* @throws {TypeError} If `meta.schema` is specified but is not an array, object or `false`.
|
81
|
+
* @returns {Object|null} JSON Schema for the rule's options. `null` if `meta.schema` is `false`.
|
72
82
|
*/
|
73
83
|
function getRuleOptionsSchema(rule) {
|
74
84
|
|
75
|
-
if (!rule) {
|
85
|
+
if (!rule.meta) {
|
86
|
+
return { ...noOptionsSchema }; // default if `meta.schema` is not specified
|
87
|
+
}
|
88
|
+
|
89
|
+
const schema = rule.meta.schema;
|
90
|
+
|
91
|
+
if (typeof schema === "undefined") {
|
92
|
+
return { ...noOptionsSchema }; // default if `meta.schema` is not specified
|
93
|
+
}
|
94
|
+
|
95
|
+
// `schema:false` is an allowed explicit opt-out of options validation for the rule
|
96
|
+
if (schema === false) {
|
76
97
|
return null;
|
77
98
|
}
|
78
99
|
|
79
|
-
|
100
|
+
if (typeof schema !== "object" || schema === null) {
|
101
|
+
throw new TypeError("Rule's `meta.schema` must be an array or object");
|
102
|
+
}
|
80
103
|
|
104
|
+
// ESLint-specific array form needs to be converted into a valid JSON Schema definition
|
81
105
|
if (Array.isArray(schema)) {
|
82
106
|
if (schema.length) {
|
83
107
|
return {
|
@@ -87,16 +111,13 @@ function getRuleOptionsSchema(rule) {
|
|
87
111
|
maxItems: schema.length
|
88
112
|
};
|
89
113
|
}
|
90
|
-
return {
|
91
|
-
type: "array",
|
92
|
-
minItems: 0,
|
93
|
-
maxItems: 0
|
94
|
-
};
|
95
114
|
|
115
|
+
// `schema:[]` is an explicit way to specify that the rule does not accept any options
|
116
|
+
return { ...noOptionsSchema };
|
96
117
|
}
|
97
118
|
|
98
|
-
//
|
99
|
-
return schema
|
119
|
+
// `schema:<object>` is assumed to be a valid JSON Schema definition
|
120
|
+
return schema;
|
100
121
|
}
|
101
122
|
|
102
123
|
|
@@ -9,11 +9,6 @@
|
|
9
9
|
// Requirements
|
10
10
|
//-----------------------------------------------------------------------------
|
11
11
|
|
12
|
-
/*
|
13
|
-
* Note: This can be removed in ESLint v9 because structuredClone is available globally
|
14
|
-
* starting in Node.js v17.
|
15
|
-
*/
|
16
|
-
const structuredClone = require("@ungap/structured-clone").default;
|
17
12
|
const { normalizeSeverityToNumber } = require("../shared/severity");
|
18
13
|
|
19
14
|
//-----------------------------------------------------------------------------
|
@@ -53,6 +48,15 @@ function isNonNullObject(value) {
|
|
53
48
|
return typeof value === "object" && value !== null;
|
54
49
|
}
|
55
50
|
|
51
|
+
/**
|
52
|
+
* Check if a value is a non-null non-array object.
|
53
|
+
* @param {any} value The value to check.
|
54
|
+
* @returns {boolean} `true` if the value is a non-null non-array object.
|
55
|
+
*/
|
56
|
+
function isNonArrayObject(value) {
|
57
|
+
return isNonNullObject(value) && !Array.isArray(value);
|
58
|
+
}
|
59
|
+
|
56
60
|
/**
|
57
61
|
* Check if a value is undefined.
|
58
62
|
* @param {any} value The value to check.
|
@@ -63,19 +67,27 @@ function isUndefined(value) {
|
|
63
67
|
}
|
64
68
|
|
65
69
|
/**
|
66
|
-
* Deeply merges two objects.
|
70
|
+
* Deeply merges two non-array objects.
|
67
71
|
* @param {Object} first The base object.
|
68
72
|
* @param {Object} second The overrides object.
|
73
|
+
* @param {Map<string, Map<string, Object>>} [mergeMap] Maps the combination of first and second arguments to a merged result.
|
69
74
|
* @returns {Object} An object with properties from both first and second.
|
70
75
|
*/
|
71
|
-
function deepMerge(first
|
76
|
+
function deepMerge(first, second, mergeMap = new Map()) {
|
72
77
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
let secondMergeMap = mergeMap.get(first);
|
79
|
+
|
80
|
+
if (secondMergeMap) {
|
81
|
+
const result = secondMergeMap.get(second);
|
82
|
+
|
83
|
+
if (result) {
|
84
|
+
|
85
|
+
// If this combination of first and second arguments has been already visited, return the previously created result.
|
86
|
+
return result;
|
87
|
+
}
|
88
|
+
} else {
|
89
|
+
secondMergeMap = new Map();
|
90
|
+
mergeMap.set(first, secondMergeMap);
|
79
91
|
}
|
80
92
|
|
81
93
|
/*
|
@@ -89,27 +101,25 @@ function deepMerge(first = {}, second = {}) {
|
|
89
101
|
...second
|
90
102
|
};
|
91
103
|
|
104
|
+
delete result.__proto__; // eslint-disable-line no-proto -- don't merge own property "__proto__"
|
105
|
+
|
106
|
+
// Store the pending result for this combination of first and second arguments.
|
107
|
+
secondMergeMap.set(second, result);
|
108
|
+
|
92
109
|
for (const key of Object.keys(second)) {
|
93
110
|
|
94
111
|
// avoid hairy edge case
|
95
|
-
if (key === "__proto__") {
|
112
|
+
if (key === "__proto__" || !Object.prototype.propertyIsEnumerable.call(first, key)) {
|
96
113
|
continue;
|
97
114
|
}
|
98
115
|
|
99
116
|
const firstValue = first[key];
|
100
117
|
const secondValue = second[key];
|
101
118
|
|
102
|
-
if (
|
103
|
-
result[key] = deepMerge(firstValue, secondValue);
|
104
|
-
} else if (isUndefined(
|
105
|
-
|
106
|
-
result[key] = deepMerge(
|
107
|
-
Array.isArray(secondValue) ? [] : {},
|
108
|
-
secondValue
|
109
|
-
);
|
110
|
-
} else if (!isUndefined(secondValue)) {
|
111
|
-
result[key] = secondValue;
|
112
|
-
}
|
119
|
+
if (isNonArrayObject(firstValue) && isNonArrayObject(secondValue)) {
|
120
|
+
result[key] = deepMerge(firstValue, secondValue, mergeMap);
|
121
|
+
} else if (isUndefined(secondValue)) {
|
122
|
+
result[key] = firstValue;
|
113
123
|
}
|
114
124
|
}
|
115
125
|
|
@@ -66,6 +66,25 @@ function throwRuleNotFoundError({ pluginName, ruleName }, config) {
|
|
66
66
|
throw new TypeError(errorMessage);
|
67
67
|
}
|
68
68
|
|
69
|
+
/**
|
70
|
+
* The error type when a rule has an invalid `meta.schema`.
|
71
|
+
*/
|
72
|
+
class InvalidRuleOptionsSchemaError extends Error {
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Creates a new instance.
|
76
|
+
* @param {string} ruleId Id of the rule that has an invalid `meta.schema`.
|
77
|
+
* @param {Error} processingError Error caught while processing the `meta.schema`.
|
78
|
+
*/
|
79
|
+
constructor(ruleId, processingError) {
|
80
|
+
super(
|
81
|
+
`Error while processing options validation schema of rule '${ruleId}': ${processingError.message}`,
|
82
|
+
{ cause: processingError }
|
83
|
+
);
|
84
|
+
this.code = "ESLINT_INVALID_RULE_OPTIONS_SCHEMA";
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
69
88
|
//-----------------------------------------------------------------------------
|
70
89
|
// Exports
|
71
90
|
//-----------------------------------------------------------------------------
|
@@ -130,10 +149,14 @@ class RuleValidator {
|
|
130
149
|
|
131
150
|
// Precompile and cache validator the first time
|
132
151
|
if (!this.validators.has(rule)) {
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
152
|
+
try {
|
153
|
+
const schema = getRuleOptionsSchema(rule);
|
154
|
+
|
155
|
+
if (schema) {
|
156
|
+
this.validators.set(rule, ajv.compile(schema));
|
157
|
+
}
|
158
|
+
} catch (err) {
|
159
|
+
throw new InvalidRuleOptionsSchemaError(ruleId, err);
|
137
160
|
}
|
138
161
|
}
|
139
162
|
|