eslint 8.22.0 → 8.33.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.
Files changed (80) hide show
  1. package/README.md +51 -45
  2. package/bin/eslint.js +2 -4
  3. package/conf/globals.js +6 -1
  4. package/conf/rule-type-list.json +2 -2
  5. package/lib/cli-engine/file-enumerator.js +4 -2
  6. package/lib/cli-engine/formatters/formatters-meta.json +46 -0
  7. package/lib/cli-engine/formatters/html.js +76 -51
  8. package/lib/cli.js +163 -40
  9. package/lib/config/default-config.js +2 -2
  10. package/lib/config/flat-config-array.js +1 -1
  11. package/lib/eslint/eslint-helpers.js +409 -87
  12. package/lib/eslint/eslint.js +5 -2
  13. package/lib/eslint/flat-eslint.js +113 -110
  14. package/lib/linter/code-path-analysis/code-path-segment.js +2 -2
  15. package/lib/linter/code-path-analysis/code-path-state.js +7 -7
  16. package/lib/linter/code-path-analysis/debug-helpers.js +3 -3
  17. package/lib/linter/code-path-analysis/id-generator.js +2 -2
  18. package/lib/linter/config-comment-parser.js +1 -2
  19. package/lib/linter/linter.js +17 -7
  20. package/lib/linter/timing.js +4 -4
  21. package/lib/options.js +293 -239
  22. package/lib/rule-tester/flat-rule-tester.js +13 -11
  23. package/lib/rule-tester/rule-tester.js +15 -11
  24. package/lib/rules/array-callback-return.js +2 -2
  25. package/lib/rules/comma-dangle.js +3 -3
  26. package/lib/rules/for-direction.js +1 -1
  27. package/lib/rules/func-name-matching.js +2 -2
  28. package/lib/rules/getter-return.js +14 -8
  29. package/lib/rules/global-require.js +2 -1
  30. package/lib/rules/id-length.js +43 -2
  31. package/lib/rules/indent-legacy.js +4 -4
  32. package/lib/rules/indent.js +23 -15
  33. package/lib/rules/index.js +3 -0
  34. package/lib/rules/key-spacing.js +50 -38
  35. package/lib/rules/lines-around-comment.js +2 -2
  36. package/lib/rules/logical-assignment-operators.js +474 -0
  37. package/lib/rules/multiline-ternary.js +2 -2
  38. package/lib/rules/new-cap.js +2 -2
  39. package/lib/rules/no-else-return.js +1 -1
  40. package/lib/rules/no-empty-static-block.js +47 -0
  41. package/lib/rules/no-empty.js +19 -2
  42. package/lib/rules/no-extra-boolean-cast.js +1 -1
  43. package/lib/rules/no-extra-parens.js +18 -3
  44. package/lib/rules/no-fallthrough.js +26 -5
  45. package/lib/rules/no-implicit-coercion.js +20 -1
  46. package/lib/rules/no-implicit-globals.js +5 -0
  47. package/lib/rules/no-invalid-regexp.js +40 -18
  48. package/lib/rules/no-labels.js +1 -1
  49. package/lib/rules/no-lone-blocks.js +1 -1
  50. package/lib/rules/no-loss-of-precision.js +2 -2
  51. package/lib/rules/no-magic-numbers.js +18 -1
  52. package/lib/rules/no-misleading-character-class.js +4 -4
  53. package/lib/rules/no-new-native-nonconstructor.js +64 -0
  54. package/lib/rules/no-obj-calls.js +1 -1
  55. package/lib/rules/no-restricted-exports.js +106 -10
  56. package/lib/rules/no-return-await.js +28 -1
  57. package/lib/rules/no-underscore-dangle.js +36 -11
  58. package/lib/rules/no-unneeded-ternary.js +1 -1
  59. package/lib/rules/no-use-before-define.js +1 -1
  60. package/lib/rules/no-useless-computed-key.js +1 -1
  61. package/lib/rules/no-var.js +2 -2
  62. package/lib/rules/no-warning-comments.js +24 -5
  63. package/lib/rules/padded-blocks.js +1 -1
  64. package/lib/rules/prefer-arrow-callback.js +4 -3
  65. package/lib/rules/prefer-const.js +13 -1
  66. package/lib/rules/prefer-named-capture-group.js +71 -6
  67. package/lib/rules/prefer-object-spread.js +1 -1
  68. package/lib/rules/prefer-regex-literals.js +147 -32
  69. package/lib/rules/prefer-rest-params.js +1 -1
  70. package/lib/rules/require-yield.js +0 -1
  71. package/lib/rules/strict.js +1 -1
  72. package/lib/rules/utils/ast-utils.js +10 -4
  73. package/lib/shared/directives.js +15 -0
  74. package/lib/shared/logging.js +1 -1
  75. package/lib/shared/runtime-info.js +1 -1
  76. package/lib/shared/traverser.js +1 -1
  77. package/lib/shared/types.js +15 -2
  78. package/lib/source-code/token-store/cursor.js +1 -1
  79. package/messages/print-config-with-directory-path.js +1 -1
  80. package/package.json +27 -27
@@ -16,7 +16,6 @@ const findUp = require("find-up");
16
16
  const { version } = require("../../package.json");
17
17
  const { Linter } = require("../linter");
18
18
  const { getRuleFromConfig } = require("../config/flat-config-helpers");
19
- const { gitignoreToMinimatch } = require("@humanwhocodes/gitignore-to-minimatch");
20
19
  const {
21
20
  Legacy: {
22
21
  ConfigOps: {
@@ -28,7 +27,6 @@ const {
28
27
  } = require("@eslint/eslintrc");
29
28
 
30
29
  const {
31
- fileExists,
32
30
  findFiles,
33
31
  getCacheFile,
34
32
 
@@ -59,6 +57,7 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
59
57
  /** @typedef {import("../shared/types").LintMessage} LintMessage */
60
58
  /** @typedef {import("../shared/types").ParserOptions} ParserOptions */
61
59
  /** @typedef {import("../shared/types").Plugin} Plugin */
60
+ /** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
62
61
  /** @typedef {import("../shared/types").RuleConf} RuleConf */
63
62
  /** @typedef {import("../shared/types").Rule} Rule */
64
63
  /** @typedef {ReturnType<ConfigArray.extractConfig>} ExtractedConfig */
@@ -73,13 +72,11 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
73
72
  * @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
74
73
  * @property {string} [cwd] The value to use for the current working directory.
75
74
  * @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
76
- * @property {string[]} [extensions] An array of file extensions to check.
77
75
  * @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
78
76
  * @property {string[]} [fixTypes] Array of rule types to apply fixes for.
79
77
  * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
80
- * @property {boolean} [ignore] False disables use of .eslintignore.
81
- * @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
82
- * @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to .eslintignore.
78
+ * @property {boolean} [ignore] False disables all ignore patterns except for the default ones.
79
+ * @property {string[]} [ignorePatterns] Ignore file patterns to use in addition to config ignores.
83
80
  * @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
84
81
  * @property {boolean|string} [overrideConfigFile] Searches for default config file when falsy;
85
82
  * doesn't do any config file lookup when `true`; considered to be a config filename
@@ -96,6 +93,7 @@ const FLAT_CONFIG_FILENAME = "eslint.config.js";
96
93
  const debug = require("debug")("eslint:flat-eslint");
97
94
  const removedFormatters = new Set(["table", "codeframe"]);
98
95
  const privateMembers = new WeakMap();
96
+ const importedConfigFileModificationTime = new Map();
99
97
 
100
98
  /**
101
99
  * It will calculate the error and warning count for collection of messages per file
@@ -152,30 +150,6 @@ function calculateStatsPerRun(results) {
152
150
  });
153
151
  }
154
152
 
155
- /**
156
- * Loads global ignore patterns from an ignore file (usually .eslintignore).
157
- * @param {string} filePath The filename to load.
158
- * @returns {ignore} A function encapsulating the ignore patterns.
159
- * @throws {Error} If the file cannot be read.
160
- * @private
161
- */
162
- async function loadIgnoreFilePatterns(filePath) {
163
- debug(`Loading ignore file: ${filePath}`);
164
-
165
- try {
166
- const ignoreFileText = await fs.readFile(filePath, { encoding: "utf8" });
167
-
168
- return ignoreFileText
169
- .split(/\r?\n/gu)
170
- .filter(line => line.trim() !== "" && !line.startsWith("#"));
171
-
172
- } catch (e) {
173
- debug(`Error reading ignore file: ${filePath}`);
174
- e.message = `Cannot read ignore file: ${filePath}\nError: ${e.message}`;
175
- throw e;
176
- }
177
- }
178
-
179
153
  /**
180
154
  * Create rulesMeta object.
181
155
  * @param {Map<string,Rule>} rules a map of rules from which to generate the object.
@@ -188,6 +162,16 @@ function createRulesMeta(rules) {
188
162
  }, {});
189
163
  }
190
164
 
165
+ /**
166
+ * Return the absolute path of a file named `"__placeholder__.js"` in a given directory.
167
+ * This is used as a replacement for a missing file path.
168
+ * @param {string} cwd An absolute directory path.
169
+ * @returns {string} The absolute path of a file named `"__placeholder__.js"` in the given directory.
170
+ */
171
+ function getPlaceholderPath(cwd) {
172
+ return path.join(cwd, "__placeholder__.js");
173
+ }
174
+
191
175
  /** @type {WeakMap<ExtractedConfig, DeprecatedRuleInfo[]>} */
192
176
  const usedDeprecatedRulesCache = new WeakMap();
193
177
 
@@ -204,7 +188,7 @@ function getOrFindUsedDeprecatedRules(eslint, maybeFilePath) {
204
188
  } = privateMembers.get(eslint);
205
189
  const filePath = path.isAbsolute(maybeFilePath)
206
190
  ? maybeFilePath
207
- : path.join(cwd, "__placeholder__.js");
191
+ : getPlaceholderPath(cwd);
208
192
  const config = configs.getConfig(filePath);
209
193
 
210
194
  // Most files use the same config, so cache it.
@@ -289,24 +273,51 @@ function findFlatConfigFile(cwd) {
289
273
  /**
290
274
  * Load the config array from the given filename.
291
275
  * @param {string} filePath The filename to load from.
292
- * @param {Object} options Options to help load the config file.
293
- * @param {string} options.basePath The base path for the config array.
294
- * @param {boolean} options.shouldIgnore Whether to honor ignore patterns.
295
- * @returns {Promise<FlatConfigArray>} The config array loaded from the config file.
276
+ * @returns {Promise<any>} The config loaded from the config file.
296
277
  */
297
- async function loadFlatConfigFile(filePath, { basePath, shouldIgnore }) {
278
+ async function loadFlatConfigFile(filePath) {
298
279
  debug(`Loading config from ${filePath}`);
299
280
 
300
281
  const fileURL = pathToFileURL(filePath);
301
282
 
302
283
  debug(`Config file URL is ${fileURL}`);
303
284
 
304
- const module = await import(fileURL);
285
+ const mtime = (await fs.stat(filePath)).mtime.getTime();
305
286
 
306
- return new FlatConfigArray(module.default, {
307
- basePath,
308
- shouldIgnore
309
- });
287
+ /*
288
+ * Append a query with the config file's modification time (`mtime`) in order
289
+ * to import the current version of the config file. Without the query, `import()` would
290
+ * cache the config file module by the pathname only, and then always return
291
+ * the same version (the one that was actual when the module was imported for the first time).
292
+ *
293
+ * This ensures that the config file module is loaded and executed again
294
+ * if it has been changed since the last time it was imported.
295
+ * If it hasn't been changed, `import()` will just return the cached version.
296
+ *
297
+ * Note that we should not overuse queries (e.g., by appending the current time
298
+ * to always reload the config file module) as that could cause memory leaks
299
+ * because entries are never removed from the import cache.
300
+ */
301
+ fileURL.searchParams.append("mtime", mtime);
302
+
303
+ /*
304
+ * With queries, we can bypass the import cache. However, when import-ing a CJS module,
305
+ * Node.js uses the require infrastructure under the hood. That includes the require cache,
306
+ * which caches the config file module by its file path (queries have no effect).
307
+ * Therefore, we also need to clear the require cache before importing the config file module.
308
+ * In order to get the same behavior with ESM and CJS config files, in particular - to reload
309
+ * the config file only if it has been changed, we track file modification times and clear
310
+ * the require cache only if the file has been changed.
311
+ */
312
+ if (importedConfigFileModificationTime.get(filePath) !== mtime) {
313
+ delete require.cache[filePath];
314
+ }
315
+
316
+ const config = (await import(fileURL)).default;
317
+
318
+ importedConfigFileModificationTime.set(filePath, mtime);
319
+
320
+ return config;
310
321
  }
311
322
 
312
323
  /**
@@ -317,12 +328,11 @@ async function loadFlatConfigFile(filePath, { basePath, shouldIgnore }) {
317
328
  */
318
329
  async function calculateConfigArray(eslint, {
319
330
  cwd,
331
+ baseConfig,
320
332
  overrideConfig,
321
333
  configFile,
322
334
  ignore: shouldIgnore,
323
- ignorePath,
324
- ignorePatterns,
325
- extensions
335
+ ignorePatterns
326
336
  }) {
327
337
 
328
338
  // check for cached instance
@@ -350,45 +360,24 @@ async function calculateConfigArray(eslint, {
350
360
  basePath = path.resolve(path.dirname(configFilePath));
351
361
  }
352
362
 
353
- // load config array
354
- let configs;
355
363
 
364
+ const configs = new FlatConfigArray(baseConfig || [], { basePath, shouldIgnore });
365
+
366
+ // load config file
356
367
  if (configFilePath) {
357
- configs = await loadFlatConfigFile(configFilePath, {
358
- basePath,
359
- shouldIgnore
360
- });
361
- } else {
362
- configs = new FlatConfigArray([], { basePath, shouldIgnore });
368
+ const fileConfig = await loadFlatConfigFile(configFilePath);
369
+
370
+ if (Array.isArray(fileConfig)) {
371
+ configs.push(...fileConfig);
372
+ } else {
373
+ configs.push(fileConfig);
374
+ }
363
375
  }
364
376
 
365
377
  // add in any configured defaults
366
378
  configs.push(...slots.defaultConfigs);
367
379
 
368
- // if there are any extensions, create configs for them for easier matching
369
- if (extensions && extensions.length) {
370
- configs.push({
371
- files: extensions.map(ext => `**/*${ext}`)
372
- });
373
- }
374
-
375
380
  let allIgnorePatterns = [];
376
- let ignoreFilePath;
377
-
378
- // load ignore file if necessary
379
- if (shouldIgnore) {
380
- if (ignorePath) {
381
- ignoreFilePath = path.resolve(cwd, ignorePath);
382
- allIgnorePatterns = await loadIgnoreFilePatterns(ignoreFilePath);
383
- } else {
384
- ignoreFilePath = path.resolve(cwd, ".eslintignore");
385
-
386
- // no error if .eslintignore doesn't exist`
387
- if (fileExists(ignoreFilePath)) {
388
- allIgnorePatterns = await loadIgnoreFilePatterns(ignoreFilePath);
389
- }
390
- }
391
- }
392
381
 
393
382
  // append command line ignore patterns
394
383
  if (ignorePatterns) {
@@ -414,17 +403,6 @@ async function calculateConfigArray(eslint, {
414
403
  const negated = pattern.startsWith("!");
415
404
  const basePattern = negated ? pattern.slice(1) : pattern;
416
405
 
417
- /*
418
- * Ignore patterns are considered relative to a directory
419
- * when the pattern contains a slash in a position other
420
- * than the last character. If that's the case, we need to
421
- * add the relative ignore path to the current pattern to
422
- * get the correct behavior. Otherwise, no change is needed.
423
- */
424
- if (!basePattern.includes("/") || basePattern.endsWith("/")) {
425
- return pattern;
426
- }
427
-
428
406
  return (negated ? "!" : "") +
429
407
  path.posix.join(relativeIgnorePath, basePattern);
430
408
  });
@@ -437,7 +415,7 @@ async function calculateConfigArray(eslint, {
437
415
  * so they can override default ignores.
438
416
  */
439
417
  configs.push({
440
- ignores: allIgnorePatterns.map(gitignoreToMinimatch)
418
+ ignores: allIgnorePatterns
441
419
  });
442
420
  }
443
421
 
@@ -490,7 +468,7 @@ function verifyText({
490
468
  * `config.extractConfig(filePath)` requires an absolute path, but `linter`
491
469
  * doesn't know CWD, so it gives `linter` an absolute path always.
492
470
  */
493
- const filePathToVerify = filePath === "<text>" ? path.join(cwd, "__placeholder__.js") : filePath;
471
+ const filePathToVerify = filePath === "<text>" ? getPlaceholderPath(cwd) : filePath;
494
472
  const { fixed, messages, output } = linter.verifyAndFix(
495
473
  text,
496
474
  configs,
@@ -515,6 +493,7 @@ function verifyText({
515
493
  const result = {
516
494
  filePath: filePath === "<text>" ? filePath : path.resolve(filePath),
517
495
  messages,
496
+ suppressedMessages: linter.getSuppressedMessages(),
518
497
  ...calculateStatsPerFile(messages)
519
498
  };
520
499
 
@@ -586,6 +565,14 @@ function *iterateRuleDeprecationWarnings(configs) {
586
565
  }
587
566
  }
588
567
 
568
+ /**
569
+ * Creates an error to be thrown when an array of results passed to `getRulesMetaForResults` was not created by the current engine.
570
+ * @returns {TypeError} An error object.
571
+ */
572
+ function createExtraneousResultsError() {
573
+ return new TypeError("Results object was not created from this ESLint instance.");
574
+ }
575
+
589
576
  //-----------------------------------------------------------------------------
590
577
  // Main API
591
578
  //-----------------------------------------------------------------------------
@@ -689,11 +676,13 @@ class FlatESLint {
689
676
 
690
677
  results.forEach(result => {
691
678
  const filteredMessages = result.messages.filter(isErrorMessage);
679
+ const filteredSuppressedMessages = result.suppressedMessages.filter(isErrorMessage);
692
680
 
693
681
  if (filteredMessages.length > 0) {
694
682
  filtered.push({
695
683
  ...result,
696
684
  messages: filteredMessages,
685
+ suppressedMessages: filteredSuppressedMessages,
697
686
  errorCount: filteredMessages.length,
698
687
  warningCount: 0,
699
688
  fixableErrorCount: result.fixableErrorCount,
@@ -714,14 +703,16 @@ class FlatESLint {
714
703
  */
715
704
  getRulesMetaForResults(results) {
716
705
 
717
- const resultRules = new Map();
718
-
719
706
  // short-circuit simple case
720
707
  if (results.length === 0) {
721
- return resultRules;
708
+ return {};
722
709
  }
723
710
 
724
- const { configs } = privateMembers.get(this);
711
+ const resultRules = new Map();
712
+ const {
713
+ configs,
714
+ options: { cwd }
715
+ } = privateMembers.get(this);
725
716
 
726
717
  /*
727
718
  * We can only accurately return rules meta information for linting results if the
@@ -730,7 +721,7 @@ class FlatESLint {
730
721
  * to let the user know we can't do anything here.
731
722
  */
732
723
  if (!configs) {
733
- throw new TypeError("Results object was not created from this ESLint instance.");
724
+ throw createExtraneousResultsError();
734
725
  }
735
726
 
736
727
  for (const result of results) {
@@ -739,18 +730,26 @@ class FlatESLint {
739
730
  * Normalize filename for <text>.
740
731
  */
741
732
  const filePath = result.filePath === "<text>"
742
- ? "__placeholder__.js" : result.filePath;
733
+ ? getPlaceholderPath(cwd) : result.filePath;
734
+ const allMessages = result.messages.concat(result.suppressedMessages);
743
735
 
744
- /*
745
- * All of the plugin and rule information is contained within the
746
- * calculated config for the given file.
747
- */
748
- const config = configs.getConfig(filePath);
736
+ for (const { ruleId } of allMessages) {
737
+ if (!ruleId) {
738
+ continue;
739
+ }
749
740
 
750
- for (const { ruleId } of result.messages) {
741
+ /*
742
+ * All of the plugin and rule information is contained within the
743
+ * calculated config for the given file.
744
+ */
745
+ const config = configs.getConfig(filePath);
746
+
747
+ if (!config) {
748
+ throw createExtraneousResultsError();
749
+ }
751
750
  const rule = getRuleFromConfig(ruleId, config);
752
751
 
753
- // ensure the rule exists exists
752
+ // ensure the rule exists
754
753
  if (!rule) {
755
754
  throw new TypeError(`Could not find the rule "${ruleId}".`);
756
755
  }
@@ -786,8 +785,8 @@ class FlatESLint {
786
785
  fix,
787
786
  fixTypes,
788
787
  reportUnusedDisableDirectives,
789
- extensions,
790
- globInputPaths
788
+ globInputPaths,
789
+ errorOnUnmatchedPattern
791
790
  } = eslintOptions;
792
791
  const startTime = Date.now();
793
792
  const usedConfigs = [];
@@ -812,9 +811,9 @@ class FlatESLint {
812
811
  const filePaths = await findFiles({
813
812
  patterns: typeof patterns === "string" ? [patterns] : patterns,
814
813
  cwd,
815
- extensions,
816
814
  globInputPaths,
817
- configs
815
+ configs,
816
+ errorOnUnmatchedPattern
818
817
  });
819
818
 
820
819
  debug(`${filePaths.length} files found in: ${Date.now() - startTime}ms`);
@@ -877,7 +876,7 @@ class FlatESLint {
877
876
  }
878
877
 
879
878
 
880
- // set up fixer for fixtypes if necessary
879
+ // set up fixer for fixTypes if necessary
881
880
  let fixer = fix;
882
881
 
883
882
  if (fix && fixTypesSet) {
@@ -1053,7 +1052,7 @@ class FlatESLint {
1053
1052
  * The following values are allowed:
1054
1053
  * - `undefined` ... Load `stylish` builtin formatter.
1055
1054
  * - A builtin formatter name ... Load the builtin formatter.
1056
- * - A thirdparty formatter name:
1055
+ * - A third-party formatter name:
1057
1056
  * - `foo` → `eslint-formatter-foo`
1058
1057
  * - `@foo` → `@foo/eslint-formatter`
1059
1058
  * - `@foo/bar` → `@foo/eslint-formatter-bar`
@@ -1085,7 +1084,7 @@ class FlatESLint {
1085
1084
  const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
1086
1085
 
1087
1086
  // TODO: This is pretty dirty...would be nice to clean up at some point.
1088
- formatterPath = ModuleResolver.resolve(npmFormat, path.join(cwd, "__placeholder__.js"));
1087
+ formatterPath = ModuleResolver.resolve(npmFormat, getPlaceholderPath(cwd));
1089
1088
  } catch {
1090
1089
  formatterPath = path.resolve(__dirname, "../", "cli-engine", "formatters", `${normalizedFormatName}.js`);
1091
1090
  }
@@ -1119,14 +1118,17 @@ class FlatESLint {
1119
1118
  /**
1120
1119
  * The main formatter method.
1121
1120
  * @param {LintResults[]} results The lint results to format.
1121
+ * @param {ResultsMeta} resultsMeta Warning count and max threshold.
1122
1122
  * @returns {string} The formatted lint results.
1123
1123
  */
1124
- format(results) {
1124
+ format(results, resultsMeta) {
1125
1125
  let rulesMeta = null;
1126
1126
 
1127
1127
  results.sort(compareResultsByFilePath);
1128
1128
 
1129
1129
  return formatter(results, {
1130
+ ...resultsMeta,
1131
+ cwd,
1130
1132
  get rulesMeta() {
1131
1133
  if (!rulesMeta) {
1132
1134
  rulesMeta = eslint.getRulesMetaForResults(results);
@@ -1175,5 +1177,6 @@ class FlatESLint {
1175
1177
  //------------------------------------------------------------------------------
1176
1178
 
1177
1179
  module.exports = {
1178
- FlatESLint
1180
+ FlatESLint,
1181
+ findFlatConfigFile
1179
1182
  };
@@ -88,10 +88,10 @@ class CodePathSegment {
88
88
  }
89
89
  });
90
90
 
91
- /* istanbul ignore if */
91
+ /* c8 ignore start */
92
92
  if (debug.enabled) {
93
93
  this.internal.nodes = [];
94
- }
94
+ }/* c8 ignore stop */
95
95
  }
96
96
 
97
97
  /**
@@ -59,7 +59,7 @@ function getContinueContext(state, label) {
59
59
  context = context.upper;
60
60
  }
61
61
 
62
- /* istanbul ignore next: foolproof (syntax error) */
62
+ /* c8 ignore next */
63
63
  return null;
64
64
  }
65
65
 
@@ -79,7 +79,7 @@ function getBreakContext(state, label) {
79
79
  context = context.upper;
80
80
  }
81
81
 
82
- /* istanbul ignore next: foolproof (syntax error) */
82
+ /* c8 ignore next */
83
83
  return null;
84
84
  }
85
85
 
@@ -433,7 +433,7 @@ class CodePathState {
433
433
  */
434
434
  return context;
435
435
 
436
- /* istanbul ignore next */
436
+ /* c8 ignore next */
437
437
  default:
438
438
  throw new Error("unreachable");
439
439
  }
@@ -1030,7 +1030,7 @@ class CodePathState {
1030
1030
  };
1031
1031
  break;
1032
1032
 
1033
- /* istanbul ignore next */
1033
+ /* c8 ignore next */
1034
1034
  default:
1035
1035
  throw new Error(`unknown type: "${type}"`);
1036
1036
  }
@@ -1095,7 +1095,7 @@ class CodePathState {
1095
1095
  );
1096
1096
  break;
1097
1097
 
1098
- /* istanbul ignore next */
1098
+ /* c8 ignore next */
1099
1099
  default:
1100
1100
  throw new Error("unreachable");
1101
1101
  }
@@ -1392,11 +1392,12 @@ class CodePathState {
1392
1392
 
1393
1393
  const context = getBreakContext(this, label);
1394
1394
 
1395
- /* istanbul ignore else: foolproof (syntax error) */
1395
+
1396
1396
  if (context) {
1397
1397
  context.brokenForkContext.add(forkContext.head);
1398
1398
  }
1399
1399
 
1400
+ /* c8 ignore next */
1400
1401
  forkContext.replaceHead(forkContext.makeUnreachable(-1, -1));
1401
1402
  }
1402
1403
 
@@ -1417,7 +1418,6 @@ class CodePathState {
1417
1418
 
1418
1419
  const context = getContinueContext(this, label);
1419
1420
 
1420
- /* istanbul ignore else: foolproof (syntax error) */
1421
1421
  if (context) {
1422
1422
  if (context.continueDestSegments) {
1423
1423
  makeLooped(this, forkContext.head, context.continueDestSegments);
@@ -20,7 +20,7 @@ const debug = require("debug")("eslint:code-path");
20
20
  * @param {CodePathSegment} segment A segment to get.
21
21
  * @returns {string} Id of the segment.
22
22
  */
23
- /* istanbul ignore next */
23
+ /* c8 ignore next */
24
24
  function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc -- Ignoring
25
25
  return segment.id + (segment.reachable ? "" : "!");
26
26
  }
@@ -67,7 +67,7 @@ module.exports = {
67
67
  * @param {boolean} leaving A flag whether or not it's leaving
68
68
  * @returns {void}
69
69
  */
70
- dumpState: !debug.enabled ? debug : /* istanbul ignore next */ function(node, state, leaving) {
70
+ dumpState: !debug.enabled ? debug : /* c8 ignore next */ function(node, state, leaving) {
71
71
  for (let i = 0; i < state.currentSegments.length; ++i) {
72
72
  const segInternal = state.currentSegments[i].internal;
73
73
 
@@ -98,7 +98,7 @@ module.exports = {
98
98
  * @see http://www.graphviz.org
99
99
  * @see http://www.webgraphviz.com
100
100
  */
101
- dumpDot: !debug.enabled ? debug : /* istanbul ignore next */ function(codePath) {
101
+ dumpDot: !debug.enabled ? debug : /* c8 ignore next */ function(codePath) {
102
102
  let text =
103
103
  "\n" +
104
104
  "digraph {\n" +
@@ -33,10 +33,10 @@ class IdGenerator {
33
33
  next() {
34
34
  this.n = 1 + this.n | 0;
35
35
 
36
- /* istanbul ignore if */
36
+ /* c8 ignore start */
37
37
  if (this.n < 0) {
38
38
  this.n = 1;
39
- }
39
+ }/* c8 ignore stop */
40
40
 
41
41
  return this.prefix + this.n;
42
42
  }
@@ -131,8 +131,7 @@ module.exports = class ConfigCommentParser {
131
131
 
132
132
  const items = {};
133
133
 
134
- // Collapse whitespace around commas
135
- string.replace(/\s*,\s*/gu, ",").split(/,+/u).forEach(name => {
134
+ string.split(",").forEach(name => {
136
135
  const trimmedName = name.trim();
137
136
 
138
137
  if (trimmedName) {
@@ -18,6 +18,9 @@ const
18
18
  merge = require("lodash.merge"),
19
19
  pkg = require("../../package.json"),
20
20
  astUtils = require("../shared/ast-utils"),
21
+ {
22
+ directivesPattern
23
+ } = require("../shared/directives"),
21
24
  {
22
25
  Legacy: {
23
26
  ConfigOps,
@@ -213,6 +216,7 @@ function addDeclaredGlobals(globalScope, configGlobals, { exportedVariables, ena
213
216
 
214
217
  if (variable) {
215
218
  variable.eslintUsed = true;
219
+ variable.eslintExported = true;
216
220
  }
217
221
  });
218
222
 
@@ -376,7 +380,7 @@ function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
376
380
  ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
377
381
  const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
378
382
 
379
- const match = /^(eslint(?:-env|-enable|-disable(?:(?:-next)?-line)?)?|exported|globals?)(?:\s|$)/u.exec(directivePart);
383
+ const match = directivesPattern.exec(directivePart);
380
384
 
381
385
  if (!match) {
382
386
  return;
@@ -1601,12 +1605,18 @@ class Linter {
1601
1605
  languageOptions.ecmaVersion
1602
1606
  );
1603
1607
 
1604
- // add configured globals and language globals
1605
- const configuredGlobals = {
1606
- ...(getGlobalsForEcmaVersion(languageOptions.ecmaVersion)),
1607
- ...(languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0),
1608
- ...languageOptions.globals
1609
- };
1608
+ /*
1609
+ * add configured globals and language globals
1610
+ *
1611
+ * using Object.assign instead of object spread for performance reasons
1612
+ * https://github.com/eslint/eslint/issues/16302
1613
+ */
1614
+ const configuredGlobals = Object.assign(
1615
+ {},
1616
+ getGlobalsForEcmaVersion(languageOptions.ecmaVersion),
1617
+ languageOptions.sourceType === "commonjs" ? globals.commonjs : void 0,
1618
+ languageOptions.globals
1619
+ );
1610
1620
 
1611
1621
  // double check that there is a parser to avoid mysterious error messages
1612
1622
  if (!languageOptions.parser) {
@@ -9,7 +9,7 @@
9
9
  // Helpers
10
10
  //------------------------------------------------------------------------------
11
11
 
12
- /* istanbul ignore next */
12
+ /* c8 ignore next */
13
13
  /**
14
14
  * Align the string to left
15
15
  * @param {string} str string to evaluate
@@ -22,7 +22,7 @@ function alignLeft(str, len, ch) {
22
22
  return str + new Array(len - str.length + 1).join(ch || " ");
23
23
  }
24
24
 
25
- /* istanbul ignore next */
25
+ /* c8 ignore next */
26
26
  /**
27
27
  * Align the string to right
28
28
  * @param {string} str string to evaluate
@@ -64,7 +64,7 @@ function getListSize() {
64
64
  return TIMING_ENV_VAR_AS_INTEGER > 10 ? TIMING_ENV_VAR_AS_INTEGER : MINIMUM_SIZE;
65
65
  }
66
66
 
67
- /* istanbul ignore next */
67
+ /* c8 ignore next */
68
68
  /**
69
69
  * display the data
70
70
  * @param {Object} data Data object to be displayed
@@ -119,7 +119,7 @@ function display(data) {
119
119
  console.log(table.join("\n")); // eslint-disable-line no-console -- Debugging function
120
120
  }
121
121
 
122
- /* istanbul ignore next */
122
+ /* c8 ignore next */
123
123
  module.exports = (function() {
124
124
 
125
125
  const data = Object.create(null);