eslint 9.27.0 → 9.29.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 (37) hide show
  1. package/README.md +1 -1
  2. package/conf/ecma-version.js +1 -1
  3. package/conf/globals.js +10 -0
  4. package/lib/cli.js +20 -23
  5. package/lib/config/config-loader.js +32 -21
  6. package/lib/config/config.js +34 -11
  7. package/lib/eslint/eslint.js +18 -21
  8. package/lib/languages/js/source-code/source-code.js +104 -27
  9. package/lib/linter/apply-disable-directives.js +2 -4
  10. package/lib/linter/code-path-analysis/code-path-analyzer.js +8 -9
  11. package/lib/linter/linter.js +30 -61
  12. package/lib/linter/source-code-traverser.js +327 -0
  13. package/lib/linter/source-code-visitor.js +81 -0
  14. package/lib/options.js +7 -0
  15. package/lib/rules/class-methods-use-this.js +7 -0
  16. package/lib/rules/func-style.js +57 -7
  17. package/lib/rules/no-implicit-globals.js +31 -15
  18. package/lib/rules/no-magic-numbers.js +98 -5
  19. package/lib/rules/no-promise-executor-return.js +4 -35
  20. package/lib/rules/no-restricted-globals.js +35 -2
  21. package/lib/rules/no-restricted-properties.js +24 -10
  22. package/lib/rules/no-setter-return.js +13 -48
  23. package/lib/rules/no-shadow.js +262 -6
  24. package/lib/rules/no-unassigned-vars.js +14 -6
  25. package/lib/rules/no-use-before-define.js +99 -1
  26. package/lib/rules/no-var.js +14 -2
  27. package/lib/rules/prefer-arrow-callback.js +9 -0
  28. package/lib/rules/prefer-regex-literals.js +1 -18
  29. package/lib/services/suppressions-service.js +8 -0
  30. package/lib/services/warning-service.js +85 -0
  31. package/lib/shared/naming.js +109 -0
  32. package/lib/shared/relative-module-resolver.js +28 -0
  33. package/lib/types/index.d.ts +18 -7
  34. package/lib/types/rules.d.ts +52 -2
  35. package/package.json +12 -10
  36. package/lib/linter/node-event-generator.js +0 -256
  37. package/lib/linter/safe-emitter.js +0 -52
package/README.md CHANGED
@@ -329,7 +329,7 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
329
329
  <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>
330
330
  <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
331
331
  <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" 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" alt="American Express" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
332
- <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.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" alt="GitBook" height="32"></a> <a href="https://nolebase.ayaka.io"><img src="https://avatars.githubusercontent.com/u/11081491" alt="Neko" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
332
+ <p><a href="https://sentry.io"><img src="https://github.com/getsentry.png" alt="Sentry" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <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.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" alt="GitBook" height="32"></a> <a href="https://nolebase.ayaka.io"><img src="https://avatars.githubusercontent.com/u/11081491" alt="Neko" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
333
333
  <h3>Technology Sponsors</h3>
334
334
  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.
335
335
  <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>
@@ -9,7 +9,7 @@
9
9
  * The latest ECMAScript version supported by ESLint.
10
10
  * @type {number} year-based ECMAScript version
11
11
  */
12
- const LATEST_ECMA_VERSION = 2025;
12
+ const LATEST_ECMA_VERSION = 2026;
13
13
 
14
14
  module.exports = {
15
15
  LATEST_ECMA_VERSION,
package/conf/globals.js CHANGED
@@ -135,6 +135,15 @@ const es2024 = {
135
135
 
136
136
  const es2025 = {
137
137
  ...es2024,
138
+ Float16Array: false,
139
+ Iterator: false,
140
+ };
141
+
142
+ const es2026 = {
143
+ ...es2025,
144
+ AsyncDisposableStack: false,
145
+ DisposableStack: false,
146
+ SuppressedError: false,
138
147
  };
139
148
 
140
149
  //-----------------------------------------------------------------------------
@@ -156,4 +165,5 @@ module.exports = {
156
165
  es2023,
157
166
  es2024,
158
167
  es2025,
168
+ es2026,
159
169
  };
package/lib/cli.js CHANGED
@@ -28,13 +28,14 @@ const fs = require("node:fs"),
28
28
  log = require("./shared/logging"),
29
29
  RuntimeInfo = require("./shared/runtime-info"),
30
30
  { normalizeSeverityToString } = require("./shared/severity");
31
- const {
32
- Legacy: { naming },
33
- } = require("@eslint/eslintrc");
34
31
  const { ModuleImporter } = require("@humanwhocodes/module-importer");
35
32
  const { getCacheFile } = require("./eslint/eslint-helpers");
36
33
  const { SuppressionsService } = require("./services/suppressions-service");
37
34
  const debug = require("debug")("eslint:cli");
35
+ const {
36
+ normalizePackageName,
37
+ getShorthandName,
38
+ } = require("./shared/naming.js");
38
39
 
39
40
  //------------------------------------------------------------------------------
40
41
  // Types
@@ -67,10 +68,7 @@ async function loadPlugins(importer, pluginNames) {
67
68
 
68
69
  await Promise.all(
69
70
  pluginNames.map(async pluginName => {
70
- const longName = naming.normalizePackageName(
71
- pluginName,
72
- "eslint-plugin",
73
- );
71
+ const longName = normalizePackageName(pluginName, "eslint-plugin");
74
72
  const module = await importer.import(longName);
75
73
 
76
74
  if (!("default" in module)) {
@@ -79,10 +77,7 @@ async function loadPlugins(importer, pluginNames) {
79
77
  );
80
78
  }
81
79
 
82
- const shortName = naming.getShorthandName(
83
- pluginName,
84
- "eslint-plugin",
85
- );
80
+ const shortName = getShorthandName(pluginName, "eslint-plugin");
86
81
 
87
82
  plugins[shortName] = module.default;
88
83
  }),
@@ -439,10 +434,8 @@ const cli = {
439
434
  debug("Using flat config?", usingFlatConfig);
440
435
 
441
436
  if (allowFlatConfig && !usingFlatConfig) {
442
- process.emitWarning(
443
- "You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.",
444
- "ESLintRCWarning",
445
- );
437
+ const { WarningService } = require("./services/warning-service");
438
+ new WarningService().emitESLintRCWarning();
446
439
  }
447
440
 
448
441
  const CLIOptions = createCLIOptions(usingFlatConfig);
@@ -736,17 +729,21 @@ const cli = {
736
729
  );
737
730
  }
738
731
 
739
- const unusedSuppressionsCount =
740
- Object.keys(unusedSuppressions).length;
732
+ if (!options.passOnUnprunedSuppressions) {
733
+ const unusedSuppressionsCount =
734
+ Object.keys(unusedSuppressions).length;
741
735
 
742
- if (unusedSuppressionsCount > 0) {
743
- log.error(
744
- "There are suppressions left that do not occur anymore. Consider re-running the command with `--prune-suppressions`.",
745
- );
746
- debug(JSON.stringify(unusedSuppressions, null, 2));
736
+ if (unusedSuppressionsCount > 0) {
737
+ log.error(
738
+ "There are suppressions left that do not occur anymore. Consider re-running the command with `--prune-suppressions`.",
739
+ );
740
+ debug(JSON.stringify(unusedSuppressions, null, 2));
741
+
742
+ return 2;
743
+ }
747
744
  }
748
745
 
749
- if (shouldExitForFatalErrors || unusedSuppressionsCount > 0) {
746
+ if (shouldExitForFatalErrors) {
750
747
  return 2;
751
748
  }
752
749
 
@@ -15,6 +15,7 @@ const findUp = require("find-up");
15
15
  const { pathToFileURL } = require("node:url");
16
16
  const debug = require("debug")("eslint:config-loader");
17
17
  const { FlatConfigArray } = require("./flat-config-array");
18
+ const { WarningService } = require("../services/warning-service");
18
19
 
19
20
  //-----------------------------------------------------------------------------
20
21
  // Types
@@ -32,6 +33,7 @@ const { FlatConfigArray } = require("./flat-config-array");
32
33
  * @property {Array<string>} [ignorePatterns] The ignore patterns to use.
33
34
  * @property {Config|Array<Config>} [overrideConfig] The override config to use.
34
35
  * @property {boolean} [hasUnstableNativeNodeJsTSConfigFlag] The flag to indicate whether the `unstable_native_nodejs_ts_config` flag is enabled.
36
+ * @property {WarningService} [warningService] The warning service to use.
35
37
  */
36
38
 
37
39
  //------------------------------------------------------------------------------
@@ -137,12 +139,13 @@ function isNativeTypeScriptSupportEnabled() {
137
139
  * @since 9.24.0
138
140
  */
139
141
  async function loadTypeScriptConfigFileWithJiti(filePath, fileURL, mtime) {
140
- // eslint-disable-next-line no-use-before-define -- `ConfigLoader.loadJiti` can be overwritten for testing
141
- const { createJiti } = await ConfigLoader.loadJiti().catch(() => {
142
- throw new Error(
143
- "The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.",
144
- );
145
- });
142
+ const { createJiti, version: jitiVersion } =
143
+ // eslint-disable-next-line no-use-before-define -- `ConfigLoader.loadJiti` can be overwritten for testing
144
+ await ConfigLoader.loadJiti().catch(() => {
145
+ throw new Error(
146
+ "The 'jiti' library is required for loading TypeScript configuration files. Make sure to install it.",
147
+ );
148
+ });
146
149
 
147
150
  // `createJiti` was added in jiti v2.
148
151
  if (typeof createJiti !== "function") {
@@ -155,11 +158,15 @@ async function loadTypeScriptConfigFileWithJiti(filePath, fileURL, mtime) {
155
158
  * Disabling `moduleCache` allows us to reload a
156
159
  * config file when the last modified timestamp changes.
157
160
  */
158
-
159
- const jiti = createJiti(__filename, {
161
+ const jitiOptions = {
160
162
  moduleCache: false,
161
- interopDefault: false,
162
- });
163
+ };
164
+
165
+ if (jitiVersion.startsWith("2.1.")) {
166
+ jitiOptions.interopDefault = false;
167
+ }
168
+
169
+ const jiti = createJiti(__filename, jitiOptions);
163
170
  const config = await jiti.import(fileURL.href);
164
171
 
165
172
  importedConfigFileModificationTime.set(filePath, mtime);
@@ -301,7 +308,9 @@ class ConfigLoader {
301
308
  * @param {ConfigLoaderOptions} options The options to use when loading configuration files.
302
309
  */
303
310
  constructor(options) {
304
- this.#options = options;
311
+ this.#options = options.warningService
312
+ ? options
313
+ : { ...options, warningService: new WarningService() };
305
314
  }
306
315
 
307
316
  /**
@@ -494,11 +503,12 @@ class ConfigLoader {
494
503
 
495
504
  /**
496
505
  * Used to import the jiti dependency. This method is exposed internally for testing purposes.
497
- * @returns {Promise<Record<string, unknown>>} A promise that fulfills with a module object
498
- * or rejects with an error if jiti is not found.
506
+ * @returns {Promise<{createJiti: Function|undefined, version: string;}>} A promise that fulfills with an object containing the jiti module's createJiti function and version.
499
507
  */
500
- static loadJiti() {
501
- return import("jiti");
508
+ static async loadJiti() {
509
+ const { createJiti } = await import("jiti");
510
+ const version = require("jiti/package.json").version;
511
+ return { createJiti, version };
502
512
  }
503
513
 
504
514
  /**
@@ -561,6 +571,7 @@ class ConfigLoader {
561
571
  overrideConfig,
562
572
  hasUnstableNativeNodeJsTSConfigFlag = false,
563
573
  defaultConfigs = [],
574
+ warningService,
564
575
  } = options;
565
576
 
566
577
  debug(
@@ -622,10 +633,7 @@ class ConfigLoader {
622
633
  }
623
634
 
624
635
  if (emptyConfig) {
625
- globalThis.process?.emitWarning?.(
626
- `Running ESLint with an empty config (from ${configFilePath}). Please double-check that this is what you want. If you want to run ESLint with an empty config, export [{}] to remove this warning.`,
627
- "ESLintEmptyConfigWarning",
628
- );
636
+ warningService.emitEmptyConfigWarning(configFilePath);
629
637
  }
630
638
  }
631
639
 
@@ -713,8 +721,11 @@ class LegacyConfigLoader extends ConfigLoader {
713
721
  * @param {ConfigLoaderOptions} options The options to use when loading configuration files.
714
722
  */
715
723
  constructor(options) {
716
- super(options);
717
- this.#options = options;
724
+ const normalizedOptions = options.warningService
725
+ ? options
726
+ : { ...options, warningService: new WarningService() };
727
+ super(normalizedOptions);
728
+ this.#options = normalizedOptions;
718
729
  }
719
730
 
720
731
  /**
@@ -265,6 +265,27 @@ function getObjectId(object) {
265
265
  return name;
266
266
  }
267
267
 
268
+ /**
269
+ * Asserts that a value is not a function.
270
+ * @param {any} value The value to check.
271
+ * @param {string} key The key of the value in the object.
272
+ * @param {string} objectKey The key of the object being checked.
273
+ * @returns {void}
274
+ * @throws {TypeError} If the value is a function.
275
+ */
276
+ function assertNotFunction(value, key, objectKey) {
277
+ if (typeof value === "function") {
278
+ const error = new TypeError(
279
+ `Cannot serialize key "${key}" in "${objectKey}": Function values are not supported.`,
280
+ );
281
+
282
+ error.messageTemplate = "config-serialize-function";
283
+ error.messageData = { key, objectKey };
284
+
285
+ throw error;
286
+ }
287
+ }
288
+
268
289
  /**
269
290
  * Converts a languageOptions object to a JSON representation.
270
291
  * @param {Record<string, any>} languageOptions The options to create a JSON
@@ -274,6 +295,14 @@ function getObjectId(object) {
274
295
  * @throws {TypeError} If a function is found in the languageOptions.
275
296
  */
276
297
  function languageOptionsToJSON(languageOptions, objectKey = "languageOptions") {
298
+ if (typeof languageOptions.toJSON === "function") {
299
+ const result = languageOptions.toJSON();
300
+
301
+ assertNotFunction(result, "toJSON", objectKey);
302
+
303
+ return result;
304
+ }
305
+
277
306
  const result = {};
278
307
 
279
308
  for (const [key, value] of Object.entries(languageOptions)) {
@@ -281,7 +310,10 @@ function languageOptionsToJSON(languageOptions, objectKey = "languageOptions") {
281
310
  if (typeof value === "object") {
282
311
  const name = getObjectId(value);
283
312
 
284
- if (name && hasMethod(value)) {
313
+ if (typeof value.toJSON === "function") {
314
+ result[key] = value.toJSON();
315
+ assertNotFunction(result[key], key, objectKey);
316
+ } else if (name && hasMethod(value)) {
285
317
  result[key] = name;
286
318
  } else {
287
319
  result[key] = languageOptionsToJSON(value, key);
@@ -289,16 +321,7 @@ function languageOptionsToJSON(languageOptions, objectKey = "languageOptions") {
289
321
  continue;
290
322
  }
291
323
 
292
- if (typeof value === "function") {
293
- const error = new TypeError(
294
- `Cannot serialize key "${key}" in ${objectKey}: Function values are not supported.`,
295
- );
296
-
297
- error.messageTemplate = "config-serialize-function";
298
- error.messageData = { key, objectKey };
299
-
300
- throw error;
301
- }
324
+ assertNotFunction(value, key, objectKey);
302
325
  }
303
326
 
304
327
  result[key] = value;
@@ -15,13 +15,6 @@ const path = require("node:path");
15
15
  const { version } = require("../../package.json");
16
16
  const { Linter } = require("../linter");
17
17
  const { defaultConfig } = require("../config/default-config");
18
- const {
19
- Legacy: {
20
- ConfigOps: { getRuleSeverity },
21
- ModuleResolver,
22
- naming,
23
- },
24
- } = require("@eslint/eslintrc");
25
18
 
26
19
  const {
27
20
  findFiles,
@@ -40,6 +33,14 @@ const { pathToFileURL } = require("node:url");
40
33
  const LintResultCache = require("../cli-engine/lint-result-cache");
41
34
  const { Retrier } = require("@humanwhocodes/retry");
42
35
  const { ConfigLoader, LegacyConfigLoader } = require("../config/config-loader");
36
+ const { WarningService } = require("../services/warning-service");
37
+ const { Config } = require("../config/config.js");
38
+ const {
39
+ getShorthandName,
40
+ getNamespaceFromTerm,
41
+ normalizePackageName,
42
+ } = require("../shared/naming.js");
43
+ const { resolve } = require("../shared/relative-module-resolver.js");
43
44
 
44
45
  /*
45
46
  * This is necessary to allow overwriting writeFile for testing purposes.
@@ -159,7 +160,7 @@ function getOrFindUsedDeprecatedRules(eslint, maybeFilePath) {
159
160
 
160
161
  if (config.rules) {
161
162
  for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
162
- if (getRuleSeverity(ruleConf) === 0) {
163
+ if (Config.getRuleNumericSeverity(ruleConf) === 0) {
163
164
  continue;
164
165
  }
165
166
  const rule = config.getRuleDefinition(ruleId);
@@ -173,7 +174,7 @@ function getOrFindUsedDeprecatedRules(eslint, maybeFilePath) {
173
174
  replacedBy: usesNewFormat
174
175
  ? (meta.deprecated.replacedBy?.map(
175
176
  replacement =>
176
- `${replacement.plugin?.name !== void 0 ? `${naming.getShorthandName(replacement.plugin.name, "eslint-plugin")}/` : ""}${replacement.rule?.name ?? ""}`,
177
+ `${replacement.plugin?.name !== void 0 ? `${getShorthandName(replacement.plugin.name, "eslint-plugin")}/` : ""}${replacement.rule?.name ?? ""}`,
177
178
  ) ?? [])
178
179
  : meta.replacedBy || [],
179
180
  info: usesNewFormat ? meta.deprecated : void 0,
@@ -431,10 +432,12 @@ class ESLint {
431
432
  constructor(options = {}) {
432
433
  const defaultConfigs = [];
433
434
  const processedOptions = processOptions(options);
435
+ const warningService = new WarningService();
434
436
  const linter = new Linter({
435
437
  cwd: processedOptions.cwd,
436
438
  configType: "flat",
437
439
  flags: mergeEnvironmentFlags(processedOptions.flags),
440
+ warningService,
438
441
  });
439
442
 
440
443
  const cacheFilePath = getCacheFile(
@@ -457,6 +460,7 @@ class ESLint {
457
460
  hasUnstableNativeNodeJsTSConfigFlag: linter.hasFlag(
458
461
  "unstable_native_nodejs_ts_config",
459
462
  ),
463
+ warningService,
460
464
  };
461
465
 
462
466
  this.#configLoader = linter.hasFlag("unstable_config_lookup_from_file")
@@ -485,8 +489,7 @@ class ESLint {
485
489
  for (const [pluginName, plugin] of Object.entries(
486
490
  options.plugins,
487
491
  )) {
488
- plugins[naming.getShorthandName(pluginName, "eslint-plugin")] =
489
- plugin;
492
+ plugins[getShorthandName(pluginName, "eslint-plugin")] = plugin;
490
493
  }
491
494
 
492
495
  defaultConfigs.push({
@@ -496,10 +499,7 @@ class ESLint {
496
499
 
497
500
  // Check for the .eslintignore file, and warn if it's present.
498
501
  if (existsSync(path.resolve(processedOptions.cwd, ".eslintignore"))) {
499
- process.emitWarning(
500
- 'The ".eslintignore" file is no longer supported. Switch to using the "ignores" property in "eslint.config.js": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files',
501
- "ESLintIgnoreWarning",
502
- );
502
+ warningService.emitESLintIgnoreWarning();
503
503
  }
504
504
  }
505
505
 
@@ -997,7 +997,7 @@ class ESLint {
997
997
 
998
998
  // replace \ with / for Windows compatibility
999
999
  const normalizedFormatName = name.replace(/\\/gu, "/");
1000
- const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
1000
+ const namespace = getNamespaceFromTerm(normalizedFormatName);
1001
1001
 
1002
1002
  // grab our options
1003
1003
  const { cwd } = privateMembers.get(this).options;
@@ -1009,16 +1009,13 @@ class ESLint {
1009
1009
  formatterPath = path.resolve(cwd, normalizedFormatName);
1010
1010
  } else {
1011
1011
  try {
1012
- const npmFormat = naming.normalizePackageName(
1012
+ const npmFormat = normalizePackageName(
1013
1013
  normalizedFormatName,
1014
1014
  "eslint-formatter",
1015
1015
  );
1016
1016
 
1017
1017
  // TODO: This is pretty dirty...would be nice to clean up at some point.
1018
- formatterPath = ModuleResolver.resolve(
1019
- npmFormat,
1020
- getPlaceholderPath(cwd),
1021
- );
1018
+ formatterPath = resolve(npmFormat, getPlaceholderPath(cwd));
1022
1019
  } catch {
1023
1020
  formatterPath = path.resolve(
1024
1021
  __dirname,
@@ -15,7 +15,6 @@ const { isCommentToken } = require("@eslint-community/eslint-utils"),
15
15
  globals = require("../../../../conf/globals"),
16
16
  { directivesPattern } = require("../../../shared/directives"),
17
17
  CodePathAnalyzer = require("../../../linter/code-path-analysis/code-path-analyzer"),
18
- createEmitter = require("../../../linter/safe-emitter"),
19
18
  {
20
19
  ConfigCommentParser,
21
20
  VisitNodeStep,
@@ -40,16 +39,6 @@ const { isCommentToken } = require("@eslint-community/eslint-utils"),
40
39
 
41
40
  const commentParser = new ConfigCommentParser();
42
41
 
43
- const CODE_PATH_EVENTS = [
44
- "onCodePathStart",
45
- "onCodePathEnd",
46
- "onCodePathSegmentStart",
47
- "onCodePathSegmentEnd",
48
- "onCodePathSegmentLoop",
49
- "onUnreachableCodePathSegmentStart",
50
- "onUnreachableCodePathSegmentEnd",
51
- ];
52
-
53
42
  /**
54
43
  * Validates that the given AST has the required information.
55
44
  * @param {ASTNode} ast The Program node of the AST to check.
@@ -240,6 +229,33 @@ function isSpaceBetween(sourceCode, first, second, checkInsideOfJSXText) {
240
229
  return false;
241
230
  }
242
231
 
232
+ /**
233
+ * Performs binary search to find the line number containing a given character index.
234
+ * Returns the lower bound - the index of the first element greater than the target.
235
+ * **Please note that the `lineStartIndices` should be sorted in ascending order**.
236
+ * - Time Complexity: O(log n) - Significantly faster than linear search for large files.
237
+ * @param {number[]} lineStartIndices Sorted array of line start indices.
238
+ * @param {number} target The character index to find the line number for.
239
+ * @returns {number} The 1-based line number for the target index.
240
+ * @private
241
+ */
242
+ function findLineNumberBinarySearch(lineStartIndices, target) {
243
+ let low = 0;
244
+ let high = lineStartIndices.length;
245
+
246
+ while (low < high) {
247
+ const mid = ((low + high) / 2) | 0; // Use bitwise OR to floor the division
248
+
249
+ if (target < lineStartIndices[mid]) {
250
+ high = mid;
251
+ } else {
252
+ low = mid + 1;
253
+ }
254
+ }
255
+
256
+ return low;
257
+ }
258
+
243
259
  //-----------------------------------------------------------------------------
244
260
  // Directive Comments
245
261
  //-----------------------------------------------------------------------------
@@ -316,6 +332,36 @@ function addDeclaredGlobals(
316
332
 
317
333
  return true;
318
334
  });
335
+
336
+ /*
337
+ * "implicit" contains information about implicit global variables (those created
338
+ * implicitly by assigning values to undeclared variables in non-strict code).
339
+ * Since we augment the global scope using configuration, we need to remove
340
+ * the ones that were added by configuration, as they are either built-in
341
+ * or declared elsewhere, therefore not implicit.
342
+ * Since the "implicit" property was not documented, first we'll check if it exists
343
+ * because it's possible that not all custom scope managers create this property.
344
+ * If it exists, we assume it has properties `variables` and `set`. Property
345
+ * `left` is considered optional (for example, typescript-eslint's scope manage
346
+ * has this property named `leftToBeResolved`).
347
+ */
348
+ const { implicit } = globalScope;
349
+ if (typeof implicit === "object" && implicit !== null) {
350
+ implicit.variables = implicit.variables.filter(variable => {
351
+ const name = variable.name;
352
+ if (globalScope.set.has(name)) {
353
+ implicit.set.delete(name);
354
+ return false;
355
+ }
356
+ return true;
357
+ });
358
+
359
+ if (implicit.left) {
360
+ implicit.left = implicit.left.filter(
361
+ reference => !globalScope.set.has(reference.identifier.name),
362
+ );
363
+ }
364
+ }
319
365
  }
320
366
 
321
367
  /**
@@ -392,6 +438,7 @@ class SourceCode extends TokenStore {
392
438
  ["scopes", new WeakMap()],
393
439
  ["vars", new Map()],
394
440
  ["configNodes", void 0],
441
+ ["isGlobalReference", new WeakMap()],
395
442
  ]);
396
443
 
397
444
  /**
@@ -690,9 +737,9 @@ class SourceCode extends TokenStore {
690
737
 
691
738
  /**
692
739
  * Converts a source text index into a (line, column) pair.
693
- * @param {number} index The index of a character in a file
740
+ * @param {number} index The index of a character in a file.
694
741
  * @throws {TypeError|RangeError} If non-numeric index or index out of range.
695
- * @returns {{line: number, column: number}} A {line, column} location object with a 0-indexed column
742
+ * @returns {{line: number, column: number}} A {line, column} location object with 1-indexed line and 0-indexed column.
696
743
  * @public
697
744
  */
698
745
  getLocFromIndex(index) {
@@ -727,7 +774,7 @@ class SourceCode extends TokenStore {
727
774
  const lineNumber =
728
775
  index >= this.lineStartIndices.at(-1)
729
776
  ? this.lineStartIndices.length
730
- : this.lineStartIndices.findIndex(el => index < el);
777
+ : findLineNumberBinarySearch(this.lineStartIndices, index);
731
778
 
732
779
  return {
733
780
  line: lineNumber,
@@ -872,6 +919,41 @@ class SourceCode extends TokenStore {
872
919
  return ancestorsStartingAtParent.reverse();
873
920
  }
874
921
 
922
+ /**
923
+ * Determines whether the given identifier node is a reference to a global variable.
924
+ * @param {ASTNode} node `Identifier` node to check.
925
+ * @returns {boolean} True if the identifier is a reference to a global variable.
926
+ */
927
+ isGlobalReference(node) {
928
+ if (!node) {
929
+ throw new TypeError("Missing required argument: node.");
930
+ }
931
+
932
+ const cache = this[caches].get("isGlobalReference");
933
+
934
+ if (cache.has(node)) {
935
+ return cache.get(node);
936
+ }
937
+
938
+ if (node.type !== "Identifier") {
939
+ cache.set(node, false);
940
+ return false;
941
+ }
942
+
943
+ const variable = this.scopeManager.scopes[0].set.get(node.name);
944
+
945
+ if (!variable || variable.defs.length > 0) {
946
+ cache.set(node, false);
947
+ return false;
948
+ }
949
+
950
+ const result = variable.references.some(
951
+ ({ identifier }) => identifier === node,
952
+ );
953
+ cache.set(node, result);
954
+ return result;
955
+ }
956
+
875
957
  /**
876
958
  * Returns the location of the given node or token.
877
959
  * @param {ASTNode|Token} nodeOrToken The node or token to get the location of.
@@ -1209,7 +1291,6 @@ class SourceCode extends TokenStore {
1209
1291
  * custom parsers to return any AST, we need to ensure that the traversal
1210
1292
  * logic works for any AST.
1211
1293
  */
1212
- const emitter = createEmitter();
1213
1294
  let analyzer = {
1214
1295
  enterNode(node) {
1215
1296
  steps.push(
@@ -1229,7 +1310,14 @@ class SourceCode extends TokenStore {
1229
1310
  }),
1230
1311
  );
1231
1312
  },
1232
- emitter,
1313
+ emit(eventName, args) {
1314
+ steps.push(
1315
+ new CallMethodStep({
1316
+ target: eventName,
1317
+ args,
1318
+ }),
1319
+ );
1320
+ },
1233
1321
  };
1234
1322
 
1235
1323
  /*
@@ -1243,17 +1331,6 @@ class SourceCode extends TokenStore {
1243
1331
  */
1244
1332
  if (this.isESTree) {
1245
1333
  analyzer = new CodePathAnalyzer(analyzer);
1246
-
1247
- CODE_PATH_EVENTS.forEach(eventName => {
1248
- emitter.on(eventName, (...args) => {
1249
- steps.push(
1250
- new CallMethodStep({
1251
- target: eventName,
1252
- args,
1253
- }),
1254
- );
1255
- });
1256
- });
1257
1334
  }
1258
1335
 
1259
1336
  /*
@@ -19,9 +19,7 @@
19
19
  //------------------------------------------------------------------------------
20
20
 
21
21
  const escapeRegExp = require("escape-string-regexp");
22
- const {
23
- Legacy: { ConfigOps },
24
- } = require("@eslint/eslintrc/universal");
22
+ const { Config } = require("../config/config.js");
25
23
 
26
24
  /**
27
25
  * Compares the locations of two objects in a source file
@@ -539,7 +537,7 @@ module.exports = ({
539
537
  configuredRules && ruleFilter
540
538
  ? new Set(
541
539
  Object.keys(configuredRules).filter(ruleId => {
542
- const severity = ConfigOps.getRuleSeverity(
540
+ const severity = Config.getRuleNumericSeverity(
543
541
  configuredRules[ruleId],
544
542
  );
545
543