oxlint-plugin-react-doctor 0.4.2-dev.98066c2 → 0.4.2-dev.a254414

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 +348 -348
  2. package/dist/index.js +613 -437
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -719,7 +719,7 @@ const activityWrapsEffectHeavySubtree = defineRule({
719
719
  if (totalEffects === 0) return;
720
720
  context.report({
721
721
  node: openingElement,
722
- message: `Every hide & show rebuilds ${effectfulChildren.join(", ")} from scratch because <Activity> wraps them & they use ${totalEffects} effect hook${totalEffects === 1 ? "" : "s"}.`
722
+ message: `Every hide and show rebuilds ${effectfulChildren.join(", ")} from scratch because <Activity> wraps components with ${totalEffects} effect hook${totalEffects === 1 ? "" : "s"}.`
723
723
  });
724
724
  }
725
725
  };
@@ -1414,12 +1414,12 @@ const objectHasAccessibleChild = (jsxElement, settings) => {
1414
1414
  };
1415
1415
  //#endregion
1416
1416
  //#region src/plugin/rules/a11y/alt-text.ts
1417
- const MISSING_ALT_PROP = "Blind users can't use this image because screen readers skip it without `alt`, so add `alt=\"...\"` (or `alt=\"\"` if decorative).";
1418
- const MISSING_ALT_VALUE = "Blind users can't use this image because its `alt` is empty or invalid, so add a short description (or `alt=\"\"` if decorative).";
1419
- const ARIA_LABEL_VALUE = "Blind users hear nothing here because `aria-label` has no value, so give it a short description.";
1420
- const ARIA_LABELLEDBY_VALUE = "Blind users hear nothing here because `aria-labelledby` has no value, so point it at the id of the text that labels this.";
1417
+ const MISSING_ALT_PROP = "Screen reader users cannot access this image without `alt`. Add `alt=\"image_description\"`, or `alt=\"\"` if it is decorative.";
1418
+ const MISSING_ALT_VALUE = "Screen reader users cannot access this image because its `alt` is empty or invalid. Add a short description, or `alt=\"\"` if it is decorative.";
1419
+ const ARIA_LABEL_VALUE = "Screen reader users hear nothing here because `aria-label` has no value, so give it a short description.";
1420
+ const ARIA_LABELLEDBY_VALUE = "Screen reader users hear nothing here because `aria-labelledby` has no value, so point it at the id of the text that labels this.";
1421
1421
  const PREFER_ALT = "Screen readers skip a decorative image more reliably with `alt=\"\"` than `role=\"presentation\"`, so use `alt=\"\"` instead.";
1422
- const MESSAGE_OBJECT = "Blind users can't use this `<object>` because screen readers can't describe it, so add `alt`, `aria-label`, `aria-labelledby`, `title`, or inner fallback text.";
1422
+ const MESSAGE_OBJECT = "Screen reader users cannot use this `<object>` because assistive tech cannot describe it, so add `alt`, `aria-label`, `aria-labelledby`, `title`, or inner fallback text.";
1423
1423
  const MESSAGE_AREA = "Blind users can't use this `<area>` of the image map because screen readers can't describe it, so add `alt`, `aria-label`, or `aria-labelledby`.";
1424
1424
  const MESSAGE_INPUT_IMAGE = "Blind users can't use this image button because screen readers can't describe it, so add `alt`, `aria-label`, or `aria-labelledby`.";
1425
1425
  const resolveSettings$52 = (settings) => {
@@ -2748,7 +2748,7 @@ const ABSTRACT_ROLES = new Set([
2748
2748
  const PRESENTATION_ROLES$2 = new Set(["presentation", "none"]);
2749
2749
  //#endregion
2750
2750
  //#region src/plugin/rules/a11y/aria-role.ts
2751
- const buildBaseMessage = (suffix) => `Screen reader users get no help from this \`role\` because it isn't a valid ARIA role, so use a real, non-abstract role.${suffix}`;
2751
+ const buildBaseMessage = (suffix) => `This \`role\` is not a valid ARIA role, so assistive tech cannot expose it correctly. Use a real, non-abstract role.${suffix}`;
2752
2752
  const resolveSettings$49 = (settings) => {
2753
2753
  const reactDoctor = settings?.["react-doctor"];
2754
2754
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.ariaRole ?? {} : {};
@@ -2762,7 +2762,7 @@ const ariaRole = defineRule({
2762
2762
  title: "Invalid ARIA role",
2763
2763
  tags: ["react-jsx-only"],
2764
2764
  severity: "error",
2765
- recommendation: "Use a real, non-abstract ARIA role.",
2765
+ recommendation: "Use a real, non-abstract ARIA role so assistive tech can expose the element correctly.",
2766
2766
  category: "Accessibility",
2767
2767
  create: (context) => {
2768
2768
  const settings = resolveSettings$49(context.settings);
@@ -3557,7 +3557,7 @@ const asyncDeferAwait = defineRule({
3557
3557
  title: "await before an early-return guard",
3558
3558
  severity: "warn",
3559
3559
  tags: ["test-noise"],
3560
- recommendation: "Move the `await` below the early-return guard so the skip path stays fast",
3560
+ recommendation: "Move the `await` below the early-return guard so the skip path stays fast and avoids unnecessary async work.",
3561
3561
  create: (context) => {
3562
3562
  const inspectStatements = (statements) => {
3563
3563
  for (let statementIndex = 0; statementIndex < statements.length - 1; statementIndex++) {
@@ -3800,7 +3800,7 @@ const autocompleteValid = defineRule({
3800
3800
  title: "Invalid autocomplete value",
3801
3801
  tags: ["react-jsx-only"],
3802
3802
  severity: "warn",
3803
- recommendation: "Use a valid autofill token in `autoComplete`.",
3803
+ recommendation: "Use a valid autofill token in `autoComplete` so browsers can fill the right field reliably.",
3804
3804
  category: "Accessibility",
3805
3805
  create: (context) => {
3806
3806
  const settings = resolveSettings$48(context.settings);
@@ -3855,7 +3855,7 @@ const isCreateElementCall = (node) => {
3855
3855
  //#endregion
3856
3856
  //#region src/plugin/rules/react-builtins/button-has-type.ts
3857
3857
  const MISSING_MESSAGE$2 = "Your users can submit the form by accident because a `<button>` with no `type` defaults to submit.";
3858
- const INVALID_MESSAGE = "This button's `type` is invalid.";
3858
+ const INVALID_MESSAGE = "This button has an invalid `type`, so the browser may treat it like a submit button.";
3859
3859
  const resolveSettings$47 = (settings) => {
3860
3860
  const reactDoctor = settings?.["react-doctor"];
3861
3861
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.buttonHasType ?? {} : {};
@@ -3896,7 +3896,7 @@ const buttonHasType = defineRule({
3896
3896
  id: "button-has-type",
3897
3897
  title: "Button missing explicit type",
3898
3898
  severity: "warn",
3899
- recommendation: "Always set a `type` on a `<button>`: `type=\"button\"`, `\"submit\"`, or `\"reset\"`.",
3899
+ recommendation: "Set an explicit button `type` so plain buttons do not submit forms by accident: `type=\"button\"`, `\"submit\"`, or `\"reset\"`.",
3900
3900
  create: (context) => {
3901
3901
  const settings = resolveSettings$47(context.settings);
3902
3902
  const isTestlikeFile = isTestlikeFilename(context.filename);
@@ -3966,7 +3966,7 @@ const buttonHasType = defineRule({
3966
3966
  //#endregion
3967
3967
  //#region src/plugin/rules/react-builtins/checked-requires-onchange-or-readonly.ts
3968
3968
  const MISSING_MESSAGE$1 = "Your users can't toggle this input because `checked` has no `onChange`.";
3969
- const EXCLUSIVE_MESSAGE = "This input behaves unpredictably with both `checked` & `defaultChecked` set.";
3969
+ const EXCLUSIVE_MESSAGE = "This input mixes `checked` with `defaultChecked`, so React can't tell whether it is controlled or uncontrolled.";
3970
3970
  const resolveSettings$46 = (settings) => {
3971
3971
  const reactDoctor = settings?.["react-doctor"];
3972
3972
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.checkedRequiresOnchangeOrReadonly ?? {} : {};
@@ -4017,7 +4017,7 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
4017
4017
  id: "checked-requires-onchange-or-readonly",
4018
4018
  title: "Checked input without onChange",
4019
4019
  severity: "warn",
4020
- recommendation: "Add `onChange` (controlled) or `readOnly` (display-only), or use `defaultChecked` for an uncontrolled checkbox.",
4020
+ recommendation: "Add `onChange`, `readOnly`, or `defaultChecked` so React knows whether the checkbox is editable, display-only, or uncontrolled.",
4021
4021
  category: "Correctness",
4022
4022
  create: (context) => {
4023
4023
  const settings = resolveSettings$46(context.settings);
@@ -4511,7 +4511,7 @@ const noRedundantPaddingAxes = defineRule({
4511
4511
  severity: "warn",
4512
4512
  defaultEnabled: false,
4513
4513
  category: "Architecture",
4514
- recommendation: "Collapse `px-N py-N` to `p-N` when both sides match. Keep them split only when one side changes at a breakpoint (`py-2 md:py-3`).",
4514
+ recommendation: "Collapse matching padding axes to `p-N` so duplicated classes do not make spacing harder to scan; keep split axes only when breakpoints differ.",
4515
4515
  create: (context) => ({ JSXAttribute(jsxAttribute) {
4516
4516
  if (!isNodeOfType(jsxAttribute.name, "JSXIdentifier") || jsxAttribute.name.name !== "className") return;
4517
4517
  const classNameLiteral = getClassNameLiteral(jsxAttribute);
@@ -4521,7 +4521,7 @@ const noRedundantPaddingAxes = defineRule({
4521
4521
  if (matchedPairs.length === 0) return;
4522
4522
  for (const matchedPair of matchedPairs) context.report({
4523
4523
  node: jsxAttribute,
4524
- message: `px-${matchedPair.value} & py-${matchedPair.value} are the same.`
4524
+ message: `px-${matchedPair.value} and py-${matchedPair.value} duplicate p-${matchedPair.value}, so the class list is noisier without changing spacing.`
4525
4525
  });
4526
4526
  } })
4527
4527
  });
@@ -4535,7 +4535,7 @@ const noRedundantSizeAxes = defineRule({
4535
4535
  severity: "warn",
4536
4536
  defaultEnabled: false,
4537
4537
  category: "Architecture",
4538
- recommendation: "Collapse `w-N h-N` to `size-N` (Tailwind v3.4+) when both sides match.",
4538
+ recommendation: "Collapse matching width and height to `size-N` so duplicated classes do not make layout harder to scan.",
4539
4539
  create: (context) => ({ JSXAttribute(jsxAttribute) {
4540
4540
  if (!isNodeOfType(jsxAttribute.name, "JSXIdentifier") || jsxAttribute.name.name !== "className") return;
4541
4541
  const classNameLiteral = getClassNameLiteral(jsxAttribute);
@@ -4545,7 +4545,7 @@ const noRedundantSizeAxes = defineRule({
4545
4545
  if (matchedPairs.length === 0) return;
4546
4546
  for (const matchedPair of matchedPairs) context.report({
4547
4547
  node: jsxAttribute,
4548
- message: `w-${matchedPair.value} & h-${matchedPair.value} are the same.`
4548
+ message: `w-${matchedPair.value} and h-${matchedPair.value} duplicate size-${matchedPair.value}, so the class list is noisier without changing layout.`
4549
4549
  });
4550
4550
  } })
4551
4551
  });
@@ -4580,7 +4580,7 @@ const noSpaceOnFlexChildren = defineRule({
4580
4580
  const spaceValue = spaceMatch[2];
4581
4581
  context.report({
4582
4582
  node: jsxAttribute,
4583
- message: `space-${spaceAxis}-${spaceValue} on a flex or grid parent breaks spacing for your users.`
4583
+ message: `space-${spaceAxis}-${spaceValue} on a flex or grid parent can leave uneven gaps when children hide, wrap, or render in RTL layouts.`
4584
4584
  });
4585
4585
  } })
4586
4586
  });
@@ -4593,14 +4593,14 @@ const noThreePeriodEllipsis = defineRule({
4593
4593
  severity: "warn",
4594
4594
  defaultEnabled: false,
4595
4595
  category: "Architecture",
4596
- recommendation: "Use the real ellipsis \"…\" (or `&hellip;`) instead of three dots. Good for labels like \"Rename…\" and \"Loading…\".",
4596
+ recommendation: "Use the real ellipsis character (\"…\") so UI labels look polished and consistent instead of like three separate periods.",
4597
4597
  create: (context) => ({ JSXText(jsxTextNode) {
4598
4598
  const textValue = typeof jsxTextNode.value === "string" ? jsxTextNode.value : "";
4599
4599
  if (!TRAILING_THREE_PERIOD_ELLIPSIS_PATTERN.test(textValue)) return;
4600
4600
  if (isInsideExcludedTypographyAncestor(jsxTextNode)) return;
4601
4601
  context.report({
4602
4602
  node: jsxTextNode,
4603
- message: "Three dots (\"...\") look unpolished to your users."
4603
+ message: "Use the real ellipsis character (\"…\") instead of three period characters."
4604
4604
  });
4605
4605
  } })
4606
4606
  });
@@ -4662,7 +4662,7 @@ const noVagueButtonLabel = defineRule({
4662
4662
  if (!VAGUE_BUTTON_LABELS.has(normalizedLabel)) return;
4663
4663
  context.report({
4664
4664
  node: jsxElementNode.openingElement ?? jsxElementNode,
4665
- message: `Screen reader & unsure users can't tell what "${labelText}" does.`
4665
+ message: `Screen reader users may not know what "${labelText}" does. Use a specific action label.`
4666
4666
  });
4667
4667
  } })
4668
4668
  });
@@ -5248,10 +5248,10 @@ const effectNeedsCleanup = defineRule({
5248
5248
  if (usages.length === 0) return;
5249
5249
  if (effectHasCleanupReturn(callback, usages)) return;
5250
5250
  const firstUsage = usages[0];
5251
- const verb = firstUsage.kind === "timer" ? "schedules" : "subscribes via";
5251
+ const resourceKind = firstUsage.kind === "timer" ? "timer" : "subscription";
5252
5252
  context.report({
5253
5253
  node,
5254
- message: `\`${firstUsage.resourceName}(...)\` leaks memory because useEffect ${verb} it but never cleans it up.`
5254
+ message: `\`${firstUsage.resourceName}\` creates a ${resourceKind} in useEffect without returning cleanup. Return a cleanup function so it does not leak after unmount.`
5255
5255
  });
5256
5256
  } })
5257
5257
  });
@@ -5899,21 +5899,21 @@ const isOutsideAllFunctions = (symbol) => {
5899
5899
  //#region src/plugin/rules/react-builtins/exhaustive-deps-messages.ts
5900
5900
  const buildMissingDepMessage = (hookName, depName) => `\`${hookName}\` can run with a stale \`${depName}\` & show your users old data.`;
5901
5901
  const buildUnnecessaryDepMessage = (hookName, depName) => `\`${hookName}\` re-runs whenever \`${depName}\` changes even though it never uses it.`;
5902
- const buildDuplicateDepMessage = (hookName, depName) => `\`${hookName}\` lists \`${depName}\` twice in its dependency array.`;
5903
- const buildLiteralDepMessage = (hookName) => `A literal in \`${hookName}\`'s dependency array never changes & does nothing.`;
5902
+ const buildDuplicateDepMessage = (hookName, depName) => `\`${hookName}\` lists \`${depName}\` twice, adding dependency-array noise without changing when it runs.`;
5903
+ const buildLiteralDepMessage = (hookName) => `A literal in \`${hookName}\`'s dependency array never changes, so it adds noise without protecting against stale values.`;
5904
5904
  const buildRefCurrentDepMessage = (hookName, depName) => `\`${hookName}\` won't re-run when \`${depName}\` changes, since a ref never triggers a redraw.`;
5905
- const buildNonArrayDepsMessage = (hookName) => `\`${hookName}\`'s dependencies can't be checked because its second argument isn't an inline array.`;
5905
+ const buildNonArrayDepsMessage = (hookName) => `\`${hookName}\`'s dependencies can't be checked because its second argument isn't an inline array, so stale values can slip through.`;
5906
5906
  const buildMissingDepArrayMessage = (hookName) => `\`${hookName}\` re-runs on every render with no dependency array.`;
5907
5907
  const buildMissingCallbackMessage = (hookName) => `\`${hookName}\` crashes without a function as its first argument.`;
5908
- const buildEffectEventDepMessage = () => `A function from \`useEffectEvent\` is stable & shouldn't sit in the dependency array.`;
5909
- const buildSpreadDepMessage = (hookName) => `\`${hookName}\`'s dependencies can't be checked because of a spread in the array.`;
5910
- const buildComplexDepMessage = (hookName) => `\`${hookName}\`'s dependencies can't be checked because of a complex expression in the array.`;
5908
+ const buildEffectEventDepMessage = () => `A function from \`useEffectEvent\` is stable, so listing it adds noise and defeats the event/dependency split.`;
5909
+ const buildSpreadDepMessage = (hookName) => `A spread in \`${hookName}\`'s dependency array hides the actual deps, so stale values can slip through.`;
5910
+ const buildComplexDepMessage = (hookName) => `A complex expression in \`${hookName}\`'s dependency array hides the real value, so stale values can slip through.`;
5911
5911
  const buildAsyncEffectMessage = (hookName) => `\`${hookName}\` was given an async function, so its cleanup breaks.`;
5912
- const buildUnknownCallbackMessage = (hookName) => `\`${hookName}\`'s dependencies can't be checked because its function is defined elsewhere.`;
5912
+ const buildUnknownCallbackMessage = (hookName) => `\`${hookName}\`'s callback is defined elsewhere, so dependencies can't be checked and stale values can slip through.`;
5913
5913
  const buildUnstableDepMessage = (hookName, depName) => `\`${depName}\` is rebuilt every render, so \`${hookName}\` runs every time.`;
5914
5914
  const buildSetStateWithoutDepsMessage = (hookName, setterName) => `\`${hookName}\` calls \`${setterName}\` with no dependency array, so it can loop forever & freeze the component.`;
5915
5915
  const buildRefCleanupMessage = (depName) => `Your cleanup may read the wrong node since the ref \`${depName}\` can change before it runs.`;
5916
- const buildAssignmentMessage = (name) => `Assigning to \`${name}\` inside a hook is thrown away after each render.`;
5916
+ const buildAssignmentMessage = (name) => `Assigning to \`${name}\` inside a hook is thrown away after each render, so the next render reads the old value.`;
5917
5917
  //#endregion
5918
5918
  //#region src/plugin/rules/react-builtins/exhaustive-deps-settings.ts
5919
5919
  const resolveExhaustiveDepsSettings = (settings) => {
@@ -6816,13 +6816,13 @@ const flattenJsxName = (name) => {
6816
6816
  return "";
6817
6817
  };
6818
6818
  const isSupportedJsxName = (name) => isNodeOfType(name, "JSXIdentifier") || isNodeOfType(name, "JSXMemberExpression");
6819
- const buildMessage$24 = (propName, message) => message ?? `Your project blocks the \`${propName}\` prop on this component.`;
6819
+ const buildMessage$24 = (propName, message) => message ?? `Your project blocks the \`${propName}\` prop on this component, so this bypasses the component API contract.`;
6820
6820
  const forbidComponentProps = defineRule({
6821
6821
  id: "forbid-component-props",
6822
- title: "Forbidden prop on component",
6822
+ title: "Blocked component prop bypasses API contract",
6823
6823
  severity: "warn",
6824
6824
  defaultEnabled: false,
6825
- recommendation: "List the props you want to block per component in the `forbidComponentProps.forbid` setting.",
6825
+ recommendation: "Configure blocked component props so callers cannot bypass the component API contract.",
6826
6826
  category: "Architecture",
6827
6827
  create: (context) => {
6828
6828
  const entries = resolveSettings$43(context.settings).forbid.map(normalizeEntry);
@@ -6853,7 +6853,7 @@ const forbidComponentProps = defineRule({
6853
6853
  });
6854
6854
  //#endregion
6855
6855
  //#region src/plugin/rules/react-builtins/forbid-dom-props.ts
6856
- const buildMessage$23 = (propName, customMessage) => customMessage ?? `Your project blocks the \`${propName}\` prop on plain HTML tags.`;
6856
+ const buildMessage$23 = (propName, customMessage) => customMessage ?? `Your project blocks the \`${propName}\` prop on plain HTML tags, so this bypasses the agreed DOM API contract.`;
6857
6857
  const resolveSettings$42 = (settings) => {
6858
6858
  const reactDoctor = settings?.["react-doctor"];
6859
6859
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.forbidDomProps ?? {} : {};
@@ -6871,9 +6871,9 @@ const resolveSettings$42 = (settings) => {
6871
6871
  };
6872
6872
  const forbidDomProps = defineRule({
6873
6873
  id: "forbid-dom-props",
6874
- title: "Forbidden DOM prop used",
6874
+ title: "Blocked DOM prop bypasses project contract",
6875
6875
  severity: "warn",
6876
- recommendation: "List the HTML props you want to block in the `forbidDomProps.forbid` setting.",
6876
+ recommendation: "Configure blocked DOM props so plain HTML tags stay on the agreed DOM API surface.",
6877
6877
  category: "Architecture",
6878
6878
  create: (context) => {
6879
6879
  const forbidMap = resolveSettings$42(context.settings);
@@ -6934,7 +6934,7 @@ const isReactFunctionCall = (node, expectedCall) => {
6934
6934
  };
6935
6935
  //#endregion
6936
6936
  //#region src/plugin/rules/react-builtins/forbid-elements.ts
6937
- const buildMessage$22 = (element, customHelp) => customHelp ? `Your project blocks \`<${element}>\` here. ${customHelp}` : `Your project blocks \`<${element}>\` here.`;
6937
+ const buildMessage$22 = (element, customHelp) => customHelp ? `Your project blocks \`<${element}>\` here. ${customHelp}` : `Your project blocks \`<${element}>\` here, so code stays on the approved UI surface.`;
6938
6938
  const resolveSettings$41 = (settings) => {
6939
6939
  const reactDoctor = settings?.["react-doctor"];
6940
6940
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.forbidElements ?? {} : {};
@@ -6946,9 +6946,9 @@ const resolveSettings$41 = (settings) => {
6946
6946
  const flattenMemberName$1 = flattenCalleeName;
6947
6947
  const forbidElements = defineRule({
6948
6948
  id: "forbid-elements",
6949
- title: "Forbidden element used",
6949
+ title: "Blocked element bypasses approved UI primitives",
6950
6950
  severity: "warn",
6951
- recommendation: "List the element names you want to block in the `forbidElements.forbid` setting.",
6951
+ recommendation: "Configure blocked elements so code stays on the approved UI primitives.",
6952
6952
  category: "Architecture",
6953
6953
  create: (context) => {
6954
6954
  const forbidMap = resolveSettings$41(context.settings);
@@ -6992,7 +6992,7 @@ const forwardRefUsesRef = defineRule({
6992
6992
  id: "forward-ref-uses-ref",
6993
6993
  title: "forwardRef without ref parameter",
6994
6994
  severity: "warn",
6995
- recommendation: "Either accept a `ref` parameter, or drop the `forwardRef` wrapper.",
6995
+ recommendation: "Accept the `ref` parameter or drop `forwardRef` so parents are not promised a ref that never reaches the node.",
6996
6996
  category: "Architecture",
6997
6997
  create: (context) => ({ CallExpression(node) {
6998
6998
  if (getCalleeName$1(node) !== "forwardRef") return;
@@ -7053,8 +7053,8 @@ const headingHasContent = defineRule({
7053
7053
  });
7054
7054
  //#endregion
7055
7055
  //#region src/plugin/rules/react-builtins/hook-use-state.ts
7056
- const REQUIRE_DESTRUCTURE_MESSAGE = "This `useState` result is hard to follow.";
7057
- const NAMING_CONVENTION_MESSAGE = "The setter here is hard to spot.";
7056
+ const REQUIRE_DESTRUCTURE_MESSAGE = "`useState` should be destructured as `[value, setValue]` so readers can see the state value and setter together.";
7057
+ const NAMING_CONVENTION_MESSAGE = "This `useState` setter does not match its value name, so updates are harder to trace.";
7058
7058
  const resolveSettings$39 = (settings) => {
7059
7059
  const reactDoctor = settings?.["react-doctor"];
7060
7060
  return { allowDestructuredState: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.hookUseState ?? {} : {}).allowDestructuredState ?? false };
@@ -7079,7 +7079,7 @@ const hookUseState = defineRule({
7079
7079
  title: "useState not destructured",
7080
7080
  severity: "warn",
7081
7081
  defaultEnabled: false,
7082
- recommendation: "Destructure useState as `const [thing, setThing] = useState(…)`.",
7082
+ recommendation: "Destructure `useState` as `const [thing, setThing] = useState(…)` so state reads and writes stay visible together.",
7083
7083
  category: "Architecture",
7084
7084
  create: (context) => {
7085
7085
  const { allowDestructuredState } = resolveSettings$39(context.settings);
@@ -7157,7 +7157,7 @@ const HOOKS_WITH_DEP_ARRAY = new Set([
7157
7157
  "useMemo",
7158
7158
  "useImperativeHandle"
7159
7159
  ]);
7160
- const NAN_MESSAGE = "`NaN` in a dependency array silently breaks your hook, since React never sees it change.";
7160
+ const NAN_MESSAGE = "`NaN` in a dependency array never compares as changed with `Object.is`, so normalize the value before passing it as a dependency.";
7161
7161
  const isNanLiteral = (node) => {
7162
7162
  if (isNodeOfType(node, "Identifier") && node.name === "NaN") return true;
7163
7163
  if (isNodeOfType(node, "MemberExpression") && !node.computed && isNodeOfType(node.object, "Identifier") && node.object.name === "Number" && isNodeOfType(node.property, "Identifier") && node.property.name === "NaN") return true;
@@ -7460,7 +7460,7 @@ const htmlNoNestedInteractive = defineRule({
7460
7460
  });
7461
7461
  //#endregion
7462
7462
  //#region src/plugin/rules/a11y/iframe-has-title.ts
7463
- const MESSAGE$43 = "Blind users can't tell what this `<iframe>` holds because screen readers have no title to read, so add a `title` describing its content.";
7463
+ const MESSAGE$43 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
7464
7464
  const evaluateTitleValue = (value) => {
7465
7465
  if (!value) return "missing";
7466
7466
  if (isNodeOfType(value, "Literal")) {
@@ -7490,7 +7490,7 @@ const iframeHasTitle = defineRule({
7490
7490
  title: "iframe missing title",
7491
7491
  tags: ["react-jsx-only"],
7492
7492
  severity: "warn",
7493
- recommendation: "Add a descriptive `title` to every `<iframe>`.",
7493
+ recommendation: "Add a descriptive `title` so screen reader users know what the embedded frame contains.",
7494
7494
  category: "Accessibility",
7495
7495
  create: (context) => ({ JSXOpeningElement(node) {
7496
7496
  const tag = getElementType(node, context.settings);
@@ -7558,7 +7558,7 @@ const iframeMissingSandbox = defineRule({
7558
7558
  id: "iframe-missing-sandbox",
7559
7559
  title: "iframe missing sandbox attribute",
7560
7560
  severity: "warn",
7561
- recommendation: "Add `sandbox=\"\"` (or a curated value) to your iframe.",
7561
+ recommendation: "Add `sandbox=\"\"` or a curated value so embedded pages cannot get full access to your site by default.",
7562
7562
  category: "Security",
7563
7563
  create: (context) => ({
7564
7564
  JSXOpeningElement(node) {
@@ -7766,7 +7766,7 @@ const interactiveSupportsFocus = defineRule({
7766
7766
  title: "Interactive element not focusable",
7767
7767
  tags: ["react-jsx-only"],
7768
7768
  severity: "warn",
7769
- recommendation: "Add `tabIndex` to elements with interactive roles and handlers.",
7769
+ recommendation: "Add keyboard focus support so users can reach interactive elements without a pointer.",
7770
7770
  category: "Accessibility",
7771
7771
  create: (context) => {
7772
7772
  const settings = resolveSettings$36(context.settings);
@@ -8584,7 +8584,7 @@ const jsHoistRegexp = defineRule({
8584
8584
  create: (context) => createLoopAwareVisitors({ NewExpression(node) {
8585
8585
  if (isNodeOfType(node.callee, "Identifier") && node.callee.name === "RegExp") context.report({
8586
8586
  node,
8587
- message: "This slows the loop because new RegExp() rebuilds the pattern every pass, so move it to a constant outside the loop"
8587
+ message: "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop."
8588
8588
  });
8589
8589
  } })
8590
8590
  });
@@ -8958,7 +8958,7 @@ const jsSetMapLookups = defineRule({
8958
8958
  if (isIndexedArrayElementWithStringArgument(node.callee.object, node.arguments?.[0])) return;
8959
8959
  context.report({
8960
8960
  node,
8961
- message: `This gets slow because array.${methodName}() inside a loop scans the whole list every time, so use a Set for instant lookups`
8961
+ message: `This scales poorly because \`array.${methodName}()\` inside a loop scans the whole list every time. Use a Set for constant-time lookups.`
8962
8962
  });
8963
8963
  } })
8964
8964
  });
@@ -8969,7 +8969,7 @@ const jsTosortedImmutable = defineRule({
8969
8969
  title: "Spread copy before sort()",
8970
8970
  tags: ["test-noise"],
8971
8971
  severity: "warn",
8972
- disabledBy: ["react-native"],
8972
+ disabledBy: ["react-native", "pre-es2023"],
8973
8973
  recommendation: "Use `array.toSorted()` (ES2023) instead of `[...array].sort()` so you sort without copying the array first",
8974
8974
  create: (context) => ({ CallExpression(node) {
8975
8975
  if (!isMemberProperty(node.callee, "sort")) return;
@@ -8982,9 +8982,9 @@ const jsTosortedImmutable = defineRule({
8982
8982
  });
8983
8983
  //#endregion
8984
8984
  //#region src/plugin/rules/react-builtins/jsx-boolean-value.ts
8985
- const NEVER_MESSAGE$2 = () => `This prop is written inconsistently.`;
8986
- const ALWAYS_MESSAGE$2 = () => `This prop is written inconsistently.`;
8987
- const FALSE_OMITTED_MESSAGE = (attributeName) => `\`${attributeName}={false}\` does nothing.`;
8985
+ const NEVER_MESSAGE$2 = () => "This boolean prop style disagrees with the project setting, so equivalent true props are harder to scan consistently.";
8986
+ const ALWAYS_MESSAGE$2 = () => "This boolean prop style disagrees with the project setting, so equivalent true props are harder to scan consistently.";
8987
+ const FALSE_OMITTED_MESSAGE = (attributeName) => `\`${attributeName}={false}\` does nothing, so the explicit false value adds noise without changing output.`;
8988
8988
  const resolveSettings$35 = (settings) => {
8989
8989
  const reactDoctor = settings?.["react-doctor"];
8990
8990
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxBooleanValue ?? {} : {};
@@ -9000,7 +9000,7 @@ const jsxBooleanValue = defineRule({
9000
9000
  title: "Inconsistent boolean prop notation",
9001
9001
  severity: "warn",
9002
9002
  defaultEnabled: false,
9003
- recommendation: "Pick a single boolean-attribute style across the codebase (default: omit `={true}`).",
9003
+ recommendation: "Use one boolean-attribute style so equivalent true props scan the same across the codebase.",
9004
9004
  category: "Architecture",
9005
9005
  create: (context) => {
9006
9006
  const settings = resolveSettings$35(context.settings);
@@ -9054,8 +9054,8 @@ const jsxBooleanValue = defineRule({
9054
9054
  });
9055
9055
  //#endregion
9056
9056
  //#region src/plugin/rules/react-builtins/jsx-curly-brace-presence.ts
9057
- const UNNECESSARY_BRACES_MESSAGE = "These curly braces do nothing here.";
9058
- const REQUIRED_BRACES_MESSAGE = "This value needs curly braces `{ }` to read as an expression.";
9057
+ const UNNECESSARY_BRACES_MESSAGE = "These curly braces wrap a literal value, so they add JSX noise without changing output.";
9058
+ const REQUIRED_BRACES_MESSAGE = "This JSX value needs `{ }` so React reads it as an expression instead of text.";
9059
9059
  const isAllowedMode = (value) => value === "always" || value === "never" || value === "ignore";
9060
9060
  const resolveSettings$34 = (settings) => {
9061
9061
  const reactDoctor = settings?.["react-doctor"];
@@ -9150,7 +9150,7 @@ const jsxCurlyBracePresence = defineRule({
9150
9150
  title: "Unnecessary curly braces in JSX",
9151
9151
  severity: "warn",
9152
9152
  defaultEnabled: false,
9153
- recommendation: "Pick a consistent quoting style for JSX literal values.",
9153
+ recommendation: "Use one JSX literal style so equivalent markup scans the same without extra JSX noise.",
9154
9154
  category: "Architecture",
9155
9155
  create: (context) => {
9156
9156
  const settings = resolveSettings$34(context.settings);
@@ -9234,8 +9234,8 @@ const containsJsxElement = (root) => {
9234
9234
  };
9235
9235
  //#endregion
9236
9236
  //#region src/plugin/rules/react-builtins/jsx-filename-extension.ts
9237
- const JSX_NOT_ALLOWED = (extension) => `This file has JSX but a \`${extension}\` name.`;
9238
- const EXTENSION_ONLY_FOR_JSX = (extension) => `\`${extension}\` files are meant for JSX, but this one has none.`;
9237
+ const JSX_NOT_ALLOWED = (extension) => `This file contains JSX but uses a \`${extension}\` name, so the filename no longer signals JSX to readers or tooling conventions.`;
9238
+ const EXTENSION_ONLY_FOR_JSX = (extension) => `\`${extension}\` files are reserved for JSX here, so using that extension without JSX makes file-type conventions less useful.`;
9239
9239
  const resolveSettings$33 = (settings) => {
9240
9240
  const reactDoctor = settings?.["react-doctor"];
9241
9241
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxFilenameExtension ?? {} : {};
@@ -9255,7 +9255,7 @@ const jsxFilenameExtension = defineRule({
9255
9255
  title: "JSX in disallowed file extension",
9256
9256
  severity: "warn",
9257
9257
  defaultEnabled: false,
9258
- recommendation: "Name files with JSX `.jsx` or `.tsx`, or whatever extension your project uses.",
9258
+ recommendation: "Name JSX files with the configured extension so file names accurately signal which files contain JSX.",
9259
9259
  category: "Architecture",
9260
9260
  create: (context) => {
9261
9261
  const settings = resolveSettings$33(context.settings);
@@ -9310,8 +9310,8 @@ const isJsxFragmentElement = (node) => {
9310
9310
  };
9311
9311
  //#endregion
9312
9312
  //#region src/plugin/rules/react-builtins/jsx-fragments.ts
9313
- const SYNTAX_MESSAGE = "This fragment is written inconsistently.";
9314
- const ELEMENT_MESSAGE = "This fragment is written inconsistently.";
9313
+ const SYNTAX_MESSAGE = "`<React.Fragment>` is used where shorthand fragments are configured, so similar wrappers look different across the codebase.";
9314
+ const ELEMENT_MESSAGE = "Fragment shorthand is used where explicit fragments are configured, so similar wrappers look different across the codebase.";
9315
9315
  const resolveSettings$32 = (settings) => {
9316
9316
  const reactDoctor = settings?.["react-doctor"];
9317
9317
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxFragments ?? {} : {}).mode ?? "syntax" };
@@ -9321,7 +9321,7 @@ const jsxFragments = defineRule({
9321
9321
  title: "Inconsistent fragment syntax",
9322
9322
  severity: "warn",
9323
9323
  defaultEnabled: false,
9324
- recommendation: "Pick one fragment style across the codebase.",
9324
+ recommendation: "Use one fragment style so identical wrappers do not look different across files.",
9325
9325
  category: "Architecture",
9326
9326
  create: (context) => {
9327
9327
  const { mode } = resolveSettings$32(context.settings);
@@ -9349,8 +9349,8 @@ const jsxFragments = defineRule({
9349
9349
  });
9350
9350
  //#endregion
9351
9351
  //#region src/plugin/rules/react-builtins/jsx-handler-names.ts
9352
- const buildHandlerNameMessage = (handlerName, propKey) => `The handler "${handlerName}" for "${propKey}" is hard to recognize.`;
9353
- const buildHandlerPropMessage = (propKey, propValue) => `The prop "${propKey}" passes a handler "${propValue}".`;
9352
+ const buildHandlerNameMessage = (handlerName, propKey) => `The handler "${handlerName}" does not match the "${propKey}" event prop convention, so readers cannot trace event flow quickly.`;
9353
+ const buildHandlerPropMessage = (propKey, propValue) => `The prop "${propKey}" passes handler "${propValue}" but is not named like an event prop, so callers cannot tell it fires an event.`;
9354
9354
  const DEFAULT_HANDLER_PREFIX = "handle";
9355
9355
  const DEFAULT_HANDLER_PROP_PREFIX = "on";
9356
9356
  const splitPrefixes = (prefixes) => prefixes.split("|").map((token) => token.trim()).filter((token) => token.length > 0);
@@ -9441,7 +9441,7 @@ const jsxHandlerNames = defineRule({
9441
9441
  title: "Inconsistent event handler names",
9442
9442
  severity: "warn",
9443
9443
  defaultEnabled: false,
9444
- recommendation: "Use the `on…` prefix for event-handler props and `handle…` for handlers.",
9444
+ recommendation: "Use the `on…` prefix for event-handler props and `handle…` for handlers so readers can trace event flow.",
9445
9445
  category: "Architecture",
9446
9446
  create: (context) => {
9447
9447
  const settings = resolveSettings$31(context.settings);
@@ -9675,7 +9675,7 @@ const jsxKey = defineRule({
9675
9675
  id: "jsx-key",
9676
9676
  title: "Missing key in list",
9677
9677
  severity: "error",
9678
- recommendation: "Add a `key={...}` prop to each element produced inside `.map` / array literal.",
9678
+ recommendation: "Add a stable `key` prop so React can keep list items matched to the right data when the list changes.",
9679
9679
  create: (context) => {
9680
9680
  const settings = resolveSettings$30(context.settings);
9681
9681
  return {
@@ -9841,7 +9841,7 @@ const jsxNoCommentTextnodes = defineRule({
9841
9841
  id: "jsx-no-comment-textnodes",
9842
9842
  title: "Comment rendered as JSX text",
9843
9843
  severity: "warn",
9844
- recommendation: "Wrap JSX comments in `{/* … */}` so they're parsed as comments, not children.",
9844
+ recommendation: "Wrap JSX comments in `{/* … */}` so users do not see comment text rendered as children.",
9845
9845
  create: (context) => ({ JSXText(node) {
9846
9846
  if (!hasCommentLikePattern(node.value)) return;
9847
9847
  if (isInsideLiteralTextTag(node)) return;
@@ -9948,7 +9948,7 @@ const jsxNoConstructedContextValues = defineRule({
9948
9948
  tags: ["react-jsx-only"],
9949
9949
  severity: "warn",
9950
9950
  disabledBy: ["react-compiler"],
9951
- recommendation: "Wrap the context value in `useMemo`, or move it outside the component.",
9951
+ recommendation: "Wrap the context value in `useMemo` or move it outside the component so consumers do not redraw every render.",
9952
9952
  category: "Performance",
9953
9953
  create: (context) => {
9954
9954
  const isTestlikeFile = isTestlikeFilename(context.filename);
@@ -9989,7 +9989,7 @@ const jsxNoDuplicateProps = defineRule({
9989
9989
  id: "jsx-no-duplicate-props",
9990
9990
  title: "Duplicate props on element",
9991
9991
  severity: "error",
9992
- recommendation: "Remove or rename one of the duplicate props.",
9992
+ recommendation: "Remove or rename one of the duplicate props so the later value does not silently override the earlier one.",
9993
9993
  create: (context) => ({ JSXOpeningElement(node) {
9994
9994
  const seenPropNames = /* @__PURE__ */ new Set();
9995
9995
  for (const attribute of node.attributes) {
@@ -10306,7 +10306,7 @@ const jsxNoJsxAsProp = defineRule({
10306
10306
  tags: ["react-jsx-only"],
10307
10307
  severity: "warn",
10308
10308
  disabledBy: ["react-compiler"],
10309
- recommendation: "Move the JSX outside the component, or wrap it in `useMemo`.",
10309
+ recommendation: "Move the JSX outside the component or wrap it in `useMemo` so memoized children do not redraw every render.",
10310
10310
  category: "Performance",
10311
10311
  create: (context) => {
10312
10312
  const isTestlikeFile = isTestlikeFilename(context.filename);
@@ -10678,7 +10678,7 @@ const jsxNoNewArrayAsProp = defineRule({
10678
10678
  tags: ["react-jsx-only"],
10679
10679
  severity: "warn",
10680
10680
  disabledBy: ["react-compiler"],
10681
- recommendation: "Wrap the array in `useMemo`, or move it outside the component.",
10681
+ recommendation: "Wrap the array in `useMemo` or move it outside the component so memoized children do not redraw every render.",
10682
10682
  category: "Performance",
10683
10683
  create: (context) => {
10684
10684
  const isTestlikeFile = isTestlikeFilename(context.filename);
@@ -11141,7 +11141,7 @@ const jsxNoNewFunctionAsProp = defineRule({
11141
11141
  tags: ["react-jsx-only"],
11142
11142
  severity: "warn",
11143
11143
  disabledBy: ["react-compiler"],
11144
- recommendation: "Wrap the callback in `useCallback`, or move it outside the component.",
11144
+ recommendation: "Wrap the callback in `useCallback` or move it outside the component so memoized children do not redraw every render.",
11145
11145
  category: "Performance",
11146
11146
  create: (context) => {
11147
11147
  const isTestlikeFile = isTestlikeFilename(context.filename);
@@ -11448,7 +11448,7 @@ const jsxNoNewObjectAsProp = defineRule({
11448
11448
  tags: ["react-jsx-only"],
11449
11449
  severity: "warn",
11450
11450
  disabledBy: ["react-compiler"],
11451
- recommendation: "Wrap the object in `useMemo`, or move it outside the component.",
11451
+ recommendation: "Wrap the object in `useMemo` or move it outside the component so memoized children do not redraw every render.",
11452
11452
  category: "Performance",
11453
11453
  create: (context) => {
11454
11454
  const isTestlikeFile = isTestlikeFilename(context.filename);
@@ -11508,7 +11508,7 @@ const jsxNoScriptUrl = defineRule({
11508
11508
  id: "jsx-no-script-url",
11509
11509
  title: "javascript: URL in JSX",
11510
11510
  severity: "error",
11511
- recommendation: "Replace `javascript:` URLs with `onClick` or `onSubmit` handlers. React 19 blocks them anyway.",
11511
+ recommendation: "Use real event handlers instead of `javascript:` URLs so injected URL text cannot execute as code.",
11512
11512
  category: "Security",
11513
11513
  create: (context) => {
11514
11514
  const options = resolveSettings$28(context.settings);
@@ -11559,7 +11559,7 @@ const jsxNoUndef = defineRule({
11559
11559
  id: "jsx-no-undef",
11560
11560
  title: "Undefined JSX component",
11561
11561
  severity: "error",
11562
- recommendation: "Import the component or check for typos.",
11562
+ recommendation: "Import the component or fix the typo so React can resolve the JSX identifier at runtime.",
11563
11563
  create: (context) => ({ JSXOpeningElement(node) {
11564
11564
  const rootIdentifier = getRootIdentifier(node.name);
11565
11565
  if (!rootIdentifier) return;
@@ -11751,7 +11751,7 @@ const jsxPascalCase = defineRule({
11751
11751
  severity: "warn",
11752
11752
  defaultEnabled: false,
11753
11753
  tags: ["test-noise"],
11754
- recommendation: "Rename custom JSX components to PascalCase.",
11754
+ recommendation: "Rename custom JSX components to PascalCase so React treats them as components, not intrinsic DOM tags.",
11755
11755
  category: "Architecture",
11756
11756
  create: (context) => {
11757
11757
  const settings = resolveSettings$26(context.settings);
@@ -11815,7 +11815,7 @@ const jsxPropsNoSpreadMulti = defineRule({
11815
11815
  id: "jsx-props-no-spread-multi",
11816
11816
  title: "Same prop spread multiple times",
11817
11817
  severity: "warn",
11818
- recommendation: "Spread the same value at most once per element.",
11818
+ recommendation: "Spread each value at most once so later props cannot silently override earlier props from the same object.",
11819
11819
  create: (context) => ({ JSXOpeningElement(node) {
11820
11820
  const seenNames = /* @__PURE__ */ new Map();
11821
11821
  const reportedNames = /* @__PURE__ */ new Set();
@@ -11855,7 +11855,7 @@ const jsxPropsNoSpreading = defineRule({
11855
11855
  title: "Props spread onto element",
11856
11856
  severity: "warn",
11857
11857
  defaultEnabled: false,
11858
- recommendation: "List each prop explicitly so consumers can see what's being passed.",
11858
+ recommendation: "List each prop explicitly so consumers can see the component API instead of receiving hidden spread props.",
11859
11859
  category: "Architecture",
11860
11860
  create: (context) => {
11861
11861
  const settings = resolveSettings$25(context.settings);
@@ -12236,7 +12236,7 @@ const lang = defineRule({
12236
12236
  title: "Invalid lang attribute value",
12237
12237
  tags: ["react-jsx-only"],
12238
12238
  severity: "warn",
12239
- recommendation: "Use a valid language code, like `en` or `en-US`.",
12239
+ recommendation: "Use a valid language code like `en` or `en-US` so screen readers choose the right pronunciation rules.",
12240
12240
  category: "Accessibility",
12241
12241
  create: (context) => ({ JSXOpeningElement(node) {
12242
12242
  if (getElementType(node, context.settings) !== "html") return;
@@ -12263,7 +12263,7 @@ const lang = defineRule({
12263
12263
  });
12264
12264
  //#endregion
12265
12265
  //#region src/plugin/rules/a11y/media-has-caption.ts
12266
- const MESSAGE$32 = "Deaf & hard-of-hearing users miss this media without captions, so add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
12266
+ const MESSAGE$32 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
12267
12267
  const DEFAULT_AUDIO = ["audio"];
12268
12268
  const DEFAULT_VIDEO = ["video"];
12269
12269
  const DEFAULT_TRACK = ["track"];
@@ -12505,11 +12505,11 @@ const hasJsxAttribute = (attributes, attributeName) => Boolean(findJsxAttribute(
12505
12505
  //#region src/plugin/rules/nextjs/nextjs-image-missing-sizes.ts
12506
12506
  const nextjsImageMissingSizes = defineRule({
12507
12507
  id: "nextjs-image-missing-sizes",
12508
- title: "Image fill missing sizes",
12508
+ title: "next/image fill image is missing sizes",
12509
12509
  tags: ["test-noise"],
12510
12510
  requires: ["nextjs"],
12511
12511
  severity: "warn",
12512
- recommendation: "Add sizes for responsive behavior: `sizes=\"(max-width: 768px) 100vw, 50vw\"` matching your layout breakpoints",
12512
+ recommendation: "Add `sizes` matching your layout so `next/image` does not assume the largest candidate and make users download oversized images.",
12513
12513
  create: (context) => ({ JSXOpeningElement(node) {
12514
12514
  if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "Image") return;
12515
12515
  const attributes = node.attributes ?? [];
@@ -12545,11 +12545,11 @@ const nextjsInlineScriptMissingId = defineRule({
12545
12545
  //#region src/plugin/rules/nextjs/nextjs-missing-metadata.ts
12546
12546
  const nextjsMissingMetadata = defineRule({
12547
12547
  id: "nextjs-missing-metadata",
12548
- title: "Page missing metadata",
12548
+ title: "Page missing metadata for search previews",
12549
12549
  tags: ["test-noise"],
12550
12550
  requires: ["nextjs"],
12551
12551
  severity: "warn",
12552
- recommendation: "Add `export const metadata = { title: '...', description: '...' }` or `export async function generateMetadata()`",
12552
+ recommendation: "Add metadata or `generateMetadata()` so search engines and social previews get a title and description.",
12553
12553
  create: (context) => ({ Program(programNode) {
12554
12554
  const filename = normalizeFilename$1(context.filename ?? "");
12555
12555
  if (!PAGE_FILE_PATTERN.test(filename)) return;
@@ -12562,7 +12562,7 @@ const nextjsMissingMetadata = defineRule({
12562
12562
  return false;
12563
12563
  })) context.report({
12564
12564
  node: programNode,
12565
- message: "This page has no metadata, so search engines & social previews get no title or description."
12565
+ message: "This page has no metadata, so search engines and social previews get no title or description."
12566
12566
  });
12567
12567
  } })
12568
12568
  });
@@ -12570,7 +12570,7 @@ const nextjsMissingMetadata = defineRule({
12570
12570
  //#region src/plugin/rules/nextjs/nextjs-no-a-element.ts
12571
12571
  const nextjsNoAElement = defineRule({
12572
12572
  id: "nextjs-no-a-element",
12573
- title: "Plain anchor for internal link",
12573
+ title: "Plain anchor reloads internal Next.js links",
12574
12574
  tags: ["test-noise"],
12575
12575
  requires: ["nextjs"],
12576
12576
  severity: "warn",
@@ -12584,7 +12584,7 @@ const nextjsNoAElement = defineRule({
12584
12584
  else if (isNodeOfType(hrefAttribute.value, "JSXExpressionContainer") && isNodeOfType(hrefAttribute.value.expression, "Literal")) hrefValue = hrefAttribute.value.expression.value;
12585
12585
  if (typeof hrefValue === "string" && hrefValue.startsWith("/")) context.report({
12586
12586
  node,
12587
- message: "Plain <a> reloads the whole page on internal links."
12587
+ message: "Plain <a> reloads the whole page for internal links, so Next.js loses client-side navigation and prefetching."
12588
12588
  });
12589
12589
  } })
12590
12590
  });
@@ -12669,11 +12669,11 @@ const nextjsNoClientSideRedirect = defineRule({
12669
12669
  //#region src/plugin/rules/nextjs/nextjs-no-css-link.ts
12670
12670
  const nextjsNoCssLink = defineRule({
12671
12671
  id: "nextjs-no-css-link",
12672
- title: "Stylesheet loaded via link",
12672
+ title: "Linked stylesheet bypasses Next.js CSS optimization",
12673
12673
  tags: ["test-noise"],
12674
12674
  requires: ["nextjs"],
12675
12675
  severity: "warn",
12676
- recommendation: "Import CSS directly: `import './styles.css'` or use CSS Modules: `import styles from './Button.module.css'`",
12676
+ recommendation: "Import CSS directly or use CSS Modules so Next.js can bundle, order, and optimize the stylesheet.",
12677
12677
  create: (context) => ({ JSXOpeningElement(node) {
12678
12678
  if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "link") return;
12679
12679
  const attributes = node.attributes ?? [];
@@ -12686,7 +12686,7 @@ const nextjsNoCssLink = defineRule({
12686
12686
  if (typeof hrefValue === "string" && GOOGLE_FONTS_PATTERN.test(hrefValue)) return;
12687
12687
  context.report({
12688
12688
  node,
12689
- message: "This <link rel=\"stylesheet\"> loads unbundled, unoptimized CSS."
12689
+ message: "This <link rel=\"stylesheet\"> bypasses Next.js CSS handling, so the CSS loads unbundled and unoptimized."
12690
12690
  });
12691
12691
  } })
12692
12692
  });
@@ -12710,7 +12710,7 @@ const nextjsNoDefaultExportInRouteHandler = defineRule({
12710
12710
  tags: ["test-noise"],
12711
12711
  requires: ["nextjs"],
12712
12712
  severity: "error",
12713
- recommendation: "Replace `export default` with named HTTP method exports: `export async function GET(request) { }`",
12713
+ recommendation: "Replace `export default` with named HTTP method exports because Next.js ignores default exports in `route.ts`.",
12714
12714
  create: (context) => {
12715
12715
  let isAppRouteHandler = false;
12716
12716
  let programNode = null;
@@ -12797,11 +12797,11 @@ const nextjsNoFontLink = defineRule({
12797
12797
  //#region src/plugin/rules/nextjs/nextjs-no-google-analytics-script.ts
12798
12798
  const nextjsNoGoogleAnalyticsScript = defineRule({
12799
12799
  id: "nextjs-no-google-analytics-script",
12800
- title: "Manual Google Analytics script",
12800
+ title: "Manual Google Analytics script blocks optimized loading",
12801
12801
  tags: ["test-noise"],
12802
12802
  requires: ["nextjs"],
12803
12803
  severity: "warn",
12804
- recommendation: "Use `import { GoogleAnalytics } from '@next/third-parties/google'` for automatic optimization & smaller bundles",
12804
+ recommendation: "Use `import { GoogleAnalytics } from '@next/third-parties/google'` for automatic optimization and smaller bundles.",
12805
12805
  create: (context) => ({ JSXOpeningElement(node) {
12806
12806
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12807
12807
  if (node.name.name !== "script" && node.name.name !== "Script") return;
@@ -12810,7 +12810,7 @@ const nextjsNoGoogleAnalyticsScript = defineRule({
12810
12810
  const srcValue = isNodeOfType(srcAttribute.value, "Literal") ? srcAttribute.value.value : null;
12811
12811
  if (typeof srcValue === "string" && GOOGLE_ANALYTICS_SCRIPT_PATTERN.test(srcValue)) context.report({
12812
12812
  node,
12813
- message: "Manual Google Analytics script blocks rendering. Use @next/third-parties for optimal loading strategy."
12813
+ message: "Manual Google Analytics scripts block rendering without Next.js' optimized loading strategy."
12814
12814
  });
12815
12815
  } })
12816
12816
  });
@@ -12822,7 +12822,7 @@ const nextjsNoHeadImport = defineRule({
12822
12822
  tags: ["test-noise"],
12823
12823
  requires: ["nextjs"],
12824
12824
  severity: "error",
12825
- recommendation: "Use the Metadata API instead. Add `export const metadata = { title: '...' }` or `export async function generateMetadata()`",
12825
+ recommendation: "Use the Metadata API because `next/head` is ignored in the App Router and meta tags will not render.",
12826
12826
  create: (context) => ({ ImportDeclaration(node) {
12827
12827
  if (node.source?.value !== "next/head") return;
12828
12828
  const filename = normalizeFilename$1(context.filename ?? "");
@@ -12837,18 +12837,18 @@ const nextjsNoHeadImport = defineRule({
12837
12837
  //#region src/plugin/rules/nextjs/nextjs-no-img-element.ts
12838
12838
  const nextjsNoImgElement = defineRule({
12839
12839
  id: "nextjs-no-img-element",
12840
- title: "Plain img element",
12840
+ title: "Plain img ships unoptimized images",
12841
12841
  tags: ["test-noise"],
12842
12842
  requires: ["nextjs"],
12843
12843
  severity: "warn",
12844
- recommendation: "`import Image from 'next/image'` for automatic WebP/AVIF, lazy loading, and responsive srcset",
12844
+ recommendation: "Use `next/image` so users get optimized formats, responsive srcsets, and lazy loading instead of oversized image downloads.",
12845
12845
  create: (context) => {
12846
12846
  if (isGeneratedImageRenderContext(context)) return {};
12847
12847
  return { JSXOpeningElement(node) {
12848
12848
  if (isGeneratedImageRenderContext(context, node)) return;
12849
12849
  if (isNodeOfType(node.name, "JSXIdentifier") && node.name.name === "img") context.report({
12850
12850
  node,
12851
- message: "Plain <img> ships unoptimized, oversized images to your users."
12851
+ message: "Plain <img> ships unoptimized, oversized images."
12852
12852
  });
12853
12853
  } };
12854
12854
  }
@@ -12857,11 +12857,11 @@ const nextjsNoImgElement = defineRule({
12857
12857
  //#region src/plugin/rules/nextjs/nextjs-no-native-script.ts
12858
12858
  const nextjsNoNativeScript = defineRule({
12859
12859
  id: "nextjs-no-native-script",
12860
- title: "Plain script tag",
12860
+ title: "Plain script can block Next.js rendering",
12861
12861
  tags: ["test-noise"],
12862
12862
  requires: ["nextjs"],
12863
12863
  severity: "warn",
12864
- recommendation: "`import Script from \"next/script\"`. Use `strategy=\"afterInteractive\"` for analytics or `\"lazyOnload\"` for widgets",
12864
+ recommendation: "Use `next/script` with `strategy=\"afterInteractive\"` or `\"lazyOnload\"` so third-party scripts do not block rendering.",
12865
12865
  create: (context) => ({ JSXOpeningElement(node) {
12866
12866
  if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "script") return;
12867
12867
  const typeAttribute = findJsxAttribute(node.attributes ?? [], "type");
@@ -12869,7 +12869,7 @@ const nextjsNoNativeScript = defineRule({
12869
12869
  if (typeof typeValue === "string" && !EXECUTABLE_SCRIPT_TYPES.has(typeValue)) return;
12870
12870
  context.report({
12871
12871
  node,
12872
- message: "Plain <script> blocks rendering with no loading strategy."
12872
+ message: "Plain <script> has no Next.js loading strategy, so it can block rendering."
12873
12873
  });
12874
12874
  } })
12875
12875
  });
@@ -12902,7 +12902,7 @@ const nextjsNoRedirectInTryCatch = defineRule({
12902
12902
  tags: ["test-noise"],
12903
12903
  requires: ["nextjs"],
12904
12904
  severity: "warn",
12905
- recommendation: "Move the redirect/notFound call outside the try block, or add `unstable_rethrow(error)` in the catch",
12905
+ recommendation: "Move `redirect()` or `notFound()` outside the try block, or rethrow in `catch`, because these APIs throw control-flow errors that catch blocks swallow.",
12906
12906
  create: (context) => {
12907
12907
  let tryCatchDepth = 0;
12908
12908
  return {
@@ -14053,7 +14053,7 @@ const nextjsNoUseSearchParamsWithoutSuspense = defineRule({
14053
14053
  tags: ["test-noise"],
14054
14054
  requires: ["nextjs"],
14055
14055
  severity: "warn",
14056
- recommendation: "Wrap the component using useSearchParams: `<Suspense fallback={<Skeleton />}><SearchComponent /></Suspense>`",
14056
+ recommendation: "Wrap the component using `useSearchParams` in `<Suspense>` so the rest of the page can stay statically rendered.",
14057
14057
  create: (context) => {
14058
14058
  let isPageOrLayoutFile = false;
14059
14059
  let hasAncestorLayoutSuspense = false;
@@ -14091,7 +14091,7 @@ const nextjsNoUseSearchParamsWithoutSuspense = defineRule({
14091
14091
  if (!componentBody || !astContainsUseSearchParams(componentBody)) return;
14092
14092
  context.report({
14093
14093
  node,
14094
- message: `<${node.name.name}> uses useSearchParams() but is not wrapped in a <Suspense> boundary.`
14094
+ message: `<${node.name.name}> uses useSearchParams() outside <Suspense>, so this page falls back to client-side rendering.`
14095
14095
  });
14096
14096
  }
14097
14097
  };
@@ -14105,7 +14105,7 @@ const nextjsNoVercelOgImport = defineRule({
14105
14105
  tags: ["test-noise"],
14106
14106
  requires: ["nextjs"],
14107
14107
  severity: "warn",
14108
- recommendation: "Use `import { ImageResponse } from \"next/og\"`. The `@vercel/og` package is built into Next.js and should not be imported directly",
14108
+ recommendation: "Use `import { ImageResponse } from \"next/og\"`; do not import `@vercel/og` directly because Next.js already bundles it.",
14109
14109
  create: (context) => ({ ImportDeclaration(node) {
14110
14110
  if (node.source?.value !== "@vercel/og") return;
14111
14111
  context.report({
@@ -14619,7 +14619,7 @@ const noAdjustStateOnPropChange = defineRule({
14619
14619
  if (getArgsUpstreamRefs(analysis, ref).some((argRef) => isProp(analysis, argRef))) continue;
14620
14620
  context.report({
14621
14621
  node: callExpr,
14622
- message: "Your users briefly see the wrong value when the prop changes."
14622
+ message: "This effect adjusts state after a prop changes, so users briefly see the stale value."
14623
14623
  });
14624
14624
  }
14625
14625
  } })
@@ -14632,7 +14632,7 @@ const noAriaHiddenOnFocusable = defineRule({
14632
14632
  title: "aria-hidden on focusable element",
14633
14633
  tags: ["react-jsx-only"],
14634
14634
  severity: "warn",
14635
- recommendation: "Remove `aria-hidden` from focusable elements, or stop them being focusable.",
14635
+ recommendation: "Remove `aria-hidden` from focusable elements, or stop them being focusable, so keyboard users do not land on content screen readers hide.",
14636
14636
  category: "Accessibility",
14637
14637
  create: (context) => ({ JSXOpeningElement(node) {
14638
14638
  const ariaHidden = hasJsxPropIgnoreCase(node.attributes, "aria-hidden");
@@ -15176,7 +15176,7 @@ const noArrayIndexKey = defineRule({
15176
15176
  title: "Array index used as a key",
15177
15177
  severity: "warn",
15178
15178
  defaultEnabled: false,
15179
- recommendation: "Use a stable `key` from your data instead of the array index.",
15179
+ recommendation: "Use a stable `key` from your data so reordered items keep the right state and DOM.",
15180
15180
  category: "Performance",
15181
15181
  create: (context) => ({
15182
15182
  JSXOpeningElement(node) {
@@ -15253,7 +15253,7 @@ const noArrayIndexKey = defineRule({
15253
15253
  });
15254
15254
  //#endregion
15255
15255
  //#region src/plugin/rules/a11y/no-autofocus.ts
15256
- const MESSAGE$28 = "Screen reader & keyboard users get disoriented because `autoFocus` jumps focus on load, so remove it and let people choose where to focus.";
15256
+ const MESSAGE$28 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
15257
15257
  const resolveSettings$21 = (settings) => {
15258
15258
  const reactDoctor = settings?.["react-doctor"];
15259
15259
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -15487,7 +15487,7 @@ const noCascadingSetState = defineRule({
15487
15487
  title: "Multiple setState calls in one effect",
15488
15488
  severity: "warn",
15489
15489
  tags: ["test-noise"],
15490
- recommendation: "Combine into useReducer: `const [state, dispatch] = useReducer(reducer, initialState)`",
15490
+ recommendation: "Combine related updates in `useReducer` so one effect does not redraw the screen once per `setState` call.",
15491
15491
  create: (context) => ({ CallExpression(node) {
15492
15492
  if (!isHookCall$1(node, EFFECT_HOOK_NAMES$1)) return;
15493
15493
  if (isInitOnlyEffect(node)) return;
@@ -15533,12 +15533,12 @@ const noChainStateUpdates = defineRule({
15533
15533
  });
15534
15534
  //#endregion
15535
15535
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
15536
- const MESSAGE$27 = "Your component can render the wrong children when you pass them through a `children` prop.";
15536
+ const MESSAGE$27 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
15537
15537
  const noChildrenProp = defineRule({
15538
15538
  id: "no-children-prop",
15539
15539
  title: "Children passed as a prop",
15540
15540
  severity: "warn",
15541
- recommendation: "Nest children between the tags instead of passing a `children` prop.",
15541
+ recommendation: "Nest children between the tags so the rendered content is visible in JSX and cannot be hidden inside a props object.",
15542
15542
  create: (context) => ({
15543
15543
  JSXAttribute(node) {
15544
15544
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
@@ -15566,13 +15566,13 @@ const noChildrenProp = defineRule({
15566
15566
  });
15567
15567
  //#endregion
15568
15568
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
15569
- const MESSAGE$26 = "`React.cloneElement` breaks easily when the cloned element's props change.";
15569
+ const MESSAGE$26 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
15570
15570
  const noCloneElement = defineRule({
15571
15571
  id: "no-clone-element",
15572
- title: "Use of cloneElement",
15572
+ title: "cloneElement makes child props fragile",
15573
15573
  severity: "warn",
15574
15574
  defaultEnabled: false,
15575
- recommendation: "Pass children, render props, or use `Children.map` instead of cloning elements.",
15575
+ recommendation: "Pass children or render props instead so parent code does not depend on fragile cloned child props.",
15576
15576
  category: "Architecture",
15577
15577
  create: (context) => ({ CallExpression(node) {
15578
15578
  const callee = stripParenExpression(node.callee);
@@ -15804,7 +15804,7 @@ const noCreateStoreInRender = defineRule({
15804
15804
  title: "Store created during render",
15805
15805
  severity: "error",
15806
15806
  category: "Correctness",
15807
- recommendation: "Create the store, atom, or observable at the top level of the file, not inside a component or hook.",
15807
+ recommendation: "Create stores at module scope so subscribers are not cut off and saved state does not reset every render.",
15808
15808
  create: (context) => ({ CallExpression(node) {
15809
15809
  const factory = resolveStoreFactoryForCallee(node.callee);
15810
15810
  if (!factory) return;
@@ -15820,10 +15820,10 @@ const noCreateStoreInRender = defineRule({
15820
15820
  const MESSAGE$24 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
15821
15821
  const noDanger = defineRule({
15822
15822
  id: "no-danger",
15823
- title: "Use of dangerouslySetInnerHTML",
15823
+ title: "Raw HTML injection can run unsafe markup",
15824
15824
  severity: "warn",
15825
15825
  category: "Security",
15826
- recommendation: "Render trusted content as React children rather than injecting raw HTML.",
15826
+ recommendation: "Render trusted content as React children so attacker-controlled HTML cannot run in users' browsers.",
15827
15827
  create: (context) => ({
15828
15828
  JSXOpeningElement(node) {
15829
15829
  const propAttribute = hasJsxProp(node.attributes, "dangerouslySetInnerHTML");
@@ -15909,7 +15909,7 @@ const noDangerWithChildren = defineRule({
15909
15909
  id: "no-danger-with-children",
15910
15910
  title: "dangerouslySetInnerHTML with children",
15911
15911
  severity: "error",
15912
- recommendation: "Use either `children` or `dangerouslySetInnerHTML`, never both.",
15912
+ recommendation: "Use either `children` or `dangerouslySetInnerHTML` so React does not ignore one source of content.",
15913
15913
  category: "Correctness",
15914
15914
  create: (context) => ({
15915
15915
  JSXElement(node) {
@@ -16068,7 +16068,7 @@ const noDarkModeGlow = defineRule({
16068
16068
  if (!hasDarkBackground || !shadowValue || !shadowProperty) return;
16069
16069
  if (hasColoredGlowShadow(shadowValue)) context.report({
16070
16070
  node: shadowProperty,
16071
- message: "Your users see a cheap, overdone colored glow on the dark background, so use a subtle, neutral shadow instead."
16071
+ message: "A strong colored glow on a dark background can feel heavy. Use a subtle, neutral shadow instead."
16072
16072
  });
16073
16073
  } })
16074
16074
  });
@@ -16444,7 +16444,7 @@ const noDerivedUseState = defineRule({
16444
16444
  title: "Prop derived into useState",
16445
16445
  tags: ["test-noise"],
16446
16446
  severity: "warn",
16447
- recommendation: "Remove useState and compute the value inline: `const value = transform(propName)`",
16447
+ recommendation: "Compute the value inline so prop changes do not leave `useState` holding a stale copy.",
16448
16448
  create: (context) => {
16449
16449
  const propStackTracker = createComponentPropStackTracker();
16450
16450
  return {
@@ -16536,7 +16536,7 @@ const noDidMountSetState = defineRule({
16536
16536
  //#endregion
16537
16537
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
16538
16538
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
16539
- const MESSAGE$21 = "This can loop forever & freeze the component.";
16539
+ const MESSAGE$21 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
16540
16540
  const resolveSettings$19 = (settings) => {
16541
16541
  const reactDoctor = settings?.["react-doctor"];
16542
16542
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noDidUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -16776,7 +16776,7 @@ const noDistractingElements = defineRule({
16776
16776
  title: "Distracting marquee or blink element",
16777
16777
  tags: ["react-jsx-only"],
16778
16778
  severity: "error",
16779
- recommendation: "Replace `<marquee>` and `<blink>` with normal, accessible markup.",
16779
+ recommendation: "Replace `<marquee>` and `<blink>` with normal markup so motion does not distract or disorient users.",
16780
16780
  category: "Accessibility",
16781
16781
  create: (context) => {
16782
16782
  const { distractingTags } = resolveSettings$18(context.settings);
@@ -16805,7 +16805,7 @@ const noDocumentStartViewTransition = defineRule({
16805
16805
  if (!isNodeOfType(callee.property, "Identifier") || callee.property.name !== "startViewTransition") return;
16806
16806
  context.report({
16807
16807
  node,
16808
- message: "Your users lose React's <ViewTransition> animations when document.startViewTransition() runs directly."
16808
+ message: "Calling `document.startViewTransition()` directly can bypass React's `<ViewTransition>` animation lifecycle."
16809
16809
  });
16810
16810
  } })
16811
16811
  });
@@ -16823,13 +16823,13 @@ const noDynamicImportPath = defineRule({
16823
16823
  if (source && !isNodeOfType(source, "Literal") && !isNodeOfType(source, "TemplateLiteral")) {
16824
16824
  context.report({
16825
16825
  node,
16826
- message: "This ships in the main bundle & slows page load, since the bundler can't code-split a dynamic import path. Use a plain string path instead."
16826
+ message: "This can stay in the main bundle because the bundler cannot code-split a dynamic import path. Use a plain string path instead."
16827
16827
  });
16828
16828
  return;
16829
16829
  }
16830
16830
  if (isNodeOfType(source, "TemplateLiteral") && (source.expressions?.length ?? 0) > 0) context.report({
16831
16831
  node,
16832
- message: "This ships in the main bundle & slows page load, since the bundler can't code-split a dynamic import path. Use a plain string path instead of one with `${...}`."
16832
+ message: "This can stay in the main bundle because the bundler cannot code-split a dynamic import path with `${dynamic_path}`. Use a plain string path instead."
16833
16833
  });
16834
16834
  },
16835
16835
  CallExpression(node) {
@@ -17120,7 +17120,7 @@ const noEffectEventHandler = defineRule({
17120
17120
  title: "Effect used as an event handler",
17121
17121
  tags: ["test-noise"],
17122
17122
  severity: "warn",
17123
- recommendation: "Move the conditional logic into onClick, onChange, or onSubmit handlers directly",
17123
+ recommendation: "Move event logic into the handler that starts it so the side effect does not run late after an extra render.",
17124
17124
  create: (context) => {
17125
17125
  const propStackTracker = createComponentPropStackTracker();
17126
17126
  return {
@@ -17297,7 +17297,7 @@ const noEffectWithFreshDeps = defineRule({
17297
17297
  //#region src/plugin/rules/security/no-eval.ts
17298
17298
  const noEval = defineRule({
17299
17299
  id: "no-eval",
17300
- title: "Use of eval()",
17300
+ title: "eval() runs untrusted code strings",
17301
17301
  severity: "error",
17302
17302
  recommendation: "Use `JSON.parse` for data, or rewrite the code so it doesn't build and run code from strings.",
17303
17303
  create: (context) => ({
@@ -18170,14 +18170,14 @@ const noFetchInEffect = defineRule({
18170
18170
  id: "no-fetch-in-effect",
18171
18171
  title: "Data fetching inside an effect",
18172
18172
  severity: "warn",
18173
- recommendation: "Use `useQuery()` from @tanstack/react-query, `useSWR()`, or fetch in a Server Component instead",
18173
+ recommendation: "Use a data-fetching layer or Server Component so fetches do not race, double-fire, or leak from `useEffect`.",
18174
18174
  create: (context) => ({ CallExpression(node) {
18175
18175
  if (!isHookCall$1(node, EFFECT_HOOK_NAMES$1)) return;
18176
18176
  const callback = getEffectCallback(node);
18177
18177
  if (!callback) return;
18178
18178
  if (containsFetchCall(callback)) context.report({
18179
18179
  node,
18180
- message: "fetch() inside useEffect races, double-fires & leaks for your users."
18180
+ message: "fetch() inside useEffect can race, double-fire, or leak. Use a data-fetching layer or Server Component instead."
18181
18181
  });
18182
18182
  } })
18183
18183
  });
@@ -18191,9 +18191,9 @@ const ALLOWED_NAMESPACES = new Set([
18191
18191
  const MESSAGE$19 = "`findDOMNode` crashes your app in React 19 because it was removed.";
18192
18192
  const noFindDomNode = defineRule({
18193
18193
  id: "no-find-dom-node",
18194
- title: "Use of findDOMNode",
18194
+ title: "findDOMNode breaks component encapsulation",
18195
18195
  severity: "warn",
18196
- recommendation: "Use a ref (`useRef` or `createRef`) to reach DOM nodes instead of `findDOMNode`.",
18196
+ recommendation: "Use a ref to reach DOM nodes because `findDOMNode` was removed in React 19 and can crash the app.",
18197
18197
  create: (context) => ({ CallExpression(node) {
18198
18198
  const callee = node.callee;
18199
18199
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
@@ -18230,7 +18230,7 @@ const noFlushSync = defineRule({
18230
18230
  if (!isNodeOfType(specifier, "ImportSpecifier")) continue;
18231
18231
  if (getImportedName$1(specifier) === "flushSync") context.report({
18232
18232
  node: specifier,
18233
- message: "Your users lose View Transitions & concurrent rendering when flushSync from react-dom forces an immediate update."
18233
+ message: "`flushSync` forces an immediate update, which skips View Transitions and concurrent rendering."
18234
18234
  });
18235
18235
  }
18236
18236
  } })
@@ -18334,10 +18334,10 @@ const functionContainsReactRenderOutput = (functionNode, scopes) => containsRend
18334
18334
  //#region src/plugin/rules/architecture/no-giant-component.ts
18335
18335
  const noGiantComponent = defineRule({
18336
18336
  id: "no-giant-component",
18337
- title: "Component is too large",
18337
+ title: "Large component is hard to read and change",
18338
18338
  severity: "warn",
18339
18339
  tags: ["test-noise", "react-jsx-only"],
18340
- recommendation: "Pull each section into its own component, like `<UserHeader />` and `<UserActions />`.",
18340
+ recommendation: "Pull each section into its own component so the parent is easier to read, test, and change.",
18341
18341
  create: (context) => {
18342
18342
  const getOversizedComponentLineCount = (bodyNode) => {
18343
18343
  if (!bodyNode.loc) return null;
@@ -18575,11 +18575,11 @@ const noInlineBounceEasing = defineRule({
18575
18575
  if (!value) continue;
18576
18576
  if ((key === "transition" || key === "transitionTimingFunction" || key === "animation" || key === "animationTimingFunction") && isOvershootCubicBezier(value)) context.report({
18577
18577
  node: property,
18578
- message: "Your users see a dated, bouncy animation, so use ease-out or cubic-bezier(0.16, 1, 0.3, 1) for a smooth finish."
18578
+ message: "This bouncy easing can feel distracting. Use ease-out or cubic-bezier(0.16, 1, 0.3, 1) for a smoother finish."
18579
18579
  });
18580
18580
  if ((key === "animation" || key === "animationName") && hasBounceAnimationName(value)) context.report({
18581
18581
  node: property,
18582
- message: "Your users see a tacky bounce animation, so use a smooth ease-out (like ease-out-quart or expo) for a natural finish."
18582
+ message: "This bounce animation can feel distracting. Use a smooth ease-out, like ease-out-quart or expo, for a natural finish."
18583
18583
  });
18584
18584
  }
18585
18585
  },
@@ -18597,7 +18597,7 @@ const noInlineBounceEasing = defineRule({
18597
18597
  //#region src/plugin/rules/design/no-inline-exhaustive-style.ts
18598
18598
  const noInlineExhaustiveStyle = defineRule({
18599
18599
  id: "no-inline-exhaustive-style",
18600
- title: "Too many inline style properties",
18600
+ title: "Large inline style object rebuilds every render",
18601
18601
  severity: "warn",
18602
18602
  tags: ["test-noise", "react-jsx-only"],
18603
18603
  recommendation: "Move the styles to a CSS class, CSS module, Tailwind utilities, or a styled component. Big inline objects are hard to read and rebuild on every update.",
@@ -18717,7 +18717,7 @@ const noInteractiveElementToNoninteractiveRole = defineRule({
18717
18717
  //#region src/plugin/rules/react-builtins/no-is-mounted.ts
18718
18718
  const noIsMounted = defineRule({
18719
18719
  id: "no-is-mounted",
18720
- title: "Use of isMounted",
18720
+ title: "isMounted lets async callbacks update after unmount",
18721
18721
  severity: "warn",
18722
18722
  recommendation: "`isMounted` doesn't work in modern React. Track mount state with a ref, or cancel the async work instead.",
18723
18723
  create: (context) => ({ CallExpression(node) {
@@ -18729,7 +18729,7 @@ const noIsMounted = defineRule({
18729
18729
  if (ancestor.type === "MethodDefinition" || ancestor.type === "Property") {
18730
18730
  context.report({
18731
18731
  node,
18732
- message: "`isMounted` is unreliable in modern React & leads to bugs."
18732
+ message: "`isMounted` is unreliable in modern React, so async callbacks can update state after unmount."
18733
18733
  });
18734
18734
  return;
18735
18735
  }
@@ -18834,7 +18834,7 @@ const noLargeAnimatedBlur = defineRule({
18834
18834
  const blurRadius = Number.parseFloat(match[1]);
18835
18835
  if (blurRadius > 10) context.report({
18836
18836
  node: property,
18837
- message: `This can run out of GPU memory on phones because blur(${blurRadius}px) gets heavier as the blur & element grow, so use a smaller blur or a smaller element`
18837
+ message: `Large animated blurs can use significant GPU memory on phones because blur(${blurRadius}px) gets heavier as the blur and element grow. Use a smaller blur or a smaller element.`
18838
18838
  });
18839
18839
  }
18840
18840
  } })
@@ -18928,6 +18928,7 @@ const noLegacyClassLifecycles = defineRule({
18928
18928
  title: "Legacy class lifecycle methods",
18929
18929
  severity: "error",
18930
18930
  category: "Correctness",
18931
+ requires: ["react"],
18931
18932
  tags: ["migration-hint"],
18932
18933
  recommendation: "Move `componentWillMount` work to `componentDidMount`, `componentWillReceiveProps` to `componentDidUpdate` or the static `getDerivedStateFromProps`, and `componentWillUpdate` to `getSnapshotBeforeUpdate` plus `componentDidUpdate`. The `UNSAFE_` prefix only hides the warning. React 19 removes both.",
18933
18934
  create: (context) => {
@@ -18971,6 +18972,7 @@ const noLegacyContextApi = defineRule({
18971
18972
  title: "Legacy context API",
18972
18973
  severity: "error",
18973
18974
  category: "Correctness",
18975
+ requires: ["react"],
18974
18976
  tags: ["migration-hint"],
18975
18977
  recommendation: "Swap `childContextTypes` + `getChildContext` for `const MyContext = createContext(...)` and `<MyContext.Provider value={...}>`. Swap `contextTypes` for `static contextType = MyContext` or `useContext()` in a function component. Move the provider and every consumer together, or some consumers read the wrong context.",
18976
18978
  create: (context) => {
@@ -19078,10 +19080,10 @@ const collectBooleanLikePropsFromBody = (componentBody, propsParamName) => {
19078
19080
  };
19079
19081
  const noManyBooleanProps = defineRule({
19080
19082
  id: "no-many-boolean-props",
19081
- title: "Too many boolean props",
19083
+ title: "Boolean prop combinations are hard to test",
19082
19084
  severity: "warn",
19083
19085
  tags: ["test-noise", "react-jsx-only"],
19084
- recommendation: "Split into smaller components or named variants, like `<Button.Primary />` and `<DialogConfirm />`, instead of stacking `isPrimary` and `isConfirm` flags.",
19086
+ recommendation: "Split boolean-heavy APIs into smaller components or named variants so combinations stay testable.",
19085
19087
  create: (context) => {
19086
19088
  const reportIfMany = (booleanLikePropNames, componentName, reportNode) => {
19087
19089
  if (booleanLikePropNames.length >= 4) context.report({
@@ -19212,7 +19214,7 @@ const noMoment = defineRule({
19212
19214
  });
19213
19215
  //#endregion
19214
19216
  //#region src/plugin/rules/react-builtins/no-multi-comp.ts
19215
- const MESSAGE$17 = "This file is harder to navigate with more than one component.";
19217
+ const MESSAGE$17 = "This file declares several components, so each component is harder to find, test, and change.";
19216
19218
  const resolveSettings$16 = (settings) => {
19217
19219
  const reactDoctor = settings?.["react-doctor"];
19218
19220
  return { ignoreStateless: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noMultiComp ?? {} : {}).ignoreStateless ?? false };
@@ -19509,7 +19511,7 @@ const noMultiComp = defineRule({
19509
19511
  id: "no-multi-comp",
19510
19512
  title: "Multiple components in one file",
19511
19513
  severity: "warn",
19512
- recommendation: "Move secondary components into their own files.",
19514
+ recommendation: "Move secondary components into their own files so each component stays easier to find, test, and change.",
19513
19515
  category: "Architecture",
19514
19516
  create: (context) => {
19515
19517
  const settings = resolveSettings$16(context.settings);
@@ -19589,11 +19591,11 @@ const noMutableInDeps = defineRule({
19589
19591
  if (!issue) continue;
19590
19592
  if (issue.kind === "ref-current") context.report({
19591
19593
  node: element,
19592
- message: `Your effect silently never re-runs on "${issue.rootName}.current" because changing a ref doesn't redraw the screen.`
19594
+ message: `Changing "${issue.rootName}.current" does not re-render the component, so this dependency will not make the effect run again.`
19593
19595
  });
19594
19596
  else context.report({
19595
19597
  node: element,
19596
- message: `Your effect silently never re-runs on "${issue.rootName}.*" because values like \`location.pathname\` can change without redrawing the screen.`
19598
+ message: `Values like "${issue.rootName}.*" can change without re-rendering the component, so this dependency will not make the effect run again.`
19597
19599
  });
19598
19600
  }
19599
19601
  });
@@ -20024,7 +20026,7 @@ const noNamespace = defineRule({
20024
20026
  id: "no-namespace",
20025
20027
  title: "Namespaced JSX element",
20026
20028
  severity: "warn",
20027
- recommendation: "Drop the namespace and use a plain (Pascal-cased) component or DOM tag.",
20029
+ recommendation: "Use a plain component or DOM tag because React cannot render JSX namespaced names like `ns:Foo`.",
20028
20030
  create: (context) => ({
20029
20031
  JSXOpeningElement(node) {
20030
20032
  if (!isNodeOfType(node.name, "JSXNamespacedName")) return;
@@ -20056,7 +20058,7 @@ const noNestedComponentDefinition = defineRule({
20056
20058
  tags: ["test-noise", "react-jsx-only"],
20057
20059
  severity: "error",
20058
20060
  category: "Correctness",
20059
- recommendation: "Move to a separate file or to module scope above the parent component",
20061
+ recommendation: "Move it to module scope or a separate file so React does not recreate the component and erase its state on every parent render.",
20060
20062
  create: (context) => {
20061
20063
  const componentStack = [];
20062
20064
  return {
@@ -20121,7 +20123,7 @@ const noNoninteractiveElementInteractions = defineRule({
20121
20123
  });
20122
20124
  //#endregion
20123
20125
  //#region src/plugin/rules/a11y/no-noninteractive-element-to-interactive-role.ts
20124
- const buildMessage$11 = (tag, role) => `Screen reader users get confused because role \`${role}\` makes \`<${tag}>\` act interactive when it isn't, so use a real interactive element instead.`;
20126
+ const buildMessage$11 = (tag, role) => `Role \`${role}\` gives \`<${tag}>\` interactive semantics even though the element is noninteractive, so screen reader users get the wrong controls.`;
20125
20127
  const DEFAULT_ALLOWED_ROLES = {
20126
20128
  ul: [
20127
20129
  "menu",
@@ -20960,13 +20962,13 @@ const noRandomKey = defineRule({
20960
20962
  if (!freshDescription) return;
20961
20963
  context.report({
20962
20964
  node: node.value,
20963
- message: `Your users lose typed input, focus & scroll position because \`key={${freshDescription}}\` makes a new key every render, so React rebuilds every item. Use a stable id from the item.`
20965
+ message: `A changing key makes React rebuild each item, which can reset typed input, focus, and scroll position. Use a stable id from the item instead of \`key={${freshDescription}}\`.`
20964
20966
  });
20965
20967
  } })
20966
20968
  });
20967
20969
  //#endregion
20968
20970
  //#region src/plugin/rules/react-builtins/no-react-children.ts
20969
- const MESSAGE$14 = "`React.Children` breaks easily when the children change shape.";
20971
+ const MESSAGE$14 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
20970
20972
  const isChildrenIdentifier = (node, contextNode) => {
20971
20973
  if (!isNodeOfType(node, "Identifier") || node.name !== "Children") return false;
20972
20974
  return isImportedFromModule(contextNode, "Children", "react");
@@ -20980,10 +20982,10 @@ const isReactNamespaceMember = (node, contextNode) => {
20980
20982
  };
20981
20983
  const noReactChildren = defineRule({
20982
20984
  id: "no-react-children",
20983
- title: "Use of React.Children",
20985
+ title: "React.Children is fragile when child shape changes",
20984
20986
  severity: "warn",
20985
20987
  defaultEnabled: false,
20986
- recommendation: "Pass children as props or render them directly instead of using React.Children.",
20988
+ recommendation: "Pass children as explicit props or render them directly so child shape changes do not break traversal logic.",
20987
20989
  category: "Architecture",
20988
20990
  create: (context) => ({ CallExpression(node) {
20989
20991
  const calleeOuter = stripParenExpression(node.callee);
@@ -21081,7 +21083,7 @@ const reportTestUtilsImports = (node, context) => {
21081
21083
  };
21082
21084
  const noReactDomDeprecatedApis = defineRule({
21083
21085
  id: "no-react-dom-deprecated-apis",
21084
- title: "Deprecated react-dom APIs",
21086
+ title: "Deprecated react-dom APIs break in React 19",
21085
21087
  requires: ["react:18"],
21086
21088
  tags: ["test-noise", "migration-hint"],
21087
21089
  severity: "warn",
@@ -21098,7 +21100,7 @@ const noReactDomDeprecatedApis = defineRule({
21098
21100
  });
21099
21101
  const noReact19DeprecatedApis = defineRule({
21100
21102
  id: "no-react19-deprecated-apis",
21101
- title: "Deprecated React 19 APIs",
21103
+ title: "React 19 API migration can break callers",
21102
21104
  requires: ["react:19"],
21103
21105
  tags: ["test-noise", "migration-hint"],
21104
21106
  severity: "warn",
@@ -21206,7 +21208,7 @@ const noRedundantRoles = defineRule({
21206
21208
  title: "Redundant ARIA role",
21207
21209
  tags: ["react-jsx-only"],
21208
21210
  severity: "warn",
21209
- recommendation: "Remove `role` attributes that match what the element already does.",
21211
+ recommendation: "Remove redundant `role` attributes so assistive tech reads the element's native semantics without extra noise.",
21210
21212
  category: "Accessibility",
21211
21213
  create: (context) => {
21212
21214
  const settings = resolveSettings$13(context.settings);
@@ -21276,7 +21278,7 @@ const noRenderInRender = defineRule({
21276
21278
  title: "Component rendered by inline function call",
21277
21279
  severity: "warn",
21278
21280
  tags: ["test-noise"],
21279
- recommendation: "Make it a named component, like `const ListItem = ({ item }) => <div>{item.name}</div>`.",
21281
+ recommendation: "Make it a named component so React preserves its identity and does not remount its state.",
21280
21282
  create: (context) => ({ JSXExpressionContainer(node) {
21281
21283
  const expression = node.expression;
21282
21284
  if (!isNodeOfType(expression, "CallExpression")) return;
@@ -21295,7 +21297,7 @@ const noRenderInRender = defineRule({
21295
21297
  const RENDER_PROP_PATTERN = /^render[A-Z]/;
21296
21298
  const noRenderPropChildren = defineRule({
21297
21299
  id: "no-render-prop-children",
21298
- title: "Too many render props",
21300
+ title: "Render-prop slots make this component hard to extend",
21299
21301
  tags: ["test-noise"],
21300
21302
  severity: "warn",
21301
21303
  recommendation: "Swap `renderXxx` props for child components like `<Modal.Header>` or plain `children`, so the parent doesn't control every slot.",
@@ -22171,7 +22173,7 @@ const noSelfUpdatingEffect = defineRule({
22171
22173
  reportedStateNames.add(stateName);
22172
22174
  context.report({
22173
22175
  node: setterCall,
22174
- message: `${setterCall.callee.name}() loops forever because it sets \`${stateName}\`, the same state this effect watches.`
22176
+ message: `${setterCall.callee.name}() updates \`${stateName}\`, which is also in this effect's dependency list. Guard the update or move the derivation out of the effect.`
22175
22177
  });
22176
22178
  }
22177
22179
  }
@@ -22203,13 +22205,13 @@ const getParentComponent = (node) => {
22203
22205
  };
22204
22206
  //#endregion
22205
22207
  //#region src/plugin/rules/react-builtins/no-set-state.ts
22206
- const MESSAGE$12 = "Your project discourages `this.setState` here.";
22208
+ const MESSAGE$12 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
22207
22209
  const noSetState = defineRule({
22208
22210
  id: "no-set-state",
22209
- title: "Use of this.setState",
22211
+ title: "Local class state forbidden",
22210
22212
  severity: "warn",
22211
22213
  defaultEnabled: false,
22212
- recommendation: "Lift state up or use an external store instead of `this.setState`.",
22214
+ recommendation: "Lift state up or use an external store so class-local state does not hide ownership.",
22213
22215
  category: "Architecture",
22214
22216
  create: (context) => ({ CallExpression(node) {
22215
22217
  if (!isNodeOfType(node.callee, "MemberExpression")) return;
@@ -22251,7 +22253,7 @@ const noSetStateInRender = defineRule({
22251
22253
  const setterIdentifierName = setterCall.callee.name;
22252
22254
  context.report({
22253
22255
  node: setterCall,
22254
- message: `${setterIdentifierName}() loops forever because it runs during render & triggers another render.`
22256
+ message: `${setterIdentifierName}() triggers another render while rendering. Move it to an effect or event handler, or compute the value during render.`
22255
22257
  });
22256
22258
  }
22257
22259
  };
@@ -22485,9 +22487,9 @@ const resolveSettings$11 = (settings) => {
22485
22487
  };
22486
22488
  const noStringRefs = defineRule({
22487
22489
  id: "no-string-refs",
22488
- title: "Use of string refs",
22490
+ title: "String refs are legacy and fragile",
22489
22491
  severity: "warn",
22490
- recommendation: "Use a callback ref (`ref={(node) => { this.foo = node }}`) or `useRef` instead of string refs.",
22492
+ recommendation: "Use a callback ref or `useRef` so ref ownership is explicit and not tied to legacy string lookup.",
22491
22493
  create: (context) => {
22492
22494
  const { noTemplateLiterals = false } = resolveSettings$11(context.settings);
22493
22495
  const isTestlikeFile = isTestlikeFilename(context.filename);
@@ -22566,7 +22568,7 @@ const noThisInSfc = defineRule({
22566
22568
  id: "no-this-in-sfc",
22567
22569
  title: "this used in function component",
22568
22570
  severity: "warn",
22569
- recommendation: "Read from the `props` argument instead of `this.props`.",
22571
+ recommendation: "Read from the `props` argument because function components do not have a React instance `this`.",
22570
22572
  create: (context) => {
22571
22573
  const configured = (context.settings?.react)?.createClass;
22572
22574
  const customClassFactoryNames = /* @__PURE__ */ new Set();
@@ -22710,10 +22712,10 @@ const noUncontrolledInput = defineRule({
22710
22712
  const hasAllowedPartner = VALUE_PARTNER_ATTRIBUTES.some((partnerAttributeName) => findJsxAttribute(attributes, partnerAttributeName));
22711
22713
  if (isNodeOfType(valueAttribute.value, "JSXExpressionContainer") && isNodeOfType(valueAttribute.value.expression, "Identifier") && undefinedInitialStateNames.has(valueAttribute.value.expression.name)) {
22712
22714
  const stateName = valueAttribute.value.expression.name;
22713
- const partnerHint = hasAllowedPartner ? "Give useState a starting value" : "Give useState a starting value & add onChange (or readOnly)";
22715
+ const partnerHint = hasAllowedPartner ? "Give useState a starting value" : "Give useState a starting value and add onChange (or readOnly)";
22714
22716
  context.report({
22715
22717
  node: child,
22716
- message: `Your users hit a console warning & a field that can reset because "${stateName}" starts undefined, so <${tagName} value={${stateName}}> flips from uncontrolled to controlled. ${partnerHint} (e.g. \`useState("")\`).`
22718
+ message: `This can trigger a console warning and reset the field because "${stateName}" starts undefined, so <${tagName} value={${stateName}}> flips from uncontrolled to controlled. ${partnerHint} (e.g. \`useState("")\`).`
22717
22719
  });
22718
22720
  return;
22719
22721
  }
@@ -22775,7 +22777,7 @@ const noUnescapedEntities = defineRule({
22775
22777
  title: "Unescaped entities in JSX",
22776
22778
  severity: "warn",
22777
22779
  defaultEnabled: false,
22778
- recommendation: "Replace bare `'` / `\"` / `>` / `}` characters in JSX text with HTML entities.",
22780
+ recommendation: "Replace bare `'` / `\"` / `>` / `}` characters with HTML entities so literal UI text is encoded consistently.",
22779
22781
  create: (context) => ({ JSXText(node) {
22780
22782
  const value = node.value;
22781
22783
  for (const character of value) if (character in ESCAPED_VERSIONS) {
@@ -23710,7 +23712,7 @@ const noUnknownProperty = defineRule({
23710
23712
  id: "no-unknown-property",
23711
23713
  title: "Unknown DOM property",
23712
23714
  severity: "warn",
23713
- recommendation: "Use the prop name React expects, like `className`, `htmlFor`, or `tabIndex`.",
23715
+ recommendation: "Use the prop name React expects, like `className`, `htmlFor`, or `tabIndex`, so the attribute is applied correctly.",
23714
23716
  create: (context) => {
23715
23717
  const { ignore = [], requireDataLowercase = false } = resolveSettings$10(context.settings);
23716
23718
  const ignoreSet = new Set(ignore);
@@ -23796,7 +23798,7 @@ const UNSAFE_ALIASES = new Set([
23796
23798
  "componentWillReceiveProps",
23797
23799
  "componentWillUpdate"
23798
23800
  ]);
23799
- const buildMessage$6 = (methodName) => `\`${methodName}\` causes subtle bugs & React is removing it.`;
23801
+ const buildMessage$6 = (methodName) => `\`${methodName}\` runs during unsafe legacy render timing and is deprecated, so React may double-invoke or remove it.`;
23800
23802
  const resolveSettings$9 = (settings) => {
23801
23803
  const reactDoctor = settings?.["react-doctor"];
23802
23804
  return { checkAliases: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noUnsafe ?? {} : {}).checkAliases ?? false };
@@ -23829,7 +23831,7 @@ const noUnsafe = defineRule({
23829
23831
  id: "no-unsafe",
23830
23832
  title: "Unsafe legacy lifecycle method",
23831
23833
  severity: "warn",
23832
- recommendation: "Replace `UNSAFE_componentWillMount` / `…WillReceiveProps` / `…WillUpdate` with the modern equivalents.",
23834
+ recommendation: "Move setup to `constructor` or `componentDidMount`, prop-derived state to `getDerivedStateFromProps`, and update side effects to `componentDidUpdate` so React does not rely on deprecated unsafe lifecycles.",
23833
23835
  create: (context) => {
23834
23836
  const { checkAliases } = resolveSettings$9(context.settings);
23835
23837
  const flagsUnsafePrefix = isReactVersionAtLeast(getConfiguredReactMajorMinor(context.settings), 16, 3);
@@ -24085,7 +24087,7 @@ const noUnstableNestedComponents = defineRule({
24085
24087
  id: "no-unstable-nested-components",
24086
24088
  title: "Component defined inside a component",
24087
24089
  severity: "warn",
24088
- recommendation: "Move nested components to the top of the file. Never define one inside another.",
24090
+ recommendation: "Move nested components to module scope so React does not remount them and lose state on every render.",
24089
24091
  category: "Performance",
24090
24092
  create: (context) => {
24091
24093
  const settings = resolveSettings$8(context.settings);
@@ -24259,7 +24261,7 @@ const noWideLetterSpacing = defineRule({
24259
24261
  //#endregion
24260
24262
  //#region src/plugin/rules/react-builtins/no-will-update-set-state.ts
24261
24263
  const LIFECYCLE_NAMES = new Set(["componentWillUpdate", "UNSAFE_componentWillUpdate"]);
24262
- const MESSAGE$9 = "This can loop forever & freeze the component.";
24264
+ const MESSAGE$9 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
24263
24265
  const resolveSettings$7 = (settings) => {
24264
24266
  const reactDoctor = settings?.["react-doctor"];
24265
24267
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noWillUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -24282,7 +24284,7 @@ const noWillUpdateSetState = defineRule({
24282
24284
  id: "no-will-update-set-state",
24283
24285
  title: "setState in componentWillUpdate",
24284
24286
  severity: "warn",
24285
- recommendation: "Don't set state in `componentWillUpdate`. Use `getDerivedStateFromProps` or `componentDidUpdate` instead.",
24287
+ recommendation: "Avoid setState in componentWillUpdate because it can loop forever; derive state before render or move guarded updates to componentDidUpdate.",
24286
24288
  create: (context) => {
24287
24289
  const { mode } = resolveSettings$7(context.settings);
24288
24290
  const activeLifecycleNames = isReactBelow16_3(context.settings) ? new Set(["componentWillUpdate"]) : LIFECYCLE_NAMES;
@@ -24316,7 +24318,7 @@ const noZIndex9999 = defineRule({
24316
24318
  const zValue = getStylePropertyNumberValue(property);
24317
24319
  if (zValue !== null && Math.abs(zValue) >= 1e3) context.report({
24318
24320
  node: property,
24319
- message: `z-index ${zValue} is way too high & usually hides a layering bug instead of fixing it, so use a small set scale, like 1 to 50.`
24321
+ message: `z-index ${zValue} is unusually high and can hide a layering bug instead of fixing it. Use a small set scale, like 1 to 50.`
24320
24322
  });
24321
24323
  }
24322
24324
  },
@@ -24499,12 +24501,12 @@ const UTILITY_FILE_BASENAMES = new Set([
24499
24501
  ]);
24500
24502
  //#endregion
24501
24503
  //#region src/plugin/rules/react-builtins/only-export-components.ts
24502
- const NAMED_EXPORT_MESSAGE = "Fast Refresh stops working when a file exports non-components.";
24503
- const ANONYMOUS_MESSAGE = "Fast Refresh can't track an unnamed component & full-reloads instead.";
24504
- const EXPORT_ALL_MESSAGE = "`export *` hides what's exported, so Fast Refresh stops working.";
24505
- const REACT_CONTEXT_MESSAGE = "Fast Refresh stops working when a file exports a context too.";
24506
- const LOCAL_COMPONENT_MESSAGE = "Fast Refresh skips this component because it isn't exported.";
24507
- const NO_EXPORT_MESSAGE = "Fast Refresh can't track this component because the file exports nothing.";
24504
+ const NAMED_EXPORT_MESSAGE = "This file exports non-components, so Fast Refresh can't safely preserve component state.";
24505
+ const ANONYMOUS_MESSAGE = "This component is unnamed, so Fast Refresh can't track it and falls back to a full reload.";
24506
+ const EXPORT_ALL_MESSAGE = "`export *` hides what's exported, so Fast Refresh can't safely preserve component state.";
24507
+ const REACT_CONTEXT_MESSAGE = "This file exports a context with components, so Fast Refresh can't safely preserve component state.";
24508
+ const LOCAL_COMPONENT_MESSAGE = "This component is not exported, so Fast Refresh skips it and local edits can full-reload.";
24509
+ const NO_EXPORT_MESSAGE = "This file exports nothing, so Fast Refresh can't track the component and local edits can full-reload.";
24508
24510
  const DEFAULT_REACT_HOCS = [
24509
24511
  "memo",
24510
24512
  "forwardRef",
@@ -24657,7 +24659,7 @@ const onlyExportComponents = defineRule({
24657
24659
  id: "only-export-components",
24658
24660
  title: "Non-component export in component file",
24659
24661
  severity: "warn",
24660
- recommendation: "Move non-component exports out of files that export components.",
24662
+ recommendation: "Move non-component exports out of component files so Fast Refresh can preserve component state instead of full-reloading.",
24661
24663
  category: "Architecture",
24662
24664
  create: (context) => {
24663
24665
  const settings = resolveSettings$6(context.settings);
@@ -24878,10 +24880,10 @@ const isChildrenMemberExpression = (node) => {
24878
24880
  };
24879
24881
  const preactNoChildrenLength = defineRule({
24880
24882
  id: "preact-no-children-length",
24881
- title: "Array methods on props.children",
24883
+ title: "Array methods on Preact children can crash",
24882
24884
  requires: ["preact"],
24883
24885
  severity: "warn",
24884
- recommendation: "Wrap with `toChildArray(children)` from `preact` before accessing array methods or `.length`.",
24886
+ recommendation: "Wrap with `toChildArray(children)` because Preact's `props.children` is not always an array and array methods can crash.",
24885
24887
  create: (context) => ({ MemberExpression(node) {
24886
24888
  if (node.computed) return;
24887
24889
  if (!isNodeOfType(node.property, "Identifier")) return;
@@ -24915,10 +24917,10 @@ const REACT_HOOK_NAMES = new Set([
24915
24917
  const buildMessage$4 = (importedNames) => `Your users hit \`__H\` undefined errors because importing ${importedNames.map((innerName) => `\`${innerName}\``).join(", ")} from \`react\` in a pure-Preact project loads a second copy of the hook state, so import from \`preact/hooks\` (or \`preact/compat\`) instead.`;
24916
24918
  const preactNoReactHooksImport = defineRule({
24917
24919
  id: "preact-no-react-hooks-import",
24918
- title: "Hooks imported from react",
24920
+ title: "React hook imports break pure Preact hook state",
24919
24921
  requires: ["pure-preact"],
24920
24922
  severity: "warn",
24921
- recommendation: "Replace `from \"react\"` with `from \"preact/hooks\"` (or `from \"preact/compat\"` if other React API surface is needed).",
24923
+ recommendation: "Import hooks from `preact/hooks` so they share Preact's renderer state instead of loading a second hook implementation.",
24922
24924
  create: (context) => ({ ImportDeclaration(node) {
24923
24925
  const source = node.source;
24924
24926
  if (!isNodeOfType(source, "Literal") || source.value !== "react") return;
@@ -24978,7 +24980,7 @@ const preactNoRenderArguments = defineRule({
24978
24980
  title: "render() reads props from arguments",
24979
24981
  requires: ["preact"],
24980
24982
  severity: "warn",
24981
- recommendation: "Read state/props from `this.props` / `this.state` inside `render()` instead of declaring positional parameters.",
24983
+ recommendation: "Read from `this.props` and `this.state` because `preact/compat` uses React's parameterless `render()` and positional props/state become undefined.",
24982
24984
  create: (context) => ({ MethodDefinition(node) {
24983
24985
  if (!isInstanceMethodNamedRender(node)) return;
24984
24986
  if (!isInsideEs6Component$1(node)) return;
@@ -25000,7 +25002,7 @@ const preactPreferOndblclick = defineRule({
25000
25002
  title: "onDoubleClick instead of onDblClick",
25001
25003
  requires: ["pure-preact"],
25002
25004
  severity: "warn",
25003
- recommendation: "Rename the handler from `onDoubleClick` to `onDblClick` to match the DOM event name.",
25005
+ recommendation: "Rename `onDoubleClick` to `onDblClick` because Preact core listens for the DOM `dblclick` event name and `onDoubleClick` never fires.",
25004
25006
  create: (context) => ({ JSXOpeningElement(node) {
25005
25007
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
25006
25008
  const tagName = node.name.name;
@@ -25072,8 +25074,8 @@ const preferDynamicImport = defineRule({
25072
25074
  });
25073
25075
  //#endregion
25074
25076
  //#region src/plugin/rules/react-builtins/prefer-es6-class.ts
25075
- const ALWAYS_MESSAGE$1 = "`createReactClass` is legacy & adds a dependency.";
25076
- const NEVER_MESSAGE$1 = "This component is defined inconsistently.";
25077
+ const ALWAYS_MESSAGE$1 = "`createReactClass` is legacy and adds a dependency, so this component diverges from modern React class syntax.";
25078
+ const NEVER_MESSAGE$1 = "This component uses an ES6 class where `createReactClass` is configured, so component style is inconsistent across the codebase.";
25077
25079
  const resolveSettings$5 = (settings) => {
25078
25080
  const reactDoctor = settings?.["react-doctor"];
25079
25081
  if (typeof reactDoctor !== "object" || reactDoctor === null) return {};
@@ -25084,7 +25086,7 @@ const preferEs6Class = defineRule({
25084
25086
  title: "createClass instead of ES6 class",
25085
25087
  severity: "warn",
25086
25088
  defaultEnabled: false,
25087
- recommendation: "Pick one component style for the whole codebase: `class extends React.Component` (default) or `createReactClass` (legacy).",
25089
+ recommendation: "Pick one component style so readers do not have to switch between legacy `createReactClass` patterns and modern class components.",
25088
25090
  category: "Architecture",
25089
25091
  create: (context) => {
25090
25092
  const { mode = "always" } = resolveSettings$5(context.settings);
@@ -25243,7 +25245,7 @@ const preferExplicitVariants = defineRule({
25243
25245
  });
25244
25246
  //#endregion
25245
25247
  //#region src/plugin/rules/react-builtins/prefer-function-component.ts
25246
- const MESSAGE$7 = "This class component is harder to maintain than a function component.";
25248
+ const MESSAGE$7 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
25247
25249
  const resolveSettings$4 = (settings) => {
25248
25250
  const reactDoctor = settings?.["react-doctor"];
25249
25251
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.preferFunctionComponent ?? {} : {};
@@ -25269,7 +25271,7 @@ const preferFunctionComponent = defineRule({
25269
25271
  title: "Class component instead of function",
25270
25272
  severity: "warn",
25271
25273
  defaultEnabled: false,
25272
- recommendation: "Re-write the class component as a function component using hooks.",
25274
+ recommendation: "Rewrite the class component as a function component so state and effects use modern hook patterns instead of class lifecycles.",
25273
25275
  category: "Architecture",
25274
25276
  create: (context) => {
25275
25277
  const settings = resolveSettings$4(context.settings);
@@ -25621,7 +25623,7 @@ const preferTagOverRole = defineRule({
25621
25623
  title: "Role used instead of HTML tag",
25622
25624
  tags: ["react-jsx-only"],
25623
25625
  severity: "warn",
25624
- recommendation: "Replace `role` with the matching HTML element when one exists.",
25626
+ recommendation: "Use the matching HTML element when one exists so browsers and assistive tech get native semantics.",
25625
25627
  category: "Accessibility",
25626
25628
  create: (context) => ({ JSXOpeningElement(node) {
25627
25629
  const tag = getElementType(node, context.settings);
@@ -25927,7 +25929,7 @@ const preferUseReducer = defineRule({
25927
25929
  title: "Many related useState calls",
25928
25930
  tags: ["test-noise"],
25929
25931
  severity: "warn",
25930
- recommendation: "Group related state: `const [state, dispatch] = useReducer(reducer, { field1, field2, ... })`",
25932
+ recommendation: "Group related state in `useReducer` so one logical update does not fan out into separate renders.",
25931
25933
  create: (context) => {
25932
25934
  const reportExcessiveUseState = (body, componentName) => {
25933
25935
  if (!isNodeOfType(body, "BlockStatement")) return;
@@ -25959,7 +25961,7 @@ const preferUseReducer = defineRule({
25959
25961
  //#region src/plugin/rules/tanstack-query/query-destructure-result.ts
25960
25962
  const queryDestructureResult = defineRule({
25961
25963
  id: "query-destructure-result",
25962
- title: "Destructure TanStack Query result",
25964
+ title: "Whole query result subscribes to every field",
25963
25965
  tags: ["test-noise"],
25964
25966
  requires: ["tanstack-query"],
25965
25967
  severity: "error",
@@ -26012,7 +26014,7 @@ const queryNoQueryInEffect = defineRule({
26012
26014
  tags: ["test-noise"],
26013
26015
  requires: ["tanstack-query"],
26014
26016
  severity: "warn",
26015
- recommendation: "React Query refetches automatically via queryKey changes and the `enabled` option. A manual refetch() in useEffect is usually unnecessary.",
26017
+ recommendation: "Use `queryKey` changes or `enabled` so React Query schedules the fetch once instead of refetching again from `useEffect`.",
26016
26018
  create: (context) => ({ CallExpression(node) {
26017
26019
  if (!isHookCall$1(node, EFFECT_HOOK_NAMES$1)) return;
26018
26020
  const callback = getEffectCallback(node);
@@ -26335,7 +26337,7 @@ const reduxUseselectorInlineDerivation = defineRule({
26335
26337
  severity: "warn",
26336
26338
  category: "Performance",
26337
26339
  disabledBy: ["react-compiler"],
26338
- recommendation: "Select the raw slice and derive with `useMemo`, or use `createSelector` from `reselect`.",
26340
+ recommendation: "Select the raw slice and memoize derivation so Redux actions do not rebuild a collection and redraw this component.",
26339
26341
  create: (context) => {
26340
26342
  let aliases = /* @__PURE__ */ new Set();
26341
26343
  return {
@@ -26395,7 +26397,7 @@ const reduxUseselectorReturnsNewCollection = defineRule({
26395
26397
  severity: "warn",
26396
26398
  category: "Performance",
26397
26399
  disabledBy: ["react-compiler"],
26398
- recommendation: "Return a primitive, split into multiple useSelector calls, or pass `shallowEqual` from `react-redux` as the second argument.",
26400
+ recommendation: "Return a stable selected value, split selectors, or pass `shallowEqual` so every Redux action does not redraw this component.",
26399
26401
  create: (context) => {
26400
26402
  let aliases = /* @__PURE__ */ new Set();
26401
26403
  return {
@@ -26421,7 +26423,7 @@ const renderingAnimateSvgWrapper = defineRule({
26421
26423
  title: "Animating an SVG directly",
26422
26424
  tags: ["test-noise"],
26423
26425
  severity: "warn",
26424
- recommendation: "Wrap the SVG: `<motion.div animate={...}><svg>...</svg></motion.div>`",
26426
+ recommendation: "Wrap the SVG in a motion element so animation props apply to a stable wrapper instead of the SVG node itself.",
26425
26427
  create: (context) => ({ JSXOpeningElement(node) {
26426
26428
  if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "svg") return;
26427
26429
  if (node.attributes?.some((attribute) => isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && MOTION_ANIMATE_PROPS.has(attribute.name.name))) context.report({
@@ -26571,7 +26573,7 @@ const renderingHydrationMismatchTime = defineRule({
26571
26573
  if (hasSuppressHydrationWarningAttribute(findOpeningElementOfChild(node))) return;
26572
26574
  context.report({
26573
26575
  node,
26574
- message: `This breaks hydration because ${matched.display} in JSX gives a different value on the server than in the browser, so move it into useEffect+useState to run only in the browser, or add suppressHydrationWarning to the parent if it's on purpose`
26576
+ message: `This can cause a hydration mismatch because ${matched.display} in JSX gives a different value on the server than in the browser. Move it into useEffect+useState to run only in the browser, or add suppressHydrationWarning to the parent if it's on purpose.`
26575
26577
  });
26576
26578
  return;
26577
26579
  }
@@ -26580,7 +26582,7 @@ const renderingHydrationMismatchTime = defineRule({
26580
26582
  if (hasSuppressHydrationWarningAttribute(findOpeningElementOfChild(node))) return;
26581
26583
  context.report({
26582
26584
  node: child,
26583
- message: `This breaks hydration because ${pattern.display} reached from JSX gives a different value on the server than in the browser, so move it into useEffect+useState to run only in the browser, or add suppressHydrationWarning to the parent if it's on purpose`
26585
+ message: `This can cause a hydration mismatch because ${pattern.display} reached from JSX gives a different value on the server than in the browser. Move it into useEffect+useState to run only in the browser, or add suppressHydrationWarning to the parent if it's on purpose.`
26584
26586
  });
26585
26587
  return;
26586
26588
  }
@@ -26859,7 +26861,7 @@ const requireRenderReturn = defineRule({
26859
26861
  id: "require-render-return",
26860
26862
  title: "Render method does not return",
26861
26863
  severity: "error",
26862
- recommendation: "Return JSX from your component's `render` method.",
26864
+ recommendation: "Return JSX or `null` from `render` so the component intentionally shows something or nothing.",
26863
26865
  create: (context) => {
26864
26866
  const checkFunction = (functionNode) => {
26865
26867
  const host = resolveRenderHost(functionNode);
@@ -27181,7 +27183,7 @@ const rerenderLazyRefInit = defineRule({
27181
27183
  tags: ["test-noise"],
27182
27184
  severity: "warn",
27183
27185
  category: "Performance",
27184
- recommendation: "Set it up only once: `const ref = useRef<T | null>(null); if (ref.current === null) ref.current = expensiveCall();`",
27186
+ recommendation: "Initialize the ref lazily so expensive values are not rebuilt and discarded on every render.",
27185
27187
  create: (context) => ({ CallExpression(node) {
27186
27188
  if (!isHookCall$1(node, "useRef") || !node.arguments?.length) return;
27187
27189
  const initializer = node.arguments[0];
@@ -27208,7 +27210,7 @@ const rerenderLazyStateInit = defineRule({
27208
27210
  tags: ["test-noise"],
27209
27211
  severity: "warn",
27210
27212
  category: "Performance",
27211
- recommendation: "Wrap in an arrow function so it only runs once: `useState(() => expensiveComputation())`",
27213
+ recommendation: "Wrap expensive initial state in an arrow function so the initializer does not rerun and get thrown away on every render.",
27212
27214
  create: (context) => ({ CallExpression(node) {
27213
27215
  if (!isHookCall$1(node, "useState") || !node.arguments?.length) return;
27214
27216
  const initializer = node.arguments[0];
@@ -27491,7 +27493,7 @@ const rerenderTransitionsScroll = defineRule({
27491
27493
  }
27492
27494
  context.report({
27493
27495
  node: setStateCall,
27494
- message: `This causes jank because setState in a "${eventName}" handler redraws the screen many times a second, so wrap it in startTransition, use useDeferredValue, or keep the value in a ref & throttle with requestAnimationFrame`
27496
+ message: `This can make scrolling stutter because setState in a "${eventName}" handler redraws on every event. Wrap it in startTransition, use useDeferredValue, or keep the value in a ref and throttle with requestAnimationFrame.`
27495
27497
  });
27496
27498
  } })
27497
27499
  });
@@ -27511,7 +27513,7 @@ const rnAnimateLayoutProperty = defineRetiredRule({
27511
27513
  tags: ["test-noise"],
27512
27514
  requires: ["react-native"],
27513
27515
  severity: "warn",
27514
- recommendation: "Reanimated useAnimatedStyle runs on the UI thread; layout-affecting properties driven by animation helpers, interpolate, or shared values are valid."
27516
+ recommendation: "Retired: Reanimated `useAnimatedStyle` can safely drive layout-affecting properties on the UI thread, so this pattern should not be flagged."
27515
27517
  });
27516
27518
  //#endregion
27517
27519
  //#region src/plugin/rules/react-native/rn-animation-reaction-as-derived.ts
@@ -27542,7 +27544,7 @@ const rnAnimationReactionAsDerived = defineRule({
27542
27544
  if (!isValueAssignment && !isSetCall) return;
27543
27545
  context.report({
27544
27546
  node,
27545
- message: "Your users can see a stale value when this useAnimatedReaction only copies one value to another."
27547
+ message: "This useAnimatedReaction only copies one shared value into another, so it can miss Reanimated's derived-value dependency tracking."
27546
27548
  });
27547
27549
  } })
27548
27550
  });
@@ -27559,17 +27561,17 @@ const JS_BOTTOM_SHEET_PACKAGES = new Set([
27559
27561
  ]);
27560
27562
  const rnBottomSheetPreferNative = defineRule({
27561
27563
  id: "rn-bottom-sheet-prefer-native",
27562
- title: "JS bottom sheet over native Modal",
27564
+ title: "JS bottom sheet misses native sheet behavior",
27563
27565
  tags: ["test-noise"],
27564
27566
  requires: ["react-native"],
27565
27567
  severity: "warn",
27566
- recommendation: "On RN v7+, use `<Modal presentationStyle=\"formSheet\">` for native gestures and snap points.",
27568
+ recommendation: "On RN v7+, use `<Modal presentationStyle=\"formSheet\">` so the sheet uses platform-native gestures, detents, accessibility, and presentation behavior.",
27567
27569
  create: (context) => ({ ImportDeclaration(node) {
27568
27570
  const source = node.source?.value;
27569
27571
  if (typeof source !== "string" || !JS_BOTTOM_SHEET_PACKAGES.has(source)) return;
27570
27572
  context.report({
27571
27573
  node,
27572
- message: `Your users feel a less native bottom sheet with ${source}.`
27574
+ message: `Users get JS-driven sheet gestures and presentation with ${source}, instead of the platform-native formSheet behavior.`
27573
27575
  });
27574
27576
  } })
27575
27577
  });
@@ -27655,13 +27657,13 @@ const rnDetoxMissingAwait = defineRule({
27655
27657
  if (root.calleeName === "waitFor") {
27656
27658
  context.report({
27657
27659
  node,
27658
- message: "This Detox `waitFor(...)` chain isn't awaited. Prepend `await`."
27660
+ message: "This Detox `waitFor` chain isn't awaited, so the test can continue before the condition settles. Prepend `await`."
27659
27661
  });
27660
27662
  return;
27661
27663
  }
27662
27664
  if (root.calleeName === "expect" && isDetoxExpectSubject(root.rootCall)) context.report({
27663
27665
  node,
27664
- message: "This Detox `expect(element(...))` assertion isn't awaited. Prepend `await`."
27666
+ message: "This Detox `expect(element)` assertion isn't awaited, so the test can pass or fail before the assertion settles. Prepend `await`."
27665
27667
  });
27666
27668
  } };
27667
27669
  }
@@ -28292,7 +28294,7 @@ const rnNoLegacyExpoPackages = defineRule({
28292
28294
  tags: ["test-noise"],
28293
28295
  requires: ["react-native"],
28294
28296
  severity: "warn",
28295
- recommendation: "These Expo packages are no longer maintained. Switch to the recommended replacement package.",
28297
+ recommendation: "Switch to the maintained replacement package so users are not stuck with unfixed bugs in deprecated Expo packages.",
28296
28298
  create: (context) => ({ ImportDeclaration(node) {
28297
28299
  const source = node.source?.value;
28298
28300
  if (typeof source !== "string") return;
@@ -28374,7 +28376,7 @@ const rnNoNonNativeNavigator = defineRule({
28374
28376
  if (!NON_NATIVE_NAVIGATOR_PACKAGES.get(source)) return;
28375
28377
  context.report({
28376
28378
  node,
28377
- message: `Your users feel less native transitions when ${source} uses a JS navigator.`
28379
+ message: `Users get JS-driven transitions and gestures from ${source}, instead of platform-native navigation behavior.`
28378
28380
  });
28379
28381
  } })
28380
28382
  });
@@ -28680,7 +28682,7 @@ const collectReturnedJsxElements = (expression) => {
28680
28682
  };
28681
28683
  const rnNoRenderitemKey = defineRule({
28682
28684
  id: "rn-no-renderitem-key",
28683
- title: "Useless key on renderItem JSX",
28685
+ title: "renderItem key is ignored by React Native lists",
28684
28686
  tags: ["test-noise"],
28685
28687
  requires: ["react-native"],
28686
28688
  severity: "warn",
@@ -28821,7 +28823,7 @@ const rnNoSetNativeProps = defineRule({
28821
28823
  //#region src/plugin/rules/react-native/rn-no-single-element-style-array.ts
28822
28824
  const rnNoSingleElementStyleArray = defineRule({
28823
28825
  id: "rn-no-single-element-style-array",
28824
- title: "Single-element style array",
28826
+ title: "Single-element style array adds wasted allocation",
28825
28827
  tags: ["test-noise"],
28826
28828
  requires: ["react-native"],
28827
28829
  severity: "warn",
@@ -28844,11 +28846,11 @@ const rnNoSingleElementStyleArray = defineRule({
28844
28846
  //#region src/plugin/rules/react-native/rn-prefer-content-inset-adjustment.ts
28845
28847
  const rnPreferContentInsetAdjustment = defineRetiredRule({
28846
28848
  id: "rn-prefer-content-inset-adjustment",
28847
- title: "Manual safe-area inset adjustment",
28849
+ title: "Manual safe-area insets can duplicate offsets",
28848
28850
  tags: ["test-noise"],
28849
28851
  requires: ["react-native"],
28850
28852
  severity: "warn",
28851
- recommendation: "Prefer native content inset adjustment only when it replaces manual inset plumbing; SafeAreaView wrappers are valid and intentionally ignored."
28853
+ recommendation: "Retired: SafeAreaView wrappers are valid; prefer native content inset adjustment only when manual inset plumbing causes scroll jumps or duplicated safe-area offsets."
28852
28854
  });
28853
28855
  //#endregion
28854
28856
  //#region src/react-native-dependency-names.ts
@@ -29033,7 +29035,7 @@ const rnPreferPressable = defineRule({
29033
29035
  tags: ["test-noise"],
29034
29036
  requires: ["react-native"],
29035
29037
  severity: "warn",
29036
- recommendation: "Use `<Pressable>` from react-native (or react-native-gesture-handler) instead of the old Touchable* components.",
29038
+ recommendation: "Use `<Pressable>` because Touchable* components are frozen and lack Pressable's state-based feedback and accessibility behavior.",
29037
29039
  create: (context) => ({ ImportDeclaration(node) {
29038
29040
  const source = node.source?.value;
29039
29041
  if (typeof source !== "string" || !TOUCHABLE_SOURCES.has(source)) return;
@@ -29469,7 +29471,7 @@ const roleHasRequiredAriaProps = defineRule({
29469
29471
  title: "Role missing required ARIA props",
29470
29472
  tags: ["react-jsx-only"],
29471
29473
  severity: "error",
29472
- recommendation: "Add every required `aria-*` attribute when you set an interactive role.",
29474
+ recommendation: "Add every required `aria-*` attribute so assistive tech can expose the role's state correctly.",
29473
29475
  category: "Accessibility",
29474
29476
  create: (context) => ({ JSXOpeningElement(node) {
29475
29477
  const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
@@ -32715,16 +32717,16 @@ const roleSupportsAriaProps = defineRule({
32715
32717
  });
32716
32718
  //#endregion
32717
32719
  //#region src/plugin/rules/react-builtins/rules-of-hooks.ts
32718
- const buildTopLevelMessage = (hookName) => `\`${hookName}\` crashes at the top level of a file.`;
32719
- const buildNonComponentMessage = (hookName, functionName) => `\`${hookName}\` crashes inside \`${functionName}\` because it isn't a component or a hook.`;
32720
- const buildConditionalMessage = (hookName) => `\`${hookName}\` crashes when you call it conditionally.`;
32721
- const buildLoopMessage = (hookName) => `\`${hookName}\` crashes when you call it inside a loop.`;
32722
- const buildAsyncMessage = (hookName) => `\`${hookName}\` crashes inside an async function.`;
32723
- const buildClassComponentMessage = (hookName) => `\`${hookName}\` crashes in a class component.`;
32724
- const buildTryMessage = (hookName) => `\`${hookName}\` crashes inside a try/catch/finally block.`;
32720
+ const buildTopLevelMessage = (hookName) => `\`${hookName}\` can only run inside a React component or custom Hook because React needs that render scope to track Hook state.`;
32721
+ const buildNonComponentMessage = (hookName, functionName) => `\`${hookName}\` runs inside \`${functionName}\`, which is not a component or Hook, so React cannot attach Hook state to a render.`;
32722
+ const buildConditionalMessage = (hookName) => `\`${hookName}\` changes Hook order between renders when called conditionally, so React can attach state to the wrong Hook.`;
32723
+ const buildLoopMessage = (hookName) => `\`${hookName}\` can run a different number of times inside a loop, so React can attach state to the wrong Hook.`;
32724
+ const buildAsyncMessage = (hookName) => `\`${hookName}\` runs inside an async function, so React cannot guarantee the same Hook order during render.`;
32725
+ const buildClassComponentMessage = (hookName) => `\`${hookName}\` cannot run in a class component because Hooks require a function component or custom Hook render scope.`;
32726
+ const buildTryMessage = (hookName) => `\`${hookName}\` can be skipped by try/catch/finally control flow, so React can attach state to the wrong Hook.`;
32725
32727
  const buildEffectEventCallMessage = (bindingName) => `\`${bindingName}\` comes from useEffectEvent, so it only works when called from Effects in the same component.`;
32726
32728
  const buildEffectEventAssignmentMessage = (bindingName) => `${buildEffectEventCallMessage(bindingName)} It also breaks if saved in a variable or passed around.`;
32727
- const buildEffectEventPassedDownMessage = () => `A function from useEffectEvent breaks when passed around.`;
32729
+ const buildEffectEventPassedDownMessage = () => `A function from useEffectEvent only works inside Effects in the same component, so passing it around breaks the event/dependency split.`;
32728
32730
  const ASCII_UPPERCASE_A = 65;
32729
32731
  const ASCII_UPPERCASE_Z = 90;
32730
32732
  const EFFECT_HOOK_NAMES = new Set([
@@ -32977,7 +32979,7 @@ const rulesOfHooks = defineRule({
32977
32979
  title: "Hook called conditionally",
32978
32980
  severity: "error",
32979
32981
  tags: ["test-noise"],
32980
- recommendation: "Call hooks at the top level of a React function component or a custom Hook.",
32982
+ recommendation: "Call hooks at the top level of a React function component or custom Hook so React sees the same hook order on every render.",
32981
32983
  category: "Correctness",
32982
32984
  create: (context) => {
32983
32985
  const settings = resolveSettings$3(context.settings);
@@ -33105,13 +33107,13 @@ const rulesOfHooks = defineRule({
33105
33107
  });
33106
33108
  //#endregion
33107
33109
  //#region src/plugin/rules/a11y/scope.ts
33108
- const MESSAGE$3 = "Screen reader users get no help from `scope` here because it only works on `<th>` cells, so remove it.";
33110
+ const MESSAGE$3 = "The `scope` attribute only works on `<th>` cells, so screen readers get no table-header help from it here.";
33109
33111
  const scope = defineRule({
33110
33112
  id: "scope",
33111
33113
  title: "scope attribute on non-th element",
33112
33114
  tags: ["react-jsx-only"],
33113
33115
  severity: "warn",
33114
- recommendation: "Only use `scope` on `<th>` cells.",
33116
+ recommendation: "Remove `scope` from this element or move it to the related `<th>` cell.",
33115
33117
  category: "Accessibility",
33116
33118
  create: (context) => ({ JSXOpeningElement(node) {
33117
33119
  const scopeAttribute = hasJsxProp(node.attributes, "scope");
@@ -33126,7 +33128,7 @@ const scope = defineRule({
33126
33128
  });
33127
33129
  //#endregion
33128
33130
  //#region src/plugin/rules/react-builtins/self-closing-comp.ts
33129
- const MESSAGE$2 = "This tag has no children.";
33131
+ const MESSAGE$2 = "This tag has no children, so the closing tag adds noise without changing output.";
33130
33132
  const resolveSettings$2 = (settings) => {
33131
33133
  const reactDoctor = settings?.["react-doctor"];
33132
33134
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.selfClosingComp ?? {} : {};
@@ -33145,7 +33147,7 @@ const selfClosingComp = defineRule({
33145
33147
  title: "Element not self-closing",
33146
33148
  severity: "warn",
33147
33149
  defaultEnabled: false,
33148
- recommendation: "Use the self-closing form `<X />` for elements with no children.",
33150
+ recommendation: "Use `<X />` for childless elements so empty closing tags do not add noise.",
33149
33151
  category: "Architecture",
33150
33152
  create: (context) => {
33151
33153
  const settings = resolveSettings$2(context.settings);
@@ -33342,9 +33344,9 @@ const getCandidateFromDefaultDeclaration = (node) => {
33342
33344
  };
33343
33345
  const serverAuthActions = defineRule({
33344
33346
  id: "server-auth-actions",
33345
- title: "Server action without auth check",
33347
+ title: "Unauthenticated server action can be called directly",
33346
33348
  severity: "error",
33347
- recommendation: "Add `const session = await auth()` at the top, and throw or redirect if the user isn't allowed before touching any data.",
33349
+ recommendation: "Check auth before touching data because exported server actions can be called directly by unauthenticated clients.",
33348
33350
  create: (context) => {
33349
33351
  let fileHasUseServerDirective = false;
33350
33352
  const customAuthFunctionNames = getReactDoctorStringArraySetting(context.settings, "serverAuthFunctionNames");
@@ -33725,8 +33727,8 @@ const serverSequentialIndependentAwait = defineRule({
33725
33727
  });
33726
33728
  //#endregion
33727
33729
  //#region src/plugin/rules/react-builtins/state-in-constructor.ts
33728
- const ALWAYS_MESSAGE = "This component's state is set up inconsistently.";
33729
- const NEVER_MESSAGE = "This component's state is set up inconsistently.";
33730
+ const ALWAYS_MESSAGE = "This class uses a state field instead of the configured constructor pattern, so state setup is inconsistent across the codebase.";
33731
+ const NEVER_MESSAGE = "This class sets state in the constructor instead of the configured class-field pattern, so state setup is inconsistent across the codebase.";
33730
33732
  const resolveSettings$1 = (settings) => {
33731
33733
  const reactDoctor = settings?.["react-doctor"];
33732
33734
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.stateInConstructor ?? {} : {}).mode ?? "always" };
@@ -33758,7 +33760,7 @@ const stateInConstructor = defineRule({
33758
33760
  title: "State initialized in constructor",
33759
33761
  severity: "warn",
33760
33762
  defaultEnabled: false,
33761
- recommendation: "Pick one way to set up state in class components and stick with it.",
33763
+ recommendation: "Use one class-state setup pattern so readers know where initial state lives.",
33762
33764
  category: "Architecture",
33763
33765
  create: (context) => {
33764
33766
  const { mode } = resolveSettings$1(context.settings);
@@ -33861,7 +33863,7 @@ const stylePropObject = defineRule({
33861
33863
  id: "style-prop-object",
33862
33864
  title: "Style prop is not an object",
33863
33865
  severity: "warn",
33864
- recommendation: "Pass the `style` prop as `{{ color: 'red' }}` (object literal), not a string.",
33866
+ recommendation: "Pass `style` as an object so React can apply CSS properties instead of ignoring a string style value.",
33865
33867
  category: "Correctness",
33866
33868
  create: (context) => {
33867
33869
  const { allow } = resolveSettings(context.settings);
@@ -34207,11 +34209,11 @@ const tanstackStartMissingHeadContent = defineRule({
34207
34209
  //#region src/plugin/rules/tanstack-start/tanstack-start-no-anchor-element.ts
34208
34210
  const tanstackStartNoAnchorElement = defineRule({
34209
34211
  id: "tanstack-start-no-anchor-element",
34210
- title: "Plain anchor for internal navigation",
34212
+ title: "Plain anchor reloads TanStack Router navigation",
34211
34213
  tags: ["test-noise"],
34212
34214
  requires: ["tanstack-start"],
34213
34215
  severity: "warn",
34214
- recommendation: "`import { Link } from '@tanstack/react-router'` for type-safe routes, preloading via `preload=\"intent\"`, and client-side navigation",
34216
+ recommendation: "Use `Link` from `@tanstack/react-router` so internal navigation keeps client state, preloading, and typed routes.",
34215
34217
  create: (context) => ({ JSXOpeningElement(node) {
34216
34218
  const filename = normalizeFilename$1(context.filename ?? "");
34217
34219
  if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
@@ -34224,7 +34226,7 @@ const tanstackStartNoAnchorElement = defineRule({
34224
34226
  else if (isNodeOfType(hrefAttribute.value, "JSXExpressionContainer") && isNodeOfType(hrefAttribute.value.expression, "Literal")) hrefValue = hrefAttribute.value.expression.value;
34225
34227
  if (typeof hrefValue === "string" && hrefValue.startsWith("/")) context.report({
34226
34228
  node,
34227
- message: "Plain <a> reloads the whole page on internal navigation."
34229
+ message: "Plain <a> reloads the whole page for internal navigation, so TanStack Router loses client state and preloading."
34228
34230
  });
34229
34231
  } })
34230
34232
  });
@@ -34296,7 +34298,7 @@ const tanstackStartNoNavigateInRender = defineRule({
34296
34298
  if (deferredCallbackDepth > 0 || eventHandlerDepth > 0) return;
34297
34299
  if (isNodeOfType(node.callee, "Identifier") && node.callee.name === "navigate" && (node.arguments?.length ?? 0) > 0) context.report({
34298
34300
  node,
34299
- message: "navigate() during render causes hydration errors."
34301
+ message: "navigate() runs during render here, so server and browser output can diverge during hydration."
34300
34302
  });
34301
34303
  },
34302
34304
  "CallExpression:exit"(node) {
@@ -34380,7 +34382,7 @@ const tanstackStartNoUseServerInHandler = defineRule({
34380
34382
  if (!isNodeOfType(body, "BlockStatement")) return;
34381
34383
  if (body.body?.some((statement) => isNodeOfType(statement, "ExpressionStatement") && (statement.directive === "use server" || isNodeOfType(statement.expression, "Literal") && statement.expression.value === "use server"))) context.report({
34382
34384
  node: handlerFunction,
34383
- message: "\"use server\" inside a createServerFn handler causes compile errors."
34385
+ message: "\"use server\" inside a createServerFn handler duplicates TanStack Start's server boundary, so the route can fail to compile."
34384
34386
  });
34385
34387
  } })
34386
34388
  });
@@ -34454,11 +34456,11 @@ const tanstackStartRedirectInTryCatch = defineRule({
34454
34456
  //#region src/plugin/rules/tanstack-start/tanstack-start-route-property-order.ts
34455
34457
  const tanstackStartRoutePropertyOrder = defineRule({
34456
34458
  id: "tanstack-start-route-property-order",
34457
- title: "Wrong route property order",
34459
+ title: "Route property order breaks type inference",
34458
34460
  tags: ["test-noise"],
34459
34461
  requires: ["tanstack-start"],
34460
34462
  severity: "error",
34461
- recommendation: "Follow the order: params/validateSearch loaderDeps context beforeLoad loader head. See https://tanstack.com/router/latest/docs/eslint/create-route-property-order",
34463
+ recommendation: "Follow the route property order because TanStack Router's type inference depends on earlier properties feeding later ones.",
34462
34464
  create: (context) => ({ CallExpression(node) {
34463
34465
  const optionsObject = getRouteOptionsObject(node);
34464
34466
  if (!optionsObject) return;
@@ -34488,7 +34490,7 @@ const tanstackStartRoutePropertyOrder = defineRule({
34488
34490
  //#region src/plugin/rules/tanstack-start/tanstack-start-server-fn-method-order.ts
34489
34491
  const tanstackStartServerFnMethodOrder = defineRule({
34490
34492
  id: "tanstack-start-server-fn-method-order",
34491
- title: "Wrong server function method order",
34493
+ title: "Server function method order breaks type inference",
34492
34494
  tags: ["test-noise"],
34493
34495
  requires: ["tanstack-start"],
34494
34496
  severity: "error",
@@ -34569,7 +34571,7 @@ const useLazyMotion = defineRule({
34569
34571
  return getImportedName$1(specifier) === "motion";
34570
34572
  })) context.report({
34571
34573
  node,
34572
- message: "Importing \"motion\" ships about 30 kb of extra code to your users & slows page load. Use \"m\" with LazyMotion instead."
34574
+ message: "Importing \"motion\" ships about 30 kb of extra code and slows page load. Use \"m\" with LazyMotion instead."
34573
34575
  });
34574
34576
  } })
34575
34577
  });
@@ -34606,7 +34608,7 @@ const voidDomElementsNoChildren = defineRule({
34606
34608
  id: "void-dom-elements-no-children",
34607
34609
  title: "Children on a void element",
34608
34610
  severity: "warn",
34609
- recommendation: "Remove the children, or use a tag that can hold children.",
34611
+ recommendation: "Remove the children or use a non-void tag so React does not drop content the element cannot render.",
34610
34612
  create: (context) => ({
34611
34613
  JSXElement(node) {
34612
34614
  const openingElement = node.openingElement;
@@ -34754,7 +34756,7 @@ const DEPRECATED_ZOD_ERROR_MEMBERS = new Set([
34754
34756
  "formErrors",
34755
34757
  "format"
34756
34758
  ]);
34757
- const ZOD_ERROR_API_MESSAGE = "Zod 4 dropped this ZodError method, so it breaks when you upgrade.";
34759
+ const ZOD_ERROR_API_MESSAGE = "This ZodError API was removed in Zod 4, so error handling can break during the upgrade.";
34758
34760
  const isZodErrorReference = (node) => {
34759
34761
  const inner = stripParenExpression(node);
34760
34762
  if (isNodeOfType(inner, "Identifier")) return getZodNamedImport(inner) === "ZodError";
@@ -34786,7 +34788,7 @@ const isReceiverOfDeprecatedZodErrorMember = (callExpression) => {
34786
34788
  };
34787
34789
  const zodV4NoDeprecatedErrorApis = defineRule({
34788
34790
  id: "zod-v4-no-deprecated-error-apis",
34789
- title: "Deprecated Zod error API",
34791
+ title: "Zod 3 error API breaks in Zod 4",
34790
34792
  requires: ["zod:4"],
34791
34793
  tags: ["migration-hint"],
34792
34794
  severity: "warn",
@@ -34875,7 +34877,7 @@ const parseCallUsesErrorMap = (callExpression) => {
34875
34877
  };
34876
34878
  const zodV4NoDeprecatedErrorCustomization = defineRule({
34877
34879
  id: "zod-v4-no-deprecated-error-customization",
34878
- title: "Deprecated Zod error customization",
34880
+ title: "Zod 3 error customization breaks in Zod 4",
34879
34881
  requires: ["zod:4"],
34880
34882
  tags: ["migration-hint"],
34881
34883
  severity: "warn",
@@ -34884,7 +34886,7 @@ const zodV4NoDeprecatedErrorCustomization = defineRule({
34884
34886
  if (!factoryUsesDeprecatedErrorParameter(node) && !parseCallUsesErrorMap(node)) return;
34885
34887
  context.report({
34886
34888
  node,
34887
- message: "Zod 4 changed how you customize error messages, so this breaks when you upgrade."
34889
+ message: "This Zod 3 error-customization form is not compatible with Zod 4, so custom messages can stop applying during the upgrade."
34888
34890
  });
34889
34891
  } })
34890
34892
  });
@@ -34944,7 +34946,7 @@ const LITERAL_FACTORY = new Set(["literal"]);
34944
34946
  const reportSchemaMigration = (context, node) => {
34945
34947
  context.report({
34946
34948
  node,
34947
- message: "Zod 4 deprecated or changed this API, so it breaks when you upgrade."
34949
+ message: "This Zod 3 schema API changed in Zod 4, so this schema can fail after the upgrade."
34948
34950
  });
34949
34951
  };
34950
34952
  const isCallToDeprecatedTopLevelFactory = (callExpression) => isZodFactoryCall(callExpression, DEPRECATED_TOP_LEVEL_FACTORIES);
@@ -34996,7 +34998,7 @@ const isZodNamespaceImportMemberCreate = (memberExpression) => {
34996
34998
  };
34997
34999
  const zodV4NoDeprecatedSchemaApis = defineRule({
34998
35000
  id: "zod-v4-no-deprecated-schema-apis",
34999
- title: "Deprecated Zod schema API",
35001
+ title: "Zod 3 schema API breaks in Zod 4",
35000
35002
  requires: ["zod:4"],
35001
35003
  tags: ["migration-hint"],
35002
35004
  severity: "warn",
@@ -35049,7 +35051,7 @@ const zodV4PreferTopLevelStringFormats = defineRule({
35049
35051
  if (!isDirectMethodCallOnZodFactory(node, ZOD_STRING_FACTORY, STRING_FORMAT_METHODS)) return;
35050
35052
  context.report({
35051
35053
  node,
35052
- message: "Zod 4 deprecated format methods on `z.string()`, so they break when you upgrade."
35054
+ message: "This `z.string().<format>()` check is deprecated in Zod 4, so it can break during the upgrade."
35053
35055
  });
35054
35056
  } })
35055
35057
  });
@@ -35064,7 +35066,8 @@ const reactDoctorRules = [
35064
35066
  rule: {
35065
35067
  ...activityWrapsEffectHeavySubtree,
35066
35068
  framework: "global",
35067
- category: "Bugs"
35069
+ category: "Bugs",
35070
+ requires: [...new Set(["react", ...activityWrapsEffectHeavySubtree.requires ?? []])]
35068
35071
  }
35069
35072
  },
35070
35073
  {
@@ -35075,7 +35078,8 @@ const reactDoctorRules = [
35075
35078
  rule: {
35076
35079
  ...advancedEventHandlerRefs,
35077
35080
  framework: "global",
35078
- category: "Performance"
35081
+ category: "Performance",
35082
+ requires: [...new Set(["react", ...advancedEventHandlerRefs.requires ?? []])]
35079
35083
  }
35080
35084
  },
35081
35085
  {
@@ -35086,7 +35090,8 @@ const reactDoctorRules = [
35086
35090
  rule: {
35087
35091
  ...altText,
35088
35092
  framework: "global",
35089
- category: "Accessibility"
35093
+ category: "Accessibility",
35094
+ requires: [...new Set(["react", ...altText.requires ?? []])]
35090
35095
  }
35091
35096
  },
35092
35097
  {
@@ -35097,7 +35102,8 @@ const reactDoctorRules = [
35097
35102
  rule: {
35098
35103
  ...anchorAmbiguousText,
35099
35104
  framework: "global",
35100
- category: "Accessibility"
35105
+ category: "Accessibility",
35106
+ requires: [...new Set(["react", ...anchorAmbiguousText.requires ?? []])]
35101
35107
  }
35102
35108
  },
35103
35109
  {
@@ -35108,7 +35114,8 @@ const reactDoctorRules = [
35108
35114
  rule: {
35109
35115
  ...anchorHasContent,
35110
35116
  framework: "global",
35111
- category: "Accessibility"
35117
+ category: "Accessibility",
35118
+ requires: [...new Set(["react", ...anchorHasContent.requires ?? []])]
35112
35119
  }
35113
35120
  },
35114
35121
  {
@@ -35119,7 +35126,8 @@ const reactDoctorRules = [
35119
35126
  rule: {
35120
35127
  ...anchorIsValid,
35121
35128
  framework: "global",
35122
- category: "Accessibility"
35129
+ category: "Accessibility",
35130
+ requires: [...new Set(["react", ...anchorIsValid.requires ?? []])]
35123
35131
  }
35124
35132
  },
35125
35133
  {
@@ -35130,7 +35138,8 @@ const reactDoctorRules = [
35130
35138
  rule: {
35131
35139
  ...ariaActivedescendantHasTabindex,
35132
35140
  framework: "global",
35133
- category: "Accessibility"
35141
+ category: "Accessibility",
35142
+ requires: [...new Set(["react", ...ariaActivedescendantHasTabindex.requires ?? []])]
35134
35143
  }
35135
35144
  },
35136
35145
  {
@@ -35141,7 +35150,8 @@ const reactDoctorRules = [
35141
35150
  rule: {
35142
35151
  ...ariaProps,
35143
35152
  framework: "global",
35144
- category: "Accessibility"
35153
+ category: "Accessibility",
35154
+ requires: [...new Set(["react", ...ariaProps.requires ?? []])]
35145
35155
  }
35146
35156
  },
35147
35157
  {
@@ -35152,7 +35162,8 @@ const reactDoctorRules = [
35152
35162
  rule: {
35153
35163
  ...ariaProptypes,
35154
35164
  framework: "global",
35155
- category: "Accessibility"
35165
+ category: "Accessibility",
35166
+ requires: [...new Set(["react", ...ariaProptypes.requires ?? []])]
35156
35167
  }
35157
35168
  },
35158
35169
  {
@@ -35163,7 +35174,8 @@ const reactDoctorRules = [
35163
35174
  rule: {
35164
35175
  ...ariaRole,
35165
35176
  framework: "global",
35166
- category: "Accessibility"
35177
+ category: "Accessibility",
35178
+ requires: [...new Set(["react", ...ariaRole.requires ?? []])]
35167
35179
  }
35168
35180
  },
35169
35181
  {
@@ -35174,7 +35186,8 @@ const reactDoctorRules = [
35174
35186
  rule: {
35175
35187
  ...ariaUnsupportedElements,
35176
35188
  framework: "global",
35177
- category: "Accessibility"
35189
+ category: "Accessibility",
35190
+ requires: [...new Set(["react", ...ariaUnsupportedElements.requires ?? []])]
35178
35191
  }
35179
35192
  },
35180
35193
  {
@@ -35196,7 +35209,8 @@ const reactDoctorRules = [
35196
35209
  rule: {
35197
35210
  ...asyncDeferAwait,
35198
35211
  framework: "global",
35199
- category: "Performance"
35212
+ category: "Performance",
35213
+ requires: [...new Set(["react", ...asyncDeferAwait.requires ?? []])]
35200
35214
  }
35201
35215
  },
35202
35216
  {
@@ -35218,7 +35232,8 @@ const reactDoctorRules = [
35218
35232
  rule: {
35219
35233
  ...autocompleteValid,
35220
35234
  framework: "global",
35221
- category: "Accessibility"
35235
+ category: "Accessibility",
35236
+ requires: [...new Set(["react", ...autocompleteValid.requires ?? []])]
35222
35237
  }
35223
35238
  },
35224
35239
  {
@@ -35229,7 +35244,8 @@ const reactDoctorRules = [
35229
35244
  rule: {
35230
35245
  ...buttonHasType,
35231
35246
  framework: "global",
35232
- category: "Bugs"
35247
+ category: "Bugs",
35248
+ requires: [...new Set(["react", ...buttonHasType.requires ?? []])]
35233
35249
  }
35234
35250
  },
35235
35251
  {
@@ -35240,7 +35256,8 @@ const reactDoctorRules = [
35240
35256
  rule: {
35241
35257
  ...checkedRequiresOnchangeOrReadonly,
35242
35258
  framework: "global",
35243
- category: "Bugs"
35259
+ category: "Bugs",
35260
+ requires: [...new Set(["react", ...checkedRequiresOnchangeOrReadonly.requires ?? []])]
35244
35261
  }
35245
35262
  },
35246
35263
  {
@@ -35251,7 +35268,8 @@ const reactDoctorRules = [
35251
35268
  rule: {
35252
35269
  ...clickEventsHaveKeyEvents,
35253
35270
  framework: "global",
35254
- category: "Accessibility"
35271
+ category: "Accessibility",
35272
+ requires: [...new Set(["react", ...clickEventsHaveKeyEvents.requires ?? []])]
35255
35273
  }
35256
35274
  },
35257
35275
  {
@@ -35262,7 +35280,8 @@ const reactDoctorRules = [
35262
35280
  rule: {
35263
35281
  ...clientLocalstorageNoVersion,
35264
35282
  framework: "global",
35265
- category: "Bugs"
35283
+ category: "Bugs",
35284
+ requires: [...new Set(["react", ...clientLocalstorageNoVersion.requires ?? []])]
35266
35285
  }
35267
35286
  },
35268
35287
  {
@@ -35273,7 +35292,8 @@ const reactDoctorRules = [
35273
35292
  rule: {
35274
35293
  ...clientPassiveEventListeners,
35275
35294
  framework: "global",
35276
- category: "Performance"
35295
+ category: "Performance",
35296
+ requires: [...new Set(["react", ...clientPassiveEventListeners.requires ?? []])]
35277
35297
  }
35278
35298
  },
35279
35299
  {
@@ -35284,7 +35304,8 @@ const reactDoctorRules = [
35284
35304
  rule: {
35285
35305
  ...controlHasAssociatedLabel,
35286
35306
  framework: "global",
35287
- category: "Accessibility"
35307
+ category: "Accessibility",
35308
+ requires: [...new Set(["react", ...controlHasAssociatedLabel.requires ?? []])]
35288
35309
  }
35289
35310
  },
35290
35311
  {
@@ -35295,7 +35316,8 @@ const reactDoctorRules = [
35295
35316
  rule: {
35296
35317
  ...noEmDashInJsxText,
35297
35318
  framework: "global",
35298
- category: "Maintainability"
35319
+ category: "Maintainability",
35320
+ requires: [...new Set(["react", ...noEmDashInJsxText.requires ?? []])]
35299
35321
  }
35300
35322
  },
35301
35323
  {
@@ -35306,7 +35328,8 @@ const reactDoctorRules = [
35306
35328
  rule: {
35307
35329
  ...noRedundantPaddingAxes,
35308
35330
  framework: "global",
35309
- category: "Maintainability"
35331
+ category: "Maintainability",
35332
+ requires: [...new Set(["react", ...noRedundantPaddingAxes.requires ?? []])]
35310
35333
  }
35311
35334
  },
35312
35335
  {
@@ -35317,7 +35340,8 @@ const reactDoctorRules = [
35317
35340
  rule: {
35318
35341
  ...noRedundantSizeAxes,
35319
35342
  framework: "global",
35320
- category: "Maintainability"
35343
+ category: "Maintainability",
35344
+ requires: [...new Set(["react", ...noRedundantSizeAxes.requires ?? []])]
35321
35345
  }
35322
35346
  },
35323
35347
  {
@@ -35328,7 +35352,8 @@ const reactDoctorRules = [
35328
35352
  rule: {
35329
35353
  ...noSpaceOnFlexChildren,
35330
35354
  framework: "global",
35331
- category: "Maintainability"
35355
+ category: "Maintainability",
35356
+ requires: [...new Set(["react", ...noSpaceOnFlexChildren.requires ?? []])]
35332
35357
  }
35333
35358
  },
35334
35359
  {
@@ -35339,7 +35364,8 @@ const reactDoctorRules = [
35339
35364
  rule: {
35340
35365
  ...noThreePeriodEllipsis,
35341
35366
  framework: "global",
35342
- category: "Maintainability"
35367
+ category: "Maintainability",
35368
+ requires: [...new Set(["react", ...noThreePeriodEllipsis.requires ?? []])]
35343
35369
  }
35344
35370
  },
35345
35371
  {
@@ -35350,7 +35376,8 @@ const reactDoctorRules = [
35350
35376
  rule: {
35351
35377
  ...noVagueButtonLabel,
35352
35378
  framework: "global",
35353
- category: "Accessibility"
35379
+ category: "Accessibility",
35380
+ requires: [...new Set(["react", ...noVagueButtonLabel.requires ?? []])]
35354
35381
  }
35355
35382
  },
35356
35383
  {
@@ -35361,7 +35388,8 @@ const reactDoctorRules = [
35361
35388
  rule: {
35362
35389
  ...displayName,
35363
35390
  framework: "global",
35364
- category: "Maintainability"
35391
+ category: "Maintainability",
35392
+ requires: [...new Set(["react", ...displayName.requires ?? []])]
35365
35393
  }
35366
35394
  },
35367
35395
  {
@@ -35372,7 +35400,8 @@ const reactDoctorRules = [
35372
35400
  rule: {
35373
35401
  ...effectNeedsCleanup,
35374
35402
  framework: "global",
35375
- category: "Bugs"
35403
+ category: "Bugs",
35404
+ requires: [...new Set(["react", ...effectNeedsCleanup.requires ?? []])]
35376
35405
  }
35377
35406
  },
35378
35407
  {
@@ -35383,7 +35412,8 @@ const reactDoctorRules = [
35383
35412
  rule: {
35384
35413
  ...exhaustiveDeps,
35385
35414
  framework: "global",
35386
- category: "Bugs"
35415
+ category: "Bugs",
35416
+ requires: [...new Set(["react", ...exhaustiveDeps.requires ?? []])]
35387
35417
  }
35388
35418
  },
35389
35419
  {
@@ -35406,7 +35436,8 @@ const reactDoctorRules = [
35406
35436
  rule: {
35407
35437
  ...forbidComponentProps,
35408
35438
  framework: "global",
35409
- category: "Maintainability"
35439
+ category: "Maintainability",
35440
+ requires: [...new Set(["react", ...forbidComponentProps.requires ?? []])]
35410
35441
  }
35411
35442
  },
35412
35443
  {
@@ -35417,7 +35448,8 @@ const reactDoctorRules = [
35417
35448
  rule: {
35418
35449
  ...forbidDomProps,
35419
35450
  framework: "global",
35420
- category: "Maintainability"
35451
+ category: "Maintainability",
35452
+ requires: [...new Set(["react", ...forbidDomProps.requires ?? []])]
35421
35453
  }
35422
35454
  },
35423
35455
  {
@@ -35428,7 +35460,8 @@ const reactDoctorRules = [
35428
35460
  rule: {
35429
35461
  ...forbidElements,
35430
35462
  framework: "global",
35431
- category: "Maintainability"
35463
+ category: "Maintainability",
35464
+ requires: [...new Set(["react", ...forbidElements.requires ?? []])]
35432
35465
  }
35433
35466
  },
35434
35467
  {
@@ -35439,7 +35472,8 @@ const reactDoctorRules = [
35439
35472
  rule: {
35440
35473
  ...forwardRefUsesRef,
35441
35474
  framework: "global",
35442
- category: "Maintainability"
35475
+ category: "Maintainability",
35476
+ requires: [...new Set(["react", ...forwardRefUsesRef.requires ?? []])]
35443
35477
  }
35444
35478
  },
35445
35479
  {
@@ -35450,7 +35484,8 @@ const reactDoctorRules = [
35450
35484
  rule: {
35451
35485
  ...headingHasContent,
35452
35486
  framework: "global",
35453
- category: "Accessibility"
35487
+ category: "Accessibility",
35488
+ requires: [...new Set(["react", ...headingHasContent.requires ?? []])]
35454
35489
  }
35455
35490
  },
35456
35491
  {
@@ -35461,7 +35496,8 @@ const reactDoctorRules = [
35461
35496
  rule: {
35462
35497
  ...hookUseState,
35463
35498
  framework: "global",
35464
- category: "Maintainability"
35499
+ category: "Maintainability",
35500
+ requires: [...new Set(["react", ...hookUseState.requires ?? []])]
35465
35501
  }
35466
35502
  },
35467
35503
  {
@@ -35472,7 +35508,8 @@ const reactDoctorRules = [
35472
35508
  rule: {
35473
35509
  ...hooksNoNanInDeps,
35474
35510
  framework: "global",
35475
- category: "Bugs"
35511
+ category: "Bugs",
35512
+ requires: [...new Set(["react", ...hooksNoNanInDeps.requires ?? []])]
35476
35513
  }
35477
35514
  },
35478
35515
  {
@@ -35483,7 +35520,8 @@ const reactDoctorRules = [
35483
35520
  rule: {
35484
35521
  ...htmlHasLang,
35485
35522
  framework: "global",
35486
- category: "Accessibility"
35523
+ category: "Accessibility",
35524
+ requires: [...new Set(["react", ...htmlHasLang.requires ?? []])]
35487
35525
  }
35488
35526
  },
35489
35527
  {
@@ -35527,7 +35565,8 @@ const reactDoctorRules = [
35527
35565
  rule: {
35528
35566
  ...iframeHasTitle,
35529
35567
  framework: "global",
35530
- category: "Accessibility"
35568
+ category: "Accessibility",
35569
+ requires: [...new Set(["react", ...iframeHasTitle.requires ?? []])]
35531
35570
  }
35532
35571
  },
35533
35572
  {
@@ -35538,7 +35577,8 @@ const reactDoctorRules = [
35538
35577
  rule: {
35539
35578
  ...iframeMissingSandbox,
35540
35579
  framework: "global",
35541
- category: "Security"
35580
+ category: "Security",
35581
+ requires: [...new Set(["react", ...iframeMissingSandbox.requires ?? []])]
35542
35582
  }
35543
35583
  },
35544
35584
  {
@@ -35549,7 +35589,8 @@ const reactDoctorRules = [
35549
35589
  rule: {
35550
35590
  ...imgRedundantAlt,
35551
35591
  framework: "global",
35552
- category: "Accessibility"
35592
+ category: "Accessibility",
35593
+ requires: [...new Set(["react", ...imgRedundantAlt.requires ?? []])]
35553
35594
  }
35554
35595
  },
35555
35596
  {
@@ -35560,7 +35601,8 @@ const reactDoctorRules = [
35560
35601
  rule: {
35561
35602
  ...interactiveSupportsFocus,
35562
35603
  framework: "global",
35563
- category: "Accessibility"
35604
+ category: "Accessibility",
35605
+ requires: [...new Set(["react", ...interactiveSupportsFocus.requires ?? []])]
35564
35606
  }
35565
35607
  },
35566
35608
  {
@@ -35571,7 +35613,8 @@ const reactDoctorRules = [
35571
35613
  rule: {
35572
35614
  ...jotaiDerivedAtomReturnsFreshObject,
35573
35615
  framework: "global",
35574
- category: "Bugs"
35616
+ category: "Bugs",
35617
+ requires: [...new Set(["react", ...jotaiDerivedAtomReturnsFreshObject.requires ?? []])]
35575
35618
  }
35576
35619
  },
35577
35620
  {
@@ -35582,7 +35625,8 @@ const reactDoctorRules = [
35582
35625
  rule: {
35583
35626
  ...jotaiSelectAtomInRenderBody,
35584
35627
  framework: "global",
35585
- category: "Bugs"
35628
+ category: "Bugs",
35629
+ requires: [...new Set(["react", ...jotaiSelectAtomInRenderBody.requires ?? []])]
35586
35630
  }
35587
35631
  },
35588
35632
  {
@@ -35593,7 +35637,8 @@ const reactDoctorRules = [
35593
35637
  rule: {
35594
35638
  ...jotaiTqUseRawQueryAtom,
35595
35639
  framework: "global",
35596
- category: "Bugs"
35640
+ category: "Bugs",
35641
+ requires: [...new Set(["react", ...jotaiTqUseRawQueryAtom.requires ?? []])]
35597
35642
  }
35598
35643
  },
35599
35644
  {
@@ -35758,7 +35803,8 @@ const reactDoctorRules = [
35758
35803
  rule: {
35759
35804
  ...jsxBooleanValue,
35760
35805
  framework: "global",
35761
- category: "Maintainability"
35806
+ category: "Maintainability",
35807
+ requires: [...new Set(["react", ...jsxBooleanValue.requires ?? []])]
35762
35808
  }
35763
35809
  },
35764
35810
  {
@@ -35769,7 +35815,8 @@ const reactDoctorRules = [
35769
35815
  rule: {
35770
35816
  ...jsxCurlyBracePresence,
35771
35817
  framework: "global",
35772
- category: "Maintainability"
35818
+ category: "Maintainability",
35819
+ requires: [...new Set(["react", ...jsxCurlyBracePresence.requires ?? []])]
35773
35820
  }
35774
35821
  },
35775
35822
  {
@@ -35780,7 +35827,8 @@ const reactDoctorRules = [
35780
35827
  rule: {
35781
35828
  ...jsxFilenameExtension,
35782
35829
  framework: "global",
35783
- category: "Maintainability"
35830
+ category: "Maintainability",
35831
+ requires: [...new Set(["react", ...jsxFilenameExtension.requires ?? []])]
35784
35832
  }
35785
35833
  },
35786
35834
  {
@@ -35791,7 +35839,8 @@ const reactDoctorRules = [
35791
35839
  rule: {
35792
35840
  ...jsxFragments,
35793
35841
  framework: "global",
35794
- category: "Maintainability"
35842
+ category: "Maintainability",
35843
+ requires: [...new Set(["react", ...jsxFragments.requires ?? []])]
35795
35844
  }
35796
35845
  },
35797
35846
  {
@@ -35802,7 +35851,8 @@ const reactDoctorRules = [
35802
35851
  rule: {
35803
35852
  ...jsxHandlerNames,
35804
35853
  framework: "global",
35805
- category: "Maintainability"
35854
+ category: "Maintainability",
35855
+ requires: [...new Set(["react", ...jsxHandlerNames.requires ?? []])]
35806
35856
  }
35807
35857
  },
35808
35858
  {
@@ -35813,7 +35863,8 @@ const reactDoctorRules = [
35813
35863
  rule: {
35814
35864
  ...jsxKey,
35815
35865
  framework: "global",
35816
- category: "Bugs"
35866
+ category: "Bugs",
35867
+ requires: [...new Set(["react", ...jsxKey.requires ?? []])]
35817
35868
  }
35818
35869
  },
35819
35870
  {
@@ -35824,7 +35875,8 @@ const reactDoctorRules = [
35824
35875
  rule: {
35825
35876
  ...jsxMaxDepth,
35826
35877
  framework: "global",
35827
- category: "Maintainability"
35878
+ category: "Maintainability",
35879
+ requires: [...new Set(["react", ...jsxMaxDepth.requires ?? []])]
35828
35880
  }
35829
35881
  },
35830
35882
  {
@@ -35835,7 +35887,8 @@ const reactDoctorRules = [
35835
35887
  rule: {
35836
35888
  ...jsxNoCommentTextnodes,
35837
35889
  framework: "global",
35838
- category: "Bugs"
35890
+ category: "Bugs",
35891
+ requires: [...new Set(["react", ...jsxNoCommentTextnodes.requires ?? []])]
35839
35892
  }
35840
35893
  },
35841
35894
  {
@@ -35846,7 +35899,8 @@ const reactDoctorRules = [
35846
35899
  rule: {
35847
35900
  ...jsxNoConstructedContextValues,
35848
35901
  framework: "global",
35849
- category: "Performance"
35902
+ category: "Performance",
35903
+ requires: [...new Set(["react", ...jsxNoConstructedContextValues.requires ?? []])]
35850
35904
  }
35851
35905
  },
35852
35906
  {
@@ -35857,7 +35911,8 @@ const reactDoctorRules = [
35857
35911
  rule: {
35858
35912
  ...jsxNoDuplicateProps,
35859
35913
  framework: "global",
35860
- category: "Bugs"
35914
+ category: "Bugs",
35915
+ requires: [...new Set(["react", ...jsxNoDuplicateProps.requires ?? []])]
35861
35916
  }
35862
35917
  },
35863
35918
  {
@@ -35868,7 +35923,8 @@ const reactDoctorRules = [
35868
35923
  rule: {
35869
35924
  ...jsxNoJsxAsProp,
35870
35925
  framework: "global",
35871
- category: "Performance"
35926
+ category: "Performance",
35927
+ requires: [...new Set(["react", ...jsxNoJsxAsProp.requires ?? []])]
35872
35928
  }
35873
35929
  },
35874
35930
  {
@@ -35879,7 +35935,8 @@ const reactDoctorRules = [
35879
35935
  rule: {
35880
35936
  ...jsxNoNewArrayAsProp,
35881
35937
  framework: "global",
35882
- category: "Performance"
35938
+ category: "Performance",
35939
+ requires: [...new Set(["react", ...jsxNoNewArrayAsProp.requires ?? []])]
35883
35940
  }
35884
35941
  },
35885
35942
  {
@@ -35890,7 +35947,8 @@ const reactDoctorRules = [
35890
35947
  rule: {
35891
35948
  ...jsxNoNewFunctionAsProp,
35892
35949
  framework: "global",
35893
- category: "Performance"
35950
+ category: "Performance",
35951
+ requires: [...new Set(["react", ...jsxNoNewFunctionAsProp.requires ?? []])]
35894
35952
  }
35895
35953
  },
35896
35954
  {
@@ -35901,7 +35959,8 @@ const reactDoctorRules = [
35901
35959
  rule: {
35902
35960
  ...jsxNoNewObjectAsProp,
35903
35961
  framework: "global",
35904
- category: "Performance"
35962
+ category: "Performance",
35963
+ requires: [...new Set(["react", ...jsxNoNewObjectAsProp.requires ?? []])]
35905
35964
  }
35906
35965
  },
35907
35966
  {
@@ -35912,7 +35971,8 @@ const reactDoctorRules = [
35912
35971
  rule: {
35913
35972
  ...jsxNoScriptUrl,
35914
35973
  framework: "global",
35915
- category: "Security"
35974
+ category: "Security",
35975
+ requires: [...new Set(["react", ...jsxNoScriptUrl.requires ?? []])]
35916
35976
  }
35917
35977
  },
35918
35978
  {
@@ -35923,7 +35983,8 @@ const reactDoctorRules = [
35923
35983
  rule: {
35924
35984
  ...jsxNoUndef,
35925
35985
  framework: "global",
35926
- category: "Bugs"
35986
+ category: "Bugs",
35987
+ requires: [...new Set(["react", ...jsxNoUndef.requires ?? []])]
35927
35988
  }
35928
35989
  },
35929
35990
  {
@@ -35934,7 +35995,8 @@ const reactDoctorRules = [
35934
35995
  rule: {
35935
35996
  ...jsxNoUselessFragment,
35936
35997
  framework: "global",
35937
- category: "Maintainability"
35998
+ category: "Maintainability",
35999
+ requires: [...new Set(["react", ...jsxNoUselessFragment.requires ?? []])]
35938
36000
  }
35939
36001
  },
35940
36002
  {
@@ -35945,7 +36007,8 @@ const reactDoctorRules = [
35945
36007
  rule: {
35946
36008
  ...jsxPascalCase,
35947
36009
  framework: "global",
35948
- category: "Maintainability"
36010
+ category: "Maintainability",
36011
+ requires: [...new Set(["react", ...jsxPascalCase.requires ?? []])]
35949
36012
  }
35950
36013
  },
35951
36014
  {
@@ -35956,7 +36019,8 @@ const reactDoctorRules = [
35956
36019
  rule: {
35957
36020
  ...jsxPropsNoSpreadMulti,
35958
36021
  framework: "global",
35959
- category: "Bugs"
36022
+ category: "Bugs",
36023
+ requires: [...new Set(["react", ...jsxPropsNoSpreadMulti.requires ?? []])]
35960
36024
  }
35961
36025
  },
35962
36026
  {
@@ -35967,7 +36031,8 @@ const reactDoctorRules = [
35967
36031
  rule: {
35968
36032
  ...jsxPropsNoSpreading,
35969
36033
  framework: "global",
35970
- category: "Maintainability"
36034
+ category: "Maintainability",
36035
+ requires: [...new Set(["react", ...jsxPropsNoSpreading.requires ?? []])]
35971
36036
  }
35972
36037
  },
35973
36038
  {
@@ -35978,7 +36043,8 @@ const reactDoctorRules = [
35978
36043
  rule: {
35979
36044
  ...labelHasAssociatedControl,
35980
36045
  framework: "global",
35981
- category: "Accessibility"
36046
+ category: "Accessibility",
36047
+ requires: [...new Set(["react", ...labelHasAssociatedControl.requires ?? []])]
35982
36048
  }
35983
36049
  },
35984
36050
  {
@@ -35989,7 +36055,8 @@ const reactDoctorRules = [
35989
36055
  rule: {
35990
36056
  ...lang,
35991
36057
  framework: "global",
35992
- category: "Accessibility"
36058
+ category: "Accessibility",
36059
+ requires: [...new Set(["react", ...lang.requires ?? []])]
35993
36060
  }
35994
36061
  },
35995
36062
  {
@@ -36000,7 +36067,8 @@ const reactDoctorRules = [
36000
36067
  rule: {
36001
36068
  ...mediaHasCaption,
36002
36069
  framework: "global",
36003
- category: "Accessibility"
36070
+ category: "Accessibility",
36071
+ requires: [...new Set(["react", ...mediaHasCaption.requires ?? []])]
36004
36072
  }
36005
36073
  },
36006
36074
  {
@@ -36011,7 +36079,8 @@ const reactDoctorRules = [
36011
36079
  rule: {
36012
36080
  ...mouseEventsHaveKeyEvents,
36013
36081
  framework: "global",
36014
- category: "Accessibility"
36082
+ category: "Accessibility",
36083
+ requires: [...new Set(["react", ...mouseEventsHaveKeyEvents.requires ?? []])]
36015
36084
  }
36016
36085
  },
36017
36086
  {
@@ -36275,7 +36344,8 @@ const reactDoctorRules = [
36275
36344
  rule: {
36276
36345
  ...noAccessKey,
36277
36346
  framework: "global",
36278
- category: "Accessibility"
36347
+ category: "Accessibility",
36348
+ requires: [...new Set(["react", ...noAccessKey.requires ?? []])]
36279
36349
  }
36280
36350
  },
36281
36351
  {
@@ -36286,7 +36356,8 @@ const reactDoctorRules = [
36286
36356
  rule: {
36287
36357
  ...noAdjustStateOnPropChange,
36288
36358
  framework: "global",
36289
- category: "Bugs"
36359
+ category: "Bugs",
36360
+ requires: [...new Set(["react", ...noAdjustStateOnPropChange.requires ?? []])]
36290
36361
  }
36291
36362
  },
36292
36363
  {
@@ -36297,7 +36368,8 @@ const reactDoctorRules = [
36297
36368
  rule: {
36298
36369
  ...noAriaHiddenOnFocusable,
36299
36370
  framework: "global",
36300
- category: "Accessibility"
36371
+ category: "Accessibility",
36372
+ requires: [...new Set(["react", ...noAriaHiddenOnFocusable.requires ?? []])]
36301
36373
  }
36302
36374
  },
36303
36375
  {
@@ -36319,7 +36391,8 @@ const reactDoctorRules = [
36319
36391
  rule: {
36320
36392
  ...noArrayIndexKey,
36321
36393
  framework: "global",
36322
- category: "Performance"
36394
+ category: "Performance",
36395
+ requires: [...new Set(["react", ...noArrayIndexKey.requires ?? []])]
36323
36396
  }
36324
36397
  },
36325
36398
  {
@@ -36330,7 +36403,8 @@ const reactDoctorRules = [
36330
36403
  rule: {
36331
36404
  ...noAutofocus,
36332
36405
  framework: "global",
36333
- category: "Accessibility"
36406
+ category: "Accessibility",
36407
+ requires: [...new Set(["react", ...noAutofocus.requires ?? []])]
36334
36408
  }
36335
36409
  },
36336
36410
  {
@@ -36352,7 +36426,8 @@ const reactDoctorRules = [
36352
36426
  rule: {
36353
36427
  ...noCascadingSetState,
36354
36428
  framework: "global",
36355
- category: "Bugs"
36429
+ category: "Bugs",
36430
+ requires: [...new Set(["react", ...noCascadingSetState.requires ?? []])]
36356
36431
  }
36357
36432
  },
36358
36433
  {
@@ -36363,7 +36438,8 @@ const reactDoctorRules = [
36363
36438
  rule: {
36364
36439
  ...noChainStateUpdates,
36365
36440
  framework: "global",
36366
- category: "Bugs"
36441
+ category: "Bugs",
36442
+ requires: [...new Set(["react", ...noChainStateUpdates.requires ?? []])]
36367
36443
  }
36368
36444
  },
36369
36445
  {
@@ -36374,7 +36450,8 @@ const reactDoctorRules = [
36374
36450
  rule: {
36375
36451
  ...noChildrenProp,
36376
36452
  framework: "global",
36377
- category: "Bugs"
36453
+ category: "Bugs",
36454
+ requires: [...new Set(["react", ...noChildrenProp.requires ?? []])]
36378
36455
  }
36379
36456
  },
36380
36457
  {
@@ -36385,7 +36462,8 @@ const reactDoctorRules = [
36385
36462
  rule: {
36386
36463
  ...noCloneElement,
36387
36464
  framework: "global",
36388
- category: "Maintainability"
36465
+ category: "Maintainability",
36466
+ requires: [...new Set(["react", ...noCloneElement.requires ?? []])]
36389
36467
  }
36390
36468
  },
36391
36469
  {
@@ -36396,7 +36474,8 @@ const reactDoctorRules = [
36396
36474
  rule: {
36397
36475
  ...noCreateContextInRender,
36398
36476
  framework: "global",
36399
- category: "Bugs"
36477
+ category: "Bugs",
36478
+ requires: [...new Set(["react", ...noCreateContextInRender.requires ?? []])]
36400
36479
  }
36401
36480
  },
36402
36481
  {
@@ -36407,7 +36486,8 @@ const reactDoctorRules = [
36407
36486
  rule: {
36408
36487
  ...noCreateStoreInRender,
36409
36488
  framework: "global",
36410
- category: "Bugs"
36489
+ category: "Bugs",
36490
+ requires: [...new Set(["react", ...noCreateStoreInRender.requires ?? []])]
36411
36491
  }
36412
36492
  },
36413
36493
  {
@@ -36418,7 +36498,8 @@ const reactDoctorRules = [
36418
36498
  rule: {
36419
36499
  ...noDanger,
36420
36500
  framework: "global",
36421
- category: "Security"
36501
+ category: "Security",
36502
+ requires: [...new Set(["react", ...noDanger.requires ?? []])]
36422
36503
  }
36423
36504
  },
36424
36505
  {
@@ -36429,7 +36510,8 @@ const reactDoctorRules = [
36429
36510
  rule: {
36430
36511
  ...noDangerWithChildren,
36431
36512
  framework: "global",
36432
- category: "Bugs"
36513
+ category: "Bugs",
36514
+ requires: [...new Set(["react", ...noDangerWithChildren.requires ?? []])]
36433
36515
  }
36434
36516
  },
36435
36517
  {
@@ -36462,7 +36544,8 @@ const reactDoctorRules = [
36462
36544
  rule: {
36463
36545
  ...noDerivedState,
36464
36546
  framework: "global",
36465
- category: "Bugs"
36547
+ category: "Bugs",
36548
+ requires: [...new Set(["react", ...noDerivedState.requires ?? []])]
36466
36549
  }
36467
36550
  },
36468
36551
  {
@@ -36473,7 +36556,8 @@ const reactDoctorRules = [
36473
36556
  rule: {
36474
36557
  ...noDerivedStateEffect,
36475
36558
  framework: "global",
36476
- category: "Bugs"
36559
+ category: "Bugs",
36560
+ requires: [...new Set(["react", ...noDerivedStateEffect.requires ?? []])]
36477
36561
  }
36478
36562
  },
36479
36563
  {
@@ -36484,7 +36568,8 @@ const reactDoctorRules = [
36484
36568
  rule: {
36485
36569
  ...noDerivedUseState,
36486
36570
  framework: "global",
36487
- category: "Bugs"
36571
+ category: "Bugs",
36572
+ requires: [...new Set(["react", ...noDerivedUseState.requires ?? []])]
36488
36573
  }
36489
36574
  },
36490
36575
  {
@@ -36495,7 +36580,8 @@ const reactDoctorRules = [
36495
36580
  rule: {
36496
36581
  ...noDidMountSetState,
36497
36582
  framework: "global",
36498
- category: "Bugs"
36583
+ category: "Bugs",
36584
+ requires: [...new Set(["react", ...noDidMountSetState.requires ?? []])]
36499
36585
  }
36500
36586
  },
36501
36587
  {
@@ -36506,7 +36592,8 @@ const reactDoctorRules = [
36506
36592
  rule: {
36507
36593
  ...noDidUpdateSetState,
36508
36594
  framework: "global",
36509
- category: "Bugs"
36595
+ category: "Bugs",
36596
+ requires: [...new Set(["react", ...noDidUpdateSetState.requires ?? []])]
36510
36597
  }
36511
36598
  },
36512
36599
  {
@@ -36517,7 +36604,8 @@ const reactDoctorRules = [
36517
36604
  rule: {
36518
36605
  ...noDirectMutationState,
36519
36606
  framework: "global",
36520
- category: "Bugs"
36607
+ category: "Bugs",
36608
+ requires: [...new Set(["react", ...noDirectMutationState.requires ?? []])]
36521
36609
  }
36522
36610
  },
36523
36611
  {
@@ -36528,7 +36616,8 @@ const reactDoctorRules = [
36528
36616
  rule: {
36529
36617
  ...noDirectStateMutation,
36530
36618
  framework: "global",
36531
- category: "Bugs"
36619
+ category: "Bugs",
36620
+ requires: [...new Set(["react", ...noDirectStateMutation.requires ?? []])]
36532
36621
  }
36533
36622
  },
36534
36623
  {
@@ -36550,7 +36639,8 @@ const reactDoctorRules = [
36550
36639
  rule: {
36551
36640
  ...noDistractingElements,
36552
36641
  framework: "global",
36553
- category: "Accessibility"
36642
+ category: "Accessibility",
36643
+ requires: [...new Set(["react", ...noDistractingElements.requires ?? []])]
36554
36644
  }
36555
36645
  },
36556
36646
  {
@@ -36561,7 +36651,8 @@ const reactDoctorRules = [
36561
36651
  rule: {
36562
36652
  ...noDocumentStartViewTransition,
36563
36653
  framework: "global",
36564
- category: "Bugs"
36654
+ category: "Bugs",
36655
+ requires: [...new Set(["react", ...noDocumentStartViewTransition.requires ?? []])]
36565
36656
  }
36566
36657
  },
36567
36658
  {
@@ -36583,7 +36674,8 @@ const reactDoctorRules = [
36583
36674
  rule: {
36584
36675
  ...noEffectChain,
36585
36676
  framework: "global",
36586
- category: "Bugs"
36677
+ category: "Bugs",
36678
+ requires: [...new Set(["react", ...noEffectChain.requires ?? []])]
36587
36679
  }
36588
36680
  },
36589
36681
  {
@@ -36594,7 +36686,8 @@ const reactDoctorRules = [
36594
36686
  rule: {
36595
36687
  ...noEffectEventHandler,
36596
36688
  framework: "global",
36597
- category: "Bugs"
36689
+ category: "Bugs",
36690
+ requires: [...new Set(["react", ...noEffectEventHandler.requires ?? []])]
36598
36691
  }
36599
36692
  },
36600
36693
  {
@@ -36605,7 +36698,8 @@ const reactDoctorRules = [
36605
36698
  rule: {
36606
36699
  ...noEffectEventInDeps,
36607
36700
  framework: "global",
36608
- category: "Bugs"
36701
+ category: "Bugs",
36702
+ requires: [...new Set(["react", ...noEffectEventInDeps.requires ?? []])]
36609
36703
  }
36610
36704
  },
36611
36705
  {
@@ -36616,7 +36710,8 @@ const reactDoctorRules = [
36616
36710
  rule: {
36617
36711
  ...noEffectWithFreshDeps,
36618
36712
  framework: "global",
36619
- category: "Bugs"
36713
+ category: "Bugs",
36714
+ requires: [...new Set(["react", ...noEffectWithFreshDeps.requires ?? []])]
36620
36715
  }
36621
36716
  },
36622
36717
  {
@@ -36638,7 +36733,8 @@ const reactDoctorRules = [
36638
36733
  rule: {
36639
36734
  ...noEventHandler,
36640
36735
  framework: "global",
36641
- category: "Bugs"
36736
+ category: "Bugs",
36737
+ requires: [...new Set(["react", ...noEventHandler.requires ?? []])]
36642
36738
  }
36643
36739
  },
36644
36740
  {
@@ -36649,7 +36745,8 @@ const reactDoctorRules = [
36649
36745
  rule: {
36650
36746
  ...noEventTriggerState,
36651
36747
  framework: "global",
36652
- category: "Bugs"
36748
+ category: "Bugs",
36749
+ requires: [...new Set(["react", ...noEventTriggerState.requires ?? []])]
36653
36750
  }
36654
36751
  },
36655
36752
  {
@@ -36660,7 +36757,8 @@ const reactDoctorRules = [
36660
36757
  rule: {
36661
36758
  ...noFetchInEffect,
36662
36759
  framework: "global",
36663
- category: "Bugs"
36760
+ category: "Bugs",
36761
+ requires: [...new Set(["react", ...noFetchInEffect.requires ?? []])]
36664
36762
  }
36665
36763
  },
36666
36764
  {
@@ -36671,7 +36769,8 @@ const reactDoctorRules = [
36671
36769
  rule: {
36672
36770
  ...noFindDomNode,
36673
36771
  framework: "global",
36674
- category: "Bugs"
36772
+ category: "Bugs",
36773
+ requires: [...new Set(["react", ...noFindDomNode.requires ?? []])]
36675
36774
  }
36676
36775
  },
36677
36776
  {
@@ -36682,7 +36781,8 @@ const reactDoctorRules = [
36682
36781
  rule: {
36683
36782
  ...noFlushSync,
36684
36783
  framework: "global",
36685
- category: "Performance"
36784
+ category: "Performance",
36785
+ requires: [...new Set(["react", ...noFlushSync.requires ?? []])]
36686
36786
  }
36687
36787
  },
36688
36788
  {
@@ -36726,7 +36826,8 @@ const reactDoctorRules = [
36726
36826
  rule: {
36727
36827
  ...noGlobalCssVariableAnimation,
36728
36828
  framework: "global",
36729
- category: "Performance"
36829
+ category: "Performance",
36830
+ requires: [...new Set(["react", ...noGlobalCssVariableAnimation.requires ?? []])]
36730
36831
  }
36731
36832
  },
36732
36833
  {
@@ -36759,7 +36860,8 @@ const reactDoctorRules = [
36759
36860
  rule: {
36760
36861
  ...noInitializeState,
36761
36862
  framework: "global",
36762
- category: "Bugs"
36863
+ category: "Bugs",
36864
+ requires: [...new Set(["react", ...noInitializeState.requires ?? []])]
36763
36865
  }
36764
36866
  },
36765
36867
  {
@@ -36792,7 +36894,8 @@ const reactDoctorRules = [
36792
36894
  rule: {
36793
36895
  ...noInlinePropOnMemoComponent,
36794
36896
  framework: "global",
36795
- category: "Performance"
36897
+ category: "Performance",
36898
+ requires: [...new Set(["react", ...noInlinePropOnMemoComponent.requires ?? []])]
36796
36899
  }
36797
36900
  },
36798
36901
  {
@@ -36803,7 +36906,8 @@ const reactDoctorRules = [
36803
36906
  rule: {
36804
36907
  ...noInteractiveElementToNoninteractiveRole,
36805
36908
  framework: "global",
36806
- category: "Accessibility"
36909
+ category: "Accessibility",
36910
+ requires: [...new Set(["react", ...noInteractiveElementToNoninteractiveRole.requires ?? []])]
36807
36911
  }
36808
36912
  },
36809
36913
  {
@@ -36814,7 +36918,8 @@ const reactDoctorRules = [
36814
36918
  rule: {
36815
36919
  ...noIsMounted,
36816
36920
  framework: "global",
36817
- category: "Bugs"
36921
+ category: "Bugs",
36922
+ requires: [...new Set(["react", ...noIsMounted.requires ?? []])]
36818
36923
  }
36819
36924
  },
36820
36925
  {
@@ -36847,7 +36952,8 @@ const reactDoctorRules = [
36847
36952
  rule: {
36848
36953
  ...noLargeAnimatedBlur,
36849
36954
  framework: "global",
36850
- category: "Performance"
36955
+ category: "Performance",
36956
+ requires: [...new Set(["react", ...noLargeAnimatedBlur.requires ?? []])]
36851
36957
  }
36852
36958
  },
36853
36959
  {
@@ -36858,7 +36964,8 @@ const reactDoctorRules = [
36858
36964
  rule: {
36859
36965
  ...noLayoutPropertyAnimation,
36860
36966
  framework: "global",
36861
- category: "Performance"
36967
+ category: "Performance",
36968
+ requires: [...new Set(["react", ...noLayoutPropertyAnimation.requires ?? []])]
36862
36969
  }
36863
36970
  },
36864
36971
  {
@@ -36924,7 +37031,8 @@ const reactDoctorRules = [
36924
37031
  rule: {
36925
37032
  ...noMirrorPropEffect,
36926
37033
  framework: "global",
36927
- category: "Bugs"
37034
+ category: "Bugs",
37035
+ requires: [...new Set(["react", ...noMirrorPropEffect.requires ?? []])]
36928
37036
  }
36929
37037
  },
36930
37038
  {
@@ -36946,7 +37054,8 @@ const reactDoctorRules = [
36946
37054
  rule: {
36947
37055
  ...noMultiComp,
36948
37056
  framework: "global",
36949
- category: "Maintainability"
37057
+ category: "Maintainability",
37058
+ requires: [...new Set(["react", ...noMultiComp.requires ?? []])]
36950
37059
  }
36951
37060
  },
36952
37061
  {
@@ -36957,7 +37066,8 @@ const reactDoctorRules = [
36957
37066
  rule: {
36958
37067
  ...noMutableInDeps,
36959
37068
  framework: "global",
36960
- category: "Bugs"
37069
+ category: "Bugs",
37070
+ requires: [...new Set(["react", ...noMutableInDeps.requires ?? []])]
36961
37071
  }
36962
37072
  },
36963
37073
  {
@@ -36968,7 +37078,8 @@ const reactDoctorRules = [
36968
37078
  rule: {
36969
37079
  ...noMutatingReducerState,
36970
37080
  framework: "global",
36971
- category: "Bugs"
37081
+ category: "Bugs",
37082
+ requires: [...new Set(["react", ...noMutatingReducerState.requires ?? []])]
36972
37083
  }
36973
37084
  },
36974
37085
  {
@@ -36979,7 +37090,8 @@ const reactDoctorRules = [
36979
37090
  rule: {
36980
37091
  ...noNamespace,
36981
37092
  framework: "global",
36982
- category: "Bugs"
37093
+ category: "Bugs",
37094
+ requires: [...new Set(["react", ...noNamespace.requires ?? []])]
36983
37095
  }
36984
37096
  },
36985
37097
  {
@@ -37001,7 +37113,8 @@ const reactDoctorRules = [
37001
37113
  rule: {
37002
37114
  ...noNoninteractiveElementInteractions,
37003
37115
  framework: "global",
37004
- category: "Accessibility"
37116
+ category: "Accessibility",
37117
+ requires: [...new Set(["react", ...noNoninteractiveElementInteractions.requires ?? []])]
37005
37118
  }
37006
37119
  },
37007
37120
  {
@@ -37012,7 +37125,8 @@ const reactDoctorRules = [
37012
37125
  rule: {
37013
37126
  ...noNoninteractiveElementToInteractiveRole,
37014
37127
  framework: "global",
37015
- category: "Accessibility"
37128
+ category: "Accessibility",
37129
+ requires: [...new Set(["react", ...noNoninteractiveElementToInteractiveRole.requires ?? []])]
37016
37130
  }
37017
37131
  },
37018
37132
  {
@@ -37023,7 +37137,8 @@ const reactDoctorRules = [
37023
37137
  rule: {
37024
37138
  ...noNoninteractiveTabindex,
37025
37139
  framework: "global",
37026
- category: "Accessibility"
37140
+ category: "Accessibility",
37141
+ requires: [...new Set(["react", ...noNoninteractiveTabindex.requires ?? []])]
37027
37142
  }
37028
37143
  },
37029
37144
  {
@@ -37045,7 +37160,8 @@ const reactDoctorRules = [
37045
37160
  rule: {
37046
37161
  ...noPassDataToParent,
37047
37162
  framework: "global",
37048
- category: "Bugs"
37163
+ category: "Bugs",
37164
+ requires: [...new Set(["react", ...noPassDataToParent.requires ?? []])]
37049
37165
  }
37050
37166
  },
37051
37167
  {
@@ -37056,7 +37172,8 @@ const reactDoctorRules = [
37056
37172
  rule: {
37057
37173
  ...noPassLiveStateToParent,
37058
37174
  framework: "global",
37059
- category: "Bugs"
37175
+ category: "Bugs",
37176
+ requires: [...new Set(["react", ...noPassLiveStateToParent.requires ?? []])]
37060
37177
  }
37061
37178
  },
37062
37179
  {
@@ -37067,7 +37184,8 @@ const reactDoctorRules = [
37067
37184
  rule: {
37068
37185
  ...noPermanentWillChange,
37069
37186
  framework: "global",
37070
- category: "Performance"
37187
+ category: "Performance",
37188
+ requires: [...new Set(["react", ...noPermanentWillChange.requires ?? []])]
37071
37189
  }
37072
37190
  },
37073
37191
  {
@@ -37100,7 +37218,8 @@ const reactDoctorRules = [
37100
37218
  rule: {
37101
37219
  ...noPropCallbackInEffect,
37102
37220
  framework: "global",
37103
- category: "Bugs"
37221
+ category: "Bugs",
37222
+ requires: [...new Set(["react", ...noPropCallbackInEffect.requires ?? []])]
37104
37223
  }
37105
37224
  },
37106
37225
  {
@@ -37144,7 +37263,8 @@ const reactDoctorRules = [
37144
37263
  rule: {
37145
37264
  ...noReactChildren,
37146
37265
  framework: "global",
37147
- category: "Maintainability"
37266
+ category: "Maintainability",
37267
+ requires: [...new Set(["react", ...noReactChildren.requires ?? []])]
37148
37268
  }
37149
37269
  },
37150
37270
  {
@@ -37177,7 +37297,8 @@ const reactDoctorRules = [
37177
37297
  rule: {
37178
37298
  ...noRedundantRoles,
37179
37299
  framework: "global",
37180
- category: "Accessibility"
37300
+ category: "Accessibility",
37301
+ requires: [...new Set(["react", ...noRedundantRoles.requires ?? []])]
37181
37302
  }
37182
37303
  },
37183
37304
  {
@@ -37188,7 +37309,8 @@ const reactDoctorRules = [
37188
37309
  rule: {
37189
37310
  ...noRedundantShouldComponentUpdate,
37190
37311
  framework: "global",
37191
- category: "Maintainability"
37312
+ category: "Maintainability",
37313
+ requires: [...new Set(["react", ...noRedundantShouldComponentUpdate.requires ?? []])]
37192
37314
  }
37193
37315
  },
37194
37316
  {
@@ -37221,7 +37343,8 @@ const reactDoctorRules = [
37221
37343
  rule: {
37222
37344
  ...noRenderReturnValue,
37223
37345
  framework: "global",
37224
- category: "Bugs"
37346
+ category: "Bugs",
37347
+ requires: [...new Set(["react", ...noRenderReturnValue.requires ?? []])]
37225
37348
  }
37226
37349
  },
37227
37350
  {
@@ -37232,7 +37355,8 @@ const reactDoctorRules = [
37232
37355
  rule: {
37233
37356
  ...noResetAllStateOnPropChange,
37234
37357
  framework: "global",
37235
- category: "Bugs"
37358
+ category: "Bugs",
37359
+ requires: [...new Set(["react", ...noResetAllStateOnPropChange.requires ?? []])]
37236
37360
  }
37237
37361
  },
37238
37362
  {
@@ -37243,7 +37367,8 @@ const reactDoctorRules = [
37243
37367
  rule: {
37244
37368
  ...noScaleFromZero,
37245
37369
  framework: "global",
37246
- category: "Performance"
37370
+ category: "Performance",
37371
+ requires: [...new Set(["react", ...noScaleFromZero.requires ?? []])]
37247
37372
  }
37248
37373
  },
37249
37374
  {
@@ -37265,7 +37390,8 @@ const reactDoctorRules = [
37265
37390
  rule: {
37266
37391
  ...noSelfUpdatingEffect,
37267
37392
  framework: "global",
37268
- category: "Bugs"
37393
+ category: "Bugs",
37394
+ requires: [...new Set(["react", ...noSelfUpdatingEffect.requires ?? []])]
37269
37395
  }
37270
37396
  },
37271
37397
  {
@@ -37276,7 +37402,8 @@ const reactDoctorRules = [
37276
37402
  rule: {
37277
37403
  ...noSetState,
37278
37404
  framework: "global",
37279
- category: "Maintainability"
37405
+ category: "Maintainability",
37406
+ requires: [...new Set(["react", ...noSetState.requires ?? []])]
37280
37407
  }
37281
37408
  },
37282
37409
  {
@@ -37287,7 +37414,8 @@ const reactDoctorRules = [
37287
37414
  rule: {
37288
37415
  ...noSetStateInRender,
37289
37416
  framework: "global",
37290
- category: "Bugs"
37417
+ category: "Bugs",
37418
+ requires: [...new Set(["react", ...noSetStateInRender.requires ?? []])]
37291
37419
  }
37292
37420
  },
37293
37421
  {
@@ -37309,7 +37437,8 @@ const reactDoctorRules = [
37309
37437
  rule: {
37310
37438
  ...noStaticElementInteractions,
37311
37439
  framework: "global",
37312
- category: "Accessibility"
37440
+ category: "Accessibility",
37441
+ requires: [...new Set(["react", ...noStaticElementInteractions.requires ?? []])]
37313
37442
  }
37314
37443
  },
37315
37444
  {
@@ -37320,7 +37449,8 @@ const reactDoctorRules = [
37320
37449
  rule: {
37321
37450
  ...noStringRefs,
37322
37451
  framework: "global",
37323
- category: "Bugs"
37452
+ category: "Bugs",
37453
+ requires: [...new Set(["react", ...noStringRefs.requires ?? []])]
37324
37454
  }
37325
37455
  },
37326
37456
  {
@@ -37331,7 +37461,8 @@ const reactDoctorRules = [
37331
37461
  rule: {
37332
37462
  ...noThisInSfc,
37333
37463
  framework: "global",
37334
- category: "Bugs"
37464
+ category: "Bugs",
37465
+ requires: [...new Set(["react", ...noThisInSfc.requires ?? []])]
37335
37466
  }
37336
37467
  },
37337
37468
  {
@@ -37353,7 +37484,8 @@ const reactDoctorRules = [
37353
37484
  rule: {
37354
37485
  ...noTransitionAll,
37355
37486
  framework: "global",
37356
- category: "Performance"
37487
+ category: "Performance",
37488
+ requires: [...new Set(["react", ...noTransitionAll.requires ?? []])]
37357
37489
  }
37358
37490
  },
37359
37491
  {
@@ -37386,7 +37518,8 @@ const reactDoctorRules = [
37386
37518
  rule: {
37387
37519
  ...noUnescapedEntities,
37388
37520
  framework: "global",
37389
- category: "Bugs"
37521
+ category: "Bugs",
37522
+ requires: [...new Set(["react", ...noUnescapedEntities.requires ?? []])]
37390
37523
  }
37391
37524
  },
37392
37525
  {
@@ -37397,7 +37530,8 @@ const reactDoctorRules = [
37397
37530
  rule: {
37398
37531
  ...noUnknownProperty,
37399
37532
  framework: "global",
37400
- category: "Bugs"
37533
+ category: "Bugs",
37534
+ requires: [...new Set(["react", ...noUnknownProperty.requires ?? []])]
37401
37535
  }
37402
37536
  },
37403
37537
  {
@@ -37408,7 +37542,8 @@ const reactDoctorRules = [
37408
37542
  rule: {
37409
37543
  ...noUnsafe,
37410
37544
  framework: "global",
37411
- category: "Bugs"
37545
+ category: "Bugs",
37546
+ requires: [...new Set(["react", ...noUnsafe.requires ?? []])]
37412
37547
  }
37413
37548
  },
37414
37549
  {
@@ -37419,7 +37554,8 @@ const reactDoctorRules = [
37419
37554
  rule: {
37420
37555
  ...noUnstableNestedComponents,
37421
37556
  framework: "global",
37422
- category: "Performance"
37557
+ category: "Performance",
37558
+ requires: [...new Set(["react", ...noUnstableNestedComponents.requires ?? []])]
37423
37559
  }
37424
37560
  },
37425
37561
  {
@@ -37430,7 +37566,8 @@ const reactDoctorRules = [
37430
37566
  rule: {
37431
37567
  ...noUsememoSimpleExpression,
37432
37568
  framework: "global",
37433
- category: "Performance"
37569
+ category: "Performance",
37570
+ requires: [...new Set(["react", ...noUsememoSimpleExpression.requires ?? []])]
37434
37571
  }
37435
37572
  },
37436
37573
  {
@@ -37452,7 +37589,8 @@ const reactDoctorRules = [
37452
37589
  rule: {
37453
37590
  ...noWillUpdateSetState,
37454
37591
  framework: "global",
37455
- category: "Bugs"
37592
+ category: "Bugs",
37593
+ requires: [...new Set(["react", ...noWillUpdateSetState.requires ?? []])]
37456
37594
  }
37457
37595
  },
37458
37596
  {
@@ -37474,7 +37612,8 @@ const reactDoctorRules = [
37474
37612
  rule: {
37475
37613
  ...onlyExportComponents,
37476
37614
  framework: "global",
37477
- category: "Maintainability"
37615
+ category: "Maintainability",
37616
+ requires: [...new Set(["react", ...onlyExportComponents.requires ?? []])]
37478
37617
  }
37479
37618
  },
37480
37619
  {
@@ -37551,7 +37690,8 @@ const reactDoctorRules = [
37551
37690
  rule: {
37552
37691
  ...preferEs6Class,
37553
37692
  framework: "global",
37554
- category: "Maintainability"
37693
+ category: "Maintainability",
37694
+ requires: [...new Set(["react", ...preferEs6Class.requires ?? []])]
37555
37695
  }
37556
37696
  },
37557
37697
  {
@@ -37573,7 +37713,8 @@ const reactDoctorRules = [
37573
37713
  rule: {
37574
37714
  ...preferFunctionComponent,
37575
37715
  framework: "global",
37576
- category: "Maintainability"
37716
+ category: "Maintainability",
37717
+ requires: [...new Set(["react", ...preferFunctionComponent.requires ?? []])]
37577
37718
  }
37578
37719
  },
37579
37720
  {
@@ -37584,7 +37725,8 @@ const reactDoctorRules = [
37584
37725
  rule: {
37585
37726
  ...preferHtmlDialog,
37586
37727
  framework: "global",
37587
- category: "Accessibility"
37728
+ category: "Accessibility",
37729
+ requires: [...new Set(["react", ...preferHtmlDialog.requires ?? []])]
37588
37730
  }
37589
37731
  },
37590
37732
  {
@@ -37617,7 +37759,8 @@ const reactDoctorRules = [
37617
37759
  rule: {
37618
37760
  ...preferStableEmptyFallback,
37619
37761
  framework: "global",
37620
- category: "Performance"
37762
+ category: "Performance",
37763
+ requires: [...new Set(["react", ...preferStableEmptyFallback.requires ?? []])]
37621
37764
  }
37622
37765
  },
37623
37766
  {
@@ -37628,7 +37771,8 @@ const reactDoctorRules = [
37628
37771
  rule: {
37629
37772
  ...preferTagOverRole,
37630
37773
  framework: "global",
37631
- category: "Accessibility"
37774
+ category: "Accessibility",
37775
+ requires: [...new Set(["react", ...preferTagOverRole.requires ?? []])]
37632
37776
  }
37633
37777
  },
37634
37778
  {
@@ -37639,7 +37783,8 @@ const reactDoctorRules = [
37639
37783
  rule: {
37640
37784
  ...preferUseEffectEvent,
37641
37785
  framework: "global",
37642
- category: "Bugs"
37786
+ category: "Bugs",
37787
+ requires: [...new Set(["react", ...preferUseEffectEvent.requires ?? []])]
37643
37788
  }
37644
37789
  },
37645
37790
  {
@@ -37650,7 +37795,8 @@ const reactDoctorRules = [
37650
37795
  rule: {
37651
37796
  ...preferUseSyncExternalStore,
37652
37797
  framework: "global",
37653
- category: "Bugs"
37798
+ category: "Bugs",
37799
+ requires: [...new Set(["react", ...preferUseSyncExternalStore.requires ?? []])]
37654
37800
  }
37655
37801
  },
37656
37802
  {
@@ -37661,7 +37807,8 @@ const reactDoctorRules = [
37661
37807
  rule: {
37662
37808
  ...preferUseReducer,
37663
37809
  framework: "global",
37664
- category: "Bugs"
37810
+ category: "Bugs",
37811
+ requires: [...new Set(["react", ...preferUseReducer.requires ?? []])]
37665
37812
  }
37666
37813
  },
37667
37814
  {
@@ -37760,7 +37907,8 @@ const reactDoctorRules = [
37760
37907
  rule: {
37761
37908
  ...reactInJsxScope,
37762
37909
  framework: "global",
37763
- category: "Bugs"
37910
+ category: "Bugs",
37911
+ requires: [...new Set(["react", ...reactInJsxScope.requires ?? []])]
37764
37912
  }
37765
37913
  },
37766
37914
  {
@@ -37771,7 +37919,8 @@ const reactDoctorRules = [
37771
37919
  rule: {
37772
37920
  ...reduxUseselectorInlineDerivation,
37773
37921
  framework: "global",
37774
- category: "Performance"
37922
+ category: "Performance",
37923
+ requires: [...new Set(["react", ...reduxUseselectorInlineDerivation.requires ?? []])]
37775
37924
  }
37776
37925
  },
37777
37926
  {
@@ -37782,7 +37931,8 @@ const reactDoctorRules = [
37782
37931
  rule: {
37783
37932
  ...reduxUseselectorReturnsNewCollection,
37784
37933
  framework: "global",
37785
- category: "Performance"
37934
+ category: "Performance",
37935
+ requires: [...new Set(["react", ...reduxUseselectorReturnsNewCollection.requires ?? []])]
37786
37936
  }
37787
37937
  },
37788
37938
  {
@@ -37793,7 +37943,8 @@ const reactDoctorRules = [
37793
37943
  rule: {
37794
37944
  ...renderingAnimateSvgWrapper,
37795
37945
  framework: "global",
37796
- category: "Performance"
37946
+ category: "Performance",
37947
+ requires: [...new Set(["react", ...renderingAnimateSvgWrapper.requires ?? []])]
37797
37948
  }
37798
37949
  },
37799
37950
  {
@@ -37815,7 +37966,8 @@ const reactDoctorRules = [
37815
37966
  rule: {
37816
37967
  ...renderingHoistJsx,
37817
37968
  framework: "global",
37818
- category: "Performance"
37969
+ category: "Performance",
37970
+ requires: [...new Set(["react", ...renderingHoistJsx.requires ?? []])]
37819
37971
  }
37820
37972
  },
37821
37973
  {
@@ -37826,7 +37978,8 @@ const reactDoctorRules = [
37826
37978
  rule: {
37827
37979
  ...renderingHydrationMismatchTime,
37828
37980
  framework: "global",
37829
- category: "Bugs"
37981
+ category: "Bugs",
37982
+ requires: [...new Set(["react", ...renderingHydrationMismatchTime.requires ?? []])]
37830
37983
  }
37831
37984
  },
37832
37985
  {
@@ -37837,7 +37990,8 @@ const reactDoctorRules = [
37837
37990
  rule: {
37838
37991
  ...renderingHydrationNoFlicker,
37839
37992
  framework: "global",
37840
- category: "Performance"
37993
+ category: "Performance",
37994
+ requires: [...new Set(["react", ...renderingHydrationNoFlicker.requires ?? []])]
37841
37995
  }
37842
37996
  },
37843
37997
  {
@@ -37848,7 +38002,8 @@ const reactDoctorRules = [
37848
38002
  rule: {
37849
38003
  ...renderingScriptDeferAsync,
37850
38004
  framework: "global",
37851
- category: "Performance"
38005
+ category: "Performance",
38006
+ requires: [...new Set(["react", ...renderingScriptDeferAsync.requires ?? []])]
37852
38007
  }
37853
38008
  },
37854
38009
  {
@@ -37870,7 +38025,8 @@ const reactDoctorRules = [
37870
38025
  rule: {
37871
38026
  ...renderingUsetransitionLoading,
37872
38027
  framework: "global",
37873
- category: "Performance"
38028
+ category: "Performance",
38029
+ requires: [...new Set(["react", ...renderingUsetransitionLoading.requires ?? []])]
37874
38030
  }
37875
38031
  },
37876
38032
  {
@@ -37881,7 +38037,8 @@ const reactDoctorRules = [
37881
38037
  rule: {
37882
38038
  ...requireRenderReturn,
37883
38039
  framework: "global",
37884
- category: "Bugs"
38040
+ category: "Bugs",
38041
+ requires: [...new Set(["react", ...requireRenderReturn.requires ?? []])]
37885
38042
  }
37886
38043
  },
37887
38044
  {
@@ -37892,7 +38049,8 @@ const reactDoctorRules = [
37892
38049
  rule: {
37893
38050
  ...rerenderDeferReadsHook,
37894
38051
  framework: "global",
37895
- category: "Performance"
38052
+ category: "Performance",
38053
+ requires: [...new Set(["react", ...rerenderDeferReadsHook.requires ?? []])]
37896
38054
  }
37897
38055
  },
37898
38056
  {
@@ -37903,7 +38061,8 @@ const reactDoctorRules = [
37903
38061
  rule: {
37904
38062
  ...rerenderDependencies,
37905
38063
  framework: "global",
37906
- category: "Bugs"
38064
+ category: "Bugs",
38065
+ requires: [...new Set(["react", ...rerenderDependencies.requires ?? []])]
37907
38066
  }
37908
38067
  },
37909
38068
  {
@@ -37914,7 +38073,8 @@ const reactDoctorRules = [
37914
38073
  rule: {
37915
38074
  ...rerenderDerivedStateFromHook,
37916
38075
  framework: "global",
37917
- category: "Performance"
38076
+ category: "Performance",
38077
+ requires: [...new Set(["react", ...rerenderDerivedStateFromHook.requires ?? []])]
37918
38078
  }
37919
38079
  },
37920
38080
  {
@@ -37925,7 +38085,8 @@ const reactDoctorRules = [
37925
38085
  rule: {
37926
38086
  ...rerenderFunctionalSetstate,
37927
38087
  framework: "global",
37928
- category: "Performance"
38088
+ category: "Performance",
38089
+ requires: [...new Set(["react", ...rerenderFunctionalSetstate.requires ?? []])]
37929
38090
  }
37930
38091
  },
37931
38092
  {
@@ -37936,7 +38097,8 @@ const reactDoctorRules = [
37936
38097
  rule: {
37937
38098
  ...rerenderLazyRefInit,
37938
38099
  framework: "global",
37939
- category: "Performance"
38100
+ category: "Performance",
38101
+ requires: [...new Set(["react", ...rerenderLazyRefInit.requires ?? []])]
37940
38102
  }
37941
38103
  },
37942
38104
  {
@@ -37947,7 +38109,8 @@ const reactDoctorRules = [
37947
38109
  rule: {
37948
38110
  ...rerenderLazyStateInit,
37949
38111
  framework: "global",
37950
- category: "Performance"
38112
+ category: "Performance",
38113
+ requires: [...new Set(["react", ...rerenderLazyStateInit.requires ?? []])]
37951
38114
  }
37952
38115
  },
37953
38116
  {
@@ -37958,7 +38121,8 @@ const reactDoctorRules = [
37958
38121
  rule: {
37959
38122
  ...rerenderMemoBeforeEarlyReturn,
37960
38123
  framework: "global",
37961
- category: "Performance"
38124
+ category: "Performance",
38125
+ requires: [...new Set(["react", ...rerenderMemoBeforeEarlyReturn.requires ?? []])]
37962
38126
  }
37963
38127
  },
37964
38128
  {
@@ -37969,7 +38133,8 @@ const reactDoctorRules = [
37969
38133
  rule: {
37970
38134
  ...rerenderMemoWithDefaultValue,
37971
38135
  framework: "global",
37972
- category: "Performance"
38136
+ category: "Performance",
38137
+ requires: [...new Set(["react", ...rerenderMemoWithDefaultValue.requires ?? []])]
37973
38138
  }
37974
38139
  },
37975
38140
  {
@@ -37980,7 +38145,8 @@ const reactDoctorRules = [
37980
38145
  rule: {
37981
38146
  ...rerenderStateOnlyInHandlers,
37982
38147
  framework: "global",
37983
- category: "Performance"
38148
+ category: "Performance",
38149
+ requires: [...new Set(["react", ...rerenderStateOnlyInHandlers.requires ?? []])]
37984
38150
  }
37985
38151
  },
37986
38152
  {
@@ -37991,7 +38157,8 @@ const reactDoctorRules = [
37991
38157
  rule: {
37992
38158
  ...rerenderTransitionsScroll,
37993
38159
  framework: "global",
37994
- category: "Performance"
38160
+ category: "Performance",
38161
+ requires: [...new Set(["react", ...rerenderTransitionsScroll.requires ?? []])]
37995
38162
  }
37996
38163
  },
37997
38164
  {
@@ -38410,7 +38577,8 @@ const reactDoctorRules = [
38410
38577
  rule: {
38411
38578
  ...roleHasRequiredAriaProps,
38412
38579
  framework: "global",
38413
- category: "Accessibility"
38580
+ category: "Accessibility",
38581
+ requires: [...new Set(["react", ...roleHasRequiredAriaProps.requires ?? []])]
38414
38582
  }
38415
38583
  },
38416
38584
  {
@@ -38421,7 +38589,8 @@ const reactDoctorRules = [
38421
38589
  rule: {
38422
38590
  ...roleSupportsAriaProps,
38423
38591
  framework: "global",
38424
- category: "Accessibility"
38592
+ category: "Accessibility",
38593
+ requires: [...new Set(["react", ...roleSupportsAriaProps.requires ?? []])]
38425
38594
  }
38426
38595
  },
38427
38596
  {
@@ -38432,7 +38601,8 @@ const reactDoctorRules = [
38432
38601
  rule: {
38433
38602
  ...rulesOfHooks,
38434
38603
  framework: "global",
38435
- category: "Bugs"
38604
+ category: "Bugs",
38605
+ requires: [...new Set(["react", ...rulesOfHooks.requires ?? []])]
38436
38606
  }
38437
38607
  },
38438
38608
  {
@@ -38443,7 +38613,8 @@ const reactDoctorRules = [
38443
38613
  rule: {
38444
38614
  ...scope,
38445
38615
  framework: "global",
38446
- category: "Accessibility"
38616
+ category: "Accessibility",
38617
+ requires: [...new Set(["react", ...scope.requires ?? []])]
38447
38618
  }
38448
38619
  },
38449
38620
  {
@@ -38454,7 +38625,8 @@ const reactDoctorRules = [
38454
38625
  rule: {
38455
38626
  ...selfClosingComp,
38456
38627
  framework: "global",
38457
- category: "Maintainability"
38628
+ category: "Maintainability",
38629
+ requires: [...new Set(["react", ...selfClosingComp.requires ?? []])]
38458
38630
  }
38459
38631
  },
38460
38632
  {
@@ -38561,7 +38733,8 @@ const reactDoctorRules = [
38561
38733
  rule: {
38562
38734
  ...stateInConstructor,
38563
38735
  framework: "global",
38564
- category: "Maintainability"
38736
+ category: "Maintainability",
38737
+ requires: [...new Set(["react", ...stateInConstructor.requires ?? []])]
38565
38738
  }
38566
38739
  },
38567
38740
  {
@@ -38572,7 +38745,8 @@ const reactDoctorRules = [
38572
38745
  rule: {
38573
38746
  ...stylePropObject,
38574
38747
  framework: "global",
38575
- category: "Bugs"
38748
+ category: "Bugs",
38749
+ requires: [...new Set(["react", ...stylePropObject.requires ?? []])]
38576
38750
  }
38577
38751
  },
38578
38752
  {
@@ -38583,7 +38757,8 @@ const reactDoctorRules = [
38583
38757
  rule: {
38584
38758
  ...tabindexNoPositive,
38585
38759
  framework: "global",
38586
- category: "Accessibility"
38760
+ category: "Accessibility",
38761
+ requires: [...new Set(["react", ...tabindexNoPositive.requires ?? []])]
38587
38762
  }
38588
38763
  },
38589
38764
  {
@@ -38759,7 +38934,8 @@ const reactDoctorRules = [
38759
38934
  rule: {
38760
38935
  ...voidDomElementsNoChildren,
38761
38936
  framework: "global",
38762
- category: "Bugs"
38937
+ category: "Bugs",
38938
+ requires: [...new Set(["react", ...voidDomElementsNoChildren.requires ?? []])]
38763
38939
  }
38764
38940
  },
38765
38941
  {