eslint 8.13.0 → 8.17.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 +17 -12
  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/rules/accessor-pairs.js +4 -4
  10. package/lib/rules/callback-return.js +2 -2
  11. package/lib/rules/capitalized-comments.js +1 -1
  12. package/lib/rules/consistent-this.js +1 -1
  13. package/lib/rules/dot-notation.js +2 -2
  14. package/lib/rules/function-paren-newline.js +8 -5
  15. package/lib/rules/global-require.js +3 -3
  16. package/lib/rules/indent-legacy.js +2 -2
  17. package/lib/rules/indent.js +45 -13
  18. package/lib/rules/index.js +1 -0
  19. package/lib/rules/jsx-quotes.js +1 -1
  20. package/lib/rules/lines-around-comment.js +3 -3
  21. package/lib/rules/max-lines.js +2 -2
  22. package/lib/rules/max-statements.js +1 -1
  23. package/lib/rules/newline-before-return.js +1 -1
  24. package/lib/rules/no-bitwise.js +2 -2
  25. package/lib/rules/no-console.js +1 -1
  26. package/lib/rules/no-constant-binary-expression.js +500 -0
  27. package/lib/rules/no-constant-condition.js +4 -197
  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 +31 -2
  48. package/lib/rules/no-unused-expressions.js +1 -1
  49. package/lib/rules/no-unused-vars.js +1 -1
  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/utils/ast-utils.js +203 -7
  59. package/lib/rules/valid-jsdoc.js +1 -1
  60. package/lib/rules/valid-typeof.js +4 -4
  61. package/lib/rules/yoda.js +1 -1
  62. package/lib/shared/types.js +1 -1
  63. package/package.json +25 -8
@@ -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" });
@@ -49,6 +49,10 @@ module.exports = {
49
49
  allowFunctionParams: {
50
50
  type: "boolean",
51
51
  default: true
52
+ },
53
+ enforceInClassFields: {
54
+ type: "boolean",
55
+ default: false
52
56
  }
53
57
  },
54
58
  additionalProperties: false
@@ -68,6 +72,7 @@ module.exports = {
68
72
  const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false;
69
73
  const allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false;
70
74
  const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false;
75
+ const enforceInClassFields = typeof options.enforceInClassFields !== "undefined" ? options.enforceInClassFields : false;
71
76
  const allowFunctionParams = typeof options.allowFunctionParams !== "undefined" ? options.allowFunctionParams : true;
72
77
 
73
78
  //-------------------------------------------------------------------------
@@ -81,7 +86,7 @@ module.exports = {
81
86
  * @private
82
87
  */
83
88
  function isAllowed(identifier) {
84
- return ALLOWED_VARIABLES.some(ident => ident === identifier);
89
+ return ALLOWED_VARIABLES.includes(identifier);
85
90
  }
86
91
 
87
92
  /**
@@ -261,6 +266,30 @@ module.exports = {
261
266
  }
262
267
  }
263
268
 
269
+ /**
270
+ * Check if a class field has a dangling underscore
271
+ * @param {ASTNode} node node to evaluate
272
+ * @returns {void}
273
+ * @private
274
+ */
275
+ function checkForDanglingUnderscoreInClassField(node) {
276
+ const identifier = node.key.name;
277
+
278
+ if (typeof identifier !== "undefined" && hasDanglingUnderscore(identifier) &&
279
+ enforceInClassFields &&
280
+ !isAllowed(identifier)) {
281
+ context.report({
282
+ node,
283
+ messageId: "unexpectedUnderscore",
284
+ data: {
285
+ identifier: node.key.type === "PrivateIdentifier"
286
+ ? `#${identifier}`
287
+ : identifier
288
+ }
289
+ });
290
+ }
291
+ }
292
+
264
293
  //--------------------------------------------------------------------------
265
294
  // Public API
266
295
  //--------------------------------------------------------------------------
@@ -270,7 +299,7 @@ module.exports = {
270
299
  VariableDeclarator: checkForDanglingUnderscoreInVariableExpression,
271
300
  MemberExpression: checkForDanglingUnderscoreInMemberExpression,
272
301
  MethodDefinition: checkForDanglingUnderscoreInMethod,
273
- PropertyDefinition: checkForDanglingUnderscoreInMethod,
302
+ PropertyDefinition: checkForDanglingUnderscoreInClassField,
274
303
  Property: checkForDanglingUnderscoreInMethod,
275
304
  FunctionExpression: checkForDanglingUnderscoreInFunction,
276
305
  ArrowFunctionExpression: checkForDanglingUnderscoreInFunction
@@ -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
 
@@ -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
 
@@ -32,6 +32,7 @@ const thisTagPattern = /^[\s*]*@this/mu;
32
32
 
33
33
 
34
34
  const COMMENTS_IGNORE_PATTERN = /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u;
35
+ const ESLINT_DIRECTIVE_PATTERN = /^(?:eslint[- ]|(?:globals?|exported) )/u;
35
36
  const LINEBREAKS = new Set(["\r\n", "\r", "\n", "\u2028", "\u2029"]);
36
37
 
37
38
  // A set of node types that can contain a list of statements
@@ -788,6 +789,203 @@ function getModuleExportName(node) {
788
789
  return node.value;
789
790
  }
790
791
 
792
+ /**
793
+ * Returns literal's value converted to the Boolean type
794
+ * @param {ASTNode} node any `Literal` node
795
+ * @returns {boolean | null} `true` when node is truthy, `false` when node is falsy,
796
+ * `null` when it cannot be determined.
797
+ */
798
+ function getBooleanValue(node) {
799
+ if (node.value === null) {
800
+
801
+ /*
802
+ * it might be a null literal or bigint/regex literal in unsupported environments .
803
+ * https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es5.md#regexpliteral
804
+ * https://github.com/estree/estree/blob/14df8a024956ea289bd55b9c2226a1d5b8a473ee/es2020.md#bigintliteral
805
+ */
806
+
807
+ if (node.raw === "null") {
808
+ return false;
809
+ }
810
+
811
+ // regex is always truthy
812
+ if (typeof node.regex === "object") {
813
+ return true;
814
+ }
815
+
816
+ return null;
817
+ }
818
+
819
+ return !!node.value;
820
+ }
821
+
822
+ /**
823
+ * Checks if a branch node of LogicalExpression short circuits the whole condition
824
+ * @param {ASTNode} node The branch of main condition which needs to be checked
825
+ * @param {string} operator The operator of the main LogicalExpression.
826
+ * @returns {boolean} true when condition short circuits whole condition
827
+ */
828
+ function isLogicalIdentity(node, operator) {
829
+ switch (node.type) {
830
+ case "Literal":
831
+ return (operator === "||" && getBooleanValue(node) === true) ||
832
+ (operator === "&&" && getBooleanValue(node) === false);
833
+
834
+ case "UnaryExpression":
835
+ return (operator === "&&" && node.operator === "void");
836
+
837
+ case "LogicalExpression":
838
+
839
+ /*
840
+ * handles `a && false || b`
841
+ * `false` is an identity element of `&&` but not `||`
842
+ */
843
+ return operator === node.operator &&
844
+ (
845
+ isLogicalIdentity(node.left, operator) ||
846
+ isLogicalIdentity(node.right, operator)
847
+ );
848
+
849
+ case "AssignmentExpression":
850
+ return ["||=", "&&="].includes(node.operator) &&
851
+ operator === node.operator.slice(0, -1) &&
852
+ isLogicalIdentity(node.right, operator);
853
+
854
+ // no default
855
+ }
856
+ return false;
857
+ }
858
+
859
+ /**
860
+ * Checks if an identifier is a reference to a global variable.
861
+ * @param {Scope} scope The scope in which the identifier is referenced.
862
+ * @param {ASTNode} node An identifier node to check.
863
+ * @returns {boolean} `true` if the identifier is a reference to a global variable.
864
+ */
865
+ function isReferenceToGlobalVariable(scope, node) {
866
+ const reference = scope.references.find(ref => ref.identifier === node);
867
+
868
+ return Boolean(
869
+ reference &&
870
+ reference.resolved &&
871
+ reference.resolved.scope.type === "global" &&
872
+ reference.resolved.defs.length === 0
873
+ );
874
+ }
875
+
876
+
877
+ /**
878
+ * Checks if a node has a constant truthiness value.
879
+ * @param {Scope} scope Scope in which the node appears.
880
+ * @param {ASTNode} node The AST node to check.
881
+ * @param {boolean} inBooleanPosition `true` if checking the test of a
882
+ * condition. `false` in all other cases. When `false`, checks if -- for
883
+ * both string and number -- if coerced to that type, the value will
884
+ * be constant.
885
+ * @returns {boolean} true when node's truthiness is constant
886
+ * @private
887
+ */
888
+ function isConstant(scope, node, inBooleanPosition) {
889
+
890
+ // node.elements can return null values in the case of sparse arrays ex. [,]
891
+ if (!node) {
892
+ return true;
893
+ }
894
+ switch (node.type) {
895
+ case "Literal":
896
+ case "ArrowFunctionExpression":
897
+ case "FunctionExpression":
898
+ return true;
899
+ case "ClassExpression":
900
+ case "ObjectExpression":
901
+
902
+ /**
903
+ * In theory objects like:
904
+ *
905
+ * `{toString: () => a}`
906
+ * `{valueOf: () => a}`
907
+ *
908
+ * Or a classes like:
909
+ *
910
+ * `class { static toString() { return a } }`
911
+ * `class { static valueOf() { return a } }`
912
+ *
913
+ * Are not constant verifiably when `inBooleanPosition` is
914
+ * false, but it's an edge case we've opted not to handle.
915
+ */
916
+ return true;
917
+ case "TemplateLiteral":
918
+ return (inBooleanPosition && node.quasis.some(quasi => quasi.value.cooked.length)) ||
919
+ node.expressions.every(exp => isConstant(scope, exp, false));
920
+
921
+ case "ArrayExpression": {
922
+ if (!inBooleanPosition) {
923
+ return node.elements.every(element => isConstant(scope, element, false));
924
+ }
925
+ return true;
926
+ }
927
+
928
+ case "UnaryExpression":
929
+ if (
930
+ node.operator === "void" ||
931
+ node.operator === "typeof" && inBooleanPosition
932
+ ) {
933
+ return true;
934
+ }
935
+
936
+ if (node.operator === "!") {
937
+ return isConstant(scope, node.argument, true);
938
+ }
939
+
940
+ return isConstant(scope, node.argument, false);
941
+
942
+ case "BinaryExpression":
943
+ return isConstant(scope, node.left, false) &&
944
+ isConstant(scope, node.right, false) &&
945
+ node.operator !== "in";
946
+
947
+ case "LogicalExpression": {
948
+ const isLeftConstant = isConstant(scope, node.left, inBooleanPosition);
949
+ const isRightConstant = isConstant(scope, node.right, inBooleanPosition);
950
+ const isLeftShortCircuit = (isLeftConstant && isLogicalIdentity(node.left, node.operator));
951
+ const isRightShortCircuit = (inBooleanPosition && isRightConstant && isLogicalIdentity(node.right, node.operator));
952
+
953
+ return (isLeftConstant && isRightConstant) ||
954
+ isLeftShortCircuit ||
955
+ isRightShortCircuit;
956
+ }
957
+ case "NewExpression":
958
+ return inBooleanPosition;
959
+ case "AssignmentExpression":
960
+ if (node.operator === "=") {
961
+ return isConstant(scope, node.right, inBooleanPosition);
962
+ }
963
+
964
+ if (["||=", "&&="].includes(node.operator) && inBooleanPosition) {
965
+ return isLogicalIdentity(node.right, node.operator.slice(0, -1));
966
+ }
967
+
968
+ return false;
969
+
970
+ case "SequenceExpression":
971
+ return isConstant(scope, node.expressions[node.expressions.length - 1], inBooleanPosition);
972
+ case "SpreadElement":
973
+ return isConstant(scope, node.argument, inBooleanPosition);
974
+ case "CallExpression":
975
+ if (node.callee.type === "Identifier" && node.callee.name === "Boolean") {
976
+ if (node.arguments.length === 0 || isConstant(scope, node.arguments[0], true)) {
977
+ return isReferenceToGlobalVariable(scope, node.callee);
978
+ }
979
+ }
980
+ return false;
981
+ case "Identifier":
982
+ return node.name === "undefined" && isReferenceToGlobalVariable(scope, node);
983
+
984
+ // no default
985
+ }
986
+ return false;
987
+ }
988
+
791
989
  //------------------------------------------------------------------------------
792
990
  // Public Interface
793
991
  //------------------------------------------------------------------------------
@@ -908,12 +1106,8 @@ module.exports = {
908
1106
  const comment = node.value.trim();
909
1107
 
910
1108
  return (
911
- node.type === "Line" && comment.indexOf("eslint-") === 0 ||
912
- node.type === "Block" && (
913
- comment.indexOf("global ") === 0 ||
914
- comment.indexOf("eslint ") === 0 ||
915
- comment.indexOf("eslint-") === 0
916
- )
1109
+ node.type === "Line" && comment.startsWith("eslint-") ||
1110
+ node.type === "Block" && ESLINT_DIRECTIVE_PATTERN.test(comment)
917
1111
  );
918
1112
  },
919
1113
 
@@ -1905,6 +2099,7 @@ module.exports = {
1905
2099
  return OCTAL_OR_NON_OCTAL_DECIMAL_ESCAPE_PATTERN.test(rawString);
1906
2100
  },
1907
2101
 
2102
+ isReferenceToGlobalVariable,
1908
2103
  isLogicalExpression,
1909
2104
  isCoalesceExpression,
1910
2105
  isMixedLogicalAndCoalesceExpressions,
@@ -1918,5 +2113,6 @@ module.exports = {
1918
2113
  isSameReference,
1919
2114
  isLogicalAssignmentOperator,
1920
2115
  getSwitchCaseColonToken,
1921
- getModuleExportName
2116
+ getModuleExportName,
2117
+ isConstant
1922
2118
  };
@@ -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
  /**
@@ -136,7 +136,6 @@ module.exports = {};
136
136
 
137
137
  /**
138
138
  * @typedef {Object} RuleMetaDocs
139
- * @property {string} category The category of the rule.
140
139
  * @property {string} description The description of the rule.
141
140
  * @property {boolean} recommended If `true` then the rule is included in `eslint:recommended` preset.
142
141
  * @property {string} url The URL of the rule documentation.
@@ -147,6 +146,7 @@ module.exports = {};
147
146
  * @property {boolean} [deprecated] If `true` then the rule has been deprecated.
148
147
  * @property {RuleMetaDocs} docs The document information of the rule.
149
148
  * @property {"code"|"whitespace"} [fixable] The autofix type.
149
+ * @property {boolean} [hasSuggestions] If `true` then the rule provides suggestions.
150
150
  * @property {Record<string,string>} [messages] The messages the rule reports.
151
151
  * @property {string[]} [replacedBy] The IDs of the alternative rules.
152
152
  * @property {Array|Object} schema The option schema of the rule.