eslint 7.4.0 → 7.8.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/CHANGELOG.md +86 -0
- package/README.md +29 -17
- package/conf/config-schema.js +12 -0
- package/lib/cli-engine/cascading-config-array-factory.js +12 -0
- package/lib/cli-engine/cli-engine.js +2 -2
- package/lib/cli-engine/config-array/config-array.js +12 -0
- package/lib/cli-engine/config-array/config-dependency.js +12 -0
- package/lib/cli-engine/config-array/extracted-config.js +12 -0
- package/lib/cli-engine/config-array/ignore-pattern.js +12 -0
- package/lib/cli-engine/config-array/index.js +12 -0
- package/lib/cli-engine/config-array/override-tester.js +12 -0
- package/lib/cli-engine/config-array-factory.js +13 -1
- package/lib/cli-engine/formatters/checkstyle.js +2 -2
- package/lib/eslint/eslint.js +7 -1
- package/lib/init/autoconfig.js +1 -1
- package/lib/init/config-initializer.js +3 -3
- package/lib/linter/code-path-analysis/code-path-analyzer.js +76 -0
- package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
- package/lib/linter/code-path-analysis/code-path-state.js +61 -2
- package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
- package/lib/linter/config-comment-parser.js +1 -1
- package/lib/linter/linter.js +6 -3
- package/lib/rule-tester/rule-tester.js +10 -0
- package/lib/rules/accessor-pairs.js +1 -14
- package/lib/rules/array-callback-return.js +5 -7
- package/lib/rules/arrow-body-style.js +41 -6
- package/lib/rules/comma-dangle.js +1 -2
- package/lib/rules/consistent-return.js +1 -12
- package/lib/rules/constructor-super.js +18 -1
- package/lib/rules/dot-location.js +20 -14
- package/lib/rules/dot-notation.js +36 -33
- package/lib/rules/func-call-spacing.js +42 -6
- package/lib/rules/func-name-matching.js +1 -4
- package/lib/rules/global-require.js +2 -1
- package/lib/rules/id-blacklist.js +233 -0
- package/lib/rules/id-length.js +19 -1
- package/lib/rules/indent.js +23 -3
- package/lib/rules/index.js +1 -3
- package/lib/rules/keyword-spacing.js +2 -2
- package/lib/rules/max-len.js +13 -2
- package/lib/rules/new-cap.js +10 -14
- package/lib/rules/newline-per-chained-call.js +15 -5
- package/lib/rules/no-alert.js +10 -3
- package/lib/rules/no-duplicate-case.js +23 -4
- package/lib/rules/no-eval.js +8 -38
- package/lib/rules/no-extend-native.js +37 -40
- package/lib/rules/no-extra-bind.js +57 -17
- package/lib/rules/no-extra-boolean-cast.js +7 -0
- package/lib/rules/no-extra-parens.js +27 -7
- package/lib/rules/no-implicit-coercion.js +11 -6
- package/lib/rules/no-implied-eval.js +7 -28
- package/lib/rules/no-import-assign.js +33 -32
- package/lib/rules/no-irregular-whitespace.js +22 -12
- package/lib/rules/no-loss-of-precision.js +10 -2
- package/lib/rules/no-magic-numbers.js +24 -11
- package/lib/rules/no-obj-calls.js +7 -4
- package/lib/rules/no-prototype-builtins.js +13 -3
- package/lib/rules/no-self-assign.js +3 -53
- package/lib/rules/no-setter-return.js +5 -8
- package/lib/rules/no-underscore-dangle.js +66 -21
- package/lib/rules/no-unexpected-multiline.js +2 -2
- package/lib/rules/no-unneeded-ternary.js +0 -2
- package/lib/rules/no-unused-expressions.js +55 -23
- package/lib/rules/no-useless-call.js +10 -7
- package/lib/rules/no-warning-comments.js +40 -7
- package/lib/rules/no-whitespace-before-property.js +16 -4
- package/lib/rules/object-curly-newline.js +4 -4
- package/lib/rules/operator-assignment.js +4 -43
- package/lib/rules/padding-line-between-statements.js +2 -2
- package/lib/rules/prefer-arrow-callback.js +90 -25
- package/lib/rules/prefer-exponentiation-operator.js +1 -1
- package/lib/rules/prefer-numeric-literals.js +14 -13
- package/lib/rules/prefer-promise-reject-errors.js +1 -3
- package/lib/rules/prefer-regex-literals.js +2 -5
- package/lib/rules/prefer-spread.js +2 -6
- package/lib/rules/radix.js +5 -2
- package/lib/rules/sort-imports.js +28 -0
- package/lib/rules/use-isnan.js +1 -1
- package/lib/rules/utils/ast-utils.js +363 -165
- package/lib/rules/wrap-iife.js +9 -2
- package/lib/rules/yoda.js +2 -55
- package/lib/shared/config-validator.js +14 -2
- package/lib/shared/relative-module-resolver.js +12 -0
- package/lib/shared/types.js +1 -1
- package/messages/extend-config-missing.txt +1 -1
- package/messages/no-config-found.txt +1 -1
- package/messages/plugin-conflict.txt +1 -1
- package/messages/plugin-missing.txt +1 -1
- package/messages/whitespace-found.txt +1 -1
- package/package.json +7 -7
- package/conf/environments.js +0 -168
- package/lib/shared/config-ops.js +0 -130
- package/lib/shared/naming.js +0 -97
@@ -117,10 +117,7 @@ module.exports = {
|
|
117
117
|
if (!node) {
|
118
118
|
return false;
|
119
119
|
}
|
120
|
-
return node.type === "CallExpression" &&
|
121
|
-
node.callee.type === "MemberExpression" &&
|
122
|
-
node.callee.object.name === objName &&
|
123
|
-
node.callee.property.name === funcName;
|
120
|
+
return node.type === "CallExpression" && astUtils.isSpecificMemberAccess(node.callee, objName, funcName);
|
124
121
|
}
|
125
122
|
|
126
123
|
/**
|
@@ -0,0 +1,233 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule that warns when identifier names that are
|
3
|
+
* specified in the configuration are used.
|
4
|
+
* @author Keith Cirkel (http://keithcirkel.co.uk)
|
5
|
+
*/
|
6
|
+
|
7
|
+
"use strict";
|
8
|
+
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Helpers
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Checks whether the given node represents assignment target in a normal assignment or destructuring.
|
15
|
+
* @param {ASTNode} node The node to check.
|
16
|
+
* @returns {boolean} `true` if the node is assignment target.
|
17
|
+
*/
|
18
|
+
function isAssignmentTarget(node) {
|
19
|
+
const parent = node.parent;
|
20
|
+
|
21
|
+
return (
|
22
|
+
|
23
|
+
// normal assignment
|
24
|
+
(
|
25
|
+
parent.type === "AssignmentExpression" &&
|
26
|
+
parent.left === node
|
27
|
+
) ||
|
28
|
+
|
29
|
+
// destructuring
|
30
|
+
parent.type === "ArrayPattern" ||
|
31
|
+
parent.type === "RestElement" ||
|
32
|
+
(
|
33
|
+
parent.type === "Property" &&
|
34
|
+
parent.value === node &&
|
35
|
+
parent.parent.type === "ObjectPattern"
|
36
|
+
) ||
|
37
|
+
(
|
38
|
+
parent.type === "AssignmentPattern" &&
|
39
|
+
parent.left === node
|
40
|
+
)
|
41
|
+
);
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
|
46
|
+
*
|
47
|
+
* Examples:
|
48
|
+
* import { a as b } from 'mod'; // node `a` is renamed import
|
49
|
+
* export { a as b } from 'mod'; // node `a` is renamed import
|
50
|
+
* @param {ASTNode} node `Identifier` node to check.
|
51
|
+
* @returns {boolean} `true` if the node is a renamed import.
|
52
|
+
*/
|
53
|
+
function isRenamedImport(node) {
|
54
|
+
const parent = node.parent;
|
55
|
+
|
56
|
+
return (
|
57
|
+
(
|
58
|
+
parent.type === "ImportSpecifier" &&
|
59
|
+
parent.imported !== parent.local &&
|
60
|
+
parent.imported === node
|
61
|
+
) ||
|
62
|
+
(
|
63
|
+
parent.type === "ExportSpecifier" &&
|
64
|
+
parent.parent.source && // re-export
|
65
|
+
parent.local !== parent.exported &&
|
66
|
+
parent.local === node
|
67
|
+
)
|
68
|
+
);
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Checks whether the given node is a renamed identifier node in an ObjectPattern destructuring.
|
73
|
+
*
|
74
|
+
* Examples:
|
75
|
+
* const { a : b } = foo; // node `a` is renamed node.
|
76
|
+
* @param {ASTNode} node `Identifier` node to check.
|
77
|
+
* @returns {boolean} `true` if the node is a renamed node in an ObjectPattern destructuring.
|
78
|
+
*/
|
79
|
+
function isRenamedInDestructuring(node) {
|
80
|
+
const parent = node.parent;
|
81
|
+
|
82
|
+
return (
|
83
|
+
(
|
84
|
+
!parent.computed &&
|
85
|
+
parent.type === "Property" &&
|
86
|
+
parent.parent.type === "ObjectPattern" &&
|
87
|
+
parent.value !== node &&
|
88
|
+
parent.key === node
|
89
|
+
)
|
90
|
+
);
|
91
|
+
}
|
92
|
+
|
93
|
+
/**
|
94
|
+
* Checks whether the given node represents shorthand definition of a property in an object literal.
|
95
|
+
* @param {ASTNode} node `Identifier` node to check.
|
96
|
+
* @returns {boolean} `true` if the node is a shorthand property definition.
|
97
|
+
*/
|
98
|
+
function isShorthandPropertyDefinition(node) {
|
99
|
+
const parent = node.parent;
|
100
|
+
|
101
|
+
return (
|
102
|
+
parent.type === "Property" &&
|
103
|
+
parent.parent.type === "ObjectExpression" &&
|
104
|
+
parent.shorthand
|
105
|
+
);
|
106
|
+
}
|
107
|
+
|
108
|
+
//------------------------------------------------------------------------------
|
109
|
+
// Rule Definition
|
110
|
+
//------------------------------------------------------------------------------
|
111
|
+
|
112
|
+
module.exports = {
|
113
|
+
meta: {
|
114
|
+
deprecated: true,
|
115
|
+
replacedBy: ["id-denylist"],
|
116
|
+
|
117
|
+
type: "suggestion",
|
118
|
+
|
119
|
+
docs: {
|
120
|
+
description: "disallow specified identifiers",
|
121
|
+
category: "Stylistic Issues",
|
122
|
+
recommended: false,
|
123
|
+
url: "https://eslint.org/docs/rules/id-blacklist"
|
124
|
+
},
|
125
|
+
|
126
|
+
schema: {
|
127
|
+
type: "array",
|
128
|
+
items: {
|
129
|
+
type: "string"
|
130
|
+
},
|
131
|
+
uniqueItems: true
|
132
|
+
},
|
133
|
+
messages: {
|
134
|
+
restricted: "Identifier '{{name}}' is restricted."
|
135
|
+
}
|
136
|
+
},
|
137
|
+
|
138
|
+
create(context) {
|
139
|
+
|
140
|
+
const denyList = new Set(context.options);
|
141
|
+
const reportedNodes = new Set();
|
142
|
+
|
143
|
+
let globalScope;
|
144
|
+
|
145
|
+
/**
|
146
|
+
* Checks whether the given name is restricted.
|
147
|
+
* @param {string} name The name to check.
|
148
|
+
* @returns {boolean} `true` if the name is restricted.
|
149
|
+
* @private
|
150
|
+
*/
|
151
|
+
function isRestricted(name) {
|
152
|
+
return denyList.has(name);
|
153
|
+
}
|
154
|
+
|
155
|
+
/**
|
156
|
+
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
157
|
+
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
158
|
+
* @param {ASTNode} node `Identifier` node to check.
|
159
|
+
* @returns {boolean} `true` if the node is a reference to a global variable.
|
160
|
+
*/
|
161
|
+
function isReferenceToGlobalVariable(node) {
|
162
|
+
const variable = globalScope.set.get(node.name);
|
163
|
+
|
164
|
+
return variable && variable.defs.length === 0 &&
|
165
|
+
variable.references.some(ref => ref.identifier === node);
|
166
|
+
}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Determines whether the given node should be checked.
|
170
|
+
* @param {ASTNode} node `Identifier` node.
|
171
|
+
* @returns {boolean} `true` if the node should be checked.
|
172
|
+
*/
|
173
|
+
function shouldCheck(node) {
|
174
|
+
const parent = node.parent;
|
175
|
+
|
176
|
+
/*
|
177
|
+
* Member access has special rules for checking property names.
|
178
|
+
* Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
|
179
|
+
* Write access isn't allowed, because it potentially creates a new property with a restricted name.
|
180
|
+
*/
|
181
|
+
if (
|
182
|
+
parent.type === "MemberExpression" &&
|
183
|
+
parent.property === node &&
|
184
|
+
!parent.computed
|
185
|
+
) {
|
186
|
+
return isAssignmentTarget(parent);
|
187
|
+
}
|
188
|
+
|
189
|
+
return (
|
190
|
+
parent.type !== "CallExpression" &&
|
191
|
+
parent.type !== "NewExpression" &&
|
192
|
+
!isRenamedImport(node) &&
|
193
|
+
!isRenamedInDestructuring(node) &&
|
194
|
+
!(
|
195
|
+
isReferenceToGlobalVariable(node) &&
|
196
|
+
!isShorthandPropertyDefinition(node)
|
197
|
+
)
|
198
|
+
);
|
199
|
+
}
|
200
|
+
|
201
|
+
/**
|
202
|
+
* Reports an AST node as a rule violation.
|
203
|
+
* @param {ASTNode} node The node to report.
|
204
|
+
* @returns {void}
|
205
|
+
* @private
|
206
|
+
*/
|
207
|
+
function report(node) {
|
208
|
+
if (!reportedNodes.has(node)) {
|
209
|
+
context.report({
|
210
|
+
node,
|
211
|
+
messageId: "restricted",
|
212
|
+
data: {
|
213
|
+
name: node.name
|
214
|
+
}
|
215
|
+
});
|
216
|
+
reportedNodes.add(node);
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
return {
|
221
|
+
|
222
|
+
Program() {
|
223
|
+
globalScope = context.getScope();
|
224
|
+
},
|
225
|
+
|
226
|
+
Identifier(node) {
|
227
|
+
if (isRestricted(node.name) && shouldCheck(node)) {
|
228
|
+
report(node);
|
229
|
+
}
|
230
|
+
}
|
231
|
+
};
|
232
|
+
}
|
233
|
+
};
|
package/lib/rules/id-length.js
CHANGED
@@ -39,6 +39,13 @@ module.exports = {
|
|
39
39
|
type: "string"
|
40
40
|
}
|
41
41
|
},
|
42
|
+
exceptionPatterns: {
|
43
|
+
type: "array",
|
44
|
+
uniqueItems: true,
|
45
|
+
items: {
|
46
|
+
type: "string"
|
47
|
+
}
|
48
|
+
},
|
42
49
|
properties: {
|
43
50
|
enum: ["always", "never"]
|
44
51
|
}
|
@@ -63,8 +70,19 @@ module.exports = {
|
|
63
70
|
|
64
71
|
return obj;
|
65
72
|
}, {});
|
73
|
+
const exceptionPatterns = (options.exceptionPatterns || []).map(pattern => new RegExp(pattern, "u"));
|
66
74
|
const reportedNode = new Set();
|
67
75
|
|
76
|
+
/**
|
77
|
+
* Checks if a string matches the provided exception patterns
|
78
|
+
* @param {string} name The string to check.
|
79
|
+
* @returns {boolean} if the string is a match
|
80
|
+
* @private
|
81
|
+
*/
|
82
|
+
function matchesExceptionPattern(name) {
|
83
|
+
return exceptionPatterns.some(pattern => pattern.test(name));
|
84
|
+
}
|
85
|
+
|
68
86
|
const SUPPORTED_EXPRESSIONS = {
|
69
87
|
MemberExpression: properties && function(parent) {
|
70
88
|
return !parent.computed && (
|
@@ -112,7 +130,7 @@ module.exports = {
|
|
112
130
|
const isShort = name.length < minLength;
|
113
131
|
const isLong = name.length > maxLength;
|
114
132
|
|
115
|
-
if (!(isShort || isLong) || exceptions[name]) {
|
133
|
+
if (!(isShort || isLong) || exceptions[name] || matchesExceptionPattern(name)) {
|
116
134
|
return; // Nothing to report
|
117
135
|
}
|
118
136
|
|
package/lib/rules/indent.js
CHANGED
@@ -32,6 +32,7 @@ const KNOWN_NODES = new Set([
|
|
32
32
|
"BreakStatement",
|
33
33
|
"CallExpression",
|
34
34
|
"CatchClause",
|
35
|
+
"ChainExpression",
|
35
36
|
"ClassBody",
|
36
37
|
"ClassDeclaration",
|
37
38
|
"ClassExpression",
|
@@ -934,6 +935,24 @@ module.exports = {
|
|
934
935
|
parameterParens.add(openingParen);
|
935
936
|
parameterParens.add(closingParen);
|
936
937
|
|
938
|
+
/*
|
939
|
+
* If `?.` token exists, set desired offset for that.
|
940
|
+
* This logic is copied from `MemberExpression`'s.
|
941
|
+
*/
|
942
|
+
if (node.optional) {
|
943
|
+
const dotToken = sourceCode.getTokenAfter(node.callee, astUtils.isQuestionDotToken);
|
944
|
+
const calleeParenCount = sourceCode.getTokensBetween(node.callee, dotToken, { filter: astUtils.isClosingParenToken }).length;
|
945
|
+
const firstTokenOfCallee = calleeParenCount
|
946
|
+
? sourceCode.getTokenBefore(node.callee, { skip: calleeParenCount - 1 })
|
947
|
+
: sourceCode.getFirstToken(node.callee);
|
948
|
+
const lastTokenOfCallee = sourceCode.getTokenBefore(dotToken);
|
949
|
+
const offsetBase = lastTokenOfCallee.loc.end.line === openingParen.loc.start.line
|
950
|
+
? lastTokenOfCallee
|
951
|
+
: firstTokenOfCallee;
|
952
|
+
|
953
|
+
offsets.setDesiredOffset(dotToken, offsetBase, 1);
|
954
|
+
}
|
955
|
+
|
937
956
|
const offsetAfterToken = node.callee.type === "TaggedTemplateExpression" ? sourceCode.getFirstToken(node.callee.quasi) : openingParen;
|
938
957
|
const offsetToken = sourceCode.getTokenBefore(offsetAfterToken);
|
939
958
|
|
@@ -1065,16 +1084,17 @@ module.exports = {
|
|
1065
1084
|
},
|
1066
1085
|
|
1067
1086
|
ArrowFunctionExpression(node) {
|
1068
|
-
const
|
1087
|
+
const maybeOpeningParen = sourceCode.getFirstToken(node, { skip: node.async ? 1 : 0 });
|
1069
1088
|
|
1070
|
-
if (astUtils.isOpeningParenToken(
|
1071
|
-
const openingParen =
|
1089
|
+
if (astUtils.isOpeningParenToken(maybeOpeningParen)) {
|
1090
|
+
const openingParen = maybeOpeningParen;
|
1072
1091
|
const closingParen = sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken);
|
1073
1092
|
|
1074
1093
|
parameterParens.add(openingParen);
|
1075
1094
|
parameterParens.add(closingParen);
|
1076
1095
|
addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters);
|
1077
1096
|
}
|
1097
|
+
|
1078
1098
|
addBlocklessNodeIndent(node.body);
|
1079
1099
|
},
|
1080
1100
|
|
package/lib/rules/index.js
CHANGED
@@ -56,9 +56,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
56
56
|
"grouped-accessor-pairs": () => require("./grouped-accessor-pairs"),
|
57
57
|
"guard-for-in": () => require("./guard-for-in"),
|
58
58
|
"handle-callback-err": () => require("./handle-callback-err"),
|
59
|
-
|
60
|
-
// Renamed to id-denylist.
|
61
|
-
"id-blacklist": () => require("./id-denylist"),
|
59
|
+
"id-blacklist": () => require("./id-blacklist"),
|
62
60
|
"id-denylist": () => require("./id-denylist"),
|
63
61
|
"id-length": () => require("./id-length"),
|
64
62
|
"id-match": () => require("./id-match"),
|
@@ -126,7 +126,7 @@ module.exports = {
|
|
126
126
|
!sourceCode.isSpaceBetweenTokens(prevToken, token)
|
127
127
|
) {
|
128
128
|
context.report({
|
129
|
-
loc: token.loc
|
129
|
+
loc: token.loc,
|
130
130
|
messageId: "expectedBefore",
|
131
131
|
data: token,
|
132
132
|
fix(fixer) {
|
@@ -178,7 +178,7 @@ module.exports = {
|
|
178
178
|
!sourceCode.isSpaceBetweenTokens(token, nextToken)
|
179
179
|
) {
|
180
180
|
context.report({
|
181
|
-
loc: token.loc
|
181
|
+
loc: token.loc,
|
182
182
|
messageId: "expectedAfter",
|
183
183
|
data: token,
|
184
184
|
fix(fixer) {
|
package/lib/rules/max-len.js
CHANGED
@@ -383,11 +383,22 @@ module.exports = {
|
|
383
383
|
return;
|
384
384
|
}
|
385
385
|
|
386
|
+
const loc = {
|
387
|
+
start: {
|
388
|
+
line: lineNumber,
|
389
|
+
column: 0
|
390
|
+
},
|
391
|
+
end: {
|
392
|
+
line: lineNumber,
|
393
|
+
column: textToMeasure.length
|
394
|
+
}
|
395
|
+
};
|
396
|
+
|
386
397
|
if (commentLengthApplies) {
|
387
398
|
if (lineLength > maxCommentLength) {
|
388
399
|
context.report({
|
389
400
|
node,
|
390
|
-
loc
|
401
|
+
loc,
|
391
402
|
messageId: "maxComment",
|
392
403
|
data: {
|
393
404
|
lineLength,
|
@@ -398,7 +409,7 @@ module.exports = {
|
|
398
409
|
} else if (lineLength > maxLength) {
|
399
410
|
context.report({
|
400
411
|
node,
|
401
|
-
loc
|
412
|
+
loc,
|
402
413
|
messageId: "max",
|
403
414
|
data: {
|
404
415
|
lineLength,
|
package/lib/rules/new-cap.js
CHANGED
@@ -158,15 +158,9 @@ module.exports = {
|
|
158
158
|
* @returns {string} name
|
159
159
|
*/
|
160
160
|
function extractNameFromExpression(node) {
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
if (node.callee.type === "MemberExpression") {
|
165
|
-
name = astUtils.getStaticPropertyName(node.callee) || "";
|
166
|
-
} else {
|
167
|
-
name = node.callee.name;
|
168
|
-
}
|
169
|
-
return name;
|
161
|
+
return node.callee.type === "Identifier"
|
162
|
+
? node.callee.name
|
163
|
+
: astUtils.getStaticPropertyName(node.callee) || "";
|
170
164
|
}
|
171
165
|
|
172
166
|
/**
|
@@ -212,14 +206,16 @@ module.exports = {
|
|
212
206
|
return true;
|
213
207
|
}
|
214
208
|
|
215
|
-
|
209
|
+
const callee = astUtils.skipChainExpression(node.callee);
|
210
|
+
|
211
|
+
if (calleeName === "UTC" && callee.type === "MemberExpression") {
|
216
212
|
|
217
213
|
// allow if callee is Date.UTC
|
218
|
-
return
|
219
|
-
|
214
|
+
return callee.object.type === "Identifier" &&
|
215
|
+
callee.object.name === "Date";
|
220
216
|
}
|
221
217
|
|
222
|
-
return skipProperties &&
|
218
|
+
return skipProperties && callee.type === "MemberExpression";
|
223
219
|
}
|
224
220
|
|
225
221
|
/**
|
@@ -229,7 +225,7 @@ module.exports = {
|
|
229
225
|
* @returns {void}
|
230
226
|
*/
|
231
227
|
function report(node, messageId) {
|
232
|
-
let callee = node.callee;
|
228
|
+
let callee = astUtils.skipChainExpression(node.callee);
|
233
229
|
|
234
230
|
if (callee.type === "MemberExpression") {
|
235
231
|
callee = callee.property;
|
@@ -57,7 +57,16 @@ module.exports = {
|
|
57
57
|
* @returns {string} The prefix of the node.
|
58
58
|
*/
|
59
59
|
function getPrefix(node) {
|
60
|
-
|
60
|
+
if (node.computed) {
|
61
|
+
if (node.optional) {
|
62
|
+
return "?.[";
|
63
|
+
}
|
64
|
+
return "[";
|
65
|
+
}
|
66
|
+
if (node.optional) {
|
67
|
+
return "?.";
|
68
|
+
}
|
69
|
+
return ".";
|
61
70
|
}
|
62
71
|
|
63
72
|
/**
|
@@ -76,17 +85,18 @@ module.exports = {
|
|
76
85
|
|
77
86
|
return {
|
78
87
|
"CallExpression:exit"(node) {
|
79
|
-
|
88
|
+
const callee = astUtils.skipChainExpression(node.callee);
|
89
|
+
|
90
|
+
if (callee.type !== "MemberExpression") {
|
80
91
|
return;
|
81
92
|
}
|
82
93
|
|
83
|
-
|
84
|
-
let parent = callee.object;
|
94
|
+
let parent = astUtils.skipChainExpression(callee.object);
|
85
95
|
let depth = 1;
|
86
96
|
|
87
97
|
while (parent && parent.callee) {
|
88
98
|
depth += 1;
|
89
|
-
parent = parent.callee.object;
|
99
|
+
parent = astUtils.skipChainExpression(astUtils.skipChainExpression(parent.callee).object);
|
90
100
|
}
|
91
101
|
|
92
102
|
if (depth > ignoreChainWithDepth && astUtils.isTokenOnSameLine(callee.object, callee.property)) {
|
package/lib/rules/no-alert.js
CHANGED
@@ -10,7 +10,8 @@
|
|
10
10
|
|
11
11
|
const {
|
12
12
|
getStaticPropertyName: getPropertyName,
|
13
|
-
getVariableByName
|
13
|
+
getVariableByName,
|
14
|
+
skipChainExpression
|
14
15
|
} = require("./utils/ast-utils");
|
15
16
|
|
16
17
|
//------------------------------------------------------------------------------
|
@@ -64,7 +65,13 @@ function isGlobalThisReferenceOrGlobalWindow(scope, node) {
|
|
64
65
|
if (scope.type === "global" && node.type === "ThisExpression") {
|
65
66
|
return true;
|
66
67
|
}
|
67
|
-
if (
|
68
|
+
if (
|
69
|
+
node.type === "Identifier" &&
|
70
|
+
(
|
71
|
+
node.name === "window" ||
|
72
|
+
(node.name === "globalThis" && getVariableByName(scope, "globalThis"))
|
73
|
+
)
|
74
|
+
) {
|
68
75
|
return !isShadowed(scope, node);
|
69
76
|
}
|
70
77
|
|
@@ -96,7 +103,7 @@ module.exports = {
|
|
96
103
|
create(context) {
|
97
104
|
return {
|
98
105
|
CallExpression(node) {
|
99
|
-
const callee = node.callee,
|
106
|
+
const callee = skipChainExpression(node.callee),
|
100
107
|
currentScope = context.getScope();
|
101
108
|
|
102
109
|
// without window.
|
@@ -6,6 +6,12 @@
|
|
6
6
|
|
7
7
|
"use strict";
|
8
8
|
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Requirements
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
const astUtils = require("./utils/ast-utils");
|
14
|
+
|
9
15
|
//------------------------------------------------------------------------------
|
10
16
|
// Rule Definition
|
11
17
|
//------------------------------------------------------------------------------
|
@@ -31,18 +37,31 @@ module.exports = {
|
|
31
37
|
create(context) {
|
32
38
|
const sourceCode = context.getSourceCode();
|
33
39
|
|
40
|
+
/**
|
41
|
+
* Determines whether the two given nodes are considered to be equal.
|
42
|
+
* @param {ASTNode} a First node.
|
43
|
+
* @param {ASTNode} b Second node.
|
44
|
+
* @returns {boolean} `true` if the nodes are considered to be equal.
|
45
|
+
*/
|
46
|
+
function equal(a, b) {
|
47
|
+
if (a.type !== b.type) {
|
48
|
+
return false;
|
49
|
+
}
|
50
|
+
|
51
|
+
return astUtils.equalTokens(a, b, sourceCode);
|
52
|
+
}
|
34
53
|
return {
|
35
54
|
SwitchStatement(node) {
|
36
|
-
const
|
55
|
+
const previousTests = [];
|
37
56
|
|
38
57
|
for (const switchCase of node.cases) {
|
39
58
|
if (switchCase.test) {
|
40
|
-
const
|
59
|
+
const test = switchCase.test;
|
41
60
|
|
42
|
-
if (
|
61
|
+
if (previousTests.some(previousTest => equal(previousTest, test))) {
|
43
62
|
context.report({ node: switchCase, messageId: "unexpected" });
|
44
63
|
} else {
|
45
|
-
|
64
|
+
previousTests.push(test);
|
46
65
|
}
|
47
66
|
}
|
48
67
|
}
|
package/lib/rules/no-eval.js
CHANGED
@@ -21,38 +21,6 @@ const candidatesOfGlobalObject = Object.freeze([
|
|
21
21
|
"globalThis"
|
22
22
|
]);
|
23
23
|
|
24
|
-
/**
|
25
|
-
* Checks a given node is a Identifier node of the specified name.
|
26
|
-
* @param {ASTNode} node A node to check.
|
27
|
-
* @param {string} name A name to check.
|
28
|
-
* @returns {boolean} `true` if the node is a Identifier node of the name.
|
29
|
-
*/
|
30
|
-
function isIdentifier(node, name) {
|
31
|
-
return node.type === "Identifier" && node.name === name;
|
32
|
-
}
|
33
|
-
|
34
|
-
/**
|
35
|
-
* Checks a given node is a Literal node of the specified string value.
|
36
|
-
* @param {ASTNode} node A node to check.
|
37
|
-
* @param {string} name A name to check.
|
38
|
-
* @returns {boolean} `true` if the node is a Literal node of the name.
|
39
|
-
*/
|
40
|
-
function isConstant(node, name) {
|
41
|
-
switch (node.type) {
|
42
|
-
case "Literal":
|
43
|
-
return node.value === name;
|
44
|
-
|
45
|
-
case "TemplateLiteral":
|
46
|
-
return (
|
47
|
-
node.expressions.length === 0 &&
|
48
|
-
node.quasis[0].value.cooked === name
|
49
|
-
);
|
50
|
-
|
51
|
-
default:
|
52
|
-
return false;
|
53
|
-
}
|
54
|
-
}
|
55
|
-
|
56
24
|
/**
|
57
25
|
* Checks a given node is a MemberExpression node which has the specified name's
|
58
26
|
* property.
|
@@ -62,10 +30,7 @@ function isConstant(node, name) {
|
|
62
30
|
* the specified name's property
|
63
31
|
*/
|
64
32
|
function isMember(node, name) {
|
65
|
-
return (
|
66
|
-
node.type === "MemberExpression" &&
|
67
|
-
(node.computed ? isConstant : isIdentifier)(node.property, name)
|
68
|
-
);
|
33
|
+
return astUtils.isSpecificMemberAccess(node, null, name);
|
69
34
|
}
|
70
35
|
|
71
36
|
//------------------------------------------------------------------------------
|
@@ -230,7 +195,12 @@ module.exports = {
|
|
230
195
|
"CallExpression:exit"(node) {
|
231
196
|
const callee = node.callee;
|
232
197
|
|
233
|
-
|
198
|
+
/*
|
199
|
+
* Optional call (`eval?.("code")`) is not direct eval.
|
200
|
+
* The direct eval is only step 6.a.vi of https://tc39.es/ecma262/#sec-function-calls-runtime-semantics-evaluation
|
201
|
+
* But the optional call is https://tc39.es/ecma262/#sec-optional-chaining-chain-evaluation
|
202
|
+
*/
|
203
|
+
if (!node.optional && astUtils.isSpecificId(callee, "eval")) {
|
234
204
|
report(callee);
|
235
205
|
}
|
236
206
|
}
|
@@ -241,7 +211,7 @@ module.exports = {
|
|
241
211
|
"CallExpression:exit"(node) {
|
242
212
|
const callee = node.callee;
|
243
213
|
|
244
|
-
if (
|
214
|
+
if (astUtils.isSpecificId(callee, "eval")) {
|
245
215
|
report(callee);
|
246
216
|
}
|
247
217
|
},
|