eslint 10.5.0 → 10.6.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.
package/README.md CHANGED
@@ -320,8 +320,8 @@ lumir
320
320
  </a>
321
321
  </td><td align="center" valign="top" width="11%">
322
322
  <a href="https://github.com/Pixel998">
323
- <img src="https://github.com/Pixel998.png?s=75" width="75" height="75" alt="Pixel998's Avatar"><br />
324
- Pixel998
323
+ <img src="https://github.com/Pixel998.png?s=75" width="75" height="75" alt="Pixel's Avatar"><br />
324
+ Pixel
325
325
  </a>
326
326
  </td></tr></tbody></table>
327
327
 
@@ -358,9 +358,9 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
358
358
 
359
359
  <h3>Platinum Sponsors</h3>
360
360
  <p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a></p><h3>Gold Sponsors</h3>
361
- <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a></p><h3>Silver Sponsors</h3>
361
+ <p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a> <a href="https://www.coderabbit.ai/?utm_source=cr_org&utm_medium=github"><img src="https://avatars.githubusercontent.com/u/132028505" alt="CodeRabbit" height="96"></a></p><h3>Silver Sponsors</h3>
362
362
  <p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/d472863/logo.png" alt="Vite" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/2d6c3b6/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
363
- <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" 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://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://citadel.co.jp"><img src="https://avatars.githubusercontent.com/u/75781367" alt="Citadel AI" height="32"></a></p>
363
+ <p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://opensource.sap.com"><img src="https://avatars.githubusercontent.com/u/2531208" alt="SAP" height="32"></a> <a href="https://www.crawljobs.com/"><img src="https://images.opencollective.com/crawljobs-poland/fa43a17/logo.png" alt="CrawlJobs" height="32"></a> <a href="https://syntax.fm"><img src="https://github.com/syntaxfm.png" alt="Syntax" height="32"></a> <a href="https://depot.dev"><img src="https://images.opencollective.com/depot/39125a1/logo.png" alt="Depot" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" 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://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://citadel.co.jp"><img src="https://avatars.githubusercontent.com/u/75781367" alt="Citadel AI" height="32"></a></p>
364
364
  <h3>Technology Sponsors</h3>
365
365
  Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
366
366
  <p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
@@ -191,8 +191,8 @@ function pluralize(word, count) {
191
191
 
192
192
  /**
193
193
  * Renders text along the template of x problems (x errors, x warnings)
194
- * @param {string} totalErrors Total errors
195
- * @param {string} totalWarnings Total warnings
194
+ * @param {number} totalErrors Total errors
195
+ * @param {number} totalWarnings Total warnings
196
196
  * @returns {string} The formatted string, pluralized where necessary
197
197
  */
198
198
  function renderSummary(totalErrors, totalWarnings) {
@@ -207,8 +207,8 @@ function renderSummary(totalErrors, totalWarnings) {
207
207
 
208
208
  /**
209
209
  * Get the color based on whether there are errors/warnings...
210
- * @param {string} totalErrors Total errors
211
- * @param {string} totalWarnings Total warnings
210
+ * @param {number} totalErrors Total errors
211
+ * @param {number} totalWarnings Total warnings
212
212
  * @returns {number} The color code (0 = green, 1 = yellow, 2 = red)
213
213
  */
214
214
  function renderColor(totalErrors, totalWarnings) {
@@ -71,6 +71,10 @@ module.exports = {
71
71
  if (classCount > max) {
72
72
  context.report({
73
73
  node,
74
+ loc: {
75
+ start: node.body[0].loc.start,
76
+ end: node.body.at(-1).loc.end,
77
+ },
74
78
  messageId: "maximumExceeded",
75
79
  data: {
76
80
  classCount,
@@ -91,10 +91,12 @@ module.exports = {
91
91
  function checkFunction(node) {
92
92
  const parent = node.parent;
93
93
 
94
- if (parent.type === "CallExpression") {
95
- callbackStack.push(node);
94
+ if (parent.type !== "CallExpression" || parent.callee === node) {
95
+ return;
96
96
  }
97
97
 
98
+ callbackStack.push(node);
99
+
98
100
  if (callbackStack.length > THRESHOLD) {
99
101
  const opts = { num: callbackStack.length, max: THRESHOLD };
100
102
 
@@ -28,6 +28,8 @@ const NUMERIC_OR_STRING_BINARY_OPERATORS = new Set([
28
28
  ">>>",
29
29
  ]);
30
30
 
31
+ const RELATIONAL_OPERATORS = new Set(["<", "<=", ">", ">="]);
32
+
31
33
  //------------------------------------------------------------------------------
32
34
  // Helpers
33
35
  //------------------------------------------------------------------------------
@@ -87,7 +89,9 @@ function hasConstantNullishness(scope, node, nonNullish) {
87
89
  return (
88
90
  (functionName === "Boolean" ||
89
91
  functionName === "String" ||
90
- functionName === "Number") &&
92
+ functionName === "Number" ||
93
+ functionName === "Symbol" ||
94
+ functionName === "BigInt") &&
91
95
  isReferenceToGlobalVariable(scope, node.callee)
92
96
  );
93
97
  }
@@ -372,7 +376,10 @@ function hasConstantStrictBooleanComparison(scope, node) {
372
376
  const functionName = node.callee.name;
373
377
 
374
378
  if (
375
- (functionName === "String" || functionName === "Number") &&
379
+ (functionName === "String" ||
380
+ functionName === "Number" ||
381
+ functionName === "BigInt" ||
382
+ functionName === "Symbol") &&
376
383
  isReferenceToGlobalVariable(scope, node.callee)
377
384
  ) {
378
385
  return true;
@@ -485,6 +492,33 @@ function findBinaryExpressionConstantOperand(scope, a, b, operator) {
485
492
  return null;
486
493
  }
487
494
 
495
+ /**
496
+ * Checks if a node is a statically knowable literal.
497
+ * @param {Scope} scope The scope in which the node was found.
498
+ * @param {ASTNode} node The node to test.
499
+ * @returns {boolean} `true` if the node is a literal.
500
+ */
501
+ function isStaticLiteral(scope, node) {
502
+ switch (node.type) {
503
+ case "Literal":
504
+ return true;
505
+ case "UnaryExpression":
506
+ return (
507
+ ["-", "+", "~"].includes(node.operator) &&
508
+ node.argument.type === "Literal"
509
+ );
510
+ case "Identifier":
511
+ return (
512
+ node.name === "undefined" &&
513
+ isReferenceToGlobalVariable(scope, node)
514
+ );
515
+ case "TemplateLiteral":
516
+ return node.expressions.length === 0;
517
+ default:
518
+ return false;
519
+ }
520
+ }
521
+
488
522
  //------------------------------------------------------------------------------
489
523
  // Rule Definition
490
524
  //------------------------------------------------------------------------------
@@ -499,7 +533,22 @@ module.exports = {
499
533
  recommended: true,
500
534
  url: "https://eslint.org/docs/latest/rules/no-constant-binary-expression",
501
535
  },
502
- schema: [],
536
+ defaultOptions: [
537
+ {
538
+ checkRelationalComparisons: false,
539
+ },
540
+ ],
541
+ schema: [
542
+ {
543
+ type: "object",
544
+ properties: {
545
+ checkRelationalComparisons: {
546
+ type: "boolean",
547
+ },
548
+ },
549
+ additionalProperties: false,
550
+ },
551
+ ],
503
552
  messages: {
504
553
  constantBinaryOperand:
505
554
  "Unexpected constant binary expression. Compares constantly with the {{otherSide}}-hand side of the `{{operator}}`.",
@@ -509,11 +558,14 @@ module.exports = {
509
558
  "Unexpected comparison to newly constructed object. These two values can never be equal.",
510
559
  bothAlwaysNew:
511
560
  "Unexpected comparison of two newly constructed objects. These two values can never be equal.",
561
+ constantRelationalComparison:
562
+ "Unexpected constant relational comparison. Both sides of the `{{operator}}` are literal values.",
512
563
  },
513
564
  },
514
565
 
515
566
  create(context) {
516
567
  const sourceCode = context.sourceCode;
568
+ const { checkRelationalComparisons } = context.options[0];
517
569
 
518
570
  return {
519
571
  LogicalExpression(node) {
@@ -587,6 +639,20 @@ module.exports = {
587
639
  messageId: "bothAlwaysNew",
588
640
  });
589
641
  }
642
+ } else if (
643
+ checkRelationalComparisons &&
644
+ RELATIONAL_OPERATORS.has(operator)
645
+ ) {
646
+ if (
647
+ isStaticLiteral(scope, left) &&
648
+ isStaticLiteral(scope, right)
649
+ ) {
650
+ context.report({
651
+ node,
652
+ messageId: "constantRelationalComparison",
653
+ data: { operator },
654
+ });
655
+ }
590
656
  }
591
657
  },
592
658
 
@@ -91,7 +91,8 @@ module.exports = {
91
91
  (node.type === "CallExpression" ||
92
92
  node.type === "NewExpression") &&
93
93
  node.callee.type === "Identifier" &&
94
- node.callee.name === "Boolean"
94
+ node.callee.name === "Boolean" &&
95
+ sourceCode.isGlobalReference(node.callee)
95
96
  );
96
97
  }
97
98
 
@@ -323,7 +324,8 @@ module.exports = {
323
324
  CallExpression(node) {
324
325
  if (
325
326
  node.callee.type !== "Identifier" ||
326
- node.callee.name !== "Boolean"
327
+ node.callee.name !== "Boolean" ||
328
+ !sourceCode.isGlobalReference(node.callee)
327
329
  ) {
328
330
  return;
329
331
  }
@@ -199,10 +199,11 @@ module.exports = {
199
199
  });
200
200
  }
201
201
 
202
- // Do not suggest wrapping an unnamed FunctionExpression in braces as that would be invalid syntax.
202
+ // Do not suggest wrapping an unnamed function or class expression in braces as that would be invalid syntax.
203
203
  if (
204
204
  !(
205
- node.body.type === "FunctionExpression" &&
205
+ (node.body.type === "FunctionExpression" ||
206
+ node.body.type === "ClassExpression") &&
206
207
  !node.body.id
207
208
  )
208
209
  ) {
@@ -31,12 +31,17 @@ module.exports = {
31
31
  },
32
32
 
33
33
  create(context) {
34
+ const sourceCode = context.sourceCode;
35
+
34
36
  return {
35
37
  ThrowStatement(node) {
36
38
  if (!astUtils.couldBeError(node.argument)) {
37
39
  context.report({ node, messageId: "object" });
38
40
  } else if (node.argument.type === "Identifier") {
39
- if (node.argument.name === "undefined") {
41
+ if (
42
+ node.argument.name === "undefined" &&
43
+ sourceCode.isGlobalReference(node.argument)
44
+ ) {
40
45
  context.report({ node, messageId: "undef" });
41
46
  }
42
47
  }
@@ -21,6 +21,11 @@ const PRECEDENCE_OF_EXPONENTIATION_EXPR = astUtils.getPrecedence({
21
21
  operator: "**",
22
22
  });
23
23
 
24
+ /*
25
+ * Characters that can cause continuation without preceding semi
26
+ */
27
+ const continuationChars = new Set(["(", "[", "/", "`"]);
28
+
24
29
  /**
25
30
  * Determines whether the given node needs parens if used as the base in an exponentiation binary expression.
26
31
  * @param {ASTNode} base The node to check.
@@ -149,11 +154,36 @@ module.exports = {
149
154
  shouldParenthesizeBase = doesBaseNeedParens(base),
150
155
  shouldParenthesizeExponent =
151
156
  doesExponentNeedParens(exponent),
152
- shouldParenthesizeAll =
153
- doesExponentiationExpressionNeedParens(
154
- node,
155
- sourceCode,
156
- );
157
+ isStartOfExpressionStatement =
158
+ astUtils.isStartOfExpressionStatement(node);
159
+
160
+ let shouldParenthesizeAll =
161
+ doesExponentiationExpressionNeedParens(
162
+ node,
163
+ sourceCode,
164
+ );
165
+
166
+ /*
167
+ * `function`, `class`, or `{` token at the start of an expression statement
168
+ * would cause incorrect parsing.
169
+ * https://github.com/eslint/eslint/issues/20987
170
+ */
171
+ if (
172
+ !shouldParenthesizeAll &&
173
+ !shouldParenthesizeBase &&
174
+ isStartOfExpressionStatement
175
+ ) {
176
+ const firstTokenOfBase = sourceCode.getFirstToken(base);
177
+
178
+ if (
179
+ astUtils.isOpeningBraceToken(firstTokenOfBase) ||
180
+ (firstTokenOfBase.type === "Keyword" &&
181
+ (firstTokenOfBase.value === "function" ||
182
+ firstTokenOfBase.value === "class"))
183
+ ) {
184
+ shouldParenthesizeAll = true;
185
+ }
186
+ }
157
187
 
158
188
  let prefix = "",
159
189
  suffix = "";
@@ -206,6 +236,15 @@ module.exports = {
206
236
  shouldParenthesizeAll,
207
237
  );
208
238
 
239
+ if (
240
+ !prefix &&
241
+ isStartOfExpressionStatement &&
242
+ continuationChars.has(replacement[0]) &&
243
+ astUtils.needsPrecedingSemicolon(sourceCode, node)
244
+ ) {
245
+ prefix = ";";
246
+ }
247
+
209
248
  return fixer.replaceText(
210
249
  node,
211
250
  `${prefix}${replacement}${suffix}`,
@@ -63,11 +63,15 @@ module.exports = {
63
63
  if (!callExpression.arguments.length && allowEmptyReject) {
64
64
  return;
65
65
  }
66
+
67
+ const rejectionReason = callExpression.arguments[0];
68
+
66
69
  if (
67
70
  !callExpression.arguments.length ||
68
- !astUtils.couldBeError(callExpression.arguments[0]) ||
69
- (callExpression.arguments[0].type === "Identifier" &&
70
- callExpression.arguments[0].name === "undefined")
71
+ !astUtils.couldBeError(rejectionReason) ||
72
+ (rejectionReason.type === "Identifier" &&
73
+ rejectionReason.name === "undefined" &&
74
+ sourceCode.isGlobalReference(rejectionReason))
71
75
  ) {
72
76
  context.report({
73
77
  node: callExpression,
@@ -82,10 +86,15 @@ module.exports = {
82
86
  * @returns {boolean} `true` if the call is a Promise.reject() call
83
87
  */
84
88
  function isPromiseRejectCall(node) {
85
- return astUtils.isSpecificMemberAccess(
86
- node.callee,
87
- "Promise",
88
- "reject",
89
+ return (
90
+ astUtils.isSpecificMemberAccess(
91
+ node.callee,
92
+ "Promise",
93
+ "reject",
94
+ ) &&
95
+ sourceCode.isGlobalReference(
96
+ astUtils.skipChainExpression(node.callee).object,
97
+ )
89
98
  );
90
99
  }
91
100
 
@@ -110,6 +119,7 @@ module.exports = {
110
119
  if (
111
120
  node.callee.type === "Identifier" &&
112
121
  node.callee.name === "Promise" &&
122
+ sourceCode.isGlobalReference(node.callee) &&
113
123
  node.arguments.length &&
114
124
  astUtils.isFunction(node.arguments[0]) &&
115
125
  node.arguments[0].params.length > 1 &&
@@ -57,12 +57,15 @@ function isParseIntMethod(node) {
57
57
  * - A literal except integers between 2 and 36.
58
58
  * - undefined.
59
59
  * @param {ASTNode} radix A node of radix to check.
60
+ * @param {SourceCode} sourceCode The source code object.
60
61
  * @returns {boolean} `true` if the node is valid.
61
62
  */
62
- function isValidRadix(radix) {
63
+ function isValidRadix(radix, sourceCode) {
63
64
  return !(
64
65
  (radix.type === "Literal" && !validRadixValues.has(radix.value)) ||
65
- (radix.type === "Identifier" && radix.name === "undefined")
66
+ (radix.type === "Identifier" &&
67
+ radix.name === "undefined" &&
68
+ sourceCode.isGlobalReference(radix))
66
69
  );
67
70
  }
68
71
 
@@ -148,7 +151,7 @@ module.exports = {
148
151
  break;
149
152
 
150
153
  default:
151
- if (!isValidRadix(args[1])) {
154
+ if (!isValidRadix(args[1], sourceCode)) {
152
155
  context.report({
153
156
  node,
154
157
  messageId: "invalidRadix",
@@ -2228,7 +2228,16 @@ export interface ESLintRules extends Linter.RulesRecord {
2228
2228
  * @since 8.14.0
2229
2229
  * @see https://eslint.org/docs/latest/rules/no-constant-binary-expression
2230
2230
  */
2231
- "no-constant-binary-expression": Linter.RuleEntry<[]>;
2231
+ "no-constant-binary-expression": Linter.RuleEntry<
2232
+ [
2233
+ Partial<{
2234
+ /**
2235
+ * @default false
2236
+ */
2237
+ checkRelationalComparisons: boolean;
2238
+ }>,
2239
+ ]
2240
+ >;
2232
2241
 
2233
2242
  /**
2234
2243
  * Rule to disallow constant expressions in conditions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint",
3
- "version": "10.5.0",
3
+ "version": "10.6.0",
4
4
  "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
5
5
  "description": "An AST-based pattern checker for JavaScript.",
6
6
  "type": "commonjs",
@@ -192,8 +192,8 @@
192
192
  "jiti": "^2.6.1",
193
193
  "knip": "^6.13.1",
194
194
  "lint-staged": "^11.0.0",
195
- "markdown-it": "^12.2.0",
196
- "markdown-it-container": "^3.0.0",
195
+ "markdown-it": "^14.2.0",
196
+ "markdown-it-container": "^4.0.0",
197
197
  "markdownlint-cli2": "^0.22.0",
198
198
  "marked": "^4.0.8",
199
199
  "metascraper": "^5.25.7",
@@ -205,7 +205,7 @@
205
205
  "mocha": "^11.7.1",
206
206
  "node-polyfill-webpack-plugin": "^1.0.3",
207
207
  "npm-license": "^0.3.3",
208
- "prettier": "3.8.3",
208
+ "prettier": "3.8.4",
209
209
  "progress": "^2.0.3",
210
210
  "proxyquire": "^2.0.1",
211
211
  "regenerator-runtime": "^0.14.0",