eslint 8.20.0 → 8.23.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/bin/eslint.js +2 -4
- package/conf/globals.js +6 -1
- package/lib/cli.js +123 -29
- package/lib/config/default-config.js +16 -7
- package/lib/config/flat-config-array.js +94 -14
- package/lib/config/flat-config-helpers.js +9 -1
- package/lib/eslint/eslint-helpers.js +622 -0
- package/lib/eslint/flat-eslint.js +1176 -0
- package/lib/eslint/index.js +3 -1
- package/lib/linter/config-comment-parser.js +1 -2
- package/lib/linter/linter.js +18 -1
- package/lib/options.js +290 -242
- package/lib/rule-tester/flat-rule-tester.js +40 -37
- package/lib/rule-tester/rule-tester.js +43 -1
- package/lib/rules/key-spacing.js +4 -1
- package/lib/rules/lines-around-comment.js +11 -4
- package/lib/rules/no-fallthrough.js +8 -3
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-warning-comments.js +24 -5
- package/lib/rules/object-shorthand.js +15 -0
- package/lib/rules/sort-keys.js +43 -0
- package/lib/rules/utils/ast-utils.js +9 -3
- package/lib/shared/types.js +1 -1
- package/lib/unsupported-api.js +4 -0
- package/package.json +14 -9
package/bin/eslint.js
CHANGED
@@ -9,9 +9,6 @@
|
|
9
9
|
|
10
10
|
"use strict";
|
11
11
|
|
12
|
-
// to use V8's code cache to speed up instantiation time
|
13
|
-
require("v8-compile-cache");
|
14
|
-
|
15
12
|
// must do this initialization *before* other requires in order to work
|
16
13
|
if (process.argv.includes("--debug")) {
|
17
14
|
require("debug").enable("eslint:*,-eslint:code-path,eslintrc:*");
|
@@ -137,6 +134,7 @@ ${message}`);
|
|
137
134
|
// Otherwise, call the CLI.
|
138
135
|
process.exitCode = await require("../lib/cli").execute(
|
139
136
|
process.argv,
|
140
|
-
process.argv.includes("--stdin") ? await readStdin() : null
|
137
|
+
process.argv.includes("--stdin") ? await readStdin() : null,
|
138
|
+
true
|
141
139
|
);
|
142
140
|
}()).catch(onFatalError);
|
package/conf/globals.js
CHANGED
@@ -124,6 +124,10 @@ const es2022 = {
|
|
124
124
|
...es2021
|
125
125
|
};
|
126
126
|
|
127
|
+
const es2023 = {
|
128
|
+
...es2022
|
129
|
+
};
|
130
|
+
|
127
131
|
|
128
132
|
//-----------------------------------------------------------------------------
|
129
133
|
// Exports
|
@@ -140,5 +144,6 @@ module.exports = {
|
|
140
144
|
es2019,
|
141
145
|
es2020,
|
142
146
|
es2021,
|
143
|
-
es2022
|
147
|
+
es2022,
|
148
|
+
es2023
|
144
149
|
};
|
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,14 @@ const fs = require("fs"),
|
|
19
19
|
path = require("path"),
|
20
20
|
{ promisify } = require("util"),
|
21
21
|
{ ESLint } = require("./eslint"),
|
22
|
-
|
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 { gitignoreToMinimatch } = require("@humanwhocodes/gitignore-to-minimatch");
|
29
|
+
const { ModuleImporter } = require("@humanwhocodes/module-importer");
|
25
30
|
|
26
31
|
const debug = require("debug")("eslint:cli");
|
27
32
|
|
@@ -54,17 +59,20 @@ function quietFixPredicate(message) {
|
|
54
59
|
}
|
55
60
|
|
56
61
|
/**
|
57
|
-
* Translates the CLI options into the options expected by the
|
62
|
+
* Translates the CLI options into the options expected by the ESLint constructor.
|
58
63
|
* @param {ParsedCLIOptions} cliOptions The CLI options to translate.
|
59
|
-
* @
|
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,66 @@ function translateOptions({
|
|
85
93
|
resolvePluginsRelativeTo,
|
86
94
|
rule,
|
87
95
|
rulesdir
|
88
|
-
}) {
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
+
if (ignorePattern) {
|
147
|
+
overrideConfig.push({
|
148
|
+
ignores: ignorePattern.map(gitignoreToMinimatch)
|
149
|
+
});
|
150
|
+
}
|
151
|
+
|
152
|
+
} else {
|
153
|
+
overrideConfigFile = config;
|
154
|
+
|
155
|
+
overrideConfig = {
|
101
156
|
env: env && env.reduce((obj, name) => {
|
102
157
|
obj[name] = true;
|
103
158
|
return obj;
|
@@ -115,13 +170,32 @@ function translateOptions({
|
|
115
170
|
parserOptions,
|
116
171
|
plugins: plugin,
|
117
172
|
rules: rule
|
118
|
-
}
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
173
|
+
};
|
174
|
+
}
|
175
|
+
|
176
|
+
const options = {
|
177
|
+
allowInlineConfig: inlineConfig,
|
178
|
+
cache,
|
179
|
+
cacheLocation: cacheLocation || cacheFile,
|
180
|
+
cacheStrategy,
|
181
|
+
errorOnUnmatchedPattern,
|
182
|
+
fix: (fix || fixDryRun) && (quiet ? quietFixPredicate : true),
|
183
|
+
fixTypes: fixType,
|
184
|
+
ignore,
|
185
|
+
ignorePath,
|
186
|
+
overrideConfig,
|
187
|
+
overrideConfigFile,
|
188
|
+
reportUnusedDisableDirectives: reportUnusedDisableDirectives ? "error" : void 0
|
124
189
|
};
|
190
|
+
|
191
|
+
if (configType !== "flat") {
|
192
|
+
options.resolvePluginsRelativeTo = resolvePluginsRelativeTo;
|
193
|
+
options.rulePaths = rulesdir;
|
194
|
+
options.useEslintrc = eslintrc;
|
195
|
+
options.extensions = ext;
|
196
|
+
}
|
197
|
+
|
198
|
+
return options;
|
125
199
|
}
|
126
200
|
|
127
201
|
/**
|
@@ -218,19 +292,34 @@ const cli = {
|
|
218
292
|
* Executes the CLI based on an array of arguments that is passed in.
|
219
293
|
* @param {string|Array|Object} args The arguments to process.
|
220
294
|
* @param {string} [text] The text to lint (used for TTY).
|
295
|
+
* @param {boolean} [allowFlatConfig] Whether or not to allow flat config.
|
221
296
|
* @returns {Promise<number>} The exit code for the operation.
|
222
297
|
*/
|
223
|
-
async execute(args, text) {
|
298
|
+
async execute(args, text, allowFlatConfig) {
|
224
299
|
if (Array.isArray(args)) {
|
225
300
|
debug("CLI args: %o", args.slice(2));
|
226
301
|
}
|
227
302
|
|
303
|
+
/*
|
304
|
+
* Before doing anything, we need to see if we are using a
|
305
|
+
* flat config file. If so, then we need to change the way command
|
306
|
+
* line args are parsed. This is temporary, and when we fully
|
307
|
+
* switch to flat config we can remove this logic.
|
308
|
+
*/
|
309
|
+
|
310
|
+
const usingFlatConfig = allowFlatConfig && !!(await findFlatConfigFile(process.cwd()));
|
311
|
+
|
312
|
+
debug("Using flat config?", usingFlatConfig);
|
313
|
+
|
314
|
+
const CLIOptions = createCLIOptions(usingFlatConfig);
|
315
|
+
|
228
316
|
/** @type {ParsedCLIOptions} */
|
229
317
|
let options;
|
230
318
|
|
231
319
|
try {
|
232
320
|
options = CLIOptions.parse(args);
|
233
321
|
} catch (error) {
|
322
|
+
debug("Error parsing CLI options:", error.message);
|
234
323
|
log.error(error.message);
|
235
324
|
return 2;
|
236
325
|
}
|
@@ -251,6 +340,7 @@ const cli = {
|
|
251
340
|
log.info(RuntimeInfo.environment());
|
252
341
|
return 0;
|
253
342
|
} catch (err) {
|
343
|
+
debug("Error retrieving environment info");
|
254
344
|
log.error(err.message);
|
255
345
|
return 2;
|
256
346
|
}
|
@@ -266,7 +356,9 @@ const cli = {
|
|
266
356
|
return 2;
|
267
357
|
}
|
268
358
|
|
269
|
-
const engine =
|
359
|
+
const engine = usingFlatConfig
|
360
|
+
? new FlatESLint(await translateOptions(options, "flat"))
|
361
|
+
: new ESLint(await translateOptions(options));
|
270
362
|
const fileConfig =
|
271
363
|
await engine.calculateConfigForFile(options.printConfig);
|
272
364
|
|
@@ -289,7 +381,9 @@ const cli = {
|
|
289
381
|
return 2;
|
290
382
|
}
|
291
383
|
|
292
|
-
const
|
384
|
+
const ActiveESLint = usingFlatConfig ? FlatESLint : ESLint;
|
385
|
+
|
386
|
+
const engine = new ActiveESLint(await translateOptions(options, usingFlatConfig ? "flat" : "eslintrc"));
|
293
387
|
let results;
|
294
388
|
|
295
389
|
if (useStdin) {
|
@@ -303,14 +397,14 @@ const cli = {
|
|
303
397
|
|
304
398
|
if (options.fix) {
|
305
399
|
debug("Fix mode enabled - applying fixes");
|
306
|
-
await
|
400
|
+
await ActiveESLint.outputFixes(results);
|
307
401
|
}
|
308
402
|
|
309
403
|
let resultsToPrint = results;
|
310
404
|
|
311
405
|
if (options.quiet) {
|
312
406
|
debug("Quiet mode enabled - filtering out warnings");
|
313
|
-
resultsToPrint =
|
407
|
+
resultsToPrint = ActiveESLint.getErrorResults(resultsToPrint);
|
314
408
|
}
|
315
409
|
|
316
410
|
if (await printResults(engine, resultsToPrint, options.format, options.outputFile)) {
|
@@ -15,7 +15,6 @@ const Rules = require("../rules");
|
|
15
15
|
// Helpers
|
16
16
|
//-----------------------------------------------------------------------------
|
17
17
|
|
18
|
-
|
19
18
|
exports.defaultConfig = [
|
20
19
|
{
|
21
20
|
plugins: {
|
@@ -41,21 +40,31 @@ exports.defaultConfig = [
|
|
41
40
|
})
|
42
41
|
}
|
43
42
|
},
|
44
|
-
ignores: [
|
45
|
-
"**/node_modules/**",
|
46
|
-
".git/**"
|
47
|
-
],
|
48
43
|
languageOptions: {
|
49
|
-
ecmaVersion: "latest",
|
50
44
|
sourceType: "module",
|
45
|
+
ecmaVersion: "latest",
|
51
46
|
parser: "@/espree",
|
52
47
|
parserOptions: {}
|
53
48
|
}
|
54
49
|
},
|
50
|
+
|
51
|
+
// default ignores are listed here
|
52
|
+
{
|
53
|
+
ignores: [
|
54
|
+
"**/node_modules/**",
|
55
|
+
".git/**"
|
56
|
+
]
|
57
|
+
},
|
58
|
+
|
59
|
+
// intentionally empty config to ensure these files are globbed by default
|
60
|
+
{
|
61
|
+
files: ["**/*.js", "**/*.mjs"]
|
62
|
+
},
|
55
63
|
{
|
56
64
|
files: ["**/*.cjs"],
|
57
65
|
languageOptions: {
|
58
|
-
sourceType: "commonjs"
|
66
|
+
sourceType: "commonjs",
|
67
|
+
ecmaVersion: "latest"
|
59
68
|
}
|
60
69
|
}
|
61
70
|
];
|
@@ -36,6 +36,8 @@ function splitPluginIdentifier(identifier) {
|
|
36
36
|
};
|
37
37
|
}
|
38
38
|
|
39
|
+
const originalBaseConfig = Symbol("originalBaseConfig");
|
40
|
+
|
39
41
|
//-----------------------------------------------------------------------------
|
40
42
|
// Exports
|
41
43
|
//-----------------------------------------------------------------------------
|
@@ -48,10 +50,14 @@ class FlatConfigArray extends ConfigArray {
|
|
48
50
|
/**
|
49
51
|
* Creates a new instance.
|
50
52
|
* @param {*[]} configs An array of configuration information.
|
51
|
-
* @param {{basePath: string, baseConfig: FlatConfig}} options The options
|
53
|
+
* @param {{basePath: string, shouldIgnore: boolean, baseConfig: FlatConfig}} options The options
|
52
54
|
* to use for the config array instance.
|
53
55
|
*/
|
54
|
-
constructor(configs, {
|
56
|
+
constructor(configs, {
|
57
|
+
basePath,
|
58
|
+
shouldIgnore = true,
|
59
|
+
baseConfig = defaultConfig
|
60
|
+
} = {}) {
|
55
61
|
super(configs, {
|
56
62
|
basePath,
|
57
63
|
schema: flatConfigSchema
|
@@ -62,6 +68,22 @@ class FlatConfigArray extends ConfigArray {
|
|
62
68
|
} else {
|
63
69
|
this.unshift(baseConfig);
|
64
70
|
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* The baes config used to build the config array.
|
74
|
+
* @type {Array<FlatConfig>}
|
75
|
+
*/
|
76
|
+
this[originalBaseConfig] = baseConfig;
|
77
|
+
Object.defineProperty(this, originalBaseConfig, { writable: false });
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Determines if `ignores` fields should be honored.
|
81
|
+
* If true, then all `ignores` fields are honored.
|
82
|
+
* if false, then only `ignores` fields in the baseConfig are honored.
|
83
|
+
* @type {boolean}
|
84
|
+
*/
|
85
|
+
this.shouldIgnore = shouldIgnore;
|
86
|
+
Object.defineProperty(this, "shouldIgnore", { writable: false });
|
65
87
|
}
|
66
88
|
|
67
89
|
/* eslint-disable class-methods-use-this -- Desired as instance method */
|
@@ -87,6 +109,23 @@ class FlatConfigArray extends ConfigArray {
|
|
87
109
|
return require("../../conf/eslint-all");
|
88
110
|
}
|
89
111
|
|
112
|
+
/*
|
113
|
+
* If `shouldIgnore` is false, we remove any ignore patterns specified
|
114
|
+
* in the config so long as it's not a default config and it doesn't
|
115
|
+
* have a `files` entry.
|
116
|
+
*/
|
117
|
+
if (
|
118
|
+
!this.shouldIgnore &&
|
119
|
+
!this[originalBaseConfig].includes(config) &&
|
120
|
+
config.ignores &&
|
121
|
+
!config.files
|
122
|
+
) {
|
123
|
+
/* eslint-disable-next-line no-unused-vars -- need to strip off other keys */
|
124
|
+
const { ignores, ...otherKeys } = config;
|
125
|
+
|
126
|
+
return otherKeys;
|
127
|
+
}
|
128
|
+
|
90
129
|
return config;
|
91
130
|
}
|
92
131
|
|
@@ -100,31 +139,72 @@ class FlatConfigArray extends ConfigArray {
|
|
100
139
|
[ConfigArraySymbol.finalizeConfig](config) {
|
101
140
|
|
102
141
|
const { plugins, languageOptions, processor } = config;
|
142
|
+
let parserName, processorName;
|
143
|
+
let invalidParser = false,
|
144
|
+
invalidProcessor = false;
|
103
145
|
|
104
146
|
// Check parser value
|
105
|
-
if (languageOptions && languageOptions.parser
|
106
|
-
|
147
|
+
if (languageOptions && languageOptions.parser) {
|
148
|
+
if (typeof languageOptions.parser === "string") {
|
149
|
+
const { pluginName, objectName: localParserName } = splitPluginIdentifier(languageOptions.parser);
|
107
150
|
|
108
|
-
|
109
|
-
|
110
|
-
|
151
|
+
parserName = languageOptions.parser;
|
152
|
+
|
153
|
+
if (!plugins || !plugins[pluginName] || !plugins[pluginName].parsers || !plugins[pluginName].parsers[localParserName]) {
|
154
|
+
throw new TypeError(`Key "parser": Could not find "${localParserName}" in plugin "${pluginName}".`);
|
155
|
+
}
|
111
156
|
|
112
|
-
|
157
|
+
languageOptions.parser = plugins[pluginName].parsers[localParserName];
|
158
|
+
} else {
|
159
|
+
invalidParser = true;
|
160
|
+
}
|
113
161
|
}
|
114
162
|
|
115
163
|
// Check processor value
|
116
|
-
if (processor
|
117
|
-
|
164
|
+
if (processor) {
|
165
|
+
if (typeof processor === "string") {
|
166
|
+
const { pluginName, objectName: localProcessorName } = splitPluginIdentifier(processor);
|
118
167
|
|
119
|
-
|
120
|
-
|
121
|
-
|
168
|
+
processorName = processor;
|
169
|
+
|
170
|
+
if (!plugins || !plugins[pluginName] || !plugins[pluginName].processors || !plugins[pluginName].processors[localProcessorName]) {
|
171
|
+
throw new TypeError(`Key "processor": Could not find "${localProcessorName}" in plugin "${pluginName}".`);
|
172
|
+
}
|
122
173
|
|
123
|
-
|
174
|
+
config.processor = plugins[pluginName].processors[localProcessorName];
|
175
|
+
} else {
|
176
|
+
invalidProcessor = true;
|
177
|
+
}
|
124
178
|
}
|
125
179
|
|
126
180
|
ruleValidator.validate(config);
|
127
181
|
|
182
|
+
// apply special logic for serialization into JSON
|
183
|
+
/* eslint-disable object-shorthand -- shorthand would change "this" value */
|
184
|
+
Object.defineProperty(config, "toJSON", {
|
185
|
+
value: function() {
|
186
|
+
|
187
|
+
if (invalidParser) {
|
188
|
+
throw new Error("Caching is not supported when parser is an object.");
|
189
|
+
}
|
190
|
+
|
191
|
+
if (invalidProcessor) {
|
192
|
+
throw new Error("Caching is not supported when processor is an object.");
|
193
|
+
}
|
194
|
+
|
195
|
+
return {
|
196
|
+
...this,
|
197
|
+
plugins: Object.keys(plugins),
|
198
|
+
languageOptions: {
|
199
|
+
...languageOptions,
|
200
|
+
parser: parserName
|
201
|
+
},
|
202
|
+
processor: processorName
|
203
|
+
};
|
204
|
+
}
|
205
|
+
});
|
206
|
+
/* eslint-enable object-shorthand -- ok to enable now */
|
207
|
+
|
128
208
|
return config;
|
129
209
|
}
|
130
210
|
/* eslint-enable class-methods-use-this -- Desired as instance method */
|
@@ -20,7 +20,14 @@ function parseRuleId(ruleId) {
|
|
20
20
|
|
21
21
|
// distinguish between core rules and plugin rules
|
22
22
|
if (ruleId.includes("/")) {
|
23
|
-
|
23
|
+
|
24
|
+
// mimic scoped npm packages
|
25
|
+
if (ruleId.startsWith("@")) {
|
26
|
+
pluginName = ruleId.slice(0, ruleId.lastIndexOf("/"));
|
27
|
+
} else {
|
28
|
+
pluginName = ruleId.slice(0, ruleId.indexOf("/"));
|
29
|
+
}
|
30
|
+
|
24
31
|
ruleName = ruleId.slice(pluginName.length + 1);
|
25
32
|
} else {
|
26
33
|
pluginName = "@";
|
@@ -47,6 +54,7 @@ function getRuleFromConfig(ruleId, config) {
|
|
47
54
|
const plugin = config.plugins && config.plugins[pluginName];
|
48
55
|
let rule = plugin && plugin.rules && plugin.rules[ruleName];
|
49
56
|
|
57
|
+
|
50
58
|
// normalize function rules into objects
|
51
59
|
if (rule && typeof rule === "function") {
|
52
60
|
rule = {
|