eslint 9.28.0 → 9.30.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 (107) 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 +6 -11
  5. package/lib/config/config-loader.js +2 -29
  6. package/lib/config/flat-config-array.js +1 -1
  7. package/lib/eslint/eslint.js +14 -18
  8. package/lib/languages/js/source-code/source-code.js +81 -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 +4 -4
  12. package/lib/linter/source-code-traverser.js +64 -49
  13. package/lib/linter/source-code-visitor.js +81 -0
  14. package/lib/rules/array-bracket-newline.js +3 -3
  15. package/lib/rules/array-bracket-spacing.js +3 -3
  16. package/lib/rules/array-element-newline.js +3 -3
  17. package/lib/rules/arrow-parens.js +3 -3
  18. package/lib/rules/arrow-spacing.js +3 -3
  19. package/lib/rules/block-spacing.js +3 -3
  20. package/lib/rules/brace-style.js +3 -3
  21. package/lib/rules/class-methods-use-this.js +7 -0
  22. package/lib/rules/comma-dangle.js +3 -3
  23. package/lib/rules/comma-spacing.js +3 -3
  24. package/lib/rules/comma-style.js +3 -3
  25. package/lib/rules/computed-property-spacing.js +3 -3
  26. package/lib/rules/dot-location.js +3 -3
  27. package/lib/rules/eol-last.js +3 -3
  28. package/lib/rules/func-call-spacing.js +3 -3
  29. package/lib/rules/function-call-argument-newline.js +3 -3
  30. package/lib/rules/function-paren-newline.js +3 -3
  31. package/lib/rules/generator-star-spacing.js +3 -3
  32. package/lib/rules/implicit-arrow-linebreak.js +3 -3
  33. package/lib/rules/indent-legacy.js +3 -3
  34. package/lib/rules/indent.js +3 -3
  35. package/lib/rules/jsx-quotes.js +3 -3
  36. package/lib/rules/key-spacing.js +3 -3
  37. package/lib/rules/keyword-spacing.js +3 -3
  38. package/lib/rules/line-comment-position.js +3 -3
  39. package/lib/rules/linebreak-style.js +3 -3
  40. package/lib/rules/lines-around-comment.js +3 -3
  41. package/lib/rules/lines-around-directive.js +3 -3
  42. package/lib/rules/lines-between-class-members.js +3 -3
  43. package/lib/rules/max-len.js +3 -3
  44. package/lib/rules/max-statements-per-line.js +3 -3
  45. package/lib/rules/multiline-comment-style.js +3 -3
  46. package/lib/rules/multiline-ternary.js +3 -3
  47. package/lib/rules/new-parens.js +3 -3
  48. package/lib/rules/newline-after-var.js +3 -3
  49. package/lib/rules/newline-before-return.js +3 -3
  50. package/lib/rules/newline-per-chained-call.js +3 -3
  51. package/lib/rules/no-confusing-arrow.js +3 -3
  52. package/lib/rules/no-duplicate-imports.js +65 -7
  53. package/lib/rules/no-extra-parens.js +3 -3
  54. package/lib/rules/no-extra-semi.js +3 -3
  55. package/lib/rules/no-floating-decimal.js +3 -3
  56. package/lib/rules/no-mixed-operators.js +3 -3
  57. package/lib/rules/no-mixed-spaces-and-tabs.js +3 -3
  58. package/lib/rules/no-multi-spaces.js +3 -3
  59. package/lib/rules/no-multiple-empty-lines.js +3 -3
  60. package/lib/rules/no-promise-executor-return.js +4 -35
  61. package/lib/rules/no-restricted-globals.js +35 -2
  62. package/lib/rules/no-restricted-properties.js +35 -12
  63. package/lib/rules/no-setter-return.js +13 -48
  64. package/lib/rules/no-spaced-func.js +3 -3
  65. package/lib/rules/no-tabs.js +3 -3
  66. package/lib/rules/no-trailing-spaces.js +3 -3
  67. package/lib/rules/no-unused-vars.js +1 -1
  68. package/lib/rules/no-use-before-define.js +2 -0
  69. package/lib/rules/no-var.js +14 -2
  70. package/lib/rules/no-whitespace-before-property.js +3 -3
  71. package/lib/rules/nonblock-statement-body-position.js +3 -3
  72. package/lib/rules/object-curly-newline.js +3 -3
  73. package/lib/rules/object-curly-spacing.js +3 -3
  74. package/lib/rules/object-property-newline.js +3 -3
  75. package/lib/rules/one-var-declaration-per-line.js +3 -3
  76. package/lib/rules/operator-linebreak.js +3 -3
  77. package/lib/rules/padded-blocks.js +3 -3
  78. package/lib/rules/padding-line-between-statements.js +3 -3
  79. package/lib/rules/prefer-regex-literals.js +1 -18
  80. package/lib/rules/quote-props.js +3 -3
  81. package/lib/rules/quotes.js +3 -3
  82. package/lib/rules/rest-spread-spacing.js +3 -3
  83. package/lib/rules/semi-spacing.js +3 -3
  84. package/lib/rules/semi-style.js +3 -3
  85. package/lib/rules/semi.js +3 -3
  86. package/lib/rules/space-before-blocks.js +3 -3
  87. package/lib/rules/space-before-function-paren.js +3 -3
  88. package/lib/rules/space-in-parens.js +3 -3
  89. package/lib/rules/space-infix-ops.js +3 -3
  90. package/lib/rules/space-unary-ops.js +3 -3
  91. package/lib/rules/spaced-comment.js +3 -3
  92. package/lib/rules/switch-colon-spacing.js +3 -3
  93. package/lib/rules/template-curly-spacing.js +3 -3
  94. package/lib/rules/template-tag-spacing.js +3 -3
  95. package/lib/rules/utils/ast-utils.js +45 -0
  96. package/lib/rules/wrap-iife.js +3 -3
  97. package/lib/rules/wrap-regex.js +3 -3
  98. package/lib/rules/yield-star-spacing.js +3 -3
  99. package/lib/services/suppressions-service.js +8 -0
  100. package/lib/shared/flags.js +9 -1
  101. package/lib/shared/naming.js +109 -0
  102. package/lib/shared/relative-module-resolver.js +28 -0
  103. package/lib/shared/runtime-info.js +1 -1
  104. package/lib/types/index.d.ts +15 -2
  105. package/lib/types/rules.d.ts +83 -74
  106. package/package.json +8 -8
  107. 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
  }),
@@ -642,40 +642,13 @@ class ConfigLoader {
642
642
 
643
643
  // append command line ignore patterns
644
644
  if (ignorePatterns && ignorePatterns.length > 0) {
645
- let relativeIgnorePatterns;
646
-
647
- /*
648
- * If the config file basePath is different than the cwd, then
649
- * the ignore patterns won't work correctly. Here, we adjust the
650
- * ignore pattern to include the correct relative path. Patterns
651
- * passed as `ignorePatterns` are relative to the cwd, whereas
652
- * the config file basePath can be an ancestor of the cwd.
653
- */
654
- if (basePath === cwd) {
655
- relativeIgnorePatterns = ignorePatterns;
656
- } else {
657
- // relative path must only have Unix-style separators
658
- const relativeIgnorePath = path
659
- .relative(basePath, cwd)
660
- .replace(/\\/gu, "/");
661
-
662
- relativeIgnorePatterns = ignorePatterns.map(pattern => {
663
- const negated = pattern.startsWith("!");
664
- const basePattern = negated ? pattern.slice(1) : pattern;
665
-
666
- return (
667
- (negated ? "!" : "") +
668
- path.posix.join(relativeIgnorePath, basePattern)
669
- );
670
- });
671
- }
672
-
673
645
  /*
674
646
  * Ignore patterns are added to the end of the config array
675
647
  * so they can override default ignores.
676
648
  */
677
649
  configs.push({
678
- ignores: relativeIgnorePatterns,
650
+ basePath: cwd,
651
+ ignores: ignorePatterns,
679
652
  });
680
653
  }
681
654
 
@@ -21,7 +21,7 @@ const { Config } = require("./config");
21
21
  /**
22
22
  * Fields that are considered metadata and not part of the config object.
23
23
  */
24
- const META_FIELDS = new Set(["name"]);
24
+ const META_FIELDS = new Set(["name", "basePath"]);
25
25
 
26
26
  /**
27
27
  * Wraps a config error with details about where the error occurred.
@@ -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,
@@ -41,6 +34,13 @@ const LintResultCache = require("../cli-engine/lint-result-cache");
41
34
  const { Retrier } = require("@humanwhocodes/retry");
42
35
  const { ConfigLoader, LegacyConfigLoader } = require("../config/config-loader");
43
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");
44
44
 
45
45
  /*
46
46
  * This is necessary to allow overwriting writeFile for testing purposes.
@@ -160,7 +160,7 @@ function getOrFindUsedDeprecatedRules(eslint, maybeFilePath) {
160
160
 
161
161
  if (config.rules) {
162
162
  for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
163
- if (getRuleSeverity(ruleConf) === 0) {
163
+ if (Config.getRuleNumericSeverity(ruleConf) === 0) {
164
164
  continue;
165
165
  }
166
166
  const rule = config.getRuleDefinition(ruleId);
@@ -174,7 +174,7 @@ function getOrFindUsedDeprecatedRules(eslint, maybeFilePath) {
174
174
  replacedBy: usesNewFormat
175
175
  ? (meta.deprecated.replacedBy?.map(
176
176
  replacement =>
177
- `${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 ?? ""}`,
178
178
  ) ?? [])
179
179
  : meta.replacedBy || [],
180
180
  info: usesNewFormat ? meta.deprecated : void 0,
@@ -463,7 +463,7 @@ class ESLint {
463
463
  warningService,
464
464
  };
465
465
 
466
- this.#configLoader = linter.hasFlag("unstable_config_lookup_from_file")
466
+ this.#configLoader = linter.hasFlag("v10_config_lookup_from_file")
467
467
  ? new ConfigLoader(configLoaderOptions)
468
468
  : new LegacyConfigLoader(configLoaderOptions);
469
469
 
@@ -489,8 +489,7 @@ class ESLint {
489
489
  for (const [pluginName, plugin] of Object.entries(
490
490
  options.plugins,
491
491
  )) {
492
- plugins[naming.getShorthandName(pluginName, "eslint-plugin")] =
493
- plugin;
492
+ plugins[getShorthandName(pluginName, "eslint-plugin")] = plugin;
494
493
  }
495
494
 
496
495
  defaultConfigs.push({
@@ -998,7 +997,7 @@ class ESLint {
998
997
 
999
998
  // replace \ with / for Windows compatibility
1000
999
  const normalizedFormatName = name.replace(/\\/gu, "/");
1001
- const namespace = naming.getNamespaceFromTerm(normalizedFormatName);
1000
+ const namespace = getNamespaceFromTerm(normalizedFormatName);
1002
1001
 
1003
1002
  // grab our options
1004
1003
  const { cwd } = privateMembers.get(this).options;
@@ -1010,16 +1009,13 @@ class ESLint {
1010
1009
  formatterPath = path.resolve(cwd, normalizedFormatName);
1011
1010
  } else {
1012
1011
  try {
1013
- const npmFormat = naming.normalizePackageName(
1012
+ const npmFormat = normalizePackageName(
1014
1013
  normalizedFormatName,
1015
1014
  "eslint-formatter",
1016
1015
  );
1017
1016
 
1018
1017
  // TODO: This is pretty dirty...would be nice to clean up at some point.
1019
- formatterPath = ModuleResolver.resolve(
1020
- npmFormat,
1021
- getPlaceholderPath(cwd),
1022
- );
1018
+ formatterPath = resolve(npmFormat, getPlaceholderPath(cwd));
1023
1019
  } catch {
1024
1020
  formatterPath = path.resolve(
1025
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
  //-----------------------------------------------------------------------------
@@ -422,6 +438,7 @@ class SourceCode extends TokenStore {
422
438
  ["scopes", new WeakMap()],
423
439
  ["vars", new Map()],
424
440
  ["configNodes", void 0],
441
+ ["isGlobalReference", new WeakMap()],
425
442
  ]);
426
443
 
427
444
  /**
@@ -720,9 +737,9 @@ class SourceCode extends TokenStore {
720
737
 
721
738
  /**
722
739
  * Converts a source text index into a (line, column) pair.
723
- * @param {number} index The index of a character in a file
740
+ * @param {number} index The index of a character in a file.
724
741
  * @throws {TypeError|RangeError} If non-numeric index or index out of range.
725
- * @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.
726
743
  * @public
727
744
  */
728
745
  getLocFromIndex(index) {
@@ -757,7 +774,7 @@ class SourceCode extends TokenStore {
757
774
  const lineNumber =
758
775
  index >= this.lineStartIndices.at(-1)
759
776
  ? this.lineStartIndices.length
760
- : this.lineStartIndices.findIndex(el => index < el);
777
+ : findLineNumberBinarySearch(this.lineStartIndices, index);
761
778
 
762
779
  return {
763
780
  line: lineNumber,
@@ -778,6 +795,7 @@ class SourceCode extends TokenStore {
778
795
  */
779
796
  getIndexFromLoc(loc) {
780
797
  if (
798
+ loc === null ||
781
799
  typeof loc !== "object" ||
782
800
  typeof loc.line !== "number" ||
783
801
  typeof loc.column !== "number"
@@ -799,6 +817,12 @@ class SourceCode extends TokenStore {
799
817
  );
800
818
  }
801
819
 
820
+ if (loc.column < 0) {
821
+ throw new RangeError(
822
+ `Invalid column number (column ${loc.column} requested).`,
823
+ );
824
+ }
825
+
802
826
  const lineStartIndex = this.lineStartIndices[loc.line - 1];
803
827
  const lineEndIndex =
804
828
  loc.line === this.lineStartIndices.length
@@ -902,6 +926,41 @@ class SourceCode extends TokenStore {
902
926
  return ancestorsStartingAtParent.reverse();
903
927
  }
904
928
 
929
+ /**
930
+ * Determines whether the given identifier node is a reference to a global variable.
931
+ * @param {ASTNode} node `Identifier` node to check.
932
+ * @returns {boolean} True if the identifier is a reference to a global variable.
933
+ */
934
+ isGlobalReference(node) {
935
+ if (!node) {
936
+ throw new TypeError("Missing required argument: node.");
937
+ }
938
+
939
+ const cache = this[caches].get("isGlobalReference");
940
+
941
+ if (cache.has(node)) {
942
+ return cache.get(node);
943
+ }
944
+
945
+ if (node.type !== "Identifier") {
946
+ cache.set(node, false);
947
+ return false;
948
+ }
949
+
950
+ const variable = this.scopeManager.scopes[0].set.get(node.name);
951
+
952
+ if (!variable || variable.defs.length > 0) {
953
+ cache.set(node, false);
954
+ return false;
955
+ }
956
+
957
+ const result = variable.references.some(
958
+ ({ identifier }) => identifier === node,
959
+ );
960
+ cache.set(node, result);
961
+ return result;
962
+ }
963
+
905
964
  /**
906
965
  * Returns the location of the given node or token.
907
966
  * @param {ASTNode|Token} nodeOrToken The node or token to get the location of.
@@ -1239,7 +1298,6 @@ class SourceCode extends TokenStore {
1239
1298
  * custom parsers to return any AST, we need to ensure that the traversal
1240
1299
  * logic works for any AST.
1241
1300
  */
1242
- const emitter = createEmitter();
1243
1301
  let analyzer = {
1244
1302
  enterNode(node) {
1245
1303
  steps.push(
@@ -1259,7 +1317,14 @@ class SourceCode extends TokenStore {
1259
1317
  }),
1260
1318
  );
1261
1319
  },
1262
- emitter,
1320
+ emit(eventName, args) {
1321
+ steps.push(
1322
+ new CallMethodStep({
1323
+ target: eventName,
1324
+ args,
1325
+ }),
1326
+ );
1327
+ },
1263
1328
  };
1264
1329
 
1265
1330
  /*
@@ -1273,17 +1338,6 @@ class SourceCode extends TokenStore {
1273
1338
  */
1274
1339
  if (this.isESTree) {
1275
1340
  analyzer = new CodePathAnalyzer(analyzer);
1276
-
1277
- CODE_PATH_EVENTS.forEach(eventName => {
1278
- emitter.on(eventName, (...args) => {
1279
- steps.push(
1280
- new CallMethodStep({
1281
- target: eventName,
1282
- args,
1283
- }),
1284
- );
1285
- });
1286
- });
1287
1341
  }
1288
1342
 
1289
1343
  /*
@@ -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
 
@@ -196,7 +196,7 @@ function forwardCurrentToHead(analyzer, node) {
196
196
 
197
197
  debug.dump(`${eventName} ${currentSegment.id}`);
198
198
 
199
- analyzer.emitter.emit(eventName, currentSegment, node);
199
+ analyzer.emit(eventName, [currentSegment, node]);
200
200
  }
201
201
  }
202
202
 
@@ -215,7 +215,7 @@ function forwardCurrentToHead(analyzer, node) {
215
215
 
216
216
  debug.dump(`${eventName} ${headSegment.id}`);
217
217
  CodePathSegment.markUsed(headSegment);
218
- analyzer.emitter.emit(eventName, headSegment, node);
218
+ analyzer.emit(eventName, [headSegment, node]);
219
219
  }
220
220
  }
221
221
  }
@@ -239,7 +239,7 @@ function leaveFromCurrentSegment(analyzer, node) {
239
239
 
240
240
  debug.dump(`${eventName} ${currentSegment.id}`);
241
241
 
242
- analyzer.emitter.emit(eventName, currentSegment, node);
242
+ analyzer.emit(eventName, [currentSegment, node]);
243
243
  }
244
244
 
245
245
  state.currentSegments = [];
@@ -416,7 +416,7 @@ function processCodePathToEnter(analyzer, node) {
416
416
 
417
417
  // Emits onCodePathStart events.
418
418
  debug.dump(`onCodePathStart ${codePath.id}`);
419
- analyzer.emitter.emit("onCodePathStart", codePath, node);
419
+ analyzer.emit("onCodePathStart", [codePath, node]);
420
420
  }
421
421
 
422
422
  /*
@@ -681,7 +681,7 @@ function postprocess(analyzer, node) {
681
681
 
682
682
  // Emits onCodePathEnd event of this code path.
683
683
  debug.dump(`onCodePathEnd ${codePath.id}`);
684
- analyzer.emitter.emit("onCodePathEnd", codePath, node);
684
+ analyzer.emit("onCodePathEnd", [codePath, node]);
685
685
  debug.dumpDot(codePath);
686
686
 
687
687
  codePath = analyzer.codePath = analyzer.codePath.upper;
@@ -747,7 +747,7 @@ class CodePathAnalyzer {
747
747
  */
748
748
  constructor(eventGenerator) {
749
749
  this.original = eventGenerator;
750
- this.emitter = eventGenerator.emitter;
750
+ this.emit = eventGenerator.emit;
751
751
  this.codePath = null;
752
752
  this.idGenerator = new IdGenerator("s");
753
753
  this.currentNode = null;
@@ -816,12 +816,11 @@ class CodePathAnalyzer {
816
816
  debug.dump(
817
817
  `onCodePathSegmentLoop ${fromSegment.id} -> ${toSegment.id}`,
818
818
  );
819
- this.emitter.emit(
820
- "onCodePathSegmentLoop",
819
+ this.emit("onCodePathSegmentLoop", [
821
820
  fromSegment,
822
821
  toSegment,
823
822
  this.currentNode,
824
- );
823
+ ]);
825
824
  }
826
825
  }
827
826
  }
@@ -29,8 +29,8 @@ const path = require("node:path"),
29
29
  { ConfigCommentParser } = require("@eslint/plugin-kit"),
30
30
  createReportTranslator = require("./report-translator"),
31
31
  Rules = require("./rules"),
32
- createEmitter = require("./safe-emitter"),
33
32
  SourceCodeFixer = require("./source-code-fixer"),
33
+ { SourceCodeVisitor } = require("./source-code-visitor"),
34
34
  timing = require("./timing"),
35
35
  ruleReplacements = require("../../conf/replacements.json");
36
36
  const { FlatConfigArray } = require("../config/flat-config-array");
@@ -1175,7 +1175,7 @@ function runRules(
1175
1175
  stats,
1176
1176
  slots,
1177
1177
  ) {
1178
- const emitter = createEmitter();
1178
+ const visitor = new SourceCodeVisitor();
1179
1179
 
1180
1180
  /*
1181
1181
  * Create a frozen object with the ruleContext properties and methods that are shared by all rules.
@@ -1339,13 +1339,13 @@ function runRules(
1339
1339
  ? timing.time(ruleId, ruleListeners[selector], stats)
1340
1340
  : ruleListeners[selector];
1341
1341
 
1342
- emitter.on(selector, addRuleErrorHandler(ruleListener));
1342
+ visitor.add(selector, addRuleErrorHandler(ruleListener));
1343
1343
  });
1344
1344
  });
1345
1345
 
1346
1346
  const traverser = SourceCodeTraverser.getInstance(language);
1347
1347
 
1348
- traverser.traverseSync(sourceCode, emitter, { steps });
1348
+ traverser.traverseSync(sourceCode, visitor, { steps });
1349
1349
 
1350
1350
  return lintingProblems;
1351
1351
  }