oxlint-plugin-react-doctor 0.8.1-dev.6fcd9c9 → 0.8.1-dev.7c9dbed

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 +4 -786
  2. package/dist/index.js +351 -2719
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1953,13 +1953,13 @@ const getImportDeclaration = (node) => {
1953
1953
  }
1954
1954
  return null;
1955
1955
  };
1956
- const getImportSource$1 = (declaration) => typeof declaration.source.value === "string" ? declaration.source.value : null;
1956
+ const getImportSource = (declaration) => typeof declaration.source.value === "string" ? declaration.source.value : null;
1957
1957
  const isNamedImport = (symbol, importedName, moduleSources) => {
1958
1958
  if (symbol.kind !== "import") return false;
1959
1959
  const declaration = symbol.declarationNode;
1960
1960
  if (!isNodeOfType(declaration, "ImportSpecifier")) return false;
1961
1961
  const importDeclaration = getImportDeclaration(declaration);
1962
- const source = importDeclaration ? getImportSource$1(importDeclaration) : null;
1962
+ const source = importDeclaration ? getImportSource(importDeclaration) : null;
1963
1963
  if (!source || !moduleSources.has(source)) return false;
1964
1964
  const imported = declaration.imported;
1965
1965
  return isNodeOfType(imported, "Identifier") && imported.name === importedName || isNodeOfType(imported, "Literal") && imported.value === importedName;
@@ -1968,7 +1968,7 @@ const isSatoriImport = (symbol) => {
1968
1968
  if (symbol.kind !== "import") return false;
1969
1969
  const declaration = symbol.declarationNode;
1970
1970
  const importDeclaration = getImportDeclaration(declaration);
1971
- if (!importDeclaration || getImportSource$1(importDeclaration) !== "satori") return false;
1971
+ if (!importDeclaration || getImportSource(importDeclaration) !== "satori") return false;
1972
1972
  if (isNodeOfType(declaration, "ImportDefaultSpecifier")) return true;
1973
1973
  if (!isNodeOfType(declaration, "ImportSpecifier")) return false;
1974
1974
  const imported = declaration.imported;
@@ -1989,7 +1989,7 @@ const isGeneratedImageRendererCall = (node, scopes) => {
1989
1989
  const declaration = symbol.declarationNode;
1990
1990
  if (!isNodeOfType(declaration, "ImportNamespaceSpecifier")) return false;
1991
1991
  const importDeclaration = getImportDeclaration(declaration);
1992
- const source = importDeclaration ? getImportSource$1(importDeclaration) : null;
1992
+ const source = importDeclaration ? getImportSource(importDeclaration) : null;
1993
1993
  return Boolean(source && IMAGE_RESPONSE_MODULES.has(source));
1994
1994
  };
1995
1995
  //#endregion
@@ -3173,7 +3173,7 @@ const getJsxPropStaticStringValues = (attribute, scopes) => getJsxPropStaticStri
3173
3173
  const getJsxPropExhaustiveStaticStringValues = (attribute, scopes) => getJsxPropStaticStringValuesWithMode(attribute, scopes, null, true);
3174
3174
  //#endregion
3175
3175
  //#region src/plugin/rules/a11y/anchor-has-content.ts
3176
- const MESSAGE$66 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
3176
+ const MESSAGE$62 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
3177
3177
  const hasLinkRole = (roleValue) => roleValue.trim().split(/\s+/).find((roleToken) => VALID_ARIA_ROLES.has(roleToken)) === "link";
3178
3178
  const isTransComponentsTemplate = (node) => {
3179
3179
  let current = node.parent;
@@ -3215,7 +3215,7 @@ const anchorHasContent = defineRule({
3215
3215
  if (isTransComponentsTemplate(node)) return;
3216
3216
  context.report({
3217
3217
  node: opening.name,
3218
- message: MESSAGE$66
3218
+ message: MESSAGE$62
3219
3219
  });
3220
3220
  } };
3221
3221
  }
@@ -3703,7 +3703,7 @@ const parseJsxValue = (value) => {
3703
3703
  };
3704
3704
  //#endregion
3705
3705
  //#region src/plugin/rules/a11y/aria-activedescendant-has-tabindex.ts
3706
- const MESSAGE$65 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
3706
+ const MESSAGE$61 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
3707
3707
  const ariaActivedescendantHasTabindex = defineRule({
3708
3708
  id: "aria-activedescendant-has-tabindex",
3709
3709
  title: "aria-activedescendant missing tabindex",
@@ -3721,7 +3721,7 @@ const ariaActivedescendantHasTabindex = defineRule({
3721
3721
  if (tabIndexValue === null || tabIndexValue >= -1) return;
3722
3722
  context.report({
3723
3723
  node: node.name,
3724
- message: MESSAGE$65
3724
+ message: MESSAGE$61
3725
3725
  });
3726
3726
  return;
3727
3727
  }
@@ -3729,7 +3729,7 @@ const ariaActivedescendantHasTabindex = defineRule({
3729
3729
  if (canContentEditableBeTabbable(node, context.scopes, context.settings)) return;
3730
3730
  context.report({
3731
3731
  node: node.name,
3732
- message: MESSAGE$65
3732
+ message: MESSAGE$61
3733
3733
  });
3734
3734
  } })
3735
3735
  });
@@ -6827,7 +6827,7 @@ const stripThisParameter = (parameters) => {
6827
6827
  };
6828
6828
  //#endregion
6829
6829
  //#region src/plugin/rules/security/auth-token-in-web-storage.ts
6830
- const MESSAGE$64 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
6830
+ const MESSAGE$60 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
6831
6831
  const STORAGE_NAMES = new Set(["localStorage", "sessionStorage"]);
6832
6832
  const STORAGE_GLOBALS = new Set([
6833
6833
  "window",
@@ -6999,7 +6999,7 @@ const authTokenInWebStorage = defineRule({
6999
6999
  })) return;
7000
7000
  context.report({
7001
7001
  node,
7002
- message: MESSAGE$64
7002
+ message: MESSAGE$60
7003
7003
  });
7004
7004
  },
7005
7005
  AssignmentExpression(node) {
@@ -7010,7 +7010,7 @@ const authTokenInWebStorage = defineRule({
7010
7010
  if (!propertyName || !isAuthCredentialKey(propertyName)) return;
7011
7011
  context.report({
7012
7012
  node: target,
7013
- message: MESSAGE$64
7013
+ message: MESSAGE$60
7014
7014
  });
7015
7015
  }
7016
7016
  }))
@@ -7149,7 +7149,7 @@ const CI_INSTALL_NEAR_SECRET_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b
7149
7149
  const INSTALL_COMMAND_PATTERN = /(?:npm|pnpm|yarn|bun)\s+(?:install|ci)\b/i;
7150
7150
  const SECRET_REFERENCE_PATTERN = /\bsecrets\.[A-Z0-9_]+/;
7151
7151
  const IGNORE_SCRIPTS_FLAG_PATTERN = /--ignore-scripts\b/;
7152
- const MESSAGE$63 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
7152
+ const MESSAGE$59 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
7153
7153
  const isWorkflowPath = (relativePath) => /(?:^|\/)\.github\/workflows\/[^/]+\.ya?ml$/i.test(relativePath);
7154
7154
  const scanWorkflowContent = (content) => {
7155
7155
  const lines = content.split("\n");
@@ -7194,7 +7194,7 @@ const scanWorkflowContent = (content) => {
7194
7194
  const installLineOffset = Math.max(step.lines.findIndex((stepLine) => INSTALL_COMMAND_PATTERN.test(stepLine)), 0);
7195
7195
  const installColumnIndex = step.lines[installLineOffset].search(INSTALL_COMMAND_PATTERN);
7196
7196
  return [{
7197
- message: MESSAGE$63,
7197
+ message: MESSAGE$59,
7198
7198
  line: step.startLineIndex + installLineOffset + 1,
7199
7199
  column: (installColumnIndex === -1 ? 0 : installColumnIndex) + 1
7200
7200
  }];
@@ -7204,7 +7204,7 @@ const scanWorkflowContent = (content) => {
7204
7204
  const scanNonWorkflowConfig = scanByPattern({
7205
7205
  shouldScan: (file) => isConfigOrCiPath(file.relativePath) && !file.relativePath.endsWith("package.json") && !isWorkflowPath(file.relativePath),
7206
7206
  pattern: CI_INSTALL_NEAR_SECRET_PATTERN,
7207
- message: MESSAGE$63
7207
+ message: MESSAGE$59
7208
7208
  });
7209
7209
  const scan = (file) => {
7210
7210
  if (isWorkflowPath(file.relativePath)) return scanWorkflowContent(file.content);
@@ -7954,7 +7954,7 @@ const isPureEventBlockerHandler = (attribute) => {
7954
7954
  };
7955
7955
  //#endregion
7956
7956
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
7957
- const MESSAGE$62 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
7957
+ const MESSAGE$58 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
7958
7958
  const KEY_HANDLERS = [
7959
7959
  "onKeyUp",
7960
7960
  "onKeyDown",
@@ -8165,7 +8165,7 @@ const clickEventsHaveKeyEvents = defineRule({
8165
8165
  if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
8166
8166
  context.report({
8167
8167
  node: node.name,
8168
- message: MESSAGE$62
8168
+ message: MESSAGE$58
8169
8169
  });
8170
8170
  } };
8171
8171
  }
@@ -10243,19 +10243,21 @@ const isReactComponentName = (name) => {
10243
10243
  return firstCharacter >= 65 && firstCharacter <= 90;
10244
10244
  };
10245
10245
  //#endregion
10246
- //#region src/plugin/utils/get-jsx-attribute-static-string.ts
10247
- const getJsxAttributeStaticString = (attribute) => {
10248
- if (!isNodeOfType(attribute, "JSXAttribute") || !attribute.value) return null;
10249
- if (isNodeOfType(attribute.value, "Literal") && typeof attribute.value.value === "string") return attribute.value.value;
10250
- if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
10251
- const expression = attribute.value.expression;
10252
- if (isNodeOfType(expression, "Literal") && typeof expression.value === "string") return expression.value;
10253
- if (isNodeOfType(expression, "TemplateLiteral") && expression.expressions.length === 0 && expression.quasis.length === 1) return expression.quasis[0].value.raw;
10246
+ //#region src/plugin/rules/react-ui/utils/get-class-name-literal.ts
10247
+ const getClassNameLiteral = (classAttribute) => {
10248
+ if (!isNodeOfType(classAttribute, "JSXAttribute")) return null;
10249
+ if (!classAttribute.value) return null;
10250
+ if (isNodeOfType(classAttribute.value, "Literal") && typeof classAttribute.value.value === "string") return classAttribute.value.value;
10251
+ if (isNodeOfType(classAttribute.value, "JSXExpressionContainer")) {
10252
+ const expression = classAttribute.value.expression;
10253
+ if (isNodeOfType(expression, "Literal") && typeof expression.value === "string") return expression.value;
10254
+ if (isNodeOfType(expression, "TemplateLiteral") && expression.quasis?.length === 1) return expression.quasis[0].value?.raw ?? null;
10255
+ }
10254
10256
  return null;
10255
10257
  };
10256
10258
  //#endregion
10257
10259
  //#region src/plugin/rules/a11y/control-has-associated-label.ts
10258
- const MESSAGE$61 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
10260
+ const MESSAGE$57 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
10259
10261
  const NON_OPERABLE_ELEMENTS = new Set([
10260
10262
  "td",
10261
10263
  "th",
@@ -10313,7 +10315,7 @@ const collectStaticTemplateClassTokens = (templateLiteral) => {
10313
10315
  const hasDisplayNoneClass = (opening) => {
10314
10316
  const classAttribute = hasJsxPropIgnoreCase(opening.attributes, "className") ?? hasJsxPropIgnoreCase(opening.attributes, "class");
10315
10317
  if (!classAttribute) return false;
10316
- const literalValue = getJsxAttributeStaticString(classAttribute);
10318
+ const literalValue = getClassNameLiteral(classAttribute);
10317
10319
  if (literalValue !== null) return literalValue.split(/\s+/).some(isDisplayNoneClassToken);
10318
10320
  if (classAttribute.value && isNodeOfType(classAttribute.value, "JSXExpressionContainer") && isNodeOfType(classAttribute.value.expression, "TemplateLiteral")) return collectStaticTemplateClassTokens(classAttribute.value.expression).some(isDisplayNoneClassToken);
10319
10321
  return false;
@@ -10785,7 +10787,7 @@ const controlHasAssociatedLabel = defineRule({
10785
10787
  if (candidate.enclosingBindingName !== null && labelEmbeddedNames.has(candidate.enclosingBindingName)) continue;
10786
10788
  context.report({
10787
10789
  node: candidate.opening,
10788
- message: MESSAGE$61
10790
+ message: MESSAGE$57
10789
10791
  });
10790
10792
  }
10791
10793
  }
@@ -12429,7 +12431,7 @@ const noRedundantPaddingAxes = defineRule({
12429
12431
  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.",
12430
12432
  create: (context) => ({ JSXAttribute(jsxAttribute) {
12431
12433
  if (!isNodeOfType(jsxAttribute.name, "JSXIdentifier") || jsxAttribute.name.name !== "className") return;
12432
- const classNameLiteral = getJsxAttributeStaticString(jsxAttribute);
12434
+ const classNameLiteral = getClassNameLiteral(jsxAttribute);
12433
12435
  if (!classNameLiteral) return;
12434
12436
  if (!classNameLiteral.includes("px-") || !classNameLiteral.includes("py-")) return;
12435
12437
  if (hasResponsivePrefix(classNameLiteral, "px") || hasResponsivePrefix(classNameLiteral, "py")) return;
@@ -12457,7 +12459,7 @@ const noRedundantSizeAxes = defineRule({
12457
12459
  return { JSXAttribute(jsxAttribute) {
12458
12460
  if (didReportInFile) return;
12459
12461
  if (!isNodeOfType(jsxAttribute.name, "JSXIdentifier") || jsxAttribute.name.name !== "className") return;
12460
- const classNameLiteral = getJsxAttributeStaticString(jsxAttribute);
12462
+ const classNameLiteral = getClassNameLiteral(jsxAttribute);
12461
12463
  if (!classNameLiteral) return;
12462
12464
  if (!classNameLiteral.includes("w-") || !classNameLiteral.includes("h-")) return;
12463
12465
  if (hasResponsivePrefix(classNameLiteral, "w") || hasResponsivePrefix(classNameLiteral, "h")) return;
@@ -12485,7 +12487,7 @@ const noSpaceOnFlexChildren = defineRule({
12485
12487
  recommendation: "Use `gap-*` on the flex or grid parent. `space-x-*` and `space-y-*` leave gaps when a child is hidden, miss spacing on wrapped lines, and don't flip in right-to-left layouts.",
12486
12488
  create: (context) => ({ JSXAttribute(jsxAttribute) {
12487
12489
  if (!isNodeOfType(jsxAttribute.name, "JSXIdentifier") || jsxAttribute.name.name !== "className") return;
12488
- const classNameLiteral = getJsxAttributeStaticString(jsxAttribute);
12490
+ const classNameLiteral = getClassNameLiteral(jsxAttribute);
12489
12491
  if (!classNameLiteral) return;
12490
12492
  if (!classNameLiteral.includes("space-")) return;
12491
12493
  const tokens = tokenizeClassName(classNameLiteral);
@@ -12592,7 +12594,7 @@ const noVagueButtonLabel = defineRule({
12592
12594
  });
12593
12595
  //#endregion
12594
12596
  //#region src/plugin/rules/a11y/dialog-has-accessible-name.ts
12595
- const MESSAGE$60 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
12597
+ const MESSAGE$56 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
12596
12598
  const DIALOG_ROLES = new Set(["dialog", "alertdialog"]);
12597
12599
  const NAME_PROVIDING_ATTRIBUTES = [
12598
12600
  "aria-label",
@@ -12617,7 +12619,7 @@ const dialogHasAccessibleName = defineRule({
12617
12619
  if (NAME_PROVIDING_ATTRIBUTES.some((attribute) => hasJsxPropIgnoreCase(node.attributes, attribute))) return;
12618
12620
  context.report({
12619
12621
  node: node.name,
12620
- message: MESSAGE$60
12622
+ message: MESSAGE$56
12621
12623
  });
12622
12624
  } };
12623
12625
  }
@@ -12657,7 +12659,7 @@ const isEs6Component = (node) => {
12657
12659
  };
12658
12660
  //#endregion
12659
12661
  //#region src/plugin/rules/react-builtins/display-name.ts
12660
- const MESSAGE$59 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
12662
+ const MESSAGE$55 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
12661
12663
  const DEFAULT_ADDITIONAL_HOCS = [
12662
12664
  "observer",
12663
12665
  "lazy",
@@ -12850,7 +12852,7 @@ const displayName = defineRule({
12850
12852
  const reportAt = (node) => {
12851
12853
  context.report({
12852
12854
  node,
12853
- message: MESSAGE$59
12855
+ message: MESSAGE$55
12854
12856
  });
12855
12857
  };
12856
12858
  return {
@@ -13801,18 +13803,17 @@ const isConstAliasOfGlobalArrayFrom = (node, scopes) => {
13801
13803
  const declarator = findDeclaratorForBinding(binding.bindingIdentifier);
13802
13804
  return Boolean(declarator && scopes.symbolFor(unwrappedNode)?.declarationNode === declarator && declarator.init && declarator.parent && isNodeOfType(declarator.parent, "VariableDeclaration") && declarator.parent.kind === "const" && isGlobalArrayFromMember(declarator.init, scopes));
13803
13805
  };
13804
- const executesDuringRender = (callbackExpression, scopes) => {
13805
- const callbackRoot = findTransparentExpressionRoot(callbackExpression);
13806
- const parent = callbackRoot.parent;
13806
+ const executesDuringRender = (functionNode, scopes) => {
13807
+ const parent = functionNode.parent;
13807
13808
  if (isNodeOfType(parent, "NewExpression")) {
13808
13809
  const callee = stripParenExpression(parent.callee);
13809
- return Boolean(scopes && parent.arguments?.[0] === callbackRoot && isNodeOfType(callee, "Identifier") && callee.name === "Promise" && scopes.isGlobalReference(callee));
13810
+ return Boolean(scopes && parent.arguments?.[0] === functionNode && isNodeOfType(callee, "Identifier") && callee.name === "Promise" && scopes.isGlobalReference(callee));
13810
13811
  }
13811
13812
  if (!isNodeOfType(parent, "CallExpression")) return false;
13812
- if (parent.callee === callbackRoot) return true;
13813
- if ((scopes ? isReactApiCall(parent, REACT_RENDER_PHASE_HOOK_NAMES, scopes, { allowGlobalReactNamespace: true }) : isHookCall$2(parent, REACT_RENDER_PHASE_HOOK_NAMES)) && parent.arguments?.[0] === callbackRoot) return true;
13814
- if (scopes && parent.arguments?.[1] === callbackRoot && (isGlobalArrayFromMember(parent.callee, scopes) || isConstAliasOfGlobalArrayFrom(parent.callee, scopes))) return true;
13815
- return isNodeOfType(parent.callee, "MemberExpression") && !parent.callee.computed && isNodeOfType(parent.callee.property, "Identifier") && SYNCHRONOUS_ITERATION_METHOD_NAMES.has(parent.callee.property.name) && parent.arguments?.[0] === callbackRoot;
13813
+ if (parent.callee === functionNode) return true;
13814
+ if ((scopes ? isReactApiCall(parent, REACT_RENDER_PHASE_HOOK_NAMES, scopes, { allowGlobalReactNamespace: true }) : isHookCall$2(parent, REACT_RENDER_PHASE_HOOK_NAMES)) && parent.arguments?.[0] === functionNode) return true;
13815
+ if (scopes && parent.arguments?.[1] === functionNode && (isGlobalArrayFromMember(parent.callee, scopes) || isConstAliasOfGlobalArrayFrom(parent.callee, scopes))) return true;
13816
+ return isNodeOfType(parent.callee, "MemberExpression") && !parent.callee.computed && isNodeOfType(parent.callee.property, "Identifier") && SYNCHRONOUS_ITERATION_METHOD_NAMES.has(parent.callee.property.name) && parent.arguments?.[0] === functionNode;
13816
13817
  };
13817
13818
  //#endregion
13818
13819
  //#region src/plugin/utils/find-render-phase-component-or-hook.ts
@@ -13939,10 +13940,9 @@ const doNodesCoverEveryPathFromFunctionEntry = (owner, matchingNodes, context, o
13939
13940
  //#region src/plugin/utils/get-function-binding-name.ts
13940
13941
  const getFunctionBindingIdentifier$1 = (functionNode) => {
13941
13942
  if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
13942
- const functionRoot = findTransparentExpressionRoot(functionNode);
13943
- const parent = functionRoot.parent;
13943
+ const parent = functionNode.parent;
13944
13944
  if (isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) return parent.id;
13945
- if (isNodeOfType(parent, "AssignmentExpression") && parent.right === functionRoot && isNodeOfType(parent.left, "Identifier")) return parent.left;
13945
+ if (isNodeOfType(parent, "AssignmentExpression") && parent.right === functionNode && isNodeOfType(parent.left, "Identifier")) return parent.left;
13946
13946
  if (isNodeOfType(parent, "CallExpression")) {
13947
13947
  const callParent = parent.parent;
13948
13948
  if (isNodeOfType(callParent, "VariableDeclarator") && isNodeOfType(callParent.id, "Identifier")) return callParent.id;
@@ -19264,7 +19264,7 @@ const forbidElements = defineRule({
19264
19264
  });
19265
19265
  //#endregion
19266
19266
  //#region src/plugin/rules/react-builtins/forward-ref-uses-ref.ts
19267
- const MESSAGE$58 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
19267
+ const MESSAGE$54 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
19268
19268
  const forwardRefUsesRef = defineRule({
19269
19269
  id: "forward-ref-uses-ref",
19270
19270
  title: "forwardRef without ref parameter",
@@ -19287,7 +19287,7 @@ const forwardRefUsesRef = defineRule({
19287
19287
  if (isNodeOfType(onlyParam, "RestElement")) return;
19288
19288
  context.report({
19289
19289
  node: inner,
19290
- message: MESSAGE$58
19290
+ message: MESSAGE$54
19291
19291
  });
19292
19292
  } })
19293
19293
  });
@@ -19328,7 +19328,7 @@ const gitProviderUrlInjectionRisk = defineRule({
19328
19328
  });
19329
19329
  //#endregion
19330
19330
  //#region src/plugin/rules/a11y/heading-has-content.ts
19331
- const MESSAGE$57 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
19331
+ const MESSAGE$53 = "Blind users can't use this heading to navigate because screen readers skip it empty, so add text, `aria-label`, or `aria-labelledby`.";
19332
19332
  const DEFAULT_HEADING_TAGS = [
19333
19333
  "h1",
19334
19334
  "h2",
@@ -19362,7 +19362,7 @@ const headingHasContent = defineRule({
19362
19362
  for (const attribute of ["aria-label", "aria-labelledby"]) if (hasJsxPropIgnoreCase(node.attributes, attribute)) return;
19363
19363
  context.report({
19364
19364
  node,
19365
- message: MESSAGE$57
19365
+ message: MESSAGE$53
19366
19366
  });
19367
19367
  } };
19368
19368
  }
@@ -19530,7 +19530,7 @@ const hooksNoNanInDeps = defineRule({
19530
19530
  });
19531
19531
  //#endregion
19532
19532
  //#region src/plugin/rules/a11y/html-has-lang.ts
19533
- const MESSAGE$56 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
19533
+ const MESSAGE$52 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
19534
19534
  const resolveSettings$38 = (settings) => {
19535
19535
  const reactDoctor = settings?.["react-doctor"];
19536
19536
  return { htmlTags: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.htmlHasLang ?? {} : {}).htmlTags ?? ["html"] };
@@ -19577,13 +19577,13 @@ const htmlHasLang = defineRule({
19577
19577
  if (!lang) {
19578
19578
  context.report({
19579
19579
  node: node.name,
19580
- message: MESSAGE$56
19580
+ message: MESSAGE$52
19581
19581
  });
19582
19582
  return;
19583
19583
  }
19584
19584
  if (evaluateLang(lang.value) === "empty") context.report({
19585
19585
  node: lang,
19586
- message: MESSAGE$56
19586
+ message: MESSAGE$52
19587
19587
  });
19588
19588
  } };
19589
19589
  }
@@ -19856,7 +19856,7 @@ const isJsxFragmentElement = (node, scopes) => {
19856
19856
  };
19857
19857
  //#endregion
19858
19858
  //#region src/plugin/rules/a11y/iframe-has-title.ts
19859
- const MESSAGE$55 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
19859
+ const MESSAGE$51 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
19860
19860
  const isStaticallyAriaHidden = (openingElement) => {
19861
19861
  const ariaHiddenAttribute = getAuthoritativeJsxAttribute(openingElement.attributes, "aria-hidden", false);
19862
19862
  if (!ariaHiddenAttribute) return false;
@@ -19945,14 +19945,14 @@ const iframeHasTitle = defineRule({
19945
19945
  if (!titleAttr) {
19946
19946
  if (hasSpread || tag === "iframe") context.report({
19947
19947
  node: node.name,
19948
- message: MESSAGE$55
19948
+ message: MESSAGE$51
19949
19949
  });
19950
19950
  return;
19951
19951
  }
19952
19952
  const verdict = evaluateTitleValue(titleAttr.value);
19953
19953
  if (verdict === "missing" || verdict === "empty") context.report({
19954
19954
  node: titleAttr,
19955
- message: MESSAGE$55
19955
+ message: MESSAGE$51
19956
19956
  });
19957
19957
  } })
19958
19958
  });
@@ -20077,7 +20077,7 @@ const iframeMissingSandbox = defineRule({
20077
20077
  });
20078
20078
  //#endregion
20079
20079
  //#region src/plugin/rules/a11y/img-redundant-alt.ts
20080
- const MESSAGE$54 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
20080
+ const MESSAGE$50 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
20081
20081
  const DEFAULT_COMPONENTS = ["img"];
20082
20082
  const DEFAULT_REDUNDANT_WORDS = [
20083
20083
  "image",
@@ -20142,7 +20142,7 @@ const imgRedundantAlt = defineRule({
20142
20142
  if (!altAttribute) return;
20143
20143
  if (altValueRedundant(altAttribute, settings.words)) context.report({
20144
20144
  node: altAttribute,
20145
- message: MESSAGE$54
20145
+ message: MESSAGE$50
20146
20146
  });
20147
20147
  } };
20148
20148
  }
@@ -20674,7 +20674,7 @@ const collectHandlerReferencedNames = (root) => {
20674
20674
  //#endregion
20675
20675
  //#region src/plugin/rules/jotai/jotai-select-atom-in-render-body.ts
20676
20676
  const JOTAI_SELECT_ATOM_SOURCES = ["jotai/utils", "jotai"];
20677
- const COMPONENT_NAME_PATTERN$1 = /^[A-Z]/;
20677
+ const COMPONENT_NAME_PATTERN = /^[A-Z]/;
20678
20678
  const isImportedSelectAtom = (callExpression) => {
20679
20679
  if (!isNodeOfType(callExpression.callee, "Identifier")) return false;
20680
20680
  const localName = callExpression.callee.name;
@@ -20710,14 +20710,14 @@ const getHandlerNamedBindingName = (functionNode) => {
20710
20710
  const containingFunctionIsComponentOrHook = (functionNode) => {
20711
20711
  if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) {
20712
20712
  const declaredName = functionNode.id.name;
20713
- return COMPONENT_NAME_PATTERN$1.test(declaredName) || HOOK_NAME_PATTERN$3.test(declaredName);
20713
+ return COMPONENT_NAME_PATTERN.test(declaredName) || HOOK_NAME_PATTERN$3.test(declaredName);
20714
20714
  }
20715
20715
  let cursor = functionNode.parent ?? null;
20716
20716
  while (cursor && isNodeOfType(cursor, "CallExpression")) cursor = cursor.parent ?? null;
20717
20717
  if (!cursor) return false;
20718
20718
  if (!isNodeOfType(cursor, "VariableDeclarator")) return false;
20719
20719
  if (!isNodeOfType(cursor.id, "Identifier")) return false;
20720
- return COMPONENT_NAME_PATTERN$1.test(cursor.id.name) || HOOK_NAME_PATTERN$3.test(cursor.id.name);
20720
+ return COMPONENT_NAME_PATTERN.test(cursor.id.name) || HOOK_NAME_PATTERN$3.test(cursor.id.name);
20721
20721
  };
20722
20722
  const isBindingInvokedOnRenderPath = (root, bindingName) => {
20723
20723
  let didFindRenderPathInvocation = false;
@@ -21795,7 +21795,7 @@ const isDiscardedProbeInsideTry = (node) => {
21795
21795
  return false;
21796
21796
  };
21797
21797
  const isComponentOrHookName = (name) => /^[A-Z]/.test(name) || /^use[A-Z0-9]/.test(name);
21798
- const getFunctionName$1 = (functionNode) => {
21798
+ const getFunctionName = (functionNode) => {
21799
21799
  if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id.name;
21800
21800
  const holder = functionNode.parent;
21801
21801
  if (holder && isNodeOfType(holder, "VariableDeclarator") && isNodeOfType(holder.id, "Identifier")) return holder.id.name;
@@ -21804,7 +21804,7 @@ const getFunctionName$1 = (functionNode) => {
21804
21804
  const isUncacheableOptionsMergeUtility = (node) => {
21805
21805
  const enclosingFunction = findEnclosingFunction$1(node);
21806
21806
  if (!enclosingFunction || !isFunctionLike$1(enclosingFunction)) return false;
21807
- const functionName = getFunctionName$1(enclosingFunction);
21807
+ const functionName = getFunctionName(enclosingFunction);
21808
21808
  if (!functionName || isComponentOrHookName(functionName)) return false;
21809
21809
  const parameterNames = /* @__PURE__ */ new Set();
21810
21810
  for (const parameter of enclosingFunction.params ?? []) collectPatternNames(parameter, parameterNames);
@@ -22106,7 +22106,7 @@ const getHoistableRegExpConstructionKind = (node, context) => {
22106
22106
  if (!STATEFUL_REGEXP_FLAGS_PATTERN.test(effectiveFlags)) return "stateless";
22107
22107
  return isSafeStatefulReplaceAllSearch(node, effectiveFlags, context) ? "statefulReplaceAll" : null;
22108
22108
  };
22109
- const MESSAGE$53 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
22109
+ const MESSAGE$49 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
22110
22110
  const jsHoistRegexp = defineRule({
22111
22111
  id: "js-hoist-regexp",
22112
22112
  title: "RegExp built inside a loop",
@@ -22123,7 +22123,7 @@ const jsHoistRegexp = defineRule({
22123
22123
  if (constructionKind === "statefulReplaceAll" && cachedEnvironmentHazard === "replaceAllIntegrityLost") return;
22124
22124
  context.report({
22125
22125
  node,
22126
- message: MESSAGE$53
22126
+ message: MESSAGE$49
22127
22127
  });
22128
22128
  };
22129
22129
  return createLoopAwareVisitors({
@@ -24890,7 +24890,7 @@ const jsxMaxDepth = defineRule({
24890
24890
  });
24891
24891
  //#endregion
24892
24892
  //#region src/plugin/rules/react-builtins/jsx-no-comment-textnodes.ts
24893
- const MESSAGE$52 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
24893
+ const MESSAGE$48 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
24894
24894
  const LITERAL_TEXT_TAGS = new Set([
24895
24895
  "code",
24896
24896
  "pre",
@@ -24960,7 +24960,7 @@ const jsxNoCommentTextnodes = defineRule({
24960
24960
  if (isDeliberateStyledCommentToken(node)) return;
24961
24961
  context.report({
24962
24962
  node,
24963
- message: MESSAGE$52
24963
+ message: MESSAGE$48
24964
24964
  });
24965
24965
  } })
24966
24966
  });
@@ -24991,7 +24991,7 @@ const isInsideFunctionScope = (node) => {
24991
24991
  };
24992
24992
  //#endregion
24993
24993
  //#region src/plugin/rules/react-builtins/jsx-no-constructed-context-values.ts
24994
- const MESSAGE$51 = "Every reader of this context redraws on each render because you build its `value` inline.";
24994
+ const MESSAGE$47 = "Every reader of this context redraws on each render because you build its `value` inline.";
24995
24995
  const CONTEXT_MODULES$1 = [
24996
24996
  "react",
24997
24997
  "use-context-selector",
@@ -25094,7 +25094,7 @@ const jsxNoConstructedContextValues = defineRule({
25094
25094
  if (!isConstructedValue(innerExpression)) continue;
25095
25095
  context.report({
25096
25096
  node: attribute,
25097
- message: MESSAGE$51
25097
+ message: MESSAGE$47
25098
25098
  });
25099
25099
  }
25100
25100
  }
@@ -25960,7 +25960,7 @@ const DATA_ARRAY_PROP_SUFFIXES = [
25960
25960
  ];
25961
25961
  //#endregion
25962
25962
  //#region src/plugin/rules/react-builtins/jsx-no-new-array-as-prop.ts
25963
- const MESSAGE$50 = "This child redraws every render because the prop gets a brand new array each time.";
25963
+ const MESSAGE$46 = "This child redraws every render because the prop gets a brand new array each time.";
25964
25964
  const isDataArrayPropName = (propName) => {
25965
25965
  if (DATA_ARRAY_PROP_NAMES.has(propName)) return true;
25966
25966
  for (const suffix of DATA_ARRAY_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -26047,7 +26047,7 @@ const jsxNoNewArrayAsProp = defineRule({
26047
26047
  if (!isArrayProducingExpression(expressionNode) && !followsRenderLocalArrayBinding(expressionNode, node)) return;
26048
26048
  context.report({
26049
26049
  node,
26050
- message: MESSAGE$50
26050
+ message: MESSAGE$46
26051
26051
  });
26052
26052
  }
26053
26053
  };
@@ -26305,7 +26305,7 @@ const SAFE_RECEIVER_NAMES = new Set([
26305
26305
  ]);
26306
26306
  //#endregion
26307
26307
  //#region src/plugin/rules/react-builtins/jsx-no-new-function-as-prop.ts
26308
- const MESSAGE$49 = "This child redraws every render because the prop gets a brand new function each time.";
26308
+ const MESSAGE$45 = "This child redraws every render because the prop gets a brand new function each time.";
26309
26309
  const isAccessorPredicateName = (propName) => {
26310
26310
  for (const prefix of ACCESSOR_PREDICATE_PREFIXES) {
26311
26311
  if (propName.length <= prefix.length) continue;
@@ -26512,7 +26512,7 @@ const jsxNoNewFunctionAsProp = defineRule({
26512
26512
  if (!isFunctionProducingExpression(expressionNode) && !followsRenderLocalFunctionBinding(expressionNode, node)) return;
26513
26513
  context.report({
26514
26514
  node,
26515
- message: MESSAGE$49
26515
+ message: MESSAGE$45
26516
26516
  });
26517
26517
  }
26518
26518
  };
@@ -26732,7 +26732,7 @@ const CONFIG_OBJECT_PROP_SUFFIXES = [
26732
26732
  ];
26733
26733
  //#endregion
26734
26734
  //#region src/plugin/rules/react-builtins/jsx-no-new-object-as-prop.ts
26735
- const MESSAGE$48 = "This child redraws every render because the prop gets a brand new object each time.";
26735
+ const MESSAGE$44 = "This child redraws every render because the prop gets a brand new object each time.";
26736
26736
  const isConfigObjectPropName = (propName) => {
26737
26737
  if (CONFIG_OBJECT_PROP_NAMES.has(propName)) return true;
26738
26738
  for (const suffix of CONFIG_OBJECT_PROP_SUFFIXES) if (propName.length > suffix.length && propName.endsWith(suffix)) return true;
@@ -26821,7 +26821,7 @@ const jsxNoNewObjectAsProp = defineRule({
26821
26821
  if (!isObjectProducingExpression(expressionNode) && !followsRenderLocalObjectBinding(expressionNode, node)) return;
26822
26822
  context.report({
26823
26823
  node,
26824
- message: MESSAGE$48
26824
+ message: MESSAGE$44
26825
26825
  });
26826
26826
  }
26827
26827
  };
@@ -26829,7 +26829,7 @@ const jsxNoNewObjectAsProp = defineRule({
26829
26829
  });
26830
26830
  //#endregion
26831
26831
  //#region src/plugin/rules/react-builtins/jsx-no-script-url.ts
26832
- const MESSAGE$47 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
26832
+ const MESSAGE$43 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
26833
26833
  const JAVASCRIPT_URL_PATTERN = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
26834
26834
  const resolveSettings$28 = (settings) => {
26835
26835
  const reactDoctor = settings?.["react-doctor"];
@@ -26867,7 +26867,7 @@ const jsxNoScriptUrl = defineRule({
26867
26867
  if (!value || !isNodeOfType(value, "Literal") || typeof value.value !== "string") continue;
26868
26868
  if (JAVASCRIPT_URL_PATTERN.test(value.value)) context.report({
26869
26869
  node: attribute,
26870
- message: MESSAGE$47
26870
+ message: MESSAGE$43
26871
26871
  });
26872
26872
  }
26873
26873
  } };
@@ -27210,7 +27210,7 @@ const jsxPropsNoSpreadMulti = defineRule({
27210
27210
  });
27211
27211
  //#endregion
27212
27212
  //#region src/plugin/rules/react-builtins/jsx-props-no-spreading.ts
27213
- const MESSAGE$46 = "You can't tell what props reach this element when you spread them.";
27213
+ const MESSAGE$42 = "You can't tell what props reach this element when you spread them.";
27214
27214
  const resolveSettings$25 = (settings) => {
27215
27215
  const reactDoctor = settings?.["react-doctor"];
27216
27216
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxPropsNoSpreading ?? {} : {};
@@ -27253,7 +27253,7 @@ const jsxPropsNoSpreading = defineRule({
27253
27253
  }
27254
27254
  context.report({
27255
27255
  node: attribute,
27256
- message: MESSAGE$46
27256
+ message: MESSAGE$42
27257
27257
  });
27258
27258
  didReportInFile = true;
27259
27259
  return;
@@ -27529,7 +27529,7 @@ const labelHasAssociatedControl = defineRule({
27529
27529
  });
27530
27530
  //#endregion
27531
27531
  //#region src/plugin/rules/a11y/lang.ts
27532
- const MESSAGE$45 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
27532
+ const MESSAGE$41 = "Screen readers can't pick the right voice because this `lang` isn't a real language code, so use a valid one like `en` or `en-US`.";
27533
27533
  const COMMON_LANGUAGE_PRIMARY_TAGS = new Set([
27534
27534
  "aa",
27535
27535
  "ab",
@@ -28009,7 +28009,7 @@ const lang = defineRule({
28009
28009
  if (expression.type === "Identifier" && expression.name === "undefined" || expression.type === "Literal" && expression.value === null) {
28010
28010
  context.report({
28011
28011
  node: langAttr,
28012
- message: MESSAGE$45
28012
+ message: MESSAGE$41
28013
28013
  });
28014
28014
  return;
28015
28015
  }
@@ -28018,7 +28018,7 @@ const lang = defineRule({
28018
28018
  if (value === null) return;
28019
28019
  if (!isValidLangTag(value)) context.report({
28020
28020
  node: langAttr,
28021
- message: MESSAGE$45
28021
+ message: MESSAGE$41
28022
28022
  });
28023
28023
  } })
28024
28024
  });
@@ -28063,13 +28063,13 @@ const mdxSsrExecutionRisk = defineRule({
28063
28063
  recommendation: "Use a constrained compiler for untrusted content, disable expressions/raw HTML, sandbox renderers, and avoid caching attacker-controlled output across tenants.",
28064
28064
  scan: scanByPattern({
28065
28065
  shouldScan: (file) => isProductionSourcePath(file.relativePath),
28066
- pattern: /(?:@mdx-js\/mdx|next-mdx-remote|\b(?:MDXRemote|compileMDX|evaluateMdx)\b)[\s\S]{0,700}\b(?:repo\b|customer|tenant|user[-_]?(?:content|markdown|mdx|input|provided|generated|submitted)|untrusted|searchParams|req\.|request\.|fetch\s*\(|prisma\.|db\.|database|rehypeRaw|allowDangerousHtml)/i,
28066
+ pattern: /(?:@mdx-js\/mdx|next-mdx-remote|\b(?:MDXRemote|compileMDX|evaluateMdx)\b)[\s\S]{0,700}\b(?:repo|customer|tenant|user[-_]?(?:content|markdown|mdx|input|provided|generated|submitted)|untrusted|searchParams|req\.|request\.|fetch\s*\(|prisma\.|db\.|database|rehypeRaw|allowDangerousHtml)/i,
28067
28067
  message: "MDX/markdown rendering code may evaluate user or repository content during SSR or static generation."
28068
28068
  })
28069
28069
  });
28070
28070
  //#endregion
28071
28071
  //#region src/plugin/rules/a11y/media-has-caption.ts
28072
- const MESSAGE$44 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
28072
+ const MESSAGE$40 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
28073
28073
  const DEFAULT_AUDIO = ["audio"];
28074
28074
  const DEFAULT_VIDEO = ["video"];
28075
28075
  const DEFAULT_TRACK = ["track"];
@@ -28159,7 +28159,7 @@ const mediaHasCaption = defineRule({
28159
28159
  if (!parent || !isNodeOfType(parent, "JSXElement")) {
28160
28160
  context.report({
28161
28161
  node: node.name,
28162
- message: MESSAGE$44
28162
+ message: MESSAGE$40
28163
28163
  });
28164
28164
  return;
28165
28165
  }
@@ -28178,553 +28178,12 @@ const mediaHasCaption = defineRule({
28178
28178
  return kindValue.value.toLowerCase() === "captions";
28179
28179
  })) context.report({
28180
28180
  node: node.name,
28181
- message: MESSAGE$44
28181
+ message: MESSAGE$40
28182
28182
  });
28183
28183
  } };
28184
28184
  }
28185
28185
  });
28186
28186
  //#endregion
28187
- //#region src/plugin/utils/get-class-binding-symbol.ts
28188
- const getClassBindingSymbol = (classNode, scopes) => {
28189
- if (isNodeOfType(classNode.id, "Identifier")) return scopes.symbolFor(classNode.id);
28190
- const parent = classNode.parent;
28191
- return isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier") ? scopes.symbolFor(parent.id) : null;
28192
- };
28193
- //#endregion
28194
- //#region src/plugin/utils/mobx-rule-gates.ts
28195
- const MOBX_RULE_GATES = {
28196
- "mobx-reaction-disposer-discarded": { requires: ["mobx:4"] },
28197
- "mobx-no-make-auto-observable-in-inheritance": { requires: ["mobx:6"] },
28198
- "mobx-no-computed-side-effects": { requires: ["mobx:4"] },
28199
- "mobx-async-action-requires-action": { requires: ["mobx:4"] },
28200
- "mobx-no-observer-wrapped-memo": { requires: [
28201
- "mobx:4",
28202
- "mobx-react-binding",
28203
- "react"
28204
- ] },
28205
- "mobx-make-observable-unconditional": { requires: ["mobx:6"] },
28206
- "mobx-legacy-decorator-needs-make-observable": { requires: ["mobx:6"] },
28207
- "mobx-initialize-before-make-auto-observable": { requires: ["mobx:6"] },
28208
- "mobx-observable-read-needs-observer": {
28209
- requires: [
28210
- "mobx:4",
28211
- "mobx-react-binding",
28212
- "react"
28213
- ],
28214
- disabledWhen: ["mobx-react-observer"]
28215
- },
28216
- "mobx-observer-before-inject": { requires: [
28217
- "mobx:4",
28218
- "mobx-react",
28219
- "react"
28220
- ] },
28221
- "mobx-reaction-requires-observable": { requires: ["mobx:4"] },
28222
- "mobx-no-invalid-observable-override": { requires: ["mobx:6"] },
28223
- "mobx-no-observable-prop-to-untracked-child": { requires: [
28224
- "mobx:4",
28225
- "mobx-react-binding",
28226
- "react"
28227
- ] },
28228
- "mobx-no-stale-observable-snapshot-after-await": { requires: ["mobx:4"] },
28229
- "mobx-no-reaction-comparison-value-mutation": { requires: ["mobx:4"] },
28230
- "mobx-observer-class-no-should-component-update": { requires: [
28231
- "mobx:4",
28232
- "mobx-react",
28233
- "react"
28234
- ] },
28235
- "mobx-enable-static-rendering-for-ssr": { requires: [
28236
- "mobx:4",
28237
- "mobx-react-binding",
28238
- "react",
28239
- "ssr"
28240
- ] },
28241
- "mobx-no-rest-destructure-observable": { requires: [
28242
- "mobx:4",
28243
- "mobx-react-binding",
28244
- "react"
28245
- ] },
28246
- "mobx-computed-depends-on-non-observable": { requires: ["mobx:4"] },
28247
- "mobx-no-keepalive-computed-without-disposal": { requires: ["mobx:4"] }
28248
- };
28249
- //#endregion
28250
- //#region src/plugin/utils/resolve-imported-api-reference.ts
28251
- const resolveImportSymbol = (symbol) => {
28252
- const importDeclaration = getImportDeclarationForSymbol(symbol);
28253
- if (!importDeclaration || typeof importDeclaration.source.value !== "string") return null;
28254
- if (isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) return {
28255
- source: importDeclaration.source.value,
28256
- importedName: null,
28257
- isNamespace: true
28258
- };
28259
- if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return {
28260
- source: importDeclaration.source.value,
28261
- importedName: "default",
28262
- isNamespace: false
28263
- };
28264
- const importedName = getImportedName(symbol.declarationNode);
28265
- return importedName ? {
28266
- source: importDeclaration.source.value,
28267
- importedName,
28268
- isNamespace: false
28269
- } : null;
28270
- };
28271
- const resolveIdentifierReference = (identifier, scopes, visitedSymbolIds) => {
28272
- const symbol = scopes.symbolFor(identifier);
28273
- if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
28274
- if (symbol.kind === "import") return resolveImportSymbol(symbol);
28275
- if (symbol.kind !== "const" || !symbol.initializer) return null;
28276
- visitedSymbolIds.add(symbol.id);
28277
- if (isNodeOfType(symbol.declarationNode, "VariableDeclarator") && isNodeOfType(symbol.declarationNode.id, "ObjectPattern")) {
28278
- const receiver = resolveImportedApiReference(symbol.initializer, scopes, visitedSymbolIds);
28279
- if (!receiver || !receiver.isNamespace && receiver.importedName !== "default") return null;
28280
- for (const property of symbol.declarationNode.id.properties) {
28281
- if (!isNodeOfType(property, "Property")) continue;
28282
- if ((isNodeOfType(property.value, "AssignmentPattern") ? property.value.left : property.value) !== symbol.bindingIdentifier) continue;
28283
- const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
28284
- return propertyName ? {
28285
- source: receiver.source,
28286
- importedName: propertyName,
28287
- isNamespace: false
28288
- } : null;
28289
- }
28290
- return null;
28291
- }
28292
- return resolveImportedApiReference(symbol.initializer, scopes, visitedSymbolIds);
28293
- };
28294
- const resolveImportedApiReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
28295
- const unwrappedExpression = stripParenExpression(expression);
28296
- if (isNodeOfType(unwrappedExpression, "Identifier")) return resolveIdentifierReference(unwrappedExpression, scopes, visitedSymbolIds);
28297
- if (!isNodeOfType(unwrappedExpression, "MemberExpression")) return null;
28298
- const propertyName = getStaticPropertyName(unwrappedExpression);
28299
- if (!propertyName) return null;
28300
- const receiver = resolveImportedApiReference(unwrappedExpression.object, scopes, visitedSymbolIds);
28301
- if (!receiver || !receiver.isNamespace && receiver.importedName !== "default") return null;
28302
- return {
28303
- source: receiver.source,
28304
- importedName: propertyName,
28305
- isNamespace: false
28306
- };
28307
- };
28308
- //#endregion
28309
- //#region src/plugin/rules/mobx/mobx-no-make-auto-observable-in-inheritance.ts
28310
- const MESSAGE$43 = "MobX does not support `makeAutoObservable(this)` in inherited classes. Use composition or explicit `makeObservable` annotations.";
28311
- const getEnclosingConstructorClass = (node) => {
28312
- let ancestor = node.parent;
28313
- while (ancestor) {
28314
- if (!isFunctionLike$1(ancestor)) {
28315
- ancestor = ancestor.parent ?? null;
28316
- continue;
28317
- }
28318
- const methodDefinition = ancestor.parent;
28319
- if (!isNodeOfType(methodDefinition, "MethodDefinition") || methodDefinition.kind !== "constructor") return null;
28320
- const classNode = methodDefinition.parent?.parent;
28321
- return isNodeOfType(classNode, "ClassDeclaration") || isNodeOfType(classNode, "ClassExpression") ? classNode : null;
28322
- }
28323
- return null;
28324
- };
28325
- const isNonNullSuperclass = (classNode) => {
28326
- if (!classNode.superClass) return false;
28327
- const superClass = stripParenExpression(classNode.superClass);
28328
- return !(isNodeOfType(superClass, "Literal") && superClass.value === null);
28329
- };
28330
- const subclassedClassSymbolIdsByAnalysis = /* @__PURE__ */ new WeakMap();
28331
- const getSubclassedClassSymbolIds = (node, scopes) => {
28332
- const cached = subclassedClassSymbolIdsByAnalysis.get(scopes);
28333
- if (cached) return cached;
28334
- const symbolIds = /* @__PURE__ */ new Set();
28335
- const program = findProgramRoot(node);
28336
- if (program) walkAst(program, (candidate) => {
28337
- if (!isNodeOfType(candidate, "ClassDeclaration") && !isNodeOfType(candidate, "ClassExpression") || !isNonNullSuperclass(candidate)) return;
28338
- const superClassExpression = candidate.superClass;
28339
- if (!superClassExpression) return;
28340
- const superClass = stripParenExpression(superClassExpression);
28341
- if (!isNodeOfType(superClass, "Identifier")) return;
28342
- const symbol = resolveConstIdentifierAlias(superClass, scopes);
28343
- if (symbol?.kind === "class" || symbol?.kind === "const" && isNodeOfType(symbol.initializer, "ClassExpression")) symbolIds.add(symbol.id);
28344
- });
28345
- subclassedClassSymbolIdsByAnalysis.set(scopes, symbolIds);
28346
- return symbolIds;
28347
- };
28348
- const isMakeAutoObservableCall = (callExpression, scopes) => {
28349
- const reference = resolveImportedApiReference(callExpression.callee, scopes);
28350
- return reference?.source === "mobx" && reference.importedName === "makeAutoObservable";
28351
- };
28352
- const mobxNoMakeAutoObservableInInheritance = defineRule({
28353
- id: "mobx-no-make-auto-observable-in-inheritance",
28354
- title: "Unsupported MobX auto-observable inheritance",
28355
- severity: "error",
28356
- category: "Bugs",
28357
- requires: MOBX_RULE_GATES["mobx-no-make-auto-observable-in-inheritance"].requires,
28358
- recommendation: "Replace inheritance with composition, or annotate inherited members explicitly with `makeObservable`.",
28359
- create: (context) => ({ CallExpression(callExpression) {
28360
- if (!isMakeAutoObservableCall(callExpression, context.scopes)) return;
28361
- const target = callExpression.arguments[0];
28362
- if (!target || !isNodeOfType(stripParenExpression(target), "ThisExpression")) return;
28363
- const classNode = getEnclosingConstructorClass(callExpression);
28364
- if (!classNode) return;
28365
- const classSymbol = getClassBindingSymbol(classNode, context.scopes);
28366
- const isSubclassed = Boolean(classSymbol && getSubclassedClassSymbolIds(callExpression, context.scopes).has(classSymbol.id));
28367
- if (!isNonNullSuperclass(classNode) && !isSubclassed) return;
28368
- context.report({
28369
- node: callExpression,
28370
- message: MESSAGE$43
28371
- });
28372
- } })
28373
- });
28374
- //#endregion
28375
- //#region src/plugin/rules/mobx/mobx-no-observer-wrapped-memo.ts
28376
- const OBSERVER_MODULES = new Set(["mobx-react", "mobx-react-lite"]);
28377
- const MESSAGE$42 = "`observer` cannot wrap an already memoized or observed component. Apply `observer` first, then place `memo` outside only if needed.";
28378
- const isObserverCall = (callExpression, scopes) => {
28379
- const reference = resolveImportedApiReference(callExpression.callee, scopes);
28380
- return Boolean(reference?.importedName === "observer" && OBSERVER_MODULES.has(reference.source));
28381
- };
28382
- const hasInvalidInnerWrapper = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
28383
- const unwrappedExpression = stripParenExpression(expression);
28384
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
28385
- const symbol = scopes.symbolFor(unwrappedExpression);
28386
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id)) return false;
28387
- visitedSymbolIds.add(symbol.id);
28388
- return hasInvalidInnerWrapper(symbol.initializer, scopes, visitedSymbolIds);
28389
- }
28390
- if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
28391
- const reference = resolveImportedApiReference(unwrappedExpression.callee, scopes);
28392
- if (reference?.importedName === "observer" && OBSERVER_MODULES.has(reference.source)) return true;
28393
- return reference?.source === "react" && reference.importedName === "memo";
28394
- };
28395
- const mobxNoObserverWrappedMemo = defineRule({
28396
- id: "mobx-no-observer-wrapped-memo",
28397
- title: "Invalid MobX observer wrapper order",
28398
- severity: "error",
28399
- category: "Bugs",
28400
- requires: MOBX_RULE_GATES["mobx-no-observer-wrapped-memo"].requires,
28401
- recommendation: "Pass the component directly to `observer`, or apply React `memo` outside the resulting observer component.",
28402
- create: (context) => ({ CallExpression(callExpression) {
28403
- if (!isObserverCall(callExpression, context.scopes)) return;
28404
- const componentArgument = callExpression.arguments[0];
28405
- if (!componentArgument) return;
28406
- if (!hasInvalidInnerWrapper(componentArgument, context.scopes)) return;
28407
- context.report({
28408
- node: callExpression,
28409
- message: MESSAGE$42
28410
- });
28411
- } })
28412
- });
28413
- //#endregion
28414
- //#region src/plugin/utils/is-result-discarded-call.ts
28415
- const isResultDiscardedCall = (callExpression) => {
28416
- let node = callExpression;
28417
- let parent = node.parent;
28418
- while (parent) {
28419
- if (isNodeOfType(parent, "ExpressionStatement")) return true;
28420
- if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "void") return true;
28421
- if (isNodeOfType(parent, "ArrowFunctionExpression") && parent.body === node) return true;
28422
- if (isNodeOfType(parent, "ChainExpression")) {
28423
- node = parent;
28424
- parent = node.parent;
28425
- continue;
28426
- }
28427
- if (isNodeOfType(parent, "LogicalExpression") && parent.right === node) {
28428
- node = parent;
28429
- parent = node.parent;
28430
- continue;
28431
- }
28432
- if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === node || parent.alternate === node)) {
28433
- node = parent;
28434
- parent = node.parent;
28435
- continue;
28436
- }
28437
- if (isNodeOfType(parent, "SequenceExpression")) {
28438
- const expressions = parent.expressions ?? [];
28439
- if (expressions[expressions.length - 1] !== node) return true;
28440
- node = parent;
28441
- parent = node.parent;
28442
- continue;
28443
- }
28444
- return false;
28445
- }
28446
- return false;
28447
- };
28448
- //#endregion
28449
- //#region src/plugin/utils/resolve-stable-options-object.ts
28450
- const resolveStableOptionsObject = (expression, observedPropertyNames, scopes, referenceNode = expression, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
28451
- const unwrappedExpression = stripParenExpression(expression);
28452
- if (isNodeOfType(unwrappedExpression, "ObjectExpression")) return unwrappedExpression;
28453
- if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
28454
- const symbol = scopes.symbolFor(unwrappedExpression);
28455
- if (!symbol?.initializer || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, referenceNode, scopes) || observedPropertyNames.some((propertyName) => hasPossibleStaticPropertyWriteBefore(unwrappedExpression, propertyName, referenceNode, scopes))) return null;
28456
- visitedSymbolIds.add(symbol.id);
28457
- return resolveStableOptionsObject(symbol.initializer, observedPropertyNames, scopes, referenceNode, visitedSymbolIds);
28458
- };
28459
- //#endregion
28460
- //#region src/plugin/rules/mobx/mobx-reaction-disposer-discarded.ts
28461
- const MESSAGE$41 = "This MobX reaction discards its disposer and can outlive its owner. Store and dispose it during teardown, or provide an AbortSignal.";
28462
- const LEAKING_SUBSCRIPTION_NAMES = new Set(["reaction", "autorun"]);
28463
- const OPTIONS_ARGUMENT_INDEX = {
28464
- autorun: 1,
28465
- reaction: 2
28466
- };
28467
- const DISPOSER_COERCION_NAMES = new Set([
28468
- "Boolean",
28469
- "Number",
28470
- "String"
28471
- ]);
28472
- const NON_OBSERVABLE_GLOBAL_RECEIVER_NAMES = new Set([
28473
- "Array",
28474
- "Boolean",
28475
- "JSON",
28476
- "Math",
28477
- "Number",
28478
- "Object",
28479
- "String",
28480
- "console"
28481
- ]);
28482
- const NON_OBSERVING_IMPORTED_METHOD_NAMES = new Set([
28483
- "add",
28484
- "clear",
28485
- "delete",
28486
- "remove",
28487
- "save",
28488
- "set",
28489
- "update",
28490
- "write"
28491
- ]);
28492
- const REACTION_PARAMETER_INDEX = {
28493
- autorun: 0,
28494
- reaction: 2
28495
- };
28496
- const REACTION_CALLBACK_INDEX = {
28497
- autorun: 0,
28498
- reaction: 1
28499
- };
28500
- const OBSERVATION_CALLBACK_INDEX = {
28501
- autorun: 0,
28502
- reaction: 0
28503
- };
28504
- const PROCESS_LIFETIME_WIRING_NAME_PATTERN = /^(?:register.*(?:reactions?|autoruns?)|init.*(?:stores?|reactions?|autoruns?)|setup.*(?:stores?|reactions?|autoruns?)|bootstrap(?:app(?:lication)?|stores?|reactions?|autoruns?))$/i;
28505
- const resolveLeakingSubscriptionName = (callExpression, scopes) => {
28506
- const reference = resolveImportedApiReference(callExpression.callee, scopes);
28507
- if (reference?.source !== "mobx" || !reference.importedName || !LEAKING_SUBSCRIPTION_NAMES.has(reference.importedName)) return null;
28508
- return reference.importedName;
28509
- };
28510
- const isEvaluatedAtModuleScope = (node) => {
28511
- let ancestor = node.parent;
28512
- while (ancestor) {
28513
- if (isNodeOfType(ancestor, "PropertyDefinition") || isNodeOfType(ancestor, "AccessorProperty")) {
28514
- if (!ancestor.static) return false;
28515
- ancestor = ancestor.parent ?? null;
28516
- continue;
28517
- }
28518
- if (isNodeOfType(ancestor, "StaticBlock")) {
28519
- ancestor = ancestor.parent ?? null;
28520
- continue;
28521
- }
28522
- if (isFunctionLike$1(ancestor)) {
28523
- const functionRoot = findTransparentExpressionRoot(ancestor);
28524
- const invocation = functionRoot.parent;
28525
- if (isNodeOfType(invocation, "CallExpression") && stripParenExpression(invocation.callee) === functionRoot) {
28526
- ancestor = invocation.parent ?? null;
28527
- continue;
28528
- }
28529
- return false;
28530
- }
28531
- ancestor = ancestor.parent ?? null;
28532
- }
28533
- return true;
28534
- };
28535
- const getFunctionName = (functionNode) => {
28536
- if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id.name;
28537
- const parent = functionNode.parent;
28538
- return isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier") ? parent.id.name : null;
28539
- };
28540
- const isModuleScopedFunction = (functionNode) => {
28541
- let ancestor = functionNode.parent;
28542
- while (ancestor) {
28543
- if (isFunctionLike$1(ancestor) || isNodeOfType(ancestor, "ClassBody")) return false;
28544
- if (isNodeOfType(ancestor, "Program")) return true;
28545
- ancestor = ancestor.parent ?? null;
28546
- }
28547
- return false;
28548
- };
28549
- const getDirectCalls = (functionNode, scopes) => {
28550
- const bindingIdentifier = isNodeOfType(functionNode, "FunctionDeclaration") ? functionNode.id : isNodeOfType(functionNode.parent, "VariableDeclarator") ? functionNode.parent.id : null;
28551
- if (!bindingIdentifier || !isNodeOfType(bindingIdentifier, "Identifier")) return [];
28552
- const symbol = scopes.scopeFor(functionNode).symbolsByName.get(bindingIdentifier.name);
28553
- if (!symbol) return [];
28554
- const calls = [];
28555
- const program = findProgramRoot(functionNode);
28556
- if (!program) return calls;
28557
- walkAst(program, (candidate) => {
28558
- if (!isNodeOfType(candidate, "CallExpression")) return;
28559
- const callee = stripParenExpression(candidate.callee);
28560
- if (!isNodeOfType(callee, "Identifier")) return;
28561
- if (resolveConstIdentifierAlias(callee, scopes)?.id === symbol.id) calls.push(candidate);
28562
- });
28563
- return calls;
28564
- };
28565
- const processLifetimeClassSymbolIdsByAnalysis = /* @__PURE__ */ new WeakMap();
28566
- const getProcessLifetimeClassSymbolIds = (node, scopes) => {
28567
- const cached = processLifetimeClassSymbolIdsByAnalysis.get(scopes);
28568
- if (cached) return cached;
28569
- const instantiationScopeBySymbolId = /* @__PURE__ */ new Map();
28570
- const program = findProgramRoot(node);
28571
- if (program) walkAst(program, (candidate) => {
28572
- if (!isNodeOfType(candidate, "NewExpression")) return;
28573
- const callee = stripParenExpression(candidate.callee);
28574
- const symbol = isNodeOfType(callee, "Identifier") ? resolveConstIdentifierAlias(callee, scopes) : null;
28575
- if (!symbol) return;
28576
- const wasModuleOnly = instantiationScopeBySymbolId.get(symbol.id) ?? true;
28577
- instantiationScopeBySymbolId.set(symbol.id, wasModuleOnly && isEvaluatedAtModuleScope(candidate));
28578
- });
28579
- const processLifetimeSymbolIds = /* @__PURE__ */ new Set();
28580
- for (const [symbolId, isModuleOnly] of instantiationScopeBySymbolId) if (isModuleOnly) processLifetimeSymbolIds.add(symbolId);
28581
- processLifetimeClassSymbolIdsByAnalysis.set(scopes, processLifetimeSymbolIds);
28582
- return processLifetimeSymbolIds;
28583
- };
28584
- const isProcessLifetimeWiring = (node, scopes) => {
28585
- let ancestor = node.parent;
28586
- while (ancestor) {
28587
- if (!isFunctionLike$1(ancestor)) {
28588
- ancestor = ancestor.parent ?? null;
28589
- continue;
28590
- }
28591
- const functionName = getFunctionName(ancestor);
28592
- if (functionName && PROCESS_LIFETIME_WIRING_NAME_PATTERN.test(functionName) && isModuleScopedFunction(ancestor)) {
28593
- const calls = getDirectCalls(ancestor, scopes);
28594
- return calls.length > 0 && calls.every(isEvaluatedAtModuleScope);
28595
- }
28596
- const methodDefinition = ancestor.parent;
28597
- if (isNodeOfType(methodDefinition, "MethodDefinition") && methodDefinition.kind === "constructor") {
28598
- let classNode = methodDefinition.parent?.parent;
28599
- if (isNodeOfType(classNode, "ClassDeclaration") || isNodeOfType(classNode, "ClassExpression")) {
28600
- const classRoot = findTransparentExpressionRoot(classNode);
28601
- const classInstantiation = classRoot.parent;
28602
- if (isNodeOfType(classNode, "ClassExpression") && isNodeOfType(classInstantiation, "NewExpression") && stripParenExpression(classInstantiation.callee) === classRoot) return isEvaluatedAtModuleScope(classInstantiation);
28603
- const classSymbol = getClassBindingSymbol(classNode, scopes);
28604
- return Boolean(classSymbol && getProcessLifetimeClassSymbolIds(node, scopes).has(classSymbol.id));
28605
- }
28606
- }
28607
- return false;
28608
- }
28609
- return false;
28610
- };
28611
- const mayCarryAbortSignal = (optionsArgument, scopes) => {
28612
- if (!optionsArgument) return false;
28613
- const options = resolveStableOptionsObject(optionsArgument, ["signal"], scopes);
28614
- if (!options) return true;
28615
- return options.properties.some((property) => {
28616
- if (!isNodeOfType(property, "Property")) return true;
28617
- const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
28618
- if (propertyName === null) return true;
28619
- if (propertyName !== "signal") return false;
28620
- const value = property.value;
28621
- if (isNodeOfType(value, "Identifier") && value.name === "undefined") return false;
28622
- if (isNodeOfType(value, "Literal") && value.value == null) return false;
28623
- return !(isNodeOfType(value, "UnaryExpression") && value.operator === "void");
28624
- });
28625
- };
28626
- const callbackDisposesReaction = (callExpression, subscriptionName, scopes) => {
28627
- const callbackArgument = callExpression.arguments[REACTION_CALLBACK_INDEX[subscriptionName]];
28628
- if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return false;
28629
- const callback = resolveExactLocalFunction(callbackArgument, scopes);
28630
- if (!callback || !isFunctionLike$1(callback)) return false;
28631
- const reactionParameter = callback.params?.[REACTION_PARAMETER_INDEX[subscriptionName]];
28632
- if (!reactionParameter || !isNodeOfType(reactionParameter, "Identifier")) return false;
28633
- const reactionSymbol = scopes.symbolFor(reactionParameter);
28634
- if (!reactionSymbol) return false;
28635
- let doesDisposeReaction = false;
28636
- walkAst(callback, (candidate) => {
28637
- if (doesDisposeReaction || !isNodeOfType(candidate, "CallExpression")) return;
28638
- const callee = stripParenExpression(candidate.callee);
28639
- if (!isNodeOfType(callee, "MemberExpression")) return;
28640
- const receiver = stripParenExpression(callee.object);
28641
- if (isNodeOfType(receiver, "Identifier") && scopes.symbolFor(receiver)?.id === reactionSymbol.id && getStaticPropertyKeyName(callee, { allowComputedString: true }) === "dispose") doesDisposeReaction = true;
28642
- });
28643
- return doesDisposeReaction;
28644
- };
28645
- const getMemberReceiverRoot = (memberExpression) => {
28646
- let receiver = stripParenExpression(memberExpression);
28647
- while (isNodeOfType(receiver, "MemberExpression")) receiver = stripParenExpression(receiver.object);
28648
- return receiver;
28649
- };
28650
- const observesOnlyInstanceRootedState = (callExpression, subscriptionName, scopes) => {
28651
- const callbackArgument = callExpression.arguments[OBSERVATION_CALLBACK_INDEX[subscriptionName]];
28652
- if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return false;
28653
- const callback = resolveExactLocalFunction(callbackArgument, scopes);
28654
- if (!callback || !isFunctionLike$1(callback)) return false;
28655
- let observesExternalState = false;
28656
- walkAst(callback, (candidate) => {
28657
- if (candidate !== callback && isFunctionLike$1(candidate)) return false;
28658
- if (observesExternalState || !isNodeOfType(candidate, "MemberExpression")) return;
28659
- const receiver = getMemberReceiverRoot(candidate);
28660
- const candidateRoot = findTransparentExpressionRoot(candidate);
28661
- const parent = candidateRoot.parent;
28662
- const isDirectMethodCall = isNodeOfType(parent, "CallExpression") && parent.callee === candidateRoot;
28663
- if (isNodeOfType(receiver, "ThisExpression")) {
28664
- if (isDirectMethodCall) observesExternalState = true;
28665
- return;
28666
- }
28667
- if (!isNodeOfType(receiver, "Identifier")) {
28668
- observesExternalState = true;
28669
- return;
28670
- }
28671
- const receiverSymbol = scopes.symbolFor(receiver);
28672
- if (receiverSymbol && isAstDescendant(receiverSymbol.declarationNode, callback) || scopes.isGlobalReference(receiver) && NON_OBSERVABLE_GLOBAL_RECEIVER_NAMES.has(receiver.name)) return;
28673
- if (receiverSymbol?.kind === "import" && isDirectMethodCall && NON_OBSERVING_IMPORTED_METHOD_NAMES.has(getStaticPropertyKeyName(candidate, { allowComputedString: true }) ?? "")) return;
28674
- observesExternalState = true;
28675
- });
28676
- return !observesExternalState;
28677
- };
28678
- const isForwardedFromConciseArrow = (callExpression) => {
28679
- let expressionRoot = findTransparentExpressionRoot(callExpression);
28680
- let parent = expressionRoot.parent;
28681
- while (parent) {
28682
- if (isNodeOfType(parent, "ArrowFunctionExpression") && parent.body === expressionRoot) return true;
28683
- if (isNodeOfType(parent, "LogicalExpression") && (parent.right === expressionRoot || parent.left === expressionRoot && parent.operator !== "&&") || isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === expressionRoot || parent.alternate === expressionRoot) || isNodeOfType(parent, "SequenceExpression") && parent.expressions[parent.expressions.length - 1] === expressionRoot) {
28684
- expressionRoot = findTransparentExpressionRoot(parent);
28685
- parent = expressionRoot.parent;
28686
- continue;
28687
- }
28688
- return false;
28689
- }
28690
- return false;
28691
- };
28692
- const isDisposerDiscarded = (callExpression) => {
28693
- if (isForwardedFromConciseArrow(callExpression)) return false;
28694
- const expressionRoot = findTransparentExpressionRoot(callExpression);
28695
- if (isResultDiscardedCall(callExpression)) return true;
28696
- const parent = expressionRoot.parent;
28697
- if (!parent) return false;
28698
- if (isNodeOfType(parent, "UnaryExpression") || isNodeOfType(parent, "BinaryExpression")) return true;
28699
- if ((isNodeOfType(parent, "IfStatement") || isNodeOfType(parent, "WhileStatement") || isNodeOfType(parent, "DoWhileStatement") || isNodeOfType(parent, "ForStatement")) && parent.test === expressionRoot) return true;
28700
- if (isNodeOfType(parent, "ConditionalExpression") && parent.test === expressionRoot || isNodeOfType(parent, "SwitchStatement") && parent.discriminant === expressionRoot) return true;
28701
- if (isNodeOfType(parent, "LogicalExpression") && parent.left === expressionRoot) return parent.operator === "&&" || isResultDiscardedCall(parent);
28702
- const callee = isNodeOfType(parent, "CallExpression") ? stripParenExpression(parent.callee) : null;
28703
- return Boolean(isNodeOfType(parent, "CallExpression") && parent.arguments.some((argument) => argument === expressionRoot) && isNodeOfType(callee, "Identifier") && DISPOSER_COERCION_NAMES.has(callee.name));
28704
- };
28705
- const mobxReactionDisposerDiscarded = defineRule({
28706
- id: "mobx-reaction-disposer-discarded",
28707
- title: "MobX reaction disposer discarded",
28708
- severity: "warn",
28709
- category: "Bugs",
28710
- requires: MOBX_RULE_GATES["mobx-reaction-disposer-discarded"].requires,
28711
- recommendation: "Keep the disposer returned by `reaction` or `autorun` and invoke it during teardown, or provide an AbortSignal.",
28712
- create: (context) => ({ CallExpression(callExpression) {
28713
- const subscriptionName = resolveLeakingSubscriptionName(callExpression, context.scopes);
28714
- if (!subscriptionName || !isDisposerDiscarded(callExpression)) return;
28715
- if (isEvaluatedAtModuleScope(callExpression)) return;
28716
- if (isProcessLifetimeWiring(callExpression, context.scopes)) return;
28717
- if (callbackDisposesReaction(callExpression, subscriptionName, context.scopes)) return;
28718
- if (observesOnlyInstanceRootedState(callExpression, subscriptionName, context.scopes)) return;
28719
- const optionsArgument = callExpression.arguments[OPTIONS_ARGUMENT_INDEX[subscriptionName]];
28720
- if (mayCarryAbortSignal(optionsArgument, context.scopes)) return;
28721
- context.report({
28722
- node: callExpression,
28723
- message: MESSAGE$41
28724
- });
28725
- } })
28726
- });
28727
- //#endregion
28728
28187
  //#region src/plugin/utils/has-jsx-prop.ts
28729
28188
  const hasJsxProp = (attributes, targetProp) => {
28730
28189
  for (const attribute of attributes) {
@@ -29515,11 +28974,11 @@ const hasEmailTemplateImport = (programRoot) => {
29515
28974
  return found;
29516
28975
  };
29517
28976
  //#endregion
29518
- //#region src/plugin/utils/build-source-project-index.ts
29519
- const SOURCE_PROJECT_FILE_PATTERN = /\.[cm]?[jt]sx?$/i;
29520
- const SOURCE_PROJECT_DECLARATION_FILE_PATTERN = /\.d\.[cm]?[jt]s$/i;
29521
- const SOURCE_PROJECT_MDX_FILE_PATTERN = /\.mdx$/i;
29522
- const SOURCE_PROJECT_IGNORED_DIRECTORY_NAMES = new Set([
28977
+ //#region src/plugin/utils/build-generated-image-project-index.ts
28978
+ const GENERATED_IMAGE_SOURCE_FILE_PATTERN = /\.[cm]?[jt]sx?$/i;
28979
+ const GENERATED_IMAGE_DECLARATION_FILE_PATTERN = /\.d\.[cm]?[jt]s$/i;
28980
+ const GENERATED_IMAGE_MDX_FILE_PATTERN = /\.mdx$/i;
28981
+ const GENERATED_IMAGE_IGNORED_DIRECTORY_NAMES = new Set([
29523
28982
  ".angular",
29524
28983
  ".astro",
29525
28984
  ".cache",
@@ -29540,12 +28999,12 @@ const SOURCE_PROJECT_IGNORED_DIRECTORY_NAMES = new Set([
29540
28999
  "out",
29541
29000
  "storybook-static"
29542
29001
  ]);
29543
- const sourceProjectScopeCache = /* @__PURE__ */ new WeakMap();
29544
- const getSourceProjectModuleScopes = (programNode) => {
29545
- const cachedScopes = sourceProjectScopeCache.get(programNode);
29002
+ const generatedImageScopeCache = /* @__PURE__ */ new WeakMap();
29003
+ const getGeneratedImageModuleScopes = (programNode) => {
29004
+ const cachedScopes = generatedImageScopeCache.get(programNode);
29546
29005
  if (cachedScopes) return cachedScopes;
29547
29006
  const scopes = analyzeScopes(programNode);
29548
- sourceProjectScopeCache.set(programNode, scopes);
29007
+ generatedImageScopeCache.set(programNode, scopes);
29549
29008
  return scopes;
29550
29009
  };
29551
29010
  const listProductionSourceFiles = (rootDirectory) => {
@@ -29563,7 +29022,7 @@ const listProductionSourceFiles = (rootDirectory) => {
29563
29022
  }
29564
29023
  for (const entry of entries) {
29565
29024
  const absolutePath = path.join(currentDirectory, entry.name);
29566
- const isIgnoredDirectoryName = SOURCE_PROJECT_IGNORED_DIRECTORY_NAMES.has(entry.name) || entry.name.startsWith(".") && entry.name !== ".dumi" && entry.name !== ".storybook";
29025
+ const isIgnoredDirectoryName = GENERATED_IMAGE_IGNORED_DIRECTORY_NAMES.has(entry.name) || entry.name.startsWith(".") && entry.name !== ".dumi" && entry.name !== ".storybook";
29567
29026
  if (entry.isSymbolicLink() && isIgnoredDirectoryName) continue;
29568
29027
  if (entry.isSymbolicLink()) return null;
29569
29028
  if (entry.isDirectory()) {
@@ -29572,12 +29031,12 @@ const listProductionSourceFiles = (rootDirectory) => {
29572
29031
  continue;
29573
29032
  }
29574
29033
  if (!entry.isFile() || isTestlikeFilename(absolutePath)) continue;
29575
- if (SOURCE_PROJECT_MDX_FILE_PATTERN.test(entry.name)) {
29034
+ if (GENERATED_IMAGE_MDX_FILE_PATTERN.test(entry.name)) {
29576
29035
  hasOpaqueMdxConsumerSurface = true;
29577
29036
  continue;
29578
29037
  }
29579
- if (!SOURCE_PROJECT_FILE_PATTERN.test(entry.name)) continue;
29580
- if (SOURCE_PROJECT_DECLARATION_FILE_PATTERN.test(entry.name)) continue;
29038
+ if (!GENERATED_IMAGE_SOURCE_FILE_PATTERN.test(entry.name)) continue;
29039
+ if (GENERATED_IMAGE_DECLARATION_FILE_PATTERN.test(entry.name)) continue;
29581
29040
  sourceFilePaths.push(normalizeFilename(absolutePath));
29582
29041
  }
29583
29042
  }
@@ -29622,7 +29081,7 @@ const indexModuleSources = (module, consumerModulesByFilePath, resolvedSourcePat
29622
29081
  consumerModulesByFilePath.set(normalizedSourcePath, consumerModules);
29623
29082
  });
29624
29083
  };
29625
- const buildSourceProjectIndex = (rootDirectory, currentFilePath, currentProgramNode, currentScopes) => {
29084
+ const buildGeneratedImageProjectIndex = (rootDirectory, currentFilePath, currentProgramNode, currentScopes) => {
29626
29085
  const productionSourceFiles = listProductionSourceFiles(rootDirectory);
29627
29086
  if (!productionSourceFiles) return null;
29628
29087
  const modulesByFilePath = /* @__PURE__ */ new Map();
@@ -29640,7 +29099,7 @@ const buildSourceProjectIndex = (rootDirectory, currentFilePath, currentProgramN
29640
29099
  modulesByFilePath.set(filePath, {
29641
29100
  filePath,
29642
29101
  programNode: parsedProgram,
29643
- scopes: getSourceProjectModuleScopes(parsedProgram)
29102
+ scopes: getGeneratedImageModuleScopes(parsedProgram)
29644
29103
  });
29645
29104
  }
29646
29105
  const consumerModulesByFilePath = /* @__PURE__ */ new Map();
@@ -29656,51 +29115,6 @@ const buildSourceProjectIndex = (rootDirectory, currentFilePath, currentProgramN
29656
29115
  };
29657
29116
  };
29658
29117
  //#endregion
29659
- //#region src/plugin/utils/get-direct-function-binding-identifier.ts
29660
- const getDirectFunctionBindingIdentifier = (functionNode) => {
29661
- if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
29662
- const functionValueRoot = findTransparentExpressionRoot(functionNode);
29663
- const parent = functionValueRoot.parent;
29664
- return isNodeOfType(parent, "VariableDeclarator") && parent.init === functionValueRoot && isNodeOfType(parent.id, "Identifier") ? parent.id : null;
29665
- };
29666
- //#endregion
29667
- //#region src/plugin/utils/get-function-export-names.ts
29668
- const getExportedSpecifierName$1 = (specifier) => {
29669
- const exported = specifier.exported;
29670
- if (isNodeOfType(exported, "Identifier")) return exported.name;
29671
- return isNodeOfType(exported, "Literal") && typeof exported.value === "string" ? exported.value : null;
29672
- };
29673
- const getLocalSpecifierName = (specifier) => {
29674
- const local = specifier.local;
29675
- if (isNodeOfType(local, "Identifier")) return local.name;
29676
- return isNodeOfType(local, "Literal") && typeof local.value === "string" ? local.value : null;
29677
- };
29678
- const getFunctionExportNames = (programNode, functionNode) => {
29679
- const functionValueRoot = findTransparentExpressionRoot(functionNode);
29680
- const bindingName = getDirectFunctionBindingIdentifier(functionNode)?.name ?? null;
29681
- const exportedNames = /* @__PURE__ */ new Set();
29682
- for (const statement of programNode.body) {
29683
- if (isNodeOfType(statement, "ExportDefaultDeclaration")) {
29684
- if (statement.declaration === functionValueRoot || bindingName && isNodeOfType(statement.declaration, "Identifier") && statement.declaration.name === bindingName) exportedNames.add("default");
29685
- continue;
29686
- }
29687
- if (!isNodeOfType(statement, "ExportNamedDeclaration")) continue;
29688
- const declaration = statement.declaration;
29689
- if (declaration === functionValueRoot && bindingName) exportedNames.add(bindingName);
29690
- if (declaration && isNodeOfType(declaration, "VariableDeclaration")) {
29691
- for (const declarator of declaration.declarations) if (declarator.init === functionValueRoot && isNodeOfType(declarator.id, "Identifier")) exportedNames.add(declarator.id.name);
29692
- }
29693
- if (!bindingName || statement.source) continue;
29694
- for (const specifier of statement.specifiers) {
29695
- if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
29696
- if (getLocalSpecifierName(specifier) !== bindingName) continue;
29697
- const exportedName = getExportedSpecifierName$1(specifier);
29698
- if (exportedName) exportedNames.add(exportedName);
29699
- }
29700
- }
29701
- return [...exportedNames];
29702
- };
29703
- //#endregion
29704
29118
  //#region src/plugin/utils/read-nearest-package-manifest.ts
29705
29119
  const cachedPackageDirectoryByFilename = /* @__PURE__ */ new Map();
29706
29120
  const cachedManifestByPackageDirectory = /* @__PURE__ */ new Map();
@@ -29785,6 +29199,37 @@ const getImportSpecifierName = (specifier) => {
29785
29199
  if (isNodeOfType(imported, "Identifier")) return imported.name;
29786
29200
  return isNodeOfType(imported, "Literal") && typeof imported.value === "string" ? imported.value : null;
29787
29201
  };
29202
+ const getDirectFunctionBindingIdentifier = (functionNode) => {
29203
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
29204
+ const functionValueRoot = findTransparentExpressionRoot(functionNode);
29205
+ const parent = functionValueRoot.parent;
29206
+ return isNodeOfType(parent, "VariableDeclarator") && parent.init === functionValueRoot && isNodeOfType(parent.id, "Identifier") ? parent.id : null;
29207
+ };
29208
+ const getExportNamesForFunction = (programNode, functionNode) => {
29209
+ const functionValueRoot = findTransparentExpressionRoot(functionNode);
29210
+ const bindingName = getDirectFunctionBindingIdentifier(functionNode)?.name ?? null;
29211
+ const exportedNames = /* @__PURE__ */ new Set();
29212
+ for (const statement of programNode.body) {
29213
+ if (isNodeOfType(statement, "ExportDefaultDeclaration")) {
29214
+ if (statement.declaration === functionValueRoot || bindingName && isNodeOfType(statement.declaration, "Identifier") && statement.declaration.name === bindingName) exportedNames.add("default");
29215
+ continue;
29216
+ }
29217
+ if (!isNodeOfType(statement, "ExportNamedDeclaration")) continue;
29218
+ const declaration = statement.declaration;
29219
+ if (declaration === functionValueRoot && bindingName) exportedNames.add(bindingName);
29220
+ if (declaration && isNodeOfType(declaration, "VariableDeclaration")) {
29221
+ for (const declarator of declaration.declarations) if (declarator.init === functionValueRoot && isNodeOfType(declarator.id, "Identifier")) exportedNames.add(declarator.id.name);
29222
+ }
29223
+ if (!bindingName || statement.source) continue;
29224
+ for (const specifier of statement.specifiers) {
29225
+ if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
29226
+ if (getImportedSpecifierName(specifier) !== bindingName) continue;
29227
+ const exportedName = getExportedSpecifierName(specifier);
29228
+ if (exportedName) exportedNames.add(exportedName);
29229
+ }
29230
+ }
29231
+ return [...exportedNames];
29232
+ };
29788
29233
  const isTransparentGeneratedImageValueFlow = (expression, target) => {
29789
29234
  let current = findTransparentExpressionRoot(expression);
29790
29235
  while (current !== target) {
@@ -29843,7 +29288,7 @@ const classifyInvokedExpression = (module, expression, state) => {
29843
29288
  }
29844
29289
  const forwardingFunction = getForwardingFunction(expression);
29845
29290
  if (!forwardingFunction) return false;
29846
- const exportedNames = getFunctionExportNames(module.programNode, forwardingFunction);
29291
+ const exportedNames = getExportNamesForFunction(module.programNode, forwardingFunction);
29847
29292
  if (exportedNames.length === 0) return false;
29848
29293
  for (const exportedName of exportedNames) enqueueExport(state, module.filePath, exportedName);
29849
29294
  return true;
@@ -29976,9 +29421,9 @@ const createExportedJsxGeneratedImageOwnershipAnalyzer = (context) => {
29976
29421
  const programNode = findProgramRoot(jsxNode);
29977
29422
  const enclosingFunction = findEnclosingFunction$1(jsxNode);
29978
29423
  if (!programNode || !enclosingFunction) return false;
29979
- const initialExportNames = getFunctionExportNames(programNode, enclosingFunction);
29424
+ const initialExportNames = getExportNamesForFunction(programNode, enclosingFunction);
29980
29425
  if (initialExportNames.length === 0) return false;
29981
- if (projectIndex === void 0) projectIndex = buildSourceProjectIndex(rootDirectory, filename, programNode, context.scopes);
29426
+ if (projectIndex === void 0) projectIndex = buildGeneratedImageProjectIndex(rootDirectory, filename, programNode, context.scopes);
29982
29427
  if (!projectIndex || projectIndex.hasOpaqueMdxConsumerSurface) return false;
29983
29428
  const state = {
29984
29429
  projectIndex,
@@ -30225,20 +29670,17 @@ const catchClauseRethrowsCaught = (handler) => {
30225
29670
  return didRethrow;
30226
29671
  };
30227
29672
  //#endregion
30228
- //#region src/plugin/utils/find-immediately-invoked-call-expression.ts
30229
- const findImmediatelyInvokedCallExpression = (functionNode) => {
29673
+ //#region src/plugin/utils/is-immediately-invoked-function.ts
29674
+ const isImmediatelyInvokedFunction = (functionNode) => {
30230
29675
  let wrappedCallee = functionNode;
30231
29676
  let enclosing = functionNode.parent;
30232
29677
  while (enclosing && stripParenExpression(enclosing) === functionNode) {
30233
29678
  wrappedCallee = enclosing;
30234
29679
  enclosing = enclosing.parent ?? null;
30235
29680
  }
30236
- return enclosing && isNodeOfType(enclosing, "CallExpression") && enclosing.callee === wrappedCallee ? enclosing : null;
29681
+ return Boolean(enclosing && isNodeOfType(enclosing, "CallExpression") && enclosing.callee === wrappedCallee);
30237
29682
  };
30238
29683
  //#endregion
30239
- //#region src/plugin/utils/is-immediately-invoked-function.ts
30240
- const isImmediatelyInvokedFunction = (functionNode) => Boolean(findImmediatelyInvokedCallExpression(functionNode));
30241
- //#endregion
30242
29684
  //#region src/plugin/utils/find-guarding-try-statement.ts
30243
29685
  const findGuardingTryStatement = (node) => {
30244
29686
  let child = node;
@@ -30789,7 +30231,7 @@ const nextjsNoVercelOgImport = defineRule({
30789
30231
  });
30790
30232
  //#endregion
30791
30233
  //#region src/plugin/rules/a11y/no-access-key.ts
30792
- const MESSAGE$40 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
30234
+ const MESSAGE$39 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
30793
30235
  const isUndefinedIdentifier$1 = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
30794
30236
  const noAccessKey = defineRule({
30795
30237
  id: "no-access-key",
@@ -30808,7 +30250,7 @@ const noAccessKey = defineRule({
30808
30250
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
30809
30251
  context.report({
30810
30252
  node: accessKey,
30811
- message: MESSAGE$40
30253
+ message: MESSAGE$39
30812
30254
  });
30813
30255
  return;
30814
30256
  }
@@ -30818,7 +30260,7 @@ const noAccessKey = defineRule({
30818
30260
  if (isUndefinedIdentifier$1(expression)) return;
30819
30261
  context.report({
30820
30262
  node: accessKey,
30821
- message: MESSAGE$40
30263
+ message: MESSAGE$39
30822
30264
  });
30823
30265
  }
30824
30266
  } };
@@ -33161,7 +32603,7 @@ const noAdjustStateOnPropChange = defineRule({
33161
32603
  });
33162
32604
  //#endregion
33163
32605
  //#region src/plugin/rules/a11y/no-aria-hidden-on-focusable.ts
33164
- const MESSAGE$39 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
32606
+ const MESSAGE$38 = "Screen reader users tab to this focusable element but hear nothing because `aria-hidden` skips it, so remove `aria-hidden` or stop it being focusable.";
33165
32607
  const ALWAYS_FOCUSABLE_TAGS = new Set([
33166
32608
  "button",
33167
32609
  "embed",
@@ -33273,7 +32715,7 @@ const noAriaHiddenOnFocusable = defineRule({
33273
32715
  const isImplicitlyFocusable = isNativelyFocusable(tag, node);
33274
32716
  if (isExplicitlyFocusable || isImplicitlyFocusable) context.report({
33275
32717
  node: ariaHidden,
33276
- message: MESSAGE$39
32718
+ message: MESSAGE$38
33277
32719
  });
33278
32720
  } })
33279
32721
  });
@@ -34320,7 +33762,7 @@ const isAllLiteralArrayExpression = (node) => {
34320
33762
  };
34321
33763
  //#endregion
34322
33764
  //#region src/plugin/rules/react-builtins/no-array-index-key.ts
34323
- const MESSAGE$38 = "Your users can see & submit the wrong data when this list reorders.";
33765
+ const MESSAGE$37 = "Your users can see & submit the wrong data when this list reorders.";
34324
33766
  const SECOND_INDEX_METHODS = new Set([
34325
33767
  "every",
34326
33768
  "filter",
@@ -34439,14 +33881,14 @@ const noArrayIndexKey = defineRule({
34439
33881
  if (propName !== "key") continue;
34440
33882
  if (expressionUsesIndex(property.value, indexBinding.name)) context.report({
34441
33883
  node: property,
34442
- message: MESSAGE$38
33884
+ message: MESSAGE$37
34443
33885
  });
34444
33886
  }
34445
33887
  } })
34446
33888
  });
34447
33889
  //#endregion
34448
33890
  //#region src/plugin/rules/state-and-effects/no-async-effect-callback.ts
34449
- const MESSAGE$37 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
33891
+ const MESSAGE$36 = "The `useEffect` callback is `async`, so it returns a Promise instead of a cleanup function. React calls that Promise as cleanup (a no-op) and the effect can race on unmount. Put the async work in an inner function and call it.";
34450
33892
  const noAsyncEffectCallback = defineRule({
34451
33893
  id: "no-async-effect-callback",
34452
33894
  title: "Async effect callback",
@@ -34464,13 +33906,13 @@ const noAsyncEffectCallback = defineRule({
34464
33906
  if (!callback.async) return;
34465
33907
  context.report({
34466
33908
  node: callback,
34467
- message: MESSAGE$37
33909
+ message: MESSAGE$36
34468
33910
  });
34469
33911
  } })
34470
33912
  });
34471
33913
  //#endregion
34472
33914
  //#region src/plugin/rules/a11y/no-autofocus.ts
34473
- const MESSAGE$36 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
33915
+ const MESSAGE$35 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
34474
33916
  const resolveSettings$21 = (settings) => {
34475
33917
  const reactDoctor = settings?.["react-doctor"];
34476
33918
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -34567,7 +34009,7 @@ const noAutofocus = defineRule({
34567
34009
  if (isConditionallyRendered(node)) return;
34568
34010
  context.report({
34569
34011
  node: autoFocusAttribute,
34570
- message: MESSAGE$36
34012
+ message: MESSAGE$35
34571
34013
  });
34572
34014
  } };
34573
34015
  }
@@ -35896,7 +35338,7 @@ const noChainStateUpdates = defineRule({
35896
35338
  });
35897
35339
  //#endregion
35898
35340
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
35899
- const MESSAGE$35 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
35341
+ const MESSAGE$34 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
35900
35342
  const noChildrenProp = defineRule({
35901
35343
  id: "no-children-prop",
35902
35344
  title: "Children passed as a prop",
@@ -35908,7 +35350,7 @@ const noChildrenProp = defineRule({
35908
35350
  if (node.name.name !== "children") return;
35909
35351
  context.report({
35910
35352
  node: node.name,
35911
- message: MESSAGE$35
35353
+ message: MESSAGE$34
35912
35354
  });
35913
35355
  },
35914
35356
  CallExpression(node) {
@@ -35921,7 +35363,7 @@ const noChildrenProp = defineRule({
35921
35363
  const propertyKey = property.key;
35922
35364
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "children" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "children") context.report({
35923
35365
  node: propertyKey,
35924
- message: MESSAGE$35
35366
+ message: MESSAGE$34
35925
35367
  });
35926
35368
  }
35927
35369
  }
@@ -35929,7 +35371,7 @@ const noChildrenProp = defineRule({
35929
35371
  });
35930
35372
  //#endregion
35931
35373
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
35932
- const MESSAGE$34 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
35374
+ const MESSAGE$33 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
35933
35375
  const noCloneElement = defineRule({
35934
35376
  id: "no-clone-element",
35935
35377
  title: "cloneElement makes child props fragile",
@@ -35942,7 +35384,7 @@ const noCloneElement = defineRule({
35942
35384
  if (isNodeOfType(callee, "Identifier") && callee.name === "cloneElement") {
35943
35385
  if (isImportedFromModule(node, "cloneElement", "react")) context.report({
35944
35386
  node: callee,
35945
- message: MESSAGE$34
35387
+ message: MESSAGE$33
35946
35388
  });
35947
35389
  return;
35948
35390
  }
@@ -35955,7 +35397,7 @@ const noCloneElement = defineRule({
35955
35397
  if (!isImportedFromModule(node, callee.object.name, "react")) return;
35956
35398
  context.report({
35957
35399
  node: callee,
35958
- message: MESSAGE$34
35400
+ message: MESSAGE$33
35959
35401
  });
35960
35402
  }
35961
35403
  } })
@@ -35975,7 +35417,7 @@ const getCallMethodName = (callee) => {
35975
35417
  };
35976
35418
  //#endregion
35977
35419
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
35978
- const MESSAGE$33 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
35420
+ const MESSAGE$32 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
35979
35421
  const CONTEXT_MODULES = [
35980
35422
  "react",
35981
35423
  "use-context-selector",
@@ -36023,7 +35465,7 @@ const noCreateContextInRender = defineRule({
36023
35465
  if (!componentOrHookName) return;
36024
35466
  context.report({
36025
35467
  node,
36026
- message: `${MESSAGE$33} (called inside "${componentOrHookName}")`
35468
+ message: `${MESSAGE$32} (called inside "${componentOrHookName}")`
36027
35469
  });
36028
35470
  } })
36029
35471
  });
@@ -36770,7 +36212,7 @@ const isProvenOneShotTestingLibraryComponent = (functionNode, filename, scopes)
36770
36212
  };
36771
36213
  //#endregion
36772
36214
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
36773
- const MESSAGE$32 = "`createRef()` may escape or be observed beyond the render that created it, so a later render can replace the ref object and detach the observed one. Hoist a `useRef()` call to the component's unconditional top level instead.";
36215
+ const MESSAGE$31 = "`createRef()` may escape or be observed beyond the render that created it, so a later render can replace the ref object and detach the observed one. Hoist a `useRef()` call to the component's unconditional top level instead.";
36774
36216
  const isUseMemoCallbackArgument = (functionNode, scopes) => {
36775
36217
  const parent = functionNode.parent;
36776
36218
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
@@ -36824,7 +36266,7 @@ const noCreateRefInFunctionComponent = defineRule({
36824
36266
  if (isCreateRefResultWriteOnly(node, context.filename, context.scopes)) return;
36825
36267
  context.report({
36826
36268
  node,
36827
- message: MESSAGE$32
36269
+ message: MESSAGE$31
36828
36270
  });
36829
36271
  } })
36830
36272
  });
@@ -36964,7 +36406,7 @@ const noCreateStoreInRender = defineRule({
36964
36406
  });
36965
36407
  //#endregion
36966
36408
  //#region src/plugin/rules/react-builtins/no-danger.ts
36967
- const MESSAGE$31 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
36409
+ const MESSAGE$30 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
36968
36410
  const noDanger = defineRule({
36969
36411
  id: "no-danger",
36970
36412
  title: "Raw HTML injection can run unsafe markup",
@@ -36978,7 +36420,7 @@ const noDanger = defineRule({
36978
36420
  if (!propAttribute) return;
36979
36421
  context.report({
36980
36422
  node: propAttribute.name,
36981
- message: MESSAGE$31
36423
+ message: MESSAGE$30
36982
36424
  });
36983
36425
  },
36984
36426
  CallExpression(node) {
@@ -36990,7 +36432,7 @@ const noDanger = defineRule({
36990
36432
  const propertyKey = property.key;
36991
36433
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "dangerouslySetInnerHTML" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "dangerouslySetInnerHTML") context.report({
36992
36434
  node: propertyKey,
36993
- message: MESSAGE$31
36435
+ message: MESSAGE$30
36994
36436
  });
36995
36437
  }
36996
36438
  }
@@ -37013,7 +36455,7 @@ const isMeaningfulJsxChild = (child) => {
37013
36455
  };
37014
36456
  //#endregion
37015
36457
  //#region src/plugin/rules/react-builtins/no-danger-with-children.ts
37016
- const MESSAGE$30 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
36458
+ const MESSAGE$29 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
37017
36459
  const mergePropsShape = (target, source) => {
37018
36460
  target.hasDangerously ||= source.hasDangerously;
37019
36461
  target.hasChildren ||= source.hasChildren;
@@ -37088,7 +36530,7 @@ const noDangerWithChildren = defineRule({
37088
36530
  if (!hasChildrenProp && !hasNestedChildren) return;
37089
36531
  if (hasJsxPropIgnoreCase(opening.attributes, "dangerouslySetInnerHTML") || spreadPropsShape.hasDangerously) context.report({
37090
36532
  node: opening,
37091
- message: MESSAGE$30
36533
+ message: MESSAGE$29
37092
36534
  });
37093
36535
  },
37094
36536
  CallExpression(node) {
@@ -37101,7 +36543,7 @@ const noDangerWithChildren = defineRule({
37101
36543
  const positionalChildren = node.arguments.slice(2);
37102
36544
  if (positionalChildren.length > 1 || positionalChildren.some((argument) => !isNullishExpression(argument)) || propsShape.hasChildren) context.report({
37103
36545
  node,
37104
- message: MESSAGE$30
36546
+ message: MESSAGE$29
37105
36547
  });
37106
36548
  }
37107
36549
  })
@@ -38076,7 +37518,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
38076
37518
  //#endregion
38077
37519
  //#region src/plugin/rules/react-builtins/no-did-mount-set-state.ts
38078
37520
  const LIFECYCLE_NAMES$2 = new Set(["componentDidMount"]);
38079
- const MESSAGE$29 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
37521
+ const MESSAGE$28 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
38080
37522
  const getEnclosingLifecycleFunction = (setStateCall) => {
38081
37523
  let ancestor = setStateCall.parent;
38082
37524
  while (ancestor) {
@@ -38203,7 +37645,7 @@ const noDidMountSetState = defineRule({
38203
37645
  }
38204
37646
  context.report({
38205
37647
  node: node.callee,
38206
- message: MESSAGE$29
37648
+ message: MESSAGE$28
38207
37649
  });
38208
37650
  } };
38209
37651
  }
@@ -38221,7 +37663,7 @@ const findEnclosingClass = (node) => {
38221
37663
  //#endregion
38222
37664
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
38223
37665
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
38224
- const MESSAGE$28 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
37666
+ const MESSAGE$27 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
38225
37667
  const DIFFERENCE_OPERATORS = new Set(["!=", "!=="]);
38226
37668
  const EQUALITY_OPERATORS = new Set([
38227
37669
  "==",
@@ -38615,7 +38057,7 @@ const noDidUpdateSetState = defineRule({
38615
38057
  if (isInsideDiffGuard(node, context.scopes)) return;
38616
38058
  context.report({
38617
38059
  node: node.callee,
38618
- message: MESSAGE$28
38060
+ message: MESSAGE$27
38619
38061
  });
38620
38062
  } };
38621
38063
  }
@@ -38638,7 +38080,7 @@ const isStateMemberExpression = (node) => {
38638
38080
  };
38639
38081
  //#endregion
38640
38082
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
38641
- const MESSAGE$27 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
38083
+ const MESSAGE$26 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
38642
38084
  const shouldIgnoreMutation = (node) => {
38643
38085
  let isConstructor = false;
38644
38086
  let isInsideCallExpression = false;
@@ -38660,7 +38102,7 @@ const reportIfStateMutation = (context, reportNode, target) => {
38660
38102
  if (shouldIgnoreMutation(reportNode)) return;
38661
38103
  context.report({
38662
38104
  node: reportNode,
38663
- message: MESSAGE$27
38105
+ message: MESSAGE$26
38664
38106
  });
38665
38107
  };
38666
38108
  const noDirectMutationState = defineRule({
@@ -39011,7 +38453,7 @@ const noDocumentStartViewTransition = defineRule({
39011
38453
  });
39012
38454
  //#endregion
39013
38455
  //#region src/plugin/rules/js-performance/no-document-write.ts
39014
- const MESSAGE$26 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
38456
+ const MESSAGE$25 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
39015
38457
  const WRITE_METHODS = new Set(["write", "writeln"]);
39016
38458
  const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
39017
38459
  const noDocumentWrite = defineRule({
@@ -39028,7 +38470,7 @@ const noDocumentWrite = defineRule({
39028
38470
  if (methodName === null || !WRITE_METHODS.has(methodName)) return;
39029
38471
  context.report({
39030
38472
  node,
39031
- message: MESSAGE$26
38473
+ message: MESSAGE$25
39032
38474
  });
39033
38475
  } })
39034
38476
  });
@@ -42076,7 +41518,7 @@ const IMPORT_INITIALIZER_TYPES = new Set([
42076
41518
  "ImportNamespaceSpecifier"
42077
41519
  ]);
42078
41520
  const CANCELLATION_FLAG_NAME_PATTERN = /cancel|ignore/i;
42079
- const PROMISE_CONTINUATION_METHOD_NAMES$1 = new Set([
41521
+ const PROMISE_CONTINUATION_METHOD_NAMES = new Set([
42080
41522
  "then",
42081
41523
  "catch",
42082
41524
  "finally"
@@ -42289,10 +41731,18 @@ const isCompletionSinkAfterCancellationEarlyExit = (completionSink, cancellation
42289
41731
  return false;
42290
41732
  };
42291
41733
  const isCompletionSinkGuardedByCancellationFlag = (completionSink, cancellationFlagKey, context) => isCompletionSinkInsideCancellationGuard(completionSink, cancellationFlagKey, context) || isCompletionSinkAfterCancellationEarlyExit(completionSink, cancellationFlagKey, context);
41734
+ const isDescendantOf = (node, ancestor) => {
41735
+ let currentNode = node;
41736
+ while (currentNode) {
41737
+ if (currentNode === ancestor) return true;
41738
+ currentNode = currentNode.parent ?? null;
41739
+ }
41740
+ return false;
41741
+ };
42292
41742
  const isPromiseContinuationForRequest = (functionNode, request) => {
42293
41743
  const callNode = functionNode.parent;
42294
- if (!isNodeOfType(callNode, "CallExpression") || !callNode.arguments?.some((callArgument) => callArgument === functionNode) || !isNodeOfType(callNode.callee, "MemberExpression") || callNode.callee.computed || !isNodeOfType(callNode.callee.property, "Identifier") || !PROMISE_CONTINUATION_METHOD_NAMES$1.has(callNode.callee.property.name)) return false;
42295
- return isAstDescendant(request, callNode.callee.object);
41744
+ if (!isNodeOfType(callNode, "CallExpression") || !callNode.arguments?.some((callArgument) => callArgument === functionNode) || !isNodeOfType(callNode.callee, "MemberExpression") || callNode.callee.computed || !isNodeOfType(callNode.callee.property, "Identifier") || !PROMISE_CONTINUATION_METHOD_NAMES.has(callNode.callee.property.name)) return false;
41745
+ return isDescendantOf(request, callNode.callee.object);
42296
41746
  };
42297
41747
  const isAwaitedInFunction = (request, functionNode) => {
42298
41748
  let currentNode = request.parent;
@@ -42360,7 +41810,7 @@ const ALLOWED_NAMESPACES = new Set([
42360
41810
  "ReactDOM",
42361
41811
  "ReactDom"
42362
41812
  ]);
42363
- const MESSAGE$25 = "`findDOMNode` crashes your app in React 19 because it was removed.";
41813
+ const MESSAGE$24 = "`findDOMNode` crashes your app in React 19 because it was removed.";
42364
41814
  const noFindDomNode = defineRule({
42365
41815
  id: "no-find-dom-node",
42366
41816
  title: "findDOMNode breaks component encapsulation",
@@ -42371,7 +41821,7 @@ const noFindDomNode = defineRule({
42371
41821
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
42372
41822
  if (isImportedFromModule(node, callee.name, "react-dom")) context.report({
42373
41823
  node: callee,
42374
- message: MESSAGE$25
41824
+ message: MESSAGE$24
42375
41825
  });
42376
41826
  return;
42377
41827
  }
@@ -42382,7 +41832,7 @@ const noFindDomNode = defineRule({
42382
41832
  if (callee.property.name !== "findDOMNode") return;
42383
41833
  context.report({
42384
41834
  node: callee.property,
42385
- message: MESSAGE$25
41835
+ message: MESSAGE$24
42386
41836
  });
42387
41837
  }
42388
41838
  } })
@@ -43520,7 +42970,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
43520
42970
  });
43521
42971
  //#endregion
43522
42972
  //#region src/plugin/rules/performance/no-img-lazy-with-high-fetchpriority.ts
43523
- const MESSAGE$24 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
42973
+ const MESSAGE$23 = "`<img loading=\"lazy\">` defers the request while `fetchPriority=\"high\"` asks the browser to rush it, so the two directives contradict each other. Drop one: keep `fetchPriority=\"high\"` (and eager loading) for an LCP image, or `loading=\"lazy\"` for a below-the-fold one.";
43524
42974
  const noImgLazyWithHighFetchpriority = defineRule({
43525
42975
  id: "no-img-lazy-with-high-fetchpriority",
43526
42976
  title: "Lazy image with high fetchPriority",
@@ -43534,7 +42984,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
43534
42984
  if (!fetchPriorityAttribute || getJsxPropStringValue(fetchPriorityAttribute)?.toLowerCase() !== "high") return;
43535
42985
  context.report({
43536
42986
  node: node.name,
43537
- message: MESSAGE$24
42987
+ message: MESSAGE$23
43538
42988
  });
43539
42989
  } })
43540
42990
  });
@@ -43723,7 +43173,7 @@ const noImpureStateUpdater = defineRule({
43723
43173
  });
43724
43174
  //#endregion
43725
43175
  //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
43726
- const MESSAGE$23 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
43176
+ const MESSAGE$22 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
43727
43177
  const REACT_USE_REF_OPTIONS = {
43728
43178
  allowGlobalReactNamespace: false,
43729
43179
  allowUnboundBareCalls: false
@@ -43824,7 +43274,7 @@ const noIndeterminateAttribute = defineRule({
43824
43274
  if (!inputTypeValues || !inputTypeValues.every((inputTypeValue) => inputTypeValue.toLowerCase() === "checkbox")) return;
43825
43275
  context.report({
43826
43276
  node: indeterminateAttribute,
43827
- message: MESSAGE$23
43277
+ message: MESSAGE$22
43828
43278
  });
43829
43279
  },
43830
43280
  CallExpression(node) {
@@ -43833,7 +43283,7 @@ const noIndeterminateAttribute = defineRule({
43833
43283
  if (!isProvenHtmlInputElement(receiver, context.scopes)) return;
43834
43284
  context.report({
43835
43285
  node,
43836
- message: MESSAGE$23
43286
+ message: MESSAGE$22
43837
43287
  });
43838
43288
  }
43839
43289
  };
@@ -44121,7 +43571,7 @@ const noIsMounted = defineRule({
44121
43571
  });
44122
43572
  //#endregion
44123
43573
  //#region src/plugin/rules/js-performance/no-json-parse-stringify-clone.ts
44124
- const MESSAGE$22 = "`JSON.parse(JSON.stringify(x))` deep-clones by re-serializing: it is slow on large objects and silently drops `undefined`, functions, `Date`/`Map`/`Set`, and cyclic references. Use `structuredClone(x)`.";
43574
+ const MESSAGE$21 = "`JSON.parse(JSON.stringify(x))` deep-clones by re-serializing: it is slow on large objects and silently drops `undefined`, functions, `Date`/`Map`/`Set`, and cyclic references. Use `structuredClone(x)`.";
44125
43575
  const isJsonMethodCall = (node, method) => {
44126
43576
  if (!isNodeOfType(node, "CallExpression")) return false;
44127
43577
  const callee = node.callee;
@@ -44185,13 +43635,13 @@ const noJsonParseStringifyClone = defineRule({
44185
43635
  if (isCatchParameterRoundTrip(firstArgument)) return;
44186
43636
  context.report({
44187
43637
  node,
44188
- message: MESSAGE$22
43638
+ message: MESSAGE$21
44189
43639
  });
44190
43640
  } })
44191
43641
  });
44192
43642
  //#endregion
44193
43643
  //#region src/plugin/rules/correctness/no-jsx-element-type.ts
44194
- const MESSAGE$21 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
43644
+ const MESSAGE$20 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
44195
43645
  const isJsxElementTypeReference = (node) => {
44196
43646
  if (!isNodeOfType(node, "TSTypeReference")) return false;
44197
43647
  const typeName = node.typeName;
@@ -44243,7 +43693,7 @@ const noJsxElementType = defineRule({
44243
43693
  if (isJsxImported) return;
44244
43694
  for (const typeAnnotation of flaggedAnnotations) context.report({
44245
43695
  node: typeAnnotation,
44246
- message: MESSAGE$21
43696
+ message: MESSAGE$20
44247
43697
  });
44248
43698
  }
44249
43699
  };
@@ -45524,7 +44974,7 @@ const noMoment = defineRule({
45524
44974
  });
45525
44975
  //#endregion
45526
44976
  //#region src/plugin/rules/react-builtins/no-multi-comp.ts
45527
- const MESSAGE$20 = "This file declares several components, so each component is harder to find, test, and change.";
44977
+ const MESSAGE$19 = "This file declares several components, so each component is harder to find, test, and change.";
45528
44978
  const resolveSettings$16 = (settings) => {
45529
44979
  const reactDoctor = settings?.["react-doctor"];
45530
44980
  return { ignoreStateless: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noMultiComp ?? {} : {}).ignoreStateless ?? false };
@@ -45876,7 +45326,7 @@ const noMultiComp = defineRule({
45876
45326
  if (isSmallFeatureModule || isLargeFeatureModule || isVeryLargeFeatureModule) return;
45877
45327
  for (const component of flagged.slice(1)) context.report({
45878
45328
  node: component.reportNode,
45879
- message: MESSAGE$20
45329
+ message: MESSAGE$19
45880
45330
  });
45881
45331
  } };
45882
45332
  }
@@ -45964,6 +45414,41 @@ const noMutableInDeps = defineRule({
45964
45414
  }
45965
45415
  });
45966
45416
  //#endregion
45417
+ //#region src/plugin/utils/is-result-discarded-call.ts
45418
+ const isResultDiscardedCall = (callExpression) => {
45419
+ let node = callExpression;
45420
+ let parent = node.parent;
45421
+ while (parent) {
45422
+ if (isNodeOfType(parent, "ExpressionStatement")) return true;
45423
+ if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "void") return true;
45424
+ if (isNodeOfType(parent, "ArrowFunctionExpression") && parent.body === node) return true;
45425
+ if (isNodeOfType(parent, "ChainExpression")) {
45426
+ node = parent;
45427
+ parent = node.parent;
45428
+ continue;
45429
+ }
45430
+ if (isNodeOfType(parent, "LogicalExpression") && parent.right === node) {
45431
+ node = parent;
45432
+ parent = node.parent;
45433
+ continue;
45434
+ }
45435
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === node || parent.alternate === node)) {
45436
+ node = parent;
45437
+ parent = node.parent;
45438
+ continue;
45439
+ }
45440
+ if (isNodeOfType(parent, "SequenceExpression")) {
45441
+ const expressions = parent.expressions ?? [];
45442
+ if (expressions[expressions.length - 1] !== node) return true;
45443
+ node = parent;
45444
+ parent = node.parent;
45445
+ continue;
45446
+ }
45447
+ return false;
45448
+ }
45449
+ return false;
45450
+ };
45451
+ //#endregion
45967
45452
  //#region src/plugin/rules/state-and-effects/utils/lodash-mutator-call.ts
45968
45453
  const LODASH_MUTATOR_NAMES = new Set([
45969
45454
  "set",
@@ -46054,7 +45539,7 @@ const resolveReducerFunction = (node, currentFilename) => {
46054
45539
  };
46055
45540
  //#endregion
46056
45541
  //#region src/plugin/rules/state-and-effects/no-mutating-reducer-state.ts
46057
- const MESSAGE$19 = "This reducer changes state in place, so your update is silently skipped.";
45542
+ const MESSAGE$18 = "This reducer changes state in place, so your update is silently skipped.";
46058
45543
  const SAME_REFERENCE_ARRAY_RETURN_METHODS = new Set([
46059
45544
  "copyWithin",
46060
45545
  "fill",
@@ -46258,7 +45743,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
46258
45743
  reportedNodes.add(options.crossFileConsumerCallSite);
46259
45744
  context.report({
46260
45745
  node: options.crossFileConsumerCallSite,
46261
- message: `${MESSAGE$19} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
45746
+ message: `${MESSAGE$18} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
46262
45747
  });
46263
45748
  return;
46264
45749
  }
@@ -46267,7 +45752,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
46267
45752
  reportedNodes.add(mutation.node);
46268
45753
  context.report({
46269
45754
  node: mutation.node,
46270
- message: MESSAGE$19
45755
+ message: MESSAGE$18
46271
45756
  });
46272
45757
  }
46273
45758
  };
@@ -46674,7 +46159,7 @@ const noNoninteractiveElementToInteractiveRole = defineRule({
46674
46159
  });
46675
46160
  //#endregion
46676
46161
  //#region src/plugin/rules/a11y/no-noninteractive-tabindex.ts
46677
- const MESSAGE$18 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
46162
+ const MESSAGE$17 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
46678
46163
  const KEYBOARD_HANDLER_PROP_NAMES = [
46679
46164
  "onKeyDown",
46680
46165
  "onKeyUp",
@@ -46793,7 +46278,7 @@ const noNoninteractiveTabindex = defineRule({
46793
46278
  if (numeric === null) {
46794
46279
  if (isNodeOfType(tabIndexValue, "JSXExpressionContainer") && !settings.allowExpressionValues && !isKeyboardOperable(node) && !isFocusOperable(node) && !hasJsxSpreadAttribute$1(node.attributes)) context.report({
46795
46280
  node: tabIndex,
46796
- message: MESSAGE$18
46281
+ message: MESSAGE$17
46797
46282
  });
46798
46283
  return;
46799
46284
  }
@@ -46815,7 +46300,7 @@ const noNoninteractiveTabindex = defineRule({
46815
46300
  if (!roleAttribute) {
46816
46301
  context.report({
46817
46302
  node: tabIndex,
46818
- message: MESSAGE$18
46303
+ message: MESSAGE$17
46819
46304
  });
46820
46305
  return;
46821
46306
  }
@@ -46829,7 +46314,7 @@ const noNoninteractiveTabindex = defineRule({
46829
46314
  }
46830
46315
  context.report({
46831
46316
  node: tabIndex,
46832
- message: MESSAGE$18
46317
+ message: MESSAGE$17
46833
46318
  });
46834
46319
  } };
46835
46320
  }
@@ -49035,7 +48520,7 @@ const noRandomKey = defineRule({
49035
48520
  });
49036
48521
  //#endregion
49037
48522
  //#region src/plugin/rules/react-builtins/no-react-children.ts
49038
- const MESSAGE$17 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
48523
+ const MESSAGE$16 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
49039
48524
  const isChildrenIdentifier = (node, contextNode) => {
49040
48525
  if (!isNodeOfType(node, "Identifier") || node.name !== "Children") return false;
49041
48526
  return isImportedFromModule(contextNode, "Children", "react");
@@ -49061,13 +48546,13 @@ const noReactChildren = defineRule({
49061
48546
  if (isChildrenIdentifier(memberObject, node)) {
49062
48547
  context.report({
49063
48548
  node: calleeOuter,
49064
- message: MESSAGE$17
48549
+ message: MESSAGE$16
49065
48550
  });
49066
48551
  return;
49067
48552
  }
49068
48553
  if (isReactNamespaceMember(memberObject, node)) context.report({
49069
48554
  node: calleeOuter,
49070
- message: MESSAGE$17
48555
+ message: MESSAGE$16
49071
48556
  });
49072
48557
  } })
49073
48558
  });
@@ -49949,7 +49434,7 @@ const noRenderPropChildren = defineRule({
49949
49434
  });
49950
49435
  //#endregion
49951
49436
  //#region src/plugin/rules/react-builtins/no-render-return-value.ts
49952
- const MESSAGE$16 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
49437
+ const MESSAGE$15 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
49953
49438
  const isReactDomRenderCall = (node) => isGlobalMethodCall(node, "ReactDOM", "render");
49954
49439
  const isUsedAsReturnValue = (parent) => {
49955
49440
  if (!parent) return false;
@@ -49967,7 +49452,7 @@ const noRenderReturnValue = defineRule({
49967
49452
  if (!isUsedAsReturnValue(node.parent)) return;
49968
49453
  context.report({
49969
49454
  node: node.callee,
49970
- message: MESSAGE$16
49455
+ message: MESSAGE$15
49971
49456
  });
49972
49457
  } })
49973
49458
  });
@@ -51394,7 +50879,7 @@ const noSelfUpdatingEffect = defineRule({
51394
50879
  });
51395
50880
  //#endregion
51396
50881
  //#region src/plugin/rules/react-builtins/no-set-state.ts
51397
- const MESSAGE$15 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
50882
+ const MESSAGE$14 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
51398
50883
  const noSetState = defineRule({
51399
50884
  id: "no-set-state",
51400
50885
  title: "Local class state forbidden",
@@ -51409,7 +50894,7 @@ const noSetState = defineRule({
51409
50894
  if (!getParentComponent(node)) return;
51410
50895
  context.report({
51411
50896
  node: node.callee,
51412
- message: MESSAGE$15
50897
+ message: MESSAGE$14
51413
50898
  });
51414
50899
  } })
51415
50900
  });
@@ -51779,7 +51264,7 @@ const isAbstractRole = (openingElement, settings) => {
51779
51264
  };
51780
51265
  //#endregion
51781
51266
  //#region src/plugin/rules/a11y/no-static-element-interactions.ts
51782
- const MESSAGE$14 = "Screen reader users can't tell this click handler is interactive because it has no `role`, so add a `role` or use a button or link.";
51267
+ const MESSAGE$13 = "Screen reader users can't tell this click handler is interactive because it has no `role`, so add a `role` or use a button or link.";
51783
51268
  const DEFAULT_HANDLERS = [
51784
51269
  "onClick",
51785
51270
  "onMouseDown",
@@ -51894,7 +51379,7 @@ const noStaticElementInteractions = defineRule({
51894
51379
  if (!roleAttribute || !roleAttribute.value) {
51895
51380
  context.report({
51896
51381
  node: node.name,
51897
- message: MESSAGE$14
51382
+ message: MESSAGE$13
51898
51383
  });
51899
51384
  return;
51900
51385
  }
@@ -51904,7 +51389,7 @@ const noStaticElementInteractions = defineRule({
51904
51389
  if (isRecognizedRoleString(attributeValue.value)) return;
51905
51390
  context.report({
51906
51391
  node: node.name,
51907
- message: MESSAGE$14
51392
+ message: MESSAGE$13
51908
51393
  });
51909
51394
  return;
51910
51395
  }
@@ -51912,7 +51397,7 @@ const noStaticElementInteractions = defineRule({
51912
51397
  if (!settings.allowExpressionValues) {
51913
51398
  context.report({
51914
51399
  node: node.name,
51915
- message: MESSAGE$14
51400
+ message: MESSAGE$13
51916
51401
  });
51917
51402
  return;
51918
51403
  }
@@ -51920,7 +51405,7 @@ const noStaticElementInteractions = defineRule({
51920
51405
  if (isStaticNullishExpression(expression)) {
51921
51406
  context.report({
51922
51407
  node: node.name,
51923
- message: MESSAGE$14
51408
+ message: MESSAGE$13
51924
51409
  });
51925
51410
  return;
51926
51411
  }
@@ -51930,7 +51415,7 @@ const noStaticElementInteractions = defineRule({
51930
51415
  if (branches.some((branch) => branch.role !== null && isRecognizedRoleString(branch.role))) return;
51931
51416
  context.report({
51932
51417
  node: node.name,
51933
- message: MESSAGE$14
51418
+ message: MESSAGE$13
51934
51419
  });
51935
51420
  return;
51936
51421
  }
@@ -51938,7 +51423,7 @@ const noStaticElementInteractions = defineRule({
51938
51423
  }
51939
51424
  context.report({
51940
51425
  node: node.name,
51941
- message: MESSAGE$14
51426
+ message: MESSAGE$13
51942
51427
  });
51943
51428
  } };
51944
51429
  }
@@ -52044,7 +51529,7 @@ const noStringRefs = defineRule({
52044
51529
  });
52045
51530
  //#endregion
52046
51531
  //#region src/plugin/rules/js-performance/no-sync-xhr.ts
52047
- const MESSAGE$13 = "A synchronous `XMLHttpRequest` (`.open(method, url, false)`) freezes the main thread until the request finishes, blocking all rendering and input. Use `fetch()` or an async XHR (`open(method, url, true)`).";
51532
+ const MESSAGE$12 = "A synchronous `XMLHttpRequest` (`.open(method, url, false)`) freezes the main thread until the request finishes, blocking all rendering and input. Use `fetch()` or an async XHR (`open(method, url, true)`).";
52048
51533
  const isFalseLiteral = (node) => isNodeOfType(node, "Literal") && node.value === false;
52049
51534
  const PUBLIC_ASSET_PATH_PATTERN = /(?:^|\/)public\//i;
52050
51535
  const noSyncXhr = defineRule({
@@ -52065,7 +51550,7 @@ const noSyncXhr = defineRule({
52065
51550
  if (!asyncArgument || !isFalseLiteral(stripParenExpression(asyncArgument))) return;
52066
51551
  context.report({
52067
51552
  node,
52068
- message: MESSAGE$13
51553
+ message: MESSAGE$12
52069
51554
  });
52070
51555
  };
52071
51556
  return {
@@ -52090,7 +51575,7 @@ const noSyncXhr = defineRule({
52090
51575
  });
52091
51576
  //#endregion
52092
51577
  //#region src/plugin/rules/react-builtins/no-this-in-sfc.ts
52093
- const MESSAGE$12 = "This value is `undefined` because function components have no `this`.";
51578
+ const MESSAGE$11 = "This value is `undefined` because function components have no `this`.";
52094
51579
  const isInsideClassMethod = (node, customClassFactoryNames) => {
52095
51580
  let ancestor = node.parent;
52096
51581
  while (ancestor) {
@@ -52178,7 +51663,7 @@ const noThisInSfc = defineRule({
52178
51663
  if (functionHasOwnThisMemberWrite(enclosingFunction)) return;
52179
51664
  context.report({
52180
51665
  node,
52181
- message: MESSAGE$12
51666
+ message: MESSAGE$11
52182
51667
  });
52183
51668
  } };
52184
51669
  }
@@ -54201,7 +53686,7 @@ const isElementTypeJsxAttribute = (node) => {
54201
53686
  const attributeName = node.name.name;
54202
53687
  return ELEMENT_TYPE_PROP_NAMES.has(attributeName.toLowerCase()) || attributeName.endsWith("Component");
54203
53688
  };
54204
- const isReactUseMemoCallback$1 = (call, valueNode, scopes) => call.arguments[0] === valueNode && isReactApiCall(call, "useMemo", scopes, {
53689
+ const isReactUseMemoCallback = (call, valueNode, scopes) => call.arguments[0] === valueNode && isReactApiCall(call, "useMemo", scopes, {
54205
53690
  allowGlobalReactNamespace: true,
54206
53691
  resolveNamedAliases: true
54207
53692
  });
@@ -54225,7 +53710,7 @@ const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @_
54225
53710
  case "ArrowFunctionExpression": return false;
54226
53711
  case "CallExpression":
54227
53712
  if (parent.callee === valueNode) return false;
54228
- if (isReactUseMemoCallback$1(parent, valueNode, scopes)) return false;
53713
+ if (isReactUseMemoCallback(parent, valueNode, scopes)) return false;
54229
53714
  if (isReactCreateElementCall(parent, scopes)) return true;
54230
53715
  valueNode = parent;
54231
53716
  parent = parent.parent;
@@ -54548,7 +54033,7 @@ const noWideLetterSpacing = defineRule({
54548
54033
  //#endregion
54549
54034
  //#region src/plugin/rules/react-builtins/no-will-update-set-state.ts
54550
54035
  const LIFECYCLE_NAMES = new Set(["componentWillUpdate", "UNSAFE_componentWillUpdate"]);
54551
- const MESSAGE$11 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
54036
+ const MESSAGE$10 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
54552
54037
  const resolveSettings$7 = (settings) => {
54553
54038
  const reactDoctor = settings?.["react-doctor"];
54554
54039
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noWillUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -54582,7 +54067,7 @@ const noWillUpdateSetState = defineRule({
54582
54067
  if (!isSetStateCallInLifecycle(node, activeLifecycleNames, { disallowInNestedFunctions: mode === "disallow-in-func" })) return;
54583
54068
  context.report({
54584
54069
  node: node.callee,
54585
- message: MESSAGE$11
54070
+ message: MESSAGE$10
54586
54071
  });
54587
54072
  } };
54588
54073
  }
@@ -56382,7 +55867,7 @@ const preactNoRenderArguments = defineRule({
56382
55867
  });
56383
55868
  //#endregion
56384
55869
  //#region src/plugin/rules/preact/preact-prefer-ondblclick.ts
56385
- const MESSAGE$10 = "Your users get no response from `onDoubleClick` in Preact core, where it never fires, so use `onDblClick` instead, which matches the DOM event name.";
55870
+ const MESSAGE$9 = "Your users get no response from `onDoubleClick` in Preact core, where it never fires, so use `onDblClick` instead, which matches the DOM event name.";
56386
55871
  const preactPreferOndblclick = defineRule({
56387
55872
  id: "preact-prefer-ondblclick",
56388
55873
  title: "onDoubleClick instead of onDblClick",
@@ -56397,7 +55882,7 @@ const preactPreferOndblclick = defineRule({
56397
55882
  if (!onDoubleClickAttribute) return;
56398
55883
  context.report({
56399
55884
  node: onDoubleClickAttribute,
56400
- message: MESSAGE$10
55885
+ message: MESSAGE$9
56401
55886
  });
56402
55887
  } })
56403
55888
  });
@@ -56637,7 +56122,7 @@ const preferExplicitVariants = defineRule({
56637
56122
  });
56638
56123
  //#endregion
56639
56124
  //#region src/plugin/rules/react-builtins/prefer-function-component.ts
56640
- const MESSAGE$9 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
56125
+ const MESSAGE$8 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
56641
56126
  const resolveSettings$4 = (settings) => {
56642
56127
  const reactDoctor = settings?.["react-doctor"];
56643
56128
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.preferFunctionComponent ?? {} : {};
@@ -56676,7 +56161,7 @@ const preferFunctionComponent = defineRule({
56676
56161
  const reportNode = node.id ?? node;
56677
56162
  context.report({
56678
56163
  node: reportNode,
56679
- message: MESSAGE$9
56164
+ message: MESSAGE$8
56680
56165
  });
56681
56166
  };
56682
56167
  return {
@@ -57997,8 +57482,7 @@ const preferUseReducer = defineRule({
57997
57482
  //#endregion
57998
57483
  //#region src/plugin/rules/security-scan/utils/is-public-debug-artifact-path.ts
57999
57484
  const LOCALE_DIRECTORY_PATTERN = /(?:^|\/)(?:locales?|i18n|lang|langs|translations?)\//i;
58000
- const PUBLIC_DEBUG_ARTIFACT_PATH_PATTERN = /(?:^|\/)(?:\.env(?:\.[^/]*)?|(?:debug|crash|trace|stack[-_]?trace|report|dump|phpinfo)(?:[-_.][^/]*)?\.(?:txt|log|json|html?)|[^/]+\.log)$/i;
58001
- const isPublicDebugArtifactPath = (relativePath) => isBrowserArtifactPath(relativePath, GENERATED_BUNDLE_FILE_PATTERN.test(relativePath)) && !LOCALE_DIRECTORY_PATTERN.test(relativePath) && PUBLIC_DEBUG_ARTIFACT_PATH_PATTERN.test(relativePath);
57485
+ const isPublicDebugArtifactPath = (relativePath) => isBrowserArtifactPath(relativePath, GENERATED_BUNDLE_FILE_PATTERN.test(relativePath)) && !LOCALE_DIRECTORY_PATTERN.test(relativePath) && /(?:^|\/)(?:\.env(?:\.[^/]*)?|[^/]*(?:debug|crash|trace|stack|report|dump|phpinfo)[^/]*\.(?:txt|log|json|html?)|[^/]+\.log)$/i.test(relativePath);
58002
57486
  //#endregion
58003
57487
  //#region src/plugin/rules/security-scan/public-debug-artifact.ts
58004
57488
  const publicDebugArtifact = defineRule({
@@ -58877,7 +58361,7 @@ const reactCompilerNoManualMemoization = defineRule({
58877
58361
  });
58878
58362
  //#endregion
58879
58363
  //#region src/plugin/rules/react-builtins/react-in-jsx-scope.ts
58880
- const MESSAGE$8 = "This JSX crashes because `React` isn't in scope.";
58364
+ const MESSAGE$7 = "This JSX crashes because `React` isn't in scope.";
58881
58365
  const reactInJsxScope = defineRule({
58882
58366
  id: "react-in-jsx-scope",
58883
58367
  title: "React not in scope for JSX",
@@ -58889,21 +58373,21 @@ const reactInJsxScope = defineRule({
58889
58373
  if (findVariableInitializer(node, "React")) return;
58890
58374
  context.report({
58891
58375
  node: node.name,
58892
- message: MESSAGE$8
58376
+ message: MESSAGE$7
58893
58377
  });
58894
58378
  },
58895
58379
  JSXFragment(node) {
58896
58380
  if (findVariableInitializer(node, "React")) return;
58897
58381
  context.report({
58898
58382
  node: node.openingFragment,
58899
- message: MESSAGE$8
58383
+ message: MESSAGE$7
58900
58384
  });
58901
58385
  }
58902
58386
  })
58903
58387
  });
58904
58388
  //#endregion
58905
58389
  //#region src/plugin/rules/security/react-markdown-unsanitized-raw-html.ts
58906
- const MESSAGE$7 = "React Markdown parses dynamic raw HTML when `rehype-raw` is enabled. Add `rehype-sanitize` to `rehypePlugins` or sanitize the markdown before rendering it.";
58390
+ const MESSAGE$6 = "React Markdown parses dynamic raw HTML when `rehype-raw` is enabled. Add `rehype-sanitize` to `rehypePlugins` or sanitize the markdown before rendering it.";
58907
58391
  const REACT_MARKDOWN_MODULE = "react-markdown";
58908
58392
  const REHYPE_RAW_MODULE = "rehype-raw";
58909
58393
  const REHYPE_SANITIZE_MODULE = "rehype-sanitize";
@@ -59055,7 +58539,7 @@ const reactMarkdownUnsanitizedRawHtml = defineRule({
59055
58539
  if (pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_SANITIZE_MODULE, context.scopes, /* @__PURE__ */ new Set())) || !hasDynamicUnsanitizedChildren(node, context.scopes)) return;
59056
58540
  context.report({
59057
58541
  node,
59058
- message: MESSAGE$7
58542
+ message: MESSAGE$6
59059
58543
  });
59060
58544
  } }))
59061
58545
  });
@@ -59111,7 +58595,7 @@ const inlineUseSelectorFunction = (callNode, aliases) => {
59111
58595
  };
59112
58596
  //#endregion
59113
58597
  //#region src/plugin/rules/state-and-effects/redux-useselector-inline-derivation.ts
59114
- const ALLOCATING_ARRAY_METHODS$1 = new Set([
58598
+ const ALLOCATING_ARRAY_METHODS = new Set([
59115
58599
  "filter",
59116
58600
  "map",
59117
58601
  "flatMap",
@@ -59147,7 +58631,7 @@ const getAllocatingCallSiteDescription = (expression) => {
59147
58631
  method: methodName
59148
58632
  };
59149
58633
  }
59150
- if (ALLOCATING_ARRAY_METHODS$1.has(methodName)) return {
58634
+ if (ALLOCATING_ARRAY_METHODS.has(methodName)) return {
59151
58635
  kind: "method",
59152
58636
  method: methodName
59153
58637
  };
@@ -59210,7 +58694,7 @@ const reduxUseselectorInlineDerivation = defineRule({
59210
58694
  });
59211
58695
  //#endregion
59212
58696
  //#region src/plugin/rules/state-and-effects/redux-useselector-returns-new-collection.ts
59213
- const MESSAGE$6 = "Your component re-renders on every dispatched action when useSelector returns a new object or array.";
58697
+ const MESSAGE$5 = "Your component re-renders on every dispatched action when useSelector returns a new object or array.";
59214
58698
  const isConciseBodyReturningCollection = (functionNode) => {
59215
58699
  if (!isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode, "FunctionExpression")) return false;
59216
58700
  const rawBody = functionNode.body;
@@ -59245,567 +58729,13 @@ const reduxUseselectorReturnsNewCollection = defineRule({
59245
58729
  if (!isConciseBodyReturningCollection(selectorArgument)) return;
59246
58730
  context.report({
59247
58731
  node,
59248
- message: MESSAGE$6
58732
+ message: MESSAGE$5
59249
58733
  });
59250
58734
  }
59251
58735
  };
59252
58736
  }
59253
58737
  });
59254
58738
  //#endregion
59255
- //#region src/plugin/utils/is-remotion-module-source.ts
59256
- const isRemotionModuleSource = (moduleSource) => moduleSource === "remotion" || moduleSource.startsWith("@remotion/");
59257
- //#endregion
59258
- //#region src/plugin/utils/resolve-remotion-api.ts
59259
- const resolveRemotionApi = (referenceNode, scopes) => {
59260
- const candidate = stripParenExpression(referenceNode);
59261
- if (isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "JSXIdentifier")) {
59262
- const symbol = scopes.symbolFor(candidate);
59263
- if (symbol?.kind !== "import") return null;
59264
- const importBinding = getImportBindingForName(candidate, symbol.name);
59265
- if (!importBinding || importBinding.isNamespace || importBinding.exportedName === null || !isRemotionModuleSource(importBinding.source)) return null;
59266
- return {
59267
- apiName: importBinding.exportedName,
59268
- moduleSource: importBinding.source
59269
- };
59270
- }
59271
- if (isNodeOfType(candidate, "MemberExpression")) {
59272
- const apiName = getStaticPropertyKeyName(candidate, { allowComputedString: true });
59273
- const namespaceObject = stripParenExpression(candidate.object);
59274
- if (!apiName || !isNodeOfType(namespaceObject, "Identifier")) return null;
59275
- const symbol = scopes.symbolFor(namespaceObject);
59276
- if (symbol?.kind !== "import") return null;
59277
- const importBinding = getImportBindingForName(namespaceObject, symbol.name);
59278
- if (!importBinding?.isNamespace || !isRemotionModuleSource(importBinding.source)) return null;
59279
- return {
59280
- apiName,
59281
- moduleSource: importBinding.source
59282
- };
59283
- }
59284
- if (!isNodeOfType(candidate, "JSXMemberExpression") || !isNodeOfType(candidate.object, "JSXIdentifier") || !isNodeOfType(candidate.property, "JSXIdentifier")) return null;
59285
- const symbol = scopes.symbolFor(candidate.object);
59286
- if (symbol?.kind !== "import") return null;
59287
- const importBinding = getImportBindingForName(candidate.object, symbol.name);
59288
- if (!importBinding?.isNamespace || !isRemotionModuleSource(importBinding.source)) return null;
59289
- return {
59290
- apiName: candidate.property.name,
59291
- moduleSource: importBinding.source
59292
- };
59293
- };
59294
- //#endregion
59295
- //#region src/plugin/utils/create-remotion-composition-ownership-analyzer.ts
59296
- const compositionAttributeFunctionCacheBySettings = /* @__PURE__ */ new WeakMap();
59297
- const getFunctionExportKeys = (filePath, programNode, functionNode) => getFunctionExportNames(programNode, functionNode).map((exportedName) => `${normalizeFilename(filePath)}\0${exportedName}`);
59298
- const resolveImportedCompositionFunction = (expression, module) => {
59299
- const unwrappedExpression = stripParenExpression(expression);
59300
- let importReference;
59301
- let exportedName;
59302
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
59303
- const symbol = module.scopes.symbolFor(unwrappedExpression);
59304
- if (symbol?.kind !== "import") return null;
59305
- importReference = unwrappedExpression;
59306
- exportedName = getImportBindingForName(unwrappedExpression, symbol.name)?.exportedName ?? null;
59307
- } else if (isNodeOfType(unwrappedExpression, "MemberExpression") && isNodeOfType(stripParenExpression(unwrappedExpression.object), "Identifier")) {
59308
- const namespaceObject = stripParenExpression(unwrappedExpression.object);
59309
- if (!isNodeOfType(namespaceObject, "Identifier")) return null;
59310
- const symbol = module.scopes.symbolFor(namespaceObject);
59311
- if (symbol?.kind !== "import") return null;
59312
- if (!getImportBindingForName(namespaceObject, symbol.name)?.isNamespace) return null;
59313
- importReference = namespaceObject;
59314
- exportedName = getStaticPropertyKeyName(unwrappedExpression, { allowComputedString: true });
59315
- } else return null;
59316
- if (!exportedName) return null;
59317
- const importBinding = getImportBindingForName(importReference, importReference.name);
59318
- if (!importBinding) return null;
59319
- return resolveCrossFileFunctionExportWithFilePath(module.filePath, importBinding.source, exportedName);
59320
- };
59321
- const collectCompositionAttributeFunctionKeys = (context, currentProgram, attributeName) => {
59322
- const filename = context.filename ? normalizeFilename(context.filename) : "";
59323
- const rootDirectorySetting = getReactDoctorStringSetting(context.settings, "rootDirectory");
59324
- const rootDirectory = rootDirectorySetting ? normalizeFilename(rootDirectorySetting).replace(/\/$/, "") : "";
59325
- if (!filename || !rootDirectory || filename !== rootDirectory && !filename.startsWith(`${rootDirectory}/`)) return null;
59326
- const projectIndex = buildSourceProjectIndex(rootDirectory, filename, currentProgram, context.scopes);
59327
- if (!projectIndex) return null;
59328
- const functionKeys = /* @__PURE__ */ new Set();
59329
- for (const module of projectIndex.modulesByFilePath.values()) walkAst(module.programNode, (candidate) => {
59330
- if (!isNodeOfType(candidate, "JSXOpeningElement")) return;
59331
- const apiBinding = resolveRemotionApi(candidate.name, module.scopes);
59332
- if (apiBinding?.apiName !== "Composition" || apiBinding.moduleSource !== "remotion") return;
59333
- const functionAttribute = findJsxAttribute(candidate.attributes, attributeName);
59334
- if (!functionAttribute?.value || !isNodeOfType(functionAttribute.value, "JSXExpressionContainer") || !functionAttribute.value.expression) return;
59335
- const resolvedFunction = resolveImportedCompositionFunction(functionAttribute.value.expression, module);
59336
- if (!resolvedFunction || !isNodeOfType(resolvedFunction.programNode, "Program")) return;
59337
- for (const functionKey of getFunctionExportKeys(resolvedFunction.filePath, resolvedFunction.programNode, resolvedFunction.functionNode)) functionKeys.add(functionKey);
59338
- });
59339
- return functionKeys;
59340
- };
59341
- const createRemotionCompositionAttributeOwnershipAnalyzer = (context, attributeName) => {
59342
- const settings = context.settings;
59343
- return (functionNode) => {
59344
- const currentProgram = findProgramRoot(functionNode);
59345
- if (!currentProgram || !isNodeOfType(currentProgram, "Program") || !context.filename || !settings) return false;
59346
- const currentFunctionKeys = getFunctionExportKeys(context.filename, currentProgram, functionNode);
59347
- if (currentFunctionKeys.length === 0) return false;
59348
- let cacheByAttributeName = compositionAttributeFunctionCacheBySettings.get(settings);
59349
- if (!cacheByAttributeName) {
59350
- cacheByAttributeName = /* @__PURE__ */ new Map();
59351
- compositionAttributeFunctionCacheBySettings.set(settings, cacheByAttributeName);
59352
- }
59353
- let cache = cacheByAttributeName.get(attributeName);
59354
- if (!cache) {
59355
- cache = { failedFilenames: /* @__PURE__ */ new Set() };
59356
- cacheByAttributeName.set(attributeName, cache);
59357
- }
59358
- let functionKeys = cache.functionKeys;
59359
- if (!functionKeys) {
59360
- const filename = normalizeFilename(context.filename);
59361
- if (cache.failedFilenames.has(filename)) return false;
59362
- const collectedFunctionKeys = collectCompositionAttributeFunctionKeys(context, currentProgram, attributeName);
59363
- if (!collectedFunctionKeys) {
59364
- cache.failedFilenames.add(filename);
59365
- return false;
59366
- }
59367
- functionKeys = collectedFunctionKeys;
59368
- cache.functionKeys = collectedFunctionKeys;
59369
- }
59370
- return currentFunctionKeys.some((functionKey) => functionKeys.has(functionKey));
59371
- };
59372
- };
59373
- const createRemotionCompositionOwnershipAnalyzer = (context) => createRemotionCompositionAttributeOwnershipAnalyzer(context, "component");
59374
- const createRemotionMetadataOwnershipAnalyzer = (context) => createRemotionCompositionAttributeOwnershipAnalyzer(context, "calculateMetadata");
59375
- //#endregion
59376
- //#region src/plugin/rules/correctness/remotion-calculate-metadata-fetch-signal.ts
59377
- const getParameterIdentifier = (parameter) => {
59378
- if (isNodeOfType(parameter, "Identifier")) return parameter;
59379
- if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
59380
- return null;
59381
- };
59382
- const getAbortSignalBinding = (functionNode) => {
59383
- if (!isFunctionLike$1(functionNode)) return {
59384
- parameterIdentifier: null,
59385
- signalIdentifier: null
59386
- };
59387
- const firstParameter = functionNode.params[0];
59388
- const parameterIdentifier = getParameterIdentifier(firstParameter);
59389
- if (parameterIdentifier) return {
59390
- parameterIdentifier,
59391
- signalIdentifier: null
59392
- };
59393
- if (!isNodeOfType(firstParameter, "ObjectPattern")) return {
59394
- parameterIdentifier: null,
59395
- signalIdentifier: null
59396
- };
59397
- for (const property of firstParameter.properties) {
59398
- if (!isNodeOfType(property, "Property")) continue;
59399
- if (getStaticPropertyKeyName(property) !== "abortSignal") continue;
59400
- const signalIdentifier = getParameterIdentifier(property.value);
59401
- if (signalIdentifier) return {
59402
- parameterIdentifier: null,
59403
- signalIdentifier
59404
- };
59405
- }
59406
- return {
59407
- parameterIdentifier: null,
59408
- signalIdentifier: null
59409
- };
59410
- };
59411
- const identifiersResolveToSameSymbol = (leftIdentifier, rightIdentifier, scopes) => {
59412
- if (!isNodeOfType(leftIdentifier, "Identifier") || !isNodeOfType(rightIdentifier, "Identifier")) return false;
59413
- const leftSymbol = scopes.symbolFor(leftIdentifier);
59414
- return Boolean(leftSymbol && leftSymbol === scopes.symbolFor(rightIdentifier));
59415
- };
59416
- const isMetadataAbortSignal = (expression, binding, scopes) => {
59417
- const candidate = stripParenExpression(expression);
59418
- if (binding.signalIdentifier && identifiersResolveToSameSymbol(candidate, binding.signalIdentifier, scopes)) return true;
59419
- return Boolean(binding.parameterIdentifier && isNodeOfType(candidate, "MemberExpression") && getStaticPropertyKeyName(candidate, { allowComputedString: true }) === "abortSignal" && identifiersResolveToSameSymbol(candidate.object, binding.parameterIdentifier, scopes));
59420
- };
59421
- const fetchUsesMetadataAbortSignal = (fetchCall, binding, scopes) => {
59422
- const optionsArgument = fetchCall.arguments[1];
59423
- if (!optionsArgument) return false;
59424
- const options = stripParenExpression(optionsArgument);
59425
- if (!isNodeOfType(options, "ObjectExpression")) return null;
59426
- if (options.properties.some((property) => isNodeOfType(property, "SpreadElement"))) return null;
59427
- for (let propertyIndex = options.properties.length - 1; propertyIndex >= 0; propertyIndex -= 1) {
59428
- const property = options.properties[propertyIndex];
59429
- if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "signal") return isMetadataAbortSignal(property.value, binding, scopes);
59430
- }
59431
- return false;
59432
- };
59433
- const reportFetchesWithoutAbortSignal = (functionNode, context) => {
59434
- const binding = getAbortSignalBinding(functionNode);
59435
- walkAst(functionNode, (candidate) => {
59436
- if (candidate !== functionNode && isFunctionLike$1(candidate)) return false;
59437
- if (!isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "Identifier") || candidate.callee.name !== "fetch" || !context.scopes.isGlobalReference(candidate.callee) || fetchUsesMetadataAbortSignal(candidate, binding, context.scopes) !== false) return;
59438
- context.report({
59439
- node: candidate,
59440
- message: "Pass Remotion's `abortSignal` to this fetch with `{signal: abortSignal}` so superseded metadata requests are cancelled."
59441
- });
59442
- });
59443
- };
59444
- const isCalculateMetadataFunctionType = (variableDeclarator) => {
59445
- if (!isNodeOfType(variableDeclarator.id, "Identifier")) return false;
59446
- const annotation = variableDeclarator.id.typeAnnotation;
59447
- if (!isNodeOfType(annotation, "TSTypeAnnotation") || !isNodeOfType(annotation.typeAnnotation, "TSTypeReference")) return false;
59448
- const typeName = annotation.typeAnnotation.typeName;
59449
- if (!isNodeOfType(typeName, "Identifier")) return false;
59450
- const apiBinding = getImportBindingForName(typeName, typeName.name);
59451
- return Boolean(apiBinding?.exportedName === "CalculateMetadataFunction" && apiBinding.source === "remotion");
59452
- };
59453
- const remotionCalculateMetadataFetchSignal = defineRule({
59454
- id: "remotion-calculate-metadata-fetch-signal",
59455
- title: "calculateMetadata fetch ignores abortSignal",
59456
- tags: ["react-jsx-only"],
59457
- requires: ["remotion:4"],
59458
- severity: "error",
59459
- recommendation: "Destructure `abortSignal` from the calculateMetadata argument and pass it to direct fetch calls as `{signal: abortSignal}`.",
59460
- create: (context) => {
59461
- const analyzedFunctions = /* @__PURE__ */ new WeakSet();
59462
- const isOwnedByCalculateMetadata = createRemotionMetadataOwnershipAnalyzer(context);
59463
- const analyzeFunction = (functionNode) => {
59464
- if (!functionNode || analyzedFunctions.has(functionNode)) return;
59465
- analyzedFunctions.add(functionNode);
59466
- reportFetchesWithoutAbortSignal(functionNode, context);
59467
- };
59468
- return {
59469
- CallExpression(node) {
59470
- if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== "fetch" || !context.scopes.isGlobalReference(node.callee)) return;
59471
- const functionNode = findEnclosingFunction$1(node);
59472
- if (functionNode && isOwnedByCalculateMetadata(functionNode)) analyzeFunction(functionNode);
59473
- },
59474
- JSXOpeningElement(node) {
59475
- const apiBinding = resolveRemotionApi(node.name, context.scopes);
59476
- if (apiBinding?.apiName !== "Composition" || apiBinding.moduleSource !== "remotion") return;
59477
- const calculateMetadataAttribute = findJsxAttribute(node.attributes, "calculateMetadata");
59478
- if (!calculateMetadataAttribute?.value || !isNodeOfType(calculateMetadataAttribute.value, "JSXExpressionContainer") || !calculateMetadataAttribute.value.expression) return;
59479
- analyzeFunction(resolveExactLocalFunction(calculateMetadataAttribute.value.expression, context.scopes));
59480
- },
59481
- VariableDeclarator(node) {
59482
- if (!node.init || !isCalculateMetadataFunctionType(node)) return;
59483
- analyzeFunction(resolveExactLocalFunction(node.init, context.scopes));
59484
- }
59485
- };
59486
- }
59487
- });
59488
- //#endregion
59489
- //#region src/plugin/utils/create-remotion-render-evidence-checker.ts
59490
- const REMOTION_RENDER_CALL_NAMES = new Set([
59491
- "continueRender",
59492
- "delayRender",
59493
- "getInputProps",
59494
- "random",
59495
- "spring",
59496
- "useCurrentFrame",
59497
- "useDelayRender",
59498
- "useVideoConfig"
59499
- ]);
59500
- const REMOTION_RENDER_COMPONENT_MODULE_BY_NAME = new Map([
59501
- ["Audio", "@remotion/media"],
59502
- ["Freeze", "remotion"],
59503
- ["IFrame", "remotion"],
59504
- ["Img", "remotion"],
59505
- ["Loop", "remotion"],
59506
- ["OffthreadVideo", "remotion"],
59507
- ["Sequence", "remotion"],
59508
- ["Series", "remotion"],
59509
- ["Video", "@remotion/media"]
59510
- ]);
59511
- const createRemotionRenderEvidenceChecker = (context) => {
59512
- const scopes = context.scopes;
59513
- const evidenceByFunction = /* @__PURE__ */ new WeakMap();
59514
- const registeredCompositionFunctions = /* @__PURE__ */ new WeakSet();
59515
- const inspectedPrograms = /* @__PURE__ */ new WeakSet();
59516
- const isOwnedByRegisteredComposition = createRemotionCompositionOwnershipAnalyzer(context);
59517
- const collectRegisteredCompositionFunctions = (functionNode) => {
59518
- const program = findProgramRoot(functionNode);
59519
- if (!program || inspectedPrograms.has(program)) return;
59520
- inspectedPrograms.add(program);
59521
- walkAst(program, (candidate) => {
59522
- if (!isNodeOfType(candidate, "JSXOpeningElement")) return;
59523
- const apiBinding = resolveRemotionApi(candidate.name, scopes);
59524
- if (apiBinding?.apiName !== "Composition" || apiBinding.moduleSource !== "remotion") return;
59525
- const componentAttribute = findJsxAttribute(candidate.attributes, "component");
59526
- if (!componentAttribute?.value || !isNodeOfType(componentAttribute.value, "JSXExpressionContainer") || !componentAttribute.value.expression) return;
59527
- const registeredFunction = resolveExactLocalFunction(componentAttribute.value.expression, scopes);
59528
- if (registeredFunction) registeredCompositionFunctions.add(registeredFunction);
59529
- });
59530
- };
59531
- const functionUsesRemotionRenderApi = (functionNode) => {
59532
- let hasEvidence = false;
59533
- walkAst(functionNode, (candidate) => {
59534
- if (hasEvidence) return false;
59535
- if (!isNodeOfType(candidate, "CallExpression") && !isNodeOfType(candidate, "JSXOpeningElement")) return;
59536
- const apiBinding = resolveRemotionApi(isNodeOfType(candidate, "CallExpression") ? candidate.callee : candidate.name, scopes);
59537
- if (isNodeOfType(candidate, "CallExpression") && apiBinding?.moduleSource === "remotion" && REMOTION_RENDER_CALL_NAMES.has(apiBinding.apiName) || isNodeOfType(candidate, "JSXOpeningElement") && apiBinding !== null && REMOTION_RENDER_COMPONENT_MODULE_BY_NAME.get(apiBinding.apiName) === apiBinding.moduleSource) {
59538
- hasEvidence = true;
59539
- return false;
59540
- }
59541
- });
59542
- return hasEvidence;
59543
- };
59544
- return { functionHasEvidence: (functionNode) => {
59545
- const cachedEvidence = evidenceByFunction.get(functionNode);
59546
- if (cachedEvidence !== void 0) return cachedEvidence;
59547
- collectRegisteredCompositionFunctions(functionNode);
59548
- const hasEvidence = registeredCompositionFunctions.has(functionNode) || functionUsesRemotionRenderApi(functionNode) || isOwnedByRegisteredComposition(functionNode);
59549
- evidenceByFunction.set(functionNode, hasEvidence);
59550
- return hasEvidence;
59551
- } };
59552
- };
59553
- //#endregion
59554
- //#region src/plugin/rules/correctness/remotion-deterministic-randomness.ts
59555
- const isGlobalMathObject = (node, scopes) => {
59556
- const candidate = stripParenExpression(node);
59557
- if (isNodeOfType(candidate, "Identifier")) return candidate.name === "Math" && scopes.isGlobalReference(candidate);
59558
- return Boolean(isNodeOfType(candidate, "MemberExpression") && getStaticPropertyKeyName(candidate, { allowComputedString: true }) === "Math" && isNodeOfType(candidate.object, "Identifier") && candidate.object.name === "globalThis" && scopes.isGlobalReference(candidate.object));
59559
- };
59560
- const remotionDeterministicRandomness = defineRule({
59561
- id: "remotion-deterministic-randomness",
59562
- title: "Randomness changes between rendered frames",
59563
- tags: ["react-jsx-only"],
59564
- requires: ["remotion:4"],
59565
- severity: "error",
59566
- recommendation: "Use Remotion's seeded `random(seed)` helper so the same frame produces the same value in every render tab.",
59567
- create: (context) => {
59568
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59569
- return { CallExpression(node) {
59570
- const callee = stripParenExpression(node.callee);
59571
- if (!isNodeOfType(callee, "MemberExpression") || getStaticPropertyKeyName(callee, { allowComputedString: true }) !== "random" || !isGlobalMathObject(callee.object, context.scopes)) return;
59572
- const componentOrHook = findRenderPhaseComponentOrHook(node, context.scopes) ?? findEnclosingFunction$1(node);
59573
- if (!componentOrHook || !renderEvidence.functionHasEvidence(componentOrHook)) return;
59574
- const displayName = componentOrHookDisplayNameForFunction(componentOrHook);
59575
- if (displayName && !isReactHookName(displayName) && !functionHasReactComponentEvidence(componentOrHook, context.scopes, context.cfg)) return;
59576
- context.report({
59577
- node,
59578
- message: "`Math.random()` can return a different value in each parallel Remotion render tab, so the same frame is not deterministic. Use `random(seed)` from `remotion` instead."
59579
- });
59580
- } };
59581
- }
59582
- });
59583
- //#endregion
59584
- //#region src/plugin/utils/create-remotion-css-time-rule-visitors.ts
59585
- const createRemotionCssTimeRuleVisitors = (context, options) => {
59586
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59587
- return {
59588
- Property(node) {
59589
- if (!options.stylePropertyNames.has(getStaticPropertyKeyName(node) ?? "")) return;
59590
- const styleObject = node.parent;
59591
- const expressionContainer = styleObject?.parent;
59592
- const styleAttribute = expressionContainer?.parent;
59593
- if (!isNodeOfType(styleObject, "ObjectExpression") || !isNodeOfType(expressionContainer, "JSXExpressionContainer") || expressionContainer.expression !== styleObject || !isNodeOfType(styleAttribute, "JSXAttribute") || !isNodeOfType(styleAttribute.name, "JSXIdentifier") || styleAttribute.name.name !== "style") return;
59594
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
59595
- if (!renderFunction || !renderEvidence.functionHasEvidence(renderFunction)) return;
59596
- context.report({
59597
- node,
59598
- message: options.styleMessage
59599
- });
59600
- },
59601
- JSXAttribute(node) {
59602
- if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "className") return;
59603
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
59604
- if (!renderFunction || !renderEvidence.functionHasEvidence(renderFunction)) return;
59605
- const className = getJsxAttributeStaticString(node);
59606
- if (!className) return;
59607
- if (className.split(/\s+/).filter(Boolean).some((classToken) => options.classTokenIsForbidden(classToken.split(":").at(-1) ?? ""))) context.report({
59608
- node,
59609
- message: options.classMessage
59610
- });
59611
- }
59612
- };
59613
- };
59614
- //#endregion
59615
- //#region src/plugin/rules/correctness/remotion-no-css-animation.ts
59616
- const ANIMATION_STYLE_PROPERTY_NAMES = new Set(["animation", "animationName"]);
59617
- const remotionNoCssAnimation = defineRule({
59618
- id: "remotion-no-css-animation",
59619
- title: "CSS animation is not frame-driven",
59620
- tags: ["react-jsx-only"],
59621
- requires: ["remotion:4"],
59622
- severity: "error",
59623
- recommendation: "Drive the property from `useCurrentFrame()` with `interpolate()` so every rendered frame is deterministic.",
59624
- create: (context) => createRemotionCssTimeRuleVisitors(context, {
59625
- classTokenIsForbidden: (classToken) => classToken.startsWith("animate-") && classToken !== "animate-none",
59626
- classMessage: "Tailwind animations advance on browser time, so Remotion can capture inconsistent frames. Drive the property from `useCurrentFrame()` instead.",
59627
- styleMessage: "CSS animations advance on browser time, so Remotion can capture inconsistent frames. Drive the property from `useCurrentFrame()` instead.",
59628
- stylePropertyNames: ANIMATION_STYLE_PROPERTY_NAMES
59629
- })
59630
- });
59631
- //#endregion
59632
- //#region src/plugin/rules/correctness/remotion-no-css-transition.ts
59633
- const TRANSITION_STYLE_PROPERTY_NAMES = new Set(["transition", "transitionProperty"]);
59634
- const remotionNoCssTransition = defineRule({
59635
- id: "remotion-no-css-transition",
59636
- title: "CSS transition is not frame-driven",
59637
- tags: ["react-jsx-only"],
59638
- requires: ["remotion:4"],
59639
- severity: "error",
59640
- recommendation: "Drive the property from `useCurrentFrame()` with `interpolate()` so every rendered frame is deterministic.",
59641
- create: (context) => createRemotionCssTimeRuleVisitors(context, {
59642
- classTokenIsForbidden: (classToken) => (classToken === "transition" || classToken.startsWith("transition-")) && classToken !== "transition-none",
59643
- classMessage: "Tailwind transitions advance on browser time, so Remotion can capture inconsistent frames. Drive the property from `useCurrentFrame()` instead.",
59644
- styleMessage: "CSS transitions advance on browser time, so Remotion can capture inconsistent frames. Drive the property from `useCurrentFrame()` instead.",
59645
- stylePropertyNames: TRANSITION_STYLE_PROPERTY_NAMES
59646
- })
59647
- });
59648
- //#endregion
59649
- //#region src/plugin/rules/correctness/remotion-no-css-url-assets.ts
59650
- const CSS_URL_ASSET_PROPERTY_NAMES = new Set([
59651
- "backgroundImage",
59652
- "maskImage",
59653
- "WebkitMaskImage"
59654
- ]);
59655
- const CSS_URL_PATTERN = /\burl\(\s*(["']?)([^"')]+)\1\s*\)/i;
59656
- const isEmbeddedAssetSource = (assetSource) => assetSource.startsWith("data:") || assetSource.startsWith("#");
59657
- const getStaticStringExpression = (node) => {
59658
- if (isNodeOfType(node, "Literal") && typeof node.value === "string") return node.value;
59659
- if (isNodeOfType(node, "TemplateLiteral") && node.expressions.length === 0 && node.quasis.length === 1) return node.quasis[0].value.raw;
59660
- return null;
59661
- };
59662
- const isReactUseMemoCallback = (node, scopes) => {
59663
- const parent = node.parent;
59664
- return Boolean(isNodeOfType(parent, "CallExpression") && parent.arguments[0] === node && isReactApiCall(parent, "useMemo", scopes, { resolveNamedAliases: true }));
59665
- };
59666
- const componentPreloadsStaticImage = (componentNode, assetSource, scopes) => {
59667
- let hasPreload = false;
59668
- walkAst(componentNode, (child) => {
59669
- if (hasPreload) return false;
59670
- if (child !== componentNode && isFunctionLike$1(child) && !isReactUseMemoCallback(child, scopes)) return false;
59671
- if (!isNodeOfType(child, "JSXOpeningElement")) return;
59672
- const apiBinding = resolveRemotionApi(child.name, scopes);
59673
- if (apiBinding?.apiName !== "Img" || apiBinding.moduleSource !== "remotion") return;
59674
- const sourceAttribute = findJsxAttribute(child.attributes, "src");
59675
- if (sourceAttribute && getJsxAttributeStaticString(sourceAttribute) === assetSource) {
59676
- hasPreload = true;
59677
- return false;
59678
- }
59679
- });
59680
- return hasPreload;
59681
- };
59682
- const remotionNoCssUrlAssets = defineRule({
59683
- id: "remotion-no-css-url-assets",
59684
- title: "CSS URL asset can flicker in Remotion",
59685
- tags: ["react-jsx-only"],
59686
- requires: ["remotion:4"],
59687
- severity: "error",
59688
- recommendation: "Render the asset with `Img` inside an `AbsoluteFill`, or preload the same source with a hidden `Img` when a CSS mask is required.",
59689
- create: (context) => {
59690
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59691
- return { Property(node) {
59692
- if (!CSS_URL_ASSET_PROPERTY_NAMES.has(getStaticPropertyKeyName(node) ?? "")) return;
59693
- const styleObject = node.parent;
59694
- const expressionContainer = styleObject?.parent;
59695
- const styleAttribute = expressionContainer?.parent;
59696
- if (!isNodeOfType(styleObject, "ObjectExpression") || !isNodeOfType(expressionContainer, "JSXExpressionContainer") || expressionContainer.expression !== styleObject || !isNodeOfType(styleAttribute, "JSXAttribute") || !isNodeOfType(styleAttribute.name, "JSXIdentifier") || styleAttribute.name.name !== "style") return;
59697
- const cssValue = getStaticStringExpression(node.value);
59698
- const urlMatch = cssValue ? CSS_URL_PATTERN.exec(cssValue) : null;
59699
- if (!urlMatch) return;
59700
- const assetSource = urlMatch[2].trim();
59701
- if (isEmbeddedAssetSource(assetSource)) return;
59702
- const componentNode = findRenderPhaseComponentOrHook(node, context.scopes);
59703
- if (!componentNode || !renderEvidence.functionHasEvidence(componentNode) || componentPreloadsStaticImage(componentNode, assetSource, context.scopes)) return;
59704
- context.report({
59705
- node,
59706
- message: "Remotion cannot detect when a CSS `url()` asset has loaded, so the rendered frame can flicker. Render or preload the source with <Img> instead."
59707
- });
59708
- } };
59709
- }
59710
- });
59711
- //#endregion
59712
- //#region src/plugin/rules/correctness/remotion-no-module-scope-delay-render.ts
59713
- const remotionNoModuleScopeDelayRender = defineRule({
59714
- id: "remotion-no-module-scope-delay-render",
59715
- title: "Module-scoped delayRender blocks every composition",
59716
- requires: ["remotion:4"],
59717
- severity: "error",
59718
- recommendation: "Create the handle once inside the component. Use `useDelayRender()` on Remotion 4.0.342 or newer, or lazy `useState(() => delayRender())` on earlier versions.",
59719
- create: (context) => ({ CallExpression(node) {
59720
- const apiBinding = resolveRemotionApi(node.callee, context.scopes);
59721
- if (apiBinding?.apiName !== "delayRender" || apiBinding.moduleSource !== "remotion" || findEnclosingFunction$1(node)) return;
59722
- context.report({
59723
- node,
59724
- message: "A module-scoped `delayRender()` handle blocks all compositions and composition discovery. Move it inside the component and create it once with `useDelayRender()` or a lazy `useState` initializer."
59725
- });
59726
- } })
59727
- });
59728
- //#endregion
59729
- //#region src/plugin/rules/correctness/remotion-no-native-media-elements.ts
59730
- const REMOTION_MEDIA_REPLACEMENT_BY_TAG = new Map([
59731
- ["audio", "`Audio` from `@remotion/media`"],
59732
- ["iframe", "`IFrame` from `remotion`"],
59733
- ["img", "`Img` from `remotion`"],
59734
- ["video", "`Video` from `@remotion/media`"]
59735
- ]);
59736
- const remotionNoNativeMediaElements = defineRule({
59737
- id: "remotion-no-native-media-elements",
59738
- title: "Native media element bypasses Remotion loading",
59739
- tags: ["react-jsx-only"],
59740
- requires: ["remotion:4"],
59741
- severity: "error",
59742
- recommendation: "Use Remotion's media components so rendering waits for assets and seeks media to the requested frame.",
59743
- create: (context) => {
59744
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59745
- return { JSXOpeningElement(node) {
59746
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
59747
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
59748
- if (!renderFunction || !renderEvidence.functionHasEvidence(renderFunction)) return;
59749
- const replacement = REMOTION_MEDIA_REPLACEMENT_BY_TAG.get(node.name.name);
59750
- if (!replacement) return;
59751
- context.report({
59752
- node,
59753
- message: `Native <${node.name.name}> does not let Remotion reliably wait for and synchronize the asset. Use ${replacement} instead.`
59754
- });
59755
- } };
59756
- }
59757
- });
59758
- //#endregion
59759
- //#region src/plugin/rules/correctness/remotion-no-next-image.ts
59760
- const remotionNoNextImage = defineRule({
59761
- id: "remotion-no-next-image",
59762
- title: "Next.js Image can flicker in Remotion",
59763
- tags: ["react-jsx-only"],
59764
- requires: ["remotion:4"],
59765
- severity: "error",
59766
- recommendation: "Use `Img` from `remotion`, which delays rendering until the image is loaded.",
59767
- create: (context) => {
59768
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59769
- return { JSXOpeningElement(node) {
59770
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
59771
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
59772
- if (!renderFunction || !renderEvidence.functionHasEvidence(renderFunction)) return;
59773
- const symbol = context.scopes.symbolFor(node.name);
59774
- if (symbol?.kind !== "import") return;
59775
- const importBinding = getImportBindingForName(node.name, symbol.name);
59776
- if (importBinding?.source !== "next/image" || importBinding.exportedName !== "default") return;
59777
- context.report({
59778
- node,
59779
- message: "Next.js <Image> does not expose a reliable loading signal to Remotion, so rendered frames can flicker. Use <Img> from `remotion` instead."
59780
- });
59781
- } };
59782
- }
59783
- });
59784
- //#endregion
59785
- //#region src/plugin/rules/correctness/remotion-stable-delay-render-handle.ts
59786
- const isUseStateLazyInitializer = (node, scopes) => {
59787
- const enclosingFunction = findEnclosingFunction$1(node);
59788
- if (!enclosingFunction) return false;
59789
- const parent = enclosingFunction.parent;
59790
- return Boolean(isNodeOfType(parent, "CallExpression") && parent.arguments[0] === enclosingFunction && isReactApiCall(parent, "useState", scopes, { resolveNamedAliases: true }));
59791
- };
59792
- const remotionStableDelayRenderHandle = defineRule({
59793
- id: "remotion-stable-delay-render-handle",
59794
- title: "delayRender handle is recreated during render",
59795
- tags: ["react-jsx-only"],
59796
- requires: ["remotion:4"],
59797
- severity: "error",
59798
- recommendation: "Prefer `useDelayRender()`, or initialize `delayRender()` once with `useState(() => delayRender())`.",
59799
- create: (context) => ({ CallExpression(node) {
59800
- const apiBinding = resolveRemotionApi(node.callee, context.scopes);
59801
- if (apiBinding?.apiName !== "delayRender" || apiBinding.moduleSource !== "remotion" || !findRenderPhaseComponentOrHook(node, context.scopes) || isUseStateLazyInitializer(node, context.scopes)) return;
59802
- context.report({
59803
- node,
59804
- message: "Calling `delayRender()` during every component render creates another outstanding handle and can make rendering time out. Use `useDelayRender()` or a lazy `useState` initializer."
59805
- });
59806
- } })
59807
- });
59808
- //#endregion
59809
58739
  //#region src/plugin/rules/performance/rendering-animate-svg-wrapper.ts
59810
58740
  const renderingAnimateSvgWrapper = defineRule({
59811
58741
  id: "rendering-animate-svg-wrapper",
@@ -60926,7 +59856,7 @@ const functionBodyHasReturnWithValue = (functionNode) => {
60926
59856
  //#endregion
60927
59857
  //#region src/plugin/rules/react-builtins/require-render-return.ts
60928
59858
  const RENDER_METHOD_NAME = "render";
60929
- const MESSAGE$5 = "Your users see nothing because this `render` method returns nothing.";
59859
+ const MESSAGE$4 = "Your users see nothing because this `render` method returns nothing.";
60930
59860
  const isStaticRenderKey = (key) => {
60931
59861
  if (isNodeOfType(key, "Identifier")) return key.name === RENDER_METHOD_NAME;
60932
59862
  if (isNodeOfType(key, "Literal")) return key.value === RENDER_METHOD_NAME;
@@ -60986,7 +59916,7 @@ const requireRenderReturn = defineRule({
60986
59916
  if (functionBodyHasReturnWithValue(functionNode)) return;
60987
59917
  context.report({
60988
59918
  node: host.reportNode,
60989
- message: MESSAGE$5
59919
+ message: MESSAGE$4
60990
59920
  });
60991
59921
  };
60992
59922
  return {
@@ -62235,85 +61165,6 @@ const isSetterCalledDuringRender = (root, setterName) => {
62235
61165
  return found;
62236
61166
  };
62237
61167
  //#endregion
62238
- //#region src/plugin/utils/is-descendant-without-function-boundary.ts
62239
- const isDescendantWithoutFunctionBoundary = (descendant, ancestor) => {
62240
- let current = descendant;
62241
- while (current && current !== ancestor) {
62242
- if (current !== descendant && isFunctionLike$1(current)) return false;
62243
- current = current.parent;
62244
- }
62245
- return current === ancestor;
62246
- };
62247
- //#endregion
62248
- //#region src/plugin/utils/can-execute-before-async-suspension.ts
62249
- const collectSuspensionNodes = (functionNode, options) => {
62250
- const suspensionNodes = new Set(options.suspensionNodes);
62251
- walkAst(functionNode, (node) => {
62252
- if (node !== functionNode && isFunctionLike$1(node)) return false;
62253
- if (!options.suspensionNodes && isNodeOfType(node, "AwaitExpression") || isNodeOfType(node, "ForOfStatement") && node.await) suspensionNodes.add(node);
62254
- });
62255
- return suspensionNodes;
62256
- };
62257
- const isInsideForAwaitPostSuspensionRegion = (node, functionNode) => {
62258
- let current = node;
62259
- while (current && current !== functionNode) {
62260
- const parent = current.parent;
62261
- if (parent && isNodeOfType(parent, "ForOfStatement") && parent.await && (parent.left === current || parent.body === current)) return true;
62262
- current = parent;
62263
- }
62264
- return false;
62265
- };
62266
- const suspensionBlockNode = (suspensionNode) => isNodeOfType(suspensionNode, "ForOfStatement") ? suspensionNode.right : suspensionNode;
62267
- const doesSuspensionOccurBeforeNode = (suspensionNode, node) => {
62268
- if (isNodeOfType(suspensionNode, "AwaitExpression")) {
62269
- if (isDescendantWithoutFunctionBoundary(node, suspensionNode.argument)) return false;
62270
- } else if (isNodeOfType(suspensionNode, "ForOfStatement") && isDescendantWithoutFunctionBoundary(node, suspensionNode.right)) return false;
62271
- if (isDescendantWithoutFunctionBoundary(suspensionNode, node)) return true;
62272
- const suspensionStart = getRangeStart(suspensionBlockNode(suspensionNode));
62273
- const nodeStart = getRangeStart(node);
62274
- return suspensionStart !== null && nodeStart !== null && suspensionStart < nodeStart;
62275
- };
62276
- const findExecutionNodeWithinFunction = (node, functionNode, context) => {
62277
- let executionNode = node;
62278
- let executionOwner = context.cfg.enclosingFunction(executionNode);
62279
- while (executionOwner && executionOwner !== functionNode) {
62280
- if (!isFunctionLike$1(executionOwner) || executionOwner.async || executionOwner.generator) return null;
62281
- const callExpression = findImmediatelyInvokedCallExpression(executionOwner);
62282
- if (!callExpression) return null;
62283
- executionNode = callExpression;
62284
- executionOwner = context.cfg.enclosingFunction(callExpression);
62285
- }
62286
- return executionOwner === functionNode ? executionNode : null;
62287
- };
62288
- const canExecuteBeforeAsyncSuspension = (node, functionNode, context, options = {}) => {
62289
- if (!isFunctionLike$1(functionNode) || !functionNode.async) return isNodeReachableWithinFunction(node, context);
62290
- const executionNode = findExecutionNodeWithinFunction(node, functionNode, context);
62291
- if (!executionNode || isInsideForAwaitPostSuspensionRegion(executionNode, functionNode)) return false;
62292
- const functionCfg = context.cfg.cfgFor(functionNode);
62293
- const targetBlock = functionCfg?.blockOf(executionNode);
62294
- if (!functionCfg || !targetBlock) return false;
62295
- const suspensionsByBlock = /* @__PURE__ */ new Map();
62296
- for (const suspensionNode of collectSuspensionNodes(functionNode, options)) {
62297
- const suspensionBlock = functionCfg.blockOf(suspensionBlockNode(suspensionNode));
62298
- if (!suspensionBlock) continue;
62299
- const blockSuspensions = suspensionsByBlock.get(suspensionBlock) ?? [];
62300
- blockSuspensions.push(suspensionNode);
62301
- suspensionsByBlock.set(suspensionBlock, blockSuspensions);
62302
- }
62303
- const visitedBlocks = /* @__PURE__ */ new Set();
62304
- const pendingBlocks = [functionCfg.entry];
62305
- while (pendingBlocks.length > 0) {
62306
- const block = pendingBlocks.pop();
62307
- if (!block || visitedBlocks.has(block)) continue;
62308
- visitedBlocks.add(block);
62309
- const blockSuspensions = suspensionsByBlock.get(block) ?? [];
62310
- if (block === targetBlock) return !blockSuspensions.some((suspensionNode) => doesSuspensionOccurBeforeNode(suspensionNode, executionNode));
62311
- if (blockSuspensions.length > 0) continue;
62312
- for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
62313
- }
62314
- return false;
62315
- };
62316
- //#endregion
62317
61168
  //#region src/plugin/rules/state-and-effects/utils/create-external-location-invalidation-checker.ts
62318
61169
  const HISTORY_LOCATION_MUTATION_METHOD_NAMES = new Set(["pushState", "replaceState"]);
62319
61170
  const AGGREGATE_MUTATION_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
@@ -62506,6 +61357,14 @@ const buildLocationInvalidationIndex = (componentBody, componentFunction, contex
62506
61357
  });
62507
61358
  return index;
62508
61359
  };
61360
+ const isDescendantWithoutFunctionBoundary = (descendant, ancestor) => {
61361
+ let current = descendant;
61362
+ while (current && current !== ancestor) {
61363
+ if (current !== descendant && isFunctionLike$1(current)) return false;
61364
+ current = current.parent;
61365
+ }
61366
+ return current === ancestor;
61367
+ };
62509
61368
  const areInMutuallyExclusiveConditionalBranches = (firstNode, secondNode) => {
62510
61369
  const firstBranches = /* @__PURE__ */ new Map();
62511
61370
  let current = firstNode;
@@ -62817,7 +61676,39 @@ const canNodeReachNormalFunctionExit = (node, functionNode, index) => {
62817
61676
  }
62818
61677
  return false;
62819
61678
  };
62820
- const canReactBatchMutationAfterExecution = (executionNode, mutationNode, owner, index) => (isExclusiveIntrinsicReactEventHandler(owner, index) || index.effectCallbacks.has(owner)) && canNodeReachNode(executionNode, mutationNode, index) && canExecuteBeforeAsyncSuspension(mutationNode, owner, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(owner) }) && canNodeReachNormalFunctionExit(mutationNode, owner, index);
61679
+ const canExecuteBeforeAsyncSuspension = (node, functionNode, index) => {
61680
+ if (!isFunctionLike$1(functionNode) || !functionNode.async) return isNodeReachableWithinFunction(node, index.context);
61681
+ const functionCfg = index.context.cfg.cfgFor(functionNode);
61682
+ const targetBlock = functionCfg?.blockOf(node);
61683
+ if (!functionCfg || !targetBlock) return false;
61684
+ const awaitsByBlock = /* @__PURE__ */ new Map();
61685
+ for (const awaitExpression of index.awaitExpressionsByOwner.get(functionNode) ?? []) {
61686
+ const awaitBlock = functionCfg.blockOf(awaitExpression);
61687
+ if (!awaitBlock) continue;
61688
+ const blockAwaits = awaitsByBlock.get(awaitBlock) ?? [];
61689
+ blockAwaits.push(awaitExpression);
61690
+ awaitsByBlock.set(awaitBlock, blockAwaits);
61691
+ }
61692
+ const visitedBlocks = /* @__PURE__ */ new Set();
61693
+ const pendingBlocks = [functionCfg.entry];
61694
+ const targetStart = getRangeStart(node);
61695
+ while (pendingBlocks.length > 0) {
61696
+ const block = pendingBlocks.pop();
61697
+ if (!block || visitedBlocks.has(block)) continue;
61698
+ visitedBlocks.add(block);
61699
+ const blockAwaits = awaitsByBlock.get(block) ?? [];
61700
+ if (block === targetBlock) return !blockAwaits.some((awaitExpression) => {
61701
+ if (isNodeOfType(awaitExpression, "AwaitExpression") && isDescendantWithoutFunctionBoundary(node, awaitExpression.argument)) return false;
61702
+ if (isDescendantWithoutFunctionBoundary(awaitExpression, node)) return true;
61703
+ const awaitStart = getRangeStart(awaitExpression);
61704
+ return awaitStart !== null && targetStart !== null && awaitStart < targetStart;
61705
+ });
61706
+ if (blockAwaits.length > 0) continue;
61707
+ for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
61708
+ }
61709
+ return false;
61710
+ };
61711
+ const canReactBatchMutationAfterExecution = (executionNode, mutationNode, owner, index) => (isExclusiveIntrinsicReactEventHandler(owner, index) || index.effectCallbacks.has(owner)) && canNodeReachNode(executionNode, mutationNode, index) && canExecuteBeforeAsyncSuspension(mutationNode, owner, index) && canNodeReachNormalFunctionExit(mutationNode, owner, index);
62821
61712
  const functionMaySynchronouslyMutateLocation = (functionNode, index, visitingFunctions, cycleAffectedFunctions) => {
62822
61713
  const cachedResult = index.synchronousMutationResultByFunction.get(functionNode);
62823
61714
  if (cachedResult !== void 0) return cachedResult;
@@ -62831,7 +61722,7 @@ const functionMaySynchronouslyMutateLocation = (functionNode, index, visitingFun
62831
61722
  return false;
62832
61723
  }
62833
61724
  visitingFunctions.add(functionNode);
62834
- const doesMutateSynchronously = [...collectLocationMutationExecutions(functionNode, index, visitingFunctions, cycleAffectedFunctions)].some((mutationExecution) => canExecuteBeforeAsyncSuspension(mutationExecution, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) }) && canNodeReachNormalFunctionExit(mutationExecution, functionNode, index));
61725
+ const doesMutateSynchronously = [...collectLocationMutationExecutions(functionNode, index, visitingFunctions, cycleAffectedFunctions)].some((mutationExecution) => canExecuteBeforeAsyncSuspension(mutationExecution, functionNode, index) && canNodeReachNormalFunctionExit(mutationExecution, functionNode, index));
62835
61726
  visitingFunctions.delete(functionNode);
62836
61727
  if (doesMutateSynchronously || !cycleAffectedFunctions.has(functionNode)) index.synchronousMutationResultByFunction.set(functionNode, doesMutateSynchronously);
62837
61728
  return doesMutateSynchronously;
@@ -62858,20 +61749,20 @@ const functionMustSynchronouslyRemoveLocationListener = (functionNode, registrat
62858
61749
  for (const removal of index.listenerRemovals) {
62859
61750
  if (!isDefinitelyMatchingLocationListenerRemoval(registration, removal)) continue;
62860
61751
  if (index.context.cfg.enclosingFunction(removal.callExpression) !== functionNode) continue;
62861
- if (canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) })) removalExecutions.push(removal.callExpression);
61752
+ if (canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.push(removal.callExpression);
62862
61753
  }
62863
61754
  for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
62864
61755
  const calledFunction = index.calledFunctionByExpression.get(expression);
62865
- if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) }) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, nextVisitingFunctions)) removalExecutions.push(expression);
61756
+ if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, nextVisitingFunctions)) removalExecutions.push(expression);
62866
61757
  }
62867
61758
  return doNodesCoverEveryPathFromFunctionEntry(functionNode, removalExecutions, index.context);
62868
61759
  };
62869
61760
  const collectSynchronousLocationListenerRemovalExecutions = (functionNode, registration, index) => {
62870
61761
  const removalExecutions = /* @__PURE__ */ new Set();
62871
- for (const removal of index.listenerRemovals) if (isDefinitelyMatchingLocationListenerRemoval(registration, removal) && index.context.cfg.enclosingFunction(removal.callExpression) === functionNode && canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) })) removalExecutions.add(removal.callExpression);
61762
+ for (const removal of index.listenerRemovals) if (isDefinitelyMatchingLocationListenerRemoval(registration, removal) && index.context.cfg.enclosingFunction(removal.callExpression) === functionNode && canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.add(removal.callExpression);
62872
61763
  for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
62873
61764
  const calledFunction = index.calledFunctionByExpression.get(expression);
62874
- if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) }) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, /* @__PURE__ */ new Set())) removalExecutions.add(expression);
61765
+ if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, /* @__PURE__ */ new Set())) removalExecutions.add(expression);
62875
61766
  }
62876
61767
  return removalExecutions;
62877
61768
  };
@@ -69577,7 +68468,6 @@ const countOwnScopeHookCalls = (functionNode, scopes, settings, countedFunctionN
69577
68468
  };
69578
68469
  const MIN_HOOK_CALLS_FOR_RENDER_SCOPE = 2;
69579
68470
  const RENDER_SCOPE_FACTORY_NAME_PATTERN = /^_?(?:init|create|make|build)(?:[A-Z0-9_]|$)/;
69580
- const HOOK_NAMESPACE_NAME_PATTERN = /Hooks?$/;
69581
68471
  const isLocalNonHookFunctionCallee = (call, scopes, settings, countedFunctionNodes) => {
69582
68472
  const callee = call.callee;
69583
68473
  if (!isNodeOfType(callee, "Identifier")) return false;
@@ -69602,15 +68492,6 @@ const isPackageImportedNonReactHookCallee = (call) => {
69602
68492
  if (PATH_ALIAS_IMPORT_PATTERN.test(importSource)) return false;
69603
68493
  return !isReactEcosystemImportSource(importSource);
69604
68494
  };
69605
- const isDefaultImportedClassApiMemberCallee = (call) => {
69606
- const callee = call.callee;
69607
- if (!isNodeOfType(callee, "MemberExpression")) return false;
69608
- if (!isNodeOfType(callee.object, "Identifier")) return false;
69609
- if (HOOK_NAMESPACE_NAME_PATTERN.test(callee.object.name)) return false;
69610
- const importBinding = getImportBindingForName(call, callee.object.name);
69611
- if (!importBinding || importBinding.exportedName !== "default") return false;
69612
- return !REACT_RUNTIME_MODULE_SOURCES.has(importBinding.source);
69613
- };
69614
68495
  const isProjectOwnedMdxComponentsGetter = (call) => {
69615
68496
  const callee = call.callee;
69616
68497
  if (!isNodeOfType(callee, "Identifier") || callee.name !== "useMDXComponents") return false;
@@ -69760,7 +68641,6 @@ const rulesOfHooks = defineRule({
69760
68641
  return;
69761
68642
  }
69762
68643
  if (isInsideClassComponent(node)) {
69763
- if (isPackageImportedNonReactHookCallee(node) || isDefaultImportedClassApiMemberCallee(node)) return;
69764
68644
  context.report({
69765
68645
  node: node.callee,
69766
68646
  message: buildClassComponentMessage(hookName)
@@ -69867,7 +68747,7 @@ const rulesOfHooks = defineRule({
69867
68747
  });
69868
68748
  //#endregion
69869
68749
  //#region src/plugin/rules/a11y/scope.ts
69870
- const MESSAGE$4 = "The `scope` attribute only works on `<th>` cells, so screen readers get no table-header help from it here.";
68750
+ const MESSAGE$3 = "The `scope` attribute only works on `<th>` cells, so screen readers get no table-header help from it here.";
69871
68751
  const scope = defineRule({
69872
68752
  id: "scope",
69873
68753
  title: "scope attribute on non-th element",
@@ -69882,7 +68762,7 @@ const scope = defineRule({
69882
68762
  if (!HTML_TAGS.has(tag)) return;
69883
68763
  if (tag !== "th") context.report({
69884
68764
  node: scopeAttribute,
69885
- message: MESSAGE$4
68765
+ message: MESSAGE$3
69886
68766
  });
69887
68767
  } })
69888
68768
  });
@@ -69893,13 +68773,13 @@ const secretInFallback = defineRule({
69893
68773
  recommendation: "Remove the literal fallback and fail closed (throw when the variable is unset). The hardcoded value is a committed secret, and the `??`/`||` default makes the app run with it in any environment that forgot to set the var.",
69894
68774
  scan: scanByPattern({
69895
68775
  shouldScan: (file) => isProductionSourcePath(file.relativePath),
69896
- pattern: /\bprocess\.env\.(?!(?:(?:(?:NEXT|EXPO|GATSBY|NUXT|REACT_APP|VITE)_)?PUBLIC_[A-Z0-9_]*(?<!_SECRET)(?<!_PRIVATE_KEY)(?<!_PASSWORD)(?<!_PASSWD)|[A-Z0-9_]*(?:PUBLISHABLE|ANON)_KEY)(?![A-Z0-9_]))[A-Z][A-Z0-9_]*(?:SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE_KEY|API_?KEY|APIKEY|ACCESS_KEY|CLIENT_SECRET|CREDENTIAL|SIGNING_KEY|ENCRYPTION_KEY|WEBHOOK_SECRET|SERVICE_ROLE)[A-Z0-9_]*(?<!_(?:NAME|HEADER|ENDPOINT|URL|URI|ID|PREFIX|SUFFIX|PARAM|PARAMS|FIELD|ISSUER|AUDIENCE|ALGORITHM|ALG|REGION|BUCKET|HOST|HOSTNAME|PORT|PATH|VERSION|SCOPE|TYPE|FORMAT|EXPIRY|TTL))\s*(?:\?\?|\|\|)\s*(["'`])(?![0-9]+["'`])(?![a-z][a-z0-9]*(?:[_-][a-z0-9]+)*[_-](?:token|secret|key|password|passwd|credential)s?["'`])(?![\w-]{0,12}0{8,}["'`])(?!test test test test test test test test test test test junk["'`])(?!(?:changeme|change[_-]?me|placeholder|your[_-]|example|sample|dummy|development|local|todo|replace[_-]?me|https?:\/\/|x{3,}|\*{3,}))[^"'`\n]{8,}\1/i,
68776
+ pattern: /\bprocess\.env\.(?!(?:(?:(?:NEXT|EXPO|GATSBY|NUXT|REACT_APP|VITE)_)?PUBLIC_[A-Z0-9_]*(?<!_SECRET)(?<!_PRIVATE_KEY)(?<!_PASSWORD)(?<!_PASSWD)|[A-Z0-9_]*(?:PUBLISHABLE|ANON)_KEY)(?![A-Z0-9_]))[A-Z][A-Z0-9_]*(?:SECRET|TOKEN|PASSWORD|PASSWD|PRIVATE_KEY|API_?KEY|APIKEY|ACCESS_KEY|CLIENT_SECRET|CREDENTIAL|SIGNING_KEY|ENCRYPTION_KEY|WEBHOOK_SECRET|SERVICE_ROLE)[A-Z0-9_]*(?<!_(?:NAME|HEADER|ENDPOINT|URL|URI|ID|PREFIX|SUFFIX|PARAM|PARAMS|FIELD|ISSUER|AUDIENCE|ALGORITHM|ALG|REGION|BUCKET|HOST|HOSTNAME|PORT|PATH|VERSION|SCOPE|TYPE|FORMAT|EXPIRY|TTL))\s*(?:\?\?|\|\|)\s*(["'`])(?![0-9]+["'`])(?![a-z][a-z0-9]*(?:[_-][a-z0-9]+)*[_-](?:token|secret|key|password|passwd|credential)s?["'`])(?![\w-]{0,12}0{8,}["'`])(?!(?:changeme|change[_-]?me|placeholder|your[_-]|example|sample|dummy|development|local|todo|replace[_-]?me|https?:\/\/|x{3,}|\*{3,}))[^"'`\n]{8,}\1/i,
69897
68777
  message: "A secret env var has a hardcoded string fallback: the literal is a committed secret and the app fails open (uses it) when the variable is unset."
69898
68778
  })
69899
68779
  });
69900
68780
  //#endregion
69901
68781
  //#region src/plugin/rules/react-builtins/self-closing-comp.ts
69902
- const MESSAGE$3 = "This tag has no children, so the closing tag adds noise without changing output.";
68782
+ const MESSAGE$2 = "This tag has no children, so the closing tag adds noise without changing output.";
69903
68783
  const resolveSettings$2 = (settings) => {
69904
68784
  const reactDoctor = settings?.["react-doctor"];
69905
68785
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.selfClosingComp ?? {} : {};
@@ -69938,7 +68818,7 @@ const selfClosingComp = defineRule({
69938
68818
  if (isHtmlTag && !settings.html) return;
69939
68819
  context.report({
69940
68820
  node: node.openingElement,
69941
- message: MESSAGE$3
68821
+ message: MESSAGE$2
69942
68822
  });
69943
68823
  } };
69944
68824
  }
@@ -70163,30 +69043,7 @@ const isAuthGuardName = (calleeName) => {
70163
69043
  //#endregion
70164
69044
  //#region src/plugin/utils/is-non-privileged-server-action.ts
70165
69045
  const NON_DATA_EFFECT_FUNCTION_NAMES = new Set([...CACHE_REVALIDATION_FUNCTION_NAMES, ...NEXTJS_NAVIGATION_FUNCTIONS]);
70166
- const LOCALE_PREFERENCE_VERB_TOKENS = new Set(["set", "update"]);
70167
- const LOCALE_PREFERENCE_NOUN_TOKENS = new Set(["language", "locale"]);
70168
- const COOKIE_DELETION_METHOD_NAME = "delete";
70169
- const isCacheOrNavigationCall = (node) => {
70170
- if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "Identifier")) return false;
70171
- return NON_DATA_EFFECT_FUNCTION_NAMES.has(node.callee.name) && getImportSourceForName(node, node.callee.name) !== null;
70172
- };
70173
- const isCallerScopedLocalePreferenceCall = (node) => {
70174
- if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "Identifier") || node.arguments?.length !== 1) return false;
70175
- const nameTokens = tokenizeIdentifierWords(node.callee.name);
70176
- if (!nameTokens.some((token) => LOCALE_PREFERENCE_VERB_TOKENS.has(token))) return false;
70177
- if (!nameTokens.some((token) => LOCALE_PREFERENCE_NOUN_TOKENS.has(token))) return false;
70178
- const importSource = getImportSourceForName(node, node.callee.name)?.toLowerCase();
70179
- return Boolean(importSource?.includes("i18n") || importSource?.includes("locale"));
70180
- };
70181
- const isCallerScopedCookieCall = (node, cookieBindingNames) => {
70182
- if (!isNodeOfType(node, "CallExpression")) return false;
70183
- if (isNodeOfType(node.callee, "Identifier")) return node.callee.name === "cookies" && getImportSourceForName(node, node.callee.name) === "next/headers";
70184
- if (!isNodeOfType(node.callee, "MemberExpression") || !isNodeOfType(node.callee.property, "Identifier") || node.callee.property.name !== COOKIE_DELETION_METHOD_NAME) return false;
70185
- const receiver = stripParenExpression(node.callee.object);
70186
- if (isNodeOfType(receiver, "Identifier")) return cookieBindingNames.has(receiver.name);
70187
- return isCookiesOrAwaitedCookiesCall(receiver) && getImportSourceForName(receiver, "cookies") === "next/headers";
70188
- };
70189
- const isKnownNonDataEffectCall = (node, cookieBindingNames) => isCacheOrNavigationCall(node) || isCallerScopedLocalePreferenceCall(node) || isCallerScopedCookieCall(node, cookieBindingNames);
69046
+ const isCacheOrNavigationCall = (node) => isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && NON_DATA_EFFECT_FUNCTION_NAMES.has(node.callee.name) && getImportSourceForName(node, node.callee.name) !== null;
70190
69047
  const unwrapExpression = (node) => {
70191
69048
  let current = node;
70192
69049
  while (current) {
@@ -70202,10 +69059,10 @@ const unwrapExpression = (node) => {
70202
69059
  }
70203
69060
  return null;
70204
69061
  };
70205
- const isDataExposingValue = (node, cookieBindingNames) => {
69062
+ const isDataExposingValue = (node) => {
70206
69063
  const value = unwrapExpression(node);
70207
69064
  if (!value) return false;
70208
- if (isKnownNonDataEffectCall(value, cookieBindingNames)) return false;
69065
+ if (isCacheOrNavigationCall(value)) return false;
70209
69066
  return !isLiteralOnlyExpression(value);
70210
69067
  };
70211
69068
  const isLiteralOnlyExpression = (node) => {
@@ -70223,23 +69080,22 @@ const getReturnedOrThrownArgument = (node) => {
70223
69080
  if (isNodeOfType(node, "ThrowStatement")) return node.argument ?? null;
70224
69081
  return null;
70225
69082
  };
70226
- const isDataExposingReturnOrThrow = (node, cookieBindingNames) => isDataExposingValue(getReturnedOrThrownArgument(node), cookieBindingNames);
70227
- const isPrivilegedEffect = (node, cookieBindingNames) => isNodeOfType(node, "CallExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete" || isDataExposingReturnOrThrow(node, cookieBindingNames);
69083
+ const isDataExposingReturnOrThrow = (node) => isDataExposingValue(getReturnedOrThrownArgument(node));
69084
+ const isPrivilegedEffect = (node) => isNodeOfType(node, "CallExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete" || isDataExposingReturnOrThrow(node);
70228
69085
  const isNonPrivilegedServerAction = (functionNode) => {
70229
69086
  const functionBody = functionNode.body;
70230
69087
  if (!functionBody) return false;
70231
- const cookieBindingNames = getImportSourceForName(functionBody, "cookies") === "next/headers" ? collectLocallyScopedCookieBindings(functionBody) : /* @__PURE__ */ new Set();
70232
- if (!isNodeOfType(functionBody, "BlockStatement") && isDataExposingValue(functionBody, cookieBindingNames)) return false;
69088
+ if (!isNodeOfType(functionBody, "BlockStatement") && isDataExposingValue(functionBody)) return false;
70233
69089
  let hasNonDataEffectCall = false;
70234
69090
  let hasPrivilegedEffect = false;
70235
69091
  walkAst(functionBody, (child) => {
70236
69092
  if (hasPrivilegedEffect) return false;
70237
69093
  if (child !== functionBody && isFunctionLike$1(child)) return false;
70238
- if (isKnownNonDataEffectCall(child, cookieBindingNames)) {
69094
+ if (isCacheOrNavigationCall(child)) {
70239
69095
  hasNonDataEffectCall = true;
70240
69096
  return;
70241
69097
  }
70242
- if (isPrivilegedEffect(child, cookieBindingNames)) {
69098
+ if (isPrivilegedEffect(child)) {
70243
69099
  hasPrivilegedEffect = true;
70244
69100
  return false;
70245
69101
  }
@@ -70318,34 +69174,6 @@ const getAuthScanRoots = (functionNode) => {
70318
69174
  if (isNodeOfType(bodyNode, "BlockStatement")) return (bodyNode.body ?? []).slice(0, 10);
70319
69175
  return [bodyNode];
70320
69176
  };
70321
- const COMPONENT_NAME_PATTERN = /^[A-Z]/;
70322
- const TEST_APP_SOURCE_PATH_PATTERN = /(?:^|[/\\])test[/\\](?:app|src)[/\\]/;
70323
- const containsJsxOutsideNestedFunctions = (rootNode) => {
70324
- let containsJsx = false;
70325
- walkAst(rootNode, (child) => {
70326
- if (containsJsx) return false;
70327
- if (child !== rootNode && isFunctionLike$1(child)) return false;
70328
- if (isNodeOfType(child, "JSXElement") || isNodeOfType(child, "JSXFragment")) {
70329
- containsJsx = true;
70330
- return false;
70331
- }
70332
- });
70333
- return containsJsx;
70334
- };
70335
- const isComponentLikeServerExport = (candidate) => {
70336
- if (!COMPONENT_NAME_PATTERN.test(candidate.displayName)) return false;
70337
- const functionBody = candidate.functionNode.body;
70338
- if (!isNodeOfType(functionBody, "BlockStatement")) return containsJsxOutsideNestedFunctions(functionBody);
70339
- let hasReturnedJsx = false;
70340
- walkAst(functionBody, (child) => {
70341
- if (hasReturnedJsx) return false;
70342
- if (child !== functionBody && isFunctionLike$1(child)) return false;
70343
- if (!isNodeOfType(child, "ReturnStatement") || !child.argument) return;
70344
- hasReturnedJsx = containsJsxOutsideNestedFunctions(child.argument);
70345
- return false;
70346
- });
70347
- return hasReturnedJsx;
70348
- };
70349
69177
  const CREDENTIAL_MERGE_TAIL_TOKENS = {
70350
69178
  sign: new Set([
70351
69179
  "in",
@@ -70410,7 +69238,6 @@ const isCredentialEstablishingActionName = (actionName) => {
70410
69238
  const hasPublicNameToken = (actionName) => tokenizeIdentifierWords(actionName).includes("public");
70411
69239
  const inspectServerAction = (candidate, fileHasUseServerDirective, allowedFunctionNames, context) => {
70412
69240
  if (!(fileHasUseServerDirective || hasUseServerDirective(candidate.functionNode))) return;
70413
- if (isComponentLikeServerExport(candidate)) return;
70414
69241
  if (isCredentialEstablishingActionName(candidate.displayName)) return;
70415
69242
  if (hasPublicNameToken(candidate.displayName)) return;
70416
69243
  if (containsAuthCheck(getAuthScanRoots(candidate.functionNode), allowedFunctionNames, GENERIC_AUTH_METHOD_NAMES)) return;
@@ -70448,42 +69275,18 @@ const serverAuthActions = defineRule({
70448
69275
  title: "Unauthenticated server action can be called directly",
70449
69276
  severity: "error",
70450
69277
  recommendation: "Check auth before touching data because exported server actions can be called directly by unauthenticated clients.",
70451
- create: skipNonProductionFiles((context) => {
70452
- const shouldSkipTestAppSource = Boolean(context.filename && TEST_APP_SOURCE_PATH_PATTERN.test(context.filename));
69278
+ create: (context) => {
70453
69279
  let fileHasUseServerDirective = false;
70454
- let programNode = null;
70455
- const inspectedFunctions = /* @__PURE__ */ new Set();
70456
69280
  const customAuthFunctionNames = getReactDoctorStringArraySetting(context.settings, "serverAuthFunctionNames");
70457
69281
  const allowedFunctionNames = customAuthFunctionNames.length > 0 ? new Set([...AUTH_FUNCTION_NAMES, ...customAuthFunctionNames]) : AUTH_FUNCTION_NAMES;
70458
- const inspect = (candidate) => {
70459
- if (inspectedFunctions.has(candidate.functionNode)) return;
70460
- inspectedFunctions.add(candidate.functionNode);
70461
- inspectServerAction(candidate, fileHasUseServerDirective, allowedFunctionNames, context);
70462
- };
69282
+ const inspect = (candidate) => inspectServerAction(candidate, fileHasUseServerDirective, allowedFunctionNames, context);
70463
69283
  return {
70464
- Program(currentProgramNode) {
70465
- programNode = currentProgramNode;
70466
- fileHasUseServerDirective = hasDirective(currentProgramNode, "use server");
69284
+ Program(programNode) {
69285
+ fileHasUseServerDirective = hasDirective(programNode, "use server");
70467
69286
  },
70468
69287
  ExportNamedDeclaration(node) {
70469
- if (shouldSkipTestAppSource) return;
70470
69288
  const declaration = node.declaration;
70471
- if (!declaration) {
70472
- if (!programNode || node.source || node.exportKind === "type") return;
70473
- for (const specifier of node.specifiers ?? []) {
70474
- if (!isNodeOfType(specifier, "ExportSpecifier") || specifier.exportKind === "type") continue;
70475
- const exportedName = isNodeOfType(specifier.exported, "Identifier") ? specifier.exported.name : isNodeOfType(specifier.exported, "Literal") && typeof specifier.exported.value === "string" ? specifier.exported.value : null;
70476
- if (!exportedName) continue;
70477
- const exportedValue = findExportedValue(programNode, exportedName);
70478
- if (!isAsyncFunctionLikeNode(exportedValue)) continue;
70479
- inspect({
70480
- functionNode: exportedValue,
70481
- displayName: isNodeOfType(specifier.local, "Identifier") ? specifier.local.name : exportedName,
70482
- reportNode: specifier.local ?? specifier
70483
- });
70484
- }
70485
- return;
70486
- }
69289
+ if (!declaration) return;
70487
69290
  if (isAsyncFunctionLikeNode(declaration)) {
70488
69291
  if (!isNodeOfType(declaration, "FunctionDeclaration")) return;
70489
69292
  inspect({
@@ -70496,23 +69299,11 @@ const serverAuthActions = defineRule({
70496
69299
  if (isNodeOfType(declaration, "VariableDeclaration")) for (const candidate of collectCandidatesFromVariableDeclaration(declaration)) inspect(candidate);
70497
69300
  },
70498
69301
  ExportDefaultDeclaration(node) {
70499
- if (shouldSkipTestAppSource) return;
70500
- const directCandidate = getCandidateFromDefaultDeclaration(node);
70501
- if (directCandidate) {
70502
- inspect(directCandidate);
70503
- return;
70504
- }
70505
- if (!programNode) return;
70506
- const resolvedDefaultExport = findExportedValue(programNode, "default");
70507
- const candidate = isAsyncFunctionLikeNode(resolvedDefaultExport) ? {
70508
- functionNode: resolvedDefaultExport,
70509
- displayName: isNodeOfType(node.declaration, "Identifier") ? node.declaration.name : "default",
70510
- reportNode: node.declaration ?? node
70511
- } : null;
69302
+ const candidate = getCandidateFromDefaultDeclaration(node);
70512
69303
  if (candidate) inspect(candidate);
70513
69304
  }
70514
69305
  };
70515
- })
69306
+ }
70516
69307
  });
70517
69308
  //#endregion
70518
69309
  //#region src/plugin/rules/server/server-cache-with-object-literal.ts
@@ -70592,7 +69383,6 @@ const serverDedupProps = defineRule({
70592
69383
  for (const attr of node.attributes ?? []) {
70593
69384
  if (!isNodeOfType(attr, "JSXAttribute")) continue;
70594
69385
  if (!isNodeOfType(attr.name, "JSXIdentifier")) continue;
70595
- if (attr.name.name === "key") continue;
70596
69386
  if (!isNodeOfType(attr.value, "JSXExpressionContainer")) continue;
70597
69387
  const expression = attr.value.expression;
70598
69388
  if (!expression) continue;
@@ -71322,7 +70112,7 @@ const collectJsxRuntimeImports = (program) => {
71322
70112
  };
71323
70113
  //#endregion
71324
70114
  //#region src/plugin/rules/react-builtins/style-prop-object.ts
71325
- const MESSAGE$2 = "Your styles don't render because you passed the `style` prop a string instead of an object.";
70115
+ const MESSAGE$1 = "Your styles don't render because you passed the `style` prop a string instead of an object.";
71326
70116
  const resolveSettings = (settings) => {
71327
70117
  const reactDoctor = settings?.["react-doctor"];
71328
70118
  return { allow: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.stylePropObject ?? {} : {}).allow ?? [] };
@@ -71429,7 +70219,7 @@ const stylePropObject = defineRule({
71429
70219
  if (isNodeOfType(value, "Literal") && typeof value.value === "string") {
71430
70220
  context.report({
71431
70221
  node: attribute,
71432
- message: MESSAGE$2
70222
+ message: MESSAGE$1
71433
70223
  });
71434
70224
  return;
71435
70225
  }
@@ -71437,7 +70227,7 @@ const stylePropObject = defineRule({
71437
70227
  const innerExpression = value.expression;
71438
70228
  if (innerExpression && innerExpression.type !== "JSXEmptyExpression" && isInvalidStyleExpression(innerExpression)) context.report({
71439
70229
  node: attribute,
71440
- message: MESSAGE$2
70230
+ message: MESSAGE$1
71441
70231
  });
71442
70232
  }
71443
70233
  }
@@ -71460,7 +70250,7 @@ const stylePropObject = defineRule({
71460
70250
  if (!(isNodeOfType(key, "Identifier") && key.name === "style" || isNodeOfType(key, "Literal") && key.value === "style")) continue;
71461
70251
  if (isInvalidStyleExpression(property.value)) context.report({
71462
70252
  node: property,
71463
- message: MESSAGE$2
70253
+ message: MESSAGE$1
71464
70254
  });
71465
70255
  }
71466
70256
  }
@@ -71796,7 +70586,7 @@ const svgFilterClickjackingRisk = defineRule({
71796
70586
  });
71797
70587
  //#endregion
71798
70588
  //#region src/plugin/rules/a11y/tabindex-no-positive.ts
71799
- const MESSAGE$1 = "Keyboard users get jumped out of the normal order by a positive `tabIndex`, so use `0` or `-1`.";
70589
+ const MESSAGE = "Keyboard users get jumped out of the normal order by a positive `tabIndex`, so use `0` or `-1`.";
71800
70590
  const tabindexNoPositive = defineRule({
71801
70591
  id: "tabindex-no-positive",
71802
70592
  title: "Positive tabindex value",
@@ -71816,7 +70606,7 @@ const tabindexNoPositive = defineRule({
71816
70606
  } else if (tabIndex.value && isNodeOfType(tabIndex.value, "JSXExpressionContainer")) numericValue = parseJsxValue(tabIndex.value);
71817
70607
  if (numericValue !== null && numericValue > 0) context.report({
71818
70608
  node: tabIndex,
71819
- message: MESSAGE$1
70609
+ message: MESSAGE
71820
70610
  });
71821
70611
  } })
71822
70612
  });
@@ -72722,445 +71512,6 @@ const useLazyMotion = defineRule({
72722
71512
  } })
72723
71513
  });
72724
71514
  //#endregion
72725
- //#region src/plugin/rules/valtio/valtio-no-proxy-read-in-render.ts
72726
- const VALTIO_REACT_MODULE_SOURCES$1 = new Set(["valtio", "valtio/react"]);
72727
- const REACT_DEPENDENCY_ARRAY_HOOK_NAMES = new Set([
72728
- "useCallback",
72729
- "useEffect",
72730
- "useInsertionEffect",
72731
- "useLayoutEffect",
72732
- "useMemo"
72733
- ]);
72734
- const MESSAGE = "Read this Valtio value from the useSnapshot result during render. Keep proxy reads for callbacks so render uses the tracked, consistent snapshot.";
72735
- const isValtioImportSymbol = (symbol) => {
72736
- if (symbol.kind !== "import") return false;
72737
- const importDeclaration = symbol.declarationNode.parent;
72738
- return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && typeof importDeclaration.source.value === "string" && VALTIO_REACT_MODULE_SOURCES$1.has(importDeclaration.source.value));
72739
- };
72740
- const isValtioUseSnapshotCall = (node, scopes) => {
72741
- if (!isNodeOfType(node, "CallExpression")) return false;
72742
- const callee = stripParenExpression(node.callee);
72743
- if (isNodeOfType(callee, "Identifier")) {
72744
- const symbol = resolveConstIdentifierAlias(callee, scopes);
72745
- return Boolean(symbol && isValtioImportSymbol(symbol) && getImportedName(symbol.declarationNode) === "useSnapshot");
72746
- }
72747
- if (!isNodeOfType(callee, "MemberExpression") || getStaticPropertyName(callee) !== "useSnapshot") return false;
72748
- const receiver = stripParenExpression(callee.object);
72749
- if (!isNodeOfType(receiver, "Identifier")) return false;
72750
- const namespaceSymbol = resolveConstIdentifierAlias(receiver, scopes);
72751
- return Boolean(namespaceSymbol && isValtioImportSymbol(namespaceSymbol) && isNodeOfType(namespaceSymbol.declarationNode, "ImportNamespaceSpecifier"));
72752
- };
72753
- const collectPatternBindingIdentifiers = (pattern) => {
72754
- if (isNodeOfType(pattern, "Identifier")) return [pattern];
72755
- if (isNodeOfType(pattern, "AssignmentPattern")) return collectPatternBindingIdentifiers(pattern.left);
72756
- if (isNodeOfType(pattern, "RestElement")) return collectPatternBindingIdentifiers(pattern.argument);
72757
- if (isNodeOfType(pattern, "ArrayPattern")) return pattern.elements.flatMap((element) => element ? collectPatternBindingIdentifiers(element) : []);
72758
- if (isNodeOfType(pattern, "ObjectPattern")) return pattern.properties.flatMap((property) => {
72759
- if (isNodeOfType(property, "Property")) return collectPatternBindingIdentifiers(property.value);
72760
- return isNodeOfType(property, "RestElement") ? collectPatternBindingIdentifiers(property.argument) : [];
72761
- });
72762
- return [];
72763
- };
72764
- const collectAssignmentWriteTargets = (target) => {
72765
- const candidate = stripParenExpression(target);
72766
- if (isNodeOfType(candidate, "AssignmentPattern")) return collectAssignmentWriteTargets(candidate.left);
72767
- if (isNodeOfType(candidate, "RestElement")) return collectAssignmentWriteTargets(candidate.argument);
72768
- if (isNodeOfType(candidate, "ArrayPattern")) return candidate.elements.flatMap((element) => element ? collectAssignmentWriteTargets(element) : []);
72769
- if (isNodeOfType(candidate, "ObjectPattern")) return candidate.properties.flatMap((property) => isNodeOfType(property, "Property") ? collectAssignmentWriteTargets(property.value) : collectAssignmentWriteTargets(property.argument));
72770
- return [candidate];
72771
- };
72772
- const findPatternPathToBinding = (pattern, bindingIdentifier) => {
72773
- if (pattern === bindingIdentifier) return [];
72774
- if (isNodeOfType(pattern, "AssignmentPattern")) return findPatternPathToBinding(pattern.left, bindingIdentifier);
72775
- if (isNodeOfType(pattern, "RestElement")) return null;
72776
- if (isNodeOfType(pattern, "ArrayPattern")) {
72777
- for (const [elementIndex, element] of pattern.elements.entries()) {
72778
- if (!element) continue;
72779
- const nestedPath = findPatternPathToBinding(element, bindingIdentifier);
72780
- if (nestedPath) return [String(elementIndex), ...nestedPath];
72781
- }
72782
- return null;
72783
- }
72784
- if (!isNodeOfType(pattern, "ObjectPattern")) return null;
72785
- for (const property of pattern.properties) {
72786
- if (!isNodeOfType(property, "Property")) continue;
72787
- const nestedPath = findPatternPathToBinding(property.value, bindingIdentifier);
72788
- if (!nestedPath) continue;
72789
- const propertyName = getStaticPropertyKeyName(property);
72790
- return propertyName === null ? null : [propertyName, ...nestedPath];
72791
- }
72792
- return null;
72793
- };
72794
- const resolveProxyPath = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set(), allowedAliasSymbolIds = null, collectedAliasCaptures = null) => {
72795
- const candidate = stripParenExpression(expression);
72796
- if (isNodeOfType(candidate, "MemberExpression")) {
72797
- const receiverPath = resolveProxyPath(candidate.object, scopes, visitedSymbolIds, allowedAliasSymbolIds, collectedAliasCaptures);
72798
- if (!receiverPath) return null;
72799
- return {
72800
- ...receiverPath,
72801
- properties: [...receiverPath.properties, getStaticPropertyName(candidate)]
72802
- };
72803
- }
72804
- if (!isNodeOfType(candidate, "Identifier")) return null;
72805
- const symbol = scopes.symbolFor(candidate);
72806
- if (!symbol) return scopes.isGlobalReference(candidate) ? {
72807
- rootKey: `global:${candidate.name}`,
72808
- properties: []
72809
- } : null;
72810
- if (visitedSymbolIds.has(symbol.id)) return null;
72811
- if (symbol.kind === "const" && symbol.initializer && isNodeOfType(symbol.declarationNode, "VariableDeclarator")) {
72812
- const directAlias = symbol.declarationNode.id === symbol.bindingIdentifier;
72813
- const initializerNode = directAlias ? symbol.initializer : symbol.declarationNode.init;
72814
- if (!initializerNode) return {
72815
- rootKey: `symbol:${symbol.id}`,
72816
- properties: []
72817
- };
72818
- const initializer = stripParenExpression(initializerNode);
72819
- const canResolveDirectAlias = directAlias && (isNodeOfType(initializer, "Identifier") || isNodeOfType(initializer, "MemberExpression"));
72820
- const patternPath = directAlias ? null : findPatternPathToBinding(symbol.declarationNode.id, symbol.bindingIdentifier);
72821
- if ((canResolveDirectAlias || patternPath) && (allowedAliasSymbolIds === null || allowedAliasSymbolIds.has(symbol.id))) {
72822
- const nextVisitedSymbolIds = new Set(visitedSymbolIds);
72823
- nextVisitedSymbolIds.add(symbol.id);
72824
- const initializerPath = resolveProxyPath(initializer, scopes, nextVisitedSymbolIds, allowedAliasSymbolIds, collectedAliasCaptures);
72825
- if (initializerPath) {
72826
- const aliasPath = {
72827
- ...initializerPath,
72828
- properties: [...initializerPath.properties, ...patternPath ?? []]
72829
- };
72830
- const declarationEnd = symbol.declarationNode.range?.[1];
72831
- if (typeof declarationEnd === "number") collectedAliasCaptures?.set(symbol.id, {
72832
- declarationEnd,
72833
- path: aliasPath
72834
- });
72835
- return aliasPath;
72836
- }
72837
- }
72838
- }
72839
- return {
72840
- rootKey: `symbol:${symbol.id}`,
72841
- properties: []
72842
- };
72843
- };
72844
- const isScopeWithin = (candidateScope, ancestorScope) => {
72845
- let currentScope = candidateScope;
72846
- while (currentScope) {
72847
- if (currentScope === ancestorScope) return true;
72848
- currentScope = currentScope.parent;
72849
- }
72850
- return false;
72851
- };
72852
- const isPathPrefix = (prefix, candidate) => {
72853
- if (prefix.rootKey !== candidate.rootKey || prefix.properties.length > candidate.properties.length) return false;
72854
- return prefix.properties.every((propertyName, propertyIndex) => propertyName !== null && propertyName === candidate.properties[propertyIndex]);
72855
- };
72856
- const findOutermostMemberRead = (identifier) => {
72857
- let currentExpression = findTransparentExpressionRoot(identifier);
72858
- for (;;) {
72859
- const parent = currentExpression.parent;
72860
- if (!isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== stripParenExpression(currentExpression)) return currentExpression;
72861
- currentExpression = findTransparentExpressionRoot(parent);
72862
- }
72863
- };
72864
- const isReadPositionWithinAssignmentTarget = (expression) => {
72865
- let currentNode = expression;
72866
- let parentNode = currentNode.parent;
72867
- while (parentNode) {
72868
- if (isNodeOfType(parentNode, "MemberExpression") && parentNode.computed && parentNode.property === currentNode || isNodeOfType(parentNode, "Property") && parentNode.computed && parentNode.key === currentNode || isNodeOfType(parentNode, "AssignmentPattern") && parentNode.right === currentNode) return true;
72869
- if (isNodeOfType(parentNode, "AssignmentExpression") || isNodeOfType(parentNode, "UpdateExpression") || isNodeOfType(parentNode, "UnaryExpression") && parentNode.operator === "delete" || isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return false;
72870
- currentNode = parentNode;
72871
- parentNode = currentNode.parent;
72872
- }
72873
- return false;
72874
- };
72875
- const isSnapshotArgument = (expression, scopes) => {
72876
- const expressionRoot = findTransparentExpressionRoot(expression);
72877
- const parent = expressionRoot.parent;
72878
- return Boolean(parent && isNodeOfType(parent, "CallExpression") && isValtioUseSnapshotCall(parent, scopes) && parent.arguments[0] === expressionRoot);
72879
- };
72880
- const isStableProxyDependency = (expression, readPath, matchingTarget, scopes) => {
72881
- if (readPath.properties.length !== matchingTarget.path.properties.length || !isPathPrefix(matchingTarget.path, readPath)) return false;
72882
- const dependencyArray = findTransparentExpressionRoot(expression).parent;
72883
- if (!isNodeOfType(dependencyArray, "ArrayExpression")) return false;
72884
- const hookCall = dependencyArray.parent;
72885
- return Boolean(isNodeOfType(hookCall, "CallExpression") && hookCall.arguments[1] === dependencyArray && isReactApiCall(hookCall, REACT_DEPENDENCY_ARRAY_HOOK_NAMES, scopes, { allowGlobalReactNamespace: true }));
72886
- };
72887
- const getSnapshotTarget = (callExpression, context) => {
72888
- if (!isValtioUseSnapshotCall(callExpression, context.scopes)) return null;
72889
- const proxyArgument = callExpression.arguments[0];
72890
- if (!proxyArgument || isNodeOfType(proxyArgument, "SpreadElement")) return null;
72891
- const path = resolveProxyPath(proxyArgument, context.scopes);
72892
- if (!path || path.properties.some((propertyName) => propertyName === null)) return null;
72893
- const callExpressionRoot = findTransparentExpressionRoot(callExpression);
72894
- const declarator = callExpressionRoot.parent;
72895
- if (!isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== callExpressionRoot || !isNodeOfType(declarator.parent, "VariableDeclaration")) return null;
72896
- const ownerFunction = findRenderPhaseComponentOrHook(callExpression, context.scopes);
72897
- if (!ownerFunction) return null;
72898
- const snapshotBindingScopes = collectPatternBindingIdentifiers(declarator.id).flatMap((bindingIdentifier) => {
72899
- const bindingSymbol = context.scopes.symbolFor(bindingIdentifier);
72900
- return bindingSymbol ? [bindingSymbol.scope] : [];
72901
- });
72902
- const declarationEnd = declarator.range?.[1];
72903
- if (snapshotBindingScopes.length === 0 || typeof declarationEnd !== "number") return null;
72904
- return {
72905
- declarationEnd,
72906
- ownerFunction,
72907
- path,
72908
- snapshotBindingScopes
72909
- };
72910
- };
72911
- const wasTargetReplacedBeforeRead = (target, readPosition, writeTargets, readAliasCaptures, context) => writeTargets.some((writeTarget) => {
72912
- const writeNode = findTransparentExpressionRoot(writeTarget).parent;
72913
- const writePosition = writeNode?.range?.[0];
72914
- if (!writeNode || typeof writePosition !== "number" || writePosition <= target.declarationEnd || writePosition >= readPosition || findRenderPhaseComponentOrHook(writeNode, context.scopes) !== target.ownerFunction) return false;
72915
- const writePath = resolveProxyPath(writeTarget, context.scopes, /* @__PURE__ */ new Set(), null);
72916
- if (!writePath || !isPathPrefix(writePath, target.path)) return false;
72917
- return ![...readAliasCaptures.values()].some((aliasCapture) => aliasCapture.declarationEnd < writePosition && isPathPrefix(writePath, aliasCapture.path));
72918
- });
72919
- const valtioNoProxyReadInRender = defineRule({
72920
- id: "valtio-no-proxy-read-in-render",
72921
- title: "Valtio proxy read during render",
72922
- severity: "warn",
72923
- recommendation: "Read reactive render values from useSnapshot. Reserve the mutable proxy for event handlers, effects, and other callbacks that need the latest value.",
72924
- requires: ["valtio:1"],
72925
- create: (context) => {
72926
- const snapshotTargets = [];
72927
- const identifierCandidates = [];
72928
- const writeTargets = [];
72929
- return {
72930
- CallExpression(node) {
72931
- const snapshotTarget = getSnapshotTarget(node, context);
72932
- if (snapshotTarget) snapshotTargets.push(snapshotTarget);
72933
- },
72934
- Identifier(node) {
72935
- if (context.scopes.referenceFor(node)) identifierCandidates.push(node);
72936
- },
72937
- AssignmentExpression(node) {
72938
- writeTargets.push(...collectAssignmentWriteTargets(node.left));
72939
- },
72940
- UpdateExpression(node) {
72941
- writeTargets.push(node.argument);
72942
- },
72943
- "Program:exit"() {
72944
- const reportedExpressions = /* @__PURE__ */ new Set();
72945
- for (const identifier of identifierCandidates) {
72946
- const readExpression = findOutermostMemberRead(identifier);
72947
- if (reportedExpressions.has(readExpression) || isWithinAssignmentTarget(readExpression) && !isReadPositionWithinAssignmentTarget(readExpression) || isSnapshotArgument(readExpression, context.scopes)) continue;
72948
- const readAliasCaptures = /* @__PURE__ */ new Map();
72949
- const readPath = resolveProxyPath(readExpression, context.scopes, /* @__PURE__ */ new Set(), null, readAliasCaptures);
72950
- const readPosition = readExpression.range?.[0];
72951
- const ownerFunction = findRenderPhaseComponentOrHook(readExpression, context.scopes);
72952
- if (!readPath || typeof readPosition !== "number" || !ownerFunction) continue;
72953
- const readScope = context.scopes.scopeFor(readExpression);
72954
- const matchingTarget = snapshotTargets.find((target) => target.ownerFunction === ownerFunction && target.declarationEnd < readPosition && target.snapshotBindingScopes.some((bindingScope) => isScopeWithin(readScope, bindingScope)) && isPathPrefix(target.path, readPath) && (readAliasCaptures.size === 0 || [...readAliasCaptures.values()].some((aliasCapture) => isPathPrefix(aliasCapture.path, target.path) || isPathPrefix(target.path, aliasCapture.path) && readPath.properties.length > aliasCapture.path.properties.length)) && !wasTargetReplacedBeforeRead(target, readPosition, writeTargets, readAliasCaptures, context));
72955
- if (!matchingTarget || isStableProxyDependency(readExpression, readPath, matchingTarget, context.scopes)) continue;
72956
- reportedExpressions.add(readExpression);
72957
- context.report({
72958
- node: readExpression,
72959
- message: MESSAGE
72960
- });
72961
- }
72962
- }
72963
- };
72964
- }
72965
- });
72966
- //#endregion
72967
- //#region src/plugin/rules/valtio/valtio-no-snapshot-in-callback.ts
72968
- const VALTIO_REACT_MODULE_SOURCES = new Set(["valtio", "valtio/react"]);
72969
- const DEFERRED_REACT_HOOK_NAMES = new Set([
72970
- "useEffect",
72971
- "useInsertionEffect",
72972
- "useLayoutEffect"
72973
- ]);
72974
- const PROMISE_CONTINUATION_METHOD_NAMES = new Set([
72975
- "catch",
72976
- "finally",
72977
- "then"
72978
- ]);
72979
- const DEFERRED_CONSTRUCTOR_NAMES = new Set([
72980
- "IntersectionObserver",
72981
- "MutationObserver",
72982
- "PerformanceObserver",
72983
- "ResizeObserver"
72984
- ]);
72985
- const isValtioImport = (symbol) => {
72986
- const source = getImportDeclarationForSymbol(symbol)?.source.value;
72987
- return typeof source === "string" && VALTIO_REACT_MODULE_SOURCES.has(source);
72988
- };
72989
- const resolvesToValtioNamespace = (expression, scopes, visitedSymbolIds) => {
72990
- const candidate = stripParenExpression(expression);
72991
- if (!isNodeOfType(candidate, "Identifier")) return false;
72992
- const symbol = scopes.symbolFor(candidate);
72993
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
72994
- if (symbol.kind === "import" && isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") && isValtioImport(symbol)) return true;
72995
- const initializer = getDirectUnreassignedInitializer(symbol);
72996
- if (!initializer) return false;
72997
- visitedSymbolIds.add(symbol.id);
72998
- return resolvesToValtioNamespace(initializer, scopes, visitedSymbolIds);
72999
- };
73000
- const isValtioUseSnapshotCallee = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73001
- const candidate = stripParenExpression(expression);
73002
- if (isNodeOfType(candidate, "Identifier")) {
73003
- const symbol = scopes.symbolFor(candidate);
73004
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
73005
- if (symbol.kind === "import" && isValtioImport(symbol) && getImportedName(symbol.declarationNode) === "useSnapshot") return true;
73006
- const initializer = getDirectUnreassignedInitializer(symbol);
73007
- if (!initializer) return false;
73008
- visitedSymbolIds.add(symbol.id);
73009
- return isValtioUseSnapshotCallee(initializer, scopes, visitedSymbolIds);
73010
- }
73011
- if (!isNodeOfType(candidate, "MemberExpression") || getStaticPropertyName(candidate) !== "useSnapshot") return false;
73012
- return resolvesToValtioNamespace(candidate.object, scopes, visitedSymbolIds);
73013
- };
73014
- const getSnapshotOriginCall = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73015
- const candidate = stripParenExpression(expression);
73016
- if (isNodeOfType(candidate, "CallExpression")) return isValtioUseSnapshotCallee(candidate.callee, context.scopes) ? candidate : null;
73017
- if (!isNodeOfType(candidate, "Identifier")) return null;
73018
- const symbol = context.scopes.symbolFor(candidate);
73019
- if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
73020
- const initializer = getDirectUnreassignedInitializer(symbol);
73021
- if (!initializer) return null;
73022
- visitedSymbolIds.add(symbol.id);
73023
- return getSnapshotOriginCall(initializer, context, visitedSymbolIds);
73024
- };
73025
- const isJsxEventHandlerValue = (expression) => {
73026
- const expressionContainer = expression.parent;
73027
- if (!isNodeOfType(expressionContainer, "JSXExpressionContainer")) return false;
73028
- const attribute = expressionContainer.parent;
73029
- return Boolean(isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && REACT_HANDLER_PROP_PATTERN.test(attribute.name.name));
73030
- };
73031
- const isGlobalDeferredFunctionCall = (callExpression, context) => {
73032
- const callee = stripParenExpression(callExpression.callee);
73033
- if (isNodeOfType(callee, "Identifier") && TIMER_AND_SCHEDULER_DIRECT_CALLEE_NAMES.has(callee.name)) return context.scopes.isGlobalReference(callee);
73034
- if (!isNodeOfType(callee, "MemberExpression")) return false;
73035
- const methodName = getStaticPropertyName(callee);
73036
- if (!methodName || !TIMER_AND_SCHEDULER_DIRECT_CALLEE_NAMES.has(methodName)) return false;
73037
- const receiver = stripParenExpression(callee.object);
73038
- return Boolean(isNodeOfType(receiver, "Identifier") && (receiver.name === "globalThis" || receiver.name === "window") && context.scopes.isGlobalReference(receiver));
73039
- };
73040
- const isDeferredCallbackArgument = (expression, context) => {
73041
- const parent = expression.parent;
73042
- if (isNodeOfType(parent, "NewExpression")) {
73043
- const callee = stripParenExpression(parent.callee);
73044
- return Boolean(parent.arguments?.[0] === expression && isNodeOfType(callee, "Identifier") && DEFERRED_CONSTRUCTOR_NAMES.has(callee.name) && context.scopes.isGlobalReference(callee));
73045
- }
73046
- if (!isNodeOfType(parent, "CallExpression")) return false;
73047
- if (!(parent.arguments ?? []).some((argument) => argument === expression)) return false;
73048
- if (parent.arguments?.[0] === expression && isReactApiCall(parent, DEFERRED_REACT_HOOK_NAMES, context.scopes, {
73049
- allowGlobalReactNamespace: true,
73050
- resolveNamedAliases: true
73051
- })) return true;
73052
- if (parent.arguments?.[0] === expression && isGlobalDeferredFunctionCall(parent, context)) return true;
73053
- const callee = stripParenExpression(parent.callee);
73054
- if (!isNodeOfType(callee, "MemberExpression")) return false;
73055
- const methodName = getStaticPropertyName(callee);
73056
- return Boolean(methodName && (SUBSCRIPTION_METHOD_NAMES.has(methodName) || PROMISE_CONTINUATION_METHOD_NAMES.has(methodName)));
73057
- };
73058
- const isReactEffectCallbackValue = (expression, context) => {
73059
- const expressionRoot = findTransparentExpressionRoot(expression);
73060
- const parent = expressionRoot.parent;
73061
- return Boolean(isNodeOfType(parent, "CallExpression") && parent.arguments?.[0] === expressionRoot && isReactApiCall(parent, DEFERRED_REACT_HOOK_NAMES, context.scopes, {
73062
- allowGlobalReactNamespace: true,
73063
- resolveNamedAliases: true
73064
- }));
73065
- };
73066
- const isSymbolUsedAsReactEffectCallback = (symbol, context, visitedSymbolIds) => {
73067
- if (visitedSymbolIds.has(symbol.id)) return false;
73068
- visitedSymbolIds.add(symbol.id);
73069
- return symbol.references.some((reference) => {
73070
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
73071
- if (isReactEffectCallbackValue(referenceRoot, context)) return true;
73072
- const aliasSymbol = getConstAliasSymbol(referenceRoot, context);
73073
- return Boolean(aliasSymbol && isSymbolUsedAsReactEffectCallback(aliasSymbol, context, visitedSymbolIds));
73074
- });
73075
- };
73076
- const isFunctionUsedAsReactEffectCallback = (functionNode, context) => {
73077
- if (isReactEffectCallbackValue(functionNode, context)) return true;
73078
- const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
73079
- const symbol = bindingIdentifier ? context.scopes.symbolFor(bindingIdentifier) : null;
73080
- return Boolean(symbol && isSymbolUsedAsReactEffectCallback(symbol, context, /* @__PURE__ */ new Set()));
73081
- };
73082
- const isReactEffectCleanupValue = (expression, context) => {
73083
- const expressionRoot = findTransparentExpressionRoot(expression);
73084
- const returnStatement = expressionRoot.parent;
73085
- if (isNodeOfType(returnStatement, "ArrowFunctionExpression") && returnStatement.body === expressionRoot) return isFunctionUsedAsReactEffectCallback(returnStatement, context);
73086
- if (!isNodeOfType(returnStatement, "ReturnStatement") || returnStatement.argument !== expressionRoot) return false;
73087
- const effectCallback = findEnclosingFunction$1(returnStatement);
73088
- return Boolean(effectCallback && isFunctionUsedAsReactEffectCallback(effectCallback, context));
73089
- };
73090
- const isDeferredCallbackValue = (expression, context) => isJsxEventHandlerValue(expression) || isDeferredCallbackArgument(expression, context) || isReactEffectCleanupValue(expression, context);
73091
- const getConstAliasSymbol = (expression, context) => {
73092
- const expressionRoot = findTransparentExpressionRoot(expression);
73093
- const declarator = expressionRoot.parent;
73094
- if (!isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== expressionRoot || !isNodeOfType(declarator.id, "Identifier")) return null;
73095
- const aliasSymbol = context.scopes.symbolFor(declarator.id);
73096
- return aliasSymbol?.kind === "const" ? aliasSymbol : null;
73097
- };
73098
- const isCallTarget = (expression) => {
73099
- const parent = expression.parent;
73100
- return isNodeOfType(parent, "CallExpression") && parent.callee === expression ? parent : null;
73101
- };
73102
- const getSynchronousCallbackInvocation = (expression, context) => {
73103
- if (!executesDuringRender(expression, context.scopes)) return null;
73104
- const parent = expression.parent;
73105
- return isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression") ? parent : null;
73106
- };
73107
- const isNodeExecutedFromDeferredCallback = (node, snapshotOwner, context, visitedFunctionSymbolIds) => {
73108
- const enclosingFunction = findEnclosingFunction$1(node);
73109
- if (!enclosingFunction || enclosingFunction === snapshotOwner) return false;
73110
- return isFunctionExecutedAsDeferredCallback(enclosingFunction, snapshotOwner, context, visitedFunctionSymbolIds);
73111
- };
73112
- const isSymbolExecutedAsDeferredCallback = (symbol, snapshotOwner, context, visitedFunctionSymbolIds) => {
73113
- if (visitedFunctionSymbolIds.has(symbol.id)) return false;
73114
- visitedFunctionSymbolIds.add(symbol.id);
73115
- return symbol.references.some((reference) => {
73116
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
73117
- if (isDeferredCallbackValue(referenceRoot, context)) return true;
73118
- const aliasSymbol = getConstAliasSymbol(referenceRoot, context);
73119
- if (aliasSymbol && isSymbolExecutedAsDeferredCallback(aliasSymbol, snapshotOwner, context, visitedFunctionSymbolIds)) return true;
73120
- const callTarget = isCallTarget(referenceRoot);
73121
- if (callTarget) return isNodeExecutedFromDeferredCallback(callTarget, snapshotOwner, context, visitedFunctionSymbolIds);
73122
- const callbackInvocation = getSynchronousCallbackInvocation(referenceRoot, context);
73123
- return Boolean(callbackInvocation && isNodeExecutedFromDeferredCallback(callbackInvocation, snapshotOwner, context, visitedFunctionSymbolIds));
73124
- });
73125
- };
73126
- const isFunctionExecutedAsDeferredCallback = (functionNode, snapshotOwner, context, visitedFunctionSymbolIds) => {
73127
- const functionRoot = findTransparentExpressionRoot(functionNode);
73128
- if (isDeferredCallbackValue(functionRoot, context)) return true;
73129
- const synchronousInvocation = getSynchronousCallbackInvocation(functionRoot, context);
73130
- if (synchronousInvocation) return isNodeExecutedFromDeferredCallback(synchronousInvocation, snapshotOwner, context, visitedFunctionSymbolIds);
73131
- const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
73132
- if (!bindingIdentifier) return false;
73133
- const symbol = context.scopes.symbolFor(bindingIdentifier);
73134
- return Boolean(symbol && isSymbolExecutedAsDeferredCallback(symbol, snapshotOwner, context, visitedFunctionSymbolIds));
73135
- };
73136
- const isDirectSnapshotAliasInitializer = (identifier) => {
73137
- const expressionRoot = findTransparentExpressionRoot(identifier);
73138
- const parent = expressionRoot.parent;
73139
- return Boolean(isNodeOfType(parent, "VariableDeclarator") && parent.init === expressionRoot && isNodeOfType(parent.id, "Identifier"));
73140
- };
73141
- const valtioNoSnapshotInCallback = defineRule({
73142
- id: "valtio-no-snapshot-in-callback",
73143
- title: "Valtio snapshot read in a callback",
73144
- severity: "warn",
73145
- requires: ["valtio", "valtio:1"],
73146
- recommendation: "Read from the original Valtio proxy inside callbacks. `useSnapshot()` results are for render reads; callback reads can become tracked render dependencies and cause extra re-renders.",
73147
- create: (context) => ({ Identifier(node) {
73148
- const reference = context.scopes.referenceFor(node);
73149
- if (!reference || reference.flag === "write") return;
73150
- const snapshotOriginCall = getSnapshotOriginCall(node, context);
73151
- if (!snapshotOriginCall || isDirectSnapshotAliasInitializer(node)) return;
73152
- const snapshotOwner = findEnclosingFunction$1(snapshotOriginCall);
73153
- if (!snapshotOwner) return;
73154
- const referenceOwner = findEnclosingFunction$1(node);
73155
- if (!referenceOwner || referenceOwner === snapshotOwner) return;
73156
- if (!isFunctionExecutedAsDeferredCallback(referenceOwner, snapshotOwner, context, /* @__PURE__ */ new Set())) return;
73157
- context.report({
73158
- node,
73159
- message: "This callback reads a Valtio snapshot. Read the original proxy instead so callback-only fields do not become tracked render dependencies."
73160
- });
73161
- } })
73162
- });
73163
- //#endregion
73164
71515
  //#region src/plugin/rules/react-builtins/void-dom-elements-no-children.ts
73165
71516
  const VOID_DOM_ELEMENTS = new Set([
73166
71517
  "area",
@@ -73694,516 +72045,6 @@ const zodV4PreferTopLevelStringFormats = defineRule({
73694
72045
  })
73695
72046
  });
73696
72047
  //#endregion
73697
- //#region src/plugin/utils/function-returns-collection-at-path.ts
73698
- const resolveImmutableInitializer = (expression, scopes, visitedSymbolIds) => {
73699
- const candidate = stripParenExpression(expression);
73700
- if (!isNodeOfType(candidate, "Identifier")) return candidate;
73701
- const symbol = scopes.symbolFor(candidate);
73702
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return candidate;
73703
- visitedSymbolIds.add(symbol.id);
73704
- return resolveImmutableInitializer(symbol.initializer, scopes, visitedSymbolIds);
73705
- };
73706
- const expressionAtPropertyPath = (expression, propertyPath, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73707
- const candidate = resolveImmutableInitializer(expression, scopes, visitedSymbolIds);
73708
- const [propertyName, ...remainingPath] = propertyPath;
73709
- if (!propertyName) return candidate;
73710
- if (!isNodeOfType(candidate, "ObjectExpression")) return null;
73711
- for (const property of candidate.properties) {
73712
- if (!isNodeOfType(property, "Property")) continue;
73713
- if (getStaticPropertyKeyName(property) !== propertyName) continue;
73714
- return expressionAtPropertyPath(property.value, remainingPath, scopes, visitedSymbolIds);
73715
- }
73716
- return null;
73717
- };
73718
- const isGlobalConstructor = (expression, constructorNames, scopes) => {
73719
- const candidate = stripParenExpression(expression);
73720
- return isNodeOfType(candidate, "Identifier") && constructorNames.has(candidate.name) && scopes.isGlobalReference(candidate);
73721
- };
73722
- const isCollectionExpression = (expression, collectionKind, scopes) => {
73723
- const candidate = stripParenExpression(expression);
73724
- if (collectionKind === "array" && isNodeOfType(candidate, "ArrayExpression")) return true;
73725
- if (!isNodeOfType(candidate, "NewExpression")) return false;
73726
- return isGlobalConstructor(candidate.callee, collectionKind === "array" ? new Set(["Array"]) : new Set(["Map", "Set"]), scopes);
73727
- };
73728
- const functionReturnsCollectionAtPath = ({ collectionKind, functionNode, propertyPath, scopes }) => {
73729
- if (!isFunctionLike$1(functionNode)) return false;
73730
- const returnedExpressions = isNodeOfType(functionNode.body, "BlockStatement") ? collectFunctionReturnStatements(functionNode).flatMap((returnStatement) => returnStatement.argument ? [returnStatement.argument] : []) : [functionNode.body];
73731
- return returnedExpressions.length > 0 && returnedExpressions.every((returnedExpression) => {
73732
- const collectionExpression = expressionAtPropertyPath(returnedExpression, propertyPath, scopes);
73733
- return Boolean(collectionExpression && isCollectionExpression(collectionExpression, collectionKind, scopes));
73734
- });
73735
- };
73736
- //#endregion
73737
- //#region src/plugin/utils/resolve-zustand-api.ts
73738
- const ZUSTAND_APIS_BY_MODULE = new Map([
73739
- ["zustand", new Set([
73740
- "create",
73741
- "createStore",
73742
- "useStore"
73743
- ])],
73744
- ["zustand/vanilla", new Set(["createStore"])],
73745
- ["zustand/react", new Set(["useStore"])],
73746
- ["zustand/traditional", new Set(["createWithEqualityFn", "useStoreWithEqualityFn"])],
73747
- ["zustand/shallow", new Set(["shallow", "useShallow"])],
73748
- ["zustand/react/shallow", new Set(["useShallow"])],
73749
- ["zustand/middleware", new Set([
73750
- "combine",
73751
- "createJSONStorage",
73752
- "devtools",
73753
- "persist",
73754
- "redux",
73755
- "subscribeWithSelector"
73756
- ])],
73757
- ["zustand/middleware/immer", new Set(["immer"])]
73758
- ]);
73759
- const STATE_CREATOR_MIDDLEWARE_ARGUMENT_INDEX = new Map([
73760
- ["combine", 1],
73761
- ["devtools", 0],
73762
- ["immer", 0],
73763
- ["persist", 0],
73764
- ["subscribeWithSelector", 0]
73765
- ]);
73766
- const toZustandApiName = (value) => {
73767
- switch (value) {
73768
- case "combine":
73769
- case "create":
73770
- case "createJSONStorage":
73771
- case "createStore":
73772
- case "createWithEqualityFn":
73773
- case "devtools":
73774
- case "immer":
73775
- case "persist":
73776
- case "redux":
73777
- case "shallow":
73778
- case "subscribeWithSelector":
73779
- case "useShallow":
73780
- case "useStore":
73781
- case "useStoreWithEqualityFn": return value;
73782
- default: return null;
73783
- }
73784
- };
73785
- const getImportSource = (symbol) => {
73786
- const declaration = getImportDeclarationForSymbol(symbol);
73787
- if (!declaration || isTypeOnlyImport(declaration) || isNodeOfType(symbol.declarationNode, "ImportSpecifier") && symbol.declarationNode.importKind === "type") return null;
73788
- return typeof declaration.source.value === "string" ? declaration.source.value : null;
73789
- };
73790
- const bindingFromImportSymbol = (symbol) => {
73791
- const moduleSource = getImportSource(symbol);
73792
- const supportedApiNames = moduleSource ? ZUSTAND_APIS_BY_MODULE.get(moduleSource) : null;
73793
- if (!moduleSource || !supportedApiNames) return null;
73794
- if (moduleSource === "zustand" && isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return {
73795
- apiName: "create",
73796
- moduleSource
73797
- };
73798
- const importedName = getImportedName(symbol.declarationNode);
73799
- const apiName = importedName ? toZustandApiName(importedName) : null;
73800
- return apiName && supportedApiNames.has(apiName) ? {
73801
- apiName,
73802
- moduleSource
73803
- } : null;
73804
- };
73805
- const namespaceImportSource = (expression, scopes) => {
73806
- if (!isNodeOfType(expression, "Identifier")) return null;
73807
- const symbol = resolveConstIdentifierAlias(expression, scopes);
73808
- if (!symbol || !isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) return null;
73809
- return getImportSource(symbol);
73810
- };
73811
- const resolveZustandApiBinding = (expression, scopes) => {
73812
- const candidate = stripParenExpression(expression);
73813
- if (isNodeOfType(candidate, "Identifier")) {
73814
- const symbol = resolveConstIdentifierAlias(candidate, scopes);
73815
- if (!symbol) return null;
73816
- if (symbol.kind === "import") return bindingFromImportSymbol(symbol);
73817
- if (symbol.kind !== "const" || !symbol.initializer) return null;
73818
- return resolveZustandApiBinding(symbol.initializer, scopes);
73819
- }
73820
- if (!isNodeOfType(candidate, "MemberExpression")) return null;
73821
- const propertyName = getStaticPropertyName(candidate);
73822
- const moduleSource = namespaceImportSource(stripParenExpression(candidate.object), scopes);
73823
- const supportedApiNames = moduleSource ? ZUSTAND_APIS_BY_MODULE.get(moduleSource) : null;
73824
- const apiName = propertyName ? toZustandApiName(propertyName) : null;
73825
- return apiName && moduleSource && supportedApiNames?.has(apiName) ? {
73826
- apiName,
73827
- moduleSource
73828
- } : null;
73829
- };
73830
- const resolveStateCreator = (expression, scopes, middlewareNames, visitedExpressions = /* @__PURE__ */ new Set()) => {
73831
- const candidate = stripParenExpression(expression);
73832
- if (visitedExpressions.has(candidate)) return null;
73833
- visitedExpressions.add(candidate);
73834
- const creatorFunction = resolveExactLocalFunction(expression, scopes);
73835
- if (isFunctionLike$1(creatorFunction)) return creatorFunction;
73836
- if (!isNodeOfType(candidate, "CallExpression")) return null;
73837
- const middleware = resolveZustandApiBinding(candidate.callee, scopes);
73838
- const creatorArgumentIndex = middleware ? STATE_CREATOR_MIDDLEWARE_ARGUMENT_INDEX.get(middleware.apiName) : null;
73839
- if (!middleware || creatorArgumentIndex === void 0 || creatorArgumentIndex === null) return null;
73840
- const creatorArgument = candidate.arguments[creatorArgumentIndex];
73841
- if (!creatorArgument || isNodeOfType(creatorArgument, "SpreadElement")) return null;
73842
- middlewareNames.add(middleware.apiName);
73843
- return resolveStateCreator(creatorArgument, scopes, middlewareNames, visitedExpressions);
73844
- };
73845
- const isStoreFactoryApi = (binding) => binding?.apiName === "create" || binding?.apiName === "createStore" || binding?.apiName === "createWithEqualityFn";
73846
- const resolveCurriedStoreFactoryBinding = (expression, scopes) => {
73847
- const candidate = stripParenExpression(expression);
73848
- if (isNodeOfType(candidate, "Identifier")) {
73849
- const symbol = resolveConstIdentifierAlias(candidate, scopes);
73850
- if (symbol?.kind !== "const" || !symbol.initializer) return null;
73851
- return resolveCurriedStoreFactoryBinding(symbol.initializer, scopes);
73852
- }
73853
- if (!isNodeOfType(candidate, "CallExpression") || candidate.arguments.length > 0) return null;
73854
- const factoryBinding = resolveZustandApiBinding(candidate.callee, scopes);
73855
- return isStoreFactoryApi(factoryBinding) ? factoryBinding : null;
73856
- };
73857
- const resolveZustandStoreCreator = (callExpression, scopes) => {
73858
- const factoryCall = resolveZustandStoreFactoryCall(callExpression, scopes);
73859
- if (!factoryCall) return null;
73860
- const middlewareNames = /* @__PURE__ */ new Set();
73861
- const creatorFunction = resolveStateCreator(factoryCall.creatorArgument, scopes, middlewareNames);
73862
- if (!creatorFunction) return null;
73863
- return {
73864
- creatorFunction,
73865
- factoryApiName: factoryCall.factoryApiName,
73866
- middlewareNames
73867
- };
73868
- };
73869
- const resolveZustandStoreFactoryCall = (callExpression, scopes) => {
73870
- let factoryBinding = resolveZustandApiBinding(callExpression.callee, scopes);
73871
- if (!isStoreFactoryApi(factoryBinding)) {
73872
- factoryBinding = resolveCurriedStoreFactoryBinding(callExpression.callee, scopes);
73873
- if (!isStoreFactoryApi(factoryBinding)) return null;
73874
- }
73875
- const creatorArgument = callExpression.arguments[0];
73876
- if (!creatorArgument || isNodeOfType(creatorArgument, "SpreadElement")) return null;
73877
- return {
73878
- callExpression,
73879
- creatorArgument,
73880
- factoryApiName: factoryBinding.apiName
73881
- };
73882
- };
73883
- //#endregion
73884
- //#region src/plugin/rules/state-and-effects/zustand-no-fresh-selector-result.ts
73885
- const ALLOCATING_ARRAY_METHODS = new Set([
73886
- "filter",
73887
- "flat",
73888
- "flatMap",
73889
- "map",
73890
- "toReversed",
73891
- "toSorted",
73892
- "toSpliced",
73893
- "with"
73894
- ]);
73895
- const SAME_REFERENCE_ARRAY_METHODS = new Set(["reverse", "sort"]);
73896
- const ALLOCATING_NAMESPACE_METHOD_KINDS = new Map([["Array", new Map([["from", "array"], ["of", "array"]])], ["Object", new Map([
73897
- ["create", "object"],
73898
- ["entries", "array"],
73899
- ["fromEntries", "object"],
73900
- ["keys", "array"],
73901
- ["values", "array"]
73902
- ])]]);
73903
- const isNullishEqualityArgument = (argument, scopes) => {
73904
- const candidate = stripParenExpression(argument);
73905
- return isNodeOfType(candidate, "Identifier") && candidate.name === "undefined" && scopes.isGlobalReference(candidate) || isNodeOfType(candidate, "Literal") && candidate.value === null || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "void";
73906
- };
73907
- const hasExplicitEqualityArgument = (argumentsList, equalityArgumentIndex, scopes) => {
73908
- const equalityArgument = argumentsList[equalityArgumentIndex];
73909
- if (!equalityArgument) return false;
73910
- if (isNodeOfType(equalityArgument, "SpreadElement")) return true;
73911
- return !isNullishEqualityArgument(equalityArgument, scopes);
73912
- };
73913
- const resolveZustandStoreCreation = (expression, scopes) => {
73914
- const candidate = stripParenExpression(expression);
73915
- if (!isNodeOfType(candidate, "CallExpression")) return null;
73916
- const factoryCall = resolveZustandStoreFactoryCall(candidate, scopes);
73917
- if (!factoryCall || factoryCall.factoryApiName !== "create" && factoryCall.factoryApiName !== "createWithEqualityFn") return null;
73918
- return {
73919
- creatorFunction: resolveZustandStoreCreator(candidate, scopes)?.creatorFunction ?? null,
73920
- hasDefaultEquality: factoryCall.factoryApiName === "createWithEqualityFn" && hasExplicitEqualityArgument(candidate.arguments, 1, scopes),
73921
- supportsEqualityArgument: factoryCall.factoryApiName === "createWithEqualityFn"
73922
- };
73923
- };
73924
- const resolveZustandBoundStore = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73925
- const candidate = stripParenExpression(expression);
73926
- if (!isNodeOfType(candidate, "Identifier")) return null;
73927
- const symbol = scopes.symbolFor(candidate);
73928
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73929
- visitedSymbolIds.add(symbol.id);
73930
- return resolveZustandStoreCreation(symbol.initializer, scopes) ?? resolveZustandBoundStore(symbol.initializer, scopes, visitedSymbolIds);
73931
- };
73932
- const getZustandSelectorCall = (callExpression, scopes) => {
73933
- const apiBinding = resolveZustandApiBinding(callExpression.callee, scopes);
73934
- if (apiBinding?.apiName === "useStore" || apiBinding?.apiName === "useStoreWithEqualityFn") {
73935
- const selector = callExpression.arguments[1];
73936
- if (!selector || isNodeOfType(selector, "SpreadElement")) return null;
73937
- if (apiBinding.apiName === "useStoreWithEqualityFn" && hasExplicitEqualityArgument(callExpression.arguments, 2, scopes)) return null;
73938
- return {
73939
- selector,
73940
- storeCreatorFunction: null
73941
- };
73942
- }
73943
- const boundStore = resolveZustandBoundStore(callExpression.callee, scopes);
73944
- const selector = callExpression.arguments[0];
73945
- if (!boundStore || !selector || isNodeOfType(selector, "SpreadElement")) return null;
73946
- if (boundStore.hasDefaultEquality || boundStore.supportsEqualityArgument && hasExplicitEqualityArgument(callExpression.arguments, 1, scopes)) return null;
73947
- return {
73948
- selector,
73949
- storeCreatorFunction: boundStore.creatorFunction
73950
- };
73951
- };
73952
- const isUseShallowCall = (callExpression, scopes) => resolveZustandApiBinding(callExpression.callee, scopes)?.apiName === "useShallow";
73953
- const resolveSelectorFunction = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73954
- const candidate = stripParenExpression(expression);
73955
- if (isFunctionLike$1(candidate)) return candidate;
73956
- if (isNodeOfType(candidate, "CallExpression")) {
73957
- if (isUseShallowCall(candidate, scopes)) return null;
73958
- if (!isReactApiCall(candidate, "useCallback", scopes, {
73959
- allowGlobalReactNamespace: true,
73960
- resolveNamedAliases: true
73961
- })) return null;
73962
- const callback = candidate.arguments[0];
73963
- if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
73964
- return resolveSelectorFunction(callback, scopes, visitedSymbolIds);
73965
- }
73966
- if (!isNodeOfType(candidate, "Identifier")) return null;
73967
- const symbol = scopes.symbolFor(candidate);
73968
- if (!symbol || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73969
- if (symbol.kind === "function" && isFunctionLike$1(symbol.declarationNode)) return symbol.declarationNode;
73970
- if (symbol.kind !== "const" || !symbol.initializer) return null;
73971
- visitedSymbolIds.add(symbol.id);
73972
- return resolveSelectorFunction(symbol.initializer, scopes, visitedSymbolIds);
73973
- };
73974
- const selectorStatePropertyPath = (expression, analysis, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73975
- const candidate = stripParenExpression(expression);
73976
- if (isNodeOfType(candidate, "Identifier")) {
73977
- const symbol = analysis.scopes.symbolFor(candidate);
73978
- const selectorParameter = isFunctionLike$1(analysis.selectorFunction) ? analysis.selectorFunction.params[0] : null;
73979
- if (selectorParameter && isNodeOfType(selectorParameter, "Identifier") && symbol?.id === analysis.scopes.symbolFor(selectorParameter)?.id) return [];
73980
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73981
- visitedSymbolIds.add(symbol.id);
73982
- return selectorStatePropertyPath(symbol.initializer, analysis, visitedSymbolIds);
73983
- }
73984
- if (!isNodeOfType(candidate, "MemberExpression")) return null;
73985
- const propertyName = getStaticPropertyName(candidate);
73986
- const objectPath = selectorStatePropertyPath(candidate.object, analysis, visitedSymbolIds);
73987
- return propertyName && objectPath ? [...objectPath, propertyName] : null;
73988
- };
73989
- const freshResultFromAllocatingCall = (expression, analysis, visitedSymbolIds) => {
73990
- const callee = stripParenExpression(expression.callee);
73991
- if (!isNodeOfType(callee, "MemberExpression")) return null;
73992
- const methodName = getStaticPropertyName(callee);
73993
- if (!methodName) return null;
73994
- const receiver = stripParenExpression(callee.object);
73995
- if (isNodeOfType(receiver, "Identifier") && analysis.scopes.isGlobalReference(receiver)) {
73996
- const resultKind = ALLOCATING_NAMESPACE_METHOD_KINDS.get(receiver.name)?.get(methodName);
73997
- if (resultKind) return {
73998
- kind: resultKind,
73999
- node: expression
74000
- };
74001
- if (receiver.name === "Object" && methodName === "assign") {
74002
- const target = expression.arguments[0];
74003
- if (!target || isNodeOfType(target, "SpreadElement")) return null;
74004
- const freshTarget = resolveFreshSelectorResult(target, analysis, new Set(visitedSymbolIds));
74005
- return freshTarget ? {
74006
- kind: freshTarget.kind,
74007
- node: expression
74008
- } : null;
74009
- }
74010
- }
74011
- if (ALLOCATING_ARRAY_METHODS.has(methodName)) {
74012
- if (resolveFreshSelectorResult(receiver, analysis, new Set(visitedSymbolIds))?.kind === "array") return {
74013
- kind: "array",
74014
- node: expression
74015
- };
74016
- const receiverPath = selectorStatePropertyPath(receiver, analysis);
74017
- if (receiverPath && analysis.storeCreatorFunction && functionReturnsCollectionAtPath({
74018
- collectionKind: "array",
74019
- functionNode: analysis.storeCreatorFunction,
74020
- propertyPath: receiverPath,
74021
- scopes: analysis.scopes
74022
- })) return {
74023
- kind: "array",
74024
- node: expression
74025
- };
74026
- return null;
74027
- }
74028
- if (!SAME_REFERENCE_ARRAY_METHODS.has(methodName)) return null;
74029
- return resolveFreshSelectorResult(receiver, analysis, new Set(visitedSymbolIds)) ? {
74030
- kind: "array",
74031
- node: expression
74032
- } : null;
74033
- };
74034
- const resolveFreshSelectorResult = (expression, analysis, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
74035
- const freshRenderValue = resolveFreshRenderValue(expression, analysis.scopes);
74036
- if (freshRenderValue?.kind === "array" || freshRenderValue?.kind === "function" || freshRenderValue?.kind === "instance" || freshRenderValue?.kind === "object") return {
74037
- kind: freshRenderValue.kind,
74038
- node: expression
74039
- };
74040
- const candidate = stripParenExpression(expression);
74041
- if (isNodeOfType(candidate, "CallExpression")) return freshResultFromAllocatingCall(candidate, analysis, visitedSymbolIds);
74042
- if (isNodeOfType(candidate, "ConditionalExpression")) return resolveFreshSelectorResult(candidate.consequent, analysis, new Set(visitedSymbolIds)) ?? resolveFreshSelectorResult(candidate.alternate, analysis, new Set(visitedSymbolIds));
74043
- if (isNodeOfType(candidate, "LogicalExpression")) {
74044
- if (candidate.operator === "&&") return resolveFreshSelectorResult(candidate.right, analysis, visitedSymbolIds);
74045
- return resolveFreshSelectorResult(candidate.left, analysis, new Set(visitedSymbolIds)) ?? resolveFreshSelectorResult(candidate.right, analysis, new Set(visitedSymbolIds));
74046
- }
74047
- if (isNodeOfType(candidate, "SequenceExpression")) {
74048
- const returnedExpression = candidate.expressions[candidate.expressions.length - 1];
74049
- return returnedExpression ? resolveFreshSelectorResult(returnedExpression, analysis, visitedSymbolIds) : null;
74050
- }
74051
- if (!isNodeOfType(candidate, "Identifier")) return null;
74052
- const symbol = analysis.scopes.symbolFor(candidate);
74053
- if (symbol?.kind !== "const" || symbol.scope.kind === "module" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
74054
- visitedSymbolIds.add(symbol.id);
74055
- return resolveFreshSelectorResult(symbol.initializer, analysis, visitedSymbolIds);
74056
- };
74057
- const findFreshSelectorReturn = (selectorFunction, scopes, storeCreatorFunction) => {
74058
- if (!isFunctionLike$1(selectorFunction) || !selectorFunction.body) return null;
74059
- const analysis = {
74060
- scopes,
74061
- selectorFunction,
74062
- storeCreatorFunction
74063
- };
74064
- if (!isNodeOfType(selectorFunction.body, "BlockStatement")) return resolveFreshSelectorResult(selectorFunction.body, analysis);
74065
- let freshResult = null;
74066
- walkAst(selectorFunction.body, (candidate) => {
74067
- if (freshResult) return false;
74068
- if (candidate !== selectorFunction.body && isFunctionLike$1(candidate)) return false;
74069
- if (!isNodeOfType(candidate, "ReturnStatement") || !candidate.argument) return;
74070
- freshResult = resolveFreshSelectorResult(candidate.argument, analysis);
74071
- return freshResult ? false : void 0;
74072
- });
74073
- return freshResult;
74074
- };
74075
- const zustandNoFreshSelectorResult = defineRule({
74076
- id: "zustand-no-fresh-selector-result",
74077
- title: "Zustand selector returns a fresh value",
74078
- severity: "error",
74079
- category: "Performance",
74080
- requires: ["zustand", "zustand:5"],
74081
- recommendation: "Select a stable store field, split the selector, or wrap a collection selector with `useShallow`.",
74082
- create: (context) => ({ CallExpression(node) {
74083
- const selectorCall = getZustandSelectorCall(node, context.scopes);
74084
- if (!selectorCall) return;
74085
- const selectorFunction = resolveSelectorFunction(selectorCall.selector, context.scopes);
74086
- if (!selectorFunction) return;
74087
- const freshResult = findFreshSelectorReturn(selectorFunction, context.scopes, selectorCall.storeCreatorFunction);
74088
- if (!freshResult) return;
74089
- context.report({
74090
- node: freshResult.node,
74091
- message: "This Zustand selector creates a new reference whenever the store is read, so Object.is never sees a stable snapshot and Zustand v5 can repeatedly render or hit maximum update depth. Select a stable field or use `useShallow`."
74092
- });
74093
- } })
74094
- });
74095
- //#endregion
74096
- //#region src/plugin/rules/state-and-effects/zustand-no-get-during-initialization.ts
74097
- const findGetParameterIdentifier = (parameter) => {
74098
- if (!parameter) return null;
74099
- if (isNodeOfType(parameter, "Identifier")) return parameter;
74100
- if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
74101
- return null;
74102
- };
74103
- const isGetParameterCall = (callExpression, getParameterSymbol, context) => {
74104
- const callee = stripParenExpression(callExpression.callee);
74105
- if (!isNodeOfType(callee, "Identifier")) return false;
74106
- return resolveConstIdentifierAlias(callee, context.scopes)?.id === getParameterSymbol.id;
74107
- };
74108
- const reportEagerGetCalls = (creatorFunction, getParameterSymbol, context) => {
74109
- const visitedFunctions = /* @__PURE__ */ new Set();
74110
- const reportFunctionReads = (functionNode) => {
74111
- if (!isFunctionLike$1(functionNode) || functionNode.generator || visitedFunctions.has(functionNode)) return;
74112
- visitedFunctions.add(functionNode);
74113
- walkAst(functionNode, (node) => {
74114
- if (node !== functionNode && isFunctionLike$1(node)) return false;
74115
- if (!isNodeOfType(node, "CallExpression")) return;
74116
- if (isGetParameterCall(node, getParameterSymbol, context)) {
74117
- if (!canExecuteBeforeAsyncSuspension(node, functionNode, context)) return;
74118
- context.report({
74119
- node,
74120
- message: "`get()` runs before Zustand installs the initial state, so it returns `undefined` here."
74121
- });
74122
- return;
74123
- }
74124
- if (!canExecuteBeforeAsyncSuspension(node, functionNode, context)) return;
74125
- const calledFunction = resolveExactLocalFunction(node.callee, context.scopes);
74126
- if (!calledFunction || calledFunction === functionNode) return;
74127
- reportFunctionReads(calledFunction);
74128
- });
74129
- };
74130
- reportFunctionReads(creatorFunction);
74131
- };
74132
- const zustandNoGetDuringInitialization = defineRule({
74133
- id: "zustand-no-get-during-initialization",
74134
- title: "Zustand get() called during store initialization",
74135
- severity: "error",
74136
- category: "Correctness",
74137
- recommendation: "Move the read into a deferred store action or derive the initial value without calling `get()`.",
74138
- requires: ["zustand", "zustand:1"],
74139
- create: (context) => {
74140
- const creatorFunctions = /* @__PURE__ */ new Set();
74141
- return {
74142
- CallExpression(node) {
74143
- const creator = resolveZustandStoreCreator(node, context.scopes);
74144
- if (creator) creatorFunctions.add(creator.creatorFunction);
74145
- },
74146
- "Program:exit"() {
74147
- for (const creatorFunction of creatorFunctions) {
74148
- if (creatorFunction.generator) continue;
74149
- const parameterIdentifier = findGetParameterIdentifier(creatorFunction.params[1]);
74150
- const getParameterSymbol = parameterIdentifier ? context.scopes.symbolFor(parameterIdentifier) : null;
74151
- if (!getParameterSymbol) continue;
74152
- reportEagerGetCalls(creatorFunction, getParameterSymbol, context);
74153
- }
74154
- }
74155
- };
74156
- }
74157
- });
74158
- //#endregion
74159
- //#region src/plugin/rules/state-and-effects/zustand-no-whole-store-destructure.ts
74160
- const BOUND_STORE_FACTORY_APIS = new Set(["create", "createWithEqualityFn"]);
74161
- const VANILLA_STORE_FACTORY_APIS = new Set(["createStore"]);
74162
- const isStoreFactoryValue = (rawValue, factoryApiNames, scopes) => {
74163
- const value = stripParenExpression(rawValue);
74164
- if (!isNodeOfType(value, "CallExpression")) return false;
74165
- const factoryCall = resolveZustandStoreFactoryCall(value, scopes);
74166
- return Boolean(factoryCall && factoryApiNames.has(factoryCall.factoryApiName));
74167
- };
74168
- const isStoreValue = (rawValue, factoryApiNames, scopes) => {
74169
- const value = stripParenExpression(rawValue);
74170
- if (isStoreFactoryValue(value, factoryApiNames, scopes)) return true;
74171
- if (!isNodeOfType(value, "Identifier")) return false;
74172
- const symbol = resolveConstIdentifierAlias(value, scopes);
74173
- return Boolean(symbol?.kind === "const" && symbol.initializer && isStoreFactoryValue(symbol.initializer, factoryApiNames, scopes));
74174
- };
74175
- const isBoundStoreCall = (node, scopes) => {
74176
- const callee = stripParenExpression(node.callee);
74177
- return isNodeOfType(callee, "Identifier") && isStoreValue(callee, BOUND_STORE_FACTORY_APIS, scopes);
74178
- };
74179
- const isVanillaStoreHookCall = (node, scopes) => {
74180
- if (node.arguments.length !== 1) return false;
74181
- const hook = resolveZustandApiBinding(node.callee, scopes);
74182
- if (hook?.apiName !== "useStore" && hook?.apiName !== "useStoreWithEqualityFn") return false;
74183
- return isStoreValue(node.arguments[0], VANILLA_STORE_FACTORY_APIS, scopes);
74184
- };
74185
- const isDirectReactRenderCall = (node, context) => {
74186
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
74187
- if (!renderFunction || findEnclosingFunction$1(node) !== renderFunction) return false;
74188
- const displayName = componentOrHookDisplayNameForFunction(renderFunction);
74189
- return Boolean(displayName && (isReactHookName(displayName) || functionHasReactComponentEvidence(renderFunction, context.scopes, context.cfg)));
74190
- };
74191
- const zustandNoWholeStoreDestructure = defineRule({
74192
- id: "zustand-no-whole-store-destructure",
74193
- title: "Whole Zustand store subscribed during render",
74194
- severity: "warn",
74195
- requires: ["zustand:1"],
74196
- recommendation: "Pass a selector to the Zustand hook so this component rerenders only when the state it reads changes.",
74197
- create: (context) => ({ CallExpression(node) {
74198
- if (!isDirectReactRenderCall(node, context)) return;
74199
- if (!(node.arguments.length === 0 && isBoundStoreCall(node, context.scopes)) && !isVanillaStoreHookCall(node, context.scopes)) return;
74200
- context.report({
74201
- node,
74202
- message: "This hook subscribes to the whole Zustand store, so every store update rerenders this component. Pass a selector for the state it reads."
74203
- });
74204
- } })
74205
- });
74206
- //#endregion
74207
72048
  //#region src/plugin/rule-registry.ts
74208
72049
  const reactDoctorRules = [
74209
72050
  {
@@ -75518,39 +73359,6 @@ const reactDoctorRules = [
75518
73359
  requires: [...new Set(["react", ...mediaHasCaption.requires ?? []])]
75519
73360
  }
75520
73361
  },
75521
- {
75522
- key: "react-doctor/mobx-no-make-auto-observable-in-inheritance",
75523
- id: "mobx-no-make-auto-observable-in-inheritance",
75524
- source: "react-doctor",
75525
- originallyExternal: false,
75526
- rule: {
75527
- ...mobxNoMakeAutoObservableInInheritance,
75528
- framework: "global",
75529
- category: "Bugs"
75530
- }
75531
- },
75532
- {
75533
- key: "react-doctor/mobx-no-observer-wrapped-memo",
75534
- id: "mobx-no-observer-wrapped-memo",
75535
- source: "react-doctor",
75536
- originallyExternal: false,
75537
- rule: {
75538
- ...mobxNoObserverWrappedMemo,
75539
- framework: "global",
75540
- category: "Bugs"
75541
- }
75542
- },
75543
- {
75544
- key: "react-doctor/mobx-reaction-disposer-discarded",
75545
- id: "mobx-reaction-disposer-discarded",
75546
- source: "react-doctor",
75547
- originallyExternal: false,
75548
- rule: {
75549
- ...mobxReactionDisposerDiscarded,
75550
- framework: "global",
75551
- category: "Bugs"
75552
- }
75553
- },
75554
73362
  {
75555
73363
  key: "react-doctor/mouse-events-have-key-events",
75556
73364
  id: "mouse-events-have-key-events",
@@ -77733,105 +75541,6 @@ const reactDoctorRules = [
77733
75541
  requires: [...new Set(["react", ...reduxUseselectorReturnsNewCollection.requires ?? []])]
77734
75542
  }
77735
75543
  },
77736
- {
77737
- key: "react-doctor/remotion-calculate-metadata-fetch-signal",
77738
- id: "remotion-calculate-metadata-fetch-signal",
77739
- source: "react-doctor",
77740
- originallyExternal: false,
77741
- rule: {
77742
- ...remotionCalculateMetadataFetchSignal,
77743
- framework: "global",
77744
- category: "Bugs"
77745
- }
77746
- },
77747
- {
77748
- key: "react-doctor/remotion-deterministic-randomness",
77749
- id: "remotion-deterministic-randomness",
77750
- source: "react-doctor",
77751
- originallyExternal: false,
77752
- rule: {
77753
- ...remotionDeterministicRandomness,
77754
- framework: "global",
77755
- category: "Bugs"
77756
- }
77757
- },
77758
- {
77759
- key: "react-doctor/remotion-no-css-animation",
77760
- id: "remotion-no-css-animation",
77761
- source: "react-doctor",
77762
- originallyExternal: false,
77763
- rule: {
77764
- ...remotionNoCssAnimation,
77765
- framework: "global",
77766
- category: "Bugs"
77767
- }
77768
- },
77769
- {
77770
- key: "react-doctor/remotion-no-css-transition",
77771
- id: "remotion-no-css-transition",
77772
- source: "react-doctor",
77773
- originallyExternal: false,
77774
- rule: {
77775
- ...remotionNoCssTransition,
77776
- framework: "global",
77777
- category: "Bugs"
77778
- }
77779
- },
77780
- {
77781
- key: "react-doctor/remotion-no-css-url-assets",
77782
- id: "remotion-no-css-url-assets",
77783
- source: "react-doctor",
77784
- originallyExternal: false,
77785
- rule: {
77786
- ...remotionNoCssUrlAssets,
77787
- framework: "global",
77788
- category: "Bugs"
77789
- }
77790
- },
77791
- {
77792
- key: "react-doctor/remotion-no-module-scope-delay-render",
77793
- id: "remotion-no-module-scope-delay-render",
77794
- source: "react-doctor",
77795
- originallyExternal: false,
77796
- rule: {
77797
- ...remotionNoModuleScopeDelayRender,
77798
- framework: "global",
77799
- category: "Bugs"
77800
- }
77801
- },
77802
- {
77803
- key: "react-doctor/remotion-no-native-media-elements",
77804
- id: "remotion-no-native-media-elements",
77805
- source: "react-doctor",
77806
- originallyExternal: false,
77807
- rule: {
77808
- ...remotionNoNativeMediaElements,
77809
- framework: "global",
77810
- category: "Bugs"
77811
- }
77812
- },
77813
- {
77814
- key: "react-doctor/remotion-no-next-image",
77815
- id: "remotion-no-next-image",
77816
- source: "react-doctor",
77817
- originallyExternal: false,
77818
- rule: {
77819
- ...remotionNoNextImage,
77820
- framework: "global",
77821
- category: "Bugs"
77822
- }
77823
- },
77824
- {
77825
- key: "react-doctor/remotion-stable-delay-render-handle",
77826
- id: "remotion-stable-delay-render-handle",
77827
- source: "react-doctor",
77828
- originallyExternal: false,
77829
- rule: {
77830
- ...remotionStableDelayRenderHandle,
77831
- framework: "global",
77832
- category: "Bugs"
77833
- }
77834
- },
77835
75544
  {
77836
75545
  key: "react-doctor/rendering-animate-svg-wrapper",
77837
75546
  id: "rendering-animate-svg-wrapper",
@@ -78955,30 +76664,6 @@ const reactDoctorRules = [
78955
76664
  category: "Performance"
78956
76665
  }
78957
76666
  },
78958
- {
78959
- key: "react-doctor/valtio-no-proxy-read-in-render",
78960
- id: "valtio-no-proxy-read-in-render",
78961
- source: "react-doctor",
78962
- originallyExternal: false,
78963
- rule: {
78964
- ...valtioNoProxyReadInRender,
78965
- framework: "global",
78966
- category: "Bugs",
78967
- requires: [...new Set(["react", ...valtioNoProxyReadInRender.requires ?? []])]
78968
- }
78969
- },
78970
- {
78971
- key: "react-doctor/valtio-no-snapshot-in-callback",
78972
- id: "valtio-no-snapshot-in-callback",
78973
- source: "react-doctor",
78974
- originallyExternal: false,
78975
- rule: {
78976
- ...valtioNoSnapshotInCallback,
78977
- framework: "global",
78978
- category: "Bugs",
78979
- requires: [...new Set(["react", ...valtioNoSnapshotInCallback.requires ?? []])]
78980
- }
78981
- },
78982
76667
  {
78983
76668
  key: "react-doctor/void-dom-elements-no-children",
78984
76669
  id: "void-dom-elements-no-children",
@@ -79046,42 +76731,6 @@ const reactDoctorRules = [
79046
76731
  framework: "global",
79047
76732
  category: "Maintainability"
79048
76733
  }
79049
- },
79050
- {
79051
- key: "react-doctor/zustand-no-fresh-selector-result",
79052
- id: "zustand-no-fresh-selector-result",
79053
- source: "react-doctor",
79054
- originallyExternal: false,
79055
- rule: {
79056
- ...zustandNoFreshSelectorResult,
79057
- framework: "global",
79058
- category: "Performance",
79059
- requires: [...new Set(["react", ...zustandNoFreshSelectorResult.requires ?? []])]
79060
- }
79061
- },
79062
- {
79063
- key: "react-doctor/zustand-no-get-during-initialization",
79064
- id: "zustand-no-get-during-initialization",
79065
- source: "react-doctor",
79066
- originallyExternal: false,
79067
- rule: {
79068
- ...zustandNoGetDuringInitialization,
79069
- framework: "global",
79070
- category: "Bugs",
79071
- requires: [...new Set(["react", ...zustandNoGetDuringInitialization.requires ?? []])]
79072
- }
79073
- },
79074
- {
79075
- key: "react-doctor/zustand-no-whole-store-destructure",
79076
- id: "zustand-no-whole-store-destructure",
79077
- source: "react-doctor",
79078
- originallyExternal: false,
79079
- rule: {
79080
- ...zustandNoWholeStoreDestructure,
79081
- framework: "global",
79082
- category: "Bugs",
79083
- requires: [...new Set(["react", ...zustandNoWholeStoreDestructure.requires ?? []])]
79084
- }
79085
76734
  }
79086
76735
  ];
79087
76736
  const ruleRegistry = Object.fromEntries(reactDoctorRules.map((rule) => [rule.id, rule.rule]));
@@ -79351,13 +77000,6 @@ const CROSS_FILE_RULE_IDS = new Set([
79351
77000
  "no-unguarded-browser-global-in-render-or-hook-init",
79352
77001
  "prefer-dynamic-import",
79353
77002
  "rendering-hydration-mismatch-time",
79354
- "remotion-calculate-metadata-fetch-signal",
79355
- "remotion-deterministic-randomness",
79356
- "remotion-no-css-animation",
79357
- "remotion-no-css-transition",
79358
- "remotion-no-css-url-assets",
79359
- "remotion-no-native-media-elements",
79360
- "remotion-no-next-image",
79361
77003
  "rerender-memo-with-default-value",
79362
77004
  "rn-no-legacy-shadow-styles",
79363
77005
  "rn-no-raw-text",
@@ -79575,17 +77217,7 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
79575
77217
  * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
79576
77218
  * partition), forcing a conscious classification.
79577
77219
  */
79578
- const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set([
79579
- "nextjs-no-img-element",
79580
- "only-export-components",
79581
- "remotion-calculate-metadata-fetch-signal",
79582
- "remotion-deterministic-randomness",
79583
- "remotion-no-css-animation",
79584
- "remotion-no-css-transition",
79585
- "remotion-no-css-url-assets",
79586
- "remotion-no-native-media-elements",
79587
- "remotion-no-next-image"
79588
- ]);
77220
+ const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["nextjs-no-img-element", "only-export-components"]);
79589
77221
  /**
79590
77222
  * Runs the collectors for `ruleIds` over one file and returns every
79591
77223
  * filesystem probe they made — the file's cross-file dependency set.