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.
Files changed (76) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +14 -7
  3. package/lib/init/config-initializer.js +89 -68
  4. package/lib/linter/code-path-analysis/code-path-analyzer.js +38 -0
  5. package/lib/linter/code-path-analysis/code-path-segment.js +0 -1
  6. package/lib/linter/code-path-analysis/code-path-state.js +59 -0
  7. package/lib/linter/code-path-analysis/debug-helpers.js +26 -19
  8. package/lib/rule-tester/rule-tester.js +9 -0
  9. package/lib/rules/accessor-pairs.js +1 -14
  10. package/lib/rules/array-callback-return.js +26 -17
  11. package/lib/rules/arrow-body-style.js +43 -8
  12. package/lib/rules/arrow-parens.js +91 -108
  13. package/lib/rules/camelcase.js +47 -0
  14. package/lib/rules/comma-dangle.js +2 -1
  15. package/lib/rules/consistent-return.js +1 -12
  16. package/lib/rules/constructor-super.js +1 -0
  17. package/lib/rules/curly.js +8 -1
  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/id-match.js +2 -1
  26. package/lib/rules/indent.js +19 -0
  27. package/lib/rules/index.js +3 -0
  28. package/lib/rules/key-spacing.js +6 -2
  29. package/lib/rules/keyword-spacing.js +2 -2
  30. package/lib/rules/max-len.js +13 -2
  31. package/lib/rules/max-lines.js +34 -8
  32. package/lib/rules/new-cap.js +10 -14
  33. package/lib/rules/newline-per-chained-call.js +15 -5
  34. package/lib/rules/no-alert.js +10 -3
  35. package/lib/rules/no-eval.js +8 -38
  36. package/lib/rules/no-extend-native.js +37 -40
  37. package/lib/rules/no-extra-bind.js +57 -17
  38. package/lib/rules/no-extra-boolean-cast.js +7 -0
  39. package/lib/rules/no-extra-parens.js +48 -10
  40. package/lib/rules/no-implicit-coercion.js +11 -6
  41. package/lib/rules/no-implied-eval.js +7 -28
  42. package/lib/rules/no-import-assign.js +33 -32
  43. package/lib/rules/no-irregular-whitespace.js +22 -12
  44. package/lib/rules/no-magic-numbers.js +4 -8
  45. package/lib/rules/no-obj-calls.js +7 -4
  46. package/lib/rules/no-promise-executor-return.js +121 -0
  47. package/lib/rules/no-prototype-builtins.js +13 -3
  48. package/lib/rules/no-self-assign.js +3 -53
  49. package/lib/rules/no-setter-return.js +5 -8
  50. package/lib/rules/no-unexpected-multiline.js +2 -2
  51. package/lib/rules/no-unneeded-ternary.js +0 -2
  52. package/lib/rules/no-unreachable-loop.js +150 -0
  53. package/lib/rules/no-unused-expressions.js +55 -23
  54. package/lib/rules/no-unused-vars.js +2 -1
  55. package/lib/rules/no-useless-call.js +10 -7
  56. package/lib/rules/no-whitespace-before-property.js +16 -4
  57. package/lib/rules/object-curly-newline.js +4 -4
  58. package/lib/rules/object-property-newline.js +1 -1
  59. package/lib/rules/operator-assignment.js +3 -42
  60. package/lib/rules/operator-linebreak.js +2 -5
  61. package/lib/rules/padded-blocks.js +2 -1
  62. package/lib/rules/padding-line-between-statements.js +2 -2
  63. package/lib/rules/prefer-arrow-callback.js +90 -25
  64. package/lib/rules/prefer-exponentiation-operator.js +1 -1
  65. package/lib/rules/prefer-numeric-literals.js +4 -13
  66. package/lib/rules/prefer-promise-reject-errors.js +1 -3
  67. package/lib/rules/prefer-regex-literals.js +68 -13
  68. package/lib/rules/prefer-spread.js +2 -6
  69. package/lib/rules/radix.js +5 -2
  70. package/lib/rules/semi-spacing.js +1 -0
  71. package/lib/rules/sort-imports.js +28 -0
  72. package/lib/rules/use-isnan.js +1 -1
  73. package/lib/rules/utils/ast-utils.js +317 -153
  74. package/lib/rules/wrap-iife.js +9 -2
  75. package/lib/rules/yoda.js +2 -55
  76. package/package.json +7 -7
@@ -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":
@@ -457,11 +457,18 @@ module.exports = {
457
457
 
458
458
  return {
459
459
  IfStatement(node) {
460
- if (node.parent.type !== "IfStatement") {
460
+ const parent = node.parent;
461
+ const isElseIf = parent.type === "IfStatement" && parent.alternate === node;
462
+
463
+ if (!isElseIf) {
464
+
465
+ // This is a top `if`, check the whole `if-else-if` chain
461
466
  prepareIfChecks(node).forEach(preparedCheck => {
462
467
  preparedCheck.check();
463
468
  });
464
469
  }
470
+
471
+ // Skip `else if`, it's already checked (when the top `if` was visited)
465
472
  },
466
473
 
467
474
  WhileStatement(node) {
@@ -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
  }
@@ -87,28 +87,36 @@ module.exports = {
87
87
  data: {
88
88
  key: formattedValue
89
89
  },
90
- fix(fixer) {
90
+ *fix(fixer) {
91
91
  const leftBracket = sourceCode.getTokenAfter(node.object, astUtils.isOpeningBracketToken);
92
92
  const rightBracket = sourceCode.getLastToken(node);
93
+ const nextToken = sourceCode.getTokenAfter(node);
93
94
 
94
- if (sourceCode.getFirstTokenBetween(leftBracket, rightBracket, { includeComments: true, filter: astUtils.isCommentToken })) {
95
-
96
- // Don't perform any fixes if there are comments inside the brackets.
97
- return null;
95
+ // Don't perform any fixes if there are comments inside the brackets.
96
+ if (sourceCode.commentsExistBetween(leftBracket, rightBracket)) {
97
+ return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
98
98
  }
99
99
 
100
- const tokenAfterProperty = sourceCode.getTokenAfter(rightBracket);
101
- const needsSpaceAfterProperty = tokenAfterProperty &&
102
- rightBracket.range[1] === tokenAfterProperty.range[0] &&
103
- !astUtils.canTokensBeAdjacent(String(value), tokenAfterProperty);
104
-
105
- const textBeforeDot = astUtils.isDecimalInteger(node.object) ? " " : "";
106
- const textAfterProperty = needsSpaceAfterProperty ? " " : "";
107
-
108
- return fixer.replaceTextRange(
100
+ // Replace the brackets by an identifier.
101
+ if (!node.optional) {
102
+ yield fixer.insertTextBefore(
103
+ leftBracket,
104
+ astUtils.isDecimalInteger(node.object) ? " ." : "."
105
+ );
106
+ }
107
+ yield fixer.replaceTextRange(
109
108
  [leftBracket.range[0], rightBracket.range[1]],
110
- `${textBeforeDot}.${value}${textAfterProperty}`
109
+ value
111
110
  );
111
+
112
+ // Insert a space after the property if it will be connected to the next token.
113
+ if (
114
+ nextToken &&
115
+ rightBracket.range[1] === nextToken.range[0] &&
116
+ !astUtils.canTokensBeAdjacent(String(value), nextToken)
117
+ ) {
118
+ yield fixer.insertTextAfter(node, " ");
119
+ }
112
120
  }
113
121
  });
114
122
  }
@@ -141,29 +149,24 @@ module.exports = {
141
149
  data: {
142
150
  key: node.property.name
143
151
  },
144
- fix(fixer) {
145
- const dot = sourceCode.getTokenBefore(node.property);
146
- const textAfterDot = sourceCode.text.slice(dot.range[1], node.property.range[0]);
147
-
148
- if (textAfterDot.trim()) {
152
+ *fix(fixer) {
153
+ const dotToken = sourceCode.getTokenBefore(node.property);
149
154
 
150
- // Don't perform any fixes if there are comments between the dot and the property name.
151
- return null;
155
+ // A statement that starts with `let[` is parsed as a destructuring variable declaration, not a MemberExpression.
156
+ if (node.object.type === "Identifier" && node.object.name === "let" && !node.optional) {
157
+ return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
152
158
  }
153
159
 
154
- if (node.object.type === "Identifier" && node.object.name === "let") {
155
-
156
- /*
157
- * A statement that starts with `let[` is parsed as a destructuring variable declaration, not
158
- * a MemberExpression.
159
- */
160
- return null;
160
+ // Don't perform any fixes if there are comments between the dot and the property name.
161
+ if (sourceCode.commentsExistBetween(dotToken, node.property)) {
162
+ return; // eslint-disable-line eslint-plugin/fixer-return -- false positive
161
163
  }
162
164
 
163
- return fixer.replaceTextRange(
164
- [dot.range[0], node.property.range[1]],
165
- `[${textAfterDot}"${node.property.name}"]`
166
- );
165
+ // Replace the identifier to brackets.
166
+ if (!node.optional) {
167
+ yield fixer.remove(dotToken);
168
+ }
169
+ yield fixer.replaceText(node.property, `["${node.property.name}"]`);
167
170
  }
168
171
  });
169
172
  }
@@ -126,15 +126,24 @@ module.exports = {
126
126
  messageId: "unexpectedWhitespace",
127
127
  fix(fixer) {
128
128
 
129
+ // Don't remove comments.
130
+ if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
131
+ return null;
132
+ }
133
+
134
+ // If `?.` exsits, it doesn't hide no-undexpected-multiline errors
135
+ if (node.optional) {
136
+ return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], "?.");
137
+ }
138
+
129
139
  /*
130
140
  * Only autofix if there is no newline
131
141
  * https://github.com/eslint/eslint/issues/7787
132
142
  */
133
- if (!hasNewline) {
134
- return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
143
+ if (hasNewline) {
144
+ return null;
135
145
  }
136
-
137
- return null;
146
+ return fixer.removeRange([leftToken.range[1], rightToken.range[0]]);
138
147
  }
139
148
  });
140
149
  } else if (!never && !hasWhitespace) {
@@ -149,6 +158,9 @@ module.exports = {
149
158
  },
150
159
  messageId: "missing",
151
160
  fix(fixer) {
161
+ if (node.optional) {
162
+ return null; // Not sure if inserting a space to either before/after `?.` token.
163
+ }
152
164
  return fixer.insertTextBefore(rightToken, " ");
153
165
  }
154
166
  });
@@ -161,7 +173,31 @@ module.exports = {
161
173
  },
162
174
  messageId: "unexpectedNewline",
163
175
  fix(fixer) {
164
- return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
176
+
177
+ /*
178
+ * Only autofix if there is no newline
179
+ * https://github.com/eslint/eslint/issues/7787
180
+ * But if `?.` exsits, it doesn't hide no-undexpected-multiline errors
181
+ */
182
+ if (!node.optional) {
183
+ return null;
184
+ }
185
+
186
+ // Don't remove comments.
187
+ if (sourceCode.commentsExistBetween(leftToken, rightToken)) {
188
+ return null;
189
+ }
190
+
191
+ const range = [leftToken.range[1], rightToken.range[0]];
192
+ const qdToken = sourceCode.getTokenAfter(leftToken);
193
+
194
+ if (qdToken.range[0] === leftToken.range[1]) {
195
+ return fixer.replaceTextRange(range, "?. ");
196
+ }
197
+ if (qdToken.range[1] === rightToken.range[0]) {
198
+ return fixer.replaceTextRange(range, " ?.");
199
+ }
200
+ return fixer.replaceTextRange(range, " ?. ");
165
201
  }
166
202
  });
167
203
  }
@@ -172,7 +208,7 @@ module.exports = {
172
208
  const lastToken = sourceCode.getLastToken(node);
173
209
  const lastCalleeToken = sourceCode.getLastToken(node.callee);
174
210
  const parenToken = sourceCode.getFirstTokenBetween(lastCalleeToken, lastToken, astUtils.isOpeningParenToken);
175
- const prevToken = parenToken && sourceCode.getTokenBefore(parenToken);
211
+ const prevToken = parenToken && sourceCode.getTokenBefore(parenToken, astUtils.isNotQuestionDotToken);
176
212
 
177
213
  // Parens in NewExpression are optional
178
214
  if (!(parenToken && parenToken.range[1] < node.range[1])) {
@@ -117,10 +117,7 @@ module.exports = {
117
117
  if (!node) {
118
118
  return false;
119
119
  }
120
- return node.type === "CallExpression" &&
121
- node.callee.type === "MemberExpression" &&
122
- node.callee.object.name === objName &&
123
- node.callee.property.name === funcName;
120
+ return node.type === "CallExpression" && astUtils.isSpecificMemberAccess(node.callee, objName, funcName);
124
121
  }
125
122
 
126
123
  /**
@@ -13,7 +13,8 @@ const ACCEPTABLE_PARENTS = [
13
13
  "CallExpression",
14
14
  "ConditionalExpression",
15
15
  "Program",
16
- "VariableDeclaration"
16
+ "VariableDeclaration",
17
+ "ChainExpression"
17
18
  ];
18
19
 
19
20
  /**
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @fileoverview Rule that warns when identifier names that are
3
- * blacklisted in the configuration are used.
3
+ * specified in the configuration are used.
4
4
  * @author Keith Cirkel (http://keithcirkel.co.uk)
5
5
  */
6
6
 
@@ -111,6 +111,9 @@ function isShorthandPropertyDefinition(node) {
111
111
 
112
112
  module.exports = {
113
113
  meta: {
114
+ deprecated: true,
115
+ replacedBy: ["id-denylist"],
116
+
114
117
  type: "suggestion",
115
118
 
116
119
  docs: {
@@ -128,25 +131,25 @@ module.exports = {
128
131
  uniqueItems: true
129
132
  },
130
133
  messages: {
131
- blacklisted: "Identifier '{{name}}' is blacklisted."
134
+ restricted: "Identifier '{{name}}' is restricted."
132
135
  }
133
136
  },
134
137
 
135
138
  create(context) {
136
139
 
137
- const blacklist = new Set(context.options);
140
+ const denyList = new Set(context.options);
138
141
  const reportedNodes = new Set();
139
142
 
140
143
  let globalScope;
141
144
 
142
145
  /**
143
- * Checks whether the given name is blacklisted.
146
+ * Checks whether the given name is restricted.
144
147
  * @param {string} name The name to check.
145
- * @returns {boolean} `true` if the name is blacklisted.
148
+ * @returns {boolean} `true` if the name is restricted.
146
149
  * @private
147
150
  */
148
- function isBlacklisted(name) {
149
- return blacklist.has(name);
151
+ function isRestricted(name) {
152
+ return denyList.has(name);
150
153
  }
151
154
 
152
155
  /**
@@ -172,8 +175,8 @@ module.exports = {
172
175
 
173
176
  /*
174
177
  * 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.
178
+ * Read access to a property with a restricted name is allowed, because it can be on an object that user has no control over.
179
+ * Write access isn't allowed, because it potentially creates a new property with a restricted name.
177
180
  */
178
181
  if (
179
182
  parent.type === "MemberExpression" &&
@@ -205,7 +208,7 @@ module.exports = {
205
208
  if (!reportedNodes.has(node)) {
206
209
  context.report({
207
210
  node,
208
- messageId: "blacklisted",
211
+ messageId: "restricted",
209
212
  data: {
210
213
  name: node.name
211
214
  }
@@ -221,7 +224,7 @@ module.exports = {
221
224
  },
222
225
 
223
226
  Identifier(node) {
224
- if (isBlacklisted(node.name) && shouldCheck(node)) {
227
+ if (isRestricted(node.name) && shouldCheck(node)) {
225
228
  report(node);
226
229
  }
227
230
  }
@@ -0,0 +1,230 @@
1
+ /**
2
+ * @fileoverview Rule that warns when identifier names that are
3
+ * specified in the configuration are used.
4
+ * @author Keith Cirkel (http://keithcirkel.co.uk)
5
+ */
6
+
7
+ "use strict";
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
+
108
+ //------------------------------------------------------------------------------
109
+ // Rule Definition
110
+ //------------------------------------------------------------------------------
111
+
112
+ module.exports = {
113
+ meta: {
114
+ type: "suggestion",
115
+
116
+ docs: {
117
+ description: "disallow specified identifiers",
118
+ category: "Stylistic Issues",
119
+ recommended: false,
120
+ url: "https://eslint.org/docs/rules/id-denylist"
121
+ },
122
+
123
+ schema: {
124
+ type: "array",
125
+ items: {
126
+ type: "string"
127
+ },
128
+ uniqueItems: true
129
+ },
130
+ messages: {
131
+ restricted: "Identifier '{{name}}' is restricted."
132
+ }
133
+ },
134
+
135
+ create(context) {
136
+
137
+ const denyList = new Set(context.options);
138
+ const reportedNodes = new Set();
139
+
140
+ let globalScope;
141
+
142
+ /**
143
+ * Checks whether the given name is restricted.
144
+ * @param {string} name The name to check.
145
+ * @returns {boolean} `true` if the name is restricted.
146
+ * @private
147
+ */
148
+ function isRestricted(name) {
149
+ return denyList.has(name);
150
+ }
151
+
152
+ /**
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.
155
+ * @param {ASTNode} node `Identifier` node to check.
156
+ * @returns {boolean} `true` if the node is a reference to a global variable.
157
+ */
158
+ function isReferenceToGlobalVariable(node) {
159
+ const variable = globalScope.set.get(node.name);
160
+
161
+ return variable && variable.defs.length === 0 &&
162
+ variable.references.some(ref => ref.identifier === node);
163
+ }
164
+
165
+ /**
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.
169
+ */
170
+ function shouldCheck(node) {
171
+ const parent = node.parent;
172
+
173
+ /*
174
+ * Member access has special rules for checking property names.
175
+ * Read access to a property with a restricted 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 restricted name.
177
+ */
178
+ if (
179
+ parent.type === "MemberExpression" &&
180
+ parent.property === node &&
181
+ !parent.computed
182
+ ) {
183
+ return isAssignmentTarget(parent);
184
+ }
185
+
186
+ return (
187
+ parent.type !== "CallExpression" &&
188
+ parent.type !== "NewExpression" &&
189
+ !isRenamedImport(node) &&
190
+ !isRenamedInDestructuring(node) &&
191
+ !(
192
+ isReferenceToGlobalVariable(node) &&
193
+ !isShorthandPropertyDefinition(node)
194
+ )
195
+ );
196
+ }
197
+
198
+ /**
199
+ * Reports an AST node as a rule violation.
200
+ * @param {ASTNode} node The node to report.
201
+ * @returns {void}
202
+ * @private
203
+ */
204
+ function report(node) {
205
+ if (!reportedNodes.has(node)) {
206
+ context.report({
207
+ node,
208
+ messageId: "restricted",
209
+ data: {
210
+ name: node.name
211
+ }
212
+ });
213
+ reportedNodes.add(node);
214
+ }
215
+ }
216
+
217
+ return {
218
+
219
+ Program() {
220
+ globalScope = context.getScope();
221
+ },
222
+
223
+ Identifier(node) {
224
+ if (isRestricted(node.name) && shouldCheck(node)) {
225
+ report(node);
226
+ }
227
+ }
228
+ };
229
+ }
230
+ };
@@ -39,7 +39,8 @@ module.exports = {
39
39
  type: "boolean",
40
40
  default: false
41
41
  }
42
- }
42
+ },
43
+ additionalProperties: false
43
44
  }
44
45
  ],
45
46
  messages: {
@@ -32,6 +32,7 @@ const KNOWN_NODES = new Set([
32
32
  "BreakStatement",
33
33
  "CallExpression",
34
34
  "CatchClause",
35
+ "ChainExpression",
35
36
  "ClassBody",
36
37
  "ClassDeclaration",
37
38
  "ClassExpression",
@@ -934,6 +935,24 @@ module.exports = {
934
935
  parameterParens.add(openingParen);
935
936
  parameterParens.add(closingParen);
936
937
 
938
+ /*
939
+ * If `?.` token exists, set desired offset for that.
940
+ * This logic is copied from `MemberExpression`'s.
941
+ */
942
+ if (node.optional) {
943
+ const dotToken = sourceCode.getTokenAfter(node.callee, astUtils.isQuestionDotToken);
944
+ const calleeParenCount = sourceCode.getTokensBetween(node.callee, dotToken, { filter: astUtils.isClosingParenToken }).length;
945
+ const firstTokenOfCallee = calleeParenCount
946
+ ? sourceCode.getTokenBefore(node.callee, { skip: calleeParenCount - 1 })
947
+ : sourceCode.getFirstToken(node.callee);
948
+ const lastTokenOfCallee = sourceCode.getTokenBefore(dotToken);
949
+ const offsetBase = lastTokenOfCallee.loc.end.line === openingParen.loc.start.line
950
+ ? lastTokenOfCallee
951
+ : firstTokenOfCallee;
952
+
953
+ offsets.setDesiredOffset(dotToken, offsetBase, 1);
954
+ }
955
+
937
956
  const offsetAfterToken = node.callee.type === "TaggedTemplateExpression" ? sourceCode.getFirstToken(node.callee.quasi) : openingParen;
938
957
  const offsetToken = sourceCode.getTokenBefore(offsetAfterToken);
939
958
 
@@ -57,6 +57,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
57
57
  "guard-for-in": () => require("./guard-for-in"),
58
58
  "handle-callback-err": () => require("./handle-callback-err"),
59
59
  "id-blacklist": () => require("./id-blacklist"),
60
+ "id-denylist": () => require("./id-denylist"),
60
61
  "id-length": () => require("./id-length"),
61
62
  "id-match": () => require("./id-match"),
62
63
  "implicit-arrow-linebreak": () => require("./implicit-arrow-linebreak"),
@@ -176,6 +177,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
176
177
  "no-plusplus": () => require("./no-plusplus"),
177
178
  "no-process-env": () => require("./no-process-env"),
178
179
  "no-process-exit": () => require("./no-process-exit"),
180
+ "no-promise-executor-return": () => require("./no-promise-executor-return"),
179
181
  "no-proto": () => require("./no-proto"),
180
182
  "no-prototype-builtins": () => require("./no-prototype-builtins"),
181
183
  "no-redeclare": () => require("./no-redeclare"),
@@ -212,6 +214,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
212
214
  "no-unmodified-loop-condition": () => require("./no-unmodified-loop-condition"),
213
215
  "no-unneeded-ternary": () => require("./no-unneeded-ternary"),
214
216
  "no-unreachable": () => require("./no-unreachable"),
217
+ "no-unreachable-loop": () => require("./no-unreachable-loop"),
215
218
  "no-unsafe-finally": () => require("./no-unsafe-finally"),
216
219
  "no-unsafe-negation": () => require("./no-unsafe-negation"),
217
220
  "no-unused-expressions": () => require("./no-unused-expressions"),