eslint 8.15.0 → 8.18.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 (63) hide show
  1. package/README.md +2 -7
  2. package/bin/eslint.js +1 -1
  3. package/lib/cli-engine/cli-engine.js +2 -4
  4. package/lib/cli-engine/lint-result-cache.js +1 -1
  5. package/lib/eslint/eslint.js +3 -3
  6. package/lib/linter/code-path-analysis/code-path-segment.js +1 -1
  7. package/lib/linter/code-path-analysis/code-path-state.js +1 -1
  8. package/lib/linter/code-path-analysis/code-path.js +1 -1
  9. package/lib/linter/linter.js +1 -1
  10. package/lib/linter/timing.js +2 -1
  11. package/lib/rules/accessor-pairs.js +4 -4
  12. package/lib/rules/callback-return.js +2 -2
  13. package/lib/rules/capitalized-comments.js +1 -1
  14. package/lib/rules/consistent-this.js +1 -1
  15. package/lib/rules/dot-notation.js +2 -2
  16. package/lib/rules/function-paren-newline.js +8 -5
  17. package/lib/rules/global-require.js +3 -3
  18. package/lib/rules/indent-legacy.js +2 -2
  19. package/lib/rules/indent.js +45 -13
  20. package/lib/rules/jsx-quotes.js +1 -1
  21. package/lib/rules/lines-around-comment.js +3 -3
  22. package/lib/rules/max-lines.js +2 -2
  23. package/lib/rules/max-statements.js +1 -1
  24. package/lib/rules/newline-before-return.js +1 -1
  25. package/lib/rules/no-bitwise.js +2 -2
  26. package/lib/rules/no-console.js +1 -1
  27. package/lib/rules/no-constant-binary-expression.js +3 -3
  28. package/lib/rules/no-control-regex.js +23 -10
  29. package/lib/rules/no-empty-function.js +1 -1
  30. package/lib/rules/no-extra-boolean-cast.js +3 -3
  31. package/lib/rules/no-extra-semi.js +1 -1
  32. package/lib/rules/no-global-assign.js +1 -1
  33. package/lib/rules/no-implicit-coercion.js +8 -8
  34. package/lib/rules/no-loop-func.js +1 -1
  35. package/lib/rules/no-magic-numbers.js +3 -3
  36. package/lib/rules/no-misleading-character-class.js +90 -17
  37. package/lib/rules/no-mixed-operators.js +1 -1
  38. package/lib/rules/no-mixed-requires.js +1 -1
  39. package/lib/rules/no-multi-spaces.js +1 -1
  40. package/lib/rules/no-native-reassign.js +1 -1
  41. package/lib/rules/no-new-object.js +1 -1
  42. package/lib/rules/no-new-wrappers.js +1 -1
  43. package/lib/rules/no-octal.js +2 -2
  44. package/lib/rules/no-prototype-builtins.js +3 -3
  45. package/lib/rules/no-shadow.js +5 -5
  46. package/lib/rules/no-sparse-arrays.js +1 -1
  47. package/lib/rules/no-underscore-dangle.js +1 -1
  48. package/lib/rules/no-unused-expressions.js +1 -1
  49. package/lib/rules/no-unused-vars.js +5 -5
  50. package/lib/rules/no-use-before-define.js +15 -2
  51. package/lib/rules/operator-assignment.js +2 -2
  52. package/lib/rules/prefer-const.js +1 -1
  53. package/lib/rules/prefer-reflect.js +2 -2
  54. package/lib/rules/prefer-regex-literals.js +3 -3
  55. package/lib/rules/quote-props.js +2 -2
  56. package/lib/rules/quotes.js +1 -1
  57. package/lib/rules/spaced-comment.js +1 -1
  58. package/lib/rules/valid-jsdoc.js +1 -1
  59. package/lib/rules/valid-typeof.js +4 -4
  60. package/lib/rules/yoda.js +1 -1
  61. package/lib/shared/deprecation-warnings.js +1 -8
  62. package/lib/shared/types.js +1 -1
  63. package/package.json +23 -6
@@ -30,9 +30,9 @@ function parseOptions(options) {
30
30
  }
31
31
 
32
32
  /**
33
- * Checks whether or not a node is a double logical nigating.
33
+ * Checks whether or not a node is a double logical negating.
34
34
  * @param {ASTNode} node An UnaryExpression node to check.
35
- * @returns {boolean} Whether or not the node is a double logical nigating.
35
+ * @returns {boolean} Whether or not the node is a double logical negating.
36
36
  */
37
37
  function isDoubleLogicalNegating(node) {
38
38
  return (
@@ -257,7 +257,7 @@ module.exports = {
257
257
  let operatorAllowed;
258
258
 
259
259
  // !!foo
260
- operatorAllowed = options.allow.indexOf("!!") >= 0;
260
+ operatorAllowed = options.allow.includes("!!");
261
261
  if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
262
262
  const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
263
263
 
@@ -265,7 +265,7 @@ module.exports = {
265
265
  }
266
266
 
267
267
  // ~foo.indexOf(bar)
268
- operatorAllowed = options.allow.indexOf("~") >= 0;
268
+ operatorAllowed = options.allow.includes("~");
269
269
  if (!operatorAllowed && options.boolean && isBinaryNegatingOfIndexOf(node)) {
270
270
 
271
271
  // `foo?.indexOf(bar) !== -1` will be true (== found) if the `foo` is nullish. So use `>= 0` in that case.
@@ -276,7 +276,7 @@ module.exports = {
276
276
  }
277
277
 
278
278
  // +foo
279
- operatorAllowed = options.allow.indexOf("+") >= 0;
279
+ operatorAllowed = options.allow.includes("+");
280
280
  if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
281
281
  const recommendation = `Number(${sourceCode.getText(node.argument)})`;
282
282
 
@@ -289,7 +289,7 @@ module.exports = {
289
289
  let operatorAllowed;
290
290
 
291
291
  // 1 * foo
292
- operatorAllowed = options.allow.indexOf("*") >= 0;
292
+ operatorAllowed = options.allow.includes("*");
293
293
  const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && getNonNumericOperand(node);
294
294
 
295
295
  if (nonNumericOperand) {
@@ -299,7 +299,7 @@ module.exports = {
299
299
  }
300
300
 
301
301
  // "" + foo
302
- operatorAllowed = options.allow.indexOf("+") >= 0;
302
+ operatorAllowed = options.allow.includes("+");
303
303
  if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
304
304
  const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
305
305
 
@@ -310,7 +310,7 @@ module.exports = {
310
310
  AssignmentExpression(node) {
311
311
 
312
312
  // foo += ""
313
- const operatorAllowed = options.allow.indexOf("+") >= 0;
313
+ const operatorAllowed = options.allow.includes("+");
314
314
 
315
315
  if (!operatorAllowed && options.string && isAppendEmptyString(node)) {
316
316
  const code = sourceCode.getText(getNonEmptyOperand(node));
@@ -125,7 +125,7 @@ function isSafe(loopNode, reference) {
125
125
  * The reference is every reference of the upper scope's variable we are
126
126
  * looking now.
127
127
  *
128
- * It's safeafe if the reference matches one of the following condition.
128
+ * It's safe if the reference matches one of the following condition.
129
129
  * - is readonly.
130
130
  * - doesn't exist inside a local function and after the border.
131
131
  * @param {eslint-scope.Reference} upperRef A reference to check.
@@ -80,7 +80,7 @@ module.exports = {
80
80
  const config = context.options[0] || {},
81
81
  detectObjects = !!config.detectObjects,
82
82
  enforceConst = !!config.enforceConst,
83
- ignore = (config.ignore || []).map(normalizeIgnoreValue),
83
+ ignore = new Set((config.ignore || []).map(normalizeIgnoreValue)),
84
84
  ignoreArrayIndexes = !!config.ignoreArrayIndexes,
85
85
  ignoreDefaultValues = !!config.ignoreDefaultValues;
86
86
 
@@ -92,7 +92,7 @@ module.exports = {
92
92
  * @returns {boolean} true if the value is ignored
93
93
  */
94
94
  function isIgnoredValue(value) {
95
- return ignore.indexOf(value) !== -1;
95
+ return ignore.has(value);
96
96
  }
97
97
 
98
98
  /**
@@ -209,7 +209,7 @@ module.exports = {
209
209
  });
210
210
  }
211
211
  } else if (
212
- okTypes.indexOf(parent.type) === -1 ||
212
+ !okTypes.includes(parent.type) ||
213
213
  (parent.type === "AssignmentExpression" && parent.left.type === "Identifier")
214
214
  ) {
215
215
  context.report({
@@ -4,13 +4,16 @@
4
4
  "use strict";
5
5
 
6
6
  const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("eslint-utils");
7
- const { RegExpParser, visitRegExpAST } = require("regexpp");
7
+ const { RegExpValidator, RegExpParser, visitRegExpAST } = require("regexpp");
8
8
  const { isCombiningCharacter, isEmojiModifier, isRegionalIndicatorSymbol, isSurrogatePair } = require("./utils/unicode");
9
+ const astUtils = require("./utils/ast-utils.js");
9
10
 
10
11
  //------------------------------------------------------------------------------
11
12
  // Helpers
12
13
  //------------------------------------------------------------------------------
13
14
 
15
+ const REGEXPP_LATEST_ECMA_VERSION = 2022;
16
+
14
17
  /**
15
18
  * Iterate character sequences of a given nodes.
16
19
  *
@@ -109,6 +112,8 @@ module.exports = {
109
112
  url: "https://eslint.org/docs/rules/no-misleading-character-class"
110
113
  },
111
114
 
115
+ hasSuggestions: true,
116
+
112
117
  schema: [],
113
118
 
114
119
  messages: {
@@ -116,10 +121,12 @@ module.exports = {
116
121
  combiningClass: "Unexpected combined character in character class.",
117
122
  emojiModifier: "Unexpected modified Emoji in character class.",
118
123
  regionalIndicatorSymbol: "Unexpected national flag in character class.",
119
- zwj: "Unexpected joined character sequence in character class."
124
+ zwj: "Unexpected joined character sequence in character class.",
125
+ suggestUnicodeFlag: "Add unicode 'u' flag to regex."
120
126
  }
121
127
  },
122
128
  create(context) {
129
+ const sourceCode = context.getSourceCode();
123
130
  const parser = new RegExpParser();
124
131
 
125
132
  /**
@@ -127,17 +134,10 @@ module.exports = {
127
134
  * @param {Node} node The node to report.
128
135
  * @param {string} pattern The regular expression pattern to verify.
129
136
  * @param {string} flags The flags of the regular expression.
137
+ * @param {Function} unicodeFixer Fixer for missing "u" flag.
130
138
  * @returns {void}
131
139
  */
132
- function verify(node, pattern, flags) {
133
- const has = {
134
- surrogatePairWithoutUFlag: false,
135
- combiningClass: false,
136
- variationSelector: false,
137
- emojiModifier: false,
138
- regionalIndicatorSymbol: false,
139
- zwj: false
140
- };
140
+ function verify(node, pattern, flags, unicodeFixer) {
141
141
  let patternNode;
142
142
 
143
143
  try {
@@ -153,26 +153,75 @@ module.exports = {
153
153
  return;
154
154
  }
155
155
 
156
+ const foundKinds = new Set();
157
+
156
158
  visitRegExpAST(patternNode, {
157
159
  onCharacterClassEnter(ccNode) {
158
160
  for (const chars of iterateCharacterSequence(ccNode.elements)) {
159
161
  for (const kind of kinds) {
160
- has[kind] = has[kind] || hasCharacterSequence[kind](chars);
162
+ if (hasCharacterSequence[kind](chars)) {
163
+ foundKinds.add(kind);
164
+ }
161
165
  }
162
166
  }
163
167
  }
164
168
  });
165
169
 
166
- for (const kind of kinds) {
167
- if (has[kind]) {
168
- context.report({ node, messageId: kind });
170
+ for (const kind of foundKinds) {
171
+ let suggest;
172
+
173
+ if (kind === "surrogatePairWithoutUFlag") {
174
+ suggest = [{
175
+ messageId: "suggestUnicodeFlag",
176
+ fix: unicodeFixer
177
+ }];
169
178
  }
179
+
180
+ context.report({
181
+ node,
182
+ messageId: kind,
183
+ suggest
184
+ });
170
185
  }
171
186
  }
172
187
 
188
+ /**
189
+ * Checks if the given regular expression pattern would be valid with the `u` flag.
190
+ * @param {string} pattern The regular expression pattern to verify.
191
+ * @returns {boolean} `true` if the pattern would be valid with the `u` flag.
192
+ * `false` if the pattern would be invalid with the `u` flag or the configured
193
+ * ecmaVersion doesn't support the `u` flag.
194
+ */
195
+ function isValidWithUnicodeFlag(pattern) {
196
+ const { ecmaVersion } = context.parserOptions;
197
+
198
+ // ecmaVersion is unknown or it doesn't support the 'u' flag
199
+ if (typeof ecmaVersion !== "number" || ecmaVersion <= 5) {
200
+ return false;
201
+ }
202
+
203
+ const validator = new RegExpValidator({
204
+ ecmaVersion: Math.min(ecmaVersion + 2009, REGEXPP_LATEST_ECMA_VERSION)
205
+ });
206
+
207
+ try {
208
+ validator.validatePattern(pattern, void 0, void 0, /* uFlag = */ true);
209
+ } catch {
210
+ return false;
211
+ }
212
+
213
+ return true;
214
+ }
215
+
173
216
  return {
174
217
  "Literal[regex]"(node) {
175
- verify(node, node.regex.pattern, node.regex.flags);
218
+ verify(node, node.regex.pattern, node.regex.flags, fixer => {
219
+ if (!isValidWithUnicodeFlag(node.regex.pattern)) {
220
+ return null;
221
+ }
222
+
223
+ return fixer.insertTextAfter(node, "u");
224
+ });
176
225
  },
177
226
  "Program"() {
178
227
  const scope = context.getScope();
@@ -191,7 +240,31 @@ module.exports = {
191
240
  const flags = getStringIfConstant(flagsNode, scope);
192
241
 
193
242
  if (typeof pattern === "string") {
194
- verify(node, pattern, flags || "");
243
+ verify(node, pattern, flags || "", fixer => {
244
+
245
+ if (!isValidWithUnicodeFlag(pattern)) {
246
+ return null;
247
+ }
248
+
249
+ if (node.arguments.length === 1) {
250
+ const penultimateToken = sourceCode.getLastToken(node, { skip: 1 }); // skip closing parenthesis
251
+
252
+ return fixer.insertTextAfter(
253
+ penultimateToken,
254
+ astUtils.isCommaToken(penultimateToken)
255
+ ? ' "u",'
256
+ : ', "u"'
257
+ );
258
+ }
259
+
260
+ if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
261
+ const range = [flagsNode.range[0], flagsNode.range[1] - 1];
262
+
263
+ return fixer.insertTextAfterRange(range, "u");
264
+ }
265
+
266
+ return null;
267
+ });
195
268
  }
196
269
  }
197
270
  }
@@ -64,7 +64,7 @@ function normalizeOptions(options = {}) {
64
64
  * @returns {boolean} `true` if such group existed.
65
65
  */
66
66
  function includesBothInAGroup(groups, left, right) {
67
- return groups.some(group => group.indexOf(left) !== -1 && group.indexOf(right) !== -1);
67
+ return groups.some(group => group.includes(left) && group.includes(right));
68
68
  }
69
69
 
70
70
  /**
@@ -160,7 +160,7 @@ module.exports = {
160
160
  return REQ_COMPUTED;
161
161
  }
162
162
 
163
- if (BUILTIN_MODULES.indexOf(arg.value) !== -1) {
163
+ if (BUILTIN_MODULES.includes(arg.value)) {
164
164
 
165
165
  // "var fs = require('fs');"
166
166
  return REQ_CORE;
@@ -56,7 +56,7 @@ module.exports = {
56
56
  const options = context.options[0] || {};
57
57
  const ignoreEOLComments = options.ignoreEOLComments;
58
58
  const exceptions = Object.assign({ Property: true }, options.exceptions);
59
- const hasExceptions = Object.keys(exceptions).filter(key => exceptions[key]).length > 0;
59
+ const hasExceptions = Object.keys(exceptions).some(key => exceptions[key]);
60
60
 
61
61
  /**
62
62
  * Formats value of given comment token for error message by truncating its length.
@@ -81,7 +81,7 @@ module.exports = {
81
81
  * @returns {void}
82
82
  */
83
83
  function checkVariable(variable) {
84
- if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
84
+ if (variable.writeable === false && !exceptions.includes(variable.name)) {
85
85
  variable.references.forEach(checkReference);
86
86
  }
87
87
  }
@@ -29,7 +29,7 @@ module.exports = {
29
29
  schema: [],
30
30
 
31
31
  messages: {
32
- preferLiteral: "The object literal notation {} is preferrable."
32
+ preferLiteral: "The object literal notation {} is preferable."
33
33
  }
34
34
  },
35
35
 
@@ -34,7 +34,7 @@ module.exports = {
34
34
  NewExpression(node) {
35
35
  const wrapperObjects = ["String", "Number", "Boolean"];
36
36
 
37
- if (wrapperObjects.indexOf(node.callee.name) > -1) {
37
+ if (wrapperObjects.includes(node.callee.name)) {
38
38
  context.report({
39
39
  node,
40
40
  messageId: "noConstructor",
@@ -23,7 +23,7 @@ module.exports = {
23
23
  schema: [],
24
24
 
25
25
  messages: {
26
- noOcatal: "Octal literals should not be used."
26
+ noOctal: "Octal literals should not be used."
27
27
  }
28
28
  },
29
29
 
@@ -35,7 +35,7 @@ module.exports = {
35
35
  if (typeof node.value === "number" && /^0[0-9]/u.test(node.raw)) {
36
36
  context.report({
37
37
  node,
38
- messageId: "noOcatal"
38
+ messageId: "noOctal"
39
39
  });
40
40
  }
41
41
  }
@@ -33,11 +33,11 @@ module.exports = {
33
33
  },
34
34
 
35
35
  create(context) {
36
- const DISALLOWED_PROPS = [
36
+ const DISALLOWED_PROPS = new Set([
37
37
  "hasOwnProperty",
38
38
  "isPrototypeOf",
39
39
  "propertyIsEnumerable"
40
- ];
40
+ ]);
41
41
 
42
42
  /**
43
43
  * Reports if a disallowed property is used in a CallExpression
@@ -54,7 +54,7 @@ module.exports = {
54
54
 
55
55
  const propName = astUtils.getStaticPropertyName(callee);
56
56
 
57
- if (propName !== null && DISALLOWED_PROPS.indexOf(propName) > -1) {
57
+ if (propName !== null && DISALLOWED_PROPS.has(propName)) {
58
58
  context.report({
59
59
  messageId: "prototypeBuildIn",
60
60
  loc: callee.property.loc,
@@ -15,8 +15,8 @@ const astUtils = require("./utils/ast-utils");
15
15
  // Helpers
16
16
  //------------------------------------------------------------------------------
17
17
 
18
- const FUNC_EXPR_NODE_TYPES = ["ArrowFunctionExpression", "FunctionExpression"];
19
- const CALL_EXPR_NODE_TYPE = ["CallExpression"];
18
+ const FUNC_EXPR_NODE_TYPES = new Set(["ArrowFunctionExpression", "FunctionExpression"]);
19
+ const CALL_EXPR_NODE_TYPE = new Set(["CallExpression"]);
20
20
  const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u;
21
21
  const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;
22
22
 
@@ -123,7 +123,7 @@ module.exports = {
123
123
  const { variableScope } = variable.scope;
124
124
 
125
125
 
126
- if (!(FUNC_EXPR_NODE_TYPES.includes(variableScope.block.type) && getOuterScope(variableScope) === shadowedVariable.scope)) {
126
+ if (!(FUNC_EXPR_NODE_TYPES.has(variableScope.block.type) && getOuterScope(variableScope) === shadowedVariable.scope)) {
127
127
  return false;
128
128
  }
129
129
 
@@ -132,7 +132,7 @@ module.exports = {
132
132
 
133
133
  const callExpression = findSelfOrAncestor(
134
134
  parent,
135
- node => CALL_EXPR_NODE_TYPE.includes(node.type)
135
+ node => CALL_EXPR_NODE_TYPE.has(node.type)
136
136
  );
137
137
 
138
138
  if (!callExpression) {
@@ -173,7 +173,7 @@ module.exports = {
173
173
  * @returns {boolean} Whether or not the variable name is allowed.
174
174
  */
175
175
  function isAllowed(variable) {
176
- return options.allow.indexOf(variable.name) !== -1;
176
+ return options.allow.includes(variable.name);
177
177
  }
178
178
 
179
179
  /**
@@ -37,7 +37,7 @@ module.exports = {
37
37
 
38
38
  ArrayExpression(node) {
39
39
 
40
- const emptySpot = node.elements.indexOf(null) > -1;
40
+ const emptySpot = node.elements.includes(null);
41
41
 
42
42
  if (emptySpot) {
43
43
  context.report({ node, messageId: "unexpectedSparseArray" });
@@ -86,7 +86,7 @@ module.exports = {
86
86
  * @private
87
87
  */
88
88
  function isAllowed(identifier) {
89
- return ALLOWED_VARIABLES.some(ident => ident === identifier);
89
+ return ALLOWED_VARIABLES.includes(identifier);
90
90
  }
91
91
 
92
92
  /**
@@ -124,7 +124,7 @@ module.exports = {
124
124
  */
125
125
  return (parent.type === "Program" || parent.type === "BlockStatement" &&
126
126
  (/Function/u.test(grandparent.type))) &&
127
- directives(parent).indexOf(node) >= 0;
127
+ directives(parent).includes(node);
128
128
  }
129
129
 
130
130
  /**
@@ -259,7 +259,7 @@ module.exports = {
259
259
  let scope = ref.from;
260
260
 
261
261
  while (scope) {
262
- if (nodes.indexOf(scope.block) >= 0) {
262
+ if (nodes.includes(scope.block)) {
263
263
  return true;
264
264
  }
265
265
 
@@ -484,12 +484,12 @@ module.exports = {
484
484
  }
485
485
 
486
486
  /**
487
- * Determine if an identifier is used either in for-in loops.
487
+ * Determine if an identifier is used either in for-in or for-of loops.
488
488
  * @param {Reference} ref The reference to check.
489
489
  * @returns {boolean} whether reference is used in the for-in loops
490
490
  * @private
491
491
  */
492
- function isForInRef(ref) {
492
+ function isForInOfRef(ref) {
493
493
  let target = ref.identifier.parent;
494
494
 
495
495
 
@@ -498,7 +498,7 @@ module.exports = {
498
498
  target = target.parent.parent;
499
499
  }
500
500
 
501
- if (target.type !== "ForInStatement") {
501
+ if (target.type !== "ForInStatement" && target.type !== "ForOfStatement") {
502
502
  return false;
503
503
  }
504
504
 
@@ -531,7 +531,7 @@ module.exports = {
531
531
  let rhsNode = null;
532
532
 
533
533
  return variable.references.some(ref => {
534
- if (isForInRef(ref)) {
534
+ if (isForInOfRef(ref)) {
535
535
  return true;
536
536
  }
537
537
 
@@ -21,6 +21,7 @@ function parseOptions(options) {
21
21
  let functions = true;
22
22
  let classes = true;
23
23
  let variables = true;
24
+ let allowNamedExports = false;
24
25
 
25
26
  if (typeof options === "string") {
26
27
  functions = (options !== "nofunc");
@@ -28,9 +29,10 @@ function parseOptions(options) {
28
29
  functions = options.functions !== false;
29
30
  classes = options.classes !== false;
30
31
  variables = options.variables !== false;
32
+ allowNamedExports = !!options.allowNamedExports;
31
33
  }
32
34
 
33
- return { functions, classes, variables };
35
+ return { functions, classes, variables, allowNamedExports };
34
36
  }
35
37
 
36
38
  /**
@@ -240,7 +242,8 @@ module.exports = {
240
242
  properties: {
241
243
  functions: { type: "boolean" },
242
244
  classes: { type: "boolean" },
243
- variables: { type: "boolean" }
245
+ variables: { type: "boolean" },
246
+ allowNamedExports: { type: "boolean" }
244
247
  },
245
248
  additionalProperties: false
246
249
  }
@@ -273,6 +276,16 @@ module.exports = {
273
276
  return false;
274
277
  }
275
278
 
279
+ const { identifier } = reference;
280
+
281
+ if (
282
+ options.allowNamedExports &&
283
+ identifier.parent.type === "ExportSpecifier" &&
284
+ identifier.parent.local === identifier
285
+ ) {
286
+ return false;
287
+ }
288
+
276
289
  const variable = reference.resolved;
277
290
 
278
291
  if (!variable || variable.defs.length === 0) {
@@ -22,7 +22,7 @@ const astUtils = require("./utils/ast-utils");
22
22
  * shorthand form.
23
23
  */
24
24
  function isCommutativeOperatorWithShorthand(operator) {
25
- return ["*", "&", "^", "|"].indexOf(operator) >= 0;
25
+ return ["*", "&", "^", "|"].includes(operator);
26
26
  }
27
27
 
28
28
  /**
@@ -33,7 +33,7 @@ function isCommutativeOperatorWithShorthand(operator) {
33
33
  * a shorthand form.
34
34
  */
35
35
  function isNonCommutativeOperatorWithShorthand(operator) {
36
- return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].indexOf(operator) >= 0;
36
+ return ["+", "-", "/", "%", "<<", ">>", ">>>", "**"].includes(operator);
37
37
  }
38
38
 
39
39
  //------------------------------------------------------------------------------
@@ -60,7 +60,7 @@ function canBecomeVariableDeclaration(identifier) {
60
60
  */
61
61
  function isOuterVariableInDestructing(name, initScope) {
62
62
 
63
- if (initScope.through.find(ref => ref.resolved && ref.resolved.name === name)) {
63
+ if (initScope.through.some(ref => ref.resolved && ref.resolved.name === name)) {
64
64
  return true;
65
65
  }
66
66
 
@@ -106,7 +106,7 @@ module.exports = {
106
106
  const methodName = (node.callee.property || {}).name;
107
107
  const isReflectCall = (node.callee.object || {}).name === "Reflect";
108
108
  const hasReflectSubstitute = Object.prototype.hasOwnProperty.call(reflectSubstitutes, methodName);
109
- const userConfiguredException = exceptions.indexOf(methodName) !== -1;
109
+ const userConfiguredException = exceptions.includes(methodName);
110
110
 
111
111
  if (hasReflectSubstitute && !isReflectCall && !userConfiguredException) {
112
112
  report(node, existingNames[methodName], reflectSubstitutes[methodName]);
@@ -115,7 +115,7 @@ module.exports = {
115
115
  UnaryExpression(node) {
116
116
  const isDeleteOperator = node.operator === "delete";
117
117
  const targetsIdentifier = node.argument.type === "Identifier";
118
- const userConfiguredException = exceptions.indexOf("delete") !== -1;
118
+ const userConfiguredException = exceptions.includes("delete");
119
119
 
120
120
  if (isDeleteOperator && !targetsIdentifier && !userConfiguredException) {
121
121
  report(node, "the delete keyword", "Reflect.deleteProperty");
@@ -47,7 +47,7 @@ function isStaticTemplateLiteral(node) {
47
47
  return node.type === "TemplateLiteral" && node.expressions.length === 0;
48
48
  }
49
49
 
50
- const validPrecedingTokens = [
50
+ const validPrecedingTokens = new Set([
51
51
  "(",
52
52
  ";",
53
53
  "[",
@@ -110,7 +110,7 @@ const validPrecedingTokens = [
110
110
  "debugger",
111
111
  "case",
112
112
  "throw"
113
- ];
113
+ ]);
114
114
 
115
115
 
116
116
  //------------------------------------------------------------------------------
@@ -334,7 +334,7 @@ module.exports = {
334
334
 
335
335
  const tokenBefore = sourceCode.getTokenBefore(node);
336
336
 
337
- if (tokenBefore && !validPrecedingTokens.includes(tokenBefore.value)) {
337
+ if (tokenBefore && !validPrecedingTokens.has(tokenBefore.value)) {
338
338
  noFix = true;
339
339
  }
340
340
 
@@ -95,7 +95,7 @@ module.exports = {
95
95
  * @returns {boolean} `true` if it is an ES3 token.
96
96
  */
97
97
  function isKeyword(tokenStr) {
98
- return keywords.indexOf(tokenStr) >= 0;
98
+ return keywords.includes(tokenStr);
99
99
  }
100
100
 
101
101
  /**
@@ -108,7 +108,7 @@ module.exports = {
108
108
  */
109
109
  function areQuotesRedundant(rawKey, tokens, skipNumberLiterals) {
110
110
  return tokens.length === 1 && tokens[0].start === 0 && tokens[0].end === rawKey.length &&
111
- (["Identifier", "Keyword", "Null", "Boolean"].indexOf(tokens[0].type) >= 0 ||
111
+ (["Identifier", "Keyword", "Null", "Boolean"].includes(tokens[0].type) ||
112
112
  (tokens[0].type === "Numeric" && !skipNumberLiterals && String(+tokens[0].value) === tokens[0].value));
113
113
  }
114
114
 
@@ -283,7 +283,7 @@ module.exports = {
283
283
  astUtils.isSurroundedBy(rawVal, settings.quote);
284
284
 
285
285
  if (!isValid && avoidEscape) {
286
- isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
286
+ isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.includes(settings.quote);
287
287
  }
288
288
 
289
289
  if (!isValid) {
@@ -39,7 +39,7 @@ function escapeAndRepeat(s) {
39
39
  function parseMarkersOption(markers) {
40
40
 
41
41
  // `*` is a marker for JSDoc comments.
42
- if (markers.indexOf("*") === -1) {
42
+ if (!markers.includes("*")) {
43
43
  return markers.concat("*");
44
44
  }
45
45
 
@@ -405,7 +405,7 @@ module.exports = {
405
405
  loc: getAbsoluteRange(jsdocNode, param),
406
406
  data: { name: param.name }
407
407
  });
408
- } else if (param.name.indexOf(".") === -1) {
408
+ } else if (!param.name.includes(".")) {
409
409
  paramTagsByName[param.name] = param;
410
410
  }
411
411
  });