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
@@ -8,6 +8,8 @@
|
|
8
8
|
const { escapeRegExp } = require("lodash");
|
9
9
|
const astUtils = require("./utils/ast-utils");
|
10
10
|
|
11
|
+
const CHAR_LIMIT = 40;
|
12
|
+
|
11
13
|
//------------------------------------------------------------------------------
|
12
14
|
// Rule Definition
|
13
15
|
//------------------------------------------------------------------------------
|
@@ -42,12 +44,11 @@ module.exports = {
|
|
42
44
|
],
|
43
45
|
|
44
46
|
messages: {
|
45
|
-
unexpectedComment: "Unexpected '{{matchedTerm}}' comment."
|
47
|
+
unexpectedComment: "Unexpected '{{matchedTerm}}' comment: '{{comment}}'."
|
46
48
|
}
|
47
49
|
},
|
48
50
|
|
49
51
|
create(context) {
|
50
|
-
|
51
52
|
const sourceCode = context.getSourceCode(),
|
52
53
|
configuration = context.options[0] || {},
|
53
54
|
warningTerms = configuration.terms || ["todo", "fixme", "xxx"],
|
@@ -107,7 +108,15 @@ module.exports = {
|
|
107
108
|
* \bTERM\b|\bTERM\b, this checks the entire comment
|
108
109
|
* for the term.
|
109
110
|
*/
|
110
|
-
return new RegExp(
|
111
|
+
return new RegExp(
|
112
|
+
prefix +
|
113
|
+
escaped +
|
114
|
+
suffix +
|
115
|
+
eitherOrWordBoundary +
|
116
|
+
term +
|
117
|
+
wordBoundary,
|
118
|
+
"iu"
|
119
|
+
);
|
111
120
|
}
|
112
121
|
|
113
122
|
const warningRegExps = warningTerms.map(convertToRegExp);
|
@@ -135,18 +144,40 @@ module.exports = {
|
|
135
144
|
* @returns {void} undefined.
|
136
145
|
*/
|
137
146
|
function checkComment(node) {
|
138
|
-
|
147
|
+
const comment = node.value;
|
148
|
+
|
149
|
+
if (
|
150
|
+
astUtils.isDirectiveComment(node) &&
|
151
|
+
selfConfigRegEx.test(comment)
|
152
|
+
) {
|
139
153
|
return;
|
140
154
|
}
|
141
155
|
|
142
|
-
const matches = commentContainsWarningTerm(
|
156
|
+
const matches = commentContainsWarningTerm(comment);
|
143
157
|
|
144
158
|
matches.forEach(matchedTerm => {
|
159
|
+
let commentToDisplay = "";
|
160
|
+
let truncated = false;
|
161
|
+
|
162
|
+
for (const c of comment.trim().split(/\s+/u)) {
|
163
|
+
const tmp = commentToDisplay ? `${commentToDisplay} ${c}` : c;
|
164
|
+
|
165
|
+
if (tmp.length <= CHAR_LIMIT) {
|
166
|
+
commentToDisplay = tmp;
|
167
|
+
} else {
|
168
|
+
truncated = true;
|
169
|
+
break;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
145
173
|
context.report({
|
146
174
|
node,
|
147
175
|
messageId: "unexpectedComment",
|
148
176
|
data: {
|
149
|
-
matchedTerm
|
177
|
+
matchedTerm,
|
178
|
+
comment: `${commentToDisplay}${
|
179
|
+
truncated ? "..." : ""
|
180
|
+
}`
|
150
181
|
}
|
151
182
|
});
|
152
183
|
});
|
@@ -156,7 +187,9 @@ module.exports = {
|
|
156
187
|
Program() {
|
157
188
|
const comments = sourceCode.getAllComments();
|
158
189
|
|
159
|
-
comments
|
190
|
+
comments
|
191
|
+
.filter(token => token.type !== "Shebang")
|
192
|
+
.forEach(checkComment);
|
160
193
|
}
|
161
194
|
};
|
162
195
|
}
|
@@ -49,8 +49,6 @@ module.exports = {
|
|
49
49
|
* @private
|
50
50
|
*/
|
51
51
|
function reportError(node, leftToken, rightToken) {
|
52
|
-
const replacementText = node.computed ? "" : ".";
|
53
|
-
|
54
52
|
context.report({
|
55
53
|
node,
|
56
54
|
messageId: "unexpectedWhitespace",
|
@@ -58,7 +56,9 @@ module.exports = {
|
|
58
56
|
propName: sourceCode.getText(node.property)
|
59
57
|
},
|
60
58
|
fix(fixer) {
|
61
|
-
|
59
|
+
let replacementText = "";
|
60
|
+
|
61
|
+
if (!node.computed && !node.optional && astUtils.isDecimalInteger(node.object)) {
|
62
62
|
|
63
63
|
/*
|
64
64
|
* If the object is a number literal, fixing it to something like 5.toString() would cause a SyntaxError.
|
@@ -66,6 +66,18 @@ module.exports = {
|
|
66
66
|
*/
|
67
67
|
return null;
|
68
68
|
}
|
69
|
+
|
70
|
+
// Don't fix if comments exist.
|
71
|
+
if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
|
72
|
+
return null;
|
73
|
+
}
|
74
|
+
|
75
|
+
if (node.optional) {
|
76
|
+
replacementText = "?.";
|
77
|
+
} else if (!node.computed) {
|
78
|
+
replacementText = ".";
|
79
|
+
}
|
80
|
+
|
69
81
|
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], replacementText);
|
70
82
|
}
|
71
83
|
});
|
@@ -86,7 +98,7 @@ module.exports = {
|
|
86
98
|
|
87
99
|
if (node.computed) {
|
88
100
|
rightToken = sourceCode.getTokenBefore(node.property, astUtils.isOpeningBracketToken);
|
89
|
-
leftToken = sourceCode.getTokenBefore(rightToken);
|
101
|
+
leftToken = sourceCode.getTokenBefore(rightToken, node.optional ? 1 : 0);
|
90
102
|
} else {
|
91
103
|
rightToken = sourceCode.getFirstToken(node.property);
|
92
104
|
leftToken = sourceCode.getTokenBefore(rightToken, 1);
|
@@ -224,7 +224,7 @@ module.exports = {
|
|
224
224
|
context.report({
|
225
225
|
messageId: "expectedLinebreakAfterOpeningBrace",
|
226
226
|
node,
|
227
|
-
loc: openBrace.loc
|
227
|
+
loc: openBrace.loc,
|
228
228
|
fix(fixer) {
|
229
229
|
if (hasCommentsFirstToken) {
|
230
230
|
return null;
|
@@ -238,7 +238,7 @@ module.exports = {
|
|
238
238
|
context.report({
|
239
239
|
messageId: "expectedLinebreakBeforeClosingBrace",
|
240
240
|
node,
|
241
|
-
loc: closeBrace.loc
|
241
|
+
loc: closeBrace.loc,
|
242
242
|
fix(fixer) {
|
243
243
|
if (hasCommentsLastToken) {
|
244
244
|
return null;
|
@@ -260,7 +260,7 @@ module.exports = {
|
|
260
260
|
context.report({
|
261
261
|
messageId: "unexpectedLinebreakAfterOpeningBrace",
|
262
262
|
node,
|
263
|
-
loc: openBrace.loc
|
263
|
+
loc: openBrace.loc,
|
264
264
|
fix(fixer) {
|
265
265
|
if (hasCommentsFirstToken) {
|
266
266
|
return null;
|
@@ -280,7 +280,7 @@ module.exports = {
|
|
280
280
|
context.report({
|
281
281
|
messageId: "unexpectedLinebreakBeforeClosingBrace",
|
282
282
|
node,
|
283
|
-
loc: closeBrace.loc
|
283
|
+
loc: closeBrace.loc,
|
284
284
|
fix(fixer) {
|
285
285
|
if (hasCommentsLastToken) {
|
286
286
|
return null;
|
@@ -40,45 +40,6 @@ function isNonCommutativeOperatorWithShorthand(operator) {
|
|
40
40
|
// Rule Definition
|
41
41
|
//------------------------------------------------------------------------------
|
42
42
|
|
43
|
-
/**
|
44
|
-
* Checks whether two expressions reference the same value. For example:
|
45
|
-
* a = a
|
46
|
-
* a.b = a.b
|
47
|
-
* a[0] = a[0]
|
48
|
-
* a['b'] = a['b']
|
49
|
-
* @param {ASTNode} a Left side of the comparison.
|
50
|
-
* @param {ASTNode} b Right side of the comparison.
|
51
|
-
* @returns {boolean} True if both sides match and reference the same value.
|
52
|
-
*/
|
53
|
-
function same(a, b) {
|
54
|
-
if (a.type !== b.type) {
|
55
|
-
return false;
|
56
|
-
}
|
57
|
-
|
58
|
-
switch (a.type) {
|
59
|
-
case "Identifier":
|
60
|
-
return a.name === b.name;
|
61
|
-
|
62
|
-
case "Literal":
|
63
|
-
return a.value === b.value;
|
64
|
-
|
65
|
-
case "MemberExpression":
|
66
|
-
|
67
|
-
/*
|
68
|
-
* x[0] = x[0]
|
69
|
-
* x[y] = x[y]
|
70
|
-
* x.y = x.y
|
71
|
-
*/
|
72
|
-
return same(a.object, b.object) && same(a.property, b.property);
|
73
|
-
|
74
|
-
case "ThisExpression":
|
75
|
-
return true;
|
76
|
-
|
77
|
-
default:
|
78
|
-
return false;
|
79
|
-
}
|
80
|
-
}
|
81
|
-
|
82
43
|
/**
|
83
44
|
* Determines if the left side of a node can be safely fixed (i.e. if it activates the same getters/setters and)
|
84
45
|
* toString calls regardless of whether assignment shorthand is used)
|
@@ -148,12 +109,12 @@ module.exports = {
|
|
148
109
|
const operator = expr.operator;
|
149
110
|
|
150
111
|
if (isCommutativeOperatorWithShorthand(operator) || isNonCommutativeOperatorWithShorthand(operator)) {
|
151
|
-
if (
|
112
|
+
if (astUtils.isSameReference(left, expr.left, true)) {
|
152
113
|
context.report({
|
153
114
|
node,
|
154
115
|
messageId: "replaced",
|
155
116
|
fix(fixer) {
|
156
|
-
if (canBeFixed(left)) {
|
117
|
+
if (canBeFixed(left) && canBeFixed(expr.left)) {
|
157
118
|
const equalsToken = getOperatorToken(node);
|
158
119
|
const operatorToken = getOperatorToken(expr);
|
159
120
|
const leftText = sourceCode.getText().slice(node.range[0], equalsToken.range[0]);
|
@@ -169,7 +130,7 @@ module.exports = {
|
|
169
130
|
return null;
|
170
131
|
}
|
171
132
|
});
|
172
|
-
} else if (
|
133
|
+
} else if (astUtils.isSameReference(left, expr.right, true) && isCommutativeOperatorWithShorthand(operator)) {
|
173
134
|
|
174
135
|
/*
|
175
136
|
* This case can't be fixed safely.
|
@@ -190,7 +151,7 @@ module.exports = {
|
|
190
151
|
* @returns {void}
|
191
152
|
*/
|
192
153
|
function prohibit(node) {
|
193
|
-
if (node.operator !== "=") {
|
154
|
+
if (node.operator !== "=" && !astUtils.isLogicalAssignmentOperator(node.operator)) {
|
194
155
|
context.report({
|
195
156
|
node,
|
196
157
|
messageId: "unexpected",
|
@@ -85,10 +85,10 @@ function newNodeTypeTester(type) {
|
|
85
85
|
*/
|
86
86
|
function isIIFEStatement(node) {
|
87
87
|
if (node.type === "ExpressionStatement") {
|
88
|
-
let call = node.expression;
|
88
|
+
let call = astUtils.skipChainExpression(node.expression);
|
89
89
|
|
90
90
|
if (call.type === "UnaryExpression") {
|
91
|
-
call = call.argument;
|
91
|
+
call = astUtils.skipChainExpression(call.argument);
|
92
92
|
}
|
93
93
|
return call.type === "CallExpression" && astUtils.isFunction(call.callee);
|
94
94
|
}
|
@@ -5,6 +5,8 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
const astUtils = require("./utils/ast-utils");
|
9
|
+
|
8
10
|
//------------------------------------------------------------------------------
|
9
11
|
// Helpers
|
10
12
|
//------------------------------------------------------------------------------
|
@@ -66,6 +68,7 @@ function getCallbackInfo(node) {
|
|
66
68
|
const retv = { isCallback: false, isLexicalThis: false };
|
67
69
|
let currentNode = node;
|
68
70
|
let parent = node.parent;
|
71
|
+
let bound = false;
|
69
72
|
|
70
73
|
while (currentNode) {
|
71
74
|
switch (parent.type) {
|
@@ -73,23 +76,34 @@ function getCallbackInfo(node) {
|
|
73
76
|
// Checks parents recursively.
|
74
77
|
|
75
78
|
case "LogicalExpression":
|
79
|
+
case "ChainExpression":
|
76
80
|
case "ConditionalExpression":
|
77
81
|
break;
|
78
82
|
|
79
83
|
// Checks whether the parent node is `.bind(this)` call.
|
80
84
|
case "MemberExpression":
|
81
|
-
if (
|
85
|
+
if (
|
86
|
+
parent.object === currentNode &&
|
82
87
|
!parent.property.computed &&
|
83
88
|
parent.property.type === "Identifier" &&
|
84
|
-
parent.property.name === "bind"
|
85
|
-
parent.parent.type === "CallExpression" &&
|
86
|
-
parent.parent.callee === parent
|
89
|
+
parent.property.name === "bind"
|
87
90
|
) {
|
88
|
-
|
89
|
-
parent.parent
|
90
|
-
parent
|
91
|
-
|
92
|
-
|
91
|
+
const maybeCallee = parent.parent.type === "ChainExpression"
|
92
|
+
? parent.parent
|
93
|
+
: parent;
|
94
|
+
|
95
|
+
if (astUtils.isCallee(maybeCallee)) {
|
96
|
+
if (!bound) {
|
97
|
+
bound = true; // Use only the first `.bind()` to make `isLexicalThis` value.
|
98
|
+
retv.isLexicalThis = (
|
99
|
+
maybeCallee.parent.arguments.length === 1 &&
|
100
|
+
maybeCallee.parent.arguments[0].type === "ThisExpression"
|
101
|
+
);
|
102
|
+
}
|
103
|
+
parent = maybeCallee.parent;
|
104
|
+
} else {
|
105
|
+
return retv;
|
106
|
+
}
|
93
107
|
} else {
|
94
108
|
return retv;
|
95
109
|
}
|
@@ -272,7 +286,7 @@ module.exports = {
|
|
272
286
|
context.report({
|
273
287
|
node,
|
274
288
|
messageId: "preferArrowCallback",
|
275
|
-
fix(fixer) {
|
289
|
+
*fix(fixer) {
|
276
290
|
if ((!callbackInfo.isLexicalThis && scopeInfo.this) || hasDuplicateParams(node.params)) {
|
277
291
|
|
278
292
|
/*
|
@@ -281,30 +295,81 @@ module.exports = {
|
|
281
295
|
* If the callback function has duplicates in its list of parameters (possible in sloppy mode),
|
282
296
|
* don't replace it with an arrow function, because this is a SyntaxError with arrow functions.
|
283
297
|
*/
|
284
|
-
return
|
298
|
+
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
285
299
|
}
|
286
300
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
const paramsFullText = sourceCode.text.slice(paramsLeftParen.range[0], paramsRightParen.range[1]);
|
291
|
-
const arrowFunctionText = `${asyncKeyword}${paramsFullText} => ${sourceCode.getText(node.body)}`;
|
301
|
+
// Remove `.bind(this)` if exists.
|
302
|
+
if (callbackInfo.isLexicalThis) {
|
303
|
+
const memberNode = node.parent;
|
292
304
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
305
|
+
/*
|
306
|
+
* If `.bind(this)` exists but the parent is not `.bind(this)`, don't remove it automatically.
|
307
|
+
* E.g. `(foo || function(){}).bind(this)`
|
308
|
+
*/
|
309
|
+
if (memberNode.type !== "MemberExpression") {
|
310
|
+
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
311
|
+
}
|
312
|
+
|
313
|
+
const callNode = memberNode.parent;
|
314
|
+
const firstTokenToRemove = sourceCode.getTokenAfter(memberNode.object, astUtils.isNotClosingParenToken);
|
315
|
+
const lastTokenToRemove = sourceCode.getLastToken(callNode);
|
316
|
+
|
317
|
+
/*
|
318
|
+
* If the member expression is parenthesized, don't remove the right paren.
|
319
|
+
* E.g. `(function(){}.bind)(this)`
|
320
|
+
* ^^^^^^^^^^^^
|
321
|
+
*/
|
322
|
+
if (astUtils.isParenthesised(sourceCode, memberNode)) {
|
323
|
+
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
324
|
+
}
|
325
|
+
|
326
|
+
// If comments exist in the `.bind(this)`, don't remove those.
|
327
|
+
if (sourceCode.commentsExistBetween(firstTokenToRemove, lastTokenToRemove)) {
|
328
|
+
return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
|
329
|
+
}
|
330
|
+
|
331
|
+
yield fixer.removeRange([firstTokenToRemove.range[0], lastTokenToRemove.range[1]]);
|
332
|
+
}
|
333
|
+
|
334
|
+
// Convert the function expression to an arrow function.
|
335
|
+
const functionToken = sourceCode.getFirstToken(node, node.async ? 1 : 0);
|
336
|
+
const leftParenToken = sourceCode.getTokenAfter(functionToken, astUtils.isOpeningParenToken);
|
337
|
+
|
338
|
+
if (sourceCode.commentsExistBetween(functionToken, leftParenToken)) {
|
339
|
+
|
340
|
+
// Remove only extra tokens to keep comments.
|
341
|
+
yield fixer.remove(functionToken);
|
342
|
+
if (node.id) {
|
343
|
+
yield fixer.remove(node.id);
|
344
|
+
}
|
345
|
+
} else {
|
346
|
+
|
347
|
+
// Remove extra tokens and spaces.
|
348
|
+
yield fixer.removeRange([functionToken.range[0], leftParenToken.range[0]]);
|
349
|
+
}
|
350
|
+
yield fixer.insertTextBefore(node.body, "=> ");
|
351
|
+
|
352
|
+
// Get the node that will become the new arrow function.
|
353
|
+
let replacedNode = callbackInfo.isLexicalThis ? node.parent.parent : node;
|
354
|
+
|
355
|
+
if (replacedNode.type === "ChainExpression") {
|
356
|
+
replacedNode = replacedNode.parent;
|
357
|
+
}
|
298
358
|
|
299
359
|
/*
|
300
360
|
* If the replaced node is part of a BinaryExpression, LogicalExpression, or MemberExpression, then
|
301
361
|
* the arrow function needs to be parenthesized, because `foo || () => {}` is invalid syntax even
|
302
362
|
* though `foo || function() {}` is valid.
|
303
363
|
*/
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
364
|
+
if (
|
365
|
+
replacedNode.parent.type !== "CallExpression" &&
|
366
|
+
replacedNode.parent.type !== "ConditionalExpression" &&
|
367
|
+
!astUtils.isParenthesised(sourceCode, replacedNode) &&
|
368
|
+
!astUtils.isParenthesised(sourceCode, node)
|
369
|
+
) {
|
370
|
+
yield fixer.insertTextBefore(replacedNode, "(");
|
371
|
+
yield fixer.insertTextAfter(replacedNode, ")");
|
372
|
+
}
|
308
373
|
}
|
309
374
|
});
|
310
375
|
}
|
@@ -52,7 +52,7 @@ function doesExponentNeedParens(exponent) {
|
|
52
52
|
* @returns {boolean} `true` if the expression needs to be parenthesised.
|
53
53
|
*/
|
54
54
|
function doesExponentiationExpressionNeedParens(node, sourceCode) {
|
55
|
-
const parent = node.parent;
|
55
|
+
const parent = node.parent.type === "ChainExpression" ? node.parent.parent : node.parent;
|
56
56
|
|
57
57
|
const needsParens = (
|
58
58
|
parent.type === "ClassDeclaration" ||
|
@@ -29,19 +29,10 @@ const radixMap = new Map([
|
|
29
29
|
* false otherwise.
|
30
30
|
*/
|
31
31
|
function isParseInt(calleeNode) {
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
return calleeNode.object.type === "Identifier" &&
|
37
|
-
calleeNode.object.name === "Number" &&
|
38
|
-
calleeNode.property.type === "Identifier" &&
|
39
|
-
calleeNode.property.name === "parseInt";
|
40
|
-
|
41
|
-
// no default
|
42
|
-
}
|
43
|
-
|
44
|
-
return false;
|
32
|
+
return (
|
33
|
+
astUtils.isSpecificId(calleeNode, "parseInt") ||
|
34
|
+
astUtils.isSpecificMemberAccess(calleeNode, "Number", "parseInt")
|
35
|
+
);
|
45
36
|
}
|
46
37
|
|
47
38
|
//------------------------------------------------------------------------------
|
@@ -112,6 +103,16 @@ module.exports = {
|
|
112
103
|
/*
|
113
104
|
* If the newly-produced literal would be invalid, (e.g. 0b1234),
|
114
105
|
* or it would yield an incorrect parseInt result for some other reason, don't make a fix.
|
106
|
+
*
|
107
|
+
* If `str` had numeric separators, `+replacement` will evaluate to `NaN` because unary `+`
|
108
|
+
* per the specification doesn't support numeric separators. Thus, the above condition will be `true`
|
109
|
+
* (`NaN !== anything` is always `true`) regardless of the `parseInt(str, radix)` value.
|
110
|
+
* Consequently, no autofixes will be made. This is correct behavior because `parseInt` also
|
111
|
+
* doesn't support numeric separators, but it does parse part of the string before the first `_`,
|
112
|
+
* so the autofix would be invalid:
|
113
|
+
*
|
114
|
+
* parseInt("1_1", 2) // === 1
|
115
|
+
* 0b1_1 // === 3
|
115
116
|
*/
|
116
117
|
return null;
|
117
118
|
}
|
@@ -73,9 +73,7 @@ module.exports = {
|
|
73
73
|
* @returns {boolean} `true` if the call is a Promise.reject() call
|
74
74
|
*/
|
75
75
|
function isPromiseRejectCall(node) {
|
76
|
-
return node.callee
|
77
|
-
node.callee.object.type === "Identifier" && node.callee.object.name === "Promise" &&
|
78
|
-
node.callee.property.type === "Identifier" && node.callee.property.name === "reject";
|
76
|
+
return astUtils.isSpecificMemberAccess(node.callee, "Promise", "reject");
|
79
77
|
}
|
80
78
|
|
81
79
|
//----------------------------------------------------------------------
|
@@ -102,11 +102,8 @@ module.exports = {
|
|
102
102
|
*/
|
103
103
|
function isStringRawTaggedStaticTemplateLiteral(node) {
|
104
104
|
return node.type === "TaggedTemplateExpression" &&
|
105
|
-
node.tag
|
106
|
-
node.tag.object
|
107
|
-
node.tag.object.name === "String" &&
|
108
|
-
isGlobalReference(node.tag.object) &&
|
109
|
-
astUtils.getStaticPropertyName(node.tag) === "raw" &&
|
105
|
+
astUtils.isSpecificMemberAccess(node.tag, "String", "raw") &&
|
106
|
+
isGlobalReference(astUtils.skipChainExpression(node.tag).object) &&
|
110
107
|
isStaticTemplateLiteral(node.quasi);
|
111
108
|
}
|
112
109
|
|
@@ -18,17 +18,13 @@ const astUtils = require("./utils/ast-utils");
|
|
18
18
|
*/
|
19
19
|
function isVariadicApplyCalling(node) {
|
20
20
|
return (
|
21
|
-
node.callee
|
22
|
-
node.callee.property.type === "Identifier" &&
|
23
|
-
node.callee.property.name === "apply" &&
|
24
|
-
node.callee.computed === false &&
|
21
|
+
astUtils.isSpecificMemberAccess(node.callee, null, "apply") &&
|
25
22
|
node.arguments.length === 2 &&
|
26
23
|
node.arguments[1].type !== "ArrayExpression" &&
|
27
24
|
node.arguments[1].type !== "SpreadElement"
|
28
25
|
);
|
29
26
|
}
|
30
27
|
|
31
|
-
|
32
28
|
/**
|
33
29
|
* Checks whether or not `thisArg` is not changed by `.apply()`.
|
34
30
|
* @param {ASTNode|null} expectedThis The node that is the owner of the applied function.
|
@@ -75,7 +71,7 @@ module.exports = {
|
|
75
71
|
return;
|
76
72
|
}
|
77
73
|
|
78
|
-
const applied = node.callee.object;
|
74
|
+
const applied = astUtils.skipChainExpression(astUtils.skipChainExpression(node.callee).object);
|
79
75
|
const expectedThis = (applied.type === "MemberExpression") ? applied.object : null;
|
80
76
|
const thisArg = node.arguments[0];
|
81
77
|
|
package/lib/rules/radix.js
CHANGED
@@ -166,9 +166,12 @@ module.exports = {
|
|
166
166
|
if (variable && !isShadowed(variable)) {
|
167
167
|
variable.references.forEach(reference => {
|
168
168
|
const node = reference.identifier.parent;
|
169
|
+
const maybeCallee = node.parent.type === "ChainExpression"
|
170
|
+
? node.parent
|
171
|
+
: node;
|
169
172
|
|
170
|
-
if (isParseIntMethod(node) && astUtils.isCallee(
|
171
|
-
checkArguments(
|
173
|
+
if (isParseIntMethod(node) && astUtils.isCallee(maybeCallee)) {
|
174
|
+
checkArguments(maybeCallee.parent);
|
172
175
|
}
|
173
176
|
});
|
174
177
|
}
|
@@ -44,6 +44,10 @@ module.exports = {
|
|
44
44
|
ignoreMemberSort: {
|
45
45
|
type: "boolean",
|
46
46
|
default: false
|
47
|
+
},
|
48
|
+
allowSeparatedGroups: {
|
49
|
+
type: "boolean",
|
50
|
+
default: false
|
47
51
|
}
|
48
52
|
},
|
49
53
|
additionalProperties: false
|
@@ -66,6 +70,7 @@ module.exports = {
|
|
66
70
|
ignoreDeclarationSort = configuration.ignoreDeclarationSort || false,
|
67
71
|
ignoreMemberSort = configuration.ignoreMemberSort || false,
|
68
72
|
memberSyntaxSortOrder = configuration.memberSyntaxSortOrder || ["none", "all", "multiple", "single"],
|
73
|
+
allowSeparatedGroups = configuration.allowSeparatedGroups || false,
|
69
74
|
sourceCode = context.getSourceCode();
|
70
75
|
let previousDeclaration = null;
|
71
76
|
|
@@ -115,9 +120,32 @@ module.exports = {
|
|
115
120
|
|
116
121
|
}
|
117
122
|
|
123
|
+
/**
|
124
|
+
* Calculates number of lines between two nodes. It is assumed that the given `left` node appears before
|
125
|
+
* the given `right` node in the source code. Lines are counted from the end of the `left` node till the
|
126
|
+
* start of the `right` node. If the given nodes are on the same line, it returns `0`, same as if they were
|
127
|
+
* on two consecutive lines.
|
128
|
+
* @param {ASTNode} left node that appears before the given `right` node.
|
129
|
+
* @param {ASTNode} right node that appears after the given `left` node.
|
130
|
+
* @returns {number} number of lines between nodes.
|
131
|
+
*/
|
132
|
+
function getNumberOfLinesBetween(left, right) {
|
133
|
+
return Math.max(right.loc.start.line - left.loc.end.line - 1, 0);
|
134
|
+
}
|
135
|
+
|
118
136
|
return {
|
119
137
|
ImportDeclaration(node) {
|
120
138
|
if (!ignoreDeclarationSort) {
|
139
|
+
if (
|
140
|
+
previousDeclaration &&
|
141
|
+
allowSeparatedGroups &&
|
142
|
+
getNumberOfLinesBetween(previousDeclaration, node) > 0
|
143
|
+
) {
|
144
|
+
|
145
|
+
// reset declaration sort
|
146
|
+
previousDeclaration = null;
|
147
|
+
}
|
148
|
+
|
121
149
|
if (previousDeclaration) {
|
122
150
|
const currentMemberSyntaxGroupIndex = getMemberParameterGroupIndex(node),
|
123
151
|
previousMemberSyntaxGroupIndex = getMemberParameterGroupIndex(previousDeclaration);
|
package/lib/rules/use-isnan.js
CHANGED
@@ -106,7 +106,7 @@ module.exports = {
|
|
106
106
|
* @returns {void}
|
107
107
|
*/
|
108
108
|
function checkCallExpression(node) {
|
109
|
-
const callee = node.callee;
|
109
|
+
const callee = astUtils.skipChainExpression(node.callee);
|
110
110
|
|
111
111
|
if (callee.type === "MemberExpression") {
|
112
112
|
const methodName = astUtils.getStaticPropertyName(callee);
|