eslint-linter-browserify 10.5.0 → 10.7.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 (5) hide show
  1. package/linter.cjs +690 -263
  2. package/linter.js +690 -263
  3. package/linter.min.js +3 -3
  4. package/linter.mjs +690 -263
  5. package/package.json +4 -4
package/linter.cjs CHANGED
@@ -4216,7 +4216,7 @@ function requireEslintVisitorKeys$2 () {
4216
4216
  return eslintVisitorKeys$2;
4217
4217
  }
4218
4218
 
4219
- var version = "10.5.0";
4219
+ var version = "10.7.0";
4220
4220
  var require$$3$1 = {
4221
4221
  version: version};
4222
4222
 
@@ -47526,7 +47526,7 @@ function requireAstUtils () {
47526
47526
  * @returns {boolean} Whether the node is inside a TypeScript type context.
47527
47527
  */
47528
47528
  function isInType(node) {
47529
- for (let currNode = node; ; ) {
47529
+ for (let currNode = node; ;) {
47530
47530
  const { parent } = currNode;
47531
47531
  if (!parent) {
47532
47532
  break;
@@ -53661,7 +53661,8 @@ function requireClassMethodsUseThis () {
53661
53661
  function hasImplements(node) {
53662
53662
  const classNode = node.parent.parent;
53663
53663
  return (
53664
- classNode?.type === "ClassDeclaration" &&
53664
+ (classNode?.type === "ClassDeclaration" ||
53665
+ classNode?.type === "ClassExpression") &&
53665
53666
  classNode.implements?.length > 0
53666
53667
  );
53667
53668
  }
@@ -57617,6 +57618,24 @@ function requireEqeqeq () {
57617
57618
  return isTypeOf(node.left) || isTypeOf(node.right);
57618
57619
  }
57619
57620
 
57621
+ /**
57622
+ * Gets the type of a literal node.
57623
+ * @param {ASTNode} node The node to check
57624
+ * @returns {string|null} The type of the literal
57625
+ * @private
57626
+ */
57627
+ function getLiteralType(node) {
57628
+ if (node.type === "Literal") {
57629
+ return typeof node.value;
57630
+ }
57631
+
57632
+ if (astUtils.isStaticTemplateLiteral(node)) {
57633
+ return "string";
57634
+ }
57635
+
57636
+ return null;
57637
+ }
57638
+
57620
57639
  /**
57621
57640
  * Checks if operands are literals of the same type (via typeof)
57622
57641
  * @param {ASTNode} node The node to check
@@ -57624,11 +57643,9 @@ function requireEqeqeq () {
57624
57643
  * @private
57625
57644
  */
57626
57645
  function areLiteralsAndSameType(node) {
57627
- return (
57628
- node.left.type === "Literal" &&
57629
- node.right.type === "Literal" &&
57630
- typeof node.left.value === typeof node.right.value
57631
- );
57646
+ const leftType = getLiteralType(node.left);
57647
+
57648
+ return leftType !== null && leftType === getLiteralType(node.right);
57632
57649
  }
57633
57650
 
57634
57651
  /**
@@ -57701,7 +57718,11 @@ function requireEqeqeq () {
57701
57718
  const isNull = isNullCheck(node);
57702
57719
 
57703
57720
  if (node.operator !== "==" && node.operator !== "!=") {
57704
- if (enforceInverseRuleForNull && isNull) {
57721
+ if (
57722
+ enforceInverseRuleForNull &&
57723
+ isNull &&
57724
+ (node.operator === "===" || node.operator === "!==")
57725
+ ) {
57705
57726
  report(node, node.operator.slice(0, -1));
57706
57727
  }
57707
57728
  return;
@@ -58494,9 +58515,9 @@ function requireFuncNameMatching () {
58494
58515
  },
58495
58516
 
58496
58517
  "Property, PropertyDefinition[value]"(node) {
58497
- if (
58498
- !(node.value.type === "FunctionExpression" && node.value.id)
58499
- ) {
58518
+ if (!(
58519
+ node.value.type === "FunctionExpression" && node.value.id
58520
+ )) {
58500
58521
  return;
58501
58522
  }
58502
58523
 
@@ -59810,12 +59831,10 @@ function requireGeneratorStarSpacing () {
59810
59831
  }
59811
59832
 
59812
59833
  // Only check before when preceded by `function`|`static` keyword
59813
- if (
59814
- !(
59815
- kind === "method" &&
59816
- starToken === sourceCode.getFirstToken(node.parent)
59817
- )
59818
- ) {
59834
+ if (!(
59835
+ kind === "method" &&
59836
+ starToken === sourceCode.getFirstToken(node.parent)
59837
+ )) {
59819
59838
  checkSpacing(kind, "before", prevToken, starToken);
59820
59839
  }
59821
59840
 
@@ -63780,12 +63799,10 @@ function requireIndent () {
63780
63799
  },
63781
63800
 
63782
63801
  SwitchCase(node) {
63783
- if (
63784
- !(
63785
- node.consequent.length === 1 &&
63786
- node.consequent[0].type === "BlockStatement"
63787
- )
63788
- ) {
63802
+ if (!(
63803
+ node.consequent.length === 1 &&
63804
+ node.consequent[0].type === "BlockStatement"
63805
+ )) {
63789
63806
  const caseKeyword = sourceCode.getFirstToken(node);
63790
63807
  const tokenAfterCurrentCase =
63791
63808
  sourceCode.getTokenAfter(node);
@@ -66312,12 +66329,10 @@ function requireKeySpacing () {
66312
66329
  */
66313
66330
  function isKeyValueProperty(property) {
66314
66331
  return !(
66315
- (
66316
- property.method ||
66317
- property.shorthand ||
66318
- property.kind !== "init" ||
66319
- property.type !== "Property"
66320
- ) // Could be "ExperimentalSpreadProperty" or "SpreadElement"
66332
+ property.method ||
66333
+ property.shorthand ||
66334
+ property.kind !== "init" ||
66335
+ property.type !== "Property" // Could be "ExperimentalSpreadProperty" or "SpreadElement"
66321
66336
  );
66322
66337
  }
66323
66338
 
@@ -69766,6 +69781,10 @@ function requireMaxClassesPerFile () {
69766
69781
  if (classCount > max) {
69767
69782
  context.report({
69768
69783
  node,
69784
+ loc: {
69785
+ start: node.body[0].loc.start,
69786
+ end: node.body.at(-1).loc.end,
69787
+ },
69769
69788
  messageId: "maximumExceeded",
69770
69789
  data: {
69771
69790
  classCount,
@@ -70985,6 +71004,9 @@ function requireMaxNestedCallbacks () {
70985
71004
  type: "integer",
70986
71005
  minimum: 0,
70987
71006
  },
71007
+ checkConstructorCallCallbacks: {
71008
+ type: "boolean",
71009
+ },
70988
71010
  },
70989
71011
  additionalProperties: false,
70990
71012
  },
@@ -71032,10 +71054,19 @@ function requireMaxNestedCallbacks () {
71032
71054
  function checkFunction(node) {
71033
71055
  const parent = node.parent;
71034
71056
 
71035
- if (parent.type === "CallExpression") {
71036
- callbackStack.push(node);
71057
+ if (
71058
+ (parent.type !== "CallExpression" &&
71059
+ !(
71060
+ option.checkConstructorCallCallbacks &&
71061
+ parent.type === "NewExpression"
71062
+ )) ||
71063
+ parent.callee === node
71064
+ ) {
71065
+ return;
71037
71066
  }
71038
71067
 
71068
+ callbackStack.push(node);
71069
+
71039
71070
  if (callbackStack.length > THRESHOLD) {
71040
71071
  const opts = { num: callbackStack.length, max: THRESHOLD };
71041
71072
 
@@ -74858,6 +74889,12 @@ function requireNoCompareNegZero () {
74858
74889
  if (hasRequiredNoCompareNegZero) return noCompareNegZero;
74859
74890
  hasRequiredNoCompareNegZero = 1;
74860
74891
 
74892
+ //------------------------------------------------------------------------------
74893
+ // Requirements
74894
+ //------------------------------------------------------------------------------
74895
+
74896
+ const { getVariableByName } = requireAstUtils();
74897
+
74861
74898
  //------------------------------------------------------------------------------
74862
74899
  // Rule Definition
74863
74900
  //------------------------------------------------------------------------------
@@ -74874,15 +74911,24 @@ function requireNoCompareNegZero () {
74874
74911
  },
74875
74912
 
74876
74913
  fixable: null,
74914
+ hasSuggestions: true,
74877
74915
  schema: [],
74878
74916
 
74879
74917
  messages: {
74880
74918
  unexpected:
74881
74919
  "Do not use the '{{operator}}' operator to compare against -0.",
74920
+ suggestRemoveMinus:
74921
+ "Replace '-0' with '0' (keeps the current comparison behavior).",
74922
+ suggestObjectIs:
74923
+ "Replace with 'Object.is()' (changes the comparison to distinguish -0 from +0).",
74924
+ suggestNotObjectIs:
74925
+ "Replace with '!Object.is()' (changes the comparison to distinguish -0 from +0).",
74882
74926
  },
74883
74927
  },
74884
74928
 
74885
74929
  create(context) {
74930
+ const sourceCode = context.sourceCode;
74931
+
74886
74932
  //--------------------------------------------------------------------------
74887
74933
  // Helpers
74888
74934
  //--------------------------------------------------------------------------
@@ -74900,6 +74946,18 @@ function requireNoCompareNegZero () {
74900
74946
  node.argument.value === 0
74901
74947
  );
74902
74948
  }
74949
+
74950
+ /**
74951
+ * Gets the source text of an operand, keeping parentheses where required.
74952
+ * @param {ASTNode} operand A comparison operand node.
74953
+ * @returns {string} The source text of the operand.
74954
+ */
74955
+ function getOperandText(operand) {
74956
+ const text = sourceCode.getText(operand);
74957
+
74958
+ return operand.type === "SequenceExpression" ? `(${text})` : text;
74959
+ }
74960
+
74903
74961
  const OPERATORS_TO_CHECK = new Set([
74904
74962
  ">",
74905
74963
  ">=",
@@ -74913,15 +74971,71 @@ function requireNoCompareNegZero () {
74913
74971
 
74914
74972
  return {
74915
74973
  BinaryExpression(node) {
74916
- if (OPERATORS_TO_CHECK.has(node.operator)) {
74917
- if (isNegZero(node.left) || isNegZero(node.right)) {
74918
- context.report({
74919
- node,
74920
- messageId: "unexpected",
74921
- data: { operator: node.operator },
74922
- });
74923
- }
74974
+ if (!OPERATORS_TO_CHECK.has(node.operator)) {
74975
+ return;
74924
74976
  }
74977
+
74978
+ const leftIsNegZero = isNegZero(node.left);
74979
+ const rightIsNegZero = isNegZero(node.right);
74980
+
74981
+ if (!leftIsNegZero && !rightIsNegZero) {
74982
+ return;
74983
+ }
74984
+
74985
+ context.report({
74986
+ node,
74987
+ messageId: "unexpected",
74988
+ data: { operator: node.operator },
74989
+ suggest: [
74990
+ {
74991
+ messageId: "suggestRemoveMinus",
74992
+ *fix(fixer) {
74993
+ if (leftIsNegZero) {
74994
+ yield fixer.replaceText(node.left, "0");
74995
+ }
74996
+ if (rightIsNegZero) {
74997
+ yield fixer.replaceText(node.right, "0");
74998
+ }
74999
+ },
75000
+ },
75001
+ {
75002
+ messageId:
75003
+ node.operator === "==="
75004
+ ? "suggestObjectIs"
75005
+ : "suggestNotObjectIs",
75006
+ fix(fixer) {
75007
+ if (
75008
+ (node.operator !== "===" &&
75009
+ node.operator !== "!==") ||
75010
+ sourceCode.getCommentsInside(node).length >
75011
+ 0
75012
+ ) {
75013
+ return null;
75014
+ }
75015
+
75016
+ const objectVariable = getVariableByName(
75017
+ sourceCode.getScope(node),
75018
+ "Object",
75019
+ );
75020
+
75021
+ if (
75022
+ !objectVariable ||
75023
+ objectVariable.identifiers.length > 0
75024
+ ) {
75025
+ return null;
75026
+ }
75027
+
75028
+ const negation =
75029
+ node.operator === "===" ? "" : "!";
75030
+
75031
+ return fixer.replaceText(
75032
+ node,
75033
+ `${negation}Object.is(${getOperandText(node.left)}, ${getOperandText(node.right)})`,
75034
+ );
75035
+ },
75036
+ },
75037
+ ],
75038
+ });
74925
75039
  },
74926
75040
  };
74927
75041
  },
@@ -75598,6 +75712,8 @@ function requireNoConstantBinaryExpression () {
75598
75712
  ">>>",
75599
75713
  ]);
75600
75714
 
75715
+ const RELATIONAL_OPERATORS = new Set(["<", "<=", ">", ">="]);
75716
+
75601
75717
  //------------------------------------------------------------------------------
75602
75718
  // Helpers
75603
75719
  //------------------------------------------------------------------------------
@@ -75657,7 +75773,9 @@ function requireNoConstantBinaryExpression () {
75657
75773
  return (
75658
75774
  (functionName === "Boolean" ||
75659
75775
  functionName === "String" ||
75660
- functionName === "Number") &&
75776
+ functionName === "Number" ||
75777
+ functionName === "Symbol" ||
75778
+ functionName === "BigInt") &&
75661
75779
  isReferenceToGlobalVariable(scope, node.callee)
75662
75780
  );
75663
75781
  }
@@ -75942,7 +76060,10 @@ function requireNoConstantBinaryExpression () {
75942
76060
  const functionName = node.callee.name;
75943
76061
 
75944
76062
  if (
75945
- (functionName === "String" || functionName === "Number") &&
76063
+ (functionName === "String" ||
76064
+ functionName === "Number" ||
76065
+ functionName === "BigInt" ||
76066
+ functionName === "Symbol") &&
75946
76067
  isReferenceToGlobalVariable(scope, node.callee)
75947
76068
  ) {
75948
76069
  return true;
@@ -76055,6 +76176,33 @@ function requireNoConstantBinaryExpression () {
76055
76176
  return null;
76056
76177
  }
76057
76178
 
76179
+ /**
76180
+ * Checks if a node is a statically knowable literal.
76181
+ * @param {Scope} scope The scope in which the node was found.
76182
+ * @param {ASTNode} node The node to test.
76183
+ * @returns {boolean} `true` if the node is a literal.
76184
+ */
76185
+ function isStaticLiteral(scope, node) {
76186
+ switch (node.type) {
76187
+ case "Literal":
76188
+ return true;
76189
+ case "UnaryExpression":
76190
+ return (
76191
+ ["-", "+", "~"].includes(node.operator) &&
76192
+ node.argument.type === "Literal"
76193
+ );
76194
+ case "Identifier":
76195
+ return (
76196
+ node.name === "undefined" &&
76197
+ isReferenceToGlobalVariable(scope, node)
76198
+ );
76199
+ case "TemplateLiteral":
76200
+ return node.expressions.length === 0;
76201
+ default:
76202
+ return false;
76203
+ }
76204
+ }
76205
+
76058
76206
  //------------------------------------------------------------------------------
76059
76207
  // Rule Definition
76060
76208
  //------------------------------------------------------------------------------
@@ -76069,7 +76217,22 @@ function requireNoConstantBinaryExpression () {
76069
76217
  recommended: true,
76070
76218
  url: "https://eslint.org/docs/latest/rules/no-constant-binary-expression",
76071
76219
  },
76072
- schema: [],
76220
+ defaultOptions: [
76221
+ {
76222
+ checkRelationalComparisons: false,
76223
+ },
76224
+ ],
76225
+ schema: [
76226
+ {
76227
+ type: "object",
76228
+ properties: {
76229
+ checkRelationalComparisons: {
76230
+ type: "boolean",
76231
+ },
76232
+ },
76233
+ additionalProperties: false,
76234
+ },
76235
+ ],
76073
76236
  messages: {
76074
76237
  constantBinaryOperand:
76075
76238
  "Unexpected constant binary expression. Compares constantly with the {{otherSide}}-hand side of the `{{operator}}`.",
@@ -76079,11 +76242,14 @@ function requireNoConstantBinaryExpression () {
76079
76242
  "Unexpected comparison to newly constructed object. These two values can never be equal.",
76080
76243
  bothAlwaysNew:
76081
76244
  "Unexpected comparison of two newly constructed objects. These two values can never be equal.",
76245
+ constantRelationalComparison:
76246
+ "Unexpected constant relational comparison. Both sides of the `{{operator}}` are literal values.",
76082
76247
  },
76083
76248
  },
76084
76249
 
76085
76250
  create(context) {
76086
76251
  const sourceCode = context.sourceCode;
76252
+ const { checkRelationalComparisons } = context.options[0];
76087
76253
 
76088
76254
  return {
76089
76255
  LogicalExpression(node) {
@@ -76157,6 +76323,20 @@ function requireNoConstantBinaryExpression () {
76157
76323
  messageId: "bothAlwaysNew",
76158
76324
  });
76159
76325
  }
76326
+ } else if (
76327
+ checkRelationalComparisons &&
76328
+ RELATIONAL_OPERATORS.has(operator)
76329
+ ) {
76330
+ if (
76331
+ isStaticLiteral(scope, left) &&
76332
+ isStaticLiteral(scope, right)
76333
+ ) {
76334
+ context.report({
76335
+ node,
76336
+ messageId: "constantRelationalComparison",
76337
+ data: { operator },
76338
+ });
76339
+ }
76160
76340
  }
76161
76341
  },
76162
76342
 
@@ -79614,6 +79794,8 @@ function requireNoControlRegex () {
79614
79794
  },
79615
79795
 
79616
79796
  create(context) {
79797
+ const sourceCode = context.sourceCode;
79798
+
79617
79799
  /**
79618
79800
  * Get the regex expression
79619
79801
  * @param {ASTNode} node `Literal` node to evaluate
@@ -79632,6 +79814,7 @@ function requireNoControlRegex () {
79632
79814
  node.parent.type === "CallExpression") &&
79633
79815
  node.parent.callee.type === "Identifier" &&
79634
79816
  node.parent.callee.name === "RegExp" &&
79817
+ sourceCode.isGlobalReference(node.parent.callee) &&
79635
79818
  node.parent.arguments[0] === node
79636
79819
  ) {
79637
79820
  const pattern = node.value;
@@ -83083,7 +83266,8 @@ function requireNoExtraBooleanCast () {
83083
83266
  (node.type === "CallExpression" ||
83084
83267
  node.type === "NewExpression") &&
83085
83268
  node.callee.type === "Identifier" &&
83086
- node.callee.name === "Boolean"
83269
+ node.callee.name === "Boolean" &&
83270
+ sourceCode.isGlobalReference(node.callee)
83087
83271
  );
83088
83272
  }
83089
83273
 
@@ -83315,7 +83499,8 @@ function requireNoExtraBooleanCast () {
83315
83499
  CallExpression(node) {
83316
83500
  if (
83317
83501
  node.callee.type !== "Identifier" ||
83318
- node.callee.name !== "Boolean"
83502
+ node.callee.name !== "Boolean" ||
83503
+ !sourceCode.isGlobalReference(node.callee)
83319
83504
  ) {
83320
83505
  return;
83321
83506
  }
@@ -86246,6 +86431,21 @@ function requireNoImplicitCoercion () {
86246
86431
  const [options] = context.options;
86247
86432
  const sourceCode = context.sourceCode;
86248
86433
 
86434
+ /**
86435
+ * Gets the source text of a node to be used as the argument of a
86436
+ * `Boolean()`, `Number()`, or `String()` call in a recommendation. A
86437
+ * `SequenceExpression` operand must be parenthesized, otherwise its commas
86438
+ * would be parsed as argument separators, which changes the evaluated
86439
+ * operand (for example `!!(a, b)` becomes `Boolean(a, b)`).
86440
+ * @param {ASTNode} node The operand node.
86441
+ * @returns {string} The source text, parenthesized if needed.
86442
+ */
86443
+ function getOperandText(node) {
86444
+ const text = sourceCode.getText(node);
86445
+
86446
+ return node.type === "SequenceExpression" ? `(${text})` : text;
86447
+ }
86448
+
86249
86449
  /**
86250
86450
  * Reports an error and autofixes the node
86251
86451
  * @param {ASTNode} node An ast node to report the error on.
@@ -86311,7 +86511,7 @@ function requireNoImplicitCoercion () {
86311
86511
  options.boolean &&
86312
86512
  isDoubleLogicalNegating(node)
86313
86513
  ) {
86314
- const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
86514
+ const recommendation = `Boolean(${getOperandText(node.argument.argument)})`;
86315
86515
  const variable = astUtils.getVariableByName(
86316
86516
  sourceCode.getScope(node),
86317
86517
  "Boolean",
@@ -86346,7 +86546,7 @@ function requireNoImplicitCoercion () {
86346
86546
  node.operator === "+" &&
86347
86547
  !isNumeric(node.argument)
86348
86548
  ) {
86349
- const recommendation = `Number(${sourceCode.getText(node.argument)})`;
86549
+ const recommendation = `Number(${getOperandText(node.argument)})`;
86350
86550
 
86351
86551
  report(node, recommendation, true, false);
86352
86552
  }
@@ -86361,7 +86561,7 @@ function requireNoImplicitCoercion () {
86361
86561
  node.argument.operator === "-" &&
86362
86562
  !isNumeric(node.argument.argument)
86363
86563
  ) {
86364
- const recommendation = `Number(${sourceCode.getText(node.argument.argument)})`;
86564
+ const recommendation = `Number(${getOperandText(node.argument.argument)})`;
86365
86565
 
86366
86566
  report(node, recommendation, true, false);
86367
86567
  }
@@ -86381,7 +86581,7 @@ function requireNoImplicitCoercion () {
86381
86581
  getNonNumericOperand(node);
86382
86582
 
86383
86583
  if (nonNumericOperand) {
86384
- const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
86584
+ const recommendation = `Number(${getOperandText(nonNumericOperand)})`;
86385
86585
 
86386
86586
  report(node, recommendation, true, false);
86387
86587
  }
@@ -86396,7 +86596,7 @@ function requireNoImplicitCoercion () {
86396
86596
  node.right.value === 0 &&
86397
86597
  !isNumeric(node.left)
86398
86598
  ) {
86399
- const recommendation = `Number(${sourceCode.getText(node.left)})`;
86599
+ const recommendation = `Number(${getOperandText(node.left)})`;
86400
86600
 
86401
86601
  report(node, recommendation, true, false);
86402
86602
  }
@@ -86408,7 +86608,7 @@ function requireNoImplicitCoercion () {
86408
86608
  options.string &&
86409
86609
  isConcatWithEmptyString(node)
86410
86610
  ) {
86411
- const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
86611
+ const recommendation = `String(${getOperandText(getNonEmptyOperand(node))})`;
86412
86612
 
86413
86613
  report(node, recommendation, true, false);
86414
86614
  }
@@ -86460,8 +86660,7 @@ function requireNoImplicitCoercion () {
86460
86660
  return;
86461
86661
  }
86462
86662
 
86463
- const code = sourceCode.getText(node.expressions[0]);
86464
- const recommendation = `String(${code})`;
86663
+ const recommendation = `String(${getOperandText(node.expressions[0])})`;
86465
86664
 
86466
86665
  report(node, recommendation, true, false);
86467
86666
  },
@@ -87420,6 +87619,7 @@ function requireNoInvalidRegexp () {
87420
87619
  },
87421
87620
 
87422
87621
  create(context) {
87622
+ const sourceCode = context.sourceCode;
87423
87623
  const [{ allowConstructorFlags }] = context.options;
87424
87624
  let allowedFlags = [];
87425
87625
 
@@ -87547,7 +87747,8 @@ function requireNoInvalidRegexp () {
87547
87747
  "CallExpression, NewExpression"(node) {
87548
87748
  if (
87549
87749
  node.callee.type !== "Identifier" ||
87550
- node.callee.name !== "RegExp"
87750
+ node.callee.name !== "RegExp" ||
87751
+ !sourceCode.isGlobalReference(node.callee)
87551
87752
  ) {
87552
87753
  return;
87553
87754
  }
@@ -91307,14 +91508,12 @@ function requireNoMixedSpacesAndTabs () {
91307
91508
  sourceCode.getIndexFromLoc(loc.start),
91308
91509
  );
91309
91510
 
91310
- if (
91311
- !(
91312
- containingNode &&
91313
- ["Literal", "TemplateElement"].includes(
91314
- containingNode.type,
91315
- )
91511
+ if (!(
91512
+ containingNode &&
91513
+ ["Literal", "TemplateElement"].includes(
91514
+ containingNode.type,
91316
91515
  )
91317
- ) {
91516
+ )) {
91318
91517
  context.report({
91319
91518
  node,
91320
91519
  loc,
@@ -94136,13 +94335,12 @@ function requireNoPromiseExecutorReturn () {
94136
94335
  });
94137
94336
  }
94138
94337
 
94139
- // Do not suggest wrapping an unnamed FunctionExpression in braces as that would be invalid syntax.
94140
- if (
94141
- !(
94142
- node.body.type === "FunctionExpression" &&
94143
- !node.body.id
94144
- )
94145
- ) {
94338
+ // Do not suggest wrapping an unnamed function or class expression in braces as that would be invalid syntax.
94339
+ if (!(
94340
+ (node.body.type === "FunctionExpression" ||
94341
+ node.body.type === "ClassExpression") &&
94342
+ !node.body.id
94343
+ )) {
94146
94344
  suggest.push({
94147
94345
  messageId: "wrapBraces",
94148
94346
  fix(fixer) {
@@ -97820,12 +98018,10 @@ function requireNoScriptUrl () {
97820
98018
  }
97821
98019
  },
97822
98020
  TemplateLiteral(node) {
97823
- if (
97824
- !(
97825
- node.parent &&
97826
- node.parent.type === "TaggedTemplateExpression"
97827
- )
97828
- ) {
98021
+ if (!(
98022
+ node.parent &&
98023
+ node.parent.type === "TaggedTemplateExpression"
98024
+ )) {
97829
98025
  check(node);
97830
98026
  }
97831
98027
  },
@@ -98792,12 +98988,10 @@ function requireNoShadow () {
98792
98988
 
98793
98989
  const { variableScope } = variable.scope;
98794
98990
 
98795
- if (
98796
- !(
98797
- FUNC_EXPR_NODE_TYPES.has(variableScope.block.type) &&
98798
- getOuterScope(variableScope) === shadowedVariable.scope
98799
- )
98800
- ) {
98991
+ if (!(
98992
+ FUNC_EXPR_NODE_TYPES.has(variableScope.block.type) &&
98993
+ getOuterScope(variableScope) === shadowedVariable.scope
98994
+ )) {
98801
98995
  return false;
98802
98996
  }
98803
98997
 
@@ -98922,14 +99116,12 @@ function requireNoShadow () {
98922
99116
  return false;
98923
99117
  }
98924
99118
 
98925
- if (
98926
- !(
98927
- (innerDef.type === "FunctionName" &&
98928
- innerDef.node.type === "FunctionExpression") ||
98929
- (innerDef.type === "ClassName" &&
98930
- innerDef.node.type === "ClassExpression")
98931
- )
98932
- ) {
99119
+ if (!(
99120
+ (innerDef.type === "FunctionName" &&
99121
+ innerDef.node.type === "FunctionExpression") ||
99122
+ (innerDef.type === "ClassName" &&
99123
+ innerDef.node.type === "ClassExpression")
99124
+ )) {
98933
99125
  return false;
98934
99126
  }
98935
99127
 
@@ -98949,12 +99141,10 @@ function requireNoShadow () {
98949
99141
  const nodeToCheck = innerDef.node; // FunctionExpression or ClassExpression node
98950
99142
 
98951
99143
  // Exit early if the node to check isn't inside the initializer
98952
- if (
98953
- !(
98954
- initializerNode.range[0] <= nodeToCheck.range[0] &&
98955
- nodeToCheck.range[1] <= initializerNode.range[1]
98956
- )
98957
- ) {
99144
+ if (!(
99145
+ initializerNode.range[0] <= nodeToCheck.range[0] &&
99146
+ nodeToCheck.range[1] <= initializerNode.range[1]
99147
+ )) {
98958
99148
  return false;
98959
99149
  }
98960
99150
 
@@ -100240,12 +100430,17 @@ function requireNoThrowLiteral () {
100240
100430
  },
100241
100431
 
100242
100432
  create(context) {
100433
+ const sourceCode = context.sourceCode;
100434
+
100243
100435
  return {
100244
100436
  ThrowStatement(node) {
100245
100437
  if (!astUtils.couldBeError(node.argument)) {
100246
100438
  context.report({ node, messageId: "object" });
100247
100439
  } else if (node.argument.type === "Identifier") {
100248
- if (node.argument.name === "undefined") {
100440
+ if (
100441
+ node.argument.name === "undefined" &&
100442
+ sourceCode.isGlobalReference(node.argument)
100443
+ ) {
100249
100444
  context.report({ node, messageId: "undef" });
100250
100445
  }
100251
100446
  }
@@ -115332,6 +115527,11 @@ function requirePreferExponentiationOperator () {
115332
115527
  operator: "**",
115333
115528
  });
115334
115529
 
115530
+ /*
115531
+ * Characters that can cause continuation without preceding semi
115532
+ */
115533
+ const continuationChars = new Set(["(", "[", "/", "`"]);
115534
+
115335
115535
  /**
115336
115536
  * Determines whether the given node needs parens if used as the base in an exponentiation binary expression.
115337
115537
  * @param {ASTNode} base The node to check.
@@ -115460,11 +115660,36 @@ function requirePreferExponentiationOperator () {
115460
115660
  shouldParenthesizeBase = doesBaseNeedParens(base),
115461
115661
  shouldParenthesizeExponent =
115462
115662
  doesExponentNeedParens(exponent),
115463
- shouldParenthesizeAll =
115464
- doesExponentiationExpressionNeedParens(
115465
- node,
115466
- sourceCode,
115467
- );
115663
+ isStartOfExpressionStatement =
115664
+ astUtils.isStartOfExpressionStatement(node);
115665
+
115666
+ let shouldParenthesizeAll =
115667
+ doesExponentiationExpressionNeedParens(
115668
+ node,
115669
+ sourceCode,
115670
+ );
115671
+
115672
+ /*
115673
+ * `function`, `class`, or `{` token at the start of an expression statement
115674
+ * would cause incorrect parsing.
115675
+ * https://github.com/eslint/eslint/issues/20987
115676
+ */
115677
+ if (
115678
+ !shouldParenthesizeAll &&
115679
+ !shouldParenthesizeBase &&
115680
+ isStartOfExpressionStatement
115681
+ ) {
115682
+ const firstTokenOfBase = sourceCode.getFirstToken(base);
115683
+
115684
+ if (
115685
+ astUtils.isOpeningBraceToken(firstTokenOfBase) ||
115686
+ (firstTokenOfBase.type === "Keyword" &&
115687
+ (firstTokenOfBase.value === "function" ||
115688
+ firstTokenOfBase.value === "class"))
115689
+ ) {
115690
+ shouldParenthesizeAll = true;
115691
+ }
115692
+ }
115468
115693
 
115469
115694
  let prefix = "",
115470
115695
  suffix = "";
@@ -115517,6 +115742,15 @@ function requirePreferExponentiationOperator () {
115517
115742
  shouldParenthesizeAll,
115518
115743
  );
115519
115744
 
115745
+ if (
115746
+ !prefix &&
115747
+ isStartOfExpressionStatement &&
115748
+ continuationChars.has(replacement[0]) &&
115749
+ astUtils.needsPrecedingSemicolon(sourceCode, node)
115750
+ ) {
115751
+ prefix = ";";
115752
+ }
115753
+
115520
115754
  return fixer.replaceText(
115521
115755
  node,
115522
115756
  `${prefix}${replacement}${suffix}`,
@@ -115784,14 +116018,22 @@ function requirePreferNumericLiterals () {
115784
116018
  * Checks to see if a CallExpression's callee node is `parseInt` or
115785
116019
  * `Number.parseInt`.
115786
116020
  * @param {ASTNode} calleeNode The callee node to evaluate.
116021
+ * @param {SourceCode} sourceCode The source code object.
115787
116022
  * @returns {boolean} True if the callee is `parseInt` or `Number.parseInt`,
115788
116023
  * false otherwise.
115789
116024
  */
115790
- function isParseInt(calleeNode) {
115791
- return (
115792
- astUtils.isSpecificId(calleeNode, "parseInt") ||
115793
- astUtils.isSpecificMemberAccess(calleeNode, "Number", "parseInt")
115794
- );
116025
+ function isParseInt(calleeNode, sourceCode) {
116026
+ if (astUtils.isSpecificId(calleeNode, "parseInt")) {
116027
+ return sourceCode.isGlobalReference(calleeNode);
116028
+ }
116029
+
116030
+ if (astUtils.isSpecificMemberAccess(calleeNode, "Number", "parseInt")) {
116031
+ const objectNode = astUtils.skipChainExpression(calleeNode).object;
116032
+
116033
+ return sourceCode.isGlobalReference(objectNode);
116034
+ }
116035
+
116036
+ return false;
115795
116037
  }
115796
116038
 
115797
116039
  //------------------------------------------------------------------------------
@@ -115840,7 +116082,7 @@ function requirePreferNumericLiterals () {
115840
116082
  radixNode.type === "Literal" &&
115841
116083
  typeof radix === "number" &&
115842
116084
  radixMap.has(radix) &&
115843
- isParseInt(node.callee)
116085
+ isParseInt(node.callee, sourceCode)
115844
116086
  ) {
115845
116087
  const { system, literalPrefix } = radixMap.get(radix);
115846
116088
 
@@ -115999,12 +116241,10 @@ function requirePreferObjectHasOwn () {
115999
116241
 
116000
116242
  return {
116001
116243
  CallExpression(node) {
116002
- if (
116003
- !(
116004
- node.callee.type === "MemberExpression" &&
116005
- node.callee.object.type === "MemberExpression"
116006
- )
116007
- ) {
116244
+ if (!(
116245
+ node.callee.type === "MemberExpression" &&
116246
+ node.callee.object.type === "MemberExpression"
116247
+ )) {
116008
116248
  return;
116009
116249
  }
116010
116250
 
@@ -116471,11 +116711,15 @@ function requirePreferPromiseRejectErrors () {
116471
116711
  if (!callExpression.arguments.length && allowEmptyReject) {
116472
116712
  return;
116473
116713
  }
116714
+
116715
+ const rejectionReason = callExpression.arguments[0];
116716
+
116474
116717
  if (
116475
116718
  !callExpression.arguments.length ||
116476
- !astUtils.couldBeError(callExpression.arguments[0]) ||
116477
- (callExpression.arguments[0].type === "Identifier" &&
116478
- callExpression.arguments[0].name === "undefined")
116719
+ !astUtils.couldBeError(rejectionReason) ||
116720
+ (rejectionReason.type === "Identifier" &&
116721
+ rejectionReason.name === "undefined" &&
116722
+ sourceCode.isGlobalReference(rejectionReason))
116479
116723
  ) {
116480
116724
  context.report({
116481
116725
  node: callExpression,
@@ -116490,10 +116734,15 @@ function requirePreferPromiseRejectErrors () {
116490
116734
  * @returns {boolean} `true` if the call is a Promise.reject() call
116491
116735
  */
116492
116736
  function isPromiseRejectCall(node) {
116493
- return astUtils.isSpecificMemberAccess(
116494
- node.callee,
116495
- "Promise",
116496
- "reject",
116737
+ return (
116738
+ astUtils.isSpecificMemberAccess(
116739
+ node.callee,
116740
+ "Promise",
116741
+ "reject",
116742
+ ) &&
116743
+ sourceCode.isGlobalReference(
116744
+ astUtils.skipChainExpression(node.callee).object,
116745
+ )
116497
116746
  );
116498
116747
  }
116499
116748
 
@@ -116518,6 +116767,7 @@ function requirePreferPromiseRejectErrors () {
116518
116767
  if (
116519
116768
  node.callee.type === "Identifier" &&
116520
116769
  node.callee.name === "Promise" &&
116770
+ sourceCode.isGlobalReference(node.callee) &&
116521
116771
  node.arguments.length &&
116522
116772
  astUtils.isFunction(node.arguments[0]) &&
116523
116773
  node.arguments[0].params.length > 1 &&
@@ -117970,18 +118220,13 @@ function requirePreserveCaughtError () {
117970
118220
  /**
117971
118221
  * Finds and returns information about the `cause` property of an error being thrown.
117972
118222
  * @param {ASTNode} throwStatement `ThrowStatement` to be checked.
118223
+ * @param {number} optionsIndex The index of the options argument in the error constructor.
117973
118224
  * @returns {{ value: ASTNode; multipleDefinitions: boolean; } | UNKNOWN_CAUSE | null}
117974
118225
  * Information about the `cause` of the error being thrown, such as the value node and
117975
118226
  * whether there are multiple definitions of `cause`. `null` if there is no `cause`.
117976
118227
  */
117977
- function getErrorCause(throwStatement) {
118228
+ function getErrorCause(throwStatement, optionsIndex) {
117978
118229
  const throwExpression = throwStatement.argument;
117979
- /*
117980
- * Determine which argument index holds the options object
117981
- * `AggregateError` is a special case as it accepts the `options` object as third argument.
117982
- */
117983
- const optionsIndex =
117984
- throwExpression.callee.name === "AggregateError" ? 2 : 1;
117985
118230
 
117986
118231
  /*
117987
118232
  * Make sure there is no `SpreadElement` at or before the `optionsIndex`
@@ -118082,6 +118327,7 @@ function requirePreserveCaughtError () {
118082
118327
  defaultOptions: [
118083
118328
  {
118084
118329
  requireCatchParameter: false,
118330
+ errorClassNames: [],
118085
118331
  },
118086
118332
  ],
118087
118333
 
@@ -118091,14 +118337,6 @@ function requirePreserveCaughtError () {
118091
118337
  recommended: true,
118092
118338
  url: "https://eslint.org/docs/latest/rules/preserve-caught-error", // URL to the documentation page for this rule
118093
118339
  },
118094
- /*
118095
- * TODO: We should allow passing `customErrorTypes` option once something like `typescript-eslint`'s
118096
- * `TypeOrValueSpecifier` is implemented in core Eslint.
118097
- * See:
118098
- * 1. https://typescript-eslint.io/packages/type-utils/type-or-value-specifier/
118099
- * 2. https://github.com/eslint/eslint/pull/19913#discussion_r2192608593
118100
- * 3. https://github.com/eslint/eslint/discussions/16540
118101
- */
118102
118340
  schema: [
118103
118341
  {
118104
118342
  type: "object",
@@ -118108,6 +118346,33 @@ function requirePreserveCaughtError () {
118108
118346
  description:
118109
118347
  "Requires the catch blocks to always have the caught error parameter so it is not discarded.",
118110
118348
  },
118349
+ errorClassNames: {
118350
+ type: "array",
118351
+ description:
118352
+ "Additional error class names to check for cause preservation.",
118353
+ items: {
118354
+ oneOf: [
118355
+ {
118356
+ type: "string",
118357
+ },
118358
+ {
118359
+ type: "object",
118360
+ required: ["name", "argumentPosition"],
118361
+ properties: {
118362
+ name: {
118363
+ type: "string",
118364
+ },
118365
+ argumentPosition: {
118366
+ type: "integer",
118367
+ minimum: 1,
118368
+ },
118369
+ },
118370
+ additionalProperties: false,
118371
+ },
118372
+ ],
118373
+ },
118374
+ uniqueItems: true,
118375
+ },
118111
118376
  },
118112
118377
  additionalProperties: false,
118113
118378
  },
@@ -118131,12 +118396,34 @@ function requirePreserveCaughtError () {
118131
118396
 
118132
118397
  create(context) {
118133
118398
  const sourceCode = context.sourceCode;
118134
- const [{ requireCatchParameter }] = context.options;
118399
+ const [{ requireCatchParameter, errorClassNames }] = context.options;
118400
+
118401
+ const errorClassNamesMap = new Map();
118402
+ for (const item of errorClassNames) {
118403
+ const name = typeof item === "string" ? item : item.name;
118404
+ const argumentPosition =
118405
+ typeof item === "string" ? 2 : item.argumentPosition;
118406
+
118407
+ errorClassNamesMap.set(name, argumentPosition);
118408
+ }
118135
118409
 
118136
118410
  //----------------------------------------------------------------------
118137
118411
  // Helpers
118138
118412
  //----------------------------------------------------------------------
118139
118413
 
118414
+ /**
118415
+ * Checks if the given callee refers to a built-in global Error constructor.
118416
+ * @param {ASTNode} callee The callee node.
118417
+ * @returns {boolean} `true` if the callee is a built-in global Error constructor.
118418
+ */
118419
+ function isBuiltInGlobalError(callee) {
118420
+ return (
118421
+ callee.type === "Identifier" &&
118422
+ BUILT_IN_ERROR_TYPES.has(callee.name) &&
118423
+ sourceCode.isGlobalReference(callee)
118424
+ );
118425
+ }
118426
+
118140
118427
  /**
118141
118428
  * Checks if a `ThrowStatement` is constructing and throwing a new `Error` object.
118142
118429
  *
@@ -118146,18 +118433,33 @@ function requirePreserveCaughtError () {
118146
118433
  * @returns {boolean} `true` if a new "Error" is being thrown, else `false`.
118147
118434
  */
118148
118435
  function isThrowingNewError(throwStatement) {
118436
+ if (!(
118437
+ throwStatement.argument.type === "NewExpression" ||
118438
+ throwStatement.argument.type === "CallExpression"
118439
+ )) {
118440
+ return false;
118441
+ }
118442
+
118443
+ const callee = throwStatement.argument.callee;
118444
+
118445
+ /*
118446
+ * Make sure the thrown Error instance is one of the built-in global error types.
118447
+ * Custom imports could shadow this, which would lead to false positives.
118448
+ * e.g. import { Error } from "./my-custom-error.js";
118449
+ * throw Error("Failed to perform error prone operations");
118450
+ */
118451
+ if (isBuiltInGlobalError(callee)) {
118452
+ return true;
118453
+ }
118454
+
118455
+ const target =
118456
+ callee.type === "MemberExpression" && !callee.computed
118457
+ ? callee.property
118458
+ : callee;
118459
+
118149
118460
  return (
118150
- (throwStatement.argument.type === "NewExpression" ||
118151
- throwStatement.argument.type === "CallExpression") &&
118152
- throwStatement.argument.callee.type === "Identifier" &&
118153
- BUILT_IN_ERROR_TYPES.has(throwStatement.argument.callee.name) &&
118154
- /*
118155
- * Make sure the thrown Error is instance is one of the built-in global error types.
118156
- * Custom imports could shadow this, which would lead to false positives.
118157
- * e.g. import { Error } from "./my-custom-error.js";
118158
- * throw Error("Failed to perform error prone operations");
118159
- */
118160
- sourceCode.isGlobalReference(throwStatement.argument.callee)
118461
+ target.type === "Identifier" &&
118462
+ errorClassNamesMap.has(target.name)
118161
118463
  );
118162
118464
  }
118163
118465
 
@@ -118186,6 +118488,30 @@ function requirePreserveCaughtError () {
118186
118488
  );
118187
118489
  }
118188
118490
 
118491
+ /**
118492
+ * Gets the token after which new arguments should be inserted for the
118493
+ * given argument, accounting for parentheses wrapping the argument so
118494
+ * that insertions land outside them (e.g. `new Error(("msg"))`).
118495
+ * @param {ASTNode} argNode The argument node.
118496
+ * @param {ASTNode} callNode The enclosing call or new expression.
118497
+ * @returns {Token} The token to insert after.
118498
+ */
118499
+ function getLastArgumentToken(argNode, callNode) {
118500
+ const callLastToken = sourceCode.getLastToken(callNode);
118501
+ let token = sourceCode.getLastToken(argNode);
118502
+ let nextToken = sourceCode.getTokenAfter(token);
118503
+
118504
+ while (
118505
+ nextToken &&
118506
+ nextToken !== callLastToken &&
118507
+ astUtils.isClosingParenToken(nextToken)
118508
+ ) {
118509
+ token = nextToken;
118510
+ nextToken = sourceCode.getTokenAfter(token);
118511
+ }
118512
+ return token;
118513
+ }
118514
+
118189
118515
  //----------------------------------------------------------------------
118190
118516
  // Public
118191
118517
  //----------------------------------------------------------------------
@@ -118229,8 +118555,30 @@ function requirePreserveCaughtError () {
118229
118555
  return;
118230
118556
  }
118231
118557
 
118558
+ // Determine the options argument index
118559
+ const callee = throwStatement.argument.callee;
118560
+ const errorClassName =
118561
+ callee.type === "Identifier"
118562
+ ? callee.name
118563
+ : callee.property.name;
118564
+
118565
+ const builtInGlobalError = isBuiltInGlobalError(callee);
118566
+
118567
+ let optionsIndex;
118568
+ if (builtInGlobalError) {
118569
+ optionsIndex =
118570
+ errorClassName === "AggregateError" ? 2 : 1;
118571
+ } else {
118572
+ const argumentPosition =
118573
+ errorClassNamesMap.get(errorClassName);
118574
+ optionsIndex = argumentPosition - 1;
118575
+ }
118576
+
118232
118577
  // Check if there is a cause attached to the new error
118233
- const errorCauseInfo = getErrorCause(throwStatement);
118578
+ const errorCauseInfo = getErrorCause(
118579
+ throwStatement,
118580
+ optionsIndex,
118581
+ );
118234
118582
 
118235
118583
  if (errorCauseInfo === UNKNOWN_CAUSE) {
118236
118584
  // Error options exist, but too complicated to be analyzed/fixed
@@ -118249,11 +118597,33 @@ function requirePreserveCaughtError () {
118249
118597
  const throwExpression =
118250
118598
  throwStatement.argument;
118251
118599
  const args = throwExpression.arguments;
118252
- const errorType =
118253
- throwExpression.callee.name;
118600
+
118601
+ /**
118602
+ * Inserts `cause` into the options argument if it is an `ObjectExpression`.
118603
+ * @param {ASTNode} optionsArg The options argument node.
118604
+ * @returns {Fix | null} The fix, or `null` if the argument is not an object.
118605
+ */
118606
+ function fixExistingOptions(
118607
+ optionsArg,
118608
+ ) {
118609
+ if (
118610
+ optionsArg.type ===
118611
+ "ObjectExpression"
118612
+ ) {
118613
+ return insertCauseIntoOptions(
118614
+ fixer,
118615
+ optionsArg,
118616
+ caughtError.name,
118617
+ );
118618
+ }
118619
+ return null;
118620
+ }
118254
118621
 
118255
118622
  // AggregateError: errors, message, options
118256
- if (errorType === "AggregateError") {
118623
+ if (
118624
+ builtInGlobalError &&
118625
+ errorClassName === "AggregateError"
118626
+ ) {
118257
118627
  const errorsArg = args[0];
118258
118628
  const messageArg = args[1];
118259
118629
  const optionsArg = args[2];
@@ -118290,7 +118660,10 @@ function requirePreserveCaughtError () {
118290
118660
  if (!messageArg) {
118291
118661
  // Case: `throw new AggregateError([])` → insert message and options
118292
118662
  return fixer.insertTextAfter(
118293
- errorsArg,
118663
+ getLastArgumentToken(
118664
+ errorsArg,
118665
+ throwExpression,
118666
+ ),
118294
118667
  `, "", { cause: ${caughtError.name} }`,
118295
118668
  );
118296
118669
  }
@@ -118298,32 +118671,94 @@ function requirePreserveCaughtError () {
118298
118671
  if (!optionsArg) {
118299
118672
  // Case: `throw new AggregateError([], "")` → insert error options only
118300
118673
  return fixer.insertTextAfter(
118301
- messageArg,
118674
+ getLastArgumentToken(
118675
+ messageArg,
118676
+ throwExpression,
118677
+ ),
118302
118678
  `, { cause: ${caughtError.name} }`,
118303
118679
  );
118304
118680
  }
118305
118681
 
118306
- if (
118307
- optionsArg.type ===
118308
- "ObjectExpression"
118309
- ) {
118310
- return insertCauseIntoOptions(
118311
- fixer,
118312
- optionsArg,
118313
- caughtError.name,
118682
+ return fixExistingOptions(
118683
+ optionsArg,
118684
+ );
118685
+ }
118686
+
118687
+ // Normal Error types
118688
+ if (builtInGlobalError) {
118689
+ const messageArg = args[0];
118690
+ const optionsArg = args[1];
118691
+
118692
+ if (!messageArg) {
118693
+ // Case: `throw new Error()` → insert both message and options
118694
+ const lastToken =
118695
+ sourceCode.getLastToken(
118696
+ throwExpression,
118697
+ );
118698
+ const lastCalleeToken =
118699
+ sourceCode.getLastToken(
118700
+ throwExpression.callee,
118701
+ );
118702
+ const parenToken =
118703
+ sourceCode.getFirstTokenBetween(
118704
+ lastCalleeToken,
118705
+ lastToken,
118706
+ astUtils.isOpeningParenToken,
118707
+ );
118708
+
118709
+ if (parenToken) {
118710
+ return fixer.insertTextAfter(
118711
+ parenToken,
118712
+ `"", { cause: ${caughtError.name} }`,
118713
+ );
118714
+ }
118715
+ return fixer.insertTextAfter(
118716
+ throwExpression.callee,
118717
+ `("", { cause: ${caughtError.name} })`,
118314
118718
  );
118315
118719
  }
118720
+ if (!optionsArg) {
118721
+ // Case: `throw new Error("Some message")` → insert only options
118722
+ return fixer.insertTextAfter(
118723
+ getLastArgumentToken(
118724
+ messageArg,
118725
+ throwExpression,
118726
+ ),
118727
+ `, { cause: ${caughtError.name} }`,
118728
+ );
118729
+ }
118730
+
118731
+ return fixExistingOptions(
118732
+ optionsArg,
118733
+ );
118734
+ }
118316
118735
 
118317
- // Complex dynamic options — skip
118736
+ // Custom error types
118737
+ const optionsArg = args[optionsIndex];
118738
+
118739
+ /*
118740
+ * Custom error signature is unknown, so skip the suggestion rather
118741
+ * than synthesize placeholder values for missing positional args.
118742
+ */
118743
+ if (args.length < optionsIndex) {
118318
118744
  return null;
118319
118745
  }
118320
118746
 
118321
- // Normal Error types
118322
- const messageArg = args[0];
118323
- const optionsArg = args[1];
118747
+ if (!optionsArg) {
118748
+ const lastProvidedArg = args.at(-1);
118749
+
118750
+ if (lastProvidedArg) {
118751
+ // Options slot missing, all prior args provided → append options
118752
+ return fixer.insertTextAfter(
118753
+ getLastArgumentToken(
118754
+ lastProvidedArg,
118755
+ throwExpression,
118756
+ ),
118757
+ `, { cause: ${caughtError.name} }`,
118758
+ );
118759
+ }
118324
118760
 
118325
- if (!messageArg) {
118326
- // Case: `throw new Error()` → insert both message and options
118761
+ // argumentPosition: 1 and no args → insert `{ cause: err }` inside parens
118327
118762
  const lastToken =
118328
118763
  sourceCode.getLastToken(
118329
118764
  throwExpression,
@@ -118342,34 +118777,16 @@ function requirePreserveCaughtError () {
118342
118777
  if (parenToken) {
118343
118778
  return fixer.insertTextAfter(
118344
118779
  parenToken,
118345
- `"", { cause: ${caughtError.name} }`,
118780
+ `{ cause: ${caughtError.name} }`,
118346
118781
  );
118347
118782
  }
118348
118783
  return fixer.insertTextAfter(
118349
118784
  throwExpression.callee,
118350
- `("", { cause: ${caughtError.name} })`,
118351
- );
118352
- }
118353
- if (!optionsArg) {
118354
- // Case: `throw new Error("Some message")` → insert only options
118355
- return fixer.insertTextAfter(
118356
- messageArg,
118357
- `, { cause: ${caughtError.name} }`,
118785
+ `({ cause: ${caughtError.name} })`,
118358
118786
  );
118359
118787
  }
118360
118788
 
118361
- if (
118362
- optionsArg.type ===
118363
- "ObjectExpression"
118364
- ) {
118365
- return insertCauseIntoOptions(
118366
- fixer,
118367
- optionsArg,
118368
- caughtError.name,
118369
- );
118370
- }
118371
-
118372
- return null; // Identifier or spread — do not fix
118789
+ return fixExistingOptions(optionsArg);
118373
118790
  },
118374
118791
  },
118375
118792
  ],
@@ -118382,12 +118799,10 @@ function requirePreserveCaughtError () {
118382
118799
  const { value: thrownErrorCause } = errorCauseInfo;
118383
118800
 
118384
118801
  // If there is an attached cause, verify that it matches the caught error
118385
- if (
118386
- !(
118387
- thrownErrorCause.type === "Identifier" &&
118388
- thrownErrorCause.name === caughtError.name
118389
- )
118390
- ) {
118802
+ if (!(
118803
+ thrownErrorCause.type === "Identifier" &&
118804
+ thrownErrorCause.name === caughtError.name
118805
+ )) {
118391
118806
  const suggest = errorCauseInfo.multipleDefinitions
118392
118807
  ? null // If there are multiple `cause` definitions, a suggestion could be confusing.
118393
118808
  : [
@@ -119334,21 +119749,6 @@ function requireRadix () {
119334
119749
  return variable.defs.length >= 1;
119335
119750
  }
119336
119751
 
119337
- /**
119338
- * Checks whether a given node is a MemberExpression of `parseInt` method or not.
119339
- * @param {ASTNode} node A node to check.
119340
- * @returns {boolean} `true` if the node is a MemberExpression of `parseInt`
119341
- * method.
119342
- */
119343
- function isParseIntMethod(node) {
119344
- return (
119345
- node.type === "MemberExpression" &&
119346
- !node.computed &&
119347
- node.property.type === "Identifier" &&
119348
- node.property.name === "parseInt"
119349
- );
119350
- }
119351
-
119352
119752
  /**
119353
119753
  * Checks whether a given node is a valid value of radix or not.
119354
119754
  *
@@ -119357,12 +119757,29 @@ function requireRadix () {
119357
119757
  * - A literal except integers between 2 and 36.
119358
119758
  * - undefined.
119359
119759
  * @param {ASTNode} radix A node of radix to check.
119760
+ * @param {SourceCode} sourceCode The source code object.
119360
119761
  * @returns {boolean} `true` if the node is valid.
119361
119762
  */
119362
- function isValidRadix(radix) {
119763
+ function isValidRadix(radix, sourceCode) {
119764
+ if (
119765
+ radix.type === "UnaryExpression" &&
119766
+ (radix.operator === "-" || radix.operator === "+") &&
119767
+ radix.argument.type === "Literal" &&
119768
+ typeof radix.argument.value === "number"
119769
+ ) {
119770
+ const value =
119771
+ radix.operator === "-"
119772
+ ? -radix.argument.value
119773
+ : radix.argument.value;
119774
+
119775
+ return validRadixValues.has(value);
119776
+ }
119777
+
119363
119778
  return !(
119364
119779
  (radix.type === "Literal" && !validRadixValues.has(radix.value)) ||
119365
- (radix.type === "Identifier" && radix.name === "undefined")
119780
+ (radix.type === "Identifier" &&
119781
+ radix.name === "undefined" &&
119782
+ sourceCode.isGlobalReference(radix))
119366
119783
  );
119367
119784
  }
119368
119785
 
@@ -119412,49 +119829,47 @@ function requireRadix () {
119412
119829
  */
119413
119830
  function checkArguments(node) {
119414
119831
  const args = node.arguments;
119832
+ const spreadIndex = args.findIndex(
119833
+ arg => arg.type === "SpreadElement",
119834
+ );
119415
119835
 
119416
- switch (args.length) {
119417
- case 0:
119418
- context.report({
119419
- node,
119420
- messageId: "missingParameters",
119421
- });
119422
- break;
119836
+ if (spreadIndex !== -1 && spreadIndex < 2) {
119837
+ return;
119838
+ }
119423
119839
 
119424
- case 1:
119425
- context.report({
119426
- node,
119427
- messageId: "missingRadix",
119428
- suggest: [
119429
- {
119430
- messageId: "addRadixParameter10",
119431
- fix(fixer) {
119432
- const tokens = sourceCode.getTokens(node);
119433
- const lastToken = tokens.at(-1); // Parenthesis.
119434
- const secondToLastToken = tokens.at(-2); // May or may not be a comma.
119435
- const hasTrailingComma =
119436
- secondToLastToken.type ===
119437
- "Punctuator" &&
119438
- secondToLastToken.value === ",";
119439
-
119440
- return fixer.insertTextBefore(
119441
- lastToken,
119442
- hasTrailingComma ? " 10," : ", 10",
119443
- );
119444
- },
119445
- },
119446
- ],
119447
- });
119448
- break;
119840
+ if (args.length === 0) {
119841
+ context.report({
119842
+ node,
119843
+ messageId: "missingParameters",
119844
+ });
119845
+ } else if (args.length === 1) {
119846
+ context.report({
119847
+ node,
119848
+ messageId: "missingRadix",
119849
+ suggest: [
119850
+ {
119851
+ messageId: "addRadixParameter10",
119852
+ fix(fixer) {
119853
+ const lastToken = sourceCode.getLastToken(node);
119854
+ const prevToken =
119855
+ sourceCode.getTokenBefore(lastToken);
119449
119856
 
119450
- default:
119451
- if (!isValidRadix(args[1])) {
119452
- context.report({
119453
- node,
119454
- messageId: "invalidRadix",
119455
- });
119456
- }
119457
- break;
119857
+ const hasTrailingComma =
119858
+ astUtils.isCommaToken(prevToken);
119859
+
119860
+ return fixer.insertTextBefore(
119861
+ lastToken,
119862
+ hasTrailingComma ? " 10," : ", 10",
119863
+ );
119864
+ },
119865
+ },
119866
+ ],
119867
+ });
119868
+ } else if (!isValidRadix(args[1], sourceCode)) {
119869
+ context.report({
119870
+ node,
119871
+ messageId: "invalidRadix",
119872
+ });
119458
119873
  }
119459
119874
  }
119460
119875
 
@@ -119486,7 +119901,11 @@ function requireRadix () {
119486
119901
  : parentNode;
119487
119902
 
119488
119903
  if (
119489
- isParseIntMethod(parentNode) &&
119904
+ astUtils.isSpecificMemberAccess(
119905
+ parentNode,
119906
+ "Number",
119907
+ "parseInt",
119908
+ ) &&
119490
119909
  astUtils.isCallee(maybeCallee)
119491
119910
  ) {
119492
119911
  checkArguments(maybeCallee.parent);
@@ -125421,9 +125840,10 @@ function requireUseIsnan () {
125421
125840
  /**
125422
125841
  * Determines if the given node is a NaN `Identifier` node.
125423
125842
  * @param {ASTNode|null} node The node to check.
125424
- * @returns {boolean} `true` if the node is 'NaN' identifier.
125843
+ * @param {SourceCode} sourceCode The source code object.
125844
+ * @returns {boolean} `true` if the node is a global 'NaN' identifier.
125425
125845
  */
125426
- function isNaNIdentifier(node) {
125846
+ function isNaNIdentifier(node, sourceCode) {
125427
125847
  if (!node) {
125428
125848
  return false;
125429
125849
  }
@@ -125431,10 +125851,16 @@ function requireUseIsnan () {
125431
125851
  const nodeToCheck =
125432
125852
  node.type === "SequenceExpression" ? node.expressions.at(-1) : node;
125433
125853
 
125434
- return (
125435
- astUtils.isSpecificId(nodeToCheck, "NaN") ||
125436
- astUtils.isSpecificMemberAccess(nodeToCheck, "Number", "NaN")
125437
- );
125854
+ if (astUtils.isSpecificId(nodeToCheck, "NaN")) {
125855
+ return sourceCode.isGlobalReference(nodeToCheck);
125856
+ }
125857
+
125858
+ if (astUtils.isSpecificMemberAccess(nodeToCheck, "Number", "NaN")) {
125859
+ const objectNode = astUtils.skipChainExpression(nodeToCheck).object;
125860
+ return sourceCode.isGlobalReference(objectNode);
125861
+ }
125862
+
125863
+ return false;
125438
125864
  }
125439
125865
 
125440
125866
  //------------------------------------------------------------------------------
@@ -125506,7 +125932,7 @@ function requireUseIsnan () {
125506
125932
  */
125507
125933
  function getBinaryExpressionFixer(node, wrapValue) {
125508
125934
  return fixer => {
125509
- const comparedValue = isNaNIdentifier(node.left)
125935
+ const comparedValue = isNaNIdentifier(node.left, sourceCode)
125510
125936
  ? node.right
125511
125937
  : node.left;
125512
125938
  const shouldWrap = comparedValue.type === "SequenceExpression";
@@ -125533,10 +125959,11 @@ function requireUseIsnan () {
125533
125959
  function checkBinaryExpression(node) {
125534
125960
  if (
125535
125961
  /^(?:[<>]|[!=]=)=?$/u.test(node.operator) &&
125536
- (isNaNIdentifier(node.left) || isNaNIdentifier(node.right))
125962
+ (isNaNIdentifier(node.left, sourceCode) ||
125963
+ isNaNIdentifier(node.right, sourceCode))
125537
125964
  ) {
125538
125965
  const suggestedFixes = [];
125539
- const NaNNode = isNaNIdentifier(node.left)
125966
+ const NaNNode = isNaNIdentifier(node.left, sourceCode)
125540
125967
  ? node.left
125541
125968
  : node.right;
125542
125969
 
@@ -125581,12 +126008,12 @@ function requireUseIsnan () {
125581
126008
  * @returns {void}
125582
126009
  */
125583
126010
  function checkSwitchStatement(node) {
125584
- if (isNaNIdentifier(node.discriminant)) {
126011
+ if (isNaNIdentifier(node.discriminant, sourceCode)) {
125585
126012
  context.report({ node, messageId: "switchNaN" });
125586
126013
  }
125587
126014
 
125588
126015
  for (const switchCase of node.cases) {
125589
- if (isNaNIdentifier(switchCase.test)) {
126016
+ if (isNaNIdentifier(switchCase.test, sourceCode)) {
125590
126017
  context.report({ node: switchCase, messageId: "caseNaN" });
125591
126018
  }
125592
126019
  }
@@ -125607,7 +126034,7 @@ function requireUseIsnan () {
125607
126034
  (methodName === "indexOf" ||
125608
126035
  methodName === "lastIndexOf") &&
125609
126036
  node.arguments.length <= 2 &&
125610
- isNaNIdentifier(node.arguments[0])
126037
+ isNaNIdentifier(node.arguments[0], sourceCode)
125611
126038
  ) {
125612
126039
  /*
125613
126040
  * To retain side effects, it's essential to address `NaN` beforehand, which