eslint 7.2.0 → 7.5.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 +78 -0
- package/README.md +14 -7
- package/lib/init/config-initializer.js +89 -68
- package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
- package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
- package/lib/linter/code-path-analysis/code-path-state.js +59 -0
- package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
- package/lib/rule-tester/rule-tester.js +9 -0
- package/lib/rules/accessor-pairs.js +1 -14
- package/lib/rules/array-callback-return.js +26 -17
- package/lib/rules/arrow-body-style.js +43 -8
- package/lib/rules/arrow-parens.js +91 -108
- package/lib/rules/camelcase.js +47 -0
- package/lib/rules/comma-dangle.js +2 -1
- package/lib/rules/consistent-return.js +1 -12
- package/lib/rules/constructor-super.js +1 -0
- package/lib/rules/curly.js +8 -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 +14 -11
- package/lib/rules/id-denylist.js +230 -0
- package/lib/rules/id-match.js +2 -1
- package/lib/rules/indent.js +19 -0
- package/lib/rules/index.js +3 -0
- package/lib/rules/key-spacing.js +6 -2
- package/lib/rules/keyword-spacing.js +2 -2
- package/lib/rules/max-len.js +13 -2
- package/lib/rules/max-lines.js +34 -8
- 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-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 +48 -10
- 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-magic-numbers.js +4 -8
- package/lib/rules/no-obj-calls.js +7 -4
- package/lib/rules/no-promise-executor-return.js +121 -0
- 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-unexpected-multiline.js +2 -2
- package/lib/rules/no-unneeded-ternary.js +0 -2
- package/lib/rules/no-unreachable-loop.js +150 -0
- package/lib/rules/no-unused-expressions.js +55 -23
- package/lib/rules/no-unused-vars.js +2 -1
- package/lib/rules/no-useless-call.js +10 -7
- package/lib/rules/no-whitespace-before-property.js +16 -4
- package/lib/rules/object-curly-newline.js +4 -4
- package/lib/rules/object-property-newline.js +1 -1
- package/lib/rules/operator-assignment.js +3 -42
- package/lib/rules/operator-linebreak.js +2 -5
- package/lib/rules/padded-blocks.js +2 -1
- 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 +4 -13
- package/lib/rules/prefer-promise-reject-errors.js +1 -3
- package/lib/rules/prefer-regex-literals.js +68 -13
- package/lib/rules/prefer-spread.js +2 -6
- package/lib/rules/radix.js +5 -2
- package/lib/rules/semi-spacing.js +1 -0
- package/lib/rules/sort-imports.js +28 -0
- package/lib/rules/use-isnan.js +1 -1
- package/lib/rules/utils/ast-utils.js +317 -153
- package/lib/rules/wrap-iife.js +9 -2
- package/lib/rules/yoda.js +2 -55
- package/package.json +7 -7
@@ -25,6 +25,22 @@ function getId(segment) { // eslint-disable-line jsdoc/require-jsdoc
|
|
25
25
|
return segment.id + (segment.reachable ? "" : "!");
|
26
26
|
}
|
27
27
|
|
28
|
+
/**
|
29
|
+
* Get string for the given node and operation.
|
30
|
+
* @param {ASTNode} node The node to convert.
|
31
|
+
* @param {"enter" | "exit" | undefined} label The operation label.
|
32
|
+
* @returns {string} The string representation.
|
33
|
+
*/
|
34
|
+
function nodeToString(node, label) {
|
35
|
+
const suffix = label ? `:${label}` : "";
|
36
|
+
|
37
|
+
switch (node.type) {
|
38
|
+
case "Identifier": return `${node.type}${suffix} (${node.name})`;
|
39
|
+
case "Literal": return `${node.type}${suffix} (${node.value})`;
|
40
|
+
default: return `${node.type}${suffix}`;
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
28
44
|
//------------------------------------------------------------------------------
|
29
45
|
// Public Interface
|
30
46
|
//------------------------------------------------------------------------------
|
@@ -56,9 +72,15 @@ module.exports = {
|
|
56
72
|
const segInternal = state.currentSegments[i].internal;
|
57
73
|
|
58
74
|
if (leaving) {
|
59
|
-
segInternal.
|
75
|
+
const last = segInternal.nodes.length - 1;
|
76
|
+
|
77
|
+
if (last >= 0 && segInternal.nodes[last] === nodeToString(node, "enter")) {
|
78
|
+
segInternal.nodes[last] = nodeToString(node, void 0);
|
79
|
+
} else {
|
80
|
+
segInternal.nodes.push(nodeToString(node, "exit"));
|
81
|
+
}
|
60
82
|
} else {
|
61
|
-
segInternal.nodes.push(node);
|
83
|
+
segInternal.nodes.push(nodeToString(node, "enter"));
|
62
84
|
}
|
63
85
|
}
|
64
86
|
|
@@ -104,23 +126,8 @@ module.exports = {
|
|
104
126
|
text += "style=\"rounded,dashed,filled\",fillcolor=\"#FF9800\",label=\"<<unreachable>>\\n";
|
105
127
|
}
|
106
128
|
|
107
|
-
if (segment.internal.nodes.length > 0
|
108
|
-
text +=
|
109
|
-
segment.internal.nodes.map(node => {
|
110
|
-
switch (node.type) {
|
111
|
-
case "Identifier": return `${node.type} (${node.name})`;
|
112
|
-
case "Literal": return `${node.type} (${node.value})`;
|
113
|
-
default: return node.type;
|
114
|
-
}
|
115
|
-
}),
|
116
|
-
segment.internal.exitNodes.map(node => {
|
117
|
-
switch (node.type) {
|
118
|
-
case "Identifier": return `${node.type}:exit (${node.name})`;
|
119
|
-
case "Literal": return `${node.type}:exit (${node.value})`;
|
120
|
-
default: return `${node.type}:exit`;
|
121
|
-
}
|
122
|
-
})
|
123
|
-
).join("\\n");
|
129
|
+
if (segment.internal.nodes.length > 0) {
|
130
|
+
text += segment.internal.nodes.join("\\n");
|
124
131
|
} else {
|
125
132
|
text += "????";
|
126
133
|
}
|
@@ -644,6 +644,10 @@ class RuleTester {
|
|
644
644
|
assert.ok(item.errors || item.errors === 0,
|
645
645
|
`Did not specify errors for an invalid test of ${ruleName}`);
|
646
646
|
|
647
|
+
if (Array.isArray(item.errors) && item.errors.length === 0) {
|
648
|
+
assert.fail("Invalid cases must have at least one error");
|
649
|
+
}
|
650
|
+
|
647
651
|
const ruleHasMetaMessages = hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages");
|
648
652
|
const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null;
|
649
653
|
|
@@ -651,6 +655,11 @@ class RuleTester {
|
|
651
655
|
const messages = result.messages;
|
652
656
|
|
653
657
|
if (typeof item.errors === "number") {
|
658
|
+
|
659
|
+
if (item.errors === 0) {
|
660
|
+
assert.fail("Invalid cases must have 'error' value greater than 0");
|
661
|
+
}
|
662
|
+
|
654
663
|
assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
|
655
664
|
item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
|
656
665
|
} else {
|
@@ -86,16 +86,6 @@ function isAccessorKind(node) {
|
|
86
86
|
return node.kind === "get" || node.kind === "set";
|
87
87
|
}
|
88
88
|
|
89
|
-
/**
|
90
|
-
* Checks whether or not a given node is an `Identifier` node which was named a given name.
|
91
|
-
* @param {ASTNode} node A node to check.
|
92
|
-
* @param {string} name An expected name of the node.
|
93
|
-
* @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
|
94
|
-
*/
|
95
|
-
function isIdentifier(node, name) {
|
96
|
-
return node.type === "Identifier" && node.name === name;
|
97
|
-
}
|
98
|
-
|
99
89
|
/**
|
100
90
|
* Checks whether or not a given node is an argument of a specified method call.
|
101
91
|
* @param {ASTNode} node A node to check.
|
@@ -109,10 +99,7 @@ function isArgumentOfMethodCall(node, index, object, property) {
|
|
109
99
|
|
110
100
|
return (
|
111
101
|
parent.type === "CallExpression" &&
|
112
|
-
parent.callee
|
113
|
-
parent.callee.computed === false &&
|
114
|
-
isIdentifier(parent.callee.object, object) &&
|
115
|
-
isIdentifier(parent.callee.property, property) &&
|
102
|
+
astUtils.isSpecificMemberAccess(parent.callee, object, property) &&
|
116
103
|
parent.arguments[index] === node
|
117
104
|
);
|
118
105
|
}
|
@@ -9,8 +9,6 @@
|
|
9
9
|
// Requirements
|
10
10
|
//------------------------------------------------------------------------------
|
11
11
|
|
12
|
-
const lodash = require("lodash");
|
13
|
-
|
14
12
|
const astUtils = require("./utils/ast-utils");
|
15
13
|
|
16
14
|
//------------------------------------------------------------------------------
|
@@ -30,17 +28,27 @@ function isReachable(segment) {
|
|
30
28
|
}
|
31
29
|
|
32
30
|
/**
|
33
|
-
* Checks a given node is a
|
31
|
+
* Checks a given node is a member access which has the specified name's
|
34
32
|
* property.
|
35
33
|
* @param {ASTNode} node A node to check.
|
36
|
-
* @returns {boolean} `true` if the node is a
|
37
|
-
* the specified name's property
|
34
|
+
* @returns {boolean} `true` if the node is a member access which has
|
35
|
+
* the specified name's property. The node may be a `(Chain|Member)Expression` node.
|
38
36
|
*/
|
39
37
|
function isTargetMethod(node) {
|
40
|
-
return (
|
41
|
-
|
42
|
-
|
43
|
-
|
38
|
+
return astUtils.isSpecificMemberAccess(node, null, TARGET_METHODS);
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Returns a human-legible description of an array method
|
43
|
+
* @param {string} arrayMethodName A method name to fully qualify
|
44
|
+
* @returns {string} the method name prefixed with `Array.` if it is a class method,
|
45
|
+
* or else `Array.prototype.` if it is an instance method.
|
46
|
+
*/
|
47
|
+
function fullMethodName(arrayMethodName) {
|
48
|
+
if (["from", "of", "isArray"].includes(arrayMethodName)) {
|
49
|
+
return "Array.".concat(arrayMethodName);
|
50
|
+
}
|
51
|
+
return "Array.prototype.".concat(arrayMethodName);
|
44
52
|
}
|
45
53
|
|
46
54
|
/**
|
@@ -65,6 +73,7 @@ function getArrayMethodName(node) {
|
|
65
73
|
*/
|
66
74
|
case "LogicalExpression":
|
67
75
|
case "ConditionalExpression":
|
76
|
+
case "ChainExpression":
|
68
77
|
currentNode = parent;
|
69
78
|
break;
|
70
79
|
|
@@ -153,10 +162,10 @@ module.exports = {
|
|
153
162
|
],
|
154
163
|
|
155
164
|
messages: {
|
156
|
-
expectedAtEnd: "
|
157
|
-
expectedInside: "
|
158
|
-
expectedReturnValue: "{{
|
159
|
-
expectedNoReturnValue: "{{
|
165
|
+
expectedAtEnd: "{{arrayMethodName}}() expects a value to be returned at the end of {{name}}.",
|
166
|
+
expectedInside: "{{arrayMethodName}}() expects a return value from {{name}}.",
|
167
|
+
expectedReturnValue: "{{arrayMethodName}}() expects a return value from {{name}}.",
|
168
|
+
expectedNoReturnValue: "{{arrayMethodName}}() expects no useless return value from {{name}}."
|
160
169
|
}
|
161
170
|
},
|
162
171
|
|
@@ -202,14 +211,13 @@ module.exports = {
|
|
202
211
|
}
|
203
212
|
|
204
213
|
if (messageId) {
|
205
|
-
|
214
|
+
const name = astUtils.getFunctionNameWithKind(node);
|
206
215
|
|
207
|
-
name = messageId === "expectedNoReturnValue" ? lodash.upperFirst(name) : name;
|
208
216
|
context.report({
|
209
217
|
node,
|
210
218
|
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
211
219
|
messageId,
|
212
|
-
data: { name }
|
220
|
+
data: { name, arrayMethodName: fullMethodName(funcInfo.arrayMethodName) }
|
213
221
|
});
|
214
222
|
}
|
215
223
|
}
|
@@ -273,7 +281,8 @@ module.exports = {
|
|
273
281
|
node,
|
274
282
|
messageId,
|
275
283
|
data: {
|
276
|
-
name:
|
284
|
+
name: astUtils.getFunctionNameWithKind(funcInfo.node),
|
285
|
+
arrayMethodName: fullMethodName(funcInfo.arrayMethodName)
|
277
286
|
}
|
278
287
|
});
|
279
288
|
}
|
@@ -75,6 +75,7 @@ module.exports = {
|
|
75
75
|
const never = options[0] === "never";
|
76
76
|
const requireReturnForObjectLiteral = options[1] && options[1].requireReturnForObjectLiteral;
|
77
77
|
const sourceCode = context.getSourceCode();
|
78
|
+
let funcInfo = null;
|
78
79
|
|
79
80
|
/**
|
80
81
|
* Checks whether the given node has ASI problem or not.
|
@@ -99,6 +100,21 @@ module.exports = {
|
|
99
100
|
return sourceCode.getTokenAfter(node);
|
100
101
|
}
|
101
102
|
|
103
|
+
/**
|
104
|
+
* Check whether the node is inside of a for loop's init
|
105
|
+
* @param {ASTNode} node node is inside for loop
|
106
|
+
* @returns {boolean} `true` if the node is inside of a for loop, else `false`
|
107
|
+
*/
|
108
|
+
function isInsideForLoopInitializer(node) {
|
109
|
+
if (node && node.parent) {
|
110
|
+
if (node.parent.type === "ForStatement" && node.parent.init === node) {
|
111
|
+
return true;
|
112
|
+
}
|
113
|
+
return isInsideForLoopInitializer(node.parent);
|
114
|
+
}
|
115
|
+
return false;
|
116
|
+
}
|
117
|
+
|
102
118
|
/**
|
103
119
|
* Determines whether a arrow function body needs braces
|
104
120
|
* @param {ASTNode} node The arrow function node.
|
@@ -136,7 +152,7 @@ module.exports = {
|
|
136
152
|
|
137
153
|
context.report({
|
138
154
|
node,
|
139
|
-
loc: arrowBody.loc
|
155
|
+
loc: arrowBody.loc,
|
140
156
|
messageId,
|
141
157
|
fix(fixer) {
|
142
158
|
const fixes = [];
|
@@ -178,11 +194,13 @@ module.exports = {
|
|
178
194
|
* If the first token of the reutrn value is `{` or the return value is a sequence expression,
|
179
195
|
* enclose the return value by parentheses to avoid syntax error.
|
180
196
|
*/
|
181
|
-
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression") {
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
197
|
+
if (astUtils.isOpeningBraceToken(firstValueToken) || blockBody[0].argument.type === "SequenceExpression" || (funcInfo.hasInOperator && isInsideForLoopInitializer(node))) {
|
198
|
+
if (!astUtils.isParenthesised(sourceCode, blockBody[0].argument)) {
|
199
|
+
fixes.push(
|
200
|
+
fixer.insertTextBefore(firstValueToken, "("),
|
201
|
+
fixer.insertTextAfter(lastValueToken, ")")
|
202
|
+
);
|
203
|
+
}
|
186
204
|
}
|
187
205
|
|
188
206
|
/*
|
@@ -201,7 +219,7 @@ module.exports = {
|
|
201
219
|
if (always || (asNeeded && requireReturnForObjectLiteral && arrowBody.type === "ObjectExpression")) {
|
202
220
|
context.report({
|
203
221
|
node,
|
204
|
-
loc: arrowBody.loc
|
222
|
+
loc: arrowBody.loc,
|
205
223
|
messageId: "expectedBlock",
|
206
224
|
fix(fixer) {
|
207
225
|
const fixes = [];
|
@@ -245,7 +263,24 @@ module.exports = {
|
|
245
263
|
}
|
246
264
|
|
247
265
|
return {
|
248
|
-
"
|
266
|
+
"BinaryExpression[operator='in']"() {
|
267
|
+
let info = funcInfo;
|
268
|
+
|
269
|
+
while (info) {
|
270
|
+
info.hasInOperator = true;
|
271
|
+
info = info.upper;
|
272
|
+
}
|
273
|
+
},
|
274
|
+
ArrowFunctionExpression() {
|
275
|
+
funcInfo = {
|
276
|
+
upper: funcInfo,
|
277
|
+
hasInOperator: false
|
278
|
+
};
|
279
|
+
},
|
280
|
+
"ArrowFunctionExpression:exit"(node) {
|
281
|
+
validate(node);
|
282
|
+
funcInfo = funcInfo.upper;
|
283
|
+
}
|
249
284
|
};
|
250
285
|
}
|
251
286
|
};
|
@@ -15,15 +15,12 @@ const astUtils = require("./utils/ast-utils");
|
|
15
15
|
//------------------------------------------------------------------------------
|
16
16
|
|
17
17
|
/**
|
18
|
-
*
|
19
|
-
* @param {ASTNode} node
|
20
|
-
* @returns {
|
18
|
+
* Determines if the given arrow function has block body.
|
19
|
+
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
20
|
+
* @returns {boolean} `true` if the function has block body.
|
21
21
|
*/
|
22
|
-
function
|
23
|
-
return
|
24
|
-
start: node.params[0].loc.start,
|
25
|
-
end: node.params[node.params.length - 1].loc.end
|
26
|
-
};
|
22
|
+
function hasBlockBody(node) {
|
23
|
+
return node.body.type === "BlockStatement";
|
27
24
|
}
|
28
25
|
|
29
26
|
//------------------------------------------------------------------------------
|
@@ -75,126 +72,112 @@ module.exports = {
|
|
75
72
|
const sourceCode = context.getSourceCode();
|
76
73
|
|
77
74
|
/**
|
78
|
-
*
|
79
|
-
*
|
80
|
-
* @
|
75
|
+
* Finds opening paren of parameters for the given arrow function, if it exists.
|
76
|
+
* It is assumed that the given arrow function has exactly one parameter.
|
77
|
+
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
78
|
+
* @returns {Token|null} the opening paren, or `null` if the given arrow function doesn't have parens of parameters.
|
81
79
|
*/
|
82
|
-
function
|
83
|
-
const
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
function fixParamsWithParenthesis(fixer) {
|
92
|
-
const paramToken = sourceCode.getTokenAfter(firstTokenOfParam);
|
93
|
-
|
94
|
-
/*
|
95
|
-
* ES8 allows Trailing commas in function parameter lists and calls
|
96
|
-
* https://github.com/eslint/eslint/issues/8834
|
97
|
-
*/
|
98
|
-
const closingParenToken = sourceCode.getTokenAfter(paramToken, astUtils.isClosingParenToken);
|
99
|
-
const asyncToken = isAsync ? sourceCode.getTokenBefore(firstTokenOfParam) : null;
|
100
|
-
const shouldAddSpaceForAsync = asyncToken && (asyncToken.range[1] === firstTokenOfParam.range[0]);
|
101
|
-
|
102
|
-
return fixer.replaceTextRange([
|
103
|
-
firstTokenOfParam.range[0],
|
104
|
-
closingParenToken.range[1]
|
105
|
-
], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`);
|
80
|
+
function findOpeningParenOfParams(node) {
|
81
|
+
const tokenBeforeParams = sourceCode.getTokenBefore(node.params[0]);
|
82
|
+
|
83
|
+
if (
|
84
|
+
tokenBeforeParams &&
|
85
|
+
astUtils.isOpeningParenToken(tokenBeforeParams) &&
|
86
|
+
node.range[0] <= tokenBeforeParams.range[0]
|
87
|
+
) {
|
88
|
+
return tokenBeforeParams;
|
106
89
|
}
|
107
90
|
|
108
|
-
|
109
|
-
|
110
|
-
* @returns {boolean} `true` if there are comments inside of parens, else `false`
|
111
|
-
*/
|
112
|
-
function hasCommentsInParens() {
|
113
|
-
if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
114
|
-
const closingParenToken = sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
|
91
|
+
return null;
|
92
|
+
}
|
115
93
|
|
116
|
-
|
117
|
-
|
118
|
-
|
94
|
+
/**
|
95
|
+
* Finds closing paren of parameters for the given arrow function.
|
96
|
+
* It is assumed that the given arrow function has parens of parameters and that it has exactly one parameter.
|
97
|
+
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
98
|
+
* @returns {Token} the closing paren of parameters.
|
99
|
+
*/
|
100
|
+
function getClosingParenOfParams(node) {
|
101
|
+
return sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
|
102
|
+
}
|
119
103
|
|
120
|
-
|
104
|
+
/**
|
105
|
+
* Determines whether the given arrow function has comments inside parens of parameters.
|
106
|
+
* It is assumed that the given arrow function has parens of parameters.
|
107
|
+
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
108
|
+
* @param {Token} openingParen Opening paren of parameters.
|
109
|
+
* @returns {boolean} `true` if the function has at least one comment inside of parens of parameters.
|
110
|
+
*/
|
111
|
+
function hasCommentsInParensOfParams(node, openingParen) {
|
112
|
+
return sourceCode.commentsExistBetween(openingParen, getClosingParenOfParams(node));
|
113
|
+
}
|
121
114
|
|
122
|
-
|
123
|
-
|
124
|
-
|
115
|
+
/**
|
116
|
+
* Determines whether the given arrow function has unexpected tokens before opening paren of parameters,
|
117
|
+
* in which case it will be assumed that the existing parens of parameters are necessary.
|
118
|
+
* Only tokens within the range of the arrow function (tokens that are part of the arrow function) are taken into account.
|
119
|
+
* Example: <T>(a) => b
|
120
|
+
* @param {ASTNode} node `ArrowFunctionExpression` node.
|
121
|
+
* @param {Token} openingParen Opening paren of parameters.
|
122
|
+
* @returns {boolean} `true` if the function has at least one unexpected token.
|
123
|
+
*/
|
124
|
+
function hasUnexpectedTokensBeforeOpeningParen(node, openingParen) {
|
125
|
+
const expectedCount = node.async ? 1 : 0;
|
125
126
|
|
126
|
-
|
127
|
-
|
128
|
-
requireForBlockBody &&
|
129
|
-
node.params[0].type === "Identifier" &&
|
130
|
-
!node.params[0].typeAnnotation &&
|
131
|
-
node.body.type !== "BlockStatement" &&
|
132
|
-
!node.returnType
|
133
|
-
) {
|
134
|
-
if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
135
|
-
context.report({
|
136
|
-
node,
|
137
|
-
messageId: "unexpectedParensInline",
|
138
|
-
loc: getLocation(node),
|
139
|
-
fix: fixParamsWithParenthesis
|
140
|
-
});
|
141
|
-
}
|
142
|
-
return;
|
143
|
-
}
|
127
|
+
return sourceCode.getFirstToken(node, { skip: expectedCount }) !== openingParen;
|
128
|
+
}
|
144
129
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
node,
|
152
|
-
messageId: "expectedParensBlock",
|
153
|
-
loc: getLocation(node),
|
154
|
-
fix(fixer) {
|
155
|
-
return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
|
156
|
-
}
|
157
|
-
});
|
158
|
-
}
|
159
|
-
return;
|
160
|
-
}
|
130
|
+
return {
|
131
|
+
"ArrowFunctionExpression[params.length=1]"(node) {
|
132
|
+
const shouldHaveParens = !asNeeded || requireForBlockBody && hasBlockBody(node);
|
133
|
+
const openingParen = findOpeningParenOfParams(node);
|
134
|
+
const hasParens = openingParen !== null;
|
135
|
+
const [param] = node.params;
|
161
136
|
|
162
|
-
|
163
|
-
if (asNeeded &&
|
164
|
-
node.params[0].type === "Identifier" &&
|
165
|
-
!node.params[0].typeAnnotation &&
|
166
|
-
!node.returnType
|
167
|
-
) {
|
168
|
-
if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
137
|
+
if (shouldHaveParens && !hasParens) {
|
169
138
|
context.report({
|
170
139
|
node,
|
171
|
-
messageId: "
|
172
|
-
loc:
|
173
|
-
fix
|
140
|
+
messageId: requireForBlockBody ? "expectedParensBlock" : "expectedParens",
|
141
|
+
loc: param.loc,
|
142
|
+
*fix(fixer) {
|
143
|
+
yield fixer.insertTextBefore(param, "(");
|
144
|
+
yield fixer.insertTextAfter(param, ")");
|
145
|
+
}
|
174
146
|
});
|
175
147
|
}
|
176
|
-
return;
|
177
|
-
}
|
178
148
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
149
|
+
if (
|
150
|
+
!shouldHaveParens &&
|
151
|
+
hasParens &&
|
152
|
+
param.type === "Identifier" &&
|
153
|
+
!param.typeAnnotation &&
|
154
|
+
!node.returnType &&
|
155
|
+
!hasCommentsInParensOfParams(node, openingParen) &&
|
156
|
+
!hasUnexpectedTokensBeforeOpeningParen(node, openingParen)
|
157
|
+
) {
|
184
158
|
context.report({
|
185
159
|
node,
|
186
|
-
messageId: "
|
187
|
-
loc:
|
188
|
-
fix(fixer) {
|
189
|
-
|
160
|
+
messageId: requireForBlockBody ? "unexpectedParensInline" : "unexpectedParens",
|
161
|
+
loc: param.loc,
|
162
|
+
*fix(fixer) {
|
163
|
+
const tokenBeforeOpeningParen = sourceCode.getTokenBefore(openingParen);
|
164
|
+
const closingParen = getClosingParenOfParams(node);
|
165
|
+
|
166
|
+
if (
|
167
|
+
tokenBeforeOpeningParen &&
|
168
|
+
tokenBeforeOpeningParen.range[1] === openingParen.range[0] &&
|
169
|
+
!astUtils.canTokensBeAdjacent(tokenBeforeOpeningParen, sourceCode.getFirstToken(param))
|
170
|
+
) {
|
171
|
+
yield fixer.insertTextBefore(openingParen, " ");
|
172
|
+
}
|
173
|
+
|
174
|
+
// remove parens, whitespace inside parens, and possible trailing comma
|
175
|
+
yield fixer.removeRange([openingParen.range[0], param.range[0]]);
|
176
|
+
yield fixer.removeRange([param.range[1], closingParen.range[1]]);
|
190
177
|
}
|
191
178
|
});
|
192
179
|
}
|
193
180
|
}
|
194
|
-
}
|
195
|
-
|
196
|
-
return {
|
197
|
-
"ArrowFunctionExpression[params.length=1]": parens
|
198
181
|
};
|
199
182
|
}
|
200
183
|
};
|
package/lib/rules/camelcase.js
CHANGED
@@ -32,6 +32,10 @@ module.exports = {
|
|
32
32
|
type: "boolean",
|
33
33
|
default: false
|
34
34
|
},
|
35
|
+
ignoreGlobals: {
|
36
|
+
type: "boolean",
|
37
|
+
default: false
|
38
|
+
},
|
35
39
|
properties: {
|
36
40
|
enum: ["always", "never"]
|
37
41
|
},
|
@@ -61,8 +65,11 @@ module.exports = {
|
|
61
65
|
let properties = options.properties || "";
|
62
66
|
const ignoreDestructuring = options.ignoreDestructuring;
|
63
67
|
const ignoreImports = options.ignoreImports;
|
68
|
+
const ignoreGlobals = options.ignoreGlobals;
|
64
69
|
const allow = options.allow || [];
|
65
70
|
|
71
|
+
let globalScope;
|
72
|
+
|
66
73
|
if (properties !== "always" && properties !== "never") {
|
67
74
|
properties = "always";
|
68
75
|
}
|
@@ -159,6 +166,37 @@ module.exports = {
|
|
159
166
|
return false;
|
160
167
|
}
|
161
168
|
|
169
|
+
/**
|
170
|
+
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
171
|
+
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
172
|
+
* @param {ASTNode} node `Identifier` node to check.
|
173
|
+
* @returns {boolean} `true` if the node is a reference to a global variable.
|
174
|
+
*/
|
175
|
+
function isReferenceToGlobalVariable(node) {
|
176
|
+
const variable = globalScope.set.get(node.name);
|
177
|
+
|
178
|
+
return variable && variable.defs.length === 0 &&
|
179
|
+
variable.references.some(ref => ref.identifier === node);
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* Checks whether the given node represents a reference to a property of an object in an object literal expression.
|
184
|
+
* This allows to differentiate between a global variable that is allowed to be used as a reference, and the key
|
185
|
+
* of the expressed object (which shouldn't be allowed).
|
186
|
+
* @param {ASTNode} node `Identifier` node to check.
|
187
|
+
* @returns {boolean} `true` if the node is a property name of an object literal expression
|
188
|
+
*/
|
189
|
+
function isPropertyNameInObjectLiteral(node) {
|
190
|
+
const parent = node.parent;
|
191
|
+
|
192
|
+
return (
|
193
|
+
parent.type === "Property" &&
|
194
|
+
parent.parent.type === "ObjectExpression" &&
|
195
|
+
!parent.computed &&
|
196
|
+
parent.key === node
|
197
|
+
);
|
198
|
+
}
|
199
|
+
|
162
200
|
/**
|
163
201
|
* Reports an AST node as a rule violation.
|
164
202
|
* @param {ASTNode} node The node to report.
|
@@ -174,6 +212,10 @@ module.exports = {
|
|
174
212
|
|
175
213
|
return {
|
176
214
|
|
215
|
+
Program() {
|
216
|
+
globalScope = context.getScope();
|
217
|
+
},
|
218
|
+
|
177
219
|
Identifier(node) {
|
178
220
|
|
179
221
|
/*
|
@@ -189,6 +231,11 @@ module.exports = {
|
|
189
231
|
return;
|
190
232
|
}
|
191
233
|
|
234
|
+
// Check if it's a global variable
|
235
|
+
if (ignoreGlobals && isReferenceToGlobalVariable(node) && !isPropertyNameInObjectLiteral(node)) {
|
236
|
+
return;
|
237
|
+
}
|
238
|
+
|
192
239
|
// MemberExpressions get special rules
|
193
240
|
if (node.parent.type === "MemberExpression") {
|
194
241
|
|
@@ -9,23 +9,12 @@
|
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
11
|
const lodash = require("lodash");
|
12
|
-
|
13
12
|
const astUtils = require("./utils/ast-utils");
|
14
13
|
|
15
14
|
//------------------------------------------------------------------------------
|
16
15
|
// Helpers
|
17
16
|
//------------------------------------------------------------------------------
|
18
17
|
|
19
|
-
/**
|
20
|
-
* Checks whether or not a given node is an `Identifier` node which was named a given name.
|
21
|
-
* @param {ASTNode} node A node to check.
|
22
|
-
* @param {string} name An expected name of the node.
|
23
|
-
* @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
|
24
|
-
*/
|
25
|
-
function isIdentifier(node, name) {
|
26
|
-
return node.type === "Identifier" && node.name === name;
|
27
|
-
}
|
28
|
-
|
29
18
|
/**
|
30
19
|
* Checks whether or not a given code path segment is unreachable.
|
31
20
|
* @param {CodePathSegment} segment A CodePathSegment to check.
|
@@ -165,7 +154,7 @@ module.exports = {
|
|
165
154
|
let hasReturnValue = Boolean(argument);
|
166
155
|
|
167
156
|
if (treatUndefinedAsUnspecified && hasReturnValue) {
|
168
|
-
hasReturnValue = !
|
157
|
+
hasReturnValue = !astUtils.isSpecificId(argument, "undefined") && argument.operator !== "void";
|
169
158
|
}
|
170
159
|
|
171
160
|
if (!funcInfo.hasReturn) {
|