eslint 9.11.1 → 9.12.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 +1 -1
- package/lib/cli-engine/formatters/stylish.js +2 -2
- package/lib/cli.js +1 -6
- package/lib/config/config-loader.js +694 -0
- package/lib/eslint/eslint-helpers.js +128 -134
- package/lib/eslint/eslint.js +84 -294
- package/lib/rule-tester/rule-tester.js +33 -2
- package/lib/rules/complexity.js +16 -6
- package/lib/shared/flags.js +1 -0
- package/lib/unsupported-api.js +2 -1
- package/package.json +25 -12
@@ -15,9 +15,7 @@ const fsp = fs.promises;
|
|
15
15
|
const isGlob = require("is-glob");
|
16
16
|
const hash = require("../cli-engine/hash");
|
17
17
|
const minimatch = require("minimatch");
|
18
|
-
const fswalk = require("@nodelib/fs.walk");
|
19
18
|
const globParent = require("glob-parent");
|
20
|
-
const isPathInside = require("is-path-inside");
|
21
19
|
|
22
20
|
//-----------------------------------------------------------------------------
|
23
21
|
// Fixup references
|
@@ -160,31 +158,28 @@ function isGlobPattern(pattern) {
|
|
160
158
|
* @param {string} options.pattern A glob pattern to match.
|
161
159
|
* @returns {Promise<boolean>} True if there is a glob match, false if not.
|
162
160
|
*/
|
163
|
-
function globMatch({ basePath, pattern }) {
|
161
|
+
async function globMatch({ basePath, pattern }) {
|
164
162
|
|
165
163
|
let found = false;
|
164
|
+
const { hfs } = await import("@humanfs/node");
|
166
165
|
const patternToUse = path.isAbsolute(pattern)
|
167
166
|
? normalizeToPosix(path.relative(basePath, pattern))
|
168
167
|
: pattern;
|
169
168
|
|
170
169
|
const matcher = new Minimatch(patternToUse, MINIMATCH_OPTIONS);
|
171
170
|
|
172
|
-
const
|
171
|
+
const walkSettings = {
|
173
172
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
return !found && matcher.match(relativePath, true);
|
173
|
+
directoryFilter(entry) {
|
174
|
+
return !found && matcher.match(entry.path, true);
|
178
175
|
},
|
179
176
|
|
180
177
|
entryFilter(entry) {
|
181
|
-
if (found || entry.
|
178
|
+
if (found || entry.isDirectory) {
|
182
179
|
return false;
|
183
180
|
}
|
184
181
|
|
185
|
-
|
186
|
-
|
187
|
-
if (matcher.match(relativePath)) {
|
182
|
+
if (matcher.match(entry.path)) {
|
188
183
|
found = true;
|
189
184
|
return true;
|
190
185
|
}
|
@@ -193,25 +188,11 @@ function globMatch({ basePath, pattern }) {
|
|
193
188
|
}
|
194
189
|
};
|
195
190
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
const globStream = fswalk.walkStream(basePath, fsWalkSettings);
|
200
|
-
|
201
|
-
globStream.on("data", () => {
|
202
|
-
globStream.destroy();
|
203
|
-
resolve(true);
|
204
|
-
});
|
205
|
-
|
206
|
-
// swallow errors as they're not important here
|
207
|
-
globStream.on("error", () => { });
|
208
|
-
|
209
|
-
globStream.on("end", () => {
|
210
|
-
resolve(false);
|
211
|
-
});
|
212
|
-
globStream.read();
|
213
|
-
});
|
191
|
+
if (await hfs.isDirectory(basePath)) {
|
192
|
+
return hfs.walk(basePath, walkSettings).next().then(() => found);
|
193
|
+
}
|
214
194
|
|
195
|
+
return found;
|
215
196
|
}
|
216
197
|
|
217
198
|
/**
|
@@ -225,7 +206,7 @@ function globMatch({ basePath, pattern }) {
|
|
225
206
|
* to match.
|
226
207
|
* @param {Array<string>} options.rawPatterns An array of glob patterns
|
227
208
|
* as the user inputted them. Used for errors.
|
228
|
-
* @param {
|
209
|
+
* @param {ConfigLoader|LegacyConfigLoader} options.configLoader The config array to use for
|
229
210
|
* determining what to ignore.
|
230
211
|
* @param {boolean} options.errorOnUnmatchedPattern Determines if an error
|
231
212
|
* should be thrown when a pattern is unmatched.
|
@@ -238,7 +219,7 @@ async function globSearch({
|
|
238
219
|
basePath,
|
239
220
|
patterns,
|
240
221
|
rawPatterns,
|
241
|
-
|
222
|
+
configLoader,
|
242
223
|
errorOnUnmatchedPattern
|
243
224
|
}) {
|
244
225
|
|
@@ -277,94 +258,76 @@ async function globSearch({
|
|
277
258
|
* search.
|
278
259
|
*/
|
279
260
|
const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
|
261
|
+
const { hfs } = await import("@humanfs/node");
|
280
262
|
|
281
|
-
const
|
282
|
-
|
283
|
-
|
263
|
+
const walk = hfs.walk(
|
264
|
+
basePath,
|
265
|
+
{
|
266
|
+
async directoryFilter(entry) {
|
284
267
|
|
285
|
-
|
286
|
-
|
287
|
-
* @param {Function} filter A filter function to wrap.
|
288
|
-
* @returns {Function} A function similar to the wrapped filter that rejects the promise if an error occurs.
|
289
|
-
*/
|
290
|
-
function wrapFilter(filter) {
|
291
|
-
return (...args) => {
|
292
|
-
|
293
|
-
// No need to run the filter if an error has been thrown.
|
294
|
-
if (!promiseRejected) {
|
295
|
-
try {
|
296
|
-
return filter(...args);
|
297
|
-
} catch (error) {
|
298
|
-
promiseRejected = true;
|
299
|
-
reject(error);
|
300
|
-
}
|
268
|
+
if (!matchers.some(matcher => matcher.match(entry.path, true))) {
|
269
|
+
return false;
|
301
270
|
}
|
302
|
-
return false;
|
303
|
-
};
|
304
|
-
}
|
305
271
|
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
311
|
-
const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
|
312
|
-
|
313
|
-
return matchesPattern && !configs.isDirectoryIgnored(entry.path);
|
314
|
-
}),
|
315
|
-
entryFilter: wrapFilter(entry => {
|
316
|
-
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
317
|
-
|
318
|
-
// entries may be directories or files so filter out directories
|
319
|
-
if (entry.dirent.isDirectory()) {
|
320
|
-
return false;
|
321
|
-
}
|
322
|
-
|
323
|
-
/*
|
324
|
-
* Optimization: We need to track when patterns are left unmatched
|
325
|
-
* and so we use `unmatchedPatterns` to do that. There is a bit of
|
326
|
-
* complexity here because the same file can be matched by more than
|
327
|
-
* one pattern. So, when we start, we actually need to test every
|
328
|
-
* pattern against every file. Once we know there are no remaining
|
329
|
-
* unmatched patterns, then we can switch to just looking for the
|
330
|
-
* first matching pattern for improved speed.
|
331
|
-
*/
|
332
|
-
const matchesPattern = unmatchedPatterns.size > 0
|
333
|
-
? matchers.reduce((previousValue, matcher) => {
|
334
|
-
const pathMatches = matcher.match(relativePath);
|
335
|
-
|
336
|
-
/*
|
337
|
-
* We updated the unmatched patterns set only if the path
|
338
|
-
* matches and the file has a config. If the file has no
|
339
|
-
* config, that means there wasn't a match for the
|
340
|
-
* pattern so it should not be removed.
|
341
|
-
*
|
342
|
-
* Performance note: `getConfig()` aggressively caches
|
343
|
-
* results so there is no performance penalty for calling
|
344
|
-
* it multiple times with the same argument.
|
345
|
-
*/
|
346
|
-
if (pathMatches && configs.getConfig(entry.path)) {
|
347
|
-
unmatchedPatterns.delete(matcher.pattern);
|
348
|
-
}
|
349
|
-
|
350
|
-
return pathMatches || previousValue;
|
351
|
-
}, false)
|
352
|
-
: matchers.some(matcher => matcher.match(relativePath));
|
353
|
-
|
354
|
-
return matchesPattern && configs.getConfig(entry.path) !== void 0;
|
355
|
-
})
|
272
|
+
const absolutePath = path.resolve(basePath, entry.path);
|
273
|
+
const configs = await configLoader.loadConfigArrayForDirectory(absolutePath);
|
274
|
+
|
275
|
+
return !configs.isDirectoryIgnored(absolutePath);
|
356
276
|
},
|
357
|
-
(
|
277
|
+
async entryFilter(entry) {
|
278
|
+
const absolutePath = path.resolve(basePath, entry.path);
|
358
279
|
|
359
|
-
//
|
360
|
-
if (
|
361
|
-
|
362
|
-
} else {
|
363
|
-
resolve(entries);
|
280
|
+
// entries may be directories or files so filter out directories
|
281
|
+
if (entry.isDirectory) {
|
282
|
+
return false;
|
364
283
|
}
|
284
|
+
|
285
|
+
const configs = await configLoader.loadConfigArrayForFile(absolutePath);
|
286
|
+
const config = configs.getConfig(absolutePath);
|
287
|
+
|
288
|
+
/*
|
289
|
+
* Optimization: We need to track when patterns are left unmatched
|
290
|
+
* and so we use `unmatchedPatterns` to do that. There is a bit of
|
291
|
+
* complexity here because the same file can be matched by more than
|
292
|
+
* one pattern. So, when we start, we actually need to test every
|
293
|
+
* pattern against every file. Once we know there are no remaining
|
294
|
+
* unmatched patterns, then we can switch to just looking for the
|
295
|
+
* first matching pattern for improved speed.
|
296
|
+
*/
|
297
|
+
const matchesPattern = unmatchedPatterns.size > 0
|
298
|
+
? matchers.reduce((previousValue, matcher) => {
|
299
|
+
const pathMatches = matcher.match(entry.path);
|
300
|
+
|
301
|
+
/*
|
302
|
+
* We updated the unmatched patterns set only if the path
|
303
|
+
* matches and the file has a config. If the file has no
|
304
|
+
* config, that means there wasn't a match for the
|
305
|
+
* pattern so it should not be removed.
|
306
|
+
*
|
307
|
+
* Performance note: `getConfig()` aggressively caches
|
308
|
+
* results so there is no performance penalty for calling
|
309
|
+
* it multiple times with the same argument.
|
310
|
+
*/
|
311
|
+
if (pathMatches && config) {
|
312
|
+
unmatchedPatterns.delete(matcher.pattern);
|
313
|
+
}
|
314
|
+
|
315
|
+
return pathMatches || previousValue;
|
316
|
+
}, false)
|
317
|
+
: matchers.some(matcher => matcher.match(entry.path));
|
318
|
+
|
319
|
+
return matchesPattern && config !== void 0;
|
365
320
|
}
|
366
|
-
|
367
|
-
|
321
|
+
}
|
322
|
+
);
|
323
|
+
|
324
|
+
const filePaths = [];
|
325
|
+
|
326
|
+
if (await hfs.isDirectory(basePath)) {
|
327
|
+
for await (const entry of walk) {
|
328
|
+
filePaths.push(path.resolve(basePath, entry.path));
|
329
|
+
}
|
330
|
+
}
|
368
331
|
|
369
332
|
// now check to see if we have any unmatched patterns
|
370
333
|
if (errorOnUnmatchedPattern && unmatchedPatterns.size > 0) {
|
@@ -426,14 +389,14 @@ async function throwErrorForUnmatchedPatterns({
|
|
426
389
|
* @param {Object} options The options for this function.
|
427
390
|
* @param {Map<string,GlobSearch>} options.searches
|
428
391
|
* An array of glob patterns to match.
|
429
|
-
* @param {
|
392
|
+
* @param {ConfigLoader|LegacyConfigLoader} options.configLoader The config loader to use for
|
430
393
|
* determining what to ignore.
|
431
394
|
* @param {boolean} options.errorOnUnmatchedPattern Determines if an
|
432
395
|
* unmatched glob pattern should throw an error.
|
433
396
|
* @returns {Promise<Array<string>>} An array of matching file paths
|
434
397
|
* or an empty array if there are no matches.
|
435
398
|
*/
|
436
|
-
async function globMultiSearch({ searches,
|
399
|
+
async function globMultiSearch({ searches, configLoader, errorOnUnmatchedPattern }) {
|
437
400
|
|
438
401
|
/*
|
439
402
|
* For convenience, we normalized the search map into an array of objects.
|
@@ -452,7 +415,7 @@ async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
|
|
452
415
|
basePath,
|
453
416
|
patterns,
|
454
417
|
rawPatterns,
|
455
|
-
|
418
|
+
configLoader,
|
456
419
|
errorOnUnmatchedPattern
|
457
420
|
})
|
458
421
|
)
|
@@ -505,7 +468,7 @@ async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
|
|
505
468
|
* @param {boolean} args.globInputPaths true to interpret glob patterns,
|
506
469
|
* false to not interpret glob patterns.
|
507
470
|
* @param {string} args.cwd The current working directory to find from.
|
508
|
-
* @param {
|
471
|
+
* @param {ConfigLoader|LegacyConfigLoader} args.configLoader The config loeader for the current run.
|
509
472
|
* @param {boolean} args.errorOnUnmatchedPattern Determines if an unmatched pattern
|
510
473
|
* should throw an error.
|
511
474
|
* @returns {Promise<Array<string>>} The fully resolved file paths.
|
@@ -516,7 +479,7 @@ async function findFiles({
|
|
516
479
|
patterns,
|
517
480
|
globInputPaths,
|
518
481
|
cwd,
|
519
|
-
|
482
|
+
configLoader,
|
520
483
|
errorOnUnmatchedPattern
|
521
484
|
}) {
|
522
485
|
|
@@ -526,6 +489,42 @@ async function findFiles({
|
|
526
489
|
let rawPatterns = [];
|
527
490
|
const searches = new Map([[cwd, { patterns: globbyPatterns, rawPatterns: [] }]]);
|
528
491
|
|
492
|
+
/*
|
493
|
+
* This part is a bit involved because we need to account for
|
494
|
+
* the different ways that the patterns can match directories.
|
495
|
+
* For each different way, we need to decide if we should look
|
496
|
+
* for a config file or just use the default config. (Directories
|
497
|
+
* without a config file always use the default config.)
|
498
|
+
*
|
499
|
+
* Here are the cases:
|
500
|
+
*
|
501
|
+
* 1. A directory is passed directly (e.g., "subdir"). In this case, we
|
502
|
+
* can assume that the user intends to lint this directory and we should
|
503
|
+
* not look for a config file in the parent directory, because the only
|
504
|
+
* reason to do that would be to ignore this directory (which we already
|
505
|
+
* know we don't want to do). Instead, we use the default config until we
|
506
|
+
* get to the directory that was passed, at which point we start looking
|
507
|
+
* for config files again.
|
508
|
+
*
|
509
|
+
* 2. A dot (".") or star ("*"). In this case, we want to read
|
510
|
+
* the config file in the current directory because the user is
|
511
|
+
* explicitly asking to lint the current directory. Note that "."
|
512
|
+
* will traverse into subdirectories while "*" will not.
|
513
|
+
*
|
514
|
+
* 3. A directory is passed in the form of "subdir/subsubdir".
|
515
|
+
* In this case, we don't want to look for a config file in the
|
516
|
+
* parent directory ("subdir"). We can skip looking for a config
|
517
|
+
* file until `entry.depth` is greater than 1 because there's no
|
518
|
+
* way that the pattern can match `entry.path` yet.
|
519
|
+
*
|
520
|
+
* 4. A directory glob pattern is passed (e.g., "subd*"). We want
|
521
|
+
* this case to act like case 2 because it's unclear whether or not
|
522
|
+
* any particular directory is meant to be traversed.
|
523
|
+
*
|
524
|
+
* 5. A recursive glob pattern is passed (e.g., "**"). We want this
|
525
|
+
* case to act like case 2.
|
526
|
+
*/
|
527
|
+
|
529
528
|
// check to see if we have explicit files and directories
|
530
529
|
const filePaths = patterns.map(filePath => path.resolve(cwd, filePath));
|
531
530
|
const stats = await Promise.all(
|
@@ -549,15 +548,10 @@ async function findFiles({
|
|
549
548
|
// directories need extensions attached
|
550
549
|
if (stat.isDirectory()) {
|
551
550
|
|
552
|
-
|
553
|
-
|
554
|
-
({ patterns: globbyPatterns, rawPatterns } = searches.get(cwd));
|
555
|
-
} else {
|
556
|
-
if (!searches.has(filePath)) {
|
557
|
-
searches.set(filePath, { patterns: [], rawPatterns: [] });
|
558
|
-
}
|
559
|
-
({ patterns: globbyPatterns, rawPatterns } = searches.get(filePath));
|
551
|
+
if (!searches.has(filePath)) {
|
552
|
+
searches.set(filePath, { patterns: [], rawPatterns: [] });
|
560
553
|
}
|
554
|
+
({ patterns: globbyPatterns, rawPatterns } = searches.get(filePath));
|
561
555
|
|
562
556
|
globbyPatterns.push(`${normalizeToPosix(filePath)}/**`);
|
563
557
|
rawPatterns.push(pattern);
|
@@ -569,17 +563,17 @@ async function findFiles({
|
|
569
563
|
// save patterns for later use based on whether globs are enabled
|
570
564
|
if (globInputPaths && isGlobPattern(pattern)) {
|
571
565
|
|
566
|
+
/*
|
567
|
+
* We are grouping patterns by their glob parent. This is done to
|
568
|
+
* make it easier to determine when a config file should be loaded.
|
569
|
+
*/
|
570
|
+
|
572
571
|
const basePath = path.resolve(cwd, globParent(pattern));
|
573
572
|
|
574
|
-
|
575
|
-
|
576
|
-
({ patterns: globbyPatterns, rawPatterns } = searches.get(cwd));
|
577
|
-
} else {
|
578
|
-
if (!searches.has(basePath)) {
|
579
|
-
searches.set(basePath, { patterns: [], rawPatterns: [] });
|
580
|
-
}
|
581
|
-
({ patterns: globbyPatterns, rawPatterns } = searches.get(basePath));
|
573
|
+
if (!searches.has(basePath)) {
|
574
|
+
searches.set(basePath, { patterns: [], rawPatterns: [] });
|
582
575
|
}
|
576
|
+
({ patterns: globbyPatterns, rawPatterns } = searches.get(basePath));
|
583
577
|
|
584
578
|
globbyPatterns.push(filePath);
|
585
579
|
rawPatterns.push(pattern);
|
@@ -596,7 +590,7 @@ async function findFiles({
|
|
596
590
|
// now we are safe to do the search
|
597
591
|
const globbyResults = await globMultiSearch({
|
598
592
|
searches,
|
599
|
-
|
593
|
+
configLoader,
|
600
594
|
errorOnUnmatchedPattern
|
601
595
|
});
|
602
596
|
|