eslint 7.0.0-alpha.1 → 7.0.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 +329 -0
- package/README.md +7 -7
- package/bin/eslint.js +115 -77
- package/conf/category-list.json +2 -3
- package/conf/environments.js +2 -1
- package/conf/eslint-recommended.js +3 -0
- package/lib/api.js +2 -0
- package/lib/cli-engine/cascading-config-array-factory.js +16 -2
- package/lib/cli-engine/cli-engine.js +53 -47
- package/lib/cli-engine/config-array/config-array.js +30 -1
- package/lib/cli-engine/config-array/ignore-pattern.js +7 -1
- package/lib/cli-engine/config-array-factory.js +244 -235
- package/lib/cli.js +181 -95
- package/lib/eslint/eslint.js +656 -0
- package/lib/eslint/index.js +7 -0
- package/lib/init/autoconfig.js +4 -4
- package/lib/init/config-file.js +2 -2
- package/lib/init/config-initializer.js +2 -4
- package/lib/init/source-code-utils.js +2 -2
- package/lib/linter/node-event-generator.js +2 -2
- package/lib/options.js +0 -1
- package/lib/rule-tester/rule-tester.js +178 -23
- package/lib/rules/accessor-pairs.js +2 -2
- package/lib/rules/array-callback-return.js +3 -18
- package/lib/rules/arrow-body-style.js +26 -15
- package/lib/rules/callback-return.js +4 -0
- package/lib/rules/camelcase.js +38 -1
- package/lib/rules/comma-style.js +3 -8
- package/lib/rules/computed-property-spacing.js +2 -2
- package/lib/rules/curly.js +124 -40
- package/lib/rules/func-call-spacing.js +4 -3
- package/lib/rules/func-names.js +31 -24
- package/lib/rules/getter-return.js +2 -12
- package/lib/rules/global-require.js +4 -0
- package/lib/rules/handle-callback-err.js +4 -0
- package/lib/rules/id-blacklist.js +140 -64
- package/lib/rules/id-length.js +14 -4
- package/lib/rules/indent-legacy.js +0 -16
- package/lib/rules/key-spacing.js +1 -1
- package/lib/rules/new-cap.js +1 -1
- package/lib/rules/newline-per-chained-call.js +6 -3
- package/lib/rules/no-alert.js +5 -3
- package/lib/rules/no-buffer-constructor.js +4 -0
- package/lib/rules/no-dupe-else-if.js +1 -1
- package/lib/rules/no-empty-function.js +4 -2
- package/lib/rules/no-eval.js +3 -2
- package/lib/rules/no-extra-bind.js +1 -1
- package/lib/rules/no-extra-boolean-cast.js +168 -38
- package/lib/rules/no-extra-parens.js +9 -5
- package/lib/rules/no-implied-eval.js +83 -101
- package/lib/rules/no-import-assign.js +1 -1
- package/lib/rules/no-inner-declarations.js +31 -39
- package/lib/rules/no-lone-blocks.js +1 -1
- package/lib/rules/no-magic-numbers.js +72 -37
- package/lib/rules/no-mixed-requires.js +4 -0
- package/lib/rules/no-new-object.js +15 -3
- package/lib/rules/no-new-require.js +4 -0
- package/lib/rules/no-new-wrappers.js +1 -1
- package/lib/rules/no-obj-calls.js +24 -5
- package/lib/rules/no-path-concat.js +4 -0
- package/lib/rules/no-plusplus.js +39 -3
- package/lib/rules/no-process-env.js +4 -0
- package/lib/rules/no-process-exit.js +4 -0
- package/lib/rules/no-prototype-builtins.js +1 -1
- package/lib/rules/no-restricted-modules.js +52 -18
- package/lib/rules/no-setter-return.js +1 -1
- package/lib/rules/no-sync.js +4 -0
- package/lib/rules/no-underscore-dangle.js +1 -1
- package/lib/rules/no-unexpected-multiline.js +22 -12
- package/lib/rules/no-useless-concat.js +1 -1
- package/lib/rules/operator-assignment.js +3 -3
- package/lib/rules/operator-linebreak.js +4 -16
- package/lib/rules/prefer-numeric-literals.js +3 -3
- package/lib/rules/prefer-object-spread.js +2 -2
- package/lib/rules/require-await.js +1 -1
- package/lib/rules/space-before-function-paren.js +5 -2
- package/lib/rules/template-curly-spacing.js +59 -42
- package/lib/rules/utils/ast-utils.js +65 -4
- package/lib/rules/wrap-iife.js +54 -17
- package/lib/rules/yoda.js +101 -51
- package/lib/shared/relative-module-resolver.js +1 -0
- package/lib/shared/types.js +9 -2
- package/messages/plugin-conflict.txt +7 -0
- package/package.json +27 -26
package/lib/rules/curly.js
CHANGED
@@ -141,33 +141,14 @@ module.exports = {
|
|
141
141
|
}
|
142
142
|
|
143
143
|
/**
|
144
|
-
*
|
145
|
-
*
|
146
|
-
*
|
147
|
-
* 1. The given node has the `alternate` node.
|
148
|
-
* 2. There is a `IfStatement` which doesn't have `alternate` node in the
|
149
|
-
* trailing statement chain of the `consequent` node.
|
150
|
-
* @param {ASTNode} node A IfStatement node to check.
|
151
|
-
* @returns {boolean} `true` if the node requires braces of the consequent chunk.
|
144
|
+
* Determines whether the given node has an `else` keyword token as the first token after.
|
145
|
+
* @param {ASTNode} node The node to check.
|
146
|
+
* @returns {boolean} `true` if the node is followed by an `else` keyword token.
|
152
147
|
*/
|
153
|
-
function
|
154
|
-
|
155
|
-
if (node.consequent.body.length >= 2) {
|
156
|
-
return true;
|
157
|
-
}
|
158
|
-
|
159
|
-
for (
|
160
|
-
let currentNode = node.consequent.body[0];
|
161
|
-
currentNode;
|
162
|
-
currentNode = astUtils.getTrailingStatement(currentNode)
|
163
|
-
) {
|
164
|
-
if (currentNode.type === "IfStatement" && !currentNode.alternate) {
|
165
|
-
return true;
|
166
|
-
}
|
167
|
-
}
|
168
|
-
}
|
148
|
+
function isFollowedByElseKeyword(node) {
|
149
|
+
const nextToken = sourceCode.getTokenAfter(node);
|
169
150
|
|
170
|
-
return
|
151
|
+
return Boolean(nextToken) && isElseKeywordToken(nextToken);
|
171
152
|
}
|
172
153
|
|
173
154
|
/**
|
@@ -224,6 +205,110 @@ module.exports = {
|
|
224
205
|
return false;
|
225
206
|
}
|
226
207
|
|
208
|
+
/**
|
209
|
+
* Determines whether the code represented by the given node contains an `if` statement
|
210
|
+
* that would become associated with an `else` keyword directly appended to that code.
|
211
|
+
*
|
212
|
+
* Examples where it returns `true`:
|
213
|
+
*
|
214
|
+
* if (a)
|
215
|
+
* foo();
|
216
|
+
*
|
217
|
+
* if (a) {
|
218
|
+
* foo();
|
219
|
+
* }
|
220
|
+
*
|
221
|
+
* if (a)
|
222
|
+
* foo();
|
223
|
+
* else if (b)
|
224
|
+
* bar();
|
225
|
+
*
|
226
|
+
* while (a)
|
227
|
+
* if (b)
|
228
|
+
* if(c)
|
229
|
+
* foo();
|
230
|
+
* else
|
231
|
+
* bar();
|
232
|
+
*
|
233
|
+
* Examples where it returns `false`:
|
234
|
+
*
|
235
|
+
* if (a)
|
236
|
+
* foo();
|
237
|
+
* else
|
238
|
+
* bar();
|
239
|
+
*
|
240
|
+
* while (a) {
|
241
|
+
* if (b)
|
242
|
+
* if(c)
|
243
|
+
* foo();
|
244
|
+
* else
|
245
|
+
* bar();
|
246
|
+
* }
|
247
|
+
*
|
248
|
+
* while (a)
|
249
|
+
* if (b) {
|
250
|
+
* if(c)
|
251
|
+
* foo();
|
252
|
+
* }
|
253
|
+
* else
|
254
|
+
* bar();
|
255
|
+
* @param {ASTNode} node Node representing the code to check.
|
256
|
+
* @returns {boolean} `true` if an `if` statement within the code would become associated with an `else` appended to that code.
|
257
|
+
*/
|
258
|
+
function hasUnsafeIf(node) {
|
259
|
+
switch (node.type) {
|
260
|
+
case "IfStatement":
|
261
|
+
if (!node.alternate) {
|
262
|
+
return true;
|
263
|
+
}
|
264
|
+
return hasUnsafeIf(node.alternate);
|
265
|
+
case "ForStatement":
|
266
|
+
case "ForInStatement":
|
267
|
+
case "ForOfStatement":
|
268
|
+
case "LabeledStatement":
|
269
|
+
case "WithStatement":
|
270
|
+
case "WhileStatement":
|
271
|
+
return hasUnsafeIf(node.body);
|
272
|
+
default:
|
273
|
+
return false;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
/**
|
278
|
+
* Determines whether the existing curly braces around the single statement are necessary to preserve the semantics of the code.
|
279
|
+
* The braces, which make the given block body, are necessary in either of the following situations:
|
280
|
+
*
|
281
|
+
* 1. The statement is a lexical declaration.
|
282
|
+
* 2. Without the braces, an `if` within the statement would become associated with an `else` after the closing brace:
|
283
|
+
*
|
284
|
+
* if (a) {
|
285
|
+
* if (b)
|
286
|
+
* foo();
|
287
|
+
* }
|
288
|
+
* else
|
289
|
+
* bar();
|
290
|
+
*
|
291
|
+
* if (a)
|
292
|
+
* while (b)
|
293
|
+
* while (c) {
|
294
|
+
* while (d)
|
295
|
+
* if (e)
|
296
|
+
* while(f)
|
297
|
+
* foo();
|
298
|
+
* }
|
299
|
+
* else
|
300
|
+
* bar();
|
301
|
+
* @param {ASTNode} node `BlockStatement` body with exactly one statement directly inside. The statement can have its own nested statements.
|
302
|
+
* @returns {boolean} `true` if the braces are necessary - removing them (replacing the given `BlockStatement` body with its single statement content)
|
303
|
+
* would change the semantics of the code or produce a syntax error.
|
304
|
+
*/
|
305
|
+
function areBracesNecessary(node) {
|
306
|
+
const statement = node.body[0];
|
307
|
+
|
308
|
+
return isLexicalDeclaration(statement) ||
|
309
|
+
hasUnsafeIf(statement) && isFollowedByElseKeyword(node);
|
310
|
+
}
|
311
|
+
|
227
312
|
/**
|
228
313
|
* Prepares to check the body of a node to see if it's a block statement.
|
229
314
|
* @param {ASTNode} node The node to report if there's a problem.
|
@@ -242,30 +327,29 @@ module.exports = {
|
|
242
327
|
const hasBlock = (body.type === "BlockStatement");
|
243
328
|
let expected = null;
|
244
329
|
|
245
|
-
if (
|
330
|
+
if (hasBlock && (body.body.length !== 1 || areBracesNecessary(body))) {
|
246
331
|
expected = true;
|
247
332
|
} else if (multiOnly) {
|
248
|
-
|
249
|
-
expected = false;
|
250
|
-
}
|
333
|
+
expected = false;
|
251
334
|
} else if (multiLine) {
|
252
335
|
if (!isCollapsedOneLiner(body)) {
|
253
336
|
expected = true;
|
254
337
|
}
|
338
|
+
|
339
|
+
// otherwise, the body is allowed to have braces or not to have braces
|
340
|
+
|
255
341
|
} else if (multiOrNest) {
|
256
|
-
if (hasBlock
|
257
|
-
const
|
258
|
-
const
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
expected = leadingComments.length > 0;
|
264
|
-
}
|
265
|
-
} else if (!isOneLiner(body)) {
|
266
|
-
expected = true;
|
342
|
+
if (hasBlock) {
|
343
|
+
const statement = body.body[0];
|
344
|
+
const leadingCommentsInBlock = sourceCode.getCommentsBefore(statement);
|
345
|
+
|
346
|
+
expected = !isOneLiner(statement) || leadingCommentsInBlock.length > 0;
|
347
|
+
} else {
|
348
|
+
expected = !isOneLiner(body);
|
267
349
|
}
|
268
350
|
} else {
|
351
|
+
|
352
|
+
// default "all"
|
269
353
|
expected = true;
|
270
354
|
}
|
271
355
|
|
@@ -63,7 +63,8 @@ module.exports = {
|
|
63
63
|
},
|
64
64
|
|
65
65
|
messages: {
|
66
|
-
|
66
|
+
unexpectedWhitespace: "Unexpected whitespace between function name and paren.",
|
67
|
+
unexpectedNewline: "Unexpected newline between function name and paren.",
|
67
68
|
missing: "Missing space between function name and paren."
|
68
69
|
}
|
69
70
|
},
|
@@ -116,7 +117,7 @@ module.exports = {
|
|
116
117
|
context.report({
|
117
118
|
node,
|
118
119
|
loc: leftToken.loc.start,
|
119
|
-
messageId: "
|
120
|
+
messageId: "unexpectedWhitespace",
|
120
121
|
fix(fixer) {
|
121
122
|
|
122
123
|
/*
|
@@ -143,7 +144,7 @@ module.exports = {
|
|
143
144
|
context.report({
|
144
145
|
node,
|
145
146
|
loc: leftToken.loc.start,
|
146
|
-
messageId: "
|
147
|
+
messageId: "unexpectedNewline",
|
147
148
|
fix(fixer) {
|
148
149
|
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
|
149
150
|
}
|
package/lib/rules/func-names.js
CHANGED
@@ -119,8 +119,7 @@ module.exports = {
|
|
119
119
|
(parent.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.init === node) ||
|
120
120
|
(parent.type === "Property" && parent.value === node) ||
|
121
121
|
(parent.type === "AssignmentExpression" && parent.left.type === "Identifier" && parent.right === node) ||
|
122
|
-
(parent.type === "
|
123
|
-
(parent.type === "AssignmentPattern" && parent.right === node);
|
122
|
+
(parent.type === "AssignmentPattern" && parent.left.type === "Identifier" && parent.right === node);
|
124
123
|
}
|
125
124
|
|
126
125
|
/**
|
@@ -151,33 +150,41 @@ module.exports = {
|
|
151
150
|
});
|
152
151
|
}
|
153
152
|
|
154
|
-
|
155
|
-
|
153
|
+
/**
|
154
|
+
* The listener for function nodes.
|
155
|
+
* @param {ASTNode} node function node
|
156
|
+
* @returns {void}
|
157
|
+
*/
|
158
|
+
function handleFunction(node) {
|
156
159
|
|
157
|
-
|
158
|
-
|
160
|
+
// Skip recursive functions.
|
161
|
+
const nameVar = context.getDeclaredVariables(node)[0];
|
159
162
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
+
if (isFunctionName(nameVar) && nameVar.references.length > 0) {
|
164
|
+
return;
|
165
|
+
}
|
163
166
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
}
|
167
|
+
const hasName = Boolean(node.id && node.id.name);
|
168
|
+
const config = getConfigForNode(node);
|
169
|
+
|
170
|
+
if (config === "never") {
|
171
|
+
if (hasName && node.type !== "FunctionDeclaration") {
|
172
|
+
reportUnexpectedNamedFunction(node);
|
173
|
+
}
|
174
|
+
} else if (config === "as-needed") {
|
175
|
+
if (!hasName && !hasInferredName(node)) {
|
176
|
+
reportUnexpectedUnnamedFunction(node);
|
177
|
+
}
|
178
|
+
} else {
|
179
|
+
if (!hasName && !isObjectOrClassMethod(node)) {
|
180
|
+
reportUnexpectedUnnamedFunction(node);
|
179
181
|
}
|
180
182
|
}
|
183
|
+
}
|
184
|
+
|
185
|
+
return {
|
186
|
+
"FunctionExpression:exit": handleFunction,
|
187
|
+
"ExportDefaultDeclaration > FunctionDeclaration": handleFunction
|
181
188
|
};
|
182
189
|
}
|
183
190
|
};
|
@@ -25,17 +25,6 @@ function isReachable(segment) {
|
|
25
25
|
return segment.reachable;
|
26
26
|
}
|
27
27
|
|
28
|
-
/**
|
29
|
-
* Gets a readable location.
|
30
|
-
*
|
31
|
-
* - FunctionExpression -> the function name or `function` keyword.
|
32
|
-
* @param {ASTNode} node A function node to get.
|
33
|
-
* @returns {ASTNode|Token} The node or the token of a location.
|
34
|
-
*/
|
35
|
-
function getId(node) {
|
36
|
-
return node.id || node;
|
37
|
-
}
|
38
|
-
|
39
28
|
//------------------------------------------------------------------------------
|
40
29
|
// Rule Definition
|
41
30
|
//------------------------------------------------------------------------------
|
@@ -75,6 +64,7 @@ module.exports = {
|
|
75
64
|
create(context) {
|
76
65
|
|
77
66
|
const options = context.options[0] || { allowImplicit: false };
|
67
|
+
const sourceCode = context.getSourceCode();
|
78
68
|
|
79
69
|
let funcInfo = {
|
80
70
|
upper: null,
|
@@ -99,7 +89,7 @@ module.exports = {
|
|
99
89
|
) {
|
100
90
|
context.report({
|
101
91
|
node,
|
102
|
-
loc:
|
92
|
+
loc: astUtils.getFunctionHeadLoc(node, sourceCode),
|
103
93
|
messageId: funcInfo.hasReturn ? "expectedAlways" : "expected",
|
104
94
|
data: {
|
105
95
|
name: astUtils.getFunctionNameWithKind(funcInfo.node)
|
@@ -6,6 +6,105 @@
|
|
6
6
|
|
7
7
|
"use strict";
|
8
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
|
+
|
9
108
|
//------------------------------------------------------------------------------
|
10
109
|
// Rule Definition
|
11
110
|
//------------------------------------------------------------------------------
|
@@ -35,66 +134,64 @@ module.exports = {
|
|
35
134
|
|
36
135
|
create(context) {
|
37
136
|
|
38
|
-
|
39
|
-
//--------------------------------------------------------------------------
|
40
|
-
// Helpers
|
41
|
-
//--------------------------------------------------------------------------
|
42
|
-
|
43
|
-
const blacklist = context.options;
|
137
|
+
const blacklist = new Set(context.options);
|
44
138
|
const reportedNodes = new Set();
|
45
139
|
|
140
|
+
let globalScope;
|
46
141
|
|
47
142
|
/**
|
48
|
-
* Checks
|
49
|
-
* @param {string} name The
|
50
|
-
* @returns {boolean} if the
|
143
|
+
* Checks whether the given name is blacklisted.
|
144
|
+
* @param {string} name The name to check.
|
145
|
+
* @returns {boolean} `true` if the name is blacklisted.
|
51
146
|
* @private
|
52
147
|
*/
|
53
|
-
function
|
54
|
-
return blacklist.
|
148
|
+
function isBlacklisted(name) {
|
149
|
+
return blacklist.has(name);
|
55
150
|
}
|
56
151
|
|
57
152
|
/**
|
58
|
-
* Checks whether the given node represents
|
59
|
-
*
|
60
|
-
* Examples:
|
61
|
-
* import { a as b } from 'mod'; // node `a` is renamed import
|
62
|
-
* export { a as b } from 'mod'; // node `a` is renamed import
|
153
|
+
* Checks whether the given node represents a reference to a global variable that is not declared in the source code.
|
154
|
+
* These identifiers will be allowed, as it is assumed that user has no control over the names of external global variables.
|
63
155
|
* @param {ASTNode} node `Identifier` node to check.
|
64
|
-
* @returns {boolean} `true` if the node is a
|
156
|
+
* @returns {boolean} `true` if the node is a reference to a global variable.
|
65
157
|
*/
|
66
|
-
function
|
67
|
-
const
|
158
|
+
function isReferenceToGlobalVariable(node) {
|
159
|
+
const variable = globalScope.set.get(node.name);
|
68
160
|
|
69
|
-
return
|
70
|
-
(
|
71
|
-
parent.type === "ImportSpecifier" &&
|
72
|
-
parent.imported !== parent.local &&
|
73
|
-
parent.imported === node
|
74
|
-
) ||
|
75
|
-
(
|
76
|
-
parent.type === "ExportSpecifier" &&
|
77
|
-
parent.parent.source && // re-export
|
78
|
-
parent.local !== parent.exported &&
|
79
|
-
parent.local === node
|
80
|
-
)
|
81
|
-
);
|
161
|
+
return variable && variable.defs.length === 0 &&
|
162
|
+
variable.references.some(ref => ref.identifier === node);
|
82
163
|
}
|
83
164
|
|
84
165
|
/**
|
85
|
-
*
|
86
|
-
* @param {ASTNode} node
|
87
|
-
* @returns {boolean}
|
166
|
+
* Determines whether the given node should be checked.
|
167
|
+
* @param {ASTNode} node `Identifier` node.
|
168
|
+
* @returns {boolean} `true` if the node should be checked.
|
88
169
|
*/
|
89
|
-
function
|
170
|
+
function shouldCheck(node) {
|
90
171
|
const parent = node.parent;
|
91
172
|
|
173
|
+
/*
|
174
|
+
* Member access has special rules for checking property names.
|
175
|
+
* Read access to a property with a blacklisted name is allowed, because it can be on an object that user has no control over.
|
176
|
+
* Write access isn't allowed, because it potentially creates a new property with a blacklisted name.
|
177
|
+
*/
|
178
|
+
if (
|
179
|
+
parent.type === "MemberExpression" &&
|
180
|
+
parent.property === node &&
|
181
|
+
!parent.computed
|
182
|
+
) {
|
183
|
+
return isAssignmentTarget(parent);
|
184
|
+
}
|
185
|
+
|
92
186
|
return (
|
93
187
|
parent.type !== "CallExpression" &&
|
94
188
|
parent.type !== "NewExpression" &&
|
95
|
-
parent.parent.type !== "ObjectPattern" &&
|
96
189
|
!isRenamedImport(node) &&
|
97
|
-
|
190
|
+
!isRenamedInDestructuring(node) &&
|
191
|
+
!(
|
192
|
+
isReferenceToGlobalVariable(node) &&
|
193
|
+
!isShorthandPropertyDefinition(node)
|
194
|
+
)
|
98
195
|
);
|
99
196
|
}
|
100
197
|
|
@@ -119,36 +216,15 @@ module.exports = {
|
|
119
216
|
|
120
217
|
return {
|
121
218
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
if (node.parent.type === "MemberExpression") {
|
126
|
-
const name = node.name,
|
127
|
-
effectiveParent = node.parent.parent;
|
128
|
-
|
129
|
-
// Always check object names
|
130
|
-
if (node.parent.object.type === "Identifier" &&
|
131
|
-
node.parent.object.name === name) {
|
132
|
-
if (isInvalid(name)) {
|
133
|
-
report(node);
|
134
|
-
}
|
135
|
-
|
136
|
-
// Report AssignmentExpressions only if they are the left side of the assignment
|
137
|
-
} else if (effectiveParent.type === "AssignmentExpression" &&
|
138
|
-
(effectiveParent.right.type !== "MemberExpression" ||
|
139
|
-
effectiveParent.left.type === "MemberExpression" &&
|
140
|
-
effectiveParent.left.property.name === name)) {
|
141
|
-
if (isInvalid(name)) {
|
142
|
-
report(node);
|
143
|
-
}
|
144
|
-
}
|
219
|
+
Program() {
|
220
|
+
globalScope = context.getScope();
|
221
|
+
},
|
145
222
|
|
146
|
-
|
223
|
+
Identifier(node) {
|
224
|
+
if (isBlacklisted(node.name) && shouldCheck(node)) {
|
147
225
|
report(node);
|
148
226
|
}
|
149
227
|
}
|
150
|
-
|
151
228
|
};
|
152
|
-
|
153
229
|
}
|
154
230
|
};
|
package/lib/rules/id-length.js
CHANGED
@@ -63,6 +63,7 @@ module.exports = {
|
|
63
63
|
|
64
64
|
return obj;
|
65
65
|
}, {});
|
66
|
+
const reportedNode = new Set();
|
66
67
|
|
67
68
|
const SUPPORTED_EXPRESSIONS = {
|
68
69
|
MemberExpression: properties && function(parent) {
|
@@ -82,8 +83,15 @@ module.exports = {
|
|
82
83
|
VariableDeclarator(parent, node) {
|
83
84
|
return parent.id === node;
|
84
85
|
},
|
85
|
-
Property
|
86
|
-
|
86
|
+
Property(parent, node) {
|
87
|
+
|
88
|
+
if (parent.parent.type === "ObjectPattern") {
|
89
|
+
return (
|
90
|
+
parent.value !== parent.key && parent.value === node ||
|
91
|
+
parent.value === parent.key && parent.key === node && properties
|
92
|
+
);
|
93
|
+
}
|
94
|
+
return properties && !parent.computed && parent.key === node;
|
87
95
|
},
|
88
96
|
ImportDefaultSpecifier: true,
|
89
97
|
RestElement: true,
|
@@ -92,7 +100,8 @@ module.exports = {
|
|
92
100
|
ClassDeclaration: true,
|
93
101
|
FunctionDeclaration: true,
|
94
102
|
MethodDefinition: true,
|
95
|
-
CatchClause: true
|
103
|
+
CatchClause: true,
|
104
|
+
ArrayPattern: true
|
96
105
|
};
|
97
106
|
|
98
107
|
return {
|
@@ -109,7 +118,8 @@ module.exports = {
|
|
109
118
|
|
110
119
|
const isValidExpression = SUPPORTED_EXPRESSIONS[parent.type];
|
111
120
|
|
112
|
-
if (isValidExpression && (isValidExpression === true || isValidExpression(parent, node))) {
|
121
|
+
if (isValidExpression && !reportedNode.has(node) && (isValidExpression === true || isValidExpression(parent, node))) {
|
122
|
+
reportedNode.add(node);
|
113
123
|
context.report({
|
114
124
|
node,
|
115
125
|
messageId: isShort ? "tooShort" : "tooLong",
|