oxlint-plugin-react-doctor 0.7.3-dev.f91ede7 → 0.7.3

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 (3) hide show
  1. package/dist/index.d.ts +0 -138
  2. package/dist/index.js +162 -1097
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -791,7 +791,7 @@ const findProgramRoot = (node) => {
791
791
  };
792
792
  //#endregion
793
793
  //#region src/plugin/utils/get-imported-name.ts
794
- const getImportedName = (importSpecifier) => {
794
+ const getImportedName$1 = (importSpecifier) => {
795
795
  if (!isNodeOfType(importSpecifier, "ImportSpecifier")) return void 0;
796
796
  const imported = importSpecifier.imported;
797
797
  if (isNodeOfType(imported, "Identifier")) return imported.name;
@@ -913,7 +913,7 @@ const activityWrapsEffectHeavySubtree = defineRule({
913
913
  continue;
914
914
  }
915
915
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
916
- const importedName = getImportedName(specifier);
916
+ const importedName = getImportedName$1(specifier);
917
917
  if (!importedName || !ACTIVITY_IMPORTED_NAMES.has(importedName)) continue;
918
918
  if (isNodeOfType(specifier.local, "Identifier")) localActivityNames.add(specifier.local.name);
919
919
  }
@@ -2215,7 +2215,7 @@ const anchorAmbiguousText = defineRule({
2215
2215
  });
2216
2216
  //#endregion
2217
2217
  //#region src/plugin/rules/a11y/anchor-has-content.ts
2218
- const MESSAGE$61 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2218
+ const MESSAGE$60 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
2219
2219
  const isTransComponentsTemplate = (node) => {
2220
2220
  let current = node.parent;
2221
2221
  while (current) {
@@ -2251,7 +2251,7 @@ const anchorHasContent = defineRule({
2251
2251
  if (isTransComponentsTemplate(node)) return;
2252
2252
  context.report({
2253
2253
  node: opening.name,
2254
- message: MESSAGE$61
2254
+ message: MESSAGE$60
2255
2255
  });
2256
2256
  } };
2257
2257
  }
@@ -2682,7 +2682,7 @@ const parseJsxValue = (value) => {
2682
2682
  };
2683
2683
  //#endregion
2684
2684
  //#region src/plugin/rules/a11y/aria-activedescendant-has-tabindex.ts
2685
- const MESSAGE$60 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2685
+ const MESSAGE$59 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
2686
2686
  const mayBeContentEditable = (node) => {
2687
2687
  const attribute = hasJsxPropIgnoreCase(node.attributes, "contenteditable");
2688
2688
  if (!attribute) return false;
@@ -2713,7 +2713,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2713
2713
  if (tabIndexValue === null || tabIndexValue >= -1) return;
2714
2714
  context.report({
2715
2715
  node: node.name,
2716
- message: MESSAGE$60
2716
+ message: MESSAGE$59
2717
2717
  });
2718
2718
  return;
2719
2719
  }
@@ -2721,7 +2721,7 @@ const ariaActivedescendantHasTabindex = defineRule({
2721
2721
  if (mayBeContentEditable(node)) return;
2722
2722
  context.report({
2723
2723
  node: node.name,
2724
- message: MESSAGE$60
2724
+ message: MESSAGE$59
2725
2725
  });
2726
2726
  } })
2727
2727
  });
@@ -5139,7 +5139,7 @@ const asyncParallel = defineRule({
5139
5139
  });
5140
5140
  //#endregion
5141
5141
  //#region src/plugin/rules/security/auth-token-in-web-storage.ts
5142
- const MESSAGE$59 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
5142
+ const MESSAGE$58 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
5143
5143
  const STORAGE_NAMES = new Set(["localStorage", "sessionStorage"]);
5144
5144
  const STORAGE_GLOBALS = new Set([
5145
5145
  "window",
@@ -5200,7 +5200,7 @@ const authTokenInWebStorage = defineRule({
5200
5200
  if (keyString === null || !isAuthCredentialKey(keyString)) return;
5201
5201
  context.report({
5202
5202
  node,
5203
- message: MESSAGE$59
5203
+ message: MESSAGE$58
5204
5204
  });
5205
5205
  },
5206
5206
  AssignmentExpression(node) {
@@ -5211,7 +5211,7 @@ const authTokenInWebStorage = defineRule({
5211
5211
  if (!propertyName || !isAuthCredentialKey(propertyName)) return;
5212
5212
  context.report({
5213
5213
  node: target,
5214
- message: MESSAGE$59
5214
+ message: MESSAGE$58
5215
5215
  });
5216
5216
  }
5217
5217
  }))
@@ -5350,7 +5350,7 @@ const CI_INSTALL_NEAR_SECRET_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b
5350
5350
  const INSTALL_COMMAND_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b/i;
5351
5351
  const SECRET_REFERENCE_PATTERN = /\bsecrets\.[A-Z0-9_]+/;
5352
5352
  const IGNORE_SCRIPTS_FLAG_PATTERN = /--ignore-scripts\b/;
5353
- const MESSAGE$58 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5353
+ const MESSAGE$57 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
5354
5354
  const isWorkflowPath = (relativePath) => /(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/i.test(relativePath);
5355
5355
  const scanWorkflowContent = (content) => {
5356
5356
  const lines = content.split("\n");
@@ -5395,7 +5395,7 @@ const scanWorkflowContent = (content) => {
5395
5395
  const installLineOffset = Math.max(step.lines.findIndex((stepLine) => INSTALL_COMMAND_PATTERN.test(stepLine)), 0);
5396
5396
  const installColumnIndex = step.lines[installLineOffset].search(INSTALL_COMMAND_PATTERN);
5397
5397
  return [{
5398
- message: MESSAGE$58,
5398
+ message: MESSAGE$57,
5399
5399
  line: step.startLineIndex + installLineOffset + 1,
5400
5400
  column: (installColumnIndex === -1 ? 0 : installColumnIndex) + 1
5401
5401
  }];
@@ -5405,7 +5405,7 @@ const scanWorkflowContent = (content) => {
5405
5405
  const scanNonWorkflowConfig = scanByPattern({
5406
5406
  shouldScan: (file) => isConfigOrCiPath(file.relativePath) && !file.relativePath.endsWith("package.json") && !isWorkflowPath(file.relativePath),
5407
5407
  pattern: CI_INSTALL_NEAR_SECRET_PATTERN,
5408
- message: MESSAGE$58
5408
+ message: MESSAGE$57
5409
5409
  });
5410
5410
  const scan = (file) => {
5411
5411
  if (isWorkflowPath(file.relativePath)) return scanWorkflowContent(file.content);
@@ -5945,7 +5945,7 @@ const isPureEventBlockerHandler = (attribute) => {
5945
5945
  };
5946
5946
  //#endregion
5947
5947
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
5948
- const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
5948
+ const MESSAGE$56 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
5949
5949
  const KEY_HANDLERS = [
5950
5950
  "onKeyUp",
5951
5951
  "onKeyDown",
@@ -6114,7 +6114,7 @@ const clickEventsHaveKeyEvents = defineRule({
6114
6114
  if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler))) return;
6115
6115
  context.report({
6116
6116
  node: node.name,
6117
- message: MESSAGE$57
6117
+ message: MESSAGE$56
6118
6118
  });
6119
6119
  } };
6120
6120
  }
@@ -6343,7 +6343,7 @@ const getClassNameLiteral = (classAttribute) => {
6343
6343
  };
6344
6344
  //#endregion
6345
6345
  //#region src/plugin/rules/a11y/control-has-associated-label.ts
6346
- const MESSAGE$56 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
6346
+ const MESSAGE$55 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
6347
6347
  const NON_OPERABLE_ELEMENTS = new Set([
6348
6348
  "td",
6349
6349
  "th",
@@ -6781,7 +6781,7 @@ const controlHasAssociatedLabel = defineRule({
6781
6781
  if (candidate.enclosingBindingName !== null && labelEmbeddedNames.has(candidate.enclosingBindingName)) continue;
6782
6782
  context.report({
6783
6783
  node: candidate.opening,
6784
- message: MESSAGE$56
6784
+ message: MESSAGE$55
6785
6785
  });
6786
6786
  }
6787
6787
  }
@@ -7288,7 +7288,7 @@ const noVagueButtonLabel = defineRule({
7288
7288
  });
7289
7289
  //#endregion
7290
7290
  //#region src/plugin/rules/a11y/dialog-has-accessible-name.ts
7291
- const MESSAGE$55 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
7291
+ const MESSAGE$54 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
7292
7292
  const DIALOG_ROLES = new Set(["dialog", "alertdialog"]);
7293
7293
  const NAME_PROVIDING_ATTRIBUTES = [
7294
7294
  "aria-label",
@@ -7313,7 +7313,7 @@ const dialogHasAccessibleName = defineRule({
7313
7313
  if (NAME_PROVIDING_ATTRIBUTES.some((attribute) => hasJsxPropIgnoreCase(node.attributes, attribute))) return;
7314
7314
  context.report({
7315
7315
  node: node.name,
7316
- message: MESSAGE$55
7316
+ message: MESSAGE$54
7317
7317
  });
7318
7318
  } };
7319
7319
  }
@@ -7353,7 +7353,7 @@ const isEs6Component = (node) => {
7353
7353
  };
7354
7354
  //#endregion
7355
7355
  //#region src/plugin/rules/react-builtins/display-name.ts
7356
- const MESSAGE$54 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
7356
+ const MESSAGE$53 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
7357
7357
  const DEFAULT_ADDITIONAL_HOCS = [
7358
7358
  "observer",
7359
7359
  "lazy",
@@ -7546,7 +7546,7 @@ const displayName = defineRule({
7546
7546
  const reportAt = (node) => {
7547
7547
  context.report({
7548
7548
  node,
7549
- message: MESSAGE$54
7549
+ message: MESSAGE$53
7550
7550
  });
7551
7551
  };
7552
7552
  return {
@@ -7662,744 +7662,6 @@ const displayName = defineRule({
7662
7662
  }
7663
7663
  });
7664
7664
  //#endregion
7665
- //#region src/plugin/utils/get-static-property-key-name.ts
7666
- const getStaticPropertyKeyName = (node, options = {}) => {
7667
- if (!isNodeOfType(node, "Property")) return null;
7668
- if (node.computed) {
7669
- if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
7670
- return null;
7671
- }
7672
- if (isNodeOfType(node.key, "Identifier")) return node.key.name;
7673
- if (isNodeOfType(node.key, "Literal")) {
7674
- if (typeof node.key.value === "string") return node.key.value;
7675
- if (options.stringifyNonStringLiterals) return String(node.key.value);
7676
- }
7677
- return null;
7678
- };
7679
- //#endregion
7680
- //#region src/plugin/utils/read-static-boolean.ts
7681
- const readStaticBoolean = (node) => {
7682
- if (!node) return null;
7683
- const unwrappedNode = stripParenExpression(node);
7684
- if (!isNodeOfType(unwrappedNode, "Literal") || typeof unwrappedNode.value !== "boolean") return null;
7685
- return unwrappedNode.value;
7686
- };
7687
- //#endregion
7688
- //#region src/plugin/utils/is-react-api-call.ts
7689
- const includesApiName = (apiNames, apiName) => typeof apiNames === "string" ? apiNames === apiName : apiNames.has(apiName);
7690
- const isImportedFromReact = (symbol) => {
7691
- if (symbol.kind !== "import") return false;
7692
- const importDeclaration = symbol.declarationNode.parent;
7693
- return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react");
7694
- };
7695
- const isNamedReactApiImport = (identifier, apiNames, scopes) => {
7696
- if (!isNodeOfType(identifier, "Identifier")) return false;
7697
- const symbol = scopes.symbolFor(identifier);
7698
- if (!symbol || !isImportedFromReact(symbol)) return false;
7699
- const importedName = getImportedName(symbol.declarationNode);
7700
- return Boolean(importedName && includesApiName(apiNames, importedName));
7701
- };
7702
- const isReactNamespaceImport = (identifier, scopes) => {
7703
- if (!isNodeOfType(identifier, "Identifier")) return false;
7704
- const symbol = scopes.symbolFor(identifier);
7705
- if (!symbol || !isImportedFromReact(symbol)) return false;
7706
- return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier");
7707
- };
7708
- const isReactApiCall = (node, apiNames, scopes, options = {}) => {
7709
- if (!isNodeOfType(node, "CallExpression")) return false;
7710
- const callee = stripParenExpression(node.callee);
7711
- if (isNodeOfType(callee, "Identifier")) {
7712
- if (isNamedReactApiImport(callee, apiNames, scopes)) return true;
7713
- return Boolean(options.allowUnboundBareCalls && includesApiName(apiNames, callee.name) && scopes.isGlobalReference(callee));
7714
- }
7715
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.object, "Identifier") || !isNodeOfType(callee.property, "Identifier") || !includesApiName(apiNames, callee.property.name)) return false;
7716
- if (isReactNamespaceImport(callee.object, scopes)) return true;
7717
- return Boolean(options.allowGlobalReactNamespace && callee.object.name === "React" && scopes.isGlobalReference(callee.object));
7718
- };
7719
- //#endregion
7720
- //#region src/plugin/rules/state-and-effects/utils/build-listener-cleanup-mismatch-message.ts
7721
- const buildListenerCleanupMismatchMessage = (eventName, registrationCapture, removalCapture, callbackComparison) => {
7722
- const hasCallbackMismatch = callbackComparison === "different";
7723
- if (hasCallbackMismatch && registrationCapture !== removalCapture) return `The cleanup removes \`${eventName}\` with a different callback binding and capture ${String(removalCapture)}, but it was registered with capture ${String(registrationCapture)}. Pass the same callback binding and capture flag to both EventTarget calls.`;
7724
- if (hasCallbackMismatch) return `The cleanup removes \`${eventName}\` with a different callback binding than the one registered, so \`removeEventListener\` cannot detach that listener. Pass the same callback binding to both calls.`;
7725
- return `The cleanup removes \`${eventName}\` with capture ${String(removalCapture)}, but it was registered with capture ${String(registrationCapture)}. \`removeEventListener\` must use the same capture flag as \`addEventListener\`.`;
7726
- };
7727
- //#endregion
7728
- //#region src/plugin/rules/state-and-effects/utils/callback-may-register-event-listener.ts
7729
- const callbackMayRegisterEventListener = (callbackNode) => {
7730
- let foundCall = false;
7731
- walkAst(callbackNode, (child) => {
7732
- if (isNodeOfType(child, "CallExpression")) {
7733
- foundCall = true;
7734
- return false;
7735
- }
7736
- });
7737
- return foundCall;
7738
- };
7739
- //#endregion
7740
- //#region src/plugin/rules/state-and-effects/utils/compare-listener-callback-identities.ts
7741
- const compareListenerCallbackIdentities = (registrationIdentity, removalIdentity) => {
7742
- if (registrationIdentity.node === removalIdentity.node) return "same";
7743
- if (registrationIdentity.isConcreteFunction && removalIdentity.isConcreteFunction) return "different";
7744
- return "unknown";
7745
- };
7746
- //#endregion
7747
- //#region src/plugin/rules/state-and-effects/utils/does-listener-analysis-abort-controller.ts
7748
- const doesListenerAnalysisAbortController = (analysis, controllerSymbolId) => analysis.abortedControllerSymbolIds.has(controllerSymbolId) || analysis.exhaustiveBranches.some(({ alternate, consequent }) => doesListenerAnalysisAbortController(alternate, controllerSymbolId) && doesListenerAnalysisAbortController(consequent, controllerSymbolId));
7749
- //#endregion
7750
- //#region src/plugin/rules/state-and-effects/utils/compare-listener-target-keys.ts
7751
- const compareListenerTargetKeys = (firstTargetKey, secondTargetKey) => {
7752
- if (firstTargetKey === secondTargetKey) return "same";
7753
- if (firstTargetKey.startsWith("global:") && secondTargetKey.startsWith("global:")) return "different";
7754
- if (firstTargetKey.startsWith("fresh:") || secondTargetKey.startsWith("fresh:")) return "different";
7755
- return "unknown";
7756
- };
7757
- //#endregion
7758
- //#region src/plugin/rules/state-and-effects/utils/does-listener-analysis-cancel-registration.ts
7759
- const cancellationResult = (analysis, registration) => {
7760
- if (registration.abortControllerSymbolId !== null && doesListenerAnalysisAbortController(analysis, registration.abortControllerSymbolId)) return "cancelled";
7761
- let result = analysis.hasUnknownRemovalCall || registration.abortControllerSymbolId !== null && analysis.hasUnknownAbortCall ? "unknown" : "not-cancelled";
7762
- for (const removal of analysis.removals) {
7763
- if (removal.eventName !== registration.eventName || removal.capture !== registration.capture || removal.callbackIdentity === null || compareListenerCallbackIdentities(registration.callbackIdentity, removal.callbackIdentity) !== "same") continue;
7764
- const targetComparison = compareListenerTargetKeys(registration.targetKey, removal.targetKey);
7765
- if (targetComparison === "same") return "cancelled";
7766
- if (targetComparison === "unknown") result = "unknown";
7767
- }
7768
- for (const { alternate, consequent } of analysis.exhaustiveBranches) {
7769
- const alternateResult = cancellationResult(alternate, registration);
7770
- const consequentResult = cancellationResult(consequent, registration);
7771
- if (alternateResult === "cancelled" && consequentResult === "cancelled") return "cancelled";
7772
- if (alternateResult !== "not-cancelled" && consequentResult !== "not-cancelled") result = "unknown";
7773
- }
7774
- return result;
7775
- };
7776
- const doesListenerAnalysisCancelRegistration = (analysis, registration) => cancellationResult(analysis, registration) !== "not-cancelled";
7777
- //#endregion
7778
- //#region src/plugin/rules/state-and-effects/utils/static-member-property-name.ts
7779
- const getStaticMemberPropertyName = (node) => {
7780
- if (!node) return null;
7781
- const unwrappedNode = stripParenExpression(node);
7782
- if (!isNodeOfType(unwrappedNode, "MemberExpression")) return null;
7783
- if (!unwrappedNode.computed && isNodeOfType(unwrappedNode.property, "Identifier")) return unwrappedNode.property.name;
7784
- if (unwrappedNode.computed && isNodeOfType(unwrappedNode.property, "Literal") && typeof unwrappedNode.property.value === "string") return unwrappedNode.property.value;
7785
- return null;
7786
- };
7787
- //#endregion
7788
- //#region src/plugin/rules/state-and-effects/utils/is-listener-path-ambiguous.ts
7789
- const PATH_AMBIGUOUS_ANCESTOR_TYPES = new Set([
7790
- "CatchClause",
7791
- "ConditionalExpression",
7792
- "DoWhileStatement",
7793
- "ForInStatement",
7794
- "ForOfStatement",
7795
- "ForStatement",
7796
- "IfStatement",
7797
- "LogicalExpression",
7798
- "SwitchCase",
7799
- "SwitchStatement",
7800
- "TryStatement",
7801
- "WhileStatement"
7802
- ]);
7803
- const isGuaranteedChild = (parentNode, childNode) => {
7804
- if (isNodeOfType(parentNode, "IfStatement")) {
7805
- if (parentNode.test === childNode) return true;
7806
- const staticTestValue = readStaticBoolean(parentNode.test);
7807
- return parentNode.consequent === childNode && staticTestValue === true || parentNode.alternate === childNode && staticTestValue === false;
7808
- }
7809
- if (isNodeOfType(parentNode, "ConditionalExpression")) {
7810
- if (parentNode.test === childNode) return true;
7811
- const staticTestValue = readStaticBoolean(parentNode.test);
7812
- return parentNode.consequent === childNode && staticTestValue === true || parentNode.alternate === childNode && staticTestValue === false;
7813
- }
7814
- if (isNodeOfType(parentNode, "LogicalExpression")) {
7815
- if (parentNode.left === childNode) return true;
7816
- if (parentNode.right !== childNode) return false;
7817
- const staticLeftValue = readStaticBoolean(parentNode.left);
7818
- return parentNode.operator === "&&" && staticLeftValue === true || parentNode.operator === "||" && staticLeftValue === false;
7819
- }
7820
- if (isNodeOfType(parentNode, "TryStatement")) return parentNode.finalizer === childNode;
7821
- if (isNodeOfType(parentNode, "SwitchStatement")) return parentNode.discriminant === childNode;
7822
- if (isNodeOfType(parentNode, "ForStatement")) return parentNode.init === childNode || parentNode.test === childNode;
7823
- if (isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return parentNode.right === childNode;
7824
- if (isNodeOfType(parentNode, "WhileStatement")) return parentNode.test === childNode;
7825
- return false;
7826
- };
7827
- const isListenerPathAmbiguous = (node, bodyNode, allowAmbiguousChild) => {
7828
- let currentNode = node;
7829
- while (currentNode && currentNode !== bodyNode) {
7830
- const parentNode = currentNode.parent;
7831
- if (!parentNode) return true;
7832
- if (PATH_AMBIGUOUS_ANCESTOR_TYPES.has(parentNode.type) && !isGuaranteedChild(parentNode, currentNode) && !allowAmbiguousChild?.(parentNode, currentNode)) return true;
7833
- currentNode = parentNode;
7834
- }
7835
- return false;
7836
- };
7837
- //#endregion
7838
- //#region src/plugin/rules/state-and-effects/utils/resolve-event-listener-capture.ts
7839
- const resolveEventListenerCapture = (optionsNode) => {
7840
- if (!optionsNode) return false;
7841
- const unwrappedOptions = stripParenExpression(optionsNode);
7842
- if (isNodeOfType(unwrappedOptions, "Literal")) return typeof unwrappedOptions.value === "boolean" ? unwrappedOptions.value : null;
7843
- if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) return null;
7844
- let capture = false;
7845
- for (const property of unwrappedOptions.properties) {
7846
- if (!isNodeOfType(property, "Property")) return null;
7847
- const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
7848
- if (propertyName === null) return null;
7849
- if (!property.computed && propertyName === "__proto__") return null;
7850
- if (propertyName !== "capture") continue;
7851
- const propertyValue = stripParenExpression(property.value);
7852
- if (!isNodeOfType(propertyValue, "Literal") || typeof propertyValue.value !== "boolean") return null;
7853
- capture = propertyValue.value;
7854
- }
7855
- return capture;
7856
- };
7857
- //#endregion
7858
- //#region src/plugin/rules/state-and-effects/utils/resolve-static-once-option.ts
7859
- const resolveStaticOnceOption = (optionsNode) => {
7860
- if (!optionsNode) return false;
7861
- const unwrappedOptions = stripParenExpression(optionsNode);
7862
- if (isNodeOfType(unwrappedOptions, "Literal")) return typeof unwrappedOptions.value === "boolean" ? false : null;
7863
- if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) return null;
7864
- let once = false;
7865
- for (const property of unwrappedOptions.properties) {
7866
- if (!isNodeOfType(property, "Property")) return null;
7867
- const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
7868
- if (propertyName === null || !property.computed && propertyName === "__proto__") return null;
7869
- if (propertyName !== "once") continue;
7870
- const propertyValue = stripParenExpression(property.value);
7871
- if (!isNodeOfType(propertyValue, "Literal") || typeof propertyValue.value !== "boolean") return null;
7872
- once = propertyValue.value;
7873
- }
7874
- return once;
7875
- };
7876
- //#endregion
7877
- //#region src/plugin/rules/state-and-effects/effect-listener-cleanup-mismatch.ts
7878
- const LISTENER_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
7879
- const hasOnlyReadReferences = (symbol) => symbol.references.every((reference) => reference.flag === "read");
7880
- const isStableSymbol = (symbol) => symbol.kind === "const" || symbol.kind === "import" || (symbol.kind === "function" || symbol.kind === "parameter") && hasOnlyReadReferences(symbol);
7881
- const isPlainConstSymbol = (symbol) => symbol.kind === "const" && isNodeOfType(symbol.declarationNode, "VariableDeclarator") && isNodeOfType(symbol.declarationNode.id, "Identifier") && symbol.declarationNode.id === symbol.bindingIdentifier;
7882
- const resolveAliasedSymbol = (identifier, context, visitedSymbolIds) => {
7883
- if (!isNodeOfType(identifier, "Identifier")) return null;
7884
- const symbol = context.scopes.symbolFor(identifier);
7885
- if (!symbol || !isStableSymbol(symbol) || visitedSymbolIds.has(symbol.id)) return null;
7886
- if (symbol.kind === "const" && !isPlainConstSymbol(symbol)) return null;
7887
- visitedSymbolIds.add(symbol.id);
7888
- const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
7889
- if (symbol.kind === "const" && isNodeOfType(initializer, "Identifier")) {
7890
- const resolvedAlias = resolveAliasedSymbol(initializer, context, visitedSymbolIds);
7891
- if (resolvedAlias) return resolvedAlias;
7892
- }
7893
- return symbol;
7894
- };
7895
- const resolveCallbackIdentity = (callbackNode, context) => {
7896
- if (!callbackNode) return null;
7897
- const unwrappedCallback = stripParenExpression(callbackNode);
7898
- if (isFunctionLike$1(unwrappedCallback)) return {
7899
- node: unwrappedCallback,
7900
- isConcreteFunction: true
7901
- };
7902
- if (!isNodeOfType(unwrappedCallback, "Identifier")) return null;
7903
- const symbol = resolveAliasedSymbol(unwrappedCallback, context, /* @__PURE__ */ new Set());
7904
- if (!symbol) return null;
7905
- const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
7906
- if (initializer && isFunctionLike$1(initializer)) return {
7907
- node: initializer,
7908
- isConcreteFunction: true
7909
- };
7910
- return {
7911
- node: symbol.bindingIdentifier,
7912
- isConcreteFunction: false
7913
- };
7914
- };
7915
- const resolveTargetKey = (targetNode, context) => {
7916
- const unwrappedTarget = stripParenExpression(targetNode);
7917
- if (isNodeOfType(unwrappedTarget, "Identifier")) {
7918
- const symbol = resolveAliasedSymbol(unwrappedTarget, context, /* @__PURE__ */ new Set());
7919
- if (symbol) {
7920
- if (symbol.kind === "import" || symbol.kind === "parameter" || symbol.kind === "function") return null;
7921
- const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
7922
- if (isNodeOfType(initializer, "NewExpression") && isNodeOfType(initializer.callee, "Identifier") && !context.scopes.isGlobalReference(initializer.callee)) return null;
7923
- if (isNodeOfType(initializer, "NewExpression")) return `fresh:${symbol.id}`;
7924
- return `symbol:${symbol.id}`;
7925
- }
7926
- if (context.scopes.isGlobalReference(unwrappedTarget)) return `global:${unwrappedTarget.name}`;
7927
- return null;
7928
- }
7929
- if (isNodeOfType(unwrappedTarget, "MemberExpression") && !unwrappedTarget.computed && isNodeOfType(unwrappedTarget.property, "Identifier")) {
7930
- const objectKey = resolveTargetKey(unwrappedTarget.object, context);
7931
- if (unwrappedTarget.property.name === "document" && (objectKey === "global:window" || objectKey === "global:globalThis")) return "global:document";
7932
- if (unwrappedTarget.property.name === "window" && (objectKey === "global:window" || objectKey === "global:globalThis")) return "global:window";
7933
- return objectKey === null ? null : `${objectKey}.${unwrappedTarget.property.name}`;
7934
- }
7935
- return null;
7936
- };
7937
- const resolveStaticEventName = (eventNode, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
7938
- if (!eventNode) return null;
7939
- const unwrappedEvent = stripParenExpression(eventNode);
7940
- if (isNodeOfType(unwrappedEvent, "Literal") && typeof unwrappedEvent.value === "string") return unwrappedEvent.value;
7941
- if (isNodeOfType(unwrappedEvent, "TemplateLiteral") && unwrappedEvent.expressions.length === 0) return unwrappedEvent.quasis[0]?.value.cooked ?? "";
7942
- if (!isNodeOfType(unwrappedEvent, "Identifier")) return null;
7943
- const symbol = context.scopes.symbolFor(unwrappedEvent);
7944
- if (!symbol || !isPlainConstSymbol(symbol) || !symbol.initializer || visitedSymbolIds.has(symbol.id)) return null;
7945
- visitedSymbolIds.add(symbol.id);
7946
- return resolveStaticEventName(symbol.initializer, context, visitedSymbolIds);
7947
- };
7948
- const readStaticEventDispatch = (node, context) => {
7949
- const targetNode = readDirectMemberReceiver(node.callee, "dispatchEvent", context);
7950
- const eventArgument = node.arguments?.[0];
7951
- if (!eventArgument) return null;
7952
- const eventNode = stripParenExpression(eventArgument);
7953
- if (!targetNode || !isNodeOfType(eventNode, "NewExpression") || !isNodeOfType(eventNode.callee, "Identifier") || eventNode.callee.name !== "Event" || !context.scopes.isGlobalReference(eventNode.callee)) return null;
7954
- const target = stripParenExpression(targetNode);
7955
- if (!isNodeOfType(target, "Identifier")) return null;
7956
- const targetSymbol = resolveAliasedSymbol(target, context, /* @__PURE__ */ new Set());
7957
- const targetInitializer = targetSymbol?.initializer ? stripParenExpression(targetSymbol.initializer) : null;
7958
- if (!isNodeOfType(targetInitializer, "NewExpression") || !isNodeOfType(targetInitializer.callee, "Identifier") || targetInitializer.callee.name !== "EventTarget" || !context.scopes.isGlobalReference(targetInitializer.callee)) return null;
7959
- const targetKey = resolveTargetKey(targetNode, context);
7960
- const eventName = resolveStaticEventName(eventNode.arguments?.[0], context);
7961
- return targetKey !== null && eventName !== null ? {
7962
- eventName,
7963
- targetKey
7964
- } : null;
7965
- };
7966
- const resolveLocalAbortControllerSymbolId = (controllerNode, context) => {
7967
- const unwrappedController = stripParenExpression(controllerNode);
7968
- if (!isNodeOfType(unwrappedController, "Identifier")) return null;
7969
- const controllerSymbol = resolveAliasedSymbol(unwrappedController, context, /* @__PURE__ */ new Set());
7970
- if (!controllerSymbol || controllerSymbol.kind !== "const" || !controllerSymbol.initializer) return null;
7971
- const initializer = stripParenExpression(controllerSymbol.initializer);
7972
- if (!isNodeOfType(initializer, "NewExpression") || !isNodeOfType(initializer.callee, "Identifier") || initializer.callee.name !== "AbortController" || !context.scopes.isGlobalReference(initializer.callee)) return null;
7973
- return controllerSymbol.id;
7974
- };
7975
- const readDirectMemberReceiver = (memberNode, memberName, context) => {
7976
- if (!memberNode) return null;
7977
- const unwrappedMember = stripParenExpression(memberNode);
7978
- if (!isNodeOfType(unwrappedMember, "MemberExpression")) return null;
7979
- if ((getStaticMemberPropertyName(unwrappedMember) ?? (context && unwrappedMember.computed ? resolveStaticEventName(unwrappedMember.property, context) : null)) !== memberName) return null;
7980
- return unwrappedMember.object;
7981
- };
7982
- const isSignalObjectPatternBinding = (patternNode, bindingIdentifier) => {
7983
- if (!isNodeOfType(patternNode, "ObjectPattern")) return false;
7984
- return patternNode.properties.some((property) => {
7985
- if (!isNodeOfType(property, "Property")) return false;
7986
- if (getStaticPropertyKeyName(property, { allowComputedString: true }) !== "signal") return false;
7987
- return (isNodeOfType(property.value, "AssignmentPattern") ? property.value.left : property.value) === bindingIdentifier;
7988
- });
7989
- };
7990
- const resolveSignalAbortControllerSymbolId = (signalNode, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
7991
- const unwrappedSignal = stripParenExpression(signalNode);
7992
- const directControllerNode = readDirectMemberReceiver(unwrappedSignal, "signal");
7993
- if (directControllerNode) return resolveLocalAbortControllerSymbolId(directControllerNode, context);
7994
- if (!isNodeOfType(unwrappedSignal, "Identifier")) return null;
7995
- const signalSymbol = context.scopes.symbolFor(unwrappedSignal);
7996
- if (!signalSymbol || signalSymbol.kind !== "const" || !signalSymbol.initializer || visitedSymbolIds.has(signalSymbol.id) || !hasOnlyReadReferences(signalSymbol)) return null;
7997
- visitedSymbolIds.add(signalSymbol.id);
7998
- const declarationNode = signalSymbol.declarationNode;
7999
- if (!isNodeOfType(declarationNode, "VariableDeclarator")) return null;
8000
- if (isNodeOfType(declarationNode.id, "Identifier") && declarationNode.id === signalSymbol.bindingIdentifier) {
8001
- const initializer = stripParenExpression(signalSymbol.initializer);
8002
- if (isNodeOfType(initializer, "Identifier")) return resolveSignalAbortControllerSymbolId(initializer, context, visitedSymbolIds);
8003
- const controllerNode = readDirectMemberReceiver(initializer, "signal");
8004
- return controllerNode ? resolveLocalAbortControllerSymbolId(controllerNode, context) : null;
8005
- }
8006
- if (!isSignalObjectPatternBinding(declarationNode.id, signalSymbol.bindingIdentifier)) return null;
8007
- return resolveLocalAbortControllerSymbolId(signalSymbol.initializer, context);
8008
- };
8009
- const readControllerAbortedCondition = (conditionNode, controllerSymbolId, context) => {
8010
- const unwrappedCondition = stripParenExpression(conditionNode);
8011
- if (isNodeOfType(unwrappedCondition, "UnaryExpression") && unwrappedCondition.operator === "!") {
8012
- const operandCondition = readControllerAbortedCondition(unwrappedCondition.argument, controllerSymbolId, context);
8013
- if (operandCondition === "aborted") return "not-aborted";
8014
- if (operandCondition === "not-aborted") return "aborted";
8015
- return null;
8016
- }
8017
- if (isNodeOfType(unwrappedCondition, "BinaryExpression")) {
8018
- const leftBoolean = readStaticBoolean(unwrappedCondition.left);
8019
- const rightBoolean = readStaticBoolean(unwrappedCondition.right);
8020
- const memberNode = leftBoolean === null ? unwrappedCondition.left : rightBoolean === null ? unwrappedCondition.right : null;
8021
- const comparedBoolean = leftBoolean ?? rightBoolean;
8022
- if (memberNode && comparedBoolean !== null) {
8023
- const memberCondition = readControllerAbortedCondition(memberNode, controllerSymbolId, context);
8024
- if (memberCondition) {
8025
- const isEquality = unwrappedCondition.operator === "==" || unwrappedCondition.operator === "===";
8026
- const isInequality = unwrappedCondition.operator === "!=" || unwrappedCondition.operator === "!==";
8027
- const shouldInvert = isEquality && !comparedBoolean || isInequality && comparedBoolean;
8028
- if (isEquality || isInequality) {
8029
- if (!shouldInvert) return memberCondition;
8030
- return memberCondition === "aborted" ? "not-aborted" : "aborted";
8031
- }
8032
- }
8033
- }
8034
- return null;
8035
- }
8036
- const signalNode = readDirectMemberReceiver(unwrappedCondition, "aborted");
8037
- if (!signalNode) return null;
8038
- return resolveSignalAbortControllerSymbolId(signalNode, context) === controllerSymbolId ? "aborted" : null;
8039
- };
8040
- const isAbortGuardForChild = (parentNode, childNode, controllerSymbolId, context) => {
8041
- if (isNodeOfType(parentNode, "IfStatement") || isNodeOfType(parentNode, "ConditionalExpression")) {
8042
- const condition = readControllerAbortedCondition(parentNode.test, controllerSymbolId, context);
8043
- return parentNode.consequent === childNode && condition === "not-aborted" || parentNode.alternate === childNode && condition === "aborted";
8044
- }
8045
- if (!isNodeOfType(parentNode, "LogicalExpression") || parentNode.right !== childNode) return false;
8046
- const condition = readControllerAbortedCondition(parentNode.left, controllerSymbolId, context);
8047
- return parentNode.operator === "&&" && condition === "not-aborted" || parentNode.operator === "||" && condition === "aborted";
8048
- };
8049
- const isAbortGuaranteedByPath = (node, bodyNode, controllerSymbolId, context) => !isListenerPathAmbiguous(node, bodyNode, (parentNode, childNode) => isAbortGuardForChild(parentNode, childNode, controllerSymbolId, context));
8050
- const resolveBoundAbortControllerSymbolId = (callNode, context) => {
8051
- const callee = stripParenExpression(callNode.callee);
8052
- if (!isNodeOfType(callee, "Identifier")) return null;
8053
- const boundAbortSymbol = resolveAliasedSymbol(callee, context, /* @__PURE__ */ new Set());
8054
- if (!boundAbortSymbol?.initializer) return null;
8055
- const initializer = stripParenExpression(boundAbortSymbol.initializer);
8056
- if (!isNodeOfType(initializer, "CallExpression")) return null;
8057
- const abortMethodNode = readDirectMemberReceiver(initializer.callee, "bind");
8058
- if (!abortMethodNode) return null;
8059
- const controllerNode = readDirectMemberReceiver(abortMethodNode, "abort");
8060
- const boundThisNode = initializer.arguments?.[0];
8061
- if (!controllerNode || !boundThisNode) return null;
8062
- const controllerSymbolId = resolveLocalAbortControllerSymbolId(controllerNode, context);
8063
- const boundThisSymbolId = resolveLocalAbortControllerSymbolId(boundThisNode, context);
8064
- return controllerSymbolId !== null && controllerSymbolId === boundThisSymbolId ? controllerSymbolId : null;
8065
- };
8066
- const resolveRegistrationCancellation = (optionsNode, context) => {
8067
- const noCancellation = {
8068
- abortControllerSymbolId: null,
8069
- hasUnknownCancellation: false
8070
- };
8071
- if (!optionsNode) return noCancellation;
8072
- const unwrappedOptions = stripParenExpression(optionsNode);
8073
- if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) return noCancellation;
8074
- let resolvedCancellation = null;
8075
- for (const property of unwrappedOptions.properties) {
8076
- if (!isNodeOfType(property, "Property")) return noCancellation;
8077
- if (getStaticPropertyKeyName(property, { allowComputedString: true }) !== "signal") continue;
8078
- if (resolvedCancellation) return {
8079
- abortControllerSymbolId: null,
8080
- hasUnknownCancellation: true
8081
- };
8082
- const abortControllerSymbolId = resolveSignalAbortControllerSymbolId(property.value, context);
8083
- resolvedCancellation = {
8084
- abortControllerSymbolId,
8085
- hasUnknownCancellation: abortControllerSymbolId === null
8086
- };
8087
- }
8088
- return resolvedCancellation ?? noCancellation;
8089
- };
8090
- const readListenerCandidate = (node, methodName, context) => {
8091
- const targetNode = readDirectMemberReceiver(node.callee, methodName, context);
8092
- if (!targetNode) return null;
8093
- const targetKey = resolveTargetKey(targetNode, context);
8094
- const eventName = resolveStaticEventName(node.arguments?.[0], context);
8095
- const callbackIdentity = resolveCallbackIdentity(node.arguments?.[1], context);
8096
- const capture = resolveEventListenerCapture(node.arguments?.[2]);
8097
- if (targetKey === null || eventName === null) return null;
8098
- return {
8099
- node,
8100
- targetKey,
8101
- eventName,
8102
- callbackIdentity,
8103
- capture
8104
- };
8105
- };
8106
- const readDestructuredRemovalCandidate = (node, context) => {
8107
- const methodNode = readDirectMemberReceiver(node.callee, "call");
8108
- if (!methodNode || !isNodeOfType(methodNode, "Identifier")) return null;
8109
- const methodSymbol = context.scopes.symbolFor(methodNode);
8110
- if (!methodSymbol || methodSymbol.kind !== "const" || !hasOnlyReadReferences(methodSymbol) || !isNodeOfType(methodSymbol.declarationNode, "VariableDeclarator")) return null;
8111
- const declaration = methodSymbol.declarationNode;
8112
- if (!isNodeOfType(declaration.id, "ObjectPattern") || !declaration.init) return null;
8113
- if (!declaration.id.properties.some((property) => {
8114
- if (!isNodeOfType(property, "Property")) return false;
8115
- const bindingNode = isNodeOfType(property.value, "AssignmentPattern") ? property.value.left : property.value;
8116
- return getStaticPropertyKeyName(property, { allowComputedString: true }) === "removeEventListener" && bindingNode === methodSymbol.bindingIdentifier;
8117
- })) return null;
8118
- const targetNode = node.arguments?.[0];
8119
- if (!targetNode) return null;
8120
- const declarationTargetKey = resolveTargetKey(declaration.init, context);
8121
- const targetKey = resolveTargetKey(targetNode, context);
8122
- if (declarationTargetKey === null || targetKey === null || declarationTargetKey !== targetKey) return null;
8123
- const eventName = resolveStaticEventName(node.arguments?.[1], context);
8124
- if (eventName === null) return null;
8125
- return {
8126
- node,
8127
- targetKey,
8128
- eventName,
8129
- callbackIdentity: resolveCallbackIdentity(node.arguments?.[2], context),
8130
- capture: resolveEventListenerCapture(node.arguments?.[3])
8131
- };
8132
- };
8133
- const resolveReturnedCleanupBody = (returnedValue, context) => {
8134
- if (!returnedValue) return null;
8135
- const unwrappedValue = stripParenExpression(returnedValue);
8136
- if (isFunctionLike$1(unwrappedValue)) return unwrappedValue.body;
8137
- if (!isNodeOfType(unwrappedValue, "Identifier")) return null;
8138
- const symbol = resolveAliasedSymbol(unwrappedValue, context, /* @__PURE__ */ new Set());
8139
- if (!symbol || !symbol.initializer) return null;
8140
- const initializer = stripParenExpression(symbol.initializer);
8141
- return isFunctionLike$1(initializer) ? initializer.body : null;
8142
- };
8143
- const collectEffectListenerInputs = (effectBody, context) => {
8144
- const registrations = [];
8145
- const cleanupBodies = [];
8146
- const listenerRegistrationCountsByTarget = /* @__PURE__ */ new Map();
8147
- let returnStatementCount = 0;
8148
- if (!isNodeOfType(effectBody, "BlockStatement")) return {
8149
- registrations,
8150
- cleanupBodies,
8151
- hasCanonicalCleanupReturn: false,
8152
- returnStatementCount
8153
- };
8154
- const finalEffectStatement = effectBody.body[effectBody.body.length - 1];
8155
- const hasCanonicalCleanupReturn = isNodeOfType(finalEffectStatement, "ReturnStatement");
8156
- walkAst(effectBody, (child) => {
8157
- if (child !== effectBody && isFunctionLike$1(child)) return false;
8158
- if (isNodeOfType(child, "ClassDeclaration") || isNodeOfType(child, "ClassExpression")) return false;
8159
- if (isNodeOfType(child, "CallExpression")) {
8160
- if (isListenerPathAmbiguous(child, effectBody)) return;
8161
- const registrationTarget = readDirectMemberReceiver(child.callee, "addEventListener", context);
8162
- const registrationTargetKey = registrationTarget ? resolveTargetKey(registrationTarget, context) : null;
8163
- if (registrationTargetKey !== null) listenerRegistrationCountsByTarget.set(registrationTargetKey, (listenerRegistrationCountsByTarget.get(registrationTargetKey) ?? 0) + 1);
8164
- const candidate = readListenerCandidate(child, "addEventListener", context);
8165
- if (candidate?.callbackIdentity && candidate.capture !== null) {
8166
- const registrationCancellation = resolveRegistrationCancellation(candidate.node.arguments?.[2], context);
8167
- registrations.push({
8168
- node: candidate.node,
8169
- targetKey: candidate.targetKey,
8170
- eventName: candidate.eventName,
8171
- callbackIdentity: candidate.callbackIdentity,
8172
- capture: candidate.capture,
8173
- once: resolveStaticOnceOption(candidate.node.arguments?.[2]) === true,
8174
- abortControllerSymbolId: registrationCancellation.abortControllerSymbolId,
8175
- hasUnknownCancellation: registrationCancellation.hasUnknownCancellation
8176
- });
8177
- }
8178
- const eventDispatch = readStaticEventDispatch(child, context);
8179
- if (eventDispatch) {
8180
- const dispatchedRegistrations = registrations.filter((registration) => registration.targetKey === eventDispatch.targetKey && registration.eventName === eventDispatch.eventName);
8181
- const dispatchedRegistration = dispatchedRegistrations[0];
8182
- if (dispatchedRegistrations.length === 1 && dispatchedRegistration?.once && listenerRegistrationCountsByTarget.get(eventDispatch.targetKey) === 1 && dispatchedRegistration.callbackIdentity.isConcreteFunction && !callbackMayRegisterEventListener(dispatchedRegistration.callbackIdentity.node)) registrations.splice(registrations.indexOf(dispatchedRegistration), 1);
8183
- }
8184
- return;
8185
- }
8186
- if (isNodeOfType(child, "ReturnStatement")) {
8187
- returnStatementCount += 1;
8188
- const cleanupBody = resolveReturnedCleanupBody(child.argument, context);
8189
- if (cleanupBody) cleanupBodies.push(cleanupBody);
8190
- }
8191
- });
8192
- return {
8193
- registrations,
8194
- cleanupBodies,
8195
- hasCanonicalCleanupReturn,
8196
- returnStatementCount
8197
- };
8198
- };
8199
- const resolveCalledCleanup = (callNode, context) => {
8200
- const callee = stripParenExpression(callNode.callee);
8201
- if (!isNodeOfType(callee, "Identifier")) return null;
8202
- const cleanupSymbol = resolveAliasedSymbol(callee, context, /* @__PURE__ */ new Set());
8203
- if (!cleanupSymbol?.initializer || cleanupSymbol.kind !== "const" && cleanupSymbol.kind !== "function") return null;
8204
- const initializer = stripParenExpression(cleanupSymbol.initializer);
8205
- if (!isFunctionLike$1(initializer) || initializer.async || initializer.generator) return null;
8206
- return {
8207
- body: initializer.body,
8208
- symbolId: cleanupSymbol.id
8209
- };
8210
- };
8211
- const analyzeCleanupBody = (cleanupBody, context, visitedCleanupSymbolIds = /* @__PURE__ */ new Set(), isLoopBody = false) => {
8212
- const removals = [];
8213
- const abortedControllerSymbolIds = /* @__PURE__ */ new Set();
8214
- const exhaustiveBranches = [];
8215
- let hasAmbiguousReachability = false;
8216
- let hasUnknownAbortCall = false;
8217
- let hasUnknownRemovalCall = false;
8218
- const finalCleanupStatement = isNodeOfType(cleanupBody, "BlockStatement") ? cleanupBody.body[cleanupBody.body.length - 1] : null;
8219
- const addCleanupAnalysis = (analysis) => {
8220
- removals.push(...analysis.removals);
8221
- hasUnknownAbortCall ||= analysis.hasUnknownAbortCall;
8222
- hasUnknownRemovalCall ||= analysis.hasUnknownRemovalCall;
8223
- for (const controllerSymbolId of analysis.abortedControllerSymbolIds) abortedControllerSymbolIds.add(controllerSymbolId);
8224
- exhaustiveBranches.push(...analysis.exhaustiveBranches);
8225
- };
8226
- const addGuaranteedLoopPrefix = (loopBody) => {
8227
- const loopStatements = isNodeOfType(loopBody, "BlockStatement") ? loopBody.body : [loopBody];
8228
- for (const loopStatement of loopStatements) {
8229
- if (isNodeOfType(loopStatement, "BreakStatement") || isNodeOfType(loopStatement, "ContinueStatement")) return true;
8230
- if (isNodeOfType(loopStatement, "BlockStatement")) {
8231
- if (addGuaranteedLoopPrefix(loopStatement)) return true;
8232
- continue;
8233
- }
8234
- if (isNodeOfType(loopStatement, "IfStatement")) {
8235
- const staticTestValue = readStaticBoolean(loopStatement.test);
8236
- if (staticTestValue !== null) {
8237
- const testAnalysis = analyzeCleanupBody(loopStatement.test, context, new Set(visitedCleanupSymbolIds), true);
8238
- if (!testAnalysis) return true;
8239
- addCleanupAnalysis(testAnalysis);
8240
- const guaranteedBranch = staticTestValue ? loopStatement.consequent : loopStatement.alternate;
8241
- if (guaranteedBranch && addGuaranteedLoopPrefix(guaranteedBranch)) return true;
8242
- continue;
8243
- }
8244
- }
8245
- const loopStatementAnalysis = analyzeCleanupBody(loopStatement, context, new Set(visitedCleanupSymbolIds), true);
8246
- if (!loopStatementAnalysis) return true;
8247
- addCleanupAnalysis(loopStatementAnalysis);
8248
- }
8249
- return false;
8250
- };
8251
- walkAst(cleanupBody, (child) => {
8252
- if (child !== cleanupBody && isFunctionLike$1(child)) return false;
8253
- if (isNodeOfType(child, "ClassDeclaration") || isNodeOfType(child, "ClassExpression")) return false;
8254
- if (isNodeOfType(child, "ReturnStatement") && child === finalCleanupStatement) return;
8255
- if (isNodeOfType(child, "ReturnStatement") || isNodeOfType(child, "ThrowStatement") || isLoopBody && (isNodeOfType(child, "BreakStatement") || isNodeOfType(child, "ContinueStatement"))) {
8256
- hasAmbiguousReachability = true;
8257
- return false;
8258
- }
8259
- if (isNodeOfType(child, "ForOfStatement")) {
8260
- const iterable = stripParenExpression(child.right);
8261
- if (isNodeOfType(iterable, "ArrayExpression") && iterable.elements.some((element) => element && !isNodeOfType(element, "SpreadElement")) && !isListenerPathAmbiguous(child, cleanupBody)) addGuaranteedLoopPrefix(child.body);
8262
- return false;
8263
- }
8264
- if ((isNodeOfType(child, "IfStatement") || isNodeOfType(child, "ConditionalExpression")) && child.alternate && readStaticBoolean(child.test) === null && !isListenerPathAmbiguous(child, cleanupBody)) {
8265
- const consequentAnalysis = analyzeCleanupBody(child.consequent, context, new Set(visitedCleanupSymbolIds));
8266
- const alternateAnalysis = analyzeCleanupBody(child.alternate, context, new Set(visitedCleanupSymbolIds));
8267
- if (consequentAnalysis && alternateAnalysis) {
8268
- exhaustiveBranches.push({
8269
- alternate: alternateAnalysis,
8270
- consequent: consequentAnalysis
8271
- });
8272
- hasUnknownAbortCall ||= consequentAnalysis.hasUnknownAbortCall && alternateAnalysis.hasUnknownAbortCall;
8273
- hasUnknownRemovalCall ||= consequentAnalysis.hasUnknownRemovalCall && alternateAnalysis.hasUnknownRemovalCall;
8274
- }
8275
- const testAnalysis = analyzeCleanupBody(child.test, context, new Set(visitedCleanupSymbolIds));
8276
- if (testAnalysis) addCleanupAnalysis(testAnalysis);
8277
- return false;
8278
- }
8279
- if (!isNodeOfType(child, "CallExpression")) return;
8280
- const hasAmbiguousPath = isListenerPathAmbiguous(child, cleanupBody);
8281
- const removalTarget = readDirectMemberReceiver(child.callee, "removeEventListener", context);
8282
- if (removalTarget && !hasAmbiguousPath) {
8283
- const removal = readListenerCandidate(child, "removeEventListener", context);
8284
- if (removal) {
8285
- removals.push(removal);
8286
- hasUnknownRemovalCall ||= removal.callbackIdentity === null || removal.capture === null;
8287
- } else hasUnknownRemovalCall = true;
8288
- }
8289
- if (!removalTarget && !hasAmbiguousPath) {
8290
- const destructuredRemoval = readDestructuredRemovalCandidate(child, context);
8291
- if (destructuredRemoval) removals.push(destructuredRemoval);
8292
- }
8293
- const controllerNode = readDirectMemberReceiver(child.callee, "abort");
8294
- if (controllerNode) {
8295
- const controllerSymbolId = resolveLocalAbortControllerSymbolId(controllerNode, context);
8296
- if (controllerSymbolId === null) {
8297
- if (!hasAmbiguousPath) hasUnknownAbortCall = true;
8298
- } else if (!hasAmbiguousPath || isAbortGuaranteedByPath(child, cleanupBody, controllerSymbolId, context)) abortedControllerSymbolIds.add(controllerSymbolId);
8299
- return;
8300
- }
8301
- if (hasAmbiguousPath) return;
8302
- const boundAbortControllerSymbolId = resolveBoundAbortControllerSymbolId(child, context);
8303
- if (boundAbortControllerSymbolId !== null) {
8304
- abortedControllerSymbolIds.add(boundAbortControllerSymbolId);
8305
- return;
8306
- }
8307
- const calledCleanup = resolveCalledCleanup(child, context);
8308
- if (!calledCleanup || visitedCleanupSymbolIds.has(calledCleanup.symbolId)) return;
8309
- visitedCleanupSymbolIds.add(calledCleanup.symbolId);
8310
- const calledCleanupAnalysis = analyzeCleanupBody(calledCleanup.body, context, visitedCleanupSymbolIds);
8311
- if (!calledCleanupAnalysis) return;
8312
- addCleanupAnalysis(calledCleanupAnalysis);
8313
- });
8314
- if (hasAmbiguousReachability) return null;
8315
- return {
8316
- removals,
8317
- abortedControllerSymbolIds,
8318
- exhaustiveBranches,
8319
- hasUnknownAbortCall,
8320
- hasUnknownRemovalCall
8321
- };
8322
- };
8323
- const analyzeEffectListeners = (effectBody, context) => {
8324
- const effectInputs = collectEffectListenerInputs(effectBody, context);
8325
- if (!effectInputs.hasCanonicalCleanupReturn || effectInputs.returnStatementCount !== 1 || effectInputs.cleanupBodies.length !== 1) return null;
8326
- const removals = [];
8327
- const abortedControllerSymbolIds = /* @__PURE__ */ new Set();
8328
- const exhaustiveBranches = [];
8329
- let hasUnknownAbortCall = false;
8330
- let hasUnknownRemovalCall = false;
8331
- const addAnalysis = (analysis) => {
8332
- removals.push(...analysis.removals);
8333
- exhaustiveBranches.push(...analysis.exhaustiveBranches);
8334
- hasUnknownAbortCall ||= analysis.hasUnknownAbortCall;
8335
- hasUnknownRemovalCall ||= analysis.hasUnknownRemovalCall;
8336
- for (const controllerSymbolId of analysis.abortedControllerSymbolIds) abortedControllerSymbolIds.add(controllerSymbolId);
8337
- };
8338
- const setupAbortAnalysis = analyzeCleanupBody(effectBody, context);
8339
- for (const cleanupBody of effectInputs.cleanupBodies) {
8340
- const cleanupAnalysis = analyzeCleanupBody(cleanupBody, context);
8341
- if (!cleanupAnalysis) return null;
8342
- addAnalysis(cleanupAnalysis);
8343
- }
8344
- return {
8345
- registrations: effectInputs.registrations,
8346
- removals,
8347
- abortedControllerSymbolIds,
8348
- exhaustiveBranches,
8349
- hasUnknownAbortCall,
8350
- hasUnknownRemovalCall,
8351
- setupAbortAnalysis
8352
- };
8353
- };
8354
- const effectListenerCleanupMismatch = defineRule({
8355
- id: "effect-listener-cleanup-mismatch",
8356
- title: "Effect cleanup does not match its event listener",
8357
- severity: "error",
8358
- recommendation: "Pass the same callback binding and capture flag to `addEventListener` and `removeEventListener`, or abort the registration's local AbortController during cleanup.",
8359
- create: (context) => ({ CallExpression(node) {
8360
- if (!isReactApiCall(node, LISTENER_EFFECT_HOOK_NAMES, context.scopes)) return;
8361
- const effectCallback = getEffectCallback(node);
8362
- if (!isFunctionLike$1(effectCallback)) return;
8363
- const listenerAnalysis = analyzeEffectListeners(effectCallback.body, context);
8364
- if (!listenerAnalysis) return;
8365
- for (const registration of listenerAnalysis.registrations) {
8366
- if (registration.hasUnknownCancellation || listenerAnalysis.hasUnknownRemovalCall) continue;
8367
- if (listenerAnalysis.registrations.filter((candidateRegistration) => candidateRegistration.targetKey === registration.targetKey && candidateRegistration.eventName === registration.eventName).length > 1) continue;
8368
- if (registration.abortControllerSymbolId !== null && listenerAnalysis.hasUnknownAbortCall) continue;
8369
- if (registration.abortControllerSymbolId !== null && listenerAnalysis.setupAbortAnalysis && doesListenerAnalysisAbortController(listenerAnalysis.setupAbortAnalysis, registration.abortControllerSymbolId)) continue;
8370
- if (doesListenerAnalysisCancelRegistration(listenerAnalysis, registration)) continue;
8371
- const candidateRemovals = listenerAnalysis.removals.filter((removal) => removal.targetKey === registration.targetKey && removal.eventName === registration.eventName);
8372
- let firstProvableMismatch;
8373
- let didFindNonMismatchCandidate = false;
8374
- for (const removal of candidateRemovals) {
8375
- if (!removal.callbackIdentity || removal.capture === null) {
8376
- didFindNonMismatchCandidate = true;
8377
- break;
8378
- }
8379
- const callbackComparison = compareListenerCallbackIdentities(registration.callbackIdentity, removal.callbackIdentity);
8380
- if (callbackComparison === "unknown") {
8381
- didFindNonMismatchCandidate = true;
8382
- break;
8383
- }
8384
- if (callbackComparison === "same" && registration.capture === removal.capture) {
8385
- didFindNonMismatchCandidate = true;
8386
- break;
8387
- }
8388
- firstProvableMismatch ??= {
8389
- removalNode: removal.node,
8390
- removalCapture: removal.capture,
8391
- callbackComparison
8392
- };
8393
- }
8394
- if (candidateRemovals.length === 0 || didFindNonMismatchCandidate || !firstProvableMismatch) continue;
8395
- context.report({
8396
- node: firstProvableMismatch.removalNode,
8397
- message: buildListenerCleanupMismatchMessage(registration.eventName, registration.capture, firstProvableMismatch.removalCapture, firstProvableMismatch.callbackComparison)
8398
- });
8399
- }
8400
- } })
8401
- });
8402
- //#endregion
8403
7665
  //#region src/plugin/utils/is-react-hook-name.ts
8404
7666
  const isReactHookName = (name) => {
8405
7667
  if (!name.startsWith("use")) return false;
@@ -11037,7 +10299,7 @@ const forbidElements = defineRule({
11037
10299
  });
11038
10300
  //#endregion
11039
10301
  //#region src/plugin/rules/react-builtins/forward-ref-uses-ref.ts
11040
- const MESSAGE$53 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
10302
+ const MESSAGE$52 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
11041
10303
  const forwardRefUsesRef = defineRule({
11042
10304
  id: "forward-ref-uses-ref",
11043
10305
  title: "forwardRef without ref parameter",
@@ -11057,7 +10319,7 @@ const forwardRefUsesRef = defineRule({
11057
10319
  if (isNodeOfType(onlyParam, "RestElement")) return;
11058
10320
  context.report({
11059
10321
  node: inner,
11060
- message: MESSAGE$53
10322
+ message: MESSAGE$52
11061
10323
  });
11062
10324
  } })
11063
10325
  });
@@ -11098,7 +10360,7 @@ const gitProviderUrlInjectionRisk = defineRule({
11098
10360
  });
11099
10361
  //#endregion
11100
10362
  //#region src/plugin/rules/a11y/heading-has-content.ts
11101
- const MESSAGE$52 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
10363
+ const MESSAGE$51 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
11102
10364
  const DEFAULT_HEADING_TAGS = [
11103
10365
  "h1",
11104
10366
  "h2",
@@ -11132,7 +10394,7 @@ const headingHasContent = defineRule({
11132
10394
  for (const attribute of ["aria-label", "aria-labelledby"]) if (hasJsxPropIgnoreCase(node.attributes, attribute)) return;
11133
10395
  context.report({
11134
10396
  node,
11135
- message: MESSAGE$52
10397
+ message: MESSAGE$51
11136
10398
  });
11137
10399
  } };
11138
10400
  }
@@ -11270,7 +10532,7 @@ const hooksNoNanInDeps = defineRule({
11270
10532
  });
11271
10533
  //#endregion
11272
10534
  //#region src/plugin/rules/a11y/html-has-lang.ts
11273
- const MESSAGE$51 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
10535
+ const MESSAGE$50 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
11274
10536
  const resolveSettings$39 = (settings) => {
11275
10537
  const reactDoctor = settings?.["react-doctor"];
11276
10538
  return { htmlTags: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.htmlHasLang ?? {} : {}).htmlTags ?? ["html"] };
@@ -11317,13 +10579,13 @@ const htmlHasLang = defineRule({
11317
10579
  if (!lang) {
11318
10580
  context.report({
11319
10581
  node: node.name,
11320
- message: MESSAGE$51
10582
+ message: MESSAGE$50
11321
10583
  });
11322
10584
  return;
11323
10585
  }
11324
10586
  if (evaluateLang(lang.value) === "empty") context.report({
11325
10587
  node: lang,
11326
- message: MESSAGE$51
10588
+ message: MESSAGE$50
11327
10589
  });
11328
10590
  } };
11329
10591
  }
@@ -11567,7 +10829,7 @@ const htmlNoNestedInteractive = defineRule({
11567
10829
  });
11568
10830
  //#endregion
11569
10831
  //#region src/plugin/rules/a11y/iframe-has-title.ts
11570
- const MESSAGE$50 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
10832
+ const MESSAGE$49 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
11571
10833
  const evaluateTitleValue = (value) => {
11572
10834
  if (!value) return "missing";
11573
10835
  if (isNodeOfType(value, "Literal")) {
@@ -11607,14 +10869,14 @@ const iframeHasTitle = defineRule({
11607
10869
  if (!titleAttr) {
11608
10870
  if (hasSpread || tag === "iframe") context.report({
11609
10871
  node: node.name,
11610
- message: MESSAGE$50
10872
+ message: MESSAGE$49
11611
10873
  });
11612
10874
  return;
11613
10875
  }
11614
10876
  const verdict = evaluateTitleValue(titleAttr.value);
11615
10877
  if (verdict === "missing" || verdict === "empty") context.report({
11616
10878
  node: titleAttr,
11617
- message: MESSAGE$50
10879
+ message: MESSAGE$49
11618
10880
  });
11619
10881
  } })
11620
10882
  });
@@ -11739,7 +11001,7 @@ const iframeMissingSandbox = defineRule({
11739
11001
  });
11740
11002
  //#endregion
11741
11003
  //#region src/plugin/rules/a11y/img-redundant-alt.ts
11742
- const MESSAGE$49 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
11004
+ const MESSAGE$48 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
11743
11005
  const DEFAULT_COMPONENTS = ["img"];
11744
11006
  const DEFAULT_REDUNDANT_WORDS = [
11745
11007
  "image",
@@ -11804,7 +11066,7 @@ const imgRedundantAlt = defineRule({
11804
11066
  if (!altAttribute) return;
11805
11067
  if (altValueRedundant(altAttribute, settings.words)) context.report({
11806
11068
  node: altAttribute,
11807
- message: MESSAGE$49
11069
+ message: MESSAGE$48
11808
11070
  });
11809
11071
  } };
11810
11072
  }
@@ -12560,7 +11822,7 @@ const jotaiTqUseRawQueryAtom = defineRule({
12560
11822
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
12561
11823
  if (!isNodeOfType(specifier.local, "Identifier")) continue;
12562
11824
  if (source === "jotai-tanstack-query") {
12563
- const importedName = getImportedName(specifier);
11825
+ const importedName = getImportedName$1(specifier);
12564
11826
  if (importedName && QUERY_ATOM_FACTORY_IMPORTED_NAMES.has(importedName)) queryAtomFactoryLocalNames.add(specifier.local.name);
12565
11827
  continue;
12566
11828
  }
@@ -13574,7 +12836,7 @@ const isStaticRegExpConstruction = (node) => {
13574
12836
  const flagsArgument = node.arguments?.[1];
13575
12837
  return isNodeOfType(node.callee, "Identifier") && node.callee.name === "RegExp" && isStaticPattern(patternArgument) && (flagsArgument === void 0 || isStaticPattern(flagsArgument));
13576
12838
  };
13577
- const MESSAGE$48 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
12839
+ const MESSAGE$47 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
13578
12840
  const jsHoistRegexp = defineRule({
13579
12841
  id: "js-hoist-regexp",
13580
12842
  title: "RegExp built inside a loop",
@@ -13585,13 +12847,13 @@ const jsHoistRegexp = defineRule({
13585
12847
  NewExpression(node) {
13586
12848
  if (isStaticRegExpConstruction(node)) context.report({
13587
12849
  node,
13588
- message: MESSAGE$48
12850
+ message: MESSAGE$47
13589
12851
  });
13590
12852
  },
13591
12853
  CallExpression(node) {
13592
12854
  if (isStaticRegExpConstruction(node)) context.report({
13593
12855
  node,
13594
- message: MESSAGE$48
12856
+ message: MESSAGE$47
13595
12857
  });
13596
12858
  }
13597
12859
  }, { treatIteratorCallbacksAsLoops: true })
@@ -15673,7 +14935,7 @@ const jsxMaxDepth = defineRule({
15673
14935
  });
15674
14936
  //#endregion
15675
14937
  //#region src/plugin/rules/react-builtins/jsx-no-comment-textnodes.ts
15676
- const MESSAGE$47 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
14938
+ const MESSAGE$46 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
15677
14939
  const LITERAL_TEXT_TAGS = new Set([
15678
14940
  "code",
15679
14941
  "pre",
@@ -15743,7 +15005,7 @@ const jsxNoCommentTextnodes = defineRule({
15743
15005
  if (isDeliberateStyledCommentToken(node)) return;
15744
15006
  context.report({
15745
15007
  node,
15746
- message: MESSAGE$47
15008
+ message: MESSAGE$46
15747
15009
  });
15748
15010
  } })
15749
15011
  });
@@ -15774,7 +15036,7 @@ const isInsideFunctionScope = (node) => {
15774
15036
  };
15775
15037
  //#endregion
15776
15038
  //#region src/plugin/rules/react-builtins/jsx-no-constructed-context-values.ts
15777
- const MESSAGE$46 = "Every reader of this context redraws on each render because you build its `value` inline.";
15039
+ const MESSAGE$45 = "Every reader of this context redraws on each render because you build its `value` inline.";
15778
15040
  const CONTEXT_MODULES$1 = [
15779
15041
  "react",
15780
15042
  "use-context-selector",
@@ -15872,7 +15134,7 @@ const jsxNoConstructedContextValues = defineRule({
15872
15134
  if (!isConstructedValue(innerExpression)) continue;
15873
15135
  context.report({
15874
15136
  node: attribute,
15875
- message: MESSAGE$46
15137
+ message: MESSAGE$45
15876
15138
  });
15877
15139
  }
15878
15140
  }
@@ -16539,7 +15801,7 @@ const DATA_ARRAY_PROP_SUFFIXES = [
16539
15801
  ];
16540
15802
  //#endregion
16541
15803
  //#region src/plugin/rules/react-builtins/jsx-no-new-array-as-prop.ts
16542
- const MESSAGE$45 = "This child redraws every render because the prop gets a brand new array each time.";
15804
+ const MESSAGE$44 = "This child redraws every render because the prop gets a brand new array each time.";
16543
15805
  const isDataArrayPropName = (propName) => {
16544
15806
  if (DATA_ARRAY_PROP_NAMES.has(propName)) return true;
16545
15807
  for (const suffix of DATA_ARRAY_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -16626,7 +15888,7 @@ const jsxNoNewArrayAsProp = defineRule({
16626
15888
  if (!isArrayProducingExpression(expressionNode) && !followsRenderLocalArrayBinding(expressionNode, node)) return;
16627
15889
  context.report({
16628
15890
  node,
16629
- message: MESSAGE$45
15891
+ message: MESSAGE$44
16630
15892
  });
16631
15893
  }
16632
15894
  };
@@ -16884,7 +16146,7 @@ const SAFE_RECEIVER_NAMES = new Set([
16884
16146
  ]);
16885
16147
  //#endregion
16886
16148
  //#region src/plugin/rules/react-builtins/jsx-no-new-function-as-prop.ts
16887
- const MESSAGE$44 = "This child redraws every render because the prop gets a brand new function each time.";
16149
+ const MESSAGE$43 = "This child redraws every render because the prop gets a brand new function each time.";
16888
16150
  const isAccessorPredicateName = (propName) => {
16889
16151
  for (const prefix of ACCESSOR_PREDICATE_PREFIXES) {
16890
16152
  if (propName.length <= prefix.length) continue;
@@ -17090,7 +16352,7 @@ const jsxNoNewFunctionAsProp = defineRule({
17090
16352
  if (!isFunctionProducingExpression(expressionNode) && !followsRenderLocalFunctionBinding(expressionNode, node)) return;
17091
16353
  context.report({
17092
16354
  node,
17093
- message: MESSAGE$44
16355
+ message: MESSAGE$43
17094
16356
  });
17095
16357
  }
17096
16358
  };
@@ -17310,7 +16572,7 @@ const CONFIG_OBJECT_PROP_SUFFIXES = [
17310
16572
  ];
17311
16573
  //#endregion
17312
16574
  //#region src/plugin/rules/react-builtins/jsx-no-new-object-as-prop.ts
17313
- const MESSAGE$43 = "This child redraws every render because the prop gets a brand new object each time.";
16575
+ const MESSAGE$42 = "This child redraws every render because the prop gets a brand new object each time.";
17314
16576
  const isConfigObjectPropName = (propName) => {
17315
16577
  if (CONFIG_OBJECT_PROP_NAMES.has(propName)) return true;
17316
16578
  for (const suffix of CONFIG_OBJECT_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -17399,7 +16661,7 @@ const jsxNoNewObjectAsProp = defineRule({
17399
16661
  if (!isObjectProducingExpression(expressionNode) && !followsRenderLocalObjectBinding(expressionNode, node)) return;
17400
16662
  context.report({
17401
16663
  node,
17402
- message: MESSAGE$43
16664
+ message: MESSAGE$42
17403
16665
  });
17404
16666
  }
17405
16667
  };
@@ -17407,7 +16669,7 @@ const jsxNoNewObjectAsProp = defineRule({
17407
16669
  });
17408
16670
  //#endregion
17409
16671
  //#region src/plugin/rules/react-builtins/jsx-no-script-url.ts
17410
- const MESSAGE$42 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
16672
+ const MESSAGE$41 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
17411
16673
  const JAVASCRIPT_URL_PATTERN = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
17412
16674
  const resolveSettings$29 = (settings) => {
17413
16675
  const reactDoctor = settings?.["react-doctor"];
@@ -17448,7 +16710,7 @@ const jsxNoScriptUrl = defineRule({
17448
16710
  if (!value || !isNodeOfType(value, "Literal") || typeof value.value !== "string") continue;
17449
16711
  if (JAVASCRIPT_URL_PATTERN.test(value.value)) context.report({
17450
16712
  node: attribute,
17451
- message: MESSAGE$42
16713
+ message: MESSAGE$41
17452
16714
  });
17453
16715
  }
17454
16716
  } };
@@ -18072,7 +17334,7 @@ const jsxPropsNoSpreadMulti = defineRule({
18072
17334
  });
18073
17335
  //#endregion
18074
17336
  //#region src/plugin/rules/react-builtins/jsx-props-no-spreading.ts
18075
- const MESSAGE$41 = "You can't tell what props reach this element when you spread them.";
17337
+ const MESSAGE$40 = "You can't tell what props reach this element when you spread them.";
18076
17338
  const resolveSettings$25 = (settings) => {
18077
17339
  const reactDoctor = settings?.["react-doctor"];
18078
17340
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxPropsNoSpreading ?? {} : {};
@@ -18115,7 +17377,7 @@ const jsxPropsNoSpreading = defineRule({
18115
17377
  }
18116
17378
  context.report({
18117
17379
  node: attribute,
18118
- message: MESSAGE$41
17380
+ message: MESSAGE$40
18119
17381
  });
18120
17382
  didReportInFile = true;
18121
17383
  return;
@@ -18390,7 +17652,7 @@ const labelHasAssociatedControl = defineRule({
18390
17652
  });
18391
17653
  //#endregion
18392
17654
  //#region src/plugin/rules/a11y/lang.ts
18393
- const MESSAGE$40 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
17655
+ const MESSAGE$39 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
18394
17656
  const COMMON_LANGUAGE_PRIMARY_TAGS = new Set([
18395
17657
  "aa",
18396
17658
  "ab",
@@ -18870,7 +18132,7 @@ const lang = defineRule({
18870
18132
  if (expression.type === "Identifier" && expression.name === "undefined" || expression.type === "Literal" && expression.value === null) {
18871
18133
  context.report({
18872
18134
  node: langAttr,
18873
- message: MESSAGE$40
18135
+ message: MESSAGE$39
18874
18136
  });
18875
18137
  return;
18876
18138
  }
@@ -18879,7 +18141,7 @@ const lang = defineRule({
18879
18141
  if (value === null) return;
18880
18142
  if (!isValidLangTag(value)) context.report({
18881
18143
  node: langAttr,
18882
- message: MESSAGE$40
18144
+ message: MESSAGE$39
18883
18145
  });
18884
18146
  } })
18885
18147
  });
@@ -18924,7 +18186,7 @@ const mdxSsrExecutionRisk = defineRule({
18924
18186
  });
18925
18187
  //#endregion
18926
18188
  //#region src/plugin/rules/a11y/media-has-caption.ts
18927
- const MESSAGE$39 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
18189
+ const MESSAGE$38 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
18928
18190
  const DEFAULT_AUDIO = ["audio"];
18929
18191
  const DEFAULT_VIDEO = ["video"];
18930
18192
  const DEFAULT_TRACK = ["track"];
@@ -19013,7 +18275,7 @@ const mediaHasCaption = defineRule({
19013
18275
  if (!parent || !isNodeOfType(parent, "JSXElement")) {
19014
18276
  context.report({
19015
18277
  node: node.name,
19016
- message: MESSAGE$39
18278
+ message: MESSAGE$38
19017
18279
  });
19018
18280
  return;
19019
18281
  }
@@ -19032,7 +18294,7 @@ const mediaHasCaption = defineRule({
19032
18294
  return kindValue.value.toLowerCase() === "captions";
19033
18295
  })) context.report({
19034
18296
  node: node.name,
19035
- message: MESSAGE$39
18297
+ message: MESSAGE$38
19036
18298
  });
19037
18299
  } };
19038
18300
  }
@@ -21166,7 +20428,7 @@ const astMentionsSuspense = (programNode) => {
21166
20428
  return false;
21167
20429
  }
21168
20430
  if (isNodeOfType(child, "ImportDeclaration") && child.source?.value === "react") {
21169
- if ((child.specifiers ?? []).some((specifier) => isNodeOfType(specifier, "ImportSpecifier") && getImportedName(specifier) === "Suspense")) {
20431
+ if ((child.specifiers ?? []).some((specifier) => isNodeOfType(specifier, "ImportSpecifier") && getImportedName$1(specifier) === "Suspense")) {
21170
20432
  didDetect = true;
21171
20433
  return false;
21172
20434
  }
@@ -21210,7 +20472,7 @@ const collectSuspenseLocalNames = (programNode) => {
21210
20472
  for (const statement of programNode.body ?? []) {
21211
20473
  if (!isNodeOfType(statement, "ImportDeclaration")) continue;
21212
20474
  if (statement.source?.value !== "react") continue;
21213
- for (const specifier of statement.specifiers ?? []) if (isNodeOfType(specifier, "ImportSpecifier") && getImportedName(specifier) === "Suspense" && specifier.local?.name) names.add(specifier.local.name);
20475
+ for (const specifier of statement.specifiers ?? []) if (isNodeOfType(specifier, "ImportSpecifier") && getImportedName$1(specifier) === "Suspense" && specifier.local?.name) names.add(specifier.local.name);
21214
20476
  }
21215
20477
  return names;
21216
20478
  };
@@ -21302,7 +20564,7 @@ const nextjsNoVercelOgImport = defineRule({
21302
20564
  });
21303
20565
  //#endregion
21304
20566
  //#region src/plugin/rules/a11y/no-access-key.ts
21305
- const MESSAGE$38 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
20567
+ const MESSAGE$37 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
21306
20568
  const isUndefinedIdentifier = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
21307
20569
  const noAccessKey = defineRule({
21308
20570
  id: "no-access-key",
@@ -21321,7 +20583,7 @@ const noAccessKey = defineRule({
21321
20583
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
21322
20584
  context.report({
21323
20585
  node: accessKey,
21324
- message: MESSAGE$38
20586
+ message: MESSAGE$37
21325
20587
  });
21326
20588
  return;
21327
20589
  }
@@ -21331,7 +20593,7 @@ const noAccessKey = defineRule({
21331
20593
  if (isUndefinedIdentifier(expression)) return;
21332
20594
  context.report({
21333
20595
  node: accessKey,
21334
- message: MESSAGE$38
20596
+ message: MESSAGE$37
21335
20597
  });
21336
20598
  }
21337
20599
  } };
@@ -22199,7 +21461,7 @@ const noAdjustStateOnPropChange = defineRule({
22199
21461
  });
22200
21462
  //#endregion
22201
21463
  //#region src/plugin/rules/a11y/no-aria-hidden-on-focusable.ts
22202
- const MESSAGE$37 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
21464
+ const MESSAGE$36 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
22203
21465
  const ALWAYS_FOCUSABLE_TAGS = new Set([
22204
21466
  "button",
22205
21467
  "embed",
@@ -22311,7 +21573,7 @@ const noAriaHiddenOnFocusable = defineRule({
22311
21573
  const isImplicitlyFocusable = isNativelyFocusable(tag, node);
22312
21574
  if (isExplicitlyFocusable || isImplicitlyFocusable) context.report({
22313
21575
  node: ariaHidden,
22314
- message: MESSAGE$37
21576
+ message: MESSAGE$36
22315
21577
  });
22316
21578
  } })
22317
21579
  });
@@ -23192,7 +22454,7 @@ const isAllLiteralArrayExpression = (node) => {
23192
22454
  };
23193
22455
  //#endregion
23194
22456
  //#region src/plugin/rules/react-builtins/no-array-index-key.ts
23195
- const MESSAGE$36 = "Your users can see & submit the wrong data when this list reorders.";
22457
+ const MESSAGE$35 = "Your users can see & submit the wrong data when this list reorders.";
23196
22458
  const SECOND_INDEX_METHODS = new Set([
23197
22459
  "every",
23198
22460
  "filter",
@@ -23311,14 +22573,14 @@ const noArrayIndexKey = defineRule({
23311
22573
  if (propName !== "key") continue;
23312
22574
  if (expressionUsesIndex(property.value, indexBinding.name)) context.report({
23313
22575
  node: property,
23314
- message: MESSAGE$36
22576
+ message: MESSAGE$35
23315
22577
  });
23316
22578
  }
23317
22579
  } })
23318
22580
  });
23319
22581
  //#endregion
23320
22582
  //#region src/plugin/rules/state-and-effects/no-async-effect-callback.ts
23321
- const MESSAGE$35 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
22583
+ const MESSAGE$34 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
23322
22584
  const noAsyncEffectCallback = defineRule({
23323
22585
  id: "no-async-effect-callback",
23324
22586
  title: "Async effect callback",
@@ -23332,13 +22594,13 @@ const noAsyncEffectCallback = defineRule({
23332
22594
  if (!callback.async) return;
23333
22595
  context.report({
23334
22596
  node: callback,
23335
- message: MESSAGE$35
22597
+ message: MESSAGE$34
23336
22598
  });
23337
22599
  } })
23338
22600
  });
23339
22601
  //#endregion
23340
22602
  //#region src/plugin/rules/a11y/no-autofocus.ts
23341
- const MESSAGE$34 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
22603
+ const MESSAGE$33 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
23342
22604
  const resolveSettings$21 = (settings) => {
23343
22605
  const reactDoctor = settings?.["react-doctor"];
23344
22606
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -23435,7 +22697,7 @@ const noAutofocus = defineRule({
23435
22697
  if (isConditionallyRendered(node)) return;
23436
22698
  context.report({
23437
22699
  node: autoFocusAttribute,
23438
- message: MESSAGE$34
22700
+ message: MESSAGE$33
23439
22701
  });
23440
22702
  } };
23441
22703
  }
@@ -23795,10 +23057,6 @@ const NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES = new Set([
23795
23057
  "ClassDeclaration",
23796
23058
  "ClassExpression"
23797
23059
  ]);
23798
- const REACT_CREATE_ELEMENT_OPTIONS = {
23799
- allowGlobalReactNamespace: false,
23800
- allowUnboundBareCalls: false
23801
- };
23802
23060
  const isCallArgumentFunctionExpression = (node) => {
23803
23061
  if (node.type !== "ArrowFunctionExpression" && node.type !== "FunctionExpression") return false;
23804
23062
  const parent = node.parent;
@@ -23806,10 +23064,44 @@ const isCallArgumentFunctionExpression = (node) => {
23806
23064
  return parent.arguments.some((argumentNode) => argumentNode === node);
23807
23065
  };
23808
23066
  const isNestedRenderEvidenceBoundary = (node) => NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES.has(node.type) && !isCallArgumentFunctionExpression(node);
23067
+ const isReactImport$1 = (symbol) => {
23068
+ let importDeclaration = symbol.declarationNode?.parent;
23069
+ while (importDeclaration && !isNodeOfType(importDeclaration, "ImportDeclaration")) importDeclaration = importDeclaration.parent ?? null;
23070
+ if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration")) return false;
23071
+ return importDeclaration.source.value === "react";
23072
+ };
23073
+ const getImportedName = (symbol) => {
23074
+ if (symbol.kind !== "import") return null;
23075
+ if (!isReactImport$1(symbol)) return null;
23076
+ return getImportedName$1(symbol.declarationNode) ?? null;
23077
+ };
23078
+ const isReactNamespaceImport = (symbol) => {
23079
+ if (symbol.kind !== "import") return false;
23080
+ if (!isReactImport$1(symbol)) return false;
23081
+ return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier");
23082
+ };
23083
+ const isReactCreateElementIdentifierCall = (callee, scopes) => {
23084
+ if (!isNodeOfType(callee, "Identifier")) return false;
23085
+ const symbol = scopes.symbolFor(callee);
23086
+ return Boolean(symbol && getImportedName(symbol) === "createElement");
23087
+ };
23088
+ const isReactCreateElementMemberCall = (callee, scopes) => {
23089
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
23090
+ if (callee.computed) return false;
23091
+ if (!isNodeOfType(callee.object, "Identifier")) return false;
23092
+ if (!isNodeOfType(callee.property, "Identifier")) return false;
23093
+ if (callee.property.name !== "createElement") return false;
23094
+ const symbol = scopes.symbolFor(callee.object);
23095
+ return Boolean(symbol && isReactNamespaceImport(symbol));
23096
+ };
23097
+ const isReactCreateElementCall = (node, scopes) => {
23098
+ if (!isNodeOfType(node, "CallExpression")) return false;
23099
+ return isReactCreateElementIdentifierCall(node.callee, scopes) || isReactCreateElementMemberCall(node.callee, scopes);
23100
+ };
23809
23101
  const containsRenderOutput$1 = (node, rootNode, scopes) => {
23810
23102
  if (node !== rootNode && isNestedRenderEvidenceBoundary(node)) return false;
23811
23103
  if (node.type === "JSXElement" || node.type === "JSXFragment") return true;
23812
- if (isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS)) return true;
23104
+ if (isReactCreateElementCall(node, scopes)) return true;
23813
23105
  const nodeRecord = node;
23814
23106
  for (const key of Object.keys(nodeRecord)) {
23815
23107
  if (key === "parent") continue;
@@ -24472,7 +23764,7 @@ const noChainStateUpdates = defineRule({
24472
23764
  });
24473
23765
  //#endregion
24474
23766
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
24475
- const MESSAGE$33 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
23767
+ const MESSAGE$32 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
24476
23768
  const noChildrenProp = defineRule({
24477
23769
  id: "no-children-prop",
24478
23770
  title: "Children passed as a prop",
@@ -24484,7 +23776,7 @@ const noChildrenProp = defineRule({
24484
23776
  if (node.name.name !== "children") return;
24485
23777
  context.report({
24486
23778
  node: node.name,
24487
- message: MESSAGE$33
23779
+ message: MESSAGE$32
24488
23780
  });
24489
23781
  },
24490
23782
  CallExpression(node) {
@@ -24497,7 +23789,7 @@ const noChildrenProp = defineRule({
24497
23789
  const propertyKey = property.key;
24498
23790
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "children" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "children") context.report({
24499
23791
  node: propertyKey,
24500
- message: MESSAGE$33
23792
+ message: MESSAGE$32
24501
23793
  });
24502
23794
  }
24503
23795
  }
@@ -24505,7 +23797,7 @@ const noChildrenProp = defineRule({
24505
23797
  });
24506
23798
  //#endregion
24507
23799
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
24508
- const MESSAGE$32 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
23800
+ const MESSAGE$31 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
24509
23801
  const noCloneElement = defineRule({
24510
23802
  id: "no-clone-element",
24511
23803
  title: "cloneElement makes child props fragile",
@@ -24518,7 +23810,7 @@ const noCloneElement = defineRule({
24518
23810
  if (isNodeOfType(callee, "Identifier") && callee.name === "cloneElement") {
24519
23811
  if (isImportedFromModule(node, "cloneElement", "react")) context.report({
24520
23812
  node: callee,
24521
- message: MESSAGE$32
23813
+ message: MESSAGE$31
24522
23814
  });
24523
23815
  return;
24524
23816
  }
@@ -24531,14 +23823,14 @@ const noCloneElement = defineRule({
24531
23823
  if (!isImportedFromModule(node, callee.object.name, "react")) return;
24532
23824
  context.report({
24533
23825
  node: callee,
24534
- message: MESSAGE$32
23826
+ message: MESSAGE$31
24535
23827
  });
24536
23828
  }
24537
23829
  } })
24538
23830
  });
24539
23831
  //#endregion
24540
23832
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
24541
- const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
23833
+ const MESSAGE$30 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
24542
23834
  const CONTEXT_MODULES = [
24543
23835
  "react",
24544
23836
  "use-context-selector",
@@ -24574,13 +23866,13 @@ const noCreateContextInRender = defineRule({
24574
23866
  if (!componentOrHookName) return;
24575
23867
  context.report({
24576
23868
  node,
24577
- message: `${MESSAGE$31} (called inside "${componentOrHookName}")`
23869
+ message: `${MESSAGE$30} (called inside "${componentOrHookName}")`
24578
23870
  });
24579
23871
  } })
24580
23872
  });
24581
23873
  //#endregion
24582
23874
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
24583
- const MESSAGE$30 = "`createRef()` in a function component allocates a brand-new ref on every render, so it never holds a value between renders. Use the `useRef()` hook instead.";
23875
+ const MESSAGE$29 = "`createRef()` in a function component allocates a brand-new ref on every render, so it never holds a value between renders. Use the `useRef()` hook instead.";
24584
23876
  const isUseMemoCallbackArgument = (functionNode) => {
24585
23877
  const parent = functionNode.parent;
24586
23878
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
@@ -24610,7 +23902,7 @@ const noCreateRefInFunctionComponent = defineRule({
24610
23902
  if (!(isReactHookName(displayName) || functionContainsReactRenderOutput(enclosingFunction, context.scopes))) return;
24611
23903
  context.report({
24612
23904
  node,
24613
- message: MESSAGE$30
23905
+ message: MESSAGE$29
24614
23906
  });
24615
23907
  } })
24616
23908
  });
@@ -24750,7 +24042,7 @@ const noCreateStoreInRender = defineRule({
24750
24042
  });
24751
24043
  //#endregion
24752
24044
  //#region src/plugin/rules/react-builtins/no-danger.ts
24753
- const MESSAGE$29 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
24045
+ const MESSAGE$28 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
24754
24046
  const noDanger = defineRule({
24755
24047
  id: "no-danger",
24756
24048
  title: "Raw HTML injection can run unsafe markup",
@@ -24764,7 +24056,7 @@ const noDanger = defineRule({
24764
24056
  if (!propAttribute) return;
24765
24057
  context.report({
24766
24058
  node: propAttribute.name,
24767
- message: MESSAGE$29
24059
+ message: MESSAGE$28
24768
24060
  });
24769
24061
  },
24770
24062
  CallExpression(node) {
@@ -24776,7 +24068,7 @@ const noDanger = defineRule({
24776
24068
  const propertyKey = property.key;
24777
24069
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "dangerouslySetInnerHTML" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "dangerouslySetInnerHTML") context.report({
24778
24070
  node: propertyKey,
24779
- message: MESSAGE$29
24071
+ message: MESSAGE$28
24780
24072
  });
24781
24073
  }
24782
24074
  }
@@ -24799,7 +24091,7 @@ const isMeaningfulJsxChild = (child) => {
24799
24091
  };
24800
24092
  //#endregion
24801
24093
  //#region src/plugin/rules/react-builtins/no-danger-with-children.ts
24802
- const MESSAGE$28 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
24094
+ const MESSAGE$27 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
24803
24095
  const mergePropsShape = (target, source) => {
24804
24096
  target.hasDangerously ||= source.hasDangerously;
24805
24097
  target.hasChildren ||= source.hasChildren;
@@ -24874,7 +24166,7 @@ const noDangerWithChildren = defineRule({
24874
24166
  if (!hasChildrenProp && !hasNestedChildren) return;
24875
24167
  if (hasJsxPropIgnoreCase(opening.attributes, "dangerouslySetInnerHTML") || spreadPropsShape.hasDangerously) context.report({
24876
24168
  node: opening,
24877
- message: MESSAGE$28
24169
+ message: MESSAGE$27
24878
24170
  });
24879
24171
  },
24880
24172
  CallExpression(node) {
@@ -24887,7 +24179,7 @@ const noDangerWithChildren = defineRule({
24887
24179
  const positionalChildren = node.arguments.slice(2);
24888
24180
  if (positionalChildren.length > 1 || positionalChildren.some((argument) => !isNullishExpression(argument)) || propsShape.hasChildren) context.report({
24889
24181
  node,
24890
- message: MESSAGE$28
24182
+ message: MESSAGE$27
24891
24183
  });
24892
24184
  }
24893
24185
  })
@@ -25125,6 +24417,16 @@ const isInitialOnlyPropName = (propName) => {
25125
24417
  return /^initial[A-Z]/.test(propName) || /^default[A-Z]/.test(propName) || /^seed[A-Z]/.test(propName) || /^starting[A-Z]/.test(propName) || /^baseline[A-Z]/.test(propName) || /^preset[A-Z]/.test(propName);
25126
24418
  };
25127
24419
  //#endregion
24420
+ //#region src/plugin/rules/state-and-effects/utils/static-member-property-name.ts
24421
+ const getStaticMemberPropertyName = (node) => {
24422
+ if (!node) return null;
24423
+ const unwrappedNode = stripParenExpression(node);
24424
+ if (!isNodeOfType(unwrappedNode, "MemberExpression")) return null;
24425
+ if (!unwrappedNode.computed && isNodeOfType(unwrappedNode.property, "Identifier")) return unwrappedNode.property.name;
24426
+ if (unwrappedNode.computed && isNodeOfType(unwrappedNode.property, "Literal") && typeof unwrappedNode.property.value === "string") return unwrappedNode.property.value;
24427
+ return null;
24428
+ };
24429
+ //#endregion
25128
24430
  //#region src/plugin/rules/state-and-effects/utils/event-handler-reference.ts
25129
24431
  const isEventHandlerName = (name) => /^on[A-Z]/.test(name);
25130
24432
  const getStaticMemberReferenceName = (node, resolveName = (name) => name) => {
@@ -25133,6 +24435,13 @@ const getStaticMemberReferenceName = (node, resolveName = (name) => name) => {
25133
24435
  const propertyName = getStaticMemberPropertyName(node);
25134
24436
  return propertyName ? `${resolveName(node.object.name)}.${propertyName}` : null;
25135
24437
  };
24438
+ const getStaticPropertyKeyName = (node) => {
24439
+ if (!isNodeOfType(node, "Property")) return null;
24440
+ if (node.computed) return null;
24441
+ if (isNodeOfType(node.key, "Identifier")) return node.key.name;
24442
+ if (isNodeOfType(node.key, "Literal")) return String(node.key.value);
24443
+ return null;
24444
+ };
25136
24445
  const isEventHandlerValue = (node, eventHandlerReferenceNames, resolveName = (name) => name) => {
25137
24446
  if (isInlineFunctionExpression(node)) return true;
25138
24447
  if (isNodeOfType(node, "Identifier")) return eventHandlerReferenceNames.has(resolveName(node.name));
@@ -26114,7 +25423,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
26114
25423
  //#endregion
26115
25424
  //#region src/plugin/rules/react-builtins/no-did-mount-set-state.ts
26116
25425
  const LIFECYCLE_NAMES$2 = new Set(["componentDidMount"]);
26117
- const MESSAGE$27 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
25426
+ const MESSAGE$26 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
26118
25427
  const getNodeStart = (node) => "start" in node && typeof node.start === "number" ? node.start : -1;
26119
25428
  const getEnclosingLifecycleFunction = (setStateCall) => {
26120
25429
  let ancestor = setStateCall.parent;
@@ -26242,7 +25551,7 @@ const noDidMountSetState = defineRule({
26242
25551
  }
26243
25552
  context.report({
26244
25553
  node: node.callee,
26245
- message: MESSAGE$27
25554
+ message: MESSAGE$26
26246
25555
  });
26247
25556
  } };
26248
25557
  }
@@ -26250,7 +25559,7 @@ const noDidMountSetState = defineRule({
26250
25559
  //#endregion
26251
25560
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
26252
25561
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
26253
- const MESSAGE$26 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
25562
+ const MESSAGE$25 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
26254
25563
  const EQUALITY_OPERATORS = new Set([
26255
25564
  "==",
26256
25565
  "===",
@@ -26362,7 +25671,7 @@ const noDidUpdateSetState = defineRule({
26362
25671
  if (isInsideDiffGuard(node)) return;
26363
25672
  context.report({
26364
25673
  node: node.callee,
26365
- message: MESSAGE$26
25674
+ message: MESSAGE$25
26366
25675
  });
26367
25676
  } };
26368
25677
  }
@@ -26385,7 +25694,7 @@ const isStateMemberExpression = (node) => {
26385
25694
  };
26386
25695
  //#endregion
26387
25696
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
26388
- const MESSAGE$25 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
25697
+ const MESSAGE$24 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
26389
25698
  const shouldIgnoreMutation = (node) => {
26390
25699
  let isConstructor = false;
26391
25700
  let isInsideCallExpression = false;
@@ -26407,7 +25716,7 @@ const reportIfStateMutation = (context, reportNode, target) => {
26407
25716
  if (shouldIgnoreMutation(reportNode)) return;
26408
25717
  context.report({
26409
25718
  node: reportNode,
26410
- message: MESSAGE$25
25719
+ message: MESSAGE$24
26411
25720
  });
26412
25721
  };
26413
25722
  const noDirectMutationState = defineRule({
@@ -26753,7 +26062,7 @@ const noDocumentStartViewTransition = defineRule({
26753
26062
  });
26754
26063
  //#endregion
26755
26064
  //#region src/plugin/rules/js-performance/no-document-write.ts
26756
- const MESSAGE$24 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
26065
+ const MESSAGE$23 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
26757
26066
  const WRITE_METHODS = new Set(["write", "writeln"]);
26758
26067
  const noDocumentWrite = defineRule({
26759
26068
  id: "no-document-write",
@@ -26768,7 +26077,7 @@ const noDocumentWrite = defineRule({
26768
26077
  if (!isNodeOfType(callee.property, "Identifier") || !WRITE_METHODS.has(callee.property.name)) return;
26769
26078
  context.report({
26770
26079
  node,
26771
- message: MESSAGE$24
26080
+ message: MESSAGE$23
26772
26081
  });
26773
26082
  } })
26774
26083
  });
@@ -28166,7 +27475,7 @@ const collectVariableDeclarationReferenceNames = (node, scope, eventHandlerRefer
28166
27475
  };
28167
27476
  const collectPropertyReferenceNames = (node, scope, eventHandlerReferenceNames) => {
28168
27477
  if (!isNodeOfType(node, "Property")) return /* @__PURE__ */ new Set();
28169
- const propertyName = getStaticPropertyKeyName(node, { stringifyNonStringLiterals: true });
27478
+ const propertyName = getStaticPropertyKeyName(node);
28170
27479
  if (propertyName && isEventHandlerName(propertyName) && isEventHandlerValue(node.value, eventHandlerReferenceNames, (name) => resolveBindingName(scope, name))) return /* @__PURE__ */ new Set();
28171
27480
  const names = /* @__PURE__ */ new Set();
28172
27481
  if (node.computed) addNames$1(names, collectScopedReferenceNames(node.key, scope, eventHandlerReferenceNames));
@@ -28544,7 +27853,7 @@ const addObjectPropertyFunctionNames = (objectBindingName, node, functionLikeLoc
28544
27853
  if (!isNodeOfType(node, "ObjectExpression")) return;
28545
27854
  for (const property of node.properties ?? []) {
28546
27855
  if (!isNodeOfType(property, "Property")) continue;
28547
- const propertyName = getStaticPropertyKeyName(property, { stringifyNonStringLiterals: true });
27856
+ const propertyName = getStaticPropertyKeyName(property);
28548
27857
  if (!propertyName) continue;
28549
27858
  if (!isFunctionLikeReference(property.value, functionLikeLocalNames, scope)) continue;
28550
27859
  functionLikeLocalNames.add(`${objectBindingName}.${propertyName}`);
@@ -28903,7 +28212,7 @@ const ALLOWED_NAMESPACES = new Set([
28903
28212
  "ReactDOM",
28904
28213
  "ReactDom"
28905
28214
  ]);
28906
- const MESSAGE$23 = "`findDOMNode` crashes your app in React 19 because it was removed.";
28215
+ const MESSAGE$22 = "`findDOMNode` crashes your app in React 19 because it was removed.";
28907
28216
  const noFindDomNode = defineRule({
28908
28217
  id: "no-find-dom-node",
28909
28218
  title: "findDOMNode breaks component encapsulation",
@@ -28914,7 +28223,7 @@ const noFindDomNode = defineRule({
28914
28223
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
28915
28224
  if (isImportedFromModule(node, callee.name, "react-dom")) context.report({
28916
28225
  node: callee,
28917
- message: MESSAGE$23
28226
+ message: MESSAGE$22
28918
28227
  });
28919
28228
  return;
28920
28229
  }
@@ -28925,7 +28234,7 @@ const noFindDomNode = defineRule({
28925
28234
  if (callee.property.name !== "findDOMNode") return;
28926
28235
  context.report({
28927
28236
  node: callee.property,
28928
- message: MESSAGE$23
28237
+ message: MESSAGE$22
28929
28238
  });
28930
28239
  }
28931
28240
  } })
@@ -29055,7 +28364,7 @@ const noFlushSync = defineRule({
29055
28364
  if (node.source?.value !== "react-dom") return;
29056
28365
  for (const specifier of node.specifiers ?? []) {
29057
28366
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
29058
- if (getImportedName(specifier) !== "flushSync") continue;
28367
+ if (getImportedName$1(specifier) !== "flushSync") continue;
29059
28368
  const program = findProgram(node);
29060
28369
  if (program) {
29061
28370
  if (importsImperativeDomLibrary(program)) return;
@@ -29470,7 +28779,7 @@ const noGrayOnColoredBackground = defineRule({
29470
28779
  });
29471
28780
  //#endregion
29472
28781
  //#region src/plugin/rules/performance/no-img-lazy-with-high-fetchpriority.ts
29473
- const MESSAGE$22 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
28782
+ const MESSAGE$21 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
29474
28783
  const noImgLazyWithHighFetchpriority = defineRule({
29475
28784
  id: "no-img-lazy-with-high-fetchpriority",
29476
28785
  title: "Lazy image with high fetchPriority",
@@ -29484,158 +28793,11 @@ const noImgLazyWithHighFetchpriority = defineRule({
29484
28793
  if (!fetchPriorityAttribute || getJsxPropStringValue(fetchPriorityAttribute)?.toLowerCase() !== "high") return;
29485
28794
  context.report({
29486
28795
  node: node.name,
29487
- message: MESSAGE$22
28796
+ message: MESSAGE$21
29488
28797
  });
29489
28798
  } })
29490
28799
  });
29491
28800
  //#endregion
29492
- //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
29493
- const MESSAGE$21 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
29494
- const REACT_USE_REF_OPTIONS = {
29495
- allowGlobalReactNamespace: false,
29496
- allowUnboundBareCalls: false
29497
- };
29498
- const hasVisibleBindingNamed = (node, bindingName, scopes) => {
29499
- let scope = scopes.scopeFor(node);
29500
- while (true) {
29501
- if (scope.symbolsByName.has(bindingName)) return true;
29502
- if (!scope.parent) return false;
29503
- scope = scope.parent;
29504
- }
29505
- };
29506
- const hasEnclosingTypeParameterNamed = (node, typeParameterName) => {
29507
- let ancestor = node.parent;
29508
- while (ancestor) {
29509
- if ("typeParameters" in ancestor) {
29510
- const typeParameters = ancestor.typeParameters;
29511
- if (typeParameters && isNodeOfType(typeParameters, "TSTypeParameterDeclaration") && typeParameters.params.some((typeParameter) => isNodeOfType(typeParameter, "TSTypeParameter") && isNodeOfType(typeParameter.name, "Identifier") && typeParameter.name.name === typeParameterName)) return true;
29512
- }
29513
- ancestor = ancestor.parent;
29514
- }
29515
- return false;
29516
- };
29517
- const isHtmlInputElementType = (typeNode, scopes) => {
29518
- if (isNodeOfType(typeNode, "TSTypeReference")) return isNodeOfType(typeNode.typeName, "Identifier") && typeNode.typeName.name === "HTMLInputElement" && !hasVisibleBindingNamed(typeNode, "HTMLInputElement", scopes) && !hasEnclosingTypeParameterNamed(typeNode, "HTMLInputElement");
29519
- if (!isNodeOfType(typeNode, "TSUnionType")) return false;
29520
- let hasHtmlInputElementMember = false;
29521
- for (const unionMember of typeNode.types) {
29522
- if (isNodeOfType(unionMember, "TSNullKeyword") || isNodeOfType(unionMember, "TSUndefinedKeyword")) continue;
29523
- if (!isHtmlInputElementType(unionMember, scopes)) return false;
29524
- hasHtmlInputElementMember = true;
29525
- }
29526
- return hasHtmlInputElementMember;
29527
- };
29528
- const getBindingTypeAnnotation = (symbol) => {
29529
- if (!isNodeOfType(symbol.bindingIdentifier, "Identifier")) return null;
29530
- const annotation = symbol.bindingIdentifier.typeAnnotation;
29531
- if (!annotation || !isNodeOfType(annotation, "TSTypeAnnotation")) return null;
29532
- return annotation.typeAnnotation;
29533
- };
29534
- const getDirectConstInitializer = (symbol) => {
29535
- if (symbol.kind !== "const" || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
29536
- return symbol.initializer;
29537
- };
29538
- const hasTypedHtmlInputRefOrigin = (rawExpression, scopes) => {
29539
- const visitedSymbolIds = /* @__PURE__ */ new Set();
29540
- let expression = stripParenExpression(rawExpression);
29541
- while (isNodeOfType(expression, "Identifier")) {
29542
- const symbol = scopes.symbolFor(expression);
29543
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
29544
- const initializer = getDirectConstInitializer(symbol);
29545
- if (!initializer) return false;
29546
- visitedSymbolIds.add(symbol.id);
29547
- expression = stripParenExpression(initializer);
29548
- }
29549
- if (!isNodeOfType(expression, "CallExpression")) return false;
29550
- if (!isReactApiCall(expression, "useRef", scopes, REACT_USE_REF_OPTIONS)) return false;
29551
- if (!isNodeOfType(expression.typeArguments, "TSTypeParameterInstantiation")) return false;
29552
- const typeArgument = expression.typeArguments.params[0];
29553
- return Boolean(typeArgument && isHtmlInputElementType(typeArgument, scopes));
29554
- };
29555
- const isProvenHtmlInputElement = (rawExpression, scopes) => {
29556
- const visitedSymbolIds = /* @__PURE__ */ new Set();
29557
- let expression = stripParenExpression(rawExpression);
29558
- while (true) {
29559
- if (isNodeOfType(expression, "MemberExpression") && !expression.computed && isNodeOfType(expression.property, "Identifier") && expression.property.name === "current") return hasTypedHtmlInputRefOrigin(expression.object, scopes);
29560
- if (!isNodeOfType(expression, "Identifier")) return false;
29561
- const symbol = scopes.symbolFor(expression);
29562
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
29563
- const typeAnnotation = getBindingTypeAnnotation(symbol);
29564
- if (typeAnnotation && isHtmlInputElementType(typeAnnotation, scopes)) return true;
29565
- const initializer = getDirectConstInitializer(symbol);
29566
- if (!initializer) return false;
29567
- visitedSymbolIds.add(symbol.id);
29568
- expression = stripParenExpression(initializer);
29569
- }
29570
- };
29571
- const getIndeterminateAttributeReceiver = (node) => {
29572
- const callee = stripParenExpression(node.callee);
29573
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "setAttribute" && callee.property.name !== "toggleAttribute") return null;
29574
- const attributeName = node.arguments[0];
29575
- if (!isNodeOfType(attributeName, "Literal") || attributeName.value !== "indeterminate") return null;
29576
- if (callee.property.name === "setAttribute") {
29577
- const attributeValue = node.arguments[1];
29578
- return attributeValue && !isNodeOfType(attributeValue, "SpreadElement") ? callee.object : null;
29579
- }
29580
- if (node.arguments.length === 1) return callee.object;
29581
- const forceArgument = node.arguments[1];
29582
- if (!forceArgument || isNodeOfType(forceArgument, "SpreadElement")) return null;
29583
- const forceExpression = stripParenExpression(forceArgument);
29584
- if (!isNodeOfType(forceExpression, "Literal") || forceExpression.value !== true) return null;
29585
- return callee.object;
29586
- };
29587
- const noIndeterminateAttribute = defineRule({
29588
- id: "no-indeterminate-attribute",
29589
- title: "Indeterminate checkbox state set as an attribute",
29590
- severity: "warn",
29591
- recommendation: "Assign the checkbox element's `indeterminate` DOM property, usually through a ref, because the HTML attribute does not control its visual state.",
29592
- create: (context) => {
29593
- if (classifyReactNativeFileTarget(context) === "react-native") return {};
29594
- return {
29595
- JSXOpeningElement(node) {
29596
- if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "input") return;
29597
- let typeAttribute = null;
29598
- let typeAttributeIndex = null;
29599
- let indeterminateAttribute = null;
29600
- let indeterminateAttributeIndex = null;
29601
- let lastSpreadIndex = null;
29602
- for (let attributeIndex = 0; attributeIndex < node.attributes.length; attributeIndex++) {
29603
- const attribute = node.attributes[attributeIndex];
29604
- if (isNodeOfType(attribute, "JSXSpreadAttribute")) {
29605
- lastSpreadIndex = attributeIndex;
29606
- continue;
29607
- }
29608
- if (!isNodeOfType(attribute, "JSXAttribute") || !isNodeOfType(attribute.name, "JSXIdentifier")) continue;
29609
- if (attribute.name.name === "type") {
29610
- typeAttribute = attribute;
29611
- typeAttributeIndex = attributeIndex;
29612
- } else if (attribute.name.name === "indeterminate") {
29613
- indeterminateAttribute = attribute;
29614
- indeterminateAttributeIndex = attributeIndex;
29615
- }
29616
- }
29617
- if (!typeAttribute || typeAttributeIndex === null || !indeterminateAttribute || indeterminateAttributeIndex === null) return;
29618
- if (lastSpreadIndex !== null && (lastSpreadIndex > typeAttributeIndex || lastSpreadIndex > indeterminateAttributeIndex)) return;
29619
- const inputTypeValues = getJsxPropStaticStringValues(typeAttribute, context.scopes);
29620
- if (!inputTypeValues || !inputTypeValues.every((inputTypeValue) => inputTypeValue.toLowerCase() === "checkbox")) return;
29621
- context.report({
29622
- node: indeterminateAttribute,
29623
- message: MESSAGE$21
29624
- });
29625
- },
29626
- CallExpression(node) {
29627
- const receiver = getIndeterminateAttributeReceiver(node);
29628
- if (!receiver) return;
29629
- if (!isProvenHtmlInputElement(receiver, context.scopes)) return;
29630
- context.report({
29631
- node,
29632
- message: MESSAGE$21
29633
- });
29634
- }
29635
- };
29636
- }
29637
- });
29638
- //#endregion
29639
28801
  //#region src/plugin/utils/contains-non-deterministic-source.ts
29640
28802
  const NON_DETERMINISTIC_MEMBER_CALLS = new Set([
29641
28803
  "Math.random",
@@ -31160,64 +30322,6 @@ const noManyBooleanProps = defineRule({
31160
30322
  }
31161
30323
  });
31162
30324
  //#endregion
31163
- //#region src/plugin/utils/is-global-match-media-call.ts
31164
- const isGlobalMatchMediaCall = (node, scopes) => {
31165
- if (!isNodeOfType(node, "CallExpression")) return false;
31166
- const callee = stripParenExpression(node.callee);
31167
- if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
31168
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
31169
- const receiver = stripParenExpression(callee.object);
31170
- return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
31171
- };
31172
- //#endregion
31173
- //#region src/plugin/rules/performance/no-match-media-in-state-initializer.ts
31174
- const REACT_USE_STATE_OPTIONS = {
31175
- allowGlobalReactNamespace: true,
31176
- allowUnboundBareCalls: false
31177
- };
31178
- const isReactUseStateCall = (node, context) => isReactApiCall(node, "useState", context.scopes, REACT_USE_STATE_OPTIONS);
31179
- const findDirectMatchMediaCall = (root, context) => {
31180
- let matchMediaCall = null;
31181
- walkAst(root, (visitedNode) => {
31182
- if (matchMediaCall) return false;
31183
- if (isFunctionLike$1(visitedNode)) return false;
31184
- if (isGlobalMatchMediaCall(visitedNode, context.scopes)) {
31185
- matchMediaCall = visitedNode;
31186
- return false;
31187
- }
31188
- });
31189
- return matchMediaCall;
31190
- };
31191
- const findMatchMediaCallDuringInitialization = (initializer, context) => {
31192
- const rootInitializer = stripParenExpression(initializer);
31193
- if (!isFunctionLike$1(rootInitializer)) return findDirectMatchMediaCall(rootInitializer, context);
31194
- if (rootInitializer.async || rootInitializer.generator) return null;
31195
- return findDirectMatchMediaCall(rootInitializer.body, context);
31196
- };
31197
- const noMatchMediaInStateInitializer = defineRule({
31198
- id: "no-match-media-in-state-initializer",
31199
- title: "matchMedia in state initializer",
31200
- severity: "warn",
31201
- category: "Correctness",
31202
- disabledWhen: ["vite", "cra"],
31203
- recommendation: "Prefer CSS media queries for layout, or subscribe with `useSyncExternalStore` and provide a stable server snapshot.",
31204
- create: (context) => {
31205
- if (isTestlikeFilename(context.filename)) return {};
31206
- if (classifyReactNativeFileTarget(context) === "react-native") return {};
31207
- return { CallExpression(node) {
31208
- if (!isReactUseStateCall(node, context)) return;
31209
- const initializer = node.arguments?.[0];
31210
- if (!initializer || initializer.type === "SpreadElement") return;
31211
- const matchMediaCall = findMatchMediaCallDuringInitialization(initializer, context);
31212
- if (!matchMediaCall) return;
31213
- context.report({
31214
- node: matchMediaCall,
31215
- message: "`matchMedia()` in a useState initializer can cause an SSR crash or seed different server and hydration state. Prefer CSS media queries for layout, or use `useSyncExternalStore` with a stable server snapshot."
31216
- });
31217
- } };
31218
- }
31219
- });
31220
- //#endregion
31221
30325
  //#region src/plugin/rules/state-and-effects/no-mirror-prop-effect.ts
31222
30326
  const getPropRootName = (expression, propNames) => {
31223
30327
  const rootName = getRootIdentifierName(expression, { followCallChains: true });
@@ -34001,7 +33105,7 @@ const createDeprecatedReactImportRule = ({ source, messages, handleExtraSource }
34001
33105
  for (const specifier of node.specifiers ?? []) {
34002
33106
  if (isNodeOfType(specifier, "ImportSpecifier")) {
34003
33107
  if (specifier.importKind === "type") continue;
34004
- const importedName = getImportedName(specifier);
33108
+ const importedName = getImportedName$1(specifier);
34005
33109
  if (!importedName) continue;
34006
33110
  const message = messages.get(importedName);
34007
33111
  if (message) context.report({
@@ -34054,7 +33158,7 @@ const buildTestUtilsMessage = (importedName) => {
34054
33158
  const reportTestUtilsImports = (node, context) => {
34055
33159
  for (const specifier of node.specifiers ?? []) {
34056
33160
  if (isNodeOfType(specifier, "ImportSpecifier")) {
34057
- const importedName = getImportedName(specifier) ?? "default";
33161
+ const importedName = getImportedName$1(specifier) ?? "default";
34058
33162
  context.report({
34059
33163
  node: specifier,
34060
33164
  message: buildTestUtilsMessage(importedName)
@@ -44114,7 +43218,7 @@ const rnNoDeprecatedModules = defineRule({
44114
43218
  for (const specifier of node.specifiers ?? []) {
44115
43219
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
44116
43220
  if (specifier.importKind === "type") continue;
44117
- const importedName = getImportedName(specifier);
43221
+ const importedName = getImportedName$1(specifier);
44118
43222
  if (!importedName) continue;
44119
43223
  if (!DEPRECATED_RN_MODULE_REPLACEMENTS.get(importedName)) continue;
44120
43224
  context.report({
@@ -44666,7 +43770,7 @@ const rnNoPanresponder = defineRule({
44666
43770
  for (const specifier of node.specifiers ?? []) {
44667
43771
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
44668
43772
  if (specifier.importKind === "type") continue;
44669
- if (getImportedName(specifier) !== "PanResponder") continue;
43773
+ if (getImportedName$1(specifier) !== "PanResponder") continue;
44670
43774
  context.report({
44671
43775
  node: specifier,
44672
43776
  message: "PanResponder runs gesture handling on the JS thread, which stutters under load. Use react-native-gesture-handler (`Gesture.Pan()`) so gestures run on the native UI thread."
@@ -45596,7 +44700,7 @@ const rnPreferExpoImage = defineRule({
45596
44700
  for (const specifier of node.specifiers ?? []) {
45597
44701
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
45598
44702
  if (specifier.importKind === "type") continue;
45599
- const importedName = getImportedName(specifier);
44703
+ const importedName = getImportedName$1(specifier);
45600
44704
  if (importedName !== "Image" && importedName !== "ImageBackground") continue;
45601
44705
  flaggedImports.push({
45602
44706
  localName: specifier.local.name,
@@ -45658,7 +44762,7 @@ const rnPreferPressable = defineRule({
45658
44762
  for (const specifier of node.specifiers ?? []) {
45659
44763
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
45660
44764
  if (specifier.importKind === "type") continue;
45661
- const importedName = getImportedName(specifier);
44765
+ const importedName = getImportedName$1(specifier);
45662
44766
  if (!importedName || !TOUCHABLE_COMPONENTS.has(importedName)) continue;
45663
44767
  context.report({
45664
44768
  node: specifier,
@@ -45760,7 +44864,7 @@ const rnPreferReanimated = defineRule({
45760
44864
  for (const specifier of node.specifiers ?? []) {
45761
44865
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
45762
44866
  if (specifier.importKind === "type") continue;
45763
- const importedName = getImportedName(specifier);
44867
+ const importedName = getImportedName$1(specifier);
45764
44868
  if (!importedName || !JS_THREAD_ANIMATION_IMPORTS.has(importedName)) continue;
45765
44869
  const suggestion = importedName === "LayoutAnimation" ? "Your users see stutter when LayoutAnimation runs on the JS thread." : "Your users see stutter when Animated from react-native runs on the JS thread.";
45766
44870
  context.report({
@@ -52394,7 +51498,7 @@ const useLazyMotion = defineRule({
52394
51498
  if (node.specifiers?.some((specifier) => {
52395
51499
  if (!isNodeOfType(specifier, "ImportSpecifier")) return false;
52396
51500
  if (specifier.importKind === "type") return false;
52397
- return getImportedName(specifier) === "motion";
51501
+ return getImportedName$1(specifier) === "motion";
52398
51502
  })) context.report({
52399
51503
  node,
52400
51504
  message: "Importing \"motion\" ships about 30 kb of extra code and slows page load. Use \"m\" with LazyMotion instead."
@@ -53421,18 +52525,6 @@ const reactDoctorRules = [
53421
52525
  requires: [...new Set(["react", ...displayName.requires ?? []])]
53422
52526
  }
53423
52527
  },
53424
- {
53425
- key: "react-doctor/effect-listener-cleanup-mismatch",
53426
- id: "effect-listener-cleanup-mismatch",
53427
- source: "react-doctor",
53428
- originallyExternal: false,
53429
- rule: {
53430
- ...effectListenerCleanupMismatch,
53431
- framework: "global",
53432
- category: "Bugs",
53433
- requires: [...new Set(["react", ...effectListenerCleanupMismatch.requires ?? []])]
53434
- }
53435
- },
53436
52528
  {
53437
52529
  key: "react-doctor/effect-needs-cleanup",
53438
52530
  id: "effect-needs-cleanup",
@@ -55108,17 +54200,6 @@ const reactDoctorRules = [
55108
54200
  requires: [...new Set(["react", ...noImgLazyWithHighFetchpriority.requires ?? []])]
55109
54201
  }
55110
54202
  },
55111
- {
55112
- key: "react-doctor/no-indeterminate-attribute",
55113
- id: "no-indeterminate-attribute",
55114
- source: "react-doctor",
55115
- originallyExternal: false,
55116
- rule: {
55117
- ...noIndeterminateAttribute,
55118
- framework: "global",
55119
- category: "Bugs"
55120
- }
55121
- },
55122
54203
  {
55123
54204
  key: "react-doctor/no-initialize-state",
55124
54205
  id: "no-initialize-state",
@@ -55313,18 +54394,6 @@ const reactDoctorRules = [
55313
54394
  category: "Maintainability"
55314
54395
  }
55315
54396
  },
55316
- {
55317
- key: "react-doctor/no-match-media-in-state-initializer",
55318
- id: "no-match-media-in-state-initializer",
55319
- source: "react-doctor",
55320
- originallyExternal: false,
55321
- rule: {
55322
- ...noMatchMediaInStateInitializer,
55323
- framework: "global",
55324
- category: "Bugs",
55325
- requires: [...new Set(["react", ...noMatchMediaInStateInitializer.requires ?? []])]
55326
- }
55327
- },
55328
54397
  {
55329
54398
  key: "react-doctor/no-mirror-prop-effect",
55330
54399
  id: "no-mirror-prop-effect",
@@ -58191,9 +57260,7 @@ const CROSS_FILE_RULE_IDS = new Set([
58191
57260
  "nextjs-no-use-search-params-without-suspense",
58192
57261
  "no-dynamic-import-path",
58193
57262
  "no-full-lodash-import",
58194
- "no-indeterminate-attribute",
58195
57263
  "no-locale-format-in-render",
58196
- "no-match-media-in-state-initializer",
58197
57264
  "no-mutating-reducer-state",
58198
57265
  "prefer-dynamic-import",
58199
57266
  "rendering-hydration-mismatch-time",
@@ -58328,9 +57395,7 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
58328
57395
  ["nextjs-no-use-search-params-without-suspense", collectNextjsSearchParamsDependencies],
58329
57396
  ["no-dynamic-import-path", collectNearestManifestDependencies],
58330
57397
  ["no-full-lodash-import", collectNearestManifestDependencies],
58331
- ["no-indeterminate-attribute", collectNearestManifestDependencies],
58332
57398
  ["no-locale-format-in-render", collectNearestManifestDependencies],
58333
- ["no-match-media-in-state-initializer", collectNearestManifestDependencies],
58334
57399
  ["no-mutating-reducer-state", collectMutatingReducerDependencies],
58335
57400
  ["prefer-dynamic-import", collectNearestManifestDependencies],
58336
57401
  ["rendering-hydration-mismatch-time", collectNearestManifestDependencies],