eslint 8.57.0 → 8.57.1

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.
@@ -19,6 +19,11 @@ const jsPlugin = require("@eslint/js");
19
19
  // Helpers
20
20
  //-----------------------------------------------------------------------------
21
21
 
22
+ /**
23
+ * Fields that are considered metadata and not part of the config object.
24
+ */
25
+ const META_FIELDS = new Set(["name"]);
26
+
22
27
  const ruleValidator = new RuleValidator();
23
28
 
24
29
  /**
@@ -75,7 +80,53 @@ function getObjectId(object) {
75
80
  return name;
76
81
  }
77
82
 
83
+ /**
84
+ * Wraps a config error with details about where the error occurred.
85
+ * @param {Error} error The original error.
86
+ * @param {number} originalLength The original length of the config array.
87
+ * @param {number} baseLength The length of the base config.
88
+ * @returns {TypeError} The new error with details.
89
+ */
90
+ function wrapConfigErrorWithDetails(error, originalLength, baseLength) {
91
+
92
+ let location = "user-defined";
93
+ let configIndex = error.index;
94
+
95
+ /*
96
+ * A config array is set up in this order:
97
+ * 1. Base config
98
+ * 2. Original configs
99
+ * 3. User-defined configs
100
+ * 4. CLI-defined configs
101
+ *
102
+ * So we need to adjust the index to account for the base config.
103
+ *
104
+ * - If the index is less than the base length, it's in the base config
105
+ * (as specified by `baseConfig` argument to `FlatConfigArray` constructor).
106
+ * - If the index is greater than the base length but less than the original
107
+ * length + base length, it's in the original config. The original config
108
+ * is passed to the `FlatConfigArray` constructor as the first argument.
109
+ * - Otherwise, it's in the user-defined config, which is loaded from the
110
+ * config file and merged with any command-line options.
111
+ */
112
+ if (error.index < baseLength) {
113
+ location = "base";
114
+ } else if (error.index < originalLength + baseLength) {
115
+ location = "original";
116
+ configIndex = error.index - baseLength;
117
+ } else {
118
+ configIndex = error.index - originalLength - baseLength;
119
+ }
120
+
121
+ return new TypeError(
122
+ `${error.message.slice(0, -1)} at ${location} index ${configIndex}.`,
123
+ { cause: error }
124
+ );
125
+ }
126
+
78
127
  const originalBaseConfig = Symbol("originalBaseConfig");
128
+ const originalLength = Symbol("originalLength");
129
+ const baseLength = Symbol("baseLength");
79
130
 
80
131
  //-----------------------------------------------------------------------------
81
132
  // Exports
@@ -102,12 +153,24 @@ class FlatConfigArray extends ConfigArray {
102
153
  schema: flatConfigSchema
103
154
  });
104
155
 
156
+ /**
157
+ * The original length of the array before any modifications.
158
+ * @type {number}
159
+ */
160
+ this[originalLength] = this.length;
161
+
105
162
  if (baseConfig[Symbol.iterator]) {
106
163
  this.unshift(...baseConfig);
107
164
  } else {
108
165
  this.unshift(baseConfig);
109
166
  }
110
167
 
168
+ /**
169
+ * The length of the array after applying the base config.
170
+ * @type {number}
171
+ */
172
+ this[baseLength] = this.length - this[originalLength];
173
+
111
174
  /**
112
175
  * The base config used to build the config array.
113
176
  * @type {Array<FlatConfig>}
@@ -125,6 +188,49 @@ class FlatConfigArray extends ConfigArray {
125
188
  Object.defineProperty(this, "shouldIgnore", { writable: false });
126
189
  }
127
190
 
191
+ /**
192
+ * Normalizes the array by calling the superclass method and catching/rethrowing
193
+ * any ConfigError exceptions with additional details.
194
+ * @param {any} [context] The context to use to normalize the array.
195
+ * @returns {Promise<FlatConfigArray>} A promise that resolves when the array is normalized.
196
+ */
197
+ normalize(context) {
198
+ return super.normalize(context)
199
+ .catch(error => {
200
+ if (error.name === "ConfigError") {
201
+ throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]);
202
+ }
203
+
204
+ throw error;
205
+
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Normalizes the array by calling the superclass method and catching/rethrowing
211
+ * any ConfigError exceptions with additional details.
212
+ * @param {any} [context] The context to use to normalize the array.
213
+ * @returns {FlatConfigArray} The current instance.
214
+ * @throws {TypeError} If the config is invalid.
215
+ */
216
+ normalizeSync(context) {
217
+
218
+ try {
219
+
220
+ return super.normalizeSync(context);
221
+
222
+ } catch (error) {
223
+
224
+ if (error.name === "ConfigError") {
225
+ throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]);
226
+ }
227
+
228
+ throw error;
229
+
230
+ }
231
+
232
+ }
233
+
128
234
  /* eslint-disable class-methods-use-this -- Desired as instance method */
129
235
  /**
130
236
  * Replaces a config with another config to allow us to put strings
@@ -155,15 +261,15 @@ class FlatConfigArray extends ConfigArray {
155
261
  }
156
262
 
157
263
  /*
158
- * If `shouldIgnore` is false, we remove any ignore patterns specified
159
- * in the config so long as it's not a default config and it doesn't
160
- * have a `files` entry.
264
+ * If a config object has `ignores` and no other non-meta fields, then it's an object
265
+ * for global ignores. If `shouldIgnore` is false, that object shouldn't apply,
266
+ * so we'll remove its `ignores`.
161
267
  */
162
268
  if (
163
269
  !this.shouldIgnore &&
164
270
  !this[originalBaseConfig].includes(config) &&
165
271
  config.ignores &&
166
- !config.files
272
+ Object.keys(config).filter(key => !META_FIELDS.has(key)).length === 1
167
273
  ) {
168
274
  /* eslint-disable-next-line no-unused-vars -- need to strip off other keys */
169
275
  const { ignores, ...otherKeys } = config;
@@ -15,7 +15,6 @@ 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 util = require("util");
19
18
  const fswalk = require("@nodelib/fs.walk");
20
19
  const globParent = require("glob-parent");
21
20
  const isPathInside = require("is-path-inside");
@@ -24,7 +23,6 @@ const isPathInside = require("is-path-inside");
24
23
  // Fixup references
25
24
  //-----------------------------------------------------------------------------
26
25
 
27
- const doFsWalk = util.promisify(fswalk.walk);
28
26
  const Minimatch = minimatch.Minimatch;
29
27
  const MINIMATCH_OPTIONS = { dot: true };
30
28
 
@@ -270,56 +268,92 @@ async function globSearch({
270
268
  */
271
269
  const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
272
270
 
273
- const filePaths = (await doFsWalk(basePath, {
271
+ const filePaths = (await new Promise((resolve, reject) => {
274
272
 
275
- deepFilter(entry) {
276
- const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
277
- const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
278
-
279
- return matchesPattern && !configs.isDirectoryIgnored(entry.path);
280
- },
281
- entryFilter(entry) {
282
- const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
273
+ let promiseRejected = false;
283
274
 
284
- // entries may be directories or files so filter out directories
285
- if (entry.dirent.isDirectory()) {
275
+ /**
276
+ * Wraps a boolean-returning filter function. The wrapped function will reject the promise if an error occurs.
277
+ * @param {Function} filter A filter function to wrap.
278
+ * @returns {Function} A function similar to the wrapped filter that rejects the promise if an error occurs.
279
+ */
280
+ function wrapFilter(filter) {
281
+ return (...args) => {
282
+
283
+ // No need to run the filter if an error has been thrown.
284
+ if (!promiseRejected) {
285
+ try {
286
+ return filter(...args);
287
+ } catch (error) {
288
+ promiseRejected = true;
289
+ reject(error);
290
+ }
291
+ }
286
292
  return false;
287
- }
293
+ };
294
+ }
288
295
 
289
- /*
290
- * Optimization: We need to track when patterns are left unmatched
291
- * and so we use `unmatchedPatterns` to do that. There is a bit of
292
- * complexity here because the same file can be matched by more than
293
- * one pattern. So, when we start, we actually need to test every
294
- * pattern against every file. Once we know there are no remaining
295
- * unmatched patterns, then we can switch to just looking for the
296
- * first matching pattern for improved speed.
297
- */
298
- const matchesPattern = unmatchedPatterns.size > 0
299
- ? matchers.reduce((previousValue, matcher) => {
300
- const pathMatches = matcher.match(relativePath);
296
+ fswalk.walk(
297
+ basePath,
298
+ {
299
+ deepFilter: wrapFilter(entry => {
300
+ const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
301
+ const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
302
+
303
+ return matchesPattern && !configs.isDirectoryIgnored(entry.path);
304
+ }),
305
+ entryFilter: wrapFilter(entry => {
306
+ const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
307
+
308
+ // entries may be directories or files so filter out directories
309
+ if (entry.dirent.isDirectory()) {
310
+ return false;
311
+ }
301
312
 
302
313
  /*
303
- * We updated the unmatched patterns set only if the path
304
- * matches and the file isn't ignored. If the file is
305
- * ignored, that means there wasn't a match for the
306
- * pattern so it should not be removed.
307
- *
308
- * Performance note: isFileIgnored() aggressively caches
309
- * results so there is no performance penalty for calling
310
- * it twice with the same argument.
314
+ * Optimization: We need to track when patterns are left unmatched
315
+ * and so we use `unmatchedPatterns` to do that. There is a bit of
316
+ * complexity here because the same file can be matched by more than
317
+ * one pattern. So, when we start, we actually need to test every
318
+ * pattern against every file. Once we know there are no remaining
319
+ * unmatched patterns, then we can switch to just looking for the
320
+ * first matching pattern for improved speed.
311
321
  */
312
- if (pathMatches && !configs.isFileIgnored(entry.path)) {
313
- unmatchedPatterns.delete(matcher.pattern);
314
- }
315
-
316
- return pathMatches || previousValue;
317
- }, false)
318
- : matchers.some(matcher => matcher.match(relativePath));
319
-
320
- return matchesPattern && !configs.isFileIgnored(entry.path);
321
- }
322
-
322
+ const matchesPattern = unmatchedPatterns.size > 0
323
+ ? matchers.reduce((previousValue, matcher) => {
324
+ const pathMatches = matcher.match(relativePath);
325
+
326
+ /*
327
+ * We updated the unmatched patterns set only if the path
328
+ * matches and the file isn't ignored. If the file is
329
+ * ignored, that means there wasn't a match for the
330
+ * pattern so it should not be removed.
331
+ *
332
+ * Performance note: isFileIgnored() aggressively caches
333
+ * results so there is no performance penalty for calling
334
+ * it twice with the same argument.
335
+ */
336
+ if (pathMatches && !configs.isFileIgnored(entry.path)) {
337
+ unmatchedPatterns.delete(matcher.pattern);
338
+ }
339
+
340
+ return pathMatches || previousValue;
341
+ }, false)
342
+ : matchers.some(matcher => matcher.match(relativePath));
343
+
344
+ return matchesPattern && !configs.isFileIgnored(entry.path);
345
+ })
346
+ },
347
+ (error, entries) => {
348
+
349
+ // If the promise is already rejected, calling `resolve` or `reject` will do nothing.
350
+ if (error) {
351
+ reject(error);
352
+ } else {
353
+ resolve(entries);
354
+ }
355
+ }
356
+ );
323
357
  })).map(entry => entry.path);
324
358
 
325
359
  // now check to see if we have any unmatched patterns
@@ -450,7 +484,7 @@ async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
450
484
 
451
485
  }
452
486
 
453
- return [...new Set(filePaths)];
487
+ return filePaths;
454
488
 
455
489
  }
456
490
 
@@ -499,10 +533,7 @@ async function findFiles({
499
533
 
500
534
  // files are added directly to the list
501
535
  if (stat.isFile()) {
502
- results.push({
503
- filePath,
504
- ignored: configs.isFileIgnored(filePath)
505
- });
536
+ results.push(filePath);
506
537
  }
507
538
 
508
539
  // directories need extensions attached
@@ -560,11 +591,10 @@ async function findFiles({
560
591
  });
561
592
 
562
593
  return [
563
- ...results,
564
- ...globbyResults.map(filePath => ({
565
- filePath: path.resolve(filePath),
566
- ignored: false
567
- }))
594
+ ...new Set([
595
+ ...results,
596
+ ...globbyResults.map(filePath => path.resolve(filePath))
597
+ ])
568
598
  ];
569
599
  }
570
600
 
@@ -489,7 +489,7 @@ function verifyText({
489
489
  * @returns {boolean} `true` if the linter should adopt the code block.
490
490
  */
491
491
  filterCodeBlock(blockFilename) {
492
- return configs.isExplicitMatch(blockFilename);
492
+ return configs.getConfig(blockFilename) !== void 0;
493
493
  }
494
494
  }
495
495
  );
@@ -541,6 +541,23 @@ function createExtraneousResultsError() {
541
541
  return new TypeError("Results object was not created from this ESLint instance.");
542
542
  }
543
543
 
544
+ /**
545
+ * Creates a fixer function based on the provided fix, fixTypesSet, and config.
546
+ * @param {Function|boolean} fix The original fix option.
547
+ * @param {Set<string>} fixTypesSet A set of fix types to filter messages for fixing.
548
+ * @param {FlatConfig} config The config for the file that generated the message.
549
+ * @returns {Function|boolean} The fixer function or the original fix value.
550
+ */
551
+ function getFixerForFixTypes(fix, fixTypesSet, config) {
552
+ if (!fix || !fixTypesSet) {
553
+ return fix;
554
+ }
555
+
556
+ const originalFix = (typeof fix === "function") ? fix : () => true;
557
+
558
+ return message => shouldMessageBeFixed(message, config, fixTypesSet) && originalFix(message);
559
+ }
560
+
544
561
  //-----------------------------------------------------------------------------
545
562
  // Main API
546
563
  //-----------------------------------------------------------------------------
@@ -790,13 +807,15 @@ class FlatESLint {
790
807
  */
791
808
  const results = await Promise.all(
792
809
 
793
- filePaths.map(({ filePath, ignored }) => {
810
+ filePaths.map(filePath => {
811
+
812
+ const config = configs.getConfig(filePath);
794
813
 
795
814
  /*
796
- * If a filename was entered that matches an ignore
797
- * pattern, then notify the user.
815
+ * If a filename was entered that cannot be matched
816
+ * to a config, then notify the user.
798
817
  */
799
- if (ignored) {
818
+ if (!config) {
800
819
  if (warnIgnored) {
801
820
  return createIgnoreResult(filePath, cwd);
802
821
  }
@@ -804,17 +823,6 @@ class FlatESLint {
804
823
  return void 0;
805
824
  }
806
825
 
807
- const config = configs.getConfig(filePath);
808
-
809
- /*
810
- * Sometimes a file found through a glob pattern will
811
- * be ignored. In this case, `config` will be undefined
812
- * and we just silently ignore the file.
813
- */
814
- if (!config) {
815
- return void 0;
816
- }
817
-
818
826
  // Skip if there is cached result.
819
827
  if (lintResultCache) {
820
828
  const cachedResult =
@@ -836,16 +844,7 @@ class FlatESLint {
836
844
 
837
845
 
838
846
  // set up fixer for fixTypes if necessary
839
- let fixer = fix;
840
-
841
- if (fix && fixTypesSet) {
842
-
843
- // save original value of options.fix in case it's a function
844
- const originalFix = (typeof fix === "function")
845
- ? fix : () => true;
846
-
847
- fixer = message => shouldMessageBeFixed(message, config, fixTypesSet) && originalFix(message);
848
- }
847
+ const fixer = getFixerForFixTypes(fix, fixTypesSet, config);
849
848
 
850
849
  return fs.readFile(filePath, "utf8")
851
850
  .then(text => {
@@ -942,11 +941,16 @@ class FlatESLint {
942
941
  allowInlineConfig,
943
942
  cwd,
944
943
  fix,
944
+ fixTypes,
945
945
  warnIgnored: constructorWarnIgnored
946
946
  } = eslintOptions;
947
947
  const results = [];
948
948
  const startTime = Date.now();
949
+ const fixTypesSet = fixTypes ? new Set(fixTypes) : null;
949
950
  const resolvedFilename = path.resolve(cwd, filePath || "__placeholder__.js");
951
+ const config = configs.getConfig(resolvedFilename);
952
+
953
+ const fixer = getFixerForFixTypes(fix, fixTypesSet, config);
950
954
 
951
955
  // Clear the last used config arrays.
952
956
  if (resolvedFilename && await this.isPathIgnored(resolvedFilename)) {
@@ -963,7 +967,7 @@ class FlatESLint {
963
967
  filePath: resolvedFilename.endsWith("__placeholder__.js") ? "<text>" : resolvedFilename,
964
968
  configs,
965
969
  cwd,
966
- fix,
970
+ fix: fixer,
967
971
  allowInlineConfig,
968
972
  linter
969
973
  }));
@@ -733,7 +733,7 @@ function createLanguageOptions({ globals: configuredGlobals, parser, parserOptio
733
733
  */
734
734
  function resolveGlobals(providedGlobals, enabledEnvironments) {
735
735
  return Object.assign(
736
- {},
736
+ Object.create(null),
737
737
  ...enabledEnvironments.filter(env => env.globals).map(env => env.globals),
738
738
  providedGlobals
739
739
  );
@@ -934,7 +934,7 @@ class SourceCode extends TokenStore {
934
934
  * https://github.com/eslint/eslint/issues/16302
935
935
  */
936
936
  const configGlobals = Object.assign(
937
- {},
937
+ Object.create(null), // https://github.com/eslint/eslint/issues/18363
938
938
  getGlobalsForEcmaVersion(languageOptions.ecmaVersion),
939
939
  languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0,
940
940
  languageOptions.globals
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "8.57.0",
3
+ "version": "8.57.1",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -24,7 +24,8 @@
24
24
  "lint:fix:docs:js": "node Makefile.js lintDocsJS -- fix",
25
25
  "release:generate:alpha": "node Makefile.js generatePrerelease -- alpha",
26
26
  "release:generate:beta": "node Makefile.js generatePrerelease -- beta",
27
- "release:generate:latest": "node Makefile.js generateRelease",
27
+ "release:generate:latest": "node Makefile.js generateRelease -- latest",
28
+ "release:generate:maintenance": "node Makefile.js generateRelease -- maintenance",
28
29
  "release:generate:rc": "node Makefile.js generatePrerelease -- rc",
29
30
  "release:publish": "node Makefile.js publishRelease",
30
31
  "test": "node Makefile.js test",
@@ -65,8 +66,8 @@
65
66
  "@eslint-community/eslint-utils": "^4.2.0",
66
67
  "@eslint-community/regexpp": "^4.6.1",
67
68
  "@eslint/eslintrc": "^2.1.4",
68
- "@eslint/js": "8.57.0",
69
- "@humanwhocodes/config-array": "^0.11.14",
69
+ "@eslint/js": "8.57.1",
70
+ "@humanwhocodes/config-array": "^0.13.0",
70
71
  "@humanwhocodes/module-importer": "^1.0.1",
71
72
  "@nodelib/fs.walk": "^1.2.8",
72
73
  "@ungap/structured-clone": "^1.2.0",
@@ -104,6 +105,7 @@
104
105
  "devDependencies": {
105
106
  "@babel/core": "^7.4.3",
106
107
  "@babel/preset-env": "^7.4.3",
108
+ "@sinonjs/fake-timers": "11.2.2",
107
109
  "@wdio/browser-runner": "^8.14.6",
108
110
  "@wdio/cli": "^8.14.6",
109
111
  "@wdio/concise-reporter": "^8.14.0",
@@ -124,7 +126,7 @@
124
126
  "eslint-plugin-jsdoc": "^46.2.5",
125
127
  "eslint-plugin-n": "^16.6.0",
126
128
  "eslint-plugin-unicorn": "^49.0.0",
127
- "eslint-release": "^3.2.0",
129
+ "eslint-release": "^3.3.0",
128
130
  "eslump": "^3.0.0",
129
131
  "esprima": "^4.0.1",
130
132
  "fast-glob": "^3.2.11",
@@ -159,7 +161,7 @@
159
161
  "semver": "^7.5.3",
160
162
  "shelljs": "^0.8.2",
161
163
  "sinon": "^11.0.0",
162
- "vite-plugin-commonjs": "^0.10.0",
164
+ "vite-plugin-commonjs": "0.10.1",
163
165
  "webdriverio": "^8.14.6",
164
166
  "webpack": "^5.23.0",
165
167
  "webpack-cli": "^4.5.0",