eslint 9.13.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 +2 -2
- package/lib/config/config-loader.js +193 -177
- package/lib/eslint/eslint-helpers.js +9 -9
- package/lib/eslint/eslint.js +2 -2
- 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/package.json +12 -12
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://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>
|
@@ -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
|
}
|
@@ -421,20 +421,19 @@ async function globMultiSearch({ searches, configLoader, errorOnUnmatchedPattern
|
|
421
421
|
)
|
422
422
|
);
|
423
423
|
|
424
|
-
|
425
|
-
|
424
|
+
/*
|
425
|
+
* The first loop handles errors from the glob searches. Since we can't
|
426
|
+
* use `await` inside `flatMap`, we process errors separately in this loop.
|
427
|
+
* This results in two iterations over `results`, but since the length is
|
428
|
+
* less than or equal to the number of globs and directories passed on the
|
429
|
+
* command line, the performance impact should be minimal.
|
430
|
+
*/
|
426
431
|
for (let i = 0; i < results.length; i++) {
|
427
432
|
|
428
433
|
const result = results[i];
|
429
434
|
const currentSearch = normalizedSearches[i];
|
430
435
|
|
431
436
|
if (result.status === "fulfilled") {
|
432
|
-
|
433
|
-
// if the search was successful just add the results
|
434
|
-
if (result.value.length > 0) {
|
435
|
-
filePaths.push(...result.value);
|
436
|
-
}
|
437
|
-
|
438
437
|
continue;
|
439
438
|
}
|
440
439
|
|
@@ -457,7 +456,8 @@ async function globMultiSearch({ searches, configLoader, errorOnUnmatchedPattern
|
|
457
456
|
|
458
457
|
}
|
459
458
|
|
460
|
-
|
459
|
+
// second loop for `fulfulled` results
|
460
|
+
return results.flatMap(result => result.value);
|
461
461
|
|
462
462
|
}
|
463
463
|
|
package/lib/eslint/eslint.js
CHANGED
@@ -746,7 +746,7 @@ class ESLint {
|
|
746
746
|
});
|
747
747
|
const controller = new AbortController();
|
748
748
|
const retryCodes = new Set(["ENFILE", "EMFILE"]);
|
749
|
-
const retrier = new Retrier(error => retryCodes.has(error.code));
|
749
|
+
const retrier = new Retrier(error => retryCodes.has(error.code), { concurrency: 100 });
|
750
750
|
|
751
751
|
debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);
|
752
752
|
|
@@ -830,7 +830,7 @@ class ESLint {
|
|
830
830
|
}
|
831
831
|
|
832
832
|
return result;
|
833
|
-
}))
|
833
|
+
}), { signal: controller.signal })
|
834
834
|
.catch(error => {
|
835
835
|
controller.abort(error);
|
836
836
|
throw error;
|
package/lib/rules/camelcase.js
CHANGED
@@ -238,6 +238,13 @@ module.exports = {
|
|
238
238
|
return;
|
239
239
|
}
|
240
240
|
|
241
|
+
/*
|
242
|
+
* Import attribute keys are always ignored
|
243
|
+
*/
|
244
|
+
if (astUtils.isImportAttributeKey(node)) {
|
245
|
+
return;
|
246
|
+
}
|
247
|
+
|
241
248
|
report(node);
|
242
249
|
}
|
243
250
|
|
@@ -272,7 +279,7 @@ module.exports = {
|
|
272
279
|
for (const reference of scope.through) {
|
273
280
|
const id = reference.identifier;
|
274
281
|
|
275
|
-
if (isGoodName(id.name)) {
|
282
|
+
if (isGoodName(id.name) || astUtils.isImportAttributeKey(id)) {
|
276
283
|
continue;
|
277
284
|
}
|
278
285
|
|
@@ -326,7 +333,7 @@ module.exports = {
|
|
326
333
|
"MethodDefinition > PrivateIdentifier.key",
|
327
334
|
"PropertyDefinition > PrivateIdentifier.key"
|
328
335
|
]](node) {
|
329
|
-
if (properties === "never" || isGoodName(node.name)) {
|
336
|
+
if (properties === "never" || astUtils.isImportAttributeKey(node) || isGoodName(node.name)) {
|
330
337
|
return;
|
331
338
|
}
|
332
339
|
report(node);
|
package/lib/rules/id-denylist.js
CHANGED
@@ -6,6 +6,12 @@
|
|
6
6
|
|
7
7
|
"use strict";
|
8
8
|
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Requirements
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
const astUtils = require("./utils/ast-utils");
|
14
|
+
|
9
15
|
//------------------------------------------------------------------------------
|
10
16
|
// Helpers
|
11
17
|
//------------------------------------------------------------------------------
|
@@ -154,6 +160,12 @@ module.exports = {
|
|
154
160
|
* @returns {boolean} `true` if the node should be checked.
|
155
161
|
*/
|
156
162
|
function shouldCheck(node) {
|
163
|
+
|
164
|
+
// Import attributes are defined by environments, so naming conventions shouldn't apply to them
|
165
|
+
if (astUtils.isImportAttributeKey(node)) {
|
166
|
+
return false;
|
167
|
+
}
|
168
|
+
|
157
169
|
const parent = node.parent;
|
158
170
|
|
159
171
|
/*
|
package/lib/rules/id-length.js
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
//------------------------------------------------------------------------------
|
12
12
|
|
13
13
|
const { getGraphemeCount } = require("../shared/string-utils");
|
14
|
-
const { getModuleExportName } = require("./utils/ast-utils");
|
14
|
+
const { getModuleExportName, isImportAttributeKey } = require("./utils/ast-utils");
|
15
15
|
|
16
16
|
//------------------------------------------------------------------------------
|
17
17
|
// Rule Definition
|
@@ -115,7 +115,7 @@ module.exports = {
|
|
115
115
|
isKeyAndValueSame && parent.key === node && properties
|
116
116
|
);
|
117
117
|
}
|
118
|
-
return properties && !parent.computed && parent.key.name === node.name;
|
118
|
+
return properties && !isImportAttributeKey(node) && !parent.computed && parent.key.name === node.name;
|
119
119
|
},
|
120
120
|
ImportSpecifier(parent, node) {
|
121
121
|
return (
|
package/lib/rules/id-match.js
CHANGED
@@ -5,6 +5,12 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Requirements
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const astUtils = require("./utils/ast-utils");
|
13
|
+
|
8
14
|
//------------------------------------------------------------------------------
|
9
15
|
// Rule Definition
|
10
16
|
//------------------------------------------------------------------------------
|
@@ -180,7 +186,7 @@ module.exports = {
|
|
180
186
|
parent = node.parent,
|
181
187
|
effectiveParent = (parent.type === "MemberExpression") ? parent.parent : parent;
|
182
188
|
|
183
|
-
if (isReferenceToGlobalVariable(node)) {
|
189
|
+
if (isReferenceToGlobalVariable(node) || astUtils.isImportAttributeKey(node)) {
|
184
190
|
return;
|
185
191
|
}
|
186
192
|
|
@@ -1054,7 +1054,7 @@ let needsPrecedingSemicolon;
|
|
1054
1054
|
{
|
1055
1055
|
const BREAK_OR_CONTINUE = new Set(["BreakStatement", "ContinueStatement"]);
|
1056
1056
|
|
1057
|
-
// Declaration types that
|
1057
|
+
// Declaration types that cannot be continued by a punctuator when ending with a string Literal that is a direct child.
|
1058
1058
|
const DECLARATIONS = new Set(["ExportAllDeclaration", "ExportNamedDeclaration", "ImportDeclaration"]);
|
1059
1059
|
|
1060
1060
|
const IDENTIFIER_OR_KEYWORD = new Set(["Identifier", "Keyword"]);
|
@@ -1132,6 +1132,48 @@ let needsPrecedingSemicolon;
|
|
1132
1132
|
};
|
1133
1133
|
}
|
1134
1134
|
|
1135
|
+
/**
|
1136
|
+
* Checks if a node is used as an import attribute key, either in a static or dynamic import.
|
1137
|
+
* @param {ASTNode} node The node to check.
|
1138
|
+
* @returns {boolean} Whether the node is used as an import attribute key.
|
1139
|
+
*/
|
1140
|
+
function isImportAttributeKey(node) {
|
1141
|
+
const { parent } = node;
|
1142
|
+
|
1143
|
+
// static import/re-export
|
1144
|
+
if (parent.type === "ImportAttribute" && parent.key === node) {
|
1145
|
+
return true;
|
1146
|
+
}
|
1147
|
+
|
1148
|
+
// dynamic import
|
1149
|
+
if (
|
1150
|
+
parent.type === "Property" &&
|
1151
|
+
!parent.computed &&
|
1152
|
+
(parent.key === node || parent.value === node && parent.shorthand && !parent.method) &&
|
1153
|
+
parent.parent.type === "ObjectExpression"
|
1154
|
+
) {
|
1155
|
+
const objectExpression = parent.parent;
|
1156
|
+
const objectExpressionParent = objectExpression.parent;
|
1157
|
+
|
1158
|
+
if (
|
1159
|
+
objectExpressionParent.type === "ImportExpression" &&
|
1160
|
+
objectExpressionParent.options === objectExpression
|
1161
|
+
) {
|
1162
|
+
return true;
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
// nested key
|
1166
|
+
if (
|
1167
|
+
objectExpressionParent.type === "Property" &&
|
1168
|
+
objectExpressionParent.value === objectExpression
|
1169
|
+
) {
|
1170
|
+
return isImportAttributeKey(objectExpressionParent.key);
|
1171
|
+
}
|
1172
|
+
}
|
1173
|
+
|
1174
|
+
return false;
|
1175
|
+
}
|
1176
|
+
|
1135
1177
|
//------------------------------------------------------------------------------
|
1136
1178
|
// Public Interface
|
1137
1179
|
//------------------------------------------------------------------------------
|
@@ -2288,5 +2330,6 @@ module.exports = {
|
|
2288
2330
|
isTopLevelExpressionStatement,
|
2289
2331
|
isDirective,
|
2290
2332
|
isStartOfExpressionStatement,
|
2291
|
-
needsPrecedingSemicolon
|
2333
|
+
needsPrecedingSemicolon,
|
2334
|
+
isImportAttributeKey
|
2292
2335
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "9.
|
3
|
+
"version": "9.14.0",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"type": "commonjs",
|
@@ -98,15 +98,15 @@
|
|
98
98
|
"bugs": "https://github.com/eslint/eslint/issues/",
|
99
99
|
"dependencies": {
|
100
100
|
"@eslint-community/eslint-utils": "^4.2.0",
|
101
|
-
"@eslint-community/regexpp": "^4.
|
101
|
+
"@eslint-community/regexpp": "^4.12.1",
|
102
102
|
"@eslint/config-array": "^0.18.0",
|
103
103
|
"@eslint/core": "^0.7.0",
|
104
104
|
"@eslint/eslintrc": "^3.1.0",
|
105
|
-
"@eslint/js": "9.
|
105
|
+
"@eslint/js": "9.14.0",
|
106
106
|
"@eslint/plugin-kit": "^0.2.0",
|
107
|
-
"@humanfs/node": "^0.16.
|
107
|
+
"@humanfs/node": "^0.16.6",
|
108
108
|
"@humanwhocodes/module-importer": "^1.0.1",
|
109
|
-
"@humanwhocodes/retry": "^0.
|
109
|
+
"@humanwhocodes/retry": "^0.4.0",
|
110
110
|
"@types/estree": "^1.0.6",
|
111
111
|
"@types/json-schema": "^7.0.15",
|
112
112
|
"ajv": "^6.12.4",
|
@@ -114,9 +114,9 @@
|
|
114
114
|
"cross-spawn": "^7.0.2",
|
115
115
|
"debug": "^4.3.2",
|
116
116
|
"escape-string-regexp": "^4.0.0",
|
117
|
-
"eslint-scope": "^8.
|
118
|
-
"eslint-visitor-keys": "^4.
|
119
|
-
"espree": "^10.
|
117
|
+
"eslint-scope": "^8.2.0",
|
118
|
+
"eslint-visitor-keys": "^4.2.0",
|
119
|
+
"espree": "^10.3.0",
|
120
120
|
"esquery": "^1.5.0",
|
121
121
|
"esutils": "^2.0.2",
|
122
122
|
"fast-deep-equal": "^3.1.3",
|
@@ -141,10 +141,10 @@
|
|
141
141
|
"@trunkio/launcher": "^1.3.0",
|
142
142
|
"@types/node": "^20.11.5",
|
143
143
|
"@typescript-eslint/parser": "^8.4.0",
|
144
|
-
"@wdio/browser-runner": "^9.
|
145
|
-
"@wdio/cli": "^9.
|
146
|
-
"@wdio/concise-reporter": "^9.
|
147
|
-
"@wdio/mocha-framework": "^9.
|
144
|
+
"@wdio/browser-runner": "^9.2.4",
|
145
|
+
"@wdio/cli": "^9.2.4",
|
146
|
+
"@wdio/concise-reporter": "^9.2.2",
|
147
|
+
"@wdio/mocha-framework": "^9.2.2",
|
148
148
|
"babel-loader": "^8.0.5",
|
149
149
|
"c8": "^7.12.0",
|
150
150
|
"chai": "^4.0.1",
|