eslint 9.12.0 → 9.14.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 +9 -10
- package/bin/eslint.js +5 -0
- package/lib/cli.js +13 -12
- package/lib/config/config-loader.js +209 -184
- package/lib/config/config.js +13 -10
- package/lib/config/default-config.js +2 -8
- package/lib/eslint/eslint-helpers.js +9 -9
- package/lib/eslint/eslint.js +13 -2
- package/lib/languages/js/index.js +8 -0
- package/lib/linter/linter.js +16 -19
- package/lib/rule-tester/rule-tester.js +6 -16
- package/lib/rules/camelcase.js +9 -2
- package/lib/rules/id-denylist.js +12 -0
- package/lib/rules/id-length.js +2 -2
- package/lib/rules/id-match.js +7 -1
- package/lib/rules/utils/ast-utils.js +45 -2
- package/lib/types/index.d.ts +12 -0
- package/lib/types/rules/best-practices.d.ts +33 -2
- package/lib/types/rules/deprecated.d.ts +18 -77
- package/lib/types/rules/ecmascript-6.d.ts +22 -6
- package/lib/types/rules/possible-errors.d.ts +2 -0
- package/lib/types/rules/stylistic-issues.d.ts +68 -5
- package/lib/types/rules/variables.d.ts +9 -1
- package/package.json +15 -14
package/README.md
CHANGED
@@ -289,23 +289,22 @@ Percy Ma
|
|
289
289
|
|
290
290
|
<!--teamend-->
|
291
291
|
|
292
|
+
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
293
|
+
<!--sponsorsstart-->
|
292
294
|
## Sponsors
|
293
295
|
|
294
|
-
The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
|
296
|
+
The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
|
297
|
+
to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
295
298
|
|
296
|
-
<!-- NOTE: This section is autogenerated. Do not manually edit.-->
|
297
|
-
<!--sponsorsstart-->
|
298
299
|
<h3>Platinum Sponsors</h3>
|
299
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>
|
300
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>
|
301
|
-
<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>
|
302
|
-
<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
|
303
|
-
|
304
|
-
|
305
|
-
<!--techsponsorsstart-->
|
306
|
-
<h2>Technology Sponsors</h2>
|
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://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
|
+
<h3>Technology Sponsors</h3>
|
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.
|
307
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>
|
308
|
-
<!--
|
307
|
+
<!--sponsorsend-->
|
309
308
|
|
310
309
|
[tidelift]: https://tidelift.com/funding/github/npm/eslint
|
311
310
|
[herodevs]: https://www.herodevs.com/support/eslint-nes?utm_source=ESLintWebsite&utm_medium=ESLintWebsite&utm_campaign=ESLintNES&utm_id=ESLintNES
|
package/bin/eslint.js
CHANGED
@@ -9,6 +9,11 @@
|
|
9
9
|
|
10
10
|
"use strict";
|
11
11
|
|
12
|
+
const mod = require("node:module");
|
13
|
+
|
14
|
+
// to use V8's code cache to speed up instantiation time
|
15
|
+
mod.enableCompileCache?.();
|
16
|
+
|
12
17
|
// must do this initialization *before* other requires in order to work
|
13
18
|
if (process.argv.includes("--debug")) {
|
14
19
|
require("debug").enable("eslint:*,-eslint:code-path,eslintrc:*");
|
package/lib/cli.js
CHANGED
@@ -146,24 +146,29 @@ async function translateOptions({
|
|
146
146
|
overrideConfigFile = void 0;
|
147
147
|
}
|
148
148
|
|
149
|
-
|
149
|
+
const languageOptions = {};
|
150
150
|
|
151
151
|
if (global) {
|
152
|
-
globals = global.reduce((obj, name) => {
|
152
|
+
languageOptions.globals = global.reduce((obj, name) => {
|
153
153
|
if (name.endsWith(":true")) {
|
154
154
|
obj[name.slice(0, -5)] = "writable";
|
155
155
|
} else {
|
156
156
|
obj[name] = "readonly";
|
157
157
|
}
|
158
158
|
return obj;
|
159
|
-
},
|
159
|
+
}, {});
|
160
|
+
}
|
161
|
+
|
162
|
+
if (parserOptions) {
|
163
|
+
languageOptions.parserOptions = parserOptions;
|
164
|
+
}
|
165
|
+
|
166
|
+
if (parser) {
|
167
|
+
languageOptions.parser = await importer.import(parser);
|
160
168
|
}
|
161
169
|
|
162
170
|
overrideConfig = [{
|
163
|
-
languageOptions: {
|
164
|
-
globals,
|
165
|
-
parserOptions: parserOptions || {}
|
166
|
-
},
|
171
|
+
...Object.keys(languageOptions).length > 0 ? { languageOptions } : {},
|
167
172
|
rules: rule ? rule : {}
|
168
173
|
}];
|
169
174
|
|
@@ -175,10 +180,6 @@ async function translateOptions({
|
|
175
180
|
};
|
176
181
|
}
|
177
182
|
|
178
|
-
if (parser) {
|
179
|
-
overrideConfig[0].languageOptions.parser = await importer.import(parser);
|
180
|
-
}
|
181
|
-
|
182
183
|
if (plugin) {
|
183
184
|
overrideConfig[0].plugins = await loadPlugins(importer, plugin);
|
184
185
|
}
|
@@ -378,7 +379,7 @@ const cli = {
|
|
378
379
|
debug("Using flat config?", usingFlatConfig);
|
379
380
|
|
380
381
|
if (allowFlatConfig && !usingFlatConfig) {
|
381
|
-
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");
|
382
|
+
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. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.", "ESLintRCWarning");
|
382
383
|
}
|
383
384
|
|
384
385
|
const CLIOptions = createCLIOptions(usingFlatConfig);
|
@@ -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
|
@@ -173,21 +173,22 @@ async function loadConfigFile(filePath, allowTS) {
|
|
173
173
|
*/
|
174
174
|
if (allowTS && isTS && !isDeno && !isBun) {
|
175
175
|
|
176
|
-
|
176
|
+
// eslint-disable-next-line no-use-before-define -- `ConfigLoader.loadJiti` can be overwritten for testing
|
177
|
+
const { createJiti } = await ConfigLoader.loadJiti().catch(() => {
|
177
178
|
throw new Error("The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.");
|
178
179
|
});
|
179
180
|
|
181
|
+
// `createJiti` was added in jiti v2.
|
182
|
+
if (typeof createJiti !== "function") {
|
183
|
+
throw new Error("You are using an outdated version of the 'jiti' library. Please update to the latest version of 'jiti' to ensure compatibility and access to the latest features.");
|
184
|
+
}
|
185
|
+
|
180
186
|
/*
|
181
187
|
* Disabling `moduleCache` allows us to reload a
|
182
188
|
* config file when the last modified timestamp changes.
|
183
189
|
*/
|
184
190
|
|
185
191
|
const jiti = createJiti(__filename, { moduleCache: false, interopDefault: false });
|
186
|
-
|
187
|
-
if (typeof jiti?.import !== "function") {
|
188
|
-
throw new Error("You are using an outdated version of the 'jiti' library. Please update to the latest version of 'jiti' to ensure compatibility and access to the latest features.");
|
189
|
-
}
|
190
|
-
|
191
192
|
const config = await jiti.import(fileURL.href);
|
192
193
|
|
193
194
|
importedConfigFileModificationTime.set(filePath, mtime);
|
@@ -205,96 +206,6 @@ async function loadConfigFile(filePath, allowTS) {
|
|
205
206
|
return config;
|
206
207
|
}
|
207
208
|
|
208
|
-
/**
|
209
|
-
* Calculates the config array for this run based on inputs.
|
210
|
-
* @param {string} configFilePath The absolute path to the config file to use if not overridden.
|
211
|
-
* @param {string} basePath The base path to use for relative paths in the config file.
|
212
|
-
* @param {ConfigLoaderOptions} options The options to use when loading configuration files.
|
213
|
-
* @returns {Promise<FlatConfigArray>} The config array for `eslint`.
|
214
|
-
*/
|
215
|
-
async function calculateConfigArray(configFilePath, basePath, options) {
|
216
|
-
|
217
|
-
const {
|
218
|
-
cwd,
|
219
|
-
baseConfig,
|
220
|
-
ignoreEnabled,
|
221
|
-
ignorePatterns,
|
222
|
-
overrideConfig,
|
223
|
-
defaultConfigs = [],
|
224
|
-
allowTS
|
225
|
-
} = options;
|
226
|
-
|
227
|
-
debug(`Calculating config array from config file ${configFilePath} and base path ${basePath}`);
|
228
|
-
|
229
|
-
const configs = new FlatConfigArray(baseConfig || [], { basePath, shouldIgnore: ignoreEnabled });
|
230
|
-
|
231
|
-
// load config file
|
232
|
-
if (configFilePath) {
|
233
|
-
|
234
|
-
debug(`Loading config file ${configFilePath}`);
|
235
|
-
const fileConfig = await loadConfigFile(configFilePath, allowTS);
|
236
|
-
|
237
|
-
if (Array.isArray(fileConfig)) {
|
238
|
-
configs.push(...fileConfig);
|
239
|
-
} else {
|
240
|
-
configs.push(fileConfig);
|
241
|
-
}
|
242
|
-
}
|
243
|
-
|
244
|
-
// add in any configured defaults
|
245
|
-
configs.push(...defaultConfigs);
|
246
|
-
|
247
|
-
// append command line ignore patterns
|
248
|
-
if (ignorePatterns && ignorePatterns.length > 0) {
|
249
|
-
|
250
|
-
let relativeIgnorePatterns;
|
251
|
-
|
252
|
-
/*
|
253
|
-
* If the config file basePath is different than the cwd, then
|
254
|
-
* the ignore patterns won't work correctly. Here, we adjust the
|
255
|
-
* ignore pattern to include the correct relative path. Patterns
|
256
|
-
* passed as `ignorePatterns` are relative to the cwd, whereas
|
257
|
-
* the config file basePath can be an ancestor of the cwd.
|
258
|
-
*/
|
259
|
-
if (basePath === cwd) {
|
260
|
-
relativeIgnorePatterns = ignorePatterns;
|
261
|
-
} else {
|
262
|
-
|
263
|
-
// relative path must only have Unix-style separators
|
264
|
-
const relativeIgnorePath = path.relative(basePath, cwd).replace(/\\/gu, "/");
|
265
|
-
|
266
|
-
relativeIgnorePatterns = ignorePatterns.map(pattern => {
|
267
|
-
const negated = pattern.startsWith("!");
|
268
|
-
const basePattern = negated ? pattern.slice(1) : pattern;
|
269
|
-
|
270
|
-
return (negated ? "!" : "") +
|
271
|
-
path.posix.join(relativeIgnorePath, basePattern);
|
272
|
-
});
|
273
|
-
}
|
274
|
-
|
275
|
-
/*
|
276
|
-
* Ignore patterns are added to the end of the config array
|
277
|
-
* so they can override default ignores.
|
278
|
-
*/
|
279
|
-
configs.push({
|
280
|
-
ignores: relativeIgnorePatterns
|
281
|
-
});
|
282
|
-
}
|
283
|
-
|
284
|
-
if (overrideConfig) {
|
285
|
-
if (Array.isArray(overrideConfig)) {
|
286
|
-
configs.push(...overrideConfig);
|
287
|
-
} else {
|
288
|
-
configs.push(overrideConfig);
|
289
|
-
}
|
290
|
-
}
|
291
|
-
|
292
|
-
await configs.normalize();
|
293
|
-
|
294
|
-
return configs;
|
295
|
-
}
|
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
|
-
);
|
372
|
-
|
373
|
-
if (configFilePath) {
|
374
|
-
basePath = path.dirname(configFilePath);
|
375
|
-
}
|
261
|
+
const resultPromise = ConfigLoader.locateConfigFileToUse({
|
262
|
+
useConfigFile: this.#options.configFile,
|
263
|
+
cwd: this.#options.cwd,
|
264
|
+
fromDirectory,
|
265
|
+
allowTS: this.#options.allowTS
|
266
|
+
});
|
376
267
|
|
377
|
-
|
268
|
+
// ensure `ConfigLoader.locateConfigFileToUse` is called only once for `fromDirectory`
|
269
|
+
this.#configFilePaths.set(fromDirectory, resultPromise);
|
378
270
|
|
379
|
-
//
|
380
|
-
|
271
|
+
// Unwrap the promise. This is primarily for the sync `getCachedConfigArrayForPath` method.
|
272
|
+
const result = await resultPromise;
|
381
273
|
|
382
|
-
|
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,168 @@ 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);
|
516
411
|
|
517
|
-
|
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);
|
419
|
+
|
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;
|
425
|
+
}
|
426
|
+
|
427
|
+
/**
|
428
|
+
* Used to import the jiti dependency. This method is exposed internally for testing purposes.
|
429
|
+
* @returns {Promise<Record<string, unknown>>} A promise that fulfills with a module object
|
430
|
+
* or rejects with an error if jiti is not found.
|
431
|
+
*/
|
432
|
+
static loadJiti() {
|
433
|
+
return import("jiti");
|
434
|
+
}
|
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;
|
518
572
|
}
|
519
573
|
|
520
574
|
}
|
@@ -533,13 +587,13 @@ class LegacyConfigLoader extends ConfigLoader {
|
|
533
587
|
|
534
588
|
/**
|
535
589
|
* The cached config file path for this instance.
|
536
|
-
* @type {{configFilePath:string,basePath:string}|undefined}
|
590
|
+
* @type {Promise<{configFilePath:string,basePath:string}|undefined>}
|
537
591
|
*/
|
538
592
|
#configFilePath;
|
539
593
|
|
540
594
|
/**
|
541
595
|
* The cached config array for this instance.
|
542
|
-
* @type {FlatConfigArray}
|
596
|
+
* @type {FlatConfigArray|Promise<FlatConfigArray>}
|
543
597
|
*/
|
544
598
|
#configArray;
|
545
599
|
|
@@ -560,50 +614,16 @@ class LegacyConfigLoader extends ConfigLoader {
|
|
560
614
|
* @returns {Promise<{configFilePath:string|undefined,basePath:string}>} Location information for
|
561
615
|
* the config file.
|
562
616
|
*/
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
const configFilenames = this.#options.allowTS
|
571
|
-
? [...FLAT_CONFIG_FILENAMES, ...TS_FLAT_CONFIG_FILENAMES]
|
572
|
-
: FLAT_CONFIG_FILENAMES;
|
573
|
-
|
574
|
-
// determine where to load config file from
|
575
|
-
let configFilePath;
|
576
|
-
const {
|
577
|
-
cwd,
|
578
|
-
configFile: useConfigFile
|
579
|
-
} = this.#options;
|
580
|
-
let basePath = cwd;
|
581
|
-
|
582
|
-
if (typeof useConfigFile === "string") {
|
583
|
-
debug(`[Legacy]: Override config file path is ${useConfigFile}`);
|
584
|
-
configFilePath = path.resolve(cwd, useConfigFile);
|
585
|
-
basePath = cwd;
|
586
|
-
} else if (useConfigFile !== false) {
|
587
|
-
debug("[Legacy]: Searching for eslint.config.js");
|
588
|
-
configFilePath = await findUp(
|
589
|
-
configFilenames,
|
590
|
-
{ cwd }
|
591
|
-
);
|
592
|
-
|
593
|
-
if (configFilePath) {
|
594
|
-
basePath = path.dirname(configFilePath);
|
595
|
-
}
|
596
|
-
|
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
|
+
});
|
597
624
|
}
|
598
625
|
|
599
|
-
|
600
|
-
this.#configFilePath = { configFilePath, basePath };
|
601
|
-
|
602
|
-
return {
|
603
|
-
configFilePath,
|
604
|
-
basePath
|
605
|
-
};
|
606
|
-
|
626
|
+
return this.#configFilePath;
|
607
627
|
}
|
608
628
|
|
609
629
|
/**
|
@@ -619,12 +639,13 @@ class LegacyConfigLoader extends ConfigLoader {
|
|
619
639
|
return this.#configArray;
|
620
640
|
}
|
621
641
|
|
622
|
-
|
642
|
+
// ensure `ConfigLoader.calculateConfigArray` is called only once
|
643
|
+
this.#configArray = ConfigLoader.calculateConfigArray(configFilePath, basePath, this.#options);
|
623
644
|
|
624
|
-
//
|
625
|
-
this.#configArray =
|
645
|
+
// Unwrap the promise. This is primarily for the sync `getCachedConfigArrayForPath` method.
|
646
|
+
this.#configArray = await this.#configArray;
|
626
647
|
|
627
|
-
return
|
648
|
+
return this.#configArray;
|
628
649
|
}
|
629
650
|
|
630
651
|
|
@@ -687,6 +708,10 @@ class LegacyConfigLoader extends ConfigLoader {
|
|
687
708
|
throw new Error(`Could not find config file for ${dirPath}`);
|
688
709
|
}
|
689
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
|
+
|
690
715
|
return this.#configArray;
|
691
716
|
}
|
692
717
|
}
|
package/lib/config/config.js
CHANGED
@@ -188,10 +188,6 @@ class Config {
|
|
188
188
|
this.plugins = plugins;
|
189
189
|
this.language = language;
|
190
190
|
|
191
|
-
if (languageOptions) {
|
192
|
-
this.languageOptions = languageOptions;
|
193
|
-
}
|
194
|
-
|
195
191
|
// Check language value
|
196
192
|
const { pluginName: languagePluginName, objectName: localLanguageName } = splitPluginIdentifier(language);
|
197
193
|
|
@@ -203,13 +199,20 @@ class Config {
|
|
203
199
|
|
204
200
|
this.language = plugins[languagePluginName].languages[localLanguageName];
|
205
201
|
|
202
|
+
if (this.language.defaultLanguageOptions ?? languageOptions) {
|
203
|
+
this.languageOptions = flatConfigSchema.languageOptions.merge(
|
204
|
+
this.language.defaultLanguageOptions,
|
205
|
+
languageOptions
|
206
|
+
);
|
207
|
+
} else {
|
208
|
+
this.languageOptions = {};
|
209
|
+
}
|
210
|
+
|
206
211
|
// Validate language options
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
throw new TypeError(`Key "languageOptions": ${error.message}`, { cause: error });
|
212
|
-
}
|
212
|
+
try {
|
213
|
+
this.language.validateLanguageOptions(this.languageOptions);
|
214
|
+
} catch (error) {
|
215
|
+
throw new TypeError(`Key "languageOptions": ${error.message}`, { cause: error });
|
213
216
|
}
|
214
217
|
|
215
218
|
// Check processor value
|
@@ -15,7 +15,7 @@ const Rules = require("../rules");
|
|
15
15
|
// Helpers
|
16
16
|
//-----------------------------------------------------------------------------
|
17
17
|
|
18
|
-
exports.defaultConfig = [
|
18
|
+
exports.defaultConfig = Object.freeze([
|
19
19
|
{
|
20
20
|
plugins: {
|
21
21
|
"@": {
|
@@ -42,12 +42,6 @@ exports.defaultConfig = [
|
|
42
42
|
}
|
43
43
|
},
|
44
44
|
language: "@/js",
|
45
|
-
languageOptions: {
|
46
|
-
sourceType: "module",
|
47
|
-
ecmaVersion: "latest",
|
48
|
-
parser: require("espree"),
|
49
|
-
parserOptions: {}
|
50
|
-
},
|
51
45
|
linterOptions: {
|
52
46
|
reportUnusedDisableDirectives: 1
|
53
47
|
}
|
@@ -72,4 +66,4 @@ exports.defaultConfig = [
|
|
72
66
|
ecmaVersion: "latest"
|
73
67
|
}
|
74
68
|
}
|
75
|
-
];
|
69
|
+
]);
|