eslint 7.3.1 → 7.7.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 (73) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +28 -16
  3. package/lib/cli-engine/config-array-factory.js +1 -33
  4. package/lib/cli-engine/formatters/checkstyle.js +2 -2
  5. package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
  6. package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
  7. package/lib/linter/code-path-analysis/code-path-state.js +59 -0
  8. package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
  9. package/lib/rule-tester/rule-tester.js +10 -0
  10. package/lib/rules/accessor-pairs.js +1 -14
  11. package/lib/rules/array-callback-return.js +26 -17
  12. package/lib/rules/arrow-body-style.js +43 -8
  13. package/lib/rules/arrow-parens.js +91 -108
  14. package/lib/rules/camelcase.js +47 -0
  15. package/lib/rules/comma-dangle.js +1 -2
  16. package/lib/rules/consistent-return.js +1 -12
  17. package/lib/rules/constructor-super.js +1 -0
  18. package/lib/rules/dot-location.js +20 -14
  19. package/lib/rules/dot-notation.js +36 -33
  20. package/lib/rules/func-call-spacing.js +42 -6
  21. package/lib/rules/func-name-matching.js +1 -4
  22. package/lib/rules/global-require.js +2 -1
  23. package/lib/rules/id-blacklist.js +14 -11
  24. package/lib/rules/id-denylist.js +230 -0
  25. package/lib/rules/indent.js +23 -3
  26. package/lib/rules/index.js +1 -0
  27. package/lib/rules/keyword-spacing.js +2 -2
  28. package/lib/rules/max-len.js +13 -2
  29. package/lib/rules/new-cap.js +10 -14
  30. package/lib/rules/newline-per-chained-call.js +15 -5
  31. package/lib/rules/no-alert.js +10 -3
  32. package/lib/rules/no-duplicate-case.js +23 -4
  33. package/lib/rules/no-eval.js +8 -38
  34. package/lib/rules/no-extend-native.js +37 -40
  35. package/lib/rules/no-extra-bind.js +57 -17
  36. package/lib/rules/no-extra-boolean-cast.js +7 -0
  37. package/lib/rules/no-extra-parens.js +48 -10
  38. package/lib/rules/no-implicit-coercion.js +11 -6
  39. package/lib/rules/no-implied-eval.js +7 -28
  40. package/lib/rules/no-import-assign.js +33 -32
  41. package/lib/rules/no-irregular-whitespace.js +22 -12
  42. package/lib/rules/no-magic-numbers.js +4 -8
  43. package/lib/rules/no-obj-calls.js +7 -4
  44. package/lib/rules/no-prototype-builtins.js +13 -3
  45. package/lib/rules/no-self-assign.js +3 -53
  46. package/lib/rules/no-setter-return.js +5 -8
  47. package/lib/rules/no-underscore-dangle.js +66 -21
  48. package/lib/rules/no-unexpected-multiline.js +2 -2
  49. package/lib/rules/no-unneeded-ternary.js +0 -2
  50. package/lib/rules/no-unused-expressions.js +55 -23
  51. package/lib/rules/no-useless-call.js +10 -7
  52. package/lib/rules/no-whitespace-before-property.js +16 -4
  53. package/lib/rules/object-curly-newline.js +4 -4
  54. package/lib/rules/operator-assignment.js +3 -42
  55. package/lib/rules/padding-line-between-statements.js +2 -2
  56. package/lib/rules/prefer-arrow-callback.js +90 -25
  57. package/lib/rules/prefer-exponentiation-operator.js +1 -1
  58. package/lib/rules/prefer-numeric-literals.js +4 -13
  59. package/lib/rules/prefer-promise-reject-errors.js +1 -3
  60. package/lib/rules/prefer-regex-literals.js +68 -13
  61. package/lib/rules/prefer-spread.js +2 -6
  62. package/lib/rules/radix.js +5 -2
  63. package/lib/rules/sort-imports.js +28 -0
  64. package/lib/rules/use-isnan.js +1 -1
  65. package/lib/rules/utils/ast-utils.js +317 -153
  66. package/lib/rules/wrap-iife.js +9 -2
  67. package/lib/rules/yoda.js +2 -55
  68. package/messages/extend-config-missing.txt +1 -1
  69. package/messages/no-config-found.txt +1 -1
  70. package/messages/plugin-conflict.txt +1 -1
  71. package/messages/plugin-missing.txt +1 -1
  72. package/messages/whitespace-found.txt +1 -1
  73. package/package.json +6 -7
@@ -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 MemberExpression node which has the specified name's
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 MemberExpression node which has
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
- node.type === "MemberExpression" &&
42
- TARGET_METHODS.test(astUtils.getStaticPropertyName(node) || "")
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: "Expected to return a value at the end of {{name}}.",
157
- expectedInside: "Expected to return a value in {{name}}.",
158
- expectedReturnValue: "{{name}} expected a return value.",
159
- expectedNoReturnValue: "{{name}} did not expect a return value."
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
- let name = astUtils.getFunctionNameWithKind(node);
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: lodash.upperFirst(astUtils.getFunctionNameWithKind(funcInfo.node))
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.start,
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
- fixes.push(
183
- fixer.insertTextBefore(firstValueToken, "("),
184
- fixer.insertTextAfter(lastValueToken, ")")
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.start,
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
- "ArrowFunctionExpression:exit": validate
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
- * Get location should be reported by AST node.
19
- * @param {ASTNode} node AST Node.
20
- * @returns {Location} Location information.
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 getLocation(node) {
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
- * Determines whether a arrow function argument end with `)`
79
- * @param {ASTNode} node The arrow function node.
80
- * @returns {void}
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 parens(node) {
83
- const isAsync = node.async;
84
- const firstTokenOfParam = sourceCode.getFirstToken(node, isAsync ? 1 : 0);
85
-
86
- /**
87
- * Remove the parenthesis around a parameter
88
- * @param {Fixer} fixer Fixer
89
- * @returns {string} fixed parameter
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
- * Checks whether there are comments inside the params or not.
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
- return closingParenToken && sourceCode.commentsExistBetween(firstTokenOfParam, closingParenToken);
117
- }
118
- return false;
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
- if (hasCommentsInParens()) {
123
- return;
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
- // "as-needed", { "requireForBlockBody": true }: x => x
127
- if (
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
- if (
146
- requireForBlockBody &&
147
- node.body.type === "BlockStatement"
148
- ) {
149
- if (!astUtils.isOpeningParenToken(firstTokenOfParam)) {
150
- context.report({
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
- // "as-needed": x => x
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: "unexpectedParens",
172
- loc: getLocation(node),
173
- fix: fixParamsWithParenthesis
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
- if (firstTokenOfParam.type === "Identifier") {
180
- const after = sourceCode.getTokenAfter(firstTokenOfParam);
181
-
182
- // (x) => x
183
- if (after.value !== ")") {
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: "expectedParens",
187
- loc: getLocation(node),
188
- fix(fixer) {
189
- return fixer.replaceText(firstTokenOfParam, `(${firstTokenOfParam.value})`);
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
  };
@@ -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
 
@@ -124,8 +124,7 @@ module.exports = {
124
124
  }
125
125
  ]
126
126
  }
127
- ],
128
- additionalItems: false
127
+ ]
129
128
  },
130
129
 
131
130
  messages: {
@@ -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 = !isIdentifier(argument, "undefined") && argument.operator !== "void";
157
+ hasReturnValue = !astUtils.isSpecificId(argument, "undefined") && argument.operator !== "void";
169
158
  }
170
159
 
171
160
  if (!funcInfo.hasReturn) {
@@ -50,6 +50,7 @@ function isPossibleConstructor(node) {
50
50
  case "MemberExpression":
51
51
  case "CallExpression":
52
52
  case "NewExpression":
53
+ case "ChainExpression":
53
54
  case "YieldExpression":
54
55
  case "TaggedTemplateExpression":
55
56
  case "MetaProperty":
@@ -52,31 +52,37 @@ module.exports = {
52
52
  */
53
53
  function checkDotLocation(node) {
54
54
  const property = node.property;
55
- const dot = sourceCode.getTokenBefore(property);
56
-
57
- // `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
58
- const tokenBeforeDot = sourceCode.getTokenBefore(dot);
59
-
60
- const textBeforeDot = sourceCode.getText().slice(tokenBeforeDot.range[1], dot.range[0]);
61
- const textAfterDot = sourceCode.getText().slice(dot.range[1], property.range[0]);
55
+ const dotToken = sourceCode.getTokenBefore(property);
62
56
 
63
57
  if (onObject) {
64
- if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dot)) {
65
- const neededTextAfterToken = astUtils.isDecimalIntegerNumericToken(tokenBeforeDot) ? " " : "";
66
58
 
59
+ // `obj` expression can be parenthesized, but those paren tokens are not a part of the `obj` node.
60
+ const tokenBeforeDot = sourceCode.getTokenBefore(dotToken);
61
+
62
+ if (!astUtils.isTokenOnSameLine(tokenBeforeDot, dotToken)) {
67
63
  context.report({
68
64
  node,
69
- loc: dot.loc,
65
+ loc: dotToken.loc,
70
66
  messageId: "expectedDotAfterObject",
71
- fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], property.range[0]], `${neededTextAfterToken}.${textBeforeDot}${textAfterDot}`)
67
+ *fix(fixer) {
68
+ if (dotToken.value.startsWith(".") && astUtils.isDecimalIntegerNumericToken(tokenBeforeDot)) {
69
+ yield fixer.insertTextAfter(tokenBeforeDot, ` ${dotToken.value}`);
70
+ } else {
71
+ yield fixer.insertTextAfter(tokenBeforeDot, dotToken.value);
72
+ }
73
+ yield fixer.remove(dotToken);
74
+ }
72
75
  });
73
76
  }
74
- } else if (!astUtils.isTokenOnSameLine(dot, property)) {
77
+ } else if (!astUtils.isTokenOnSameLine(dotToken, property)) {
75
78
  context.report({
76
79
  node,
77
- loc: dot.loc,
80
+ loc: dotToken.loc,
78
81
  messageId: "expectedDotBeforeProperty",
79
- fix: fixer => fixer.replaceTextRange([tokenBeforeDot.range[1], property.range[0]], `${textBeforeDot}${textAfterDot}.`)
82
+ *fix(fixer) {
83
+ yield fixer.remove(dotToken);
84
+ yield fixer.insertTextBefore(property, dotToken.value);
85
+ }
80
86
  });
81
87
  }
82
88
  }