eslint 8.15.0 → 8.16.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 (51) hide show
  1. package/README.md +1 -1
  2. package/lib/cli-engine/cli-engine.js +2 -4
  3. package/lib/cli-engine/lint-result-cache.js +1 -1
  4. package/lib/linter/code-path-analysis/code-path-segment.js +1 -1
  5. package/lib/linter/code-path-analysis/code-path-state.js +1 -1
  6. package/lib/linter/code-path-analysis/code-path.js +1 -1
  7. package/lib/rules/accessor-pairs.js +4 -4
  8. package/lib/rules/callback-return.js +2 -2
  9. package/lib/rules/capitalized-comments.js +1 -1
  10. package/lib/rules/consistent-this.js +1 -1
  11. package/lib/rules/dot-notation.js +2 -2
  12. package/lib/rules/function-paren-newline.js +7 -4
  13. package/lib/rules/global-require.js +3 -3
  14. package/lib/rules/indent-legacy.js +2 -2
  15. package/lib/rules/indent.js +1 -1
  16. package/lib/rules/jsx-quotes.js +1 -1
  17. package/lib/rules/lines-around-comment.js +3 -3
  18. package/lib/rules/max-lines.js +2 -2
  19. package/lib/rules/newline-before-return.js +1 -1
  20. package/lib/rules/no-bitwise.js +2 -2
  21. package/lib/rules/no-console.js +1 -1
  22. package/lib/rules/no-control-regex.js +23 -10
  23. package/lib/rules/no-empty-function.js +1 -1
  24. package/lib/rules/no-extra-boolean-cast.js +3 -3
  25. package/lib/rules/no-extra-semi.js +1 -1
  26. package/lib/rules/no-global-assign.js +1 -1
  27. package/lib/rules/no-implicit-coercion.js +6 -6
  28. package/lib/rules/no-magic-numbers.js +3 -3
  29. package/lib/rules/no-misleading-character-class.js +90 -17
  30. package/lib/rules/no-mixed-operators.js +1 -1
  31. package/lib/rules/no-mixed-requires.js +1 -1
  32. package/lib/rules/no-multi-spaces.js +1 -1
  33. package/lib/rules/no-native-reassign.js +1 -1
  34. package/lib/rules/no-new-wrappers.js +1 -1
  35. package/lib/rules/no-prototype-builtins.js +3 -3
  36. package/lib/rules/no-shadow.js +5 -5
  37. package/lib/rules/no-sparse-arrays.js +1 -1
  38. package/lib/rules/no-underscore-dangle.js +1 -1
  39. package/lib/rules/no-unused-expressions.js +1 -1
  40. package/lib/rules/no-unused-vars.js +1 -1
  41. package/lib/rules/operator-assignment.js +2 -2
  42. package/lib/rules/prefer-const.js +1 -1
  43. package/lib/rules/prefer-reflect.js +2 -2
  44. package/lib/rules/prefer-regex-literals.js +3 -3
  45. package/lib/rules/quote-props.js +2 -2
  46. package/lib/rules/quotes.js +1 -1
  47. package/lib/rules/spaced-comment.js +1 -1
  48. package/lib/rules/valid-jsdoc.js +1 -1
  49. package/lib/rules/valid-typeof.js +4 -4
  50. package/lib/rules/yoda.js +1 -1
  51. package/package.json +21 -6
package/README.md CHANGED
@@ -297,7 +297,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
297
297
  <!--sponsorsstart-->
298
298
  <h3>Platinum Sponsors</h3>
299
299
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
300
- <p><a href="https://contra.com"><img src="https://images.opencollective.com/contra1/c70f93f/logo.png" alt="Contra" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
300
+ <p><a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a></p><h3>Silver Sponsors</h3>
301
301
  <p><a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
302
302
  <p><a href="https://launchdarkly.com"><img src="https://images.opencollective.com/launchdarkly/574bb9e/logo.png" alt="launchdarkly" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a></p>
303
303
  <!--sponsorsend-->
@@ -366,9 +366,7 @@ function *iterateRuleDeprecationWarnings(usedConfigArrays) {
366
366
 
367
367
  // Flatten used configs.
368
368
  /** @type {ExtractedConfig[]} */
369
- const configs = [].concat(
370
- ...usedConfigArrays.map(getUsedExtractedConfigs)
371
- );
369
+ const configs = usedConfigArrays.flatMap(getUsedExtractedConfigs);
372
370
 
373
371
  // Traverse rule configs.
374
372
  for (const config of configs) {
@@ -1023,7 +1021,7 @@ class CLIEngine {
1023
1021
  let formatterPath;
1024
1022
 
1025
1023
  // if there's a slash, then it's a file (TODO: this check seems dubious for scoped npm packages)
1026
- if (!namespace && normalizedFormatName.indexOf("/") > -1) {
1024
+ if (!namespace && normalizedFormatName.includes("/")) {
1027
1025
  formatterPath = path.resolve(cwd, normalizedFormatName);
1028
1026
  } else {
1029
1027
  try {
@@ -36,7 +36,7 @@ const invalidCacheStrategyErrorMessage = `Cache strategy must be one of: ${valid
36
36
  */
37
37
  function isValidCacheStrategy(cacheStrategy) {
38
38
  return (
39
- validCacheStrategies.indexOf(cacheStrategy) !== -1
39
+ validCacheStrategies.includes(cacheStrategy)
40
40
  );
41
41
  }
42
42
 
@@ -100,7 +100,7 @@ class CodePathSegment {
100
100
  * @returns {boolean} `true` if the segment is coming from the end of a loop.
101
101
  */
102
102
  isLoopedPrevSegment(segment) {
103
- return this.internal.loopedPrevSegments.indexOf(segment) !== -1;
103
+ return this.internal.loopedPrevSegments.includes(segment);
104
104
  }
105
105
 
106
106
  /**
@@ -33,7 +33,7 @@ function addToReturnedOrThrown(dest, others, all, segments) {
33
33
  const segment = segments[i];
34
34
 
35
35
  dest.push(segment);
36
- if (others.indexOf(segment) === -1) {
36
+ if (!others.includes(segment)) {
37
37
  all.push(segment);
38
38
  }
39
39
  }
@@ -212,7 +212,7 @@ class CodePath {
212
212
  }
213
213
 
214
214
  // Reset the flag of skipping if all branches have been skipped.
215
- if (skippedSegment && segment.prevSegments.indexOf(skippedSegment) !== -1) {
215
+ if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
216
216
  skippedSegment = null;
217
217
  }
218
218
  visited[segment.id] = true;
@@ -299,12 +299,12 @@ module.exports = {
299
299
  * @private
300
300
  */
301
301
  function checkPropertyDescriptor(node) {
302
- const namesToCheck = node.properties
302
+ const namesToCheck = new Set(node.properties
303
303
  .filter(p => p.type === "Property" && p.kind === "init" && !p.computed)
304
- .map(({ key }) => key.name);
304
+ .map(({ key }) => key.name));
305
305
 
306
- const hasGetter = namesToCheck.includes("get");
307
- const hasSetter = namesToCheck.includes("set");
306
+ const hasGetter = namesToCheck.has("get");
307
+ const hasSetter = namesToCheck.has("set");
308
308
 
309
309
  if (checkSetWithoutGet && hasSetter && !hasGetter) {
310
310
  report(node, "missingGetter");
@@ -53,7 +53,7 @@ module.exports = {
53
53
  if (!node.parent) {
54
54
  return null;
55
55
  }
56
- if (types.indexOf(node.parent.type) === -1) {
56
+ if (!types.includes(node.parent.type)) {
57
57
  return findClosestParentOfType(node.parent, types);
58
58
  }
59
59
  return node.parent;
@@ -87,7 +87,7 @@ module.exports = {
87
87
  * @returns {boolean} Whether or not this function matches our callback name.
88
88
  */
89
89
  function isCallback(node) {
90
- return containsOnlyIdentifiers(node.callee) && callbacks.indexOf(sourceCode.getText(node.callee)) > -1;
90
+ return containsOnlyIdentifiers(node.callee) && callbacks.includes(sourceCode.getText(node.callee));
91
91
  }
92
92
 
93
93
  /**
@@ -185,7 +185,7 @@ module.exports = {
185
185
 
186
186
  return Boolean(
187
187
  previousTokenOrComment &&
188
- ["Block", "Line"].indexOf(previousTokenOrComment.type) !== -1
188
+ ["Block", "Line"].includes(previousTokenOrComment.type)
189
189
  );
190
190
  }
191
191
 
@@ -65,7 +65,7 @@ module.exports = {
65
65
  function checkAssignment(node, name, value) {
66
66
  const isThis = value.type === "ThisExpression";
67
67
 
68
- if (aliases.indexOf(name) !== -1) {
68
+ if (aliases.includes(name)) {
69
69
  if (!isThis || node.operator && node.operator !== "=") {
70
70
  reportBadAssignment(node, name);
71
71
  }
@@ -76,7 +76,7 @@ module.exports = {
76
76
  function checkComputedProperty(node, value) {
77
77
  if (
78
78
  validIdentifier.test(value) &&
79
- (allowKeywords || keywords.indexOf(String(value)) === -1) &&
79
+ (allowKeywords || !keywords.includes(String(value))) &&
80
80
  !(allowPattern && allowPattern.test(value))
81
81
  ) {
82
82
  const formattedValue = node.property.type === "Literal" ? JSON.stringify(value) : `\`${value}\``;
@@ -142,7 +142,7 @@ module.exports = {
142
142
  !allowKeywords &&
143
143
  !node.computed &&
144
144
  node.property.type === "Identifier" &&
145
- keywords.indexOf(String(node.property.name)) !== -1
145
+ keywords.includes(String(node.property.name))
146
146
  ) {
147
147
  context.report({
148
148
  node: node.property,
@@ -191,10 +191,13 @@ module.exports = {
191
191
  function getParenTokens(node) {
192
192
  switch (node.type) {
193
193
  case "NewExpression":
194
- if (!node.arguments.length && !(
195
- astUtils.isOpeningParenToken(sourceCode.getLastToken(node, { skip: 1 })) &&
196
- astUtils.isClosingParenToken(sourceCode.getLastToken(node))
197
- )) {
194
+ if (!node.arguments.length &&
195
+ !(
196
+ astUtils.isOpeningParenToken(sourceCode.getLastToken(node, { skip: 1 })) &&
197
+ astUtils.isClosingParenToken(sourceCode.getLastToken(node)) &&
198
+ node.callee.range[1] < node.range[1]
199
+ )
200
+ ) {
198
201
 
199
202
  // If the NewExpression does not have parens (e.g. `new Foo`), return null.
200
203
  return null;
@@ -6,7 +6,7 @@
6
6
 
7
7
  "use strict";
8
8
 
9
- const ACCEPTABLE_PARENTS = [
9
+ const ACCEPTABLE_PARENTS = new Set([
10
10
  "AssignmentExpression",
11
11
  "VariableDeclarator",
12
12
  "MemberExpression",
@@ -16,7 +16,7 @@ const ACCEPTABLE_PARENTS = [
16
16
  "Program",
17
17
  "VariableDeclaration",
18
18
  "ChainExpression"
19
- ];
19
+ ]);
20
20
 
21
21
  /**
22
22
  * Finds the eslint-scope reference in the given scope.
@@ -75,7 +75,7 @@ module.exports = {
75
75
  const currentScope = context.getScope();
76
76
 
77
77
  if (node.callee.name === "require" && !isShadowed(currentScope, node.callee)) {
78
- const isGoodRequire = context.getAncestors().every(parent => ACCEPTABLE_PARENTS.indexOf(parent.type) > -1);
78
+ const isGoodRequire = context.getAncestors().every(parent => ACCEPTABLE_PARENTS.has(parent.type));
79
79
 
80
80
  if (!isGoodRequire) {
81
81
  context.report({ node, messageId: "unexpected" });
@@ -753,7 +753,7 @@ module.exports = {
753
753
  if (typeof options.CallExpression.arguments === "number") {
754
754
  nodeIndent += options.CallExpression.arguments * indentSize;
755
755
  } else if (options.CallExpression.arguments === "first") {
756
- if (parent.arguments.indexOf(node) !== -1) {
756
+ if (parent.arguments.includes(node)) {
757
757
  nodeIndent = parent.arguments[0].loc.start.column;
758
758
  }
759
759
  } else {
@@ -840,7 +840,7 @@ module.exports = {
840
840
  "IfStatement", "WhileStatement", "ForStatement", "ForInStatement", "ForOfStatement", "DoWhileStatement", "ClassDeclaration", "TryStatement"
841
841
  ];
842
842
 
843
- if (node.parent && statementsWithProperties.indexOf(node.parent.type) !== -1 && isNodeBodyBlock(node)) {
843
+ if (node.parent && statementsWithProperties.includes(node.parent.type) && isNodeBodyBlock(node)) {
844
844
  indent = getNodeIndent(node.parent).goodChar;
845
845
  } else if (node.parent && node.parent.type === "CatchClause") {
846
846
  indent = getNodeIndent(node.parent.parent).goodChar;
@@ -796,7 +796,7 @@ module.exports = {
796
796
  let statement = node.parent && node.parent.parent;
797
797
 
798
798
  while (
799
- statement.type === "UnaryExpression" && ["!", "~", "+", "-"].indexOf(statement.operator) > -1 ||
799
+ statement.type === "UnaryExpression" && ["!", "~", "+", "-"].includes(statement.operator) ||
800
800
  statement.type === "AssignmentExpression" ||
801
801
  statement.type === "LogicalExpression" ||
802
802
  statement.type === "SequenceExpression" ||
@@ -70,7 +70,7 @@ module.exports = {
70
70
  * @public
71
71
  */
72
72
  function usesExpectedQuotes(node) {
73
- return node.value.indexOf(setting.quote) !== -1 || astUtils.isSurroundedBy(node.raw, setting.quote);
73
+ return node.value.includes(setting.quote) || astUtils.isSurroundedBy(node.raw, setting.quote);
74
74
  }
75
75
 
76
76
  return {
@@ -141,7 +141,7 @@ module.exports = {
141
141
  comments = sourceCode.getAllComments(),
142
142
  commentLines = getCommentLineNums(comments),
143
143
  emptyLines = getEmptyLineNums(lines),
144
- commentAndEmptyLines = commentLines.concat(emptyLines);
144
+ commentAndEmptyLines = new Set(commentLines.concat(emptyLines));
145
145
 
146
146
  /**
147
147
  * Returns whether or not comments are on lines starting with or ending with code
@@ -393,7 +393,7 @@ module.exports = {
393
393
  const nextTokenOrComment = sourceCode.getTokenAfter(token, { includeComments: true });
394
394
 
395
395
  // check for newline before
396
- if (!exceptionStartAllowed && before && !commentAndEmptyLines.includes(prevLineNum) &&
396
+ if (!exceptionStartAllowed && before && !commentAndEmptyLines.has(prevLineNum) &&
397
397
  !(astUtils.isCommentToken(previousTokenOrComment) && astUtils.isTokenOnSameLine(previousTokenOrComment, token))) {
398
398
  const lineStart = token.range[0] - token.loc.start.column;
399
399
  const range = [lineStart, lineStart];
@@ -408,7 +408,7 @@ module.exports = {
408
408
  }
409
409
 
410
410
  // check for newline after
411
- if (!exceptionEndAllowed && after && !commentAndEmptyLines.includes(nextLineNum) &&
411
+ if (!exceptionEndAllowed && after && !commentAndEmptyLines.has(nextLineNum) &&
412
412
  !(astUtils.isCommentToken(nextTokenOrComment) && astUtils.isTokenOnSameLine(token, nextTokenOrComment))) {
413
413
  context.report({
414
414
  node: token,
@@ -159,10 +159,10 @@ module.exports = {
159
159
  if (skipComments) {
160
160
  const comments = sourceCode.getAllComments();
161
161
 
162
- const commentLines = comments.flatMap(getLinesWithoutCode);
162
+ const commentLines = new Set(comments.flatMap(getLinesWithoutCode));
163
163
 
164
164
  lines = lines.filter(
165
- l => !commentLines.includes(l.lineNumber)
165
+ l => !commentLines.has(l.lineNumber)
166
166
  );
167
167
  }
168
168
 
@@ -47,7 +47,7 @@ module.exports = {
47
47
  function isPrecededByTokens(node, testTokens) {
48
48
  const tokenBefore = sourceCode.getTokenBefore(node);
49
49
 
50
- return testTokens.some(token => tokenBefore.value === token);
50
+ return testTokens.includes(tokenBefore.value);
51
51
  }
52
52
 
53
53
  /**
@@ -76,7 +76,7 @@ module.exports = {
76
76
  * @returns {boolean} Whether or not the node has a bitwise operator.
77
77
  */
78
78
  function hasBitwiseOperator(node) {
79
- return BITWISE_OPERATORS.indexOf(node.operator) !== -1;
79
+ return BITWISE_OPERATORS.includes(node.operator);
80
80
  }
81
81
 
82
82
  /**
@@ -85,7 +85,7 @@ module.exports = {
85
85
  * @returns {boolean} Whether or not the node has a bitwise operator.
86
86
  */
87
87
  function allowedOperator(node) {
88
- return allowed.indexOf(node.operator) !== -1;
88
+ return allowed.includes(node.operator);
89
89
  }
90
90
 
91
91
  /**
@@ -72,7 +72,7 @@ module.exports = {
72
72
  function isAllowed(node) {
73
73
  const propertyName = astUtils.getStaticPropertyName(node);
74
74
 
75
- return propertyName && allowed.indexOf(propertyName) !== -1;
75
+ return propertyName && allowed.includes(propertyName);
76
76
  }
77
77
 
78
78
  /**
@@ -30,10 +30,12 @@ const collector = new (class {
30
30
  }
31
31
  }
32
32
 
33
- collectControlChars(regexpStr) {
33
+ collectControlChars(regexpStr, flags) {
34
+ const uFlag = typeof flags === "string" && flags.includes("u");
35
+
34
36
  try {
35
37
  this._source = regexpStr;
36
- this._validator.validatePattern(regexpStr); // Call onCharacter hook
38
+ this._validator.validatePattern(regexpStr, void 0, void 0, uFlag); // Call onCharacter hook
37
39
  } catch {
38
40
 
39
41
  // Ignore syntax errors in RegExp.
@@ -68,13 +70,15 @@ module.exports = {
68
70
 
69
71
  /**
70
72
  * Get the regex expression
71
- * @param {ASTNode} node node to evaluate
72
- * @returns {RegExp|null} Regex if found else null
73
+ * @param {ASTNode} node `Literal` node to evaluate
74
+ * @returns {{ pattern: string, flags: string | null } | null} Regex if found (the given node is either a regex literal
75
+ * or a string literal that is the pattern argument of a RegExp constructor call). Otherwise `null`. If flags cannot be determined,
76
+ * the `flags` property will be `null`.
73
77
  * @private
74
78
  */
75
- function getRegExpPattern(node) {
79
+ function getRegExp(node) {
76
80
  if (node.regex) {
77
- return node.regex.pattern;
81
+ return node.regex;
78
82
  }
79
83
  if (typeof node.value === "string" &&
80
84
  (node.parent.type === "NewExpression" || node.parent.type === "CallExpression") &&
@@ -82,7 +86,15 @@ module.exports = {
82
86
  node.parent.callee.name === "RegExp" &&
83
87
  node.parent.arguments[0] === node
84
88
  ) {
85
- return node.value;
89
+ const pattern = node.value;
90
+ const flags =
91
+ node.parent.arguments.length > 1 &&
92
+ node.parent.arguments[1].type === "Literal" &&
93
+ typeof node.parent.arguments[1].value === "string"
94
+ ? node.parent.arguments[1].value
95
+ : null;
96
+
97
+ return { pattern, flags };
86
98
  }
87
99
 
88
100
  return null;
@@ -90,10 +102,11 @@ module.exports = {
90
102
 
91
103
  return {
92
104
  Literal(node) {
93
- const pattern = getRegExpPattern(node);
105
+ const regExp = getRegExp(node);
94
106
 
95
- if (pattern) {
96
- const controlCharacters = collector.collectControlChars(pattern);
107
+ if (regExp) {
108
+ const { pattern, flags } = regExp;
109
+ const controlCharacters = collector.collectControlChars(pattern, flags);
97
110
 
98
111
  if (controlCharacters.length > 0) {
99
112
  context.report({
@@ -144,7 +144,7 @@ module.exports = {
144
144
  filter: astUtils.isCommentToken
145
145
  });
146
146
 
147
- if (allowed.indexOf(kind) === -1 &&
147
+ if (!allowed.includes(kind) &&
148
148
  node.body.type === "BlockStatement" &&
149
149
  node.body.body.length === 0 &&
150
150
  innerComments.length === 0
@@ -51,13 +51,13 @@ module.exports = {
51
51
  const sourceCode = context.getSourceCode();
52
52
 
53
53
  // Node types which have a test which will coerce values to booleans.
54
- const BOOLEAN_NODE_TYPES = [
54
+ const BOOLEAN_NODE_TYPES = new Set([
55
55
  "IfStatement",
56
56
  "DoWhileStatement",
57
57
  "WhileStatement",
58
58
  "ConditionalExpression",
59
59
  "ForStatement"
60
- ];
60
+ ]);
61
61
 
62
62
  /**
63
63
  * Check if a node is a Boolean function or constructor.
@@ -95,7 +95,7 @@ module.exports = {
95
95
  (isBooleanFunctionOrConstructorCall(node.parent) &&
96
96
  node === node.parent.arguments[0]) ||
97
97
 
98
- (BOOLEAN_NODE_TYPES.indexOf(node.parent.type) !== -1 &&
98
+ (BOOLEAN_NODE_TYPES.has(node.parent.type) &&
99
99
  node === node.parent.test) ||
100
100
 
101
101
  // !<bool>
@@ -98,7 +98,7 @@ module.exports = {
98
98
  "WithStatement"
99
99
  ];
100
100
 
101
- if (allowedParentTypes.indexOf(parent.type) === -1) {
101
+ if (!allowedParentTypes.includes(parent.type)) {
102
102
  report(node);
103
103
  }
104
104
  },
@@ -78,7 +78,7 @@ module.exports = {
78
78
  * @returns {void}
79
79
  */
80
80
  function checkVariable(variable) {
81
- if (variable.writeable === false && exceptions.indexOf(variable.name) === -1) {
81
+ if (variable.writeable === false && !exceptions.includes(variable.name)) {
82
82
  variable.references.forEach(checkReference);
83
83
  }
84
84
  }
@@ -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));
@@ -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
  }
@@ -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",
@@ -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
 
@@ -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
  });
@@ -42,8 +42,8 @@ module.exports = {
42
42
 
43
43
  create(context) {
44
44
 
45
- const VALID_TYPES = ["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"],
46
- OPERATORS = ["==", "===", "!=", "!=="];
45
+ const VALID_TYPES = new Set(["symbol", "undefined", "object", "boolean", "number", "string", "function", "bigint"]),
46
+ OPERATORS = new Set(["==", "===", "!=", "!=="]);
47
47
 
48
48
  const requireStringLiterals = context.options[0] && context.options[0].requireStringLiterals;
49
49
 
@@ -85,13 +85,13 @@ module.exports = {
85
85
  if (isTypeofExpression(node)) {
86
86
  const parent = context.getAncestors().pop();
87
87
 
88
- if (parent.type === "BinaryExpression" && OPERATORS.indexOf(parent.operator) !== -1) {
88
+ if (parent.type === "BinaryExpression" && OPERATORS.has(parent.operator)) {
89
89
  const sibling = parent.left === node ? parent.right : parent.left;
90
90
 
91
91
  if (sibling.type === "Literal" || sibling.type === "TemplateLiteral" && !sibling.expressions.length) {
92
92
  const value = sibling.type === "Literal" ? sibling.value : sibling.quasis[0].value.cooked;
93
93
 
94
- if (VALID_TYPES.indexOf(value) === -1) {
94
+ if (!VALID_TYPES.has(value)) {
95
95
  context.report({ node: sibling, messageId: "invalidValue" });
96
96
  }
97
97
  } else if (sibling.type === "Identifier" && sibling.name === "undefined" && isReferenceToGlobalVariable(sibling)) {
package/lib/rules/yoda.js CHANGED
@@ -39,7 +39,7 @@ function isEqualityOperator(operator) {
39
39
  * @returns {boolean} Whether the operator is used in range tests.
40
40
  */
41
41
  function isRangeTestOperator(operator) {
42
- return ["<", "<="].indexOf(operator) >= 0;
42
+ return ["<", "<="].includes(operator);
43
43
  }
44
44
 
45
45
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "8.15.0",
3
+ "version": "8.16.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "bin": {
@@ -25,14 +25,19 @@
25
25
  "publish-release": "node Makefile.js publishRelease",
26
26
  "gensite": "node Makefile.js gensite",
27
27
  "webpack": "node Makefile.js webpack",
28
- "perf": "node Makefile.js perf"
28
+ "perf": "node Makefile.js perf",
29
+ "docs:update-links": "node tools/fetch-docs-links.js"
29
30
  },
30
31
  "gitHooks": {
31
32
  "pre-commit": "lint-staged"
32
33
  },
33
34
  "lint-staged": {
34
35
  "*.js": "eslint --fix",
35
- "*.md": "markdownlint --fix"
36
+ "*.md": "markdownlint --fix",
37
+ "docs/src/rules/*.md": [
38
+ "node tools/fetch-docs-links.js",
39
+ "git add docs/src/_data/further_reading_links.json"
40
+ ]
36
41
  },
37
42
  "files": [
38
43
  "LICENSE",
@@ -47,7 +52,7 @@
47
52
  "homepage": "https://eslint.org",
48
53
  "bugs": "https://github.com/eslint/eslint/issues/",
49
54
  "dependencies": {
50
- "@eslint/eslintrc": "^1.2.3",
55
+ "@eslint/eslintrc": "^1.3.0",
51
56
  "@humanwhocodes/config-array": "^0.9.2",
52
57
  "ajv": "^6.10.0",
53
58
  "chalk": "^4.0.0",
@@ -65,7 +70,7 @@
65
70
  "file-entry-cache": "^6.0.1",
66
71
  "functional-red-black-tree": "^1.0.1",
67
72
  "glob-parent": "^6.0.1",
68
- "globals": "^13.6.0",
73
+ "globals": "^13.15.0",
69
74
  "ignore": "^5.2.0",
70
75
  "import-fresh": "^3.0.0",
71
76
  "imurmurhash": "^0.1.4",
@@ -96,15 +101,19 @@
96
101
  "eslint": "file:.",
97
102
  "eslint-config-eslint": "file:packages/eslint-config-eslint",
98
103
  "eslint-plugin-eslint-comments": "^3.2.0",
99
- "eslint-plugin-eslint-plugin": "^4.0.1",
104
+ "eslint-plugin-eslint-plugin": "^4.2.0",
100
105
  "eslint-plugin-internal-rules": "file:tools/internal-rules",
101
106
  "eslint-plugin-jsdoc": "^37.0.0",
102
107
  "eslint-plugin-node": "^11.1.0",
108
+ "eslint-plugin-unicorn": "^42.0.0",
103
109
  "eslint-release": "^3.2.0",
104
110
  "eslump": "^3.0.0",
105
111
  "esprima": "^4.0.1",
112
+ "fast-glob": "^3.2.11",
106
113
  "fs-teardown": "^0.1.3",
107
114
  "glob": "^7.1.6",
115
+ "got": "^11.8.3",
116
+ "gray-matter": "^4.0.3",
108
117
  "jsdoc": "^3.5.5",
109
118
  "karma": "^6.1.1",
110
119
  "karma-chrome-launcher": "^3.1.0",
@@ -117,6 +126,12 @@
117
126
  "markdownlint-cli": "^0.30.0",
118
127
  "marked": "^4.0.8",
119
128
  "memfs": "^3.0.1",
129
+ "metascraper": "^5.25.7",
130
+ "metascraper-description": "^5.25.7",
131
+ "metascraper-image": "^5.29.3",
132
+ "metascraper-logo": "^5.25.7",
133
+ "metascraper-logo-favicon": "^5.25.7",
134
+ "metascraper-title": "^5.25.7",
120
135
  "mocha": "^8.3.2",
121
136
  "mocha-junit-reporter": "^2.0.0",
122
137
  "node-polyfill-webpack-plugin": "^1.0.3",