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.
Files changed (84) hide show
  1. package/CHANGELOG.md +329 -0
  2. package/README.md +7 -7
  3. package/bin/eslint.js +115 -77
  4. package/conf/category-list.json +2 -3
  5. package/conf/environments.js +2 -1
  6. package/conf/eslint-recommended.js +3 -0
  7. package/lib/api.js +2 -0
  8. package/lib/cli-engine/cascading-config-array-factory.js +16 -2
  9. package/lib/cli-engine/cli-engine.js +53 -47
  10. package/lib/cli-engine/config-array/config-array.js +30 -1
  11. package/lib/cli-engine/config-array/ignore-pattern.js +7 -1
  12. package/lib/cli-engine/config-array-factory.js +244 -235
  13. package/lib/cli.js +181 -95
  14. package/lib/eslint/eslint.js +656 -0
  15. package/lib/eslint/index.js +7 -0
  16. package/lib/init/autoconfig.js +4 -4
  17. package/lib/init/config-file.js +2 -2
  18. package/lib/init/config-initializer.js +2 -4
  19. package/lib/init/source-code-utils.js +2 -2
  20. package/lib/linter/node-event-generator.js +2 -2
  21. package/lib/options.js +0 -1
  22. package/lib/rule-tester/rule-tester.js +178 -23
  23. package/lib/rules/accessor-pairs.js +2 -2
  24. package/lib/rules/array-callback-return.js +3 -18
  25. package/lib/rules/arrow-body-style.js +26 -15
  26. package/lib/rules/callback-return.js +4 -0
  27. package/lib/rules/camelcase.js +38 -1
  28. package/lib/rules/comma-style.js +3 -8
  29. package/lib/rules/computed-property-spacing.js +2 -2
  30. package/lib/rules/curly.js +124 -40
  31. package/lib/rules/func-call-spacing.js +4 -3
  32. package/lib/rules/func-names.js +31 -24
  33. package/lib/rules/getter-return.js +2 -12
  34. package/lib/rules/global-require.js +4 -0
  35. package/lib/rules/handle-callback-err.js +4 -0
  36. package/lib/rules/id-blacklist.js +140 -64
  37. package/lib/rules/id-length.js +14 -4
  38. package/lib/rules/indent-legacy.js +0 -16
  39. package/lib/rules/key-spacing.js +1 -1
  40. package/lib/rules/new-cap.js +1 -1
  41. package/lib/rules/newline-per-chained-call.js +6 -3
  42. package/lib/rules/no-alert.js +5 -3
  43. package/lib/rules/no-buffer-constructor.js +4 -0
  44. package/lib/rules/no-dupe-else-if.js +1 -1
  45. package/lib/rules/no-empty-function.js +4 -2
  46. package/lib/rules/no-eval.js +3 -2
  47. package/lib/rules/no-extra-bind.js +1 -1
  48. package/lib/rules/no-extra-boolean-cast.js +168 -38
  49. package/lib/rules/no-extra-parens.js +9 -5
  50. package/lib/rules/no-implied-eval.js +83 -101
  51. package/lib/rules/no-import-assign.js +1 -1
  52. package/lib/rules/no-inner-declarations.js +31 -39
  53. package/lib/rules/no-lone-blocks.js +1 -1
  54. package/lib/rules/no-magic-numbers.js +72 -37
  55. package/lib/rules/no-mixed-requires.js +4 -0
  56. package/lib/rules/no-new-object.js +15 -3
  57. package/lib/rules/no-new-require.js +4 -0
  58. package/lib/rules/no-new-wrappers.js +1 -1
  59. package/lib/rules/no-obj-calls.js +24 -5
  60. package/lib/rules/no-path-concat.js +4 -0
  61. package/lib/rules/no-plusplus.js +39 -3
  62. package/lib/rules/no-process-env.js +4 -0
  63. package/lib/rules/no-process-exit.js +4 -0
  64. package/lib/rules/no-prototype-builtins.js +1 -1
  65. package/lib/rules/no-restricted-modules.js +52 -18
  66. package/lib/rules/no-setter-return.js +1 -1
  67. package/lib/rules/no-sync.js +4 -0
  68. package/lib/rules/no-underscore-dangle.js +1 -1
  69. package/lib/rules/no-unexpected-multiline.js +22 -12
  70. package/lib/rules/no-useless-concat.js +1 -1
  71. package/lib/rules/operator-assignment.js +3 -3
  72. package/lib/rules/operator-linebreak.js +4 -16
  73. package/lib/rules/prefer-numeric-literals.js +3 -3
  74. package/lib/rules/prefer-object-spread.js +2 -2
  75. package/lib/rules/require-await.js +1 -1
  76. package/lib/rules/space-before-function-paren.js +5 -2
  77. package/lib/rules/template-curly-spacing.js +59 -42
  78. package/lib/rules/utils/ast-utils.js +65 -4
  79. package/lib/rules/wrap-iife.js +54 -17
  80. package/lib/rules/yoda.js +101 -51
  81. package/lib/shared/relative-module-resolver.js +1 -0
  82. package/lib/shared/types.js +9 -2
  83. package/messages/plugin-conflict.txt +7 -0
  84. package/package.json +27 -26
@@ -141,33 +141,14 @@ module.exports = {
141
141
  }
142
142
 
143
143
  /**
144
- * Checks a given IfStatement node requires braces of the consequent chunk.
145
- * This returns `true` when below:
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 requiresBraceOfConsequent(node) {
154
- if (node.alternate && node.consequent.type === "BlockStatement") {
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 false;
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 (node.type === "IfStatement" && node.consequent === body && requiresBraceOfConsequent(node)) {
330
+ if (hasBlock && (body.body.length !== 1 || areBracesNecessary(body))) {
246
331
  expected = true;
247
332
  } else if (multiOnly) {
248
- if (hasBlock && body.body.length === 1 && !isLexicalDeclaration(body.body[0])) {
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 && body.body.length === 1 && isOneLiner(body.body[0])) {
257
- const leadingComments = sourceCode.getCommentsBefore(body.body[0]);
258
- const isLexDef = isLexicalDeclaration(body.body[0]);
259
-
260
- if (isLexDef) {
261
- expected = true;
262
- } else {
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
- unexpected: "Unexpected newline between function name and paren.",
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: "unexpected",
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: "unexpected",
147
+ messageId: "unexpectedNewline",
147
148
  fix(fixer) {
148
149
  return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
149
150
  }
@@ -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 === "ExportDefaultDeclaration" && parent.declaration === node) ||
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
- return {
155
- "FunctionExpression:exit"(node) {
153
+ /**
154
+ * The listener for function nodes.
155
+ * @param {ASTNode} node function node
156
+ * @returns {void}
157
+ */
158
+ function handleFunction(node) {
156
159
 
157
- // Skip recursive functions.
158
- const nameVar = context.getDeclaredVariables(node)[0];
160
+ // Skip recursive functions.
161
+ const nameVar = context.getDeclaredVariables(node)[0];
159
162
 
160
- if (isFunctionName(nameVar) && nameVar.references.length > 0) {
161
- return;
162
- }
163
+ if (isFunctionName(nameVar) && nameVar.references.length > 0) {
164
+ return;
165
+ }
163
166
 
164
- const hasName = Boolean(node.id && node.id.name);
165
- const config = getConfigForNode(node);
166
-
167
- if (config === "never") {
168
- if (hasName) {
169
- reportUnexpectedNamedFunction(node);
170
- }
171
- } else if (config === "as-needed") {
172
- if (!hasName && !hasInferredName(node)) {
173
- reportUnexpectedUnnamedFunction(node);
174
- }
175
- } else {
176
- if (!hasName && !isObjectOrClassMethod(node)) {
177
- reportUnexpectedUnnamedFunction(node);
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: getId(node).loc.start,
92
+ loc: astUtils.getFunctionHeadLoc(node, sourceCode),
103
93
  messageId: funcInfo.hasReturn ? "expectedAlways" : "expected",
104
94
  data: {
105
95
  name: astUtils.getFunctionNameWithKind(funcInfo.node)
@@ -48,6 +48,10 @@ function isShadowed(scope, node) {
48
48
 
49
49
  module.exports = {
50
50
  meta: {
51
+ deprecated: true,
52
+
53
+ replacedBy: ["node/global-require"],
54
+
51
55
  type: "suggestion",
52
56
 
53
57
  docs: {
@@ -11,6 +11,10 @@
11
11
 
12
12
  module.exports = {
13
13
  meta: {
14
+ deprecated: true,
15
+
16
+ replacedBy: ["node/handle-callback-err"],
17
+
14
18
  type: "suggestion",
15
19
 
16
20
  docs: {
@@ -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 if a string matches the provided pattern
49
- * @param {string} name The string to check.
50
- * @returns {boolean} if the string is a match
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 isInvalid(name) {
54
- return blacklist.indexOf(name) !== -1;
148
+ function isBlacklisted(name) {
149
+ return blacklist.has(name);
55
150
  }
56
151
 
57
152
  /**
58
- * Checks whether the given node represents an imported name that is renamed in the same import/export specifier.
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 renamed import.
156
+ * @returns {boolean} `true` if the node is a reference to a global variable.
65
157
  */
66
- function isRenamedImport(node) {
67
- const parent = node.parent;
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
- * Verifies if we should report an error or not.
86
- * @param {ASTNode} node The node to check
87
- * @returns {boolean} whether an error should be reported or not
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 shouldReport(node) {
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
- isInvalid(node.name)
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
- Identifier(node) {
123
-
124
- // MemberExpressions get special rules
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
- } else if (shouldReport(node)) {
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
  };
@@ -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: properties && function(parent, node) {
86
- return parent.key === node;
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",