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.
- package/README.md +1 -1
- package/conf/ecma-version.js +1 -1
- package/conf/globals.js +10 -0
- package/lib/cli.js +6 -11
- package/lib/config/config-loader.js +2 -29
- package/lib/config/flat-config-array.js +1 -1
- package/lib/eslint/eslint.js +14 -18
- package/lib/languages/js/source-code/source-code.js +81 -27
- package/lib/linter/apply-disable-directives.js +2 -4
- package/lib/linter/code-path-analysis/code-path-analyzer.js +8 -9
- package/lib/linter/linter.js +4 -4
- package/lib/linter/source-code-traverser.js +64 -49
- package/lib/linter/source-code-visitor.js +81 -0
- package/lib/rules/array-bracket-newline.js +3 -3
- package/lib/rules/array-bracket-spacing.js +3 -3
- package/lib/rules/array-element-newline.js +3 -3
- package/lib/rules/arrow-parens.js +3 -3
- package/lib/rules/arrow-spacing.js +3 -3
- package/lib/rules/block-spacing.js +3 -3
- package/lib/rules/brace-style.js +3 -3
- package/lib/rules/class-methods-use-this.js +7 -0
- package/lib/rules/comma-dangle.js +3 -3
- package/lib/rules/comma-spacing.js +3 -3
- package/lib/rules/comma-style.js +3 -3
- package/lib/rules/computed-property-spacing.js +3 -3
- package/lib/rules/dot-location.js +3 -3
- package/lib/rules/eol-last.js +3 -3
- package/lib/rules/func-call-spacing.js +3 -3
- package/lib/rules/function-call-argument-newline.js +3 -3
- package/lib/rules/function-paren-newline.js +3 -3
- package/lib/rules/generator-star-spacing.js +3 -3
- package/lib/rules/implicit-arrow-linebreak.js +3 -3
- package/lib/rules/indent-legacy.js +3 -3
- package/lib/rules/indent.js +3 -3
- package/lib/rules/jsx-quotes.js +3 -3
- package/lib/rules/key-spacing.js +3 -3
- package/lib/rules/keyword-spacing.js +3 -3
- package/lib/rules/line-comment-position.js +3 -3
- package/lib/rules/linebreak-style.js +3 -3
- package/lib/rules/lines-around-comment.js +3 -3
- package/lib/rules/lines-around-directive.js +3 -3
- package/lib/rules/lines-between-class-members.js +3 -3
- package/lib/rules/max-len.js +3 -3
- package/lib/rules/max-statements-per-line.js +3 -3
- package/lib/rules/multiline-comment-style.js +3 -3
- package/lib/rules/multiline-ternary.js +3 -3
- package/lib/rules/new-parens.js +3 -3
- package/lib/rules/newline-after-var.js +3 -3
- package/lib/rules/newline-before-return.js +3 -3
- package/lib/rules/newline-per-chained-call.js +3 -3
- package/lib/rules/no-confusing-arrow.js +3 -3
- package/lib/rules/no-duplicate-imports.js +65 -7
- package/lib/rules/no-extra-parens.js +3 -3
- package/lib/rules/no-extra-semi.js +3 -3
- package/lib/rules/no-floating-decimal.js +3 -3
- package/lib/rules/no-mixed-operators.js +3 -3
- package/lib/rules/no-mixed-spaces-and-tabs.js +3 -3
- package/lib/rules/no-multi-spaces.js +3 -3
- package/lib/rules/no-multiple-empty-lines.js +3 -3
- package/lib/rules/no-promise-executor-return.js +4 -35
- package/lib/rules/no-restricted-globals.js +35 -2
- package/lib/rules/no-restricted-properties.js +35 -12
- package/lib/rules/no-setter-return.js +13 -48
- package/lib/rules/no-spaced-func.js +3 -3
- package/lib/rules/no-tabs.js +3 -3
- package/lib/rules/no-trailing-spaces.js +3 -3
- package/lib/rules/no-unused-vars.js +1 -1
- package/lib/rules/no-use-before-define.js +2 -0
- package/lib/rules/no-var.js +14 -2
- package/lib/rules/no-whitespace-before-property.js +3 -3
- package/lib/rules/nonblock-statement-body-position.js +3 -3
- package/lib/rules/object-curly-newline.js +3 -3
- package/lib/rules/object-curly-spacing.js +3 -3
- package/lib/rules/object-property-newline.js +3 -3
- package/lib/rules/one-var-declaration-per-line.js +3 -3
- package/lib/rules/operator-linebreak.js +3 -3
- package/lib/rules/padded-blocks.js +3 -3
- package/lib/rules/padding-line-between-statements.js +3 -3
- package/lib/rules/prefer-regex-literals.js +1 -18
- package/lib/rules/quote-props.js +3 -3
- package/lib/rules/quotes.js +3 -3
- package/lib/rules/rest-spread-spacing.js +3 -3
- package/lib/rules/semi-spacing.js +3 -3
- package/lib/rules/semi-style.js +3 -3
- package/lib/rules/semi.js +3 -3
- package/lib/rules/space-before-blocks.js +3 -3
- package/lib/rules/space-before-function-paren.js +3 -3
- package/lib/rules/space-in-parens.js +3 -3
- package/lib/rules/space-infix-ops.js +3 -3
- package/lib/rules/space-unary-ops.js +3 -3
- package/lib/rules/spaced-comment.js +3 -3
- package/lib/rules/switch-colon-spacing.js +3 -3
- package/lib/rules/template-curly-spacing.js +3 -3
- package/lib/rules/template-tag-spacing.js +3 -3
- package/lib/rules/utils/ast-utils.js +45 -0
- package/lib/rules/wrap-iife.js +3 -3
- package/lib/rules/wrap-regex.js +3 -3
- package/lib/rules/yield-star-spacing.js +3 -3
- package/lib/services/suppressions-service.js +8 -0
- package/lib/shared/flags.js +9 -1
- package/lib/shared/naming.js +109 -0
- package/lib/shared/relative-module-resolver.js +28 -0
- package/lib/shared/runtime-info.js +1 -1
- package/lib/types/index.d.ts +15 -2
- package/lib/types/rules.d.ts +83 -74
- package/package.json +8 -8
- 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>
|
package/conf/ecma-version.js
CHANGED
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 =
|
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 =
|
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
|
-
|
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.
|
package/lib/eslint/eslint.js
CHANGED
@@ -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 (
|
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 ? `${
|
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("
|
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[
|
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 =
|
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 =
|
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 =
|
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
|
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
|
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
|
-
|
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 =
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
820
|
-
"onCodePathSegmentLoop",
|
819
|
+
this.emit("onCodePathSegmentLoop", [
|
821
820
|
fromSegment,
|
822
821
|
toSegment,
|
823
822
|
this.currentNode,
|
824
|
-
);
|
823
|
+
]);
|
825
824
|
}
|
826
825
|
}
|
827
826
|
}
|
package/lib/linter/linter.js
CHANGED
@@ -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
|
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
|
-
|
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,
|
1348
|
+
traverser.traverseSync(sourceCode, visitor, { steps });
|
1349
1349
|
|
1350
1350
|
return lintingProblems;
|
1351
1351
|
}
|