eslint 8.34.0 → 8.39.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 +7 -18
- package/conf/rule-type-list.json +2 -2
- package/lib/cli-engine/cli-engine.js +2 -2
- package/lib/cli-engine/file-enumerator.js +8 -6
- package/lib/config/default-config.js +1 -4
- package/lib/config/flat-config-array.js +77 -17
- package/lib/config/flat-config-schema.js +4 -18
- package/lib/eslint/eslint-helpers.js +3 -3
- package/lib/linter/linter.js +4 -77
- package/lib/rule-tester/flat-rule-tester.js +1 -1
- package/lib/rule-tester/rule-tester.js +1 -1
- package/lib/rules/block-scoped-var.js +2 -1
- package/lib/rules/camelcase.js +5 -4
- package/lib/rules/consistent-this.js +4 -2
- package/lib/rules/func-names.js +1 -1
- package/lib/rules/global-require.js +4 -2
- package/lib/rules/handle-callback-err.js +2 -1
- package/lib/rules/id-blacklist.js +3 -2
- package/lib/rules/id-denylist.js +3 -2
- package/lib/rules/id-match.js +3 -2
- package/lib/rules/lines-around-comment.js +11 -0
- package/lib/rules/logical-assignment-operators.js +4 -4
- package/lib/rules/multiline-comment-style.js +42 -3
- package/lib/rules/new-parens.js +5 -14
- package/lib/rules/no-alert.js +3 -1
- package/lib/rules/no-catch-shadow.js +3 -1
- package/lib/rules/no-class-assign.js +3 -1
- package/lib/rules/no-console.js +3 -2
- package/lib/rules/no-const-assign.js +3 -1
- package/lib/rules/no-constant-binary-expression.js +36 -27
- package/lib/rules/no-constant-condition.js +3 -2
- package/lib/rules/no-control-regex.js +1 -1
- package/lib/rules/no-div-regex.js +1 -1
- package/lib/rules/no-dupe-args.js +3 -1
- package/lib/rules/no-else-return.js +13 -12
- package/lib/rules/no-eval.js +5 -5
- package/lib/rules/no-ex-assign.js +3 -1
- package/lib/rules/no-extend-native.js +3 -2
- package/lib/rules/no-extra-boolean-cast.js +1 -1
- package/lib/rules/no-extra-parens.js +1 -1
- package/lib/rules/no-func-assign.js +3 -1
- package/lib/rules/no-global-assign.js +3 -2
- package/lib/rules/no-implicit-globals.js +3 -2
- package/lib/rules/no-implied-eval.js +5 -4
- package/lib/rules/no-import-assign.js +5 -3
- package/lib/rules/no-invalid-regexp.js +1 -1
- package/lib/rules/no-invalid-this.js +2 -2
- package/lib/rules/no-label-var.js +2 -1
- package/lib/rules/no-lone-blocks.js +8 -6
- package/lib/rules/no-lonely-if.js +2 -3
- package/lib/rules/no-loop-func.js +3 -1
- package/lib/rules/no-misleading-character-class.js +12 -41
- package/lib/rules/no-native-reassign.js +3 -2
- package/lib/rules/no-new-func.js +7 -6
- package/lib/rules/no-new-native-nonconstructor.js +8 -6
- package/lib/rules/no-new-object.js +4 -1
- package/lib/rules/no-new-symbol.js +8 -6
- package/lib/rules/no-obj-calls.js +8 -6
- package/lib/rules/no-param-reassign.js +2 -1
- package/lib/rules/no-promise-executor-return.js +3 -2
- package/lib/rules/no-redeclare.js +3 -3
- package/lib/rules/no-regex-spaces.js +4 -2
- package/lib/rules/no-restricted-exports.js +2 -1
- package/lib/rules/no-restricted-globals.js +4 -2
- package/lib/rules/no-setter-return.js +3 -2
- package/lib/rules/no-shadow-restricted-names.js +2 -1
- package/lib/rules/no-shadow.js +3 -2
- package/lib/rules/no-undef-init.js +1 -1
- package/lib/rules/no-undef.js +3 -2
- package/lib/rules/no-undefined.js +4 -2
- package/lib/rules/no-underscore-dangle.js +2 -1
- package/lib/rules/no-unmodified-loop-condition.js +2 -2
- package/lib/rules/no-unused-expressions.js +4 -5
- package/lib/rules/no-unused-vars.js +2 -2
- package/lib/rules/no-use-before-define.js +3 -2
- package/lib/rules/no-useless-backreference.js +9 -7
- package/lib/rules/no-useless-return.js +1 -1
- package/lib/rules/no-var.js +2 -2
- package/lib/rules/object-shorthand.js +3 -2
- package/lib/rules/prefer-arrow-callback.js +2 -2
- package/lib/rules/prefer-const.js +1 -1
- package/lib/rules/prefer-exponentiation-operator.js +5 -5
- package/lib/rules/prefer-named-capture-group.js +8 -8
- package/lib/rules/prefer-object-has-own.js +4 -2
- package/lib/rules/prefer-object-spread.js +12 -13
- package/lib/rules/prefer-promise-reject-errors.js +2 -1
- package/lib/rules/prefer-regex-literals.js +26 -27
- package/lib/rules/prefer-rest-params.js +5 -2
- package/lib/rules/radix.js +11 -11
- package/lib/rules/require-atomic-updates.js +2 -2
- package/lib/rules/require-unicode-regexp.js +67 -7
- package/lib/rules/symbol-description.js +7 -5
- package/lib/rules/utils/regular-expressions.js +42 -0
- package/lib/rules/valid-typeof.js +4 -4
- package/lib/rules/wrap-iife.js +1 -1
- package/lib/rules/wrap-regex.js +2 -3
- package/lib/rules/yoda.js +1 -1
- package/lib/source-code/source-code.js +137 -1
- package/lib/source-code/token-store/index.js +1 -1
- package/lib/source-code/token-store/utils.js +35 -20
- package/messages/no-config-found.js +1 -1
- package/package.json +13 -8
- package/conf/eslint-all.js +0 -31
- package/conf/eslint-recommended.js +0 -76
@@ -79,6 +79,8 @@ module.exports = {
|
|
79
79
|
|
80
80
|
create(context) {
|
81
81
|
|
82
|
+
const sourceCode = context.getSourceCode();
|
83
|
+
|
82
84
|
/**
|
83
85
|
* Reports a given reference.
|
84
86
|
* @param {eslint-scope.Reference} reference A reference to report.
|
@@ -94,10 +96,11 @@ module.exports = {
|
|
94
96
|
|
95
97
|
/**
|
96
98
|
* Reports references of the implicit `arguments` variable if exist.
|
99
|
+
* @param {ASTNode} node The node representing the function.
|
97
100
|
* @returns {void}
|
98
101
|
*/
|
99
|
-
function checkForArguments() {
|
100
|
-
const argumentsVar = getVariableOfArguments(
|
102
|
+
function checkForArguments(node) {
|
103
|
+
const argumentsVar = getVariableOfArguments(sourceCode.getScope(node));
|
101
104
|
|
102
105
|
if (argumentsVar) {
|
103
106
|
argumentsVar
|
package/lib/rules/radix.js
CHANGED
@@ -104,6 +104,7 @@ module.exports = {
|
|
104
104
|
|
105
105
|
create(context) {
|
106
106
|
const mode = context.options[0] || MODE_ALWAYS;
|
107
|
+
const sourceCode = context.getSourceCode();
|
107
108
|
|
108
109
|
/**
|
109
110
|
* Checks the arguments of a given CallExpression node and reports it if it
|
@@ -131,7 +132,6 @@ module.exports = {
|
|
131
132
|
{
|
132
133
|
messageId: "addRadixParameter10",
|
133
134
|
fix(fixer) {
|
134
|
-
const sourceCode = context.getSourceCode();
|
135
135
|
const tokens = sourceCode.getTokens(node);
|
136
136
|
const lastToken = tokens[tokens.length - 1]; // Parenthesis.
|
137
137
|
const secondToLastToken = tokens[tokens.length - 2]; // May or may not be a comma.
|
@@ -162,18 +162,18 @@ module.exports = {
|
|
162
162
|
}
|
163
163
|
|
164
164
|
return {
|
165
|
-
"Program:exit"() {
|
166
|
-
const scope =
|
165
|
+
"Program:exit"(node) {
|
166
|
+
const scope = sourceCode.getScope(node);
|
167
167
|
let variable;
|
168
168
|
|
169
169
|
// Check `parseInt()`
|
170
170
|
variable = astUtils.getVariableByName(scope, "parseInt");
|
171
171
|
if (variable && !isShadowed(variable)) {
|
172
172
|
variable.references.forEach(reference => {
|
173
|
-
const
|
173
|
+
const idNode = reference.identifier;
|
174
174
|
|
175
|
-
if (astUtils.isCallee(
|
176
|
-
checkArguments(
|
175
|
+
if (astUtils.isCallee(idNode)) {
|
176
|
+
checkArguments(idNode.parent);
|
177
177
|
}
|
178
178
|
});
|
179
179
|
}
|
@@ -182,12 +182,12 @@ module.exports = {
|
|
182
182
|
variable = astUtils.getVariableByName(scope, "Number");
|
183
183
|
if (variable && !isShadowed(variable)) {
|
184
184
|
variable.references.forEach(reference => {
|
185
|
-
const
|
186
|
-
const maybeCallee =
|
187
|
-
?
|
188
|
-
:
|
185
|
+
const parentNode = reference.identifier.parent;
|
186
|
+
const maybeCallee = parentNode.parent.type === "ChainExpression"
|
187
|
+
? parentNode.parent
|
188
|
+
: parentNode;
|
189
189
|
|
190
|
-
if (isParseIntMethod(
|
190
|
+
if (isParseIntMethod(parentNode) && astUtils.isCallee(maybeCallee)) {
|
191
191
|
checkArguments(maybeCallee.parent);
|
192
192
|
}
|
193
193
|
});
|
@@ -204,8 +204,8 @@ module.exports = {
|
|
204
204
|
let stack = null;
|
205
205
|
|
206
206
|
return {
|
207
|
-
onCodePathStart(codePath) {
|
208
|
-
const scope =
|
207
|
+
onCodePathStart(codePath, node) {
|
208
|
+
const scope = sourceCode.getScope(node);
|
209
209
|
const shouldVerify =
|
210
210
|
scope.type === "function" &&
|
211
211
|
(scope.block.async || scope.block.generator);
|
@@ -14,7 +14,9 @@ const {
|
|
14
14
|
CONSTRUCT,
|
15
15
|
ReferenceTracker,
|
16
16
|
getStringIfConstant
|
17
|
-
} = require("eslint-utils");
|
17
|
+
} = require("@eslint-community/eslint-utils");
|
18
|
+
const astUtils = require("./utils/ast-utils.js");
|
19
|
+
const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");
|
18
20
|
|
19
21
|
//------------------------------------------------------------------------------
|
20
22
|
// Rule Definition
|
@@ -31,7 +33,10 @@ module.exports = {
|
|
31
33
|
url: "https://eslint.org/docs/rules/require-unicode-regexp"
|
32
34
|
},
|
33
35
|
|
36
|
+
hasSuggestions: true,
|
37
|
+
|
34
38
|
messages: {
|
39
|
+
addUFlag: "Add the 'u' flag.",
|
35
40
|
requireUFlag: "Use the 'u' flag."
|
36
41
|
},
|
37
42
|
|
@@ -39,28 +44,83 @@ module.exports = {
|
|
39
44
|
},
|
40
45
|
|
41
46
|
create(context) {
|
47
|
+
|
48
|
+
const sourceCode = context.getSourceCode();
|
49
|
+
|
42
50
|
return {
|
43
51
|
"Literal[regex]"(node) {
|
44
52
|
const flags = node.regex.flags || "";
|
45
53
|
|
46
54
|
if (!flags.includes("u")) {
|
47
|
-
context.report({
|
55
|
+
context.report({
|
56
|
+
messageId: "requireUFlag",
|
57
|
+
node,
|
58
|
+
suggest: isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern)
|
59
|
+
? [
|
60
|
+
{
|
61
|
+
fix(fixer) {
|
62
|
+
return fixer.insertTextAfter(node, "u");
|
63
|
+
},
|
64
|
+
messageId: "addUFlag"
|
65
|
+
}
|
66
|
+
]
|
67
|
+
: null
|
68
|
+
});
|
48
69
|
}
|
49
70
|
},
|
50
71
|
|
51
|
-
Program() {
|
52
|
-
const scope =
|
72
|
+
Program(node) {
|
73
|
+
const scope = sourceCode.getScope(node);
|
53
74
|
const tracker = new ReferenceTracker(scope);
|
54
75
|
const trackMap = {
|
55
76
|
RegExp: { [CALL]: true, [CONSTRUCT]: true }
|
56
77
|
};
|
57
78
|
|
58
|
-
for (const { node } of tracker.iterateGlobalReferences(trackMap)) {
|
59
|
-
const flagsNode =
|
79
|
+
for (const { node: refNode } of tracker.iterateGlobalReferences(trackMap)) {
|
80
|
+
const [patternNode, flagsNode] = refNode.arguments;
|
81
|
+
|
82
|
+
if (patternNode && patternNode.type === "SpreadElement") {
|
83
|
+
continue;
|
84
|
+
}
|
85
|
+
const pattern = getStringIfConstant(patternNode, scope);
|
60
86
|
const flags = getStringIfConstant(flagsNode, scope);
|
61
87
|
|
62
88
|
if (!flagsNode || (typeof flags === "string" && !flags.includes("u"))) {
|
63
|
-
context.report({
|
89
|
+
context.report({
|
90
|
+
messageId: "requireUFlag",
|
91
|
+
node: refNode,
|
92
|
+
suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)
|
93
|
+
? [
|
94
|
+
{
|
95
|
+
fix(fixer) {
|
96
|
+
if (flagsNode) {
|
97
|
+
if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
|
98
|
+
const flagsNodeText = sourceCode.getText(flagsNode);
|
99
|
+
|
100
|
+
return fixer.replaceText(flagsNode, [
|
101
|
+
flagsNodeText.slice(0, flagsNodeText.length - 1),
|
102
|
+
flagsNodeText.slice(flagsNodeText.length - 1)
|
103
|
+
].join("u"));
|
104
|
+
}
|
105
|
+
|
106
|
+
// We intentionally don't suggest concatenating + "u" to non-literals
|
107
|
+
return null;
|
108
|
+
}
|
109
|
+
|
110
|
+
const penultimateToken = sourceCode.getLastToken(refNode, { skip: 1 }); // skip closing parenthesis
|
111
|
+
|
112
|
+
return fixer.insertTextAfter(
|
113
|
+
penultimateToken,
|
114
|
+
astUtils.isCommaToken(penultimateToken)
|
115
|
+
? ' "u",'
|
116
|
+
: ', "u"'
|
117
|
+
);
|
118
|
+
},
|
119
|
+
messageId: "addUFlag"
|
120
|
+
}
|
121
|
+
]
|
122
|
+
: null
|
123
|
+
});
|
64
124
|
}
|
65
125
|
}
|
66
126
|
}
|
@@ -35,6 +35,8 @@ module.exports = {
|
|
35
35
|
|
36
36
|
create(context) {
|
37
37
|
|
38
|
+
const sourceCode = context.getSourceCode();
|
39
|
+
|
38
40
|
/**
|
39
41
|
* Reports if node does not conform the rule in case rule is set to
|
40
42
|
* report missing description
|
@@ -51,16 +53,16 @@ module.exports = {
|
|
51
53
|
}
|
52
54
|
|
53
55
|
return {
|
54
|
-
"Program:exit"() {
|
55
|
-
const scope =
|
56
|
+
"Program:exit"(node) {
|
57
|
+
const scope = sourceCode.getScope(node);
|
56
58
|
const variable = astUtils.getVariableByName(scope, "Symbol");
|
57
59
|
|
58
60
|
if (variable && variable.defs.length === 0) {
|
59
61
|
variable.references.forEach(reference => {
|
60
|
-
const
|
62
|
+
const idNode = reference.identifier;
|
61
63
|
|
62
|
-
if (astUtils.isCallee(
|
63
|
-
checkArgument(
|
64
|
+
if (astUtils.isCallee(idNode)) {
|
65
|
+
checkArgument(idNode.parent);
|
64
66
|
}
|
65
67
|
});
|
66
68
|
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Common utils for regular expressions.
|
3
|
+
* @author Josh Goldberg
|
4
|
+
* @author Toru Nagashima
|
5
|
+
*/
|
6
|
+
|
7
|
+
"use strict";
|
8
|
+
|
9
|
+
const { RegExpValidator } = require("@eslint-community/regexpp");
|
10
|
+
|
11
|
+
const REGEXPP_LATEST_ECMA_VERSION = 2022;
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Checks if the given regular expression pattern would be valid with the `u` flag.
|
15
|
+
* @param {number} ecmaVersion ECMAScript version to parse in.
|
16
|
+
* @param {string} pattern The regular expression pattern to verify.
|
17
|
+
* @returns {boolean} `true` if the pattern would be valid with the `u` flag.
|
18
|
+
* `false` if the pattern would be invalid with the `u` flag or the configured
|
19
|
+
* ecmaVersion doesn't support the `u` flag.
|
20
|
+
*/
|
21
|
+
function isValidWithUnicodeFlag(ecmaVersion, pattern) {
|
22
|
+
if (ecmaVersion <= 5) { // ecmaVersion <= 5 doesn't support the 'u' flag
|
23
|
+
return false;
|
24
|
+
}
|
25
|
+
|
26
|
+
const validator = new RegExpValidator({
|
27
|
+
ecmaVersion: Math.min(ecmaVersion, REGEXPP_LATEST_ECMA_VERSION)
|
28
|
+
});
|
29
|
+
|
30
|
+
try {
|
31
|
+
validator.validatePattern(pattern, void 0, void 0, /* uFlag = */ true);
|
32
|
+
} catch {
|
33
|
+
return false;
|
34
|
+
}
|
35
|
+
|
36
|
+
return true;
|
37
|
+
}
|
38
|
+
|
39
|
+
module.exports = {
|
40
|
+
isValidWithUnicodeFlag,
|
41
|
+
REGEXPP_LATEST_ECMA_VERSION
|
42
|
+
};
|
@@ -44,7 +44,7 @@ module.exports = {
|
|
44
44
|
|
45
45
|
const VALID_TYPES = new Set(["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"]),
|
46
46
|
OPERATORS = new Set(["==", "===", "!=", "!=="]);
|
47
|
-
|
47
|
+
const sourceCode = context.getSourceCode();
|
48
48
|
const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
|
49
49
|
|
50
50
|
let globalScope;
|
@@ -77,13 +77,13 @@ module.exports = {
|
|
77
77
|
|
78
78
|
return {
|
79
79
|
|
80
|
-
Program() {
|
81
|
-
globalScope =
|
80
|
+
Program(node) {
|
81
|
+
globalScope = sourceCode.getScope(node);
|
82
82
|
},
|
83
83
|
|
84
84
|
UnaryExpression(node) {
|
85
85
|
if (isTypeofExpression(node)) {
|
86
|
-
const parent =
|
86
|
+
const { parent } = node;
|
87
87
|
|
88
88
|
if (parent.type === "BinaryExpression" && OPERATORS.has(parent.operator)) {
|
89
89
|
const sibling = parent.left === node ? parent.right : parent.left;
|
package/lib/rules/wrap-iife.js
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
12
|
const astUtils = require("./utils/ast-utils");
|
13
|
-
const eslintUtils = require("eslint-utils");
|
13
|
+
const eslintUtils = require("@eslint-community/eslint-utils");
|
14
14
|
|
15
15
|
//----------------------------------------------------------------------
|
16
16
|
// Helpers
|
package/lib/rules/wrap-regex.js
CHANGED
@@ -40,10 +40,9 @@ module.exports = {
|
|
40
40
|
if (nodeType === "RegularExpression") {
|
41
41
|
const beforeToken = sourceCode.getTokenBefore(node);
|
42
42
|
const afterToken = sourceCode.getTokenAfter(node);
|
43
|
-
const
|
44
|
-
const grandparent = ancestors[ancestors.length - 1];
|
43
|
+
const { parent } = node;
|
45
44
|
|
46
|
-
if (
|
45
|
+
if (parent.type === "MemberExpression" && parent.object === node &&
|
47
46
|
!(beforeToken && beforeToken.value === "(" && afterToken && afterToken.value === ")")) {
|
48
47
|
context.report({
|
49
48
|
node,
|
package/lib/rules/yoda.js
CHANGED
@@ -343,7 +343,7 @@ module.exports = {
|
|
343
343
|
) &&
|
344
344
|
!(!isEqualityOperator(node.operator) && onlyEquality) &&
|
345
345
|
isComparisonOperator(node.operator) &&
|
346
|
-
!(exceptRange && isRangeTest(
|
346
|
+
!(exceptRange && isRangeTest(node.parent))
|
347
347
|
) {
|
348
348
|
context.report({
|
349
349
|
node,
|
@@ -9,11 +9,17 @@
|
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
11
|
const
|
12
|
-
{ isCommentToken } = require("eslint-utils"),
|
12
|
+
{ isCommentToken } = require("@eslint-community/eslint-utils"),
|
13
13
|
TokenStore = require("./token-store"),
|
14
14
|
astUtils = require("../shared/ast-utils"),
|
15
15
|
Traverser = require("../shared/traverser");
|
16
16
|
|
17
|
+
//------------------------------------------------------------------------------
|
18
|
+
// Type Definitions
|
19
|
+
//------------------------------------------------------------------------------
|
20
|
+
|
21
|
+
/** @typedef {import("eslint-scope").Variable} Variable */
|
22
|
+
|
17
23
|
//------------------------------------------------------------------------------
|
18
24
|
// Private
|
19
25
|
//------------------------------------------------------------------------------
|
@@ -143,6 +149,8 @@ function isSpaceBetween(sourceCode, first, second, checkInsideOfJSXText) {
|
|
143
149
|
// Public Interface
|
144
150
|
//------------------------------------------------------------------------------
|
145
151
|
|
152
|
+
const caches = Symbol("caches");
|
153
|
+
|
146
154
|
/**
|
147
155
|
* Represents parsed source code.
|
148
156
|
*/
|
@@ -175,6 +183,13 @@ class SourceCode extends TokenStore {
|
|
175
183
|
validate(ast);
|
176
184
|
super(ast.tokens, ast.comments);
|
177
185
|
|
186
|
+
/**
|
187
|
+
* General purpose caching for the class.
|
188
|
+
*/
|
189
|
+
this[caches] = new Map([
|
190
|
+
["scopes", new WeakMap()]
|
191
|
+
]);
|
192
|
+
|
178
193
|
/**
|
179
194
|
* The flag to indicate that the source code has Unicode BOM.
|
180
195
|
* @type {boolean}
|
@@ -588,6 +603,127 @@ class SourceCode extends TokenStore {
|
|
588
603
|
|
589
604
|
return positionIndex;
|
590
605
|
}
|
606
|
+
|
607
|
+
/**
|
608
|
+
* Gets the scope for the given node
|
609
|
+
* @param {ASTNode} currentNode The node to get the scope of
|
610
|
+
* @returns {eslint-scope.Scope} The scope information for this node
|
611
|
+
* @throws {TypeError} If the `currentNode` argument is missing.
|
612
|
+
*/
|
613
|
+
getScope(currentNode) {
|
614
|
+
|
615
|
+
if (!currentNode) {
|
616
|
+
throw new TypeError("Missing required argument: node.");
|
617
|
+
}
|
618
|
+
|
619
|
+
// check cache first
|
620
|
+
const cache = this[caches].get("scopes");
|
621
|
+
const cachedScope = cache.get(currentNode);
|
622
|
+
|
623
|
+
if (cachedScope) {
|
624
|
+
return cachedScope;
|
625
|
+
}
|
626
|
+
|
627
|
+
// On Program node, get the outermost scope to avoid return Node.js special function scope or ES modules scope.
|
628
|
+
const inner = currentNode.type !== "Program";
|
629
|
+
|
630
|
+
for (let node = currentNode; node; node = node.parent) {
|
631
|
+
const scope = this.scopeManager.acquire(node, inner);
|
632
|
+
|
633
|
+
if (scope) {
|
634
|
+
if (scope.type === "function-expression-name") {
|
635
|
+
cache.set(currentNode, scope.childScopes[0]);
|
636
|
+
return scope.childScopes[0];
|
637
|
+
}
|
638
|
+
|
639
|
+
cache.set(currentNode, scope);
|
640
|
+
return scope;
|
641
|
+
}
|
642
|
+
}
|
643
|
+
|
644
|
+
cache.set(currentNode, this.scopeManager.scopes[0]);
|
645
|
+
return this.scopeManager.scopes[0];
|
646
|
+
}
|
647
|
+
|
648
|
+
/**
|
649
|
+
* Get the variables that `node` defines.
|
650
|
+
* This is a convenience method that passes through
|
651
|
+
* to the same method on the `scopeManager`.
|
652
|
+
* @param {ASTNode} node The node for which the variables are obtained.
|
653
|
+
* @returns {Array<Variable>} An array of variable nodes representing
|
654
|
+
* the variables that `node` defines.
|
655
|
+
*/
|
656
|
+
getDeclaredVariables(node) {
|
657
|
+
return this.scopeManager.getDeclaredVariables(node);
|
658
|
+
}
|
659
|
+
|
660
|
+
/* eslint-disable class-methods-use-this -- node is owned by SourceCode */
|
661
|
+
/**
|
662
|
+
* Gets all the ancestors of a given node
|
663
|
+
* @param {ASTNode} node The node
|
664
|
+
* @returns {Array<ASTNode>} All the ancestor nodes in the AST, not including the provided node, starting
|
665
|
+
* from the root node at index 0 and going inwards to the parent node.
|
666
|
+
* @throws {TypeError} When `node` is missing.
|
667
|
+
*/
|
668
|
+
getAncestors(node) {
|
669
|
+
|
670
|
+
if (!node) {
|
671
|
+
throw new TypeError("Missing required argument: node.");
|
672
|
+
}
|
673
|
+
|
674
|
+
const ancestorsStartingAtParent = [];
|
675
|
+
|
676
|
+
for (let ancestor = node.parent; ancestor; ancestor = ancestor.parent) {
|
677
|
+
ancestorsStartingAtParent.push(ancestor);
|
678
|
+
}
|
679
|
+
|
680
|
+
return ancestorsStartingAtParent.reverse();
|
681
|
+
}
|
682
|
+
/* eslint-enable class-methods-use-this -- node is owned by SourceCode */
|
683
|
+
|
684
|
+
/**
|
685
|
+
* Marks a variable as used in the current scope
|
686
|
+
* @param {string} name The name of the variable to mark as used.
|
687
|
+
* @param {ASTNode} [refNode] The closest node to the variable reference.
|
688
|
+
* @returns {boolean} True if the variable was found and marked as used, false if not.
|
689
|
+
*/
|
690
|
+
markVariableAsUsed(name, refNode = this.ast) {
|
691
|
+
|
692
|
+
const currentScope = this.getScope(refNode);
|
693
|
+
let initialScope = currentScope;
|
694
|
+
|
695
|
+
/*
|
696
|
+
* When we are in an ESM or CommonJS module, we need to start searching
|
697
|
+
* from the top-level scope, not the global scope. For ESM the top-level
|
698
|
+
* scope is the module scope; for CommonJS the top-level scope is the
|
699
|
+
* outer function scope.
|
700
|
+
*
|
701
|
+
* Without this check, we might miss a variable declared with `var` at
|
702
|
+
* the top-level because it won't exist in the global scope.
|
703
|
+
*/
|
704
|
+
if (
|
705
|
+
currentScope.type === "global" &&
|
706
|
+
currentScope.childScopes.length > 0 &&
|
707
|
+
|
708
|
+
// top-level scopes refer to a `Program` node
|
709
|
+
currentScope.childScopes[0].block === this.ast
|
710
|
+
) {
|
711
|
+
initialScope = currentScope.childScopes[0];
|
712
|
+
}
|
713
|
+
|
714
|
+
for (let scope = initialScope; scope; scope = scope.upper) {
|
715
|
+
const variable = scope.variables.find(scopeVar => scopeVar.name === name);
|
716
|
+
|
717
|
+
if (variable) {
|
718
|
+
variable.eslintUsed = true;
|
719
|
+
return true;
|
720
|
+
}
|
721
|
+
}
|
722
|
+
|
723
|
+
return false;
|
724
|
+
}
|
725
|
+
|
726
|
+
|
591
727
|
}
|
592
728
|
|
593
729
|
module.exports = SourceCode;
|
@@ -9,7 +9,7 @@
|
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
11
|
const assert = require("assert");
|
12
|
-
const { isCommentToken } = require("eslint-utils");
|
12
|
+
const { isCommentToken } = require("@eslint-community/eslint-utils");
|
13
13
|
const cursors = require("./cursors");
|
14
14
|
const ForwardTokenCursor = require("./forward-token-cursor");
|
15
15
|
const PaddedTokenCursor = require("./padded-token-cursor");
|
@@ -4,20 +4,6 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
-
//------------------------------------------------------------------------------
|
8
|
-
// Helpers
|
9
|
-
//------------------------------------------------------------------------------
|
10
|
-
|
11
|
-
/**
|
12
|
-
* Gets `token.range[0]` from the given token.
|
13
|
-
* @param {Node|Token|Comment} token The token to get.
|
14
|
-
* @returns {number} The start location.
|
15
|
-
* @private
|
16
|
-
*/
|
17
|
-
function getStartLocation(token) {
|
18
|
-
return token.range[0];
|
19
|
-
}
|
20
|
-
|
21
7
|
//------------------------------------------------------------------------------
|
22
8
|
// Exports
|
23
9
|
//------------------------------------------------------------------------------
|
@@ -30,9 +16,28 @@ function getStartLocation(token) {
|
|
30
16
|
* @returns {number} The found index or `tokens.length`.
|
31
17
|
*/
|
32
18
|
exports.search = function search(tokens, location) {
|
33
|
-
|
19
|
+
for (let minIndex = 0, maxIndex = tokens.length - 1; minIndex <= maxIndex;) {
|
34
20
|
|
35
|
-
|
21
|
+
/*
|
22
|
+
* Calculate the index in the middle between minIndex and maxIndex.
|
23
|
+
* `| 0` is used to round a fractional value down to the nearest integer: this is similar to
|
24
|
+
* using `Math.trunc()` or `Math.floor()`, but performance tests have shown this method to
|
25
|
+
* be faster.
|
26
|
+
*/
|
27
|
+
const index = (minIndex + maxIndex) / 2 | 0;
|
28
|
+
const token = tokens[index];
|
29
|
+
const tokenStartLocation = token.range[0];
|
30
|
+
|
31
|
+
if (location <= tokenStartLocation) {
|
32
|
+
if (index === minIndex) {
|
33
|
+
return index;
|
34
|
+
}
|
35
|
+
maxIndex = index;
|
36
|
+
} else {
|
37
|
+
minIndex = index + 1;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
return tokens.length;
|
36
41
|
};
|
37
42
|
|
38
43
|
/**
|
@@ -49,13 +54,18 @@ exports.getFirstIndex = function getFirstIndex(tokens, indexMap, startLoc) {
|
|
49
54
|
}
|
50
55
|
if ((startLoc - 1) in indexMap) {
|
51
56
|
const index = indexMap[startLoc - 1];
|
52
|
-
const token =
|
57
|
+
const token = tokens[index];
|
58
|
+
|
59
|
+
// If the mapped index is out of bounds, the returned cursor index will point after the end of the tokens array.
|
60
|
+
if (!token) {
|
61
|
+
return tokens.length;
|
62
|
+
}
|
53
63
|
|
54
64
|
/*
|
55
65
|
* For the map of "comment's location -> token's index", it points the next token of a comment.
|
56
66
|
* In that case, +1 is unnecessary.
|
57
67
|
*/
|
58
|
-
if (token
|
68
|
+
if (token.range[0] >= startLoc) {
|
59
69
|
return index;
|
60
70
|
}
|
61
71
|
return index + 1;
|
@@ -77,13 +87,18 @@ exports.getLastIndex = function getLastIndex(tokens, indexMap, endLoc) {
|
|
77
87
|
}
|
78
88
|
if ((endLoc - 1) in indexMap) {
|
79
89
|
const index = indexMap[endLoc - 1];
|
80
|
-
const token =
|
90
|
+
const token = tokens[index];
|
91
|
+
|
92
|
+
// If the mapped index is out of bounds, the returned cursor index will point before the end of the tokens array.
|
93
|
+
if (!token) {
|
94
|
+
return tokens.length - 1;
|
95
|
+
}
|
81
96
|
|
82
97
|
/*
|
83
98
|
* For the map of "comment's location -> token's index", it points the next token of a comment.
|
84
99
|
* In that case, -1 is necessary.
|
85
100
|
*/
|
86
|
-
if (token
|
101
|
+
if (token.range[1] > endLoc) {
|
87
102
|
return index - 1;
|
88
103
|
}
|
89
104
|
return index;
|
@@ -10,6 +10,6 @@ ESLint couldn't find a configuration file. To set up a configuration file for th
|
|
10
10
|
|
11
11
|
ESLint looked for configuration files in ${directoryPath} and its ancestors. If it found none, it then looked in your home directory.
|
12
12
|
|
13
|
-
If you think you already have a configuration file or if you need more help, please stop by the ESLint
|
13
|
+
If you think you already have a configuration file or if you need more help, please stop by the ESLint Discord server: https://eslint.org/chat
|
14
14
|
`.trimStart();
|
15
15
|
};
|