eslint 9.13.0 → 9.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/cli-engine/formatters/stylish.js +3 -3
- package/lib/cli-engine/lint-result-cache.js +1 -1
- package/lib/config/config-loader.js +193 -177
- package/lib/config/config.js +40 -24
- package/lib/eslint/eslint-helpers.js +18 -22
- package/lib/eslint/eslint.js +2 -2
- package/lib/languages/js/index.js +76 -0
- package/lib/languages/js/source-code/token-store/index.js +1 -1
- package/lib/linter/code-path-analysis/code-path-analyzer.js +1 -1
- package/lib/linter/code-path-analysis/fork-context.js +1 -1
- package/lib/linter/linter.js +36 -35
- package/lib/linter/report-translator.js +1 -1
- package/lib/rule-tester/rule-tester.js +1 -1
- package/lib/rules/accessor-pairs.js +10 -7
- package/lib/rules/array-callback-return.js +10 -8
- package/lib/rules/arrow-body-style.js +3 -1
- package/lib/rules/camelcase.js +27 -14
- package/lib/rules/class-methods-use-this.js +9 -5
- package/lib/rules/complexity.js +9 -5
- package/lib/rules/consistent-return.js +4 -4
- package/lib/rules/consistent-this.js +3 -7
- package/lib/rules/curly.js +3 -140
- package/lib/rules/default-case.js +3 -1
- package/lib/rules/dot-notation.js +9 -6
- package/lib/rules/func-names.js +3 -3
- package/lib/rules/func-style.js +10 -8
- package/lib/rules/getter-return.js +5 -5
- package/lib/rules/grouped-accessor-pairs.js +3 -1
- package/lib/rules/id-denylist.js +14 -1
- package/lib/rules/id-length.js +12 -8
- package/lib/rules/id-match.js +20 -17
- package/lib/rules/new-cap.js +15 -34
- package/lib/rules/no-bitwise.js +4 -5
- package/lib/rules/no-cond-assign.js +3 -3
- package/lib/rules/no-console.js +3 -2
- package/lib/rules/no-constant-condition.js +5 -4
- package/lib/rules/no-duplicate-imports.js +5 -4
- package/lib/rules/no-else-return.js +4 -5
- package/lib/rules/no-empty-function.js +4 -4
- package/lib/rules/no-empty-pattern.js +4 -4
- package/lib/rules/no-empty.js +6 -5
- package/lib/rules/no-eval.js +4 -5
- package/lib/rules/no-extend-native.js +3 -3
- package/lib/rules/no-extra-boolean-cast.js +3 -3
- package/lib/rules/no-fallthrough.js +12 -15
- package/lib/rules/no-global-assign.js +3 -2
- package/lib/rules/no-implicit-coercion.js +13 -24
- package/lib/rules/no-implicit-globals.js +4 -4
- package/lib/rules/no-inline-comments.js +4 -6
- package/lib/rules/no-inner-declarations.js +4 -2
- package/lib/rules/no-invalid-regexp.js +5 -4
- package/lib/rules/no-invalid-this.js +4 -4
- package/lib/rules/no-irregular-whitespace.js +24 -22
- package/lib/rules/no-labels.js +8 -7
- package/lib/rules/no-lonely-if.js +8 -2
- package/lib/rules/no-multi-assign.js +5 -10
- package/lib/rules/no-plusplus.js +4 -9
- package/lib/rules/no-promise-executor-return.js +4 -6
- package/lib/rules/no-redeclare.js +5 -8
- package/lib/rules/no-return-assign.js +3 -1
- package/lib/rules/no-self-assign.js +4 -3
- package/lib/rules/no-sequences.js +7 -7
- package/lib/rules/no-shadow.js +18 -14
- package/lib/rules/no-undef.js +4 -4
- package/lib/rules/no-underscore-dangle.js +31 -28
- package/lib/rules/no-unneeded-ternary.js +4 -4
- package/lib/rules/no-unreachable-loop.js +4 -2
- package/lib/rules/no-unsafe-negation.js +4 -4
- package/lib/rules/no-unsafe-optional-chaining.js +4 -4
- package/lib/rules/no-unused-expressions.js +17 -13
- package/lib/rules/no-use-before-define.js +14 -13
- package/lib/rules/no-useless-computed-key.js +9 -3
- package/lib/rules/no-useless-rename.js +7 -8
- package/lib/rules/no-void.js +4 -4
- package/lib/rules/no-warning-comments.js +9 -7
- package/lib/rules/operator-assignment.js +4 -2
- package/lib/rules/prefer-arrow-callback.js +5 -8
- package/lib/rules/prefer-const.js +5 -3
- package/lib/rules/prefer-promise-reject-errors.js +5 -3
- package/lib/rules/prefer-regex-literals.js +4 -3
- package/lib/rules/radix.js +3 -1
- package/lib/rules/require-atomic-updates.js +4 -3
- package/lib/rules/sort-imports.js +20 -16
- package/lib/rules/sort-keys.js +13 -16
- package/lib/rules/sort-vars.js +4 -4
- package/lib/rules/strict.js +3 -2
- package/lib/rules/unicode-bom.js +4 -2
- package/lib/rules/use-isnan.js +7 -4
- package/lib/rules/utils/ast-utils.js +186 -2
- package/lib/rules/valid-typeof.js +3 -2
- package/lib/rules/yoda.js +9 -12
- package/lib/shared/assert.js +22 -0
- package/lib/shared/deep-merge-arrays.js +60 -0
- package/lib/shared/text-table.js +67 -0
- package/lib/shared/types.js +1 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/rules/ecmascript-6.d.ts +36 -16
- package/lib/types/rules/stylistic-issues.d.ts +3 -0
- package/package.json +19 -20
package/README.md
CHANGED
@@ -299,8 +299,8 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
|
299
299
|
<h3>Platinum Sponsors</h3>
|
300
300
|
<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>
|
301
301
|
<p><a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a></p><h3>Silver Sponsors</h3>
|
302
|
-
<p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/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>
|
303
|
-
<p><a href="https://www.wordhint.net/"><img src="https://images.opencollective.com/wordhint/be86813/avatar.png" alt="WordHint" 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?v=4" alt="GitBook" 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
|
302
|
+
<p><a href="https://www.serptriumph.com/"><img src="https://images.opencollective.com/serp-triumph5/fea3074/logo.png" alt="SERP Triumph" height="64"></a> <a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/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>
|
303
|
+
<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://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://www.wordhint.net/"><img src="https://images.opencollective.com/wordhint/be86813/avatar.png" alt="WordHint" 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?v=4" alt="GitBook" 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>
|
304
304
|
<h3>Technology Sponsors</h3>
|
305
305
|
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.
|
306
306
|
<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>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
const chalk = require("chalk"),
|
8
8
|
util = require("node:util"),
|
9
|
-
table = require("text-table");
|
9
|
+
table = require("../../shared/text-table");
|
10
10
|
|
11
11
|
//------------------------------------------------------------------------------
|
12
12
|
// Helpers
|
@@ -62,8 +62,8 @@ module.exports = function(results) {
|
|
62
62
|
|
63
63
|
return [
|
64
64
|
"",
|
65
|
-
message.line || 0,
|
66
|
-
message.column || 0,
|
65
|
+
String(message.line || 0),
|
66
|
+
String(message.column || 0),
|
67
67
|
messageType,
|
68
68
|
message.message.replace(/([^ ])\.$/u, "$1"),
|
69
69
|
chalk.dim(message.ruleId || "")
|
@@ -8,11 +8,11 @@
|
|
8
8
|
// Requirements
|
9
9
|
//-----------------------------------------------------------------------------
|
10
10
|
|
11
|
-
const assert = require("node:assert");
|
12
11
|
const fs = require("node:fs");
|
13
12
|
const fileEntryCache = require("file-entry-cache");
|
14
13
|
const stringify = require("json-stable-stringify-without-jsonify");
|
15
14
|
const pkg = require("../../package.json");
|
15
|
+
const assert = require("../shared/assert");
|
16
16
|
const hash = require("./hash");
|
17
17
|
|
18
18
|
const debug = require("debug")("eslint:lint-result-cache");
|
@@ -14,7 +14,7 @@ const fs = require("node:fs/promises");
|
|
14
14
|
const findUp = require("find-up");
|
15
15
|
const { pathToFileURL } = require("node:url");
|
16
16
|
const debug = require("debug")("eslint:config-loader");
|
17
|
-
const { FlatConfigArray } = require("
|
17
|
+
const { FlatConfigArray } = require("./flat-config-array");
|
18
18
|
|
19
19
|
//-----------------------------------------------------------------------------
|
20
20
|
// Types
|
@@ -206,95 +206,6 @@ async function loadConfigFile(filePath, allowTS) {
|
|
206
206
|
return config;
|
207
207
|
}
|
208
208
|
|
209
|
-
/**
|
210
|
-
* Calculates the config array for this run based on inputs.
|
211
|
-
* @param {string} configFilePath The absolute path to the config file to use if not overridden.
|
212
|
-
* @param {string} basePath The base path to use for relative paths in the config file.
|
213
|
-
* @param {ConfigLoaderOptions} options The options to use when loading configuration files.
|
214
|
-
* @returns {Promise<FlatConfigArray>} The config array for `eslint`.
|
215
|
-
*/
|
216
|
-
async function calculateConfigArray(configFilePath, basePath, options) {
|
217
|
-
|
218
|
-
const {
|
219
|
-
cwd,
|
220
|
-
baseConfig,
|
221
|
-
ignoreEnabled,
|
222
|
-
ignorePatterns,
|
223
|
-
overrideConfig,
|
224
|
-
defaultConfigs = [],
|
225
|
-
allowTS
|
226
|
-
} = options;
|
227
|
-
|
228
|
-
debug(`Calculating config array from config file ${configFilePath} and base path ${basePath}`);
|
229
|
-
|
230
|
-
const configs = new FlatConfigArray(baseConfig || [], { basePath, shouldIgnore: ignoreEnabled });
|
231
|
-
|
232
|
-
// load config file
|
233
|
-
if (configFilePath) {
|
234
|
-
|
235
|
-
debug(`Loading config file ${configFilePath}`);
|
236
|
-
const fileConfig = await loadConfigFile(configFilePath, allowTS);
|
237
|
-
|
238
|
-
if (Array.isArray(fileConfig)) {
|
239
|
-
configs.push(...fileConfig);
|
240
|
-
} else {
|
241
|
-
configs.push(fileConfig);
|
242
|
-
}
|
243
|
-
}
|
244
|
-
|
245
|
-
// add in any configured defaults
|
246
|
-
configs.push(...defaultConfigs);
|
247
|
-
|
248
|
-
// append command line ignore patterns
|
249
|
-
if (ignorePatterns && ignorePatterns.length > 0) {
|
250
|
-
|
251
|
-
let relativeIgnorePatterns;
|
252
|
-
|
253
|
-
/*
|
254
|
-
* If the config file basePath is different than the cwd, then
|
255
|
-
* the ignore patterns won't work correctly. Here, we adjust the
|
256
|
-
* ignore pattern to include the correct relative path. Patterns
|
257
|
-
* passed as `ignorePatterns` are relative to the cwd, whereas
|
258
|
-
* the config file basePath can be an ancestor of the cwd.
|
259
|
-
*/
|
260
|
-
if (basePath === cwd) {
|
261
|
-
relativeIgnorePatterns = ignorePatterns;
|
262
|
-
} else {
|
263
|
-
|
264
|
-
// relative path must only have Unix-style separators
|
265
|
-
const relativeIgnorePath = path.relative(basePath, cwd).replace(/\\/gu, "/");
|
266
|
-
|
267
|
-
relativeIgnorePatterns = ignorePatterns.map(pattern => {
|
268
|
-
const negated = pattern.startsWith("!");
|
269
|
-
const basePattern = negated ? pattern.slice(1) : pattern;
|
270
|
-
|
271
|
-
return (negated ? "!" : "") +
|
272
|
-
path.posix.join(relativeIgnorePath, basePattern);
|
273
|
-
});
|
274
|
-
}
|
275
|
-
|
276
|
-
/*
|
277
|
-
* Ignore patterns are added to the end of the config array
|
278
|
-
* so they can override default ignores.
|
279
|
-
*/
|
280
|
-
configs.push({
|
281
|
-
ignores: relativeIgnorePatterns
|
282
|
-
});
|
283
|
-
}
|
284
|
-
|
285
|
-
if (overrideConfig) {
|
286
|
-
if (Array.isArray(overrideConfig)) {
|
287
|
-
configs.push(...overrideConfig);
|
288
|
-
} else {
|
289
|
-
configs.push(overrideConfig);
|
290
|
-
}
|
291
|
-
}
|
292
|
-
|
293
|
-
await configs.normalize();
|
294
|
-
|
295
|
-
return configs;
|
296
|
-
}
|
297
|
-
|
298
209
|
//-----------------------------------------------------------------------------
|
299
210
|
// Exports
|
300
211
|
//-----------------------------------------------------------------------------
|
@@ -307,13 +218,13 @@ class ConfigLoader {
|
|
307
218
|
|
308
219
|
/**
|
309
220
|
* Map of config file paths to the config arrays for those directories.
|
310
|
-
* @type {Map<string, FlatConfigArray
|
221
|
+
* @type {Map<string, FlatConfigArray|Promise<FlatConfigArray>>}
|
311
222
|
*/
|
312
223
|
#configArrays = new Map();
|
313
224
|
|
314
225
|
/**
|
315
226
|
* Map of absolute directory names to the config file paths for those directories.
|
316
|
-
* @type {Map<string, {configFilePath:string,basePath:string}
|
227
|
+
* @type {Map<string, {configFilePath:string,basePath:string}|Promise<{configFilePath:string,basePath:string}>>}
|
317
228
|
*/
|
318
229
|
#configFilePaths = new Map();
|
319
230
|
|
@@ -347,43 +258,22 @@ class ConfigLoader {
|
|
347
258
|
return this.#configFilePaths.get(fromDirectory);
|
348
259
|
}
|
349
260
|
|
350
|
-
const
|
351
|
-
|
352
|
-
:
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
const {
|
357
|
-
cwd,
|
358
|
-
configFile: useConfigFile
|
359
|
-
} = this.#options;
|
360
|
-
let basePath = cwd;
|
361
|
-
|
362
|
-
if (typeof useConfigFile === "string") {
|
363
|
-
debug(`Override config file path is ${useConfigFile}`);
|
364
|
-
configFilePath = path.resolve(cwd, useConfigFile);
|
365
|
-
basePath = cwd;
|
366
|
-
} else if (useConfigFile !== false) {
|
367
|
-
debug("Searching for eslint.config.js");
|
368
|
-
configFilePath = await findUp(
|
369
|
-
configFilenames,
|
370
|
-
{ cwd: fromDirectory }
|
371
|
-
);
|
261
|
+
const resultPromise = ConfigLoader.locateConfigFileToUse({
|
262
|
+
useConfigFile: this.#options.configFile,
|
263
|
+
cwd: this.#options.cwd,
|
264
|
+
fromDirectory,
|
265
|
+
allowTS: this.#options.allowTS
|
266
|
+
});
|
372
267
|
|
373
|
-
|
374
|
-
|
375
|
-
}
|
268
|
+
// ensure `ConfigLoader.locateConfigFileToUse` is called only once for `fromDirectory`
|
269
|
+
this.#configFilePaths.set(fromDirectory, resultPromise);
|
376
270
|
|
377
|
-
|
271
|
+
// Unwrap the promise. This is primarily for the sync `getCachedConfigArrayForPath` method.
|
272
|
+
const result = await resultPromise;
|
378
273
|
|
379
|
-
|
380
|
-
this.#configFilePaths.set(fromDirectory, { configFilePath, basePath });
|
381
|
-
|
382
|
-
return {
|
383
|
-
configFilePath,
|
384
|
-
basePath
|
385
|
-
};
|
274
|
+
this.#configFilePaths.set(fromDirectory, result);
|
386
275
|
|
276
|
+
return result;
|
387
277
|
}
|
388
278
|
|
389
279
|
/**
|
@@ -399,9 +289,14 @@ class ConfigLoader {
|
|
399
289
|
return this.#configArrays.get(configFilePath);
|
400
290
|
}
|
401
291
|
|
402
|
-
const
|
292
|
+
const configsPromise = ConfigLoader.calculateConfigArray(configFilePath, basePath, this.#options);
|
293
|
+
|
294
|
+
// ensure `ConfigLoader.calculateConfigArray` is called only once for `configFilePath`
|
295
|
+
this.#configArrays.set(configFilePath, configsPromise);
|
296
|
+
|
297
|
+
// Unwrap the promise. This is primarily for the sync `getCachedConfigArrayForPath` method.
|
298
|
+
const configs = await configsPromise;
|
403
299
|
|
404
|
-
// cache the config array for this instance
|
405
300
|
this.#configArrays.set(configFilePath, configs);
|
406
301
|
|
407
302
|
return configs;
|
@@ -512,9 +407,21 @@ class ConfigLoader {
|
|
512
407
|
throw new Error(`Could not find config file for ${fileOrDirPath}`);
|
513
408
|
}
|
514
409
|
|
515
|
-
const
|
410
|
+
const configFilePathInfo = this.#configFilePaths.get(absoluteDirPath);
|
411
|
+
|
412
|
+
if (typeof configFilePathInfo.then === "function") {
|
413
|
+
throw new Error(`Config file path for ${fileOrDirPath} has not yet been calculated or an error occurred during the calculation`);
|
414
|
+
}
|
415
|
+
|
416
|
+
const { configFilePath } = configFilePathInfo;
|
417
|
+
|
418
|
+
const configArray = this.#configArrays.get(configFilePath);
|
516
419
|
|
517
|
-
|
420
|
+
if (!configArray || typeof configArray.then === "function") {
|
421
|
+
throw new Error(`Config array for ${fileOrDirPath} has not yet been calculated or an error occurred during the calculation`);
|
422
|
+
}
|
423
|
+
|
424
|
+
return configArray;
|
518
425
|
}
|
519
426
|
|
520
427
|
/**
|
@@ -526,6 +433,144 @@ class ConfigLoader {
|
|
526
433
|
return import("jiti");
|
527
434
|
}
|
528
435
|
|
436
|
+
/**
|
437
|
+
* Determines which config file to use. This is determined by seeing if an
|
438
|
+
* override config file was specified, and if so, using it; otherwise, as long
|
439
|
+
* as override config file is not explicitly set to `false`, it will search
|
440
|
+
* upwards from `fromDirectory` for a file named `eslint.config.js`.
|
441
|
+
* This method is exposed internally for testing purposes.
|
442
|
+
* @param {Object} [options] the options object
|
443
|
+
* @param {string|false|undefined} options.useConfigFile The path to the config file to use.
|
444
|
+
* @param {string} options.cwd Path to a directory that should be considered as the current working directory.
|
445
|
+
* @param {string} [options.fromDirectory] The directory from which to start searching. Defaults to `cwd`.
|
446
|
+
* @param {boolean} options.allowTS Indicates if TypeScript configuration files are allowed.
|
447
|
+
* @returns {Promise<{configFilePath:string|undefined,basePath:string}>} Location information for
|
448
|
+
* the config file.
|
449
|
+
*/
|
450
|
+
static async locateConfigFileToUse({ useConfigFile, cwd, fromDirectory = cwd, allowTS }) {
|
451
|
+
|
452
|
+
const configFilenames = allowTS
|
453
|
+
? [...FLAT_CONFIG_FILENAMES, ...TS_FLAT_CONFIG_FILENAMES]
|
454
|
+
: FLAT_CONFIG_FILENAMES;
|
455
|
+
|
456
|
+
// determine where to load config file from
|
457
|
+
let configFilePath;
|
458
|
+
let basePath = cwd;
|
459
|
+
|
460
|
+
if (typeof useConfigFile === "string") {
|
461
|
+
debug(`Override config file path is ${useConfigFile}`);
|
462
|
+
configFilePath = path.resolve(cwd, useConfigFile);
|
463
|
+
basePath = cwd;
|
464
|
+
} else if (useConfigFile !== false) {
|
465
|
+
debug("Searching for eslint.config.js");
|
466
|
+
configFilePath = await findUp(
|
467
|
+
configFilenames,
|
468
|
+
{ cwd: fromDirectory }
|
469
|
+
);
|
470
|
+
|
471
|
+
if (configFilePath) {
|
472
|
+
basePath = path.dirname(configFilePath);
|
473
|
+
}
|
474
|
+
|
475
|
+
}
|
476
|
+
|
477
|
+
return {
|
478
|
+
configFilePath,
|
479
|
+
basePath
|
480
|
+
};
|
481
|
+
|
482
|
+
}
|
483
|
+
|
484
|
+
/**
|
485
|
+
* Calculates the config array for this run based on inputs.
|
486
|
+
* This method is exposed internally for testing purposes.
|
487
|
+
* @param {string} configFilePath The absolute path to the config file to use if not overridden.
|
488
|
+
* @param {string} basePath The base path to use for relative paths in the config file.
|
489
|
+
* @param {ConfigLoaderOptions} options The options to use when loading configuration files.
|
490
|
+
* @returns {Promise<FlatConfigArray>} The config array for `eslint`.
|
491
|
+
*/
|
492
|
+
static async calculateConfigArray(configFilePath, basePath, options) {
|
493
|
+
|
494
|
+
const {
|
495
|
+
cwd,
|
496
|
+
baseConfig,
|
497
|
+
ignoreEnabled,
|
498
|
+
ignorePatterns,
|
499
|
+
overrideConfig,
|
500
|
+
defaultConfigs = [],
|
501
|
+
allowTS
|
502
|
+
} = options;
|
503
|
+
|
504
|
+
debug(`Calculating config array from config file ${configFilePath} and base path ${basePath}`);
|
505
|
+
|
506
|
+
const configs = new FlatConfigArray(baseConfig || [], { basePath, shouldIgnore: ignoreEnabled });
|
507
|
+
|
508
|
+
// load config file
|
509
|
+
if (configFilePath) {
|
510
|
+
|
511
|
+
debug(`Loading config file ${configFilePath}`);
|
512
|
+
const fileConfig = await loadConfigFile(configFilePath, allowTS);
|
513
|
+
|
514
|
+
if (Array.isArray(fileConfig)) {
|
515
|
+
configs.push(...fileConfig);
|
516
|
+
} else {
|
517
|
+
configs.push(fileConfig);
|
518
|
+
}
|
519
|
+
}
|
520
|
+
|
521
|
+
// add in any configured defaults
|
522
|
+
configs.push(...defaultConfigs);
|
523
|
+
|
524
|
+
// append command line ignore patterns
|
525
|
+
if (ignorePatterns && ignorePatterns.length > 0) {
|
526
|
+
|
527
|
+
let relativeIgnorePatterns;
|
528
|
+
|
529
|
+
/*
|
530
|
+
* If the config file basePath is different than the cwd, then
|
531
|
+
* the ignore patterns won't work correctly. Here, we adjust the
|
532
|
+
* ignore pattern to include the correct relative path. Patterns
|
533
|
+
* passed as `ignorePatterns` are relative to the cwd, whereas
|
534
|
+
* the config file basePath can be an ancestor of the cwd.
|
535
|
+
*/
|
536
|
+
if (basePath === cwd) {
|
537
|
+
relativeIgnorePatterns = ignorePatterns;
|
538
|
+
} else {
|
539
|
+
|
540
|
+
// relative path must only have Unix-style separators
|
541
|
+
const relativeIgnorePath = path.relative(basePath, cwd).replace(/\\/gu, "/");
|
542
|
+
|
543
|
+
relativeIgnorePatterns = ignorePatterns.map(pattern => {
|
544
|
+
const negated = pattern.startsWith("!");
|
545
|
+
const basePattern = negated ? pattern.slice(1) : pattern;
|
546
|
+
|
547
|
+
return (negated ? "!" : "") +
|
548
|
+
path.posix.join(relativeIgnorePath, basePattern);
|
549
|
+
});
|
550
|
+
}
|
551
|
+
|
552
|
+
/*
|
553
|
+
* Ignore patterns are added to the end of the config array
|
554
|
+
* so they can override default ignores.
|
555
|
+
*/
|
556
|
+
configs.push({
|
557
|
+
ignores: relativeIgnorePatterns
|
558
|
+
});
|
559
|
+
}
|
560
|
+
|
561
|
+
if (overrideConfig) {
|
562
|
+
if (Array.isArray(overrideConfig)) {
|
563
|
+
configs.push(...overrideConfig);
|
564
|
+
} else {
|
565
|
+
configs.push(overrideConfig);
|
566
|
+
}
|
567
|
+
}
|
568
|
+
|
569
|
+
await configs.normalize();
|
570
|
+
|
571
|
+
return configs;
|
572
|
+
}
|
573
|
+
|
529
574
|
}
|
530
575
|
|
531
576
|
/**
|
@@ -542,13 +587,13 @@ class LegacyConfigLoader extends ConfigLoader {
|
|
542
587
|
|
543
588
|
/**
|
544
589
|
* The cached config file path for this instance.
|
545
|
-
* @type {{configFilePath:string,basePath:string}|undefined}
|
590
|
+
* @type {Promise<{configFilePath:string,basePath:string}|undefined>}
|
546
591
|
*/
|
547
592
|
#configFilePath;
|
548
593
|
|
549
594
|
/**
|
550
595
|
* The cached config array for this instance.
|
551
|
-
* @type {FlatConfigArray}
|
596
|
+
* @type {FlatConfigArray|Promise<FlatConfigArray>}
|
552
597
|
*/
|
553
598
|
#configArray;
|
554
599
|
|
@@ -569,50 +614,16 @@ class LegacyConfigLoader extends ConfigLoader {
|
|
569
614
|
* @returns {Promise<{configFilePath:string|undefined,basePath:string}>} Location information for
|
570
615
|
* the config file.
|
571
616
|
*/
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
const configFilenames = this.#options.allowTS
|
580
|
-
? [...FLAT_CONFIG_FILENAMES, ...TS_FLAT_CONFIG_FILENAMES]
|
581
|
-
: FLAT_CONFIG_FILENAMES;
|
582
|
-
|
583
|
-
// determine where to load config file from
|
584
|
-
let configFilePath;
|
585
|
-
const {
|
586
|
-
cwd,
|
587
|
-
configFile: useConfigFile
|
588
|
-
} = this.#options;
|
589
|
-
let basePath = cwd;
|
590
|
-
|
591
|
-
if (typeof useConfigFile === "string") {
|
592
|
-
debug(`[Legacy]: Override config file path is ${useConfigFile}`);
|
593
|
-
configFilePath = path.resolve(cwd, useConfigFile);
|
594
|
-
basePath = cwd;
|
595
|
-
} else if (useConfigFile !== false) {
|
596
|
-
debug("[Legacy]: Searching for eslint.config.js");
|
597
|
-
configFilePath = await findUp(
|
598
|
-
configFilenames,
|
599
|
-
{ cwd }
|
600
|
-
);
|
601
|
-
|
602
|
-
if (configFilePath) {
|
603
|
-
basePath = path.dirname(configFilePath);
|
604
|
-
}
|
605
|
-
|
617
|
+
#locateConfigFileToUse() {
|
618
|
+
if (!this.#configFilePath) {
|
619
|
+
this.#configFilePath = ConfigLoader.locateConfigFileToUse({
|
620
|
+
useConfigFile: this.#options.configFile,
|
621
|
+
cwd: this.#options.cwd,
|
622
|
+
allowTS: this.#options.allowTS
|
623
|
+
});
|
606
624
|
}
|
607
625
|
|
608
|
-
|
609
|
-
this.#configFilePath = { configFilePath, basePath };
|
610
|
-
|
611
|
-
return {
|
612
|
-
configFilePath,
|
613
|
-
basePath
|
614
|
-
};
|
615
|
-
|
626
|
+
return this.#configFilePath;
|
616
627
|
}
|
617
628
|
|
618
629
|
/**
|
@@ -628,12 +639,13 @@ class LegacyConfigLoader extends ConfigLoader {
|
|
628
639
|
return this.#configArray;
|
629
640
|
}
|
630
641
|
|
631
|
-
|
642
|
+
// ensure `ConfigLoader.calculateConfigArray` is called only once
|
643
|
+
this.#configArray = ConfigLoader.calculateConfigArray(configFilePath, basePath, this.#options);
|
632
644
|
|
633
|
-
//
|
634
|
-
this.#configArray =
|
645
|
+
// Unwrap the promise. This is primarily for the sync `getCachedConfigArrayForPath` method.
|
646
|
+
this.#configArray = await this.#configArray;
|
635
647
|
|
636
|
-
return
|
648
|
+
return this.#configArray;
|
637
649
|
}
|
638
650
|
|
639
651
|
|
@@ -696,6 +708,10 @@ class LegacyConfigLoader extends ConfigLoader {
|
|
696
708
|
throw new Error(`Could not find config file for ${dirPath}`);
|
697
709
|
}
|
698
710
|
|
711
|
+
if (typeof this.#configArray.then === "function") {
|
712
|
+
throw new Error(`Config array for ${dirPath} has not yet been calculated or an error occurred during the calculation`);
|
713
|
+
}
|
714
|
+
|
699
715
|
return this.#configArray;
|
700
716
|
}
|
701
717
|
}
|
package/lib/config/config.js
CHANGED
@@ -9,8 +9,10 @@
|
|
9
9
|
// Requirements
|
10
10
|
//-----------------------------------------------------------------------------
|
11
11
|
|
12
|
-
const {
|
12
|
+
const { deepMergeArrays } = require("../shared/deep-merge-arrays");
|
13
|
+
const { getRuleFromConfig } = require("./flat-config-helpers");
|
13
14
|
const { flatConfigSchema, hasMethod } = require("./flat-config-schema");
|
15
|
+
const { RuleValidator } = require("./rule-validator");
|
14
16
|
const { ObjectSchema } = require("@eslint/config-array");
|
15
17
|
|
16
18
|
//-----------------------------------------------------------------------------
|
@@ -119,28 +121,6 @@ function languageOptionsToJSON(languageOptions, objectKey = "languageOptions") {
|
|
119
121
|
return result;
|
120
122
|
}
|
121
123
|
|
122
|
-
/**
|
123
|
-
* Normalizes the rules configuration. Ensure that each rule config is
|
124
|
-
* an array and that the severity is a number. This function modifies the
|
125
|
-
* rulesConfig.
|
126
|
-
* @param {Record<string, any>} rulesConfig The rules configuration to normalize.
|
127
|
-
* @returns {void}
|
128
|
-
*/
|
129
|
-
function normalizeRulesConfig(rulesConfig) {
|
130
|
-
|
131
|
-
for (const [ruleId, ruleConfig] of Object.entries(rulesConfig)) {
|
132
|
-
|
133
|
-
// ensure rule config is an array
|
134
|
-
if (!Array.isArray(ruleConfig)) {
|
135
|
-
rulesConfig[ruleId] = [ruleConfig];
|
136
|
-
}
|
137
|
-
|
138
|
-
// normalize severity
|
139
|
-
rulesConfig[ruleId][0] = severities.get(rulesConfig[ruleId][0]);
|
140
|
-
}
|
141
|
-
|
142
|
-
}
|
143
|
-
|
144
124
|
|
145
125
|
//-----------------------------------------------------------------------------
|
146
126
|
// Exports
|
@@ -215,6 +195,11 @@ class Config {
|
|
215
195
|
throw new TypeError(`Key "languageOptions": ${error.message}`, { cause: error });
|
216
196
|
}
|
217
197
|
|
198
|
+
// Normalize language options if necessary
|
199
|
+
if (this.language.normalizeLanguageOptions) {
|
200
|
+
this.languageOptions = this.language.normalizeLanguageOptions(this.languageOptions);
|
201
|
+
}
|
202
|
+
|
218
203
|
// Check processor value
|
219
204
|
if (processor) {
|
220
205
|
this.processor = processor;
|
@@ -239,7 +224,7 @@ class Config {
|
|
239
224
|
|
240
225
|
// Process the rules
|
241
226
|
if (this.rules) {
|
242
|
-
normalizeRulesConfig(
|
227
|
+
this.#normalizeRulesConfig();
|
243
228
|
ruleValidator.validate(this);
|
244
229
|
}
|
245
230
|
}
|
@@ -276,6 +261,37 @@ class Config {
|
|
276
261
|
processor: this.#processorName
|
277
262
|
};
|
278
263
|
}
|
264
|
+
|
265
|
+
/**
|
266
|
+
* Normalizes the rules configuration. Ensures that each rule config is
|
267
|
+
* an array and that the severity is a number. Applies meta.defaultOptions.
|
268
|
+
* This function modifies `this.rules`.
|
269
|
+
* @returns {void}
|
270
|
+
*/
|
271
|
+
#normalizeRulesConfig() {
|
272
|
+
for (const [ruleId, originalConfig] of Object.entries(this.rules)) {
|
273
|
+
|
274
|
+
// ensure rule config is an array
|
275
|
+
let ruleConfig = Array.isArray(originalConfig)
|
276
|
+
? originalConfig
|
277
|
+
: [originalConfig];
|
278
|
+
|
279
|
+
// normalize severity
|
280
|
+
ruleConfig[0] = severities.get(ruleConfig[0]);
|
281
|
+
|
282
|
+
const rule = getRuleFromConfig(ruleId, this);
|
283
|
+
|
284
|
+
// apply meta.defaultOptions
|
285
|
+
const slicedOptions = ruleConfig.slice(1);
|
286
|
+
const mergedOptions = deepMergeArrays(rule?.meta?.defaultOptions, slicedOptions);
|
287
|
+
|
288
|
+
if (mergedOptions.length) {
|
289
|
+
ruleConfig = [ruleConfig[0], ...mergedOptions];
|
290
|
+
}
|
291
|
+
|
292
|
+
this.rules[ruleId] = ruleConfig;
|
293
|
+
}
|
294
|
+
}
|
279
295
|
}
|
280
296
|
|
281
297
|
module.exports = { Config };
|