oxlint-plugin-react-doctor 0.8.1-dev.7eca0ee → 0.8.1-dev.88114a2

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 -740
  2. package/dist/index.js +302 -2553
  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,
@@ -30786,7 +30231,7 @@ const nextjsNoVercelOgImport = defineRule({
30786
30231
  });
30787
30232
  //#endregion
30788
30233
  //#region src/plugin/rules/a11y/no-access-key.ts
30789
- 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.";
30790
30235
  const isUndefinedIdentifier$1 = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
30791
30236
  const noAccessKey = defineRule({
30792
30237
  id: "no-access-key",
@@ -30805,7 +30250,7 @@ const noAccessKey = defineRule({
30805
30250
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
30806
30251
  context.report({
30807
30252
  node: accessKey,
30808
- message: MESSAGE$40
30253
+ message: MESSAGE$39
30809
30254
  });
30810
30255
  return;
30811
30256
  }
@@ -30815,7 +30260,7 @@ const noAccessKey = defineRule({
30815
30260
  if (isUndefinedIdentifier$1(expression)) return;
30816
30261
  context.report({
30817
30262
  node: accessKey,
30818
- message: MESSAGE$40
30263
+ message: MESSAGE$39
30819
30264
  });
30820
30265
  }
30821
30266
  } };
@@ -33158,7 +32603,7 @@ const noAdjustStateOnPropChange = defineRule({
33158
32603
  });
33159
32604
  //#endregion
33160
32605
  //#region src/plugin/rules/a11y/no-aria-hidden-on-focusable.ts
33161
- 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.";
33162
32607
  const ALWAYS_FOCUSABLE_TAGS = new Set([
33163
32608
  "button",
33164
32609
  "embed",
@@ -33270,7 +32715,7 @@ const noAriaHiddenOnFocusable = defineRule({
33270
32715
  const isImplicitlyFocusable = isNativelyFocusable(tag, node);
33271
32716
  if (isExplicitlyFocusable || isImplicitlyFocusable) context.report({
33272
32717
  node: ariaHidden,
33273
- message: MESSAGE$39
32718
+ message: MESSAGE$38
33274
32719
  });
33275
32720
  } })
33276
32721
  });
@@ -34317,7 +33762,7 @@ const isAllLiteralArrayExpression = (node) => {
34317
33762
  };
34318
33763
  //#endregion
34319
33764
  //#region src/plugin/rules/react-builtins/no-array-index-key.ts
34320
- 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.";
34321
33766
  const SECOND_INDEX_METHODS = new Set([
34322
33767
  "every",
34323
33768
  "filter",
@@ -34436,14 +33881,14 @@ const noArrayIndexKey = defineRule({
34436
33881
  if (propName !== "key") continue;
34437
33882
  if (expressionUsesIndex(property.value, indexBinding.name)) context.report({
34438
33883
  node: property,
34439
- message: MESSAGE$38
33884
+ message: MESSAGE$37
34440
33885
  });
34441
33886
  }
34442
33887
  } })
34443
33888
  });
34444
33889
  //#endregion
34445
33890
  //#region src/plugin/rules/state-and-effects/no-async-effect-callback.ts
34446
- 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.";
34447
33892
  const noAsyncEffectCallback = defineRule({
34448
33893
  id: "no-async-effect-callback",
34449
33894
  title: "Async effect callback",
@@ -34461,13 +33906,13 @@ const noAsyncEffectCallback = defineRule({
34461
33906
  if (!callback.async) return;
34462
33907
  context.report({
34463
33908
  node: callback,
34464
- message: MESSAGE$37
33909
+ message: MESSAGE$36
34465
33910
  });
34466
33911
  } })
34467
33912
  });
34468
33913
  //#endregion
34469
33914
  //#region src/plugin/rules/a11y/no-autofocus.ts
34470
- 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.";
34471
33916
  const resolveSettings$21 = (settings) => {
34472
33917
  const reactDoctor = settings?.["react-doctor"];
34473
33918
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -34564,7 +34009,7 @@ const noAutofocus = defineRule({
34564
34009
  if (isConditionallyRendered(node)) return;
34565
34010
  context.report({
34566
34011
  node: autoFocusAttribute,
34567
- message: MESSAGE$36
34012
+ message: MESSAGE$35
34568
34013
  });
34569
34014
  } };
34570
34015
  }
@@ -35893,7 +35338,7 @@ const noChainStateUpdates = defineRule({
35893
35338
  });
35894
35339
  //#endregion
35895
35340
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
35896
- 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.";
35897
35342
  const noChildrenProp = defineRule({
35898
35343
  id: "no-children-prop",
35899
35344
  title: "Children passed as a prop",
@@ -35905,7 +35350,7 @@ const noChildrenProp = defineRule({
35905
35350
  if (node.name.name !== "children") return;
35906
35351
  context.report({
35907
35352
  node: node.name,
35908
- message: MESSAGE$35
35353
+ message: MESSAGE$34
35909
35354
  });
35910
35355
  },
35911
35356
  CallExpression(node) {
@@ -35918,7 +35363,7 @@ const noChildrenProp = defineRule({
35918
35363
  const propertyKey = property.key;
35919
35364
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "children" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "children") context.report({
35920
35365
  node: propertyKey,
35921
- message: MESSAGE$35
35366
+ message: MESSAGE$34
35922
35367
  });
35923
35368
  }
35924
35369
  }
@@ -35926,7 +35371,7 @@ const noChildrenProp = defineRule({
35926
35371
  });
35927
35372
  //#endregion
35928
35373
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
35929
- 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.";
35930
35375
  const noCloneElement = defineRule({
35931
35376
  id: "no-clone-element",
35932
35377
  title: "cloneElement makes child props fragile",
@@ -35939,7 +35384,7 @@ const noCloneElement = defineRule({
35939
35384
  if (isNodeOfType(callee, "Identifier") && callee.name === "cloneElement") {
35940
35385
  if (isImportedFromModule(node, "cloneElement", "react")) context.report({
35941
35386
  node: callee,
35942
- message: MESSAGE$34
35387
+ message: MESSAGE$33
35943
35388
  });
35944
35389
  return;
35945
35390
  }
@@ -35952,7 +35397,7 @@ const noCloneElement = defineRule({
35952
35397
  if (!isImportedFromModule(node, callee.object.name, "react")) return;
35953
35398
  context.report({
35954
35399
  node: callee,
35955
- message: MESSAGE$34
35400
+ message: MESSAGE$33
35956
35401
  });
35957
35402
  }
35958
35403
  } })
@@ -35972,7 +35417,7 @@ const getCallMethodName = (callee) => {
35972
35417
  };
35973
35418
  //#endregion
35974
35419
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
35975
- 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.";
35976
35421
  const CONTEXT_MODULES = [
35977
35422
  "react",
35978
35423
  "use-context-selector",
@@ -36020,7 +35465,7 @@ const noCreateContextInRender = defineRule({
36020
35465
  if (!componentOrHookName) return;
36021
35466
  context.report({
36022
35467
  node,
36023
- message: `${MESSAGE$33} (called inside "${componentOrHookName}")`
35468
+ message: `${MESSAGE$32} (called inside "${componentOrHookName}")`
36024
35469
  });
36025
35470
  } })
36026
35471
  });
@@ -36767,7 +36212,7 @@ const isProvenOneShotTestingLibraryComponent = (functionNode, filename, scopes)
36767
36212
  };
36768
36213
  //#endregion
36769
36214
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
36770
- 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.";
36771
36216
  const isUseMemoCallbackArgument = (functionNode, scopes) => {
36772
36217
  const parent = functionNode.parent;
36773
36218
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
@@ -36821,7 +36266,7 @@ const noCreateRefInFunctionComponent = defineRule({
36821
36266
  if (isCreateRefResultWriteOnly(node, context.filename, context.scopes)) return;
36822
36267
  context.report({
36823
36268
  node,
36824
- message: MESSAGE$32
36269
+ message: MESSAGE$31
36825
36270
  });
36826
36271
  } })
36827
36272
  });
@@ -36961,7 +36406,7 @@ const noCreateStoreInRender = defineRule({
36961
36406
  });
36962
36407
  //#endregion
36963
36408
  //#region src/plugin/rules/react-builtins/no-danger.ts
36964
- 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.";
36965
36410
  const noDanger = defineRule({
36966
36411
  id: "no-danger",
36967
36412
  title: "Raw HTML injection can run unsafe markup",
@@ -36975,7 +36420,7 @@ const noDanger = defineRule({
36975
36420
  if (!propAttribute) return;
36976
36421
  context.report({
36977
36422
  node: propAttribute.name,
36978
- message: MESSAGE$31
36423
+ message: MESSAGE$30
36979
36424
  });
36980
36425
  },
36981
36426
  CallExpression(node) {
@@ -36987,7 +36432,7 @@ const noDanger = defineRule({
36987
36432
  const propertyKey = property.key;
36988
36433
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "dangerouslySetInnerHTML" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "dangerouslySetInnerHTML") context.report({
36989
36434
  node: propertyKey,
36990
- message: MESSAGE$31
36435
+ message: MESSAGE$30
36991
36436
  });
36992
36437
  }
36993
36438
  }
@@ -37010,7 +36455,7 @@ const isMeaningfulJsxChild = (child) => {
37010
36455
  };
37011
36456
  //#endregion
37012
36457
  //#region src/plugin/rules/react-builtins/no-danger-with-children.ts
37013
- 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`.";
37014
36459
  const mergePropsShape = (target, source) => {
37015
36460
  target.hasDangerously ||= source.hasDangerously;
37016
36461
  target.hasChildren ||= source.hasChildren;
@@ -37085,7 +36530,7 @@ const noDangerWithChildren = defineRule({
37085
36530
  if (!hasChildrenProp && !hasNestedChildren) return;
37086
36531
  if (hasJsxPropIgnoreCase(opening.attributes, "dangerouslySetInnerHTML") || spreadPropsShape.hasDangerously) context.report({
37087
36532
  node: opening,
37088
- message: MESSAGE$30
36533
+ message: MESSAGE$29
37089
36534
  });
37090
36535
  },
37091
36536
  CallExpression(node) {
@@ -37098,7 +36543,7 @@ const noDangerWithChildren = defineRule({
37098
36543
  const positionalChildren = node.arguments.slice(2);
37099
36544
  if (positionalChildren.length > 1 || positionalChildren.some((argument) => !isNullishExpression(argument)) || propsShape.hasChildren) context.report({
37100
36545
  node,
37101
- message: MESSAGE$30
36546
+ message: MESSAGE$29
37102
36547
  });
37103
36548
  }
37104
36549
  })
@@ -38073,7 +37518,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
38073
37518
  //#endregion
38074
37519
  //#region src/plugin/rules/react-builtins/no-did-mount-set-state.ts
38075
37520
  const LIFECYCLE_NAMES$2 = new Set(["componentDidMount"]);
38076
- 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`.";
38077
37522
  const getEnclosingLifecycleFunction = (setStateCall) => {
38078
37523
  let ancestor = setStateCall.parent;
38079
37524
  while (ancestor) {
@@ -38200,7 +37645,7 @@ const noDidMountSetState = defineRule({
38200
37645
  }
38201
37646
  context.report({
38202
37647
  node: node.callee,
38203
- message: MESSAGE$29
37648
+ message: MESSAGE$28
38204
37649
  });
38205
37650
  } };
38206
37651
  }
@@ -38218,7 +37663,7 @@ const findEnclosingClass = (node) => {
38218
37663
  //#endregion
38219
37664
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
38220
37665
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
38221
- 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.";
38222
37667
  const DIFFERENCE_OPERATORS = new Set(["!=", "!=="]);
38223
37668
  const EQUALITY_OPERATORS = new Set([
38224
37669
  "==",
@@ -38612,7 +38057,7 @@ const noDidUpdateSetState = defineRule({
38612
38057
  if (isInsideDiffGuard(node, context.scopes)) return;
38613
38058
  context.report({
38614
38059
  node: node.callee,
38615
- message: MESSAGE$28
38060
+ message: MESSAGE$27
38616
38061
  });
38617
38062
  } };
38618
38063
  }
@@ -38635,7 +38080,7 @@ const isStateMemberExpression = (node) => {
38635
38080
  };
38636
38081
  //#endregion
38637
38082
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
38638
- 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.";
38639
38084
  const shouldIgnoreMutation = (node) => {
38640
38085
  let isConstructor = false;
38641
38086
  let isInsideCallExpression = false;
@@ -38657,7 +38102,7 @@ const reportIfStateMutation = (context, reportNode, target) => {
38657
38102
  if (shouldIgnoreMutation(reportNode)) return;
38658
38103
  context.report({
38659
38104
  node: reportNode,
38660
- message: MESSAGE$27
38105
+ message: MESSAGE$26
38661
38106
  });
38662
38107
  };
38663
38108
  const noDirectMutationState = defineRule({
@@ -39008,7 +38453,7 @@ const noDocumentStartViewTransition = defineRule({
39008
38453
  });
39009
38454
  //#endregion
39010
38455
  //#region src/plugin/rules/js-performance/no-document-write.ts
39011
- 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.";
39012
38457
  const WRITE_METHODS = new Set(["write", "writeln"]);
39013
38458
  const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
39014
38459
  const noDocumentWrite = defineRule({
@@ -39025,7 +38470,7 @@ const noDocumentWrite = defineRule({
39025
38470
  if (methodName === null || !WRITE_METHODS.has(methodName)) return;
39026
38471
  context.report({
39027
38472
  node,
39028
- message: MESSAGE$26
38473
+ message: MESSAGE$25
39029
38474
  });
39030
38475
  } })
39031
38476
  });
@@ -42073,7 +41518,7 @@ const IMPORT_INITIALIZER_TYPES = new Set([
42073
41518
  "ImportNamespaceSpecifier"
42074
41519
  ]);
42075
41520
  const CANCELLATION_FLAG_NAME_PATTERN = /cancel|ignore/i;
42076
- const PROMISE_CONTINUATION_METHOD_NAMES$1 = new Set([
41521
+ const PROMISE_CONTINUATION_METHOD_NAMES = new Set([
42077
41522
  "then",
42078
41523
  "catch",
42079
41524
  "finally"
@@ -42286,10 +41731,18 @@ const isCompletionSinkAfterCancellationEarlyExit = (completionSink, cancellation
42286
41731
  return false;
42287
41732
  };
42288
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
+ };
42289
41742
  const isPromiseContinuationForRequest = (functionNode, request) => {
42290
41743
  const callNode = functionNode.parent;
42291
- 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;
42292
- 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);
42293
41746
  };
42294
41747
  const isAwaitedInFunction = (request, functionNode) => {
42295
41748
  let currentNode = request.parent;
@@ -42357,7 +41810,7 @@ const ALLOWED_NAMESPACES = new Set([
42357
41810
  "ReactDOM",
42358
41811
  "ReactDom"
42359
41812
  ]);
42360
- 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.";
42361
41814
  const noFindDomNode = defineRule({
42362
41815
  id: "no-find-dom-node",
42363
41816
  title: "findDOMNode breaks component encapsulation",
@@ -42368,7 +41821,7 @@ const noFindDomNode = defineRule({
42368
41821
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
42369
41822
  if (isImportedFromModule(node, callee.name, "react-dom")) context.report({
42370
41823
  node: callee,
42371
- message: MESSAGE$25
41824
+ message: MESSAGE$24
42372
41825
  });
42373
41826
  return;
42374
41827
  }
@@ -42379,7 +41832,7 @@ const noFindDomNode = defineRule({
42379
41832
  if (callee.property.name !== "findDOMNode") return;
42380
41833
  context.report({
42381
41834
  node: callee.property,
42382
- message: MESSAGE$25
41835
+ message: MESSAGE$24
42383
41836
  });
42384
41837
  }
42385
41838
  } })
@@ -43517,7 +42970,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
43517
42970
  });
43518
42971
  //#endregion
43519
42972
  //#region src/plugin/rules/performance/no-img-lazy-with-high-fetchpriority.ts
43520
- 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.";
43521
42974
  const noImgLazyWithHighFetchpriority = defineRule({
43522
42975
  id: "no-img-lazy-with-high-fetchpriority",
43523
42976
  title: "Lazy image with high fetchPriority",
@@ -43531,7 +42984,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
43531
42984
  if (!fetchPriorityAttribute || getJsxPropStringValue(fetchPriorityAttribute)?.toLowerCase() !== "high") return;
43532
42985
  context.report({
43533
42986
  node: node.name,
43534
- message: MESSAGE$24
42987
+ message: MESSAGE$23
43535
42988
  });
43536
42989
  } })
43537
42990
  });
@@ -43720,7 +43173,7 @@ const noImpureStateUpdater = defineRule({
43720
43173
  });
43721
43174
  //#endregion
43722
43175
  //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
43723
- 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.";
43724
43177
  const REACT_USE_REF_OPTIONS = {
43725
43178
  allowGlobalReactNamespace: false,
43726
43179
  allowUnboundBareCalls: false
@@ -43821,7 +43274,7 @@ const noIndeterminateAttribute = defineRule({
43821
43274
  if (!inputTypeValues || !inputTypeValues.every((inputTypeValue) => inputTypeValue.toLowerCase() === "checkbox")) return;
43822
43275
  context.report({
43823
43276
  node: indeterminateAttribute,
43824
- message: MESSAGE$23
43277
+ message: MESSAGE$22
43825
43278
  });
43826
43279
  },
43827
43280
  CallExpression(node) {
@@ -43830,7 +43283,7 @@ const noIndeterminateAttribute = defineRule({
43830
43283
  if (!isProvenHtmlInputElement(receiver, context.scopes)) return;
43831
43284
  context.report({
43832
43285
  node,
43833
- message: MESSAGE$23
43286
+ message: MESSAGE$22
43834
43287
  });
43835
43288
  }
43836
43289
  };
@@ -44118,7 +43571,7 @@ const noIsMounted = defineRule({
44118
43571
  });
44119
43572
  //#endregion
44120
43573
  //#region src/plugin/rules/js-performance/no-json-parse-stringify-clone.ts
44121
- 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)`.";
44122
43575
  const isJsonMethodCall = (node, method) => {
44123
43576
  if (!isNodeOfType(node, "CallExpression")) return false;
44124
43577
  const callee = node.callee;
@@ -44182,13 +43635,13 @@ const noJsonParseStringifyClone = defineRule({
44182
43635
  if (isCatchParameterRoundTrip(firstArgument)) return;
44183
43636
  context.report({
44184
43637
  node,
44185
- message: MESSAGE$22
43638
+ message: MESSAGE$21
44186
43639
  });
44187
43640
  } })
44188
43641
  });
44189
43642
  //#endregion
44190
43643
  //#region src/plugin/rules/correctness/no-jsx-element-type.ts
44191
- 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.";
44192
43645
  const isJsxElementTypeReference = (node) => {
44193
43646
  if (!isNodeOfType(node, "TSTypeReference")) return false;
44194
43647
  const typeName = node.typeName;
@@ -44240,7 +43693,7 @@ const noJsxElementType = defineRule({
44240
43693
  if (isJsxImported) return;
44241
43694
  for (const typeAnnotation of flaggedAnnotations) context.report({
44242
43695
  node: typeAnnotation,
44243
- message: MESSAGE$21
43696
+ message: MESSAGE$20
44244
43697
  });
44245
43698
  }
44246
43699
  };
@@ -45521,7 +44974,7 @@ const noMoment = defineRule({
45521
44974
  });
45522
44975
  //#endregion
45523
44976
  //#region src/plugin/rules/react-builtins/no-multi-comp.ts
45524
- 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.";
45525
44978
  const resolveSettings$16 = (settings) => {
45526
44979
  const reactDoctor = settings?.["react-doctor"];
45527
44980
  return { ignoreStateless: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noMultiComp ?? {} : {}).ignoreStateless ?? false };
@@ -45873,7 +45326,7 @@ const noMultiComp = defineRule({
45873
45326
  if (isSmallFeatureModule || isLargeFeatureModule || isVeryLargeFeatureModule) return;
45874
45327
  for (const component of flagged.slice(1)) context.report({
45875
45328
  node: component.reportNode,
45876
- message: MESSAGE$20
45329
+ message: MESSAGE$19
45877
45330
  });
45878
45331
  } };
45879
45332
  }
@@ -45961,6 +45414,41 @@ const noMutableInDeps = defineRule({
45961
45414
  }
45962
45415
  });
45963
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
45964
45452
  //#region src/plugin/rules/state-and-effects/utils/lodash-mutator-call.ts
45965
45453
  const LODASH_MUTATOR_NAMES = new Set([
45966
45454
  "set",
@@ -46051,7 +45539,7 @@ const resolveReducerFunction = (node, currentFilename) => {
46051
45539
  };
46052
45540
  //#endregion
46053
45541
  //#region src/plugin/rules/state-and-effects/no-mutating-reducer-state.ts
46054
- 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.";
46055
45543
  const SAME_REFERENCE_ARRAY_RETURN_METHODS = new Set([
46056
45544
  "copyWithin",
46057
45545
  "fill",
@@ -46255,7 +45743,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
46255
45743
  reportedNodes.add(options.crossFileConsumerCallSite);
46256
45744
  context.report({
46257
45745
  node: options.crossFileConsumerCallSite,
46258
- message: `${MESSAGE$19} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
45746
+ message: `${MESSAGE$18} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
46259
45747
  });
46260
45748
  return;
46261
45749
  }
@@ -46264,7 +45752,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
46264
45752
  reportedNodes.add(mutation.node);
46265
45753
  context.report({
46266
45754
  node: mutation.node,
46267
- message: MESSAGE$19
45755
+ message: MESSAGE$18
46268
45756
  });
46269
45757
  }
46270
45758
  };
@@ -46671,7 +46159,7 @@ const noNoninteractiveElementToInteractiveRole = defineRule({
46671
46159
  });
46672
46160
  //#endregion
46673
46161
  //#region src/plugin/rules/a11y/no-noninteractive-tabindex.ts
46674
- 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.";
46675
46163
  const KEYBOARD_HANDLER_PROP_NAMES = [
46676
46164
  "onKeyDown",
46677
46165
  "onKeyUp",
@@ -46790,7 +46278,7 @@ const noNoninteractiveTabindex = defineRule({
46790
46278
  if (numeric === null) {
46791
46279
  if (isNodeOfType(tabIndexValue, "JSXExpressionContainer") && !settings.allowExpressionValues && !isKeyboardOperable(node) && !isFocusOperable(node) && !hasJsxSpreadAttribute$1(node.attributes)) context.report({
46792
46280
  node: tabIndex,
46793
- message: MESSAGE$18
46281
+ message: MESSAGE$17
46794
46282
  });
46795
46283
  return;
46796
46284
  }
@@ -46812,7 +46300,7 @@ const noNoninteractiveTabindex = defineRule({
46812
46300
  if (!roleAttribute) {
46813
46301
  context.report({
46814
46302
  node: tabIndex,
46815
- message: MESSAGE$18
46303
+ message: MESSAGE$17
46816
46304
  });
46817
46305
  return;
46818
46306
  }
@@ -46826,7 +46314,7 @@ const noNoninteractiveTabindex = defineRule({
46826
46314
  }
46827
46315
  context.report({
46828
46316
  node: tabIndex,
46829
- message: MESSAGE$18
46317
+ message: MESSAGE$17
46830
46318
  });
46831
46319
  } };
46832
46320
  }
@@ -49032,7 +48520,7 @@ const noRandomKey = defineRule({
49032
48520
  });
49033
48521
  //#endregion
49034
48522
  //#region src/plugin/rules/react-builtins/no-react-children.ts
49035
- 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.";
49036
48524
  const isChildrenIdentifier = (node, contextNode) => {
49037
48525
  if (!isNodeOfType(node, "Identifier") || node.name !== "Children") return false;
49038
48526
  return isImportedFromModule(contextNode, "Children", "react");
@@ -49058,13 +48546,13 @@ const noReactChildren = defineRule({
49058
48546
  if (isChildrenIdentifier(memberObject, node)) {
49059
48547
  context.report({
49060
48548
  node: calleeOuter,
49061
- message: MESSAGE$17
48549
+ message: MESSAGE$16
49062
48550
  });
49063
48551
  return;
49064
48552
  }
49065
48553
  if (isReactNamespaceMember(memberObject, node)) context.report({
49066
48554
  node: calleeOuter,
49067
- message: MESSAGE$17
48555
+ message: MESSAGE$16
49068
48556
  });
49069
48557
  } })
49070
48558
  });
@@ -49946,7 +49434,7 @@ const noRenderPropChildren = defineRule({
49946
49434
  });
49947
49435
  //#endregion
49948
49436
  //#region src/plugin/rules/react-builtins/no-render-return-value.ts
49949
- 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.";
49950
49438
  const isReactDomRenderCall = (node) => isGlobalMethodCall(node, "ReactDOM", "render");
49951
49439
  const isUsedAsReturnValue = (parent) => {
49952
49440
  if (!parent) return false;
@@ -49964,7 +49452,7 @@ const noRenderReturnValue = defineRule({
49964
49452
  if (!isUsedAsReturnValue(node.parent)) return;
49965
49453
  context.report({
49966
49454
  node: node.callee,
49967
- message: MESSAGE$16
49455
+ message: MESSAGE$15
49968
49456
  });
49969
49457
  } })
49970
49458
  });
@@ -51391,7 +50879,7 @@ const noSelfUpdatingEffect = defineRule({
51391
50879
  });
51392
50880
  //#endregion
51393
50881
  //#region src/plugin/rules/react-builtins/no-set-state.ts
51394
- 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.";
51395
50883
  const noSetState = defineRule({
51396
50884
  id: "no-set-state",
51397
50885
  title: "Local class state forbidden",
@@ -51406,7 +50894,7 @@ const noSetState = defineRule({
51406
50894
  if (!getParentComponent(node)) return;
51407
50895
  context.report({
51408
50896
  node: node.callee,
51409
- message: MESSAGE$15
50897
+ message: MESSAGE$14
51410
50898
  });
51411
50899
  } })
51412
50900
  });
@@ -51776,7 +51264,7 @@ const isAbstractRole = (openingElement, settings) => {
51776
51264
  };
51777
51265
  //#endregion
51778
51266
  //#region src/plugin/rules/a11y/no-static-element-interactions.ts
51779
- 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.";
51780
51268
  const DEFAULT_HANDLERS = [
51781
51269
  "onClick",
51782
51270
  "onMouseDown",
@@ -51891,7 +51379,7 @@ const noStaticElementInteractions = defineRule({
51891
51379
  if (!roleAttribute || !roleAttribute.value) {
51892
51380
  context.report({
51893
51381
  node: node.name,
51894
- message: MESSAGE$14
51382
+ message: MESSAGE$13
51895
51383
  });
51896
51384
  return;
51897
51385
  }
@@ -51901,7 +51389,7 @@ const noStaticElementInteractions = defineRule({
51901
51389
  if (isRecognizedRoleString(attributeValue.value)) return;
51902
51390
  context.report({
51903
51391
  node: node.name,
51904
- message: MESSAGE$14
51392
+ message: MESSAGE$13
51905
51393
  });
51906
51394
  return;
51907
51395
  }
@@ -51909,7 +51397,7 @@ const noStaticElementInteractions = defineRule({
51909
51397
  if (!settings.allowExpressionValues) {
51910
51398
  context.report({
51911
51399
  node: node.name,
51912
- message: MESSAGE$14
51400
+ message: MESSAGE$13
51913
51401
  });
51914
51402
  return;
51915
51403
  }
@@ -51917,7 +51405,7 @@ const noStaticElementInteractions = defineRule({
51917
51405
  if (isStaticNullishExpression(expression)) {
51918
51406
  context.report({
51919
51407
  node: node.name,
51920
- message: MESSAGE$14
51408
+ message: MESSAGE$13
51921
51409
  });
51922
51410
  return;
51923
51411
  }
@@ -51927,7 +51415,7 @@ const noStaticElementInteractions = defineRule({
51927
51415
  if (branches.some((branch) => branch.role !== null && isRecognizedRoleString(branch.role))) return;
51928
51416
  context.report({
51929
51417
  node: node.name,
51930
- message: MESSAGE$14
51418
+ message: MESSAGE$13
51931
51419
  });
51932
51420
  return;
51933
51421
  }
@@ -51935,7 +51423,7 @@ const noStaticElementInteractions = defineRule({
51935
51423
  }
51936
51424
  context.report({
51937
51425
  node: node.name,
51938
- message: MESSAGE$14
51426
+ message: MESSAGE$13
51939
51427
  });
51940
51428
  } };
51941
51429
  }
@@ -52041,7 +51529,7 @@ const noStringRefs = defineRule({
52041
51529
  });
52042
51530
  //#endregion
52043
51531
  //#region src/plugin/rules/js-performance/no-sync-xhr.ts
52044
- 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)`).";
52045
51533
  const isFalseLiteral = (node) => isNodeOfType(node, "Literal") && node.value === false;
52046
51534
  const PUBLIC_ASSET_PATH_PATTERN = /(?:^|\/)public\//i;
52047
51535
  const noSyncXhr = defineRule({
@@ -52062,7 +51550,7 @@ const noSyncXhr = defineRule({
52062
51550
  if (!asyncArgument || !isFalseLiteral(stripParenExpression(asyncArgument))) return;
52063
51551
  context.report({
52064
51552
  node,
52065
- message: MESSAGE$13
51553
+ message: MESSAGE$12
52066
51554
  });
52067
51555
  };
52068
51556
  return {
@@ -52087,7 +51575,7 @@ const noSyncXhr = defineRule({
52087
51575
  });
52088
51576
  //#endregion
52089
51577
  //#region src/plugin/rules/react-builtins/no-this-in-sfc.ts
52090
- 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`.";
52091
51579
  const isInsideClassMethod = (node, customClassFactoryNames) => {
52092
51580
  let ancestor = node.parent;
52093
51581
  while (ancestor) {
@@ -52175,7 +51663,7 @@ const noThisInSfc = defineRule({
52175
51663
  if (functionHasOwnThisMemberWrite(enclosingFunction)) return;
52176
51664
  context.report({
52177
51665
  node,
52178
- message: MESSAGE$12
51666
+ message: MESSAGE$11
52179
51667
  });
52180
51668
  } };
52181
51669
  }
@@ -54198,7 +53686,7 @@ const isElementTypeJsxAttribute = (node) => {
54198
53686
  const attributeName = node.name.name;
54199
53687
  return ELEMENT_TYPE_PROP_NAMES.has(attributeName.toLowerCase()) || attributeName.endsWith("Component");
54200
53688
  };
54201
- 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, {
54202
53690
  allowGlobalReactNamespace: true,
54203
53691
  resolveNamedAliases: true
54204
53692
  });
@@ -54222,7 +53710,7 @@ const isRenderFlowingReadReference = (identifier, scopes, visitedSymbols = /* @_
54222
53710
  case "ArrowFunctionExpression": return false;
54223
53711
  case "CallExpression":
54224
53712
  if (parent.callee === valueNode) return false;
54225
- if (isReactUseMemoCallback$1(parent, valueNode, scopes)) return false;
53713
+ if (isReactUseMemoCallback(parent, valueNode, scopes)) return false;
54226
53714
  if (isReactCreateElementCall(parent, scopes)) return true;
54227
53715
  valueNode = parent;
54228
53716
  parent = parent.parent;
@@ -54545,7 +54033,7 @@ const noWideLetterSpacing = defineRule({
54545
54033
  //#endregion
54546
54034
  //#region src/plugin/rules/react-builtins/no-will-update-set-state.ts
54547
54035
  const LIFECYCLE_NAMES = new Set(["componentWillUpdate", "UNSAFE_componentWillUpdate"]);
54548
- 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.";
54549
54037
  const resolveSettings$7 = (settings) => {
54550
54038
  const reactDoctor = settings?.["react-doctor"];
54551
54039
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noWillUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -54579,7 +54067,7 @@ const noWillUpdateSetState = defineRule({
54579
54067
  if (!isSetStateCallInLifecycle(node, activeLifecycleNames, { disallowInNestedFunctions: mode === "disallow-in-func" })) return;
54580
54068
  context.report({
54581
54069
  node: node.callee,
54582
- message: MESSAGE$11
54070
+ message: MESSAGE$10
54583
54071
  });
54584
54072
  } };
54585
54073
  }
@@ -56379,7 +55867,7 @@ const preactNoRenderArguments = defineRule({
56379
55867
  });
56380
55868
  //#endregion
56381
55869
  //#region src/plugin/rules/preact/preact-prefer-ondblclick.ts
56382
- 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.";
56383
55871
  const preactPreferOndblclick = defineRule({
56384
55872
  id: "preact-prefer-ondblclick",
56385
55873
  title: "onDoubleClick instead of onDblClick",
@@ -56394,7 +55882,7 @@ const preactPreferOndblclick = defineRule({
56394
55882
  if (!onDoubleClickAttribute) return;
56395
55883
  context.report({
56396
55884
  node: onDoubleClickAttribute,
56397
- message: MESSAGE$10
55885
+ message: MESSAGE$9
56398
55886
  });
56399
55887
  } })
56400
55888
  });
@@ -56634,7 +56122,7 @@ const preferExplicitVariants = defineRule({
56634
56122
  });
56635
56123
  //#endregion
56636
56124
  //#region src/plugin/rules/react-builtins/prefer-function-component.ts
56637
- 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.";
56638
56126
  const resolveSettings$4 = (settings) => {
56639
56127
  const reactDoctor = settings?.["react-doctor"];
56640
56128
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.preferFunctionComponent ?? {} : {};
@@ -56673,7 +56161,7 @@ const preferFunctionComponent = defineRule({
56673
56161
  const reportNode = node.id ?? node;
56674
56162
  context.report({
56675
56163
  node: reportNode,
56676
- message: MESSAGE$9
56164
+ message: MESSAGE$8
56677
56165
  });
56678
56166
  };
56679
56167
  return {
@@ -57994,8 +57482,7 @@ const preferUseReducer = defineRule({
57994
57482
  //#endregion
57995
57483
  //#region src/plugin/rules/security-scan/utils/is-public-debug-artifact-path.ts
57996
57484
  const LOCALE_DIRECTORY_PATTERN = /(?:^|\/)(?:locales?|i18n|lang|langs|translations?)\//i;
57997
- const PUBLIC_DEBUG_ARTIFACT_PATH_PATTERN = /(?:^|\/)(?:\.env(?:\.[^/]*)?|(?:debug|crash|trace|stack[-_]?trace|report|dump|phpinfo)(?:[-_.][^/]*)?\.(?:txt|log|json|html?)|[^/]+\.log)$/i;
57998
- 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);
57999
57486
  //#endregion
58000
57487
  //#region src/plugin/rules/security-scan/public-debug-artifact.ts
58001
57488
  const publicDebugArtifact = defineRule({
@@ -58874,7 +58361,7 @@ const reactCompilerNoManualMemoization = defineRule({
58874
58361
  });
58875
58362
  //#endregion
58876
58363
  //#region src/plugin/rules/react-builtins/react-in-jsx-scope.ts
58877
- 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.";
58878
58365
  const reactInJsxScope = defineRule({
58879
58366
  id: "react-in-jsx-scope",
58880
58367
  title: "React not in scope for JSX",
@@ -58886,21 +58373,21 @@ const reactInJsxScope = defineRule({
58886
58373
  if (findVariableInitializer(node, "React")) return;
58887
58374
  context.report({
58888
58375
  node: node.name,
58889
- message: MESSAGE$8
58376
+ message: MESSAGE$7
58890
58377
  });
58891
58378
  },
58892
58379
  JSXFragment(node) {
58893
58380
  if (findVariableInitializer(node, "React")) return;
58894
58381
  context.report({
58895
58382
  node: node.openingFragment,
58896
- message: MESSAGE$8
58383
+ message: MESSAGE$7
58897
58384
  });
58898
58385
  }
58899
58386
  })
58900
58387
  });
58901
58388
  //#endregion
58902
58389
  //#region src/plugin/rules/security/react-markdown-unsanitized-raw-html.ts
58903
- 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.";
58904
58391
  const REACT_MARKDOWN_MODULE = "react-markdown";
58905
58392
  const REHYPE_RAW_MODULE = "rehype-raw";
58906
58393
  const REHYPE_SANITIZE_MODULE = "rehype-sanitize";
@@ -59052,7 +58539,7 @@ const reactMarkdownUnsanitizedRawHtml = defineRule({
59052
58539
  if (pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_SANITIZE_MODULE, context.scopes, /* @__PURE__ */ new Set())) || !hasDynamicUnsanitizedChildren(node, context.scopes)) return;
59053
58540
  context.report({
59054
58541
  node,
59055
- message: MESSAGE$7
58542
+ message: MESSAGE$6
59056
58543
  });
59057
58544
  } }))
59058
58545
  });
@@ -59108,7 +58595,7 @@ const inlineUseSelectorFunction = (callNode, aliases) => {
59108
58595
  };
59109
58596
  //#endregion
59110
58597
  //#region src/plugin/rules/state-and-effects/redux-useselector-inline-derivation.ts
59111
- const ALLOCATING_ARRAY_METHODS$1 = new Set([
58598
+ const ALLOCATING_ARRAY_METHODS = new Set([
59112
58599
  "filter",
59113
58600
  "map",
59114
58601
  "flatMap",
@@ -59144,7 +58631,7 @@ const getAllocatingCallSiteDescription = (expression) => {
59144
58631
  method: methodName
59145
58632
  };
59146
58633
  }
59147
- if (ALLOCATING_ARRAY_METHODS$1.has(methodName)) return {
58634
+ if (ALLOCATING_ARRAY_METHODS.has(methodName)) return {
59148
58635
  kind: "method",
59149
58636
  method: methodName
59150
58637
  };
@@ -59207,7 +58694,7 @@ const reduxUseselectorInlineDerivation = defineRule({
59207
58694
  });
59208
58695
  //#endregion
59209
58696
  //#region src/plugin/rules/state-and-effects/redux-useselector-returns-new-collection.ts
59210
- 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.";
59211
58698
  const isConciseBodyReturningCollection = (functionNode) => {
59212
58699
  if (!isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode, "FunctionExpression")) return false;
59213
58700
  const rawBody = functionNode.body;
@@ -59242,567 +58729,13 @@ const reduxUseselectorReturnsNewCollection = defineRule({
59242
58729
  if (!isConciseBodyReturningCollection(selectorArgument)) return;
59243
58730
  context.report({
59244
58731
  node,
59245
- message: MESSAGE$6
58732
+ message: MESSAGE$5
59246
58733
  });
59247
58734
  }
59248
58735
  };
59249
58736
  }
59250
58737
  });
59251
58738
  //#endregion
59252
- //#region src/plugin/utils/is-remotion-module-source.ts
59253
- const isRemotionModuleSource = (moduleSource) => moduleSource === "remotion" || moduleSource.startsWith("@remotion/");
59254
- //#endregion
59255
- //#region src/plugin/utils/resolve-remotion-api.ts
59256
- const resolveRemotionApi = (referenceNode, scopes) => {
59257
- const candidate = stripParenExpression(referenceNode);
59258
- if (isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "JSXIdentifier")) {
59259
- const symbol = scopes.symbolFor(candidate);
59260
- if (symbol?.kind !== "import") return null;
59261
- const importBinding = getImportBindingForName(candidate, symbol.name);
59262
- if (!importBinding || importBinding.isNamespace || importBinding.exportedName === null || !isRemotionModuleSource(importBinding.source)) return null;
59263
- return {
59264
- apiName: importBinding.exportedName,
59265
- moduleSource: importBinding.source
59266
- };
59267
- }
59268
- if (isNodeOfType(candidate, "MemberExpression")) {
59269
- const apiName = getStaticPropertyKeyName(candidate, { allowComputedString: true });
59270
- const namespaceObject = stripParenExpression(candidate.object);
59271
- if (!apiName || !isNodeOfType(namespaceObject, "Identifier")) return null;
59272
- const symbol = scopes.symbolFor(namespaceObject);
59273
- if (symbol?.kind !== "import") return null;
59274
- const importBinding = getImportBindingForName(namespaceObject, symbol.name);
59275
- if (!importBinding?.isNamespace || !isRemotionModuleSource(importBinding.source)) return null;
59276
- return {
59277
- apiName,
59278
- moduleSource: importBinding.source
59279
- };
59280
- }
59281
- if (!isNodeOfType(candidate, "JSXMemberExpression") || !isNodeOfType(candidate.object, "JSXIdentifier") || !isNodeOfType(candidate.property, "JSXIdentifier")) return null;
59282
- const symbol = scopes.symbolFor(candidate.object);
59283
- if (symbol?.kind !== "import") return null;
59284
- const importBinding = getImportBindingForName(candidate.object, symbol.name);
59285
- if (!importBinding?.isNamespace || !isRemotionModuleSource(importBinding.source)) return null;
59286
- return {
59287
- apiName: candidate.property.name,
59288
- moduleSource: importBinding.source
59289
- };
59290
- };
59291
- //#endregion
59292
- //#region src/plugin/utils/create-remotion-composition-ownership-analyzer.ts
59293
- const compositionAttributeFunctionCacheBySettings = /* @__PURE__ */ new WeakMap();
59294
- const getFunctionExportKeys = (filePath, programNode, functionNode) => getFunctionExportNames(programNode, functionNode).map((exportedName) => `${normalizeFilename(filePath)}\0${exportedName}`);
59295
- const resolveImportedCompositionFunction = (expression, module) => {
59296
- const unwrappedExpression = stripParenExpression(expression);
59297
- let importReference;
59298
- let exportedName;
59299
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
59300
- const symbol = module.scopes.symbolFor(unwrappedExpression);
59301
- if (symbol?.kind !== "import") return null;
59302
- importReference = unwrappedExpression;
59303
- exportedName = getImportBindingForName(unwrappedExpression, symbol.name)?.exportedName ?? null;
59304
- } else if (isNodeOfType(unwrappedExpression, "MemberExpression") && isNodeOfType(stripParenExpression(unwrappedExpression.object), "Identifier")) {
59305
- const namespaceObject = stripParenExpression(unwrappedExpression.object);
59306
- if (!isNodeOfType(namespaceObject, "Identifier")) return null;
59307
- const symbol = module.scopes.symbolFor(namespaceObject);
59308
- if (symbol?.kind !== "import") return null;
59309
- if (!getImportBindingForName(namespaceObject, symbol.name)?.isNamespace) return null;
59310
- importReference = namespaceObject;
59311
- exportedName = getStaticPropertyKeyName(unwrappedExpression, { allowComputedString: true });
59312
- } else return null;
59313
- if (!exportedName) return null;
59314
- const importBinding = getImportBindingForName(importReference, importReference.name);
59315
- if (!importBinding) return null;
59316
- return resolveCrossFileFunctionExportWithFilePath(module.filePath, importBinding.source, exportedName);
59317
- };
59318
- const collectCompositionAttributeFunctionKeys = (context, currentProgram, attributeName) => {
59319
- const filename = context.filename ? normalizeFilename(context.filename) : "";
59320
- const rootDirectorySetting = getReactDoctorStringSetting(context.settings, "rootDirectory");
59321
- const rootDirectory = rootDirectorySetting ? normalizeFilename(rootDirectorySetting).replace(/\/$/, "") : "";
59322
- if (!filename || !rootDirectory || filename !== rootDirectory && !filename.startsWith(`${rootDirectory}/`)) return null;
59323
- const projectIndex = buildSourceProjectIndex(rootDirectory, filename, currentProgram, context.scopes);
59324
- if (!projectIndex) return null;
59325
- const functionKeys = /* @__PURE__ */ new Set();
59326
- for (const module of projectIndex.modulesByFilePath.values()) walkAst(module.programNode, (candidate) => {
59327
- if (!isNodeOfType(candidate, "JSXOpeningElement")) return;
59328
- const apiBinding = resolveRemotionApi(candidate.name, module.scopes);
59329
- if (apiBinding?.apiName !== "Composition" || apiBinding.moduleSource !== "remotion") return;
59330
- const functionAttribute = findJsxAttribute(candidate.attributes, attributeName);
59331
- if (!functionAttribute?.value || !isNodeOfType(functionAttribute.value, "JSXExpressionContainer") || !functionAttribute.value.expression) return;
59332
- const resolvedFunction = resolveImportedCompositionFunction(functionAttribute.value.expression, module);
59333
- if (!resolvedFunction || !isNodeOfType(resolvedFunction.programNode, "Program")) return;
59334
- for (const functionKey of getFunctionExportKeys(resolvedFunction.filePath, resolvedFunction.programNode, resolvedFunction.functionNode)) functionKeys.add(functionKey);
59335
- });
59336
- return functionKeys;
59337
- };
59338
- const createRemotionCompositionAttributeOwnershipAnalyzer = (context, attributeName) => {
59339
- const settings = context.settings;
59340
- return (functionNode) => {
59341
- const currentProgram = findProgramRoot(functionNode);
59342
- if (!currentProgram || !isNodeOfType(currentProgram, "Program") || !context.filename || !settings) return false;
59343
- const currentFunctionKeys = getFunctionExportKeys(context.filename, currentProgram, functionNode);
59344
- if (currentFunctionKeys.length === 0) return false;
59345
- let cacheByAttributeName = compositionAttributeFunctionCacheBySettings.get(settings);
59346
- if (!cacheByAttributeName) {
59347
- cacheByAttributeName = /* @__PURE__ */ new Map();
59348
- compositionAttributeFunctionCacheBySettings.set(settings, cacheByAttributeName);
59349
- }
59350
- let cache = cacheByAttributeName.get(attributeName);
59351
- if (!cache) {
59352
- cache = { failedFilenames: /* @__PURE__ */ new Set() };
59353
- cacheByAttributeName.set(attributeName, cache);
59354
- }
59355
- let functionKeys = cache.functionKeys;
59356
- if (!functionKeys) {
59357
- const filename = normalizeFilename(context.filename);
59358
- if (cache.failedFilenames.has(filename)) return false;
59359
- const collectedFunctionKeys = collectCompositionAttributeFunctionKeys(context, currentProgram, attributeName);
59360
- if (!collectedFunctionKeys) {
59361
- cache.failedFilenames.add(filename);
59362
- return false;
59363
- }
59364
- functionKeys = collectedFunctionKeys;
59365
- cache.functionKeys = collectedFunctionKeys;
59366
- }
59367
- return currentFunctionKeys.some((functionKey) => functionKeys.has(functionKey));
59368
- };
59369
- };
59370
- const createRemotionCompositionOwnershipAnalyzer = (context) => createRemotionCompositionAttributeOwnershipAnalyzer(context, "component");
59371
- const createRemotionMetadataOwnershipAnalyzer = (context) => createRemotionCompositionAttributeOwnershipAnalyzer(context, "calculateMetadata");
59372
- //#endregion
59373
- //#region src/plugin/rules/correctness/remotion-calculate-metadata-fetch-signal.ts
59374
- const getParameterIdentifier = (parameter) => {
59375
- if (isNodeOfType(parameter, "Identifier")) return parameter;
59376
- if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
59377
- return null;
59378
- };
59379
- const getAbortSignalBinding = (functionNode) => {
59380
- if (!isFunctionLike$1(functionNode)) return {
59381
- parameterIdentifier: null,
59382
- signalIdentifier: null
59383
- };
59384
- const firstParameter = functionNode.params[0];
59385
- const parameterIdentifier = getParameterIdentifier(firstParameter);
59386
- if (parameterIdentifier) return {
59387
- parameterIdentifier,
59388
- signalIdentifier: null
59389
- };
59390
- if (!isNodeOfType(firstParameter, "ObjectPattern")) return {
59391
- parameterIdentifier: null,
59392
- signalIdentifier: null
59393
- };
59394
- for (const property of firstParameter.properties) {
59395
- if (!isNodeOfType(property, "Property")) continue;
59396
- if (getStaticPropertyKeyName(property) !== "abortSignal") continue;
59397
- const signalIdentifier = getParameterIdentifier(property.value);
59398
- if (signalIdentifier) return {
59399
- parameterIdentifier: null,
59400
- signalIdentifier
59401
- };
59402
- }
59403
- return {
59404
- parameterIdentifier: null,
59405
- signalIdentifier: null
59406
- };
59407
- };
59408
- const identifiersResolveToSameSymbol = (leftIdentifier, rightIdentifier, scopes) => {
59409
- if (!isNodeOfType(leftIdentifier, "Identifier") || !isNodeOfType(rightIdentifier, "Identifier")) return false;
59410
- const leftSymbol = scopes.symbolFor(leftIdentifier);
59411
- return Boolean(leftSymbol && leftSymbol === scopes.symbolFor(rightIdentifier));
59412
- };
59413
- const isMetadataAbortSignal = (expression, binding, scopes) => {
59414
- const candidate = stripParenExpression(expression);
59415
- if (binding.signalIdentifier && identifiersResolveToSameSymbol(candidate, binding.signalIdentifier, scopes)) return true;
59416
- return Boolean(binding.parameterIdentifier && isNodeOfType(candidate, "MemberExpression") && getStaticPropertyKeyName(candidate, { allowComputedString: true }) === "abortSignal" && identifiersResolveToSameSymbol(candidate.object, binding.parameterIdentifier, scopes));
59417
- };
59418
- const fetchUsesMetadataAbortSignal = (fetchCall, binding, scopes) => {
59419
- const optionsArgument = fetchCall.arguments[1];
59420
- if (!optionsArgument) return false;
59421
- const options = stripParenExpression(optionsArgument);
59422
- if (!isNodeOfType(options, "ObjectExpression")) return null;
59423
- if (options.properties.some((property) => isNodeOfType(property, "SpreadElement"))) return null;
59424
- for (let propertyIndex = options.properties.length - 1; propertyIndex >= 0; propertyIndex -= 1) {
59425
- const property = options.properties[propertyIndex];
59426
- if (isNodeOfType(property, "Property") && getStaticPropertyKeyName(property, { allowComputedString: true }) === "signal") return isMetadataAbortSignal(property.value, binding, scopes);
59427
- }
59428
- return false;
59429
- };
59430
- const reportFetchesWithoutAbortSignal = (functionNode, context) => {
59431
- const binding = getAbortSignalBinding(functionNode);
59432
- walkAst(functionNode, (candidate) => {
59433
- if (candidate !== functionNode && isFunctionLike$1(candidate)) return false;
59434
- if (!isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "Identifier") || candidate.callee.name !== "fetch" || !context.scopes.isGlobalReference(candidate.callee) || fetchUsesMetadataAbortSignal(candidate, binding, context.scopes) !== false) return;
59435
- context.report({
59436
- node: candidate,
59437
- message: "Pass Remotion's `abortSignal` to this fetch with `{signal: abortSignal}` so superseded metadata requests are cancelled."
59438
- });
59439
- });
59440
- };
59441
- const isCalculateMetadataFunctionType = (variableDeclarator) => {
59442
- if (!isNodeOfType(variableDeclarator.id, "Identifier")) return false;
59443
- const annotation = variableDeclarator.id.typeAnnotation;
59444
- if (!isNodeOfType(annotation, "TSTypeAnnotation") || !isNodeOfType(annotation.typeAnnotation, "TSTypeReference")) return false;
59445
- const typeName = annotation.typeAnnotation.typeName;
59446
- if (!isNodeOfType(typeName, "Identifier")) return false;
59447
- const apiBinding = getImportBindingForName(typeName, typeName.name);
59448
- return Boolean(apiBinding?.exportedName === "CalculateMetadataFunction" && apiBinding.source === "remotion");
59449
- };
59450
- const remotionCalculateMetadataFetchSignal = defineRule({
59451
- id: "remotion-calculate-metadata-fetch-signal",
59452
- title: "calculateMetadata fetch ignores abortSignal",
59453
- tags: ["react-jsx-only"],
59454
- requires: ["remotion:4"],
59455
- severity: "error",
59456
- recommendation: "Destructure `abortSignal` from the calculateMetadata argument and pass it to direct fetch calls as `{signal: abortSignal}`.",
59457
- create: (context) => {
59458
- const analyzedFunctions = /* @__PURE__ */ new WeakSet();
59459
- const isOwnedByCalculateMetadata = createRemotionMetadataOwnershipAnalyzer(context);
59460
- const analyzeFunction = (functionNode) => {
59461
- if (!functionNode || analyzedFunctions.has(functionNode)) return;
59462
- analyzedFunctions.add(functionNode);
59463
- reportFetchesWithoutAbortSignal(functionNode, context);
59464
- };
59465
- return {
59466
- CallExpression(node) {
59467
- if (!isNodeOfType(node.callee, "Identifier") || node.callee.name !== "fetch" || !context.scopes.isGlobalReference(node.callee)) return;
59468
- const functionNode = findEnclosingFunction$1(node);
59469
- if (functionNode && isOwnedByCalculateMetadata(functionNode)) analyzeFunction(functionNode);
59470
- },
59471
- JSXOpeningElement(node) {
59472
- const apiBinding = resolveRemotionApi(node.name, context.scopes);
59473
- if (apiBinding?.apiName !== "Composition" || apiBinding.moduleSource !== "remotion") return;
59474
- const calculateMetadataAttribute = findJsxAttribute(node.attributes, "calculateMetadata");
59475
- if (!calculateMetadataAttribute?.value || !isNodeOfType(calculateMetadataAttribute.value, "JSXExpressionContainer") || !calculateMetadataAttribute.value.expression) return;
59476
- analyzeFunction(resolveExactLocalFunction(calculateMetadataAttribute.value.expression, context.scopes));
59477
- },
59478
- VariableDeclarator(node) {
59479
- if (!node.init || !isCalculateMetadataFunctionType(node)) return;
59480
- analyzeFunction(resolveExactLocalFunction(node.init, context.scopes));
59481
- }
59482
- };
59483
- }
59484
- });
59485
- //#endregion
59486
- //#region src/plugin/utils/create-remotion-render-evidence-checker.ts
59487
- const REMOTION_RENDER_CALL_NAMES = new Set([
59488
- "continueRender",
59489
- "delayRender",
59490
- "getInputProps",
59491
- "random",
59492
- "spring",
59493
- "useCurrentFrame",
59494
- "useDelayRender",
59495
- "useVideoConfig"
59496
- ]);
59497
- const REMOTION_RENDER_COMPONENT_MODULE_BY_NAME = new Map([
59498
- ["Audio", "@remotion/media"],
59499
- ["Freeze", "remotion"],
59500
- ["IFrame", "remotion"],
59501
- ["Img", "remotion"],
59502
- ["Loop", "remotion"],
59503
- ["OffthreadVideo", "remotion"],
59504
- ["Sequence", "remotion"],
59505
- ["Series", "remotion"],
59506
- ["Video", "@remotion/media"]
59507
- ]);
59508
- const createRemotionRenderEvidenceChecker = (context) => {
59509
- const scopes = context.scopes;
59510
- const evidenceByFunction = /* @__PURE__ */ new WeakMap();
59511
- const registeredCompositionFunctions = /* @__PURE__ */ new WeakSet();
59512
- const inspectedPrograms = /* @__PURE__ */ new WeakSet();
59513
- const isOwnedByRegisteredComposition = createRemotionCompositionOwnershipAnalyzer(context);
59514
- const collectRegisteredCompositionFunctions = (functionNode) => {
59515
- const program = findProgramRoot(functionNode);
59516
- if (!program || inspectedPrograms.has(program)) return;
59517
- inspectedPrograms.add(program);
59518
- walkAst(program, (candidate) => {
59519
- if (!isNodeOfType(candidate, "JSXOpeningElement")) return;
59520
- const apiBinding = resolveRemotionApi(candidate.name, scopes);
59521
- if (apiBinding?.apiName !== "Composition" || apiBinding.moduleSource !== "remotion") return;
59522
- const componentAttribute = findJsxAttribute(candidate.attributes, "component");
59523
- if (!componentAttribute?.value || !isNodeOfType(componentAttribute.value, "JSXExpressionContainer") || !componentAttribute.value.expression) return;
59524
- const registeredFunction = resolveExactLocalFunction(componentAttribute.value.expression, scopes);
59525
- if (registeredFunction) registeredCompositionFunctions.add(registeredFunction);
59526
- });
59527
- };
59528
- const functionUsesRemotionRenderApi = (functionNode) => {
59529
- let hasEvidence = false;
59530
- walkAst(functionNode, (candidate) => {
59531
- if (hasEvidence) return false;
59532
- if (!isNodeOfType(candidate, "CallExpression") && !isNodeOfType(candidate, "JSXOpeningElement")) return;
59533
- const apiBinding = resolveRemotionApi(isNodeOfType(candidate, "CallExpression") ? candidate.callee : candidate.name, scopes);
59534
- 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) {
59535
- hasEvidence = true;
59536
- return false;
59537
- }
59538
- });
59539
- return hasEvidence;
59540
- };
59541
- return { functionHasEvidence: (functionNode) => {
59542
- const cachedEvidence = evidenceByFunction.get(functionNode);
59543
- if (cachedEvidence !== void 0) return cachedEvidence;
59544
- collectRegisteredCompositionFunctions(functionNode);
59545
- const hasEvidence = registeredCompositionFunctions.has(functionNode) || functionUsesRemotionRenderApi(functionNode) || isOwnedByRegisteredComposition(functionNode);
59546
- evidenceByFunction.set(functionNode, hasEvidence);
59547
- return hasEvidence;
59548
- } };
59549
- };
59550
- //#endregion
59551
- //#region src/plugin/rules/correctness/remotion-deterministic-randomness.ts
59552
- const isGlobalMathObject = (node, scopes) => {
59553
- const candidate = stripParenExpression(node);
59554
- if (isNodeOfType(candidate, "Identifier")) return candidate.name === "Math" && scopes.isGlobalReference(candidate);
59555
- return Boolean(isNodeOfType(candidate, "MemberExpression") && getStaticPropertyKeyName(candidate, { allowComputedString: true }) === "Math" && isNodeOfType(candidate.object, "Identifier") && candidate.object.name === "globalThis" && scopes.isGlobalReference(candidate.object));
59556
- };
59557
- const remotionDeterministicRandomness = defineRule({
59558
- id: "remotion-deterministic-randomness",
59559
- title: "Randomness changes between rendered frames",
59560
- tags: ["react-jsx-only"],
59561
- requires: ["remotion:4"],
59562
- severity: "error",
59563
- recommendation: "Use Remotion's seeded `random(seed)` helper so the same frame produces the same value in every render tab.",
59564
- create: (context) => {
59565
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59566
- return { CallExpression(node) {
59567
- const callee = stripParenExpression(node.callee);
59568
- if (!isNodeOfType(callee, "MemberExpression") || getStaticPropertyKeyName(callee, { allowComputedString: true }) !== "random" || !isGlobalMathObject(callee.object, context.scopes)) return;
59569
- const componentOrHook = findRenderPhaseComponentOrHook(node, context.scopes) ?? findEnclosingFunction$1(node);
59570
- if (!componentOrHook || !renderEvidence.functionHasEvidence(componentOrHook)) return;
59571
- const displayName = componentOrHookDisplayNameForFunction(componentOrHook);
59572
- if (displayName && !isReactHookName(displayName) && !functionHasReactComponentEvidence(componentOrHook, context.scopes, context.cfg)) return;
59573
- context.report({
59574
- node,
59575
- 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."
59576
- });
59577
- } };
59578
- }
59579
- });
59580
- //#endregion
59581
- //#region src/plugin/utils/create-remotion-css-time-rule-visitors.ts
59582
- const createRemotionCssTimeRuleVisitors = (context, options) => {
59583
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59584
- return {
59585
- Property(node) {
59586
- if (!options.stylePropertyNames.has(getStaticPropertyKeyName(node) ?? "")) return;
59587
- const styleObject = node.parent;
59588
- const expressionContainer = styleObject?.parent;
59589
- const styleAttribute = expressionContainer?.parent;
59590
- if (!isNodeOfType(styleObject, "ObjectExpression") || !isNodeOfType(expressionContainer, "JSXExpressionContainer") || expressionContainer.expression !== styleObject || !isNodeOfType(styleAttribute, "JSXAttribute") || !isNodeOfType(styleAttribute.name, "JSXIdentifier") || styleAttribute.name.name !== "style") return;
59591
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
59592
- if (!renderFunction || !renderEvidence.functionHasEvidence(renderFunction)) return;
59593
- context.report({
59594
- node,
59595
- message: options.styleMessage
59596
- });
59597
- },
59598
- JSXAttribute(node) {
59599
- if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "className") return;
59600
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
59601
- if (!renderFunction || !renderEvidence.functionHasEvidence(renderFunction)) return;
59602
- const className = getJsxAttributeStaticString(node);
59603
- if (!className) return;
59604
- if (className.split(/\s+/).filter(Boolean).some((classToken) => options.classTokenIsForbidden(classToken.split(":").at(-1) ?? ""))) context.report({
59605
- node,
59606
- message: options.classMessage
59607
- });
59608
- }
59609
- };
59610
- };
59611
- //#endregion
59612
- //#region src/plugin/rules/correctness/remotion-no-css-animation.ts
59613
- const ANIMATION_STYLE_PROPERTY_NAMES = new Set(["animation", "animationName"]);
59614
- const remotionNoCssAnimation = defineRule({
59615
- id: "remotion-no-css-animation",
59616
- title: "CSS animation is not frame-driven",
59617
- tags: ["react-jsx-only"],
59618
- requires: ["remotion:4"],
59619
- severity: "error",
59620
- recommendation: "Drive the property from `useCurrentFrame()` with `interpolate()` so every rendered frame is deterministic.",
59621
- create: (context) => createRemotionCssTimeRuleVisitors(context, {
59622
- classTokenIsForbidden: (classToken) => classToken.startsWith("animate-") && classToken !== "animate-none",
59623
- classMessage: "Tailwind animations advance on browser time, so Remotion can capture inconsistent frames. Drive the property from `useCurrentFrame()` instead.",
59624
- styleMessage: "CSS animations advance on browser time, so Remotion can capture inconsistent frames. Drive the property from `useCurrentFrame()` instead.",
59625
- stylePropertyNames: ANIMATION_STYLE_PROPERTY_NAMES
59626
- })
59627
- });
59628
- //#endregion
59629
- //#region src/plugin/rules/correctness/remotion-no-css-transition.ts
59630
- const TRANSITION_STYLE_PROPERTY_NAMES = new Set(["transition", "transitionProperty"]);
59631
- const remotionNoCssTransition = defineRule({
59632
- id: "remotion-no-css-transition",
59633
- title: "CSS transition is not frame-driven",
59634
- tags: ["react-jsx-only"],
59635
- requires: ["remotion:4"],
59636
- severity: "error",
59637
- recommendation: "Drive the property from `useCurrentFrame()` with `interpolate()` so every rendered frame is deterministic.",
59638
- create: (context) => createRemotionCssTimeRuleVisitors(context, {
59639
- classTokenIsForbidden: (classToken) => (classToken === "transition" || classToken.startsWith("transition-")) && classToken !== "transition-none",
59640
- classMessage: "Tailwind transitions advance on browser time, so Remotion can capture inconsistent frames. Drive the property from `useCurrentFrame()` instead.",
59641
- styleMessage: "CSS transitions advance on browser time, so Remotion can capture inconsistent frames. Drive the property from `useCurrentFrame()` instead.",
59642
- stylePropertyNames: TRANSITION_STYLE_PROPERTY_NAMES
59643
- })
59644
- });
59645
- //#endregion
59646
- //#region src/plugin/rules/correctness/remotion-no-css-url-assets.ts
59647
- const CSS_URL_ASSET_PROPERTY_NAMES = new Set([
59648
- "backgroundImage",
59649
- "maskImage",
59650
- "WebkitMaskImage"
59651
- ]);
59652
- const CSS_URL_PATTERN = /\burl\(\s*(["']?)([^"')]+)\1\s*\)/i;
59653
- const isEmbeddedAssetSource = (assetSource) => assetSource.startsWith("data:") || assetSource.startsWith("#");
59654
- const getStaticStringExpression = (node) => {
59655
- if (isNodeOfType(node, "Literal") && typeof node.value === "string") return node.value;
59656
- if (isNodeOfType(node, "TemplateLiteral") && node.expressions.length === 0 && node.quasis.length === 1) return node.quasis[0].value.raw;
59657
- return null;
59658
- };
59659
- const isReactUseMemoCallback = (node, scopes) => {
59660
- const parent = node.parent;
59661
- return Boolean(isNodeOfType(parent, "CallExpression") && parent.arguments[0] === node && isReactApiCall(parent, "useMemo", scopes, { resolveNamedAliases: true }));
59662
- };
59663
- const componentPreloadsStaticImage = (componentNode, assetSource, scopes) => {
59664
- let hasPreload = false;
59665
- walkAst(componentNode, (child) => {
59666
- if (hasPreload) return false;
59667
- if (child !== componentNode && isFunctionLike$1(child) && !isReactUseMemoCallback(child, scopes)) return false;
59668
- if (!isNodeOfType(child, "JSXOpeningElement")) return;
59669
- const apiBinding = resolveRemotionApi(child.name, scopes);
59670
- if (apiBinding?.apiName !== "Img" || apiBinding.moduleSource !== "remotion") return;
59671
- const sourceAttribute = findJsxAttribute(child.attributes, "src");
59672
- if (sourceAttribute && getJsxAttributeStaticString(sourceAttribute) === assetSource) {
59673
- hasPreload = true;
59674
- return false;
59675
- }
59676
- });
59677
- return hasPreload;
59678
- };
59679
- const remotionNoCssUrlAssets = defineRule({
59680
- id: "remotion-no-css-url-assets",
59681
- title: "CSS URL asset can flicker in Remotion",
59682
- tags: ["react-jsx-only"],
59683
- requires: ["remotion:4"],
59684
- severity: "error",
59685
- recommendation: "Render the asset with `Img` inside an `AbsoluteFill`, or preload the same source with a hidden `Img` when a CSS mask is required.",
59686
- create: (context) => {
59687
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59688
- return { Property(node) {
59689
- if (!CSS_URL_ASSET_PROPERTY_NAMES.has(getStaticPropertyKeyName(node) ?? "")) return;
59690
- const styleObject = node.parent;
59691
- const expressionContainer = styleObject?.parent;
59692
- const styleAttribute = expressionContainer?.parent;
59693
- if (!isNodeOfType(styleObject, "ObjectExpression") || !isNodeOfType(expressionContainer, "JSXExpressionContainer") || expressionContainer.expression !== styleObject || !isNodeOfType(styleAttribute, "JSXAttribute") || !isNodeOfType(styleAttribute.name, "JSXIdentifier") || styleAttribute.name.name !== "style") return;
59694
- const cssValue = getStaticStringExpression(node.value);
59695
- const urlMatch = cssValue ? CSS_URL_PATTERN.exec(cssValue) : null;
59696
- if (!urlMatch) return;
59697
- const assetSource = urlMatch[2].trim();
59698
- if (isEmbeddedAssetSource(assetSource)) return;
59699
- const componentNode = findRenderPhaseComponentOrHook(node, context.scopes);
59700
- if (!componentNode || !renderEvidence.functionHasEvidence(componentNode) || componentPreloadsStaticImage(componentNode, assetSource, context.scopes)) return;
59701
- context.report({
59702
- node,
59703
- 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."
59704
- });
59705
- } };
59706
- }
59707
- });
59708
- //#endregion
59709
- //#region src/plugin/rules/correctness/remotion-no-module-scope-delay-render.ts
59710
- const remotionNoModuleScopeDelayRender = defineRule({
59711
- id: "remotion-no-module-scope-delay-render",
59712
- title: "Module-scoped delayRender blocks every composition",
59713
- requires: ["remotion:4"],
59714
- severity: "error",
59715
- recommendation: "Create the handle once inside the component. Use `useDelayRender()` on Remotion 4.0.342 or newer, or lazy `useState(() => delayRender())` on earlier versions.",
59716
- create: (context) => ({ CallExpression(node) {
59717
- const apiBinding = resolveRemotionApi(node.callee, context.scopes);
59718
- if (apiBinding?.apiName !== "delayRender" || apiBinding.moduleSource !== "remotion" || findEnclosingFunction$1(node)) return;
59719
- context.report({
59720
- node,
59721
- 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."
59722
- });
59723
- } })
59724
- });
59725
- //#endregion
59726
- //#region src/plugin/rules/correctness/remotion-no-native-media-elements.ts
59727
- const REMOTION_MEDIA_REPLACEMENT_BY_TAG = new Map([
59728
- ["audio", "`Audio` from `@remotion/media`"],
59729
- ["iframe", "`IFrame` from `remotion`"],
59730
- ["img", "`Img` from `remotion`"],
59731
- ["video", "`Video` from `@remotion/media`"]
59732
- ]);
59733
- const remotionNoNativeMediaElements = defineRule({
59734
- id: "remotion-no-native-media-elements",
59735
- title: "Native media element bypasses Remotion loading",
59736
- tags: ["react-jsx-only"],
59737
- requires: ["remotion:4"],
59738
- severity: "error",
59739
- recommendation: "Use Remotion's media components so rendering waits for assets and seeks media to the requested frame.",
59740
- create: (context) => {
59741
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59742
- return { JSXOpeningElement(node) {
59743
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
59744
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
59745
- if (!renderFunction || !renderEvidence.functionHasEvidence(renderFunction)) return;
59746
- const replacement = REMOTION_MEDIA_REPLACEMENT_BY_TAG.get(node.name.name);
59747
- if (!replacement) return;
59748
- context.report({
59749
- node,
59750
- message: `Native <${node.name.name}> does not let Remotion reliably wait for and synchronize the asset. Use ${replacement} instead.`
59751
- });
59752
- } };
59753
- }
59754
- });
59755
- //#endregion
59756
- //#region src/plugin/rules/correctness/remotion-no-next-image.ts
59757
- const remotionNoNextImage = defineRule({
59758
- id: "remotion-no-next-image",
59759
- title: "Next.js Image can flicker in Remotion",
59760
- tags: ["react-jsx-only"],
59761
- requires: ["remotion:4"],
59762
- severity: "error",
59763
- recommendation: "Use `Img` from `remotion`, which delays rendering until the image is loaded.",
59764
- create: (context) => {
59765
- const renderEvidence = createRemotionRenderEvidenceChecker(context);
59766
- return { JSXOpeningElement(node) {
59767
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
59768
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
59769
- if (!renderFunction || !renderEvidence.functionHasEvidence(renderFunction)) return;
59770
- const symbol = context.scopes.symbolFor(node.name);
59771
- if (symbol?.kind !== "import") return;
59772
- const importBinding = getImportBindingForName(node.name, symbol.name);
59773
- if (importBinding?.source !== "next/image" || importBinding.exportedName !== "default") return;
59774
- context.report({
59775
- node,
59776
- message: "Next.js <Image> does not expose a reliable loading signal to Remotion, so rendered frames can flicker. Use <Img> from `remotion` instead."
59777
- });
59778
- } };
59779
- }
59780
- });
59781
- //#endregion
59782
- //#region src/plugin/rules/correctness/remotion-stable-delay-render-handle.ts
59783
- const isUseStateLazyInitializer = (node, scopes) => {
59784
- const enclosingFunction = findEnclosingFunction$1(node);
59785
- if (!enclosingFunction) return false;
59786
- const parent = enclosingFunction.parent;
59787
- return Boolean(isNodeOfType(parent, "CallExpression") && parent.arguments[0] === enclosingFunction && isReactApiCall(parent, "useState", scopes, { resolveNamedAliases: true }));
59788
- };
59789
- const remotionStableDelayRenderHandle = defineRule({
59790
- id: "remotion-stable-delay-render-handle",
59791
- title: "delayRender handle is recreated during render",
59792
- tags: ["react-jsx-only"],
59793
- requires: ["remotion:4"],
59794
- severity: "error",
59795
- recommendation: "Prefer `useDelayRender()`, or initialize `delayRender()` once with `useState(() => delayRender())`.",
59796
- create: (context) => ({ CallExpression(node) {
59797
- const apiBinding = resolveRemotionApi(node.callee, context.scopes);
59798
- if (apiBinding?.apiName !== "delayRender" || apiBinding.moduleSource !== "remotion" || !findRenderPhaseComponentOrHook(node, context.scopes) || isUseStateLazyInitializer(node, context.scopes)) return;
59799
- context.report({
59800
- node,
59801
- message: "Calling `delayRender()` during every component render creates another outstanding handle and can make rendering time out. Use `useDelayRender()` or a lazy `useState` initializer."
59802
- });
59803
- } })
59804
- });
59805
- //#endregion
59806
58739
  //#region src/plugin/rules/performance/rendering-animate-svg-wrapper.ts
59807
58740
  const renderingAnimateSvgWrapper = defineRule({
59808
58741
  id: "rendering-animate-svg-wrapper",
@@ -60923,7 +59856,7 @@ const functionBodyHasReturnWithValue = (functionNode) => {
60923
59856
  //#endregion
60924
59857
  //#region src/plugin/rules/react-builtins/require-render-return.ts
60925
59858
  const RENDER_METHOD_NAME = "render";
60926
- 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.";
60927
59860
  const isStaticRenderKey = (key) => {
60928
59861
  if (isNodeOfType(key, "Identifier")) return key.name === RENDER_METHOD_NAME;
60929
59862
  if (isNodeOfType(key, "Literal")) return key.value === RENDER_METHOD_NAME;
@@ -60983,7 +59916,7 @@ const requireRenderReturn = defineRule({
60983
59916
  if (functionBodyHasReturnWithValue(functionNode)) return;
60984
59917
  context.report({
60985
59918
  node: host.reportNode,
60986
- message: MESSAGE$5
59919
+ message: MESSAGE$4
60987
59920
  });
60988
59921
  };
60989
59922
  return {
@@ -69535,7 +68468,6 @@ const countOwnScopeHookCalls = (functionNode, scopes, settings, countedFunctionN
69535
68468
  };
69536
68469
  const MIN_HOOK_CALLS_FOR_RENDER_SCOPE = 2;
69537
68470
  const RENDER_SCOPE_FACTORY_NAME_PATTERN = /^_?(?:init|create|make|build)(?:[A-Z0-9_]|$)/;
69538
- const HOOK_NAMESPACE_NAME_PATTERN = /Hooks?$/;
69539
68471
  const isLocalNonHookFunctionCallee = (call, scopes, settings, countedFunctionNodes) => {
69540
68472
  const callee = call.callee;
69541
68473
  if (!isNodeOfType(callee, "Identifier")) return false;
@@ -69560,15 +68492,6 @@ const isPackageImportedNonReactHookCallee = (call) => {
69560
68492
  if (PATH_ALIAS_IMPORT_PATTERN.test(importSource)) return false;
69561
68493
  return !isReactEcosystemImportSource(importSource);
69562
68494
  };
69563
- const isDefaultImportedClassApiMemberCallee = (call) => {
69564
- const callee = call.callee;
69565
- if (!isNodeOfType(callee, "MemberExpression")) return false;
69566
- if (!isNodeOfType(callee.object, "Identifier")) return false;
69567
- if (HOOK_NAMESPACE_NAME_PATTERN.test(callee.object.name)) return false;
69568
- const importBinding = getImportBindingForName(call, callee.object.name);
69569
- if (!importBinding || importBinding.exportedName !== "default") return false;
69570
- return !REACT_RUNTIME_MODULE_SOURCES.has(importBinding.source);
69571
- };
69572
68495
  const isProjectOwnedMdxComponentsGetter = (call) => {
69573
68496
  const callee = call.callee;
69574
68497
  if (!isNodeOfType(callee, "Identifier") || callee.name !== "useMDXComponents") return false;
@@ -69718,7 +68641,6 @@ const rulesOfHooks = defineRule({
69718
68641
  return;
69719
68642
  }
69720
68643
  if (isInsideClassComponent(node)) {
69721
- if (isPackageImportedNonReactHookCallee(node) || isDefaultImportedClassApiMemberCallee(node)) return;
69722
68644
  context.report({
69723
68645
  node: node.callee,
69724
68646
  message: buildClassComponentMessage(hookName)
@@ -69825,7 +68747,7 @@ const rulesOfHooks = defineRule({
69825
68747
  });
69826
68748
  //#endregion
69827
68749
  //#region src/plugin/rules/a11y/scope.ts
69828
- 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.";
69829
68751
  const scope = defineRule({
69830
68752
  id: "scope",
69831
68753
  title: "scope attribute on non-th element",
@@ -69840,7 +68762,7 @@ const scope = defineRule({
69840
68762
  if (!HTML_TAGS.has(tag)) return;
69841
68763
  if (tag !== "th") context.report({
69842
68764
  node: scopeAttribute,
69843
- message: MESSAGE$4
68765
+ message: MESSAGE$3
69844
68766
  });
69845
68767
  } })
69846
68768
  });
@@ -69851,13 +68773,13 @@ const secretInFallback = defineRule({
69851
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.",
69852
68774
  scan: scanByPattern({
69853
68775
  shouldScan: (file) => isProductionSourcePath(file.relativePath),
69854
- 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,
69855
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."
69856
68778
  })
69857
68779
  });
69858
68780
  //#endregion
69859
68781
  //#region src/plugin/rules/react-builtins/self-closing-comp.ts
69860
- 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.";
69861
68783
  const resolveSettings$2 = (settings) => {
69862
68784
  const reactDoctor = settings?.["react-doctor"];
69863
68785
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.selfClosingComp ?? {} : {};
@@ -69896,7 +68818,7 @@ const selfClosingComp = defineRule({
69896
68818
  if (isHtmlTag && !settings.html) return;
69897
68819
  context.report({
69898
68820
  node: node.openingElement,
69899
- message: MESSAGE$3
68821
+ message: MESSAGE$2
69900
68822
  });
69901
68823
  } };
69902
68824
  }
@@ -70121,30 +69043,7 @@ const isAuthGuardName = (calleeName) => {
70121
69043
  //#endregion
70122
69044
  //#region src/plugin/utils/is-non-privileged-server-action.ts
70123
69045
  const NON_DATA_EFFECT_FUNCTION_NAMES = new Set([...CACHE_REVALIDATION_FUNCTION_NAMES, ...NEXTJS_NAVIGATION_FUNCTIONS]);
70124
- const LOCALE_PREFERENCE_VERB_TOKENS = new Set(["set", "update"]);
70125
- const LOCALE_PREFERENCE_NOUN_TOKENS = new Set(["language", "locale"]);
70126
- const COOKIE_DELETION_METHOD_NAME = "delete";
70127
- const isCacheOrNavigationCall = (node) => {
70128
- if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "Identifier")) return false;
70129
- return NON_DATA_EFFECT_FUNCTION_NAMES.has(node.callee.name) && getImportSourceForName(node, node.callee.name) !== null;
70130
- };
70131
- const isCallerScopedLocalePreferenceCall = (node) => {
70132
- if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "Identifier") || node.arguments?.length !== 1) return false;
70133
- const nameTokens = tokenizeIdentifierWords(node.callee.name);
70134
- if (!nameTokens.some((token) => LOCALE_PREFERENCE_VERB_TOKENS.has(token))) return false;
70135
- if (!nameTokens.some((token) => LOCALE_PREFERENCE_NOUN_TOKENS.has(token))) return false;
70136
- const importSource = getImportSourceForName(node, node.callee.name)?.toLowerCase();
70137
- return Boolean(importSource?.includes("i18n") || importSource?.includes("locale"));
70138
- };
70139
- const isCallerScopedCookieCall = (node, cookieBindingNames) => {
70140
- if (!isNodeOfType(node, "CallExpression")) return false;
70141
- if (isNodeOfType(node.callee, "Identifier")) return node.callee.name === "cookies" && getImportSourceForName(node, node.callee.name) === "next/headers";
70142
- if (!isNodeOfType(node.callee, "MemberExpression") || !isNodeOfType(node.callee.property, "Identifier") || node.callee.property.name !== COOKIE_DELETION_METHOD_NAME) return false;
70143
- const receiver = stripParenExpression(node.callee.object);
70144
- if (isNodeOfType(receiver, "Identifier")) return cookieBindingNames.has(receiver.name);
70145
- return isCookiesOrAwaitedCookiesCall(receiver) && getImportSourceForName(receiver, "cookies") === "next/headers";
70146
- };
70147
- 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;
70148
69047
  const unwrapExpression = (node) => {
70149
69048
  let current = node;
70150
69049
  while (current) {
@@ -70160,10 +69059,10 @@ const unwrapExpression = (node) => {
70160
69059
  }
70161
69060
  return null;
70162
69061
  };
70163
- const isDataExposingValue = (node, cookieBindingNames) => {
69062
+ const isDataExposingValue = (node) => {
70164
69063
  const value = unwrapExpression(node);
70165
69064
  if (!value) return false;
70166
- if (isKnownNonDataEffectCall(value, cookieBindingNames)) return false;
69065
+ if (isCacheOrNavigationCall(value)) return false;
70167
69066
  return !isLiteralOnlyExpression(value);
70168
69067
  };
70169
69068
  const isLiteralOnlyExpression = (node) => {
@@ -70181,23 +69080,22 @@ const getReturnedOrThrownArgument = (node) => {
70181
69080
  if (isNodeOfType(node, "ThrowStatement")) return node.argument ?? null;
70182
69081
  return null;
70183
69082
  };
70184
- const isDataExposingReturnOrThrow = (node, cookieBindingNames) => isDataExposingValue(getReturnedOrThrownArgument(node), cookieBindingNames);
70185
- 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);
70186
69085
  const isNonPrivilegedServerAction = (functionNode) => {
70187
69086
  const functionBody = functionNode.body;
70188
69087
  if (!functionBody) return false;
70189
- const cookieBindingNames = getImportSourceForName(functionBody, "cookies") === "next/headers" ? collectLocallyScopedCookieBindings(functionBody) : /* @__PURE__ */ new Set();
70190
- if (!isNodeOfType(functionBody, "BlockStatement") && isDataExposingValue(functionBody, cookieBindingNames)) return false;
69088
+ if (!isNodeOfType(functionBody, "BlockStatement") && isDataExposingValue(functionBody)) return false;
70191
69089
  let hasNonDataEffectCall = false;
70192
69090
  let hasPrivilegedEffect = false;
70193
69091
  walkAst(functionBody, (child) => {
70194
69092
  if (hasPrivilegedEffect) return false;
70195
69093
  if (child !== functionBody && isFunctionLike$1(child)) return false;
70196
- if (isKnownNonDataEffectCall(child, cookieBindingNames)) {
69094
+ if (isCacheOrNavigationCall(child)) {
70197
69095
  hasNonDataEffectCall = true;
70198
69096
  return;
70199
69097
  }
70200
- if (isPrivilegedEffect(child, cookieBindingNames)) {
69098
+ if (isPrivilegedEffect(child)) {
70201
69099
  hasPrivilegedEffect = true;
70202
69100
  return false;
70203
69101
  }
@@ -70276,34 +69174,6 @@ const getAuthScanRoots = (functionNode) => {
70276
69174
  if (isNodeOfType(bodyNode, "BlockStatement")) return (bodyNode.body ?? []).slice(0, 10);
70277
69175
  return [bodyNode];
70278
69176
  };
70279
- const COMPONENT_NAME_PATTERN = /^[A-Z]/;
70280
- const TEST_APP_SOURCE_PATH_PATTERN = /(?:^|[/\\])test[/\\](?:app|src)[/\\]/;
70281
- const containsJsxOutsideNestedFunctions = (rootNode) => {
70282
- let containsJsx = false;
70283
- walkAst(rootNode, (child) => {
70284
- if (containsJsx) return false;
70285
- if (child !== rootNode && isFunctionLike$1(child)) return false;
70286
- if (isNodeOfType(child, "JSXElement") || isNodeOfType(child, "JSXFragment")) {
70287
- containsJsx = true;
70288
- return false;
70289
- }
70290
- });
70291
- return containsJsx;
70292
- };
70293
- const isComponentLikeServerExport = (candidate) => {
70294
- if (!COMPONENT_NAME_PATTERN.test(candidate.displayName)) return false;
70295
- const functionBody = candidate.functionNode.body;
70296
- if (!isNodeOfType(functionBody, "BlockStatement")) return containsJsxOutsideNestedFunctions(functionBody);
70297
- let hasReturnedJsx = false;
70298
- walkAst(functionBody, (child) => {
70299
- if (hasReturnedJsx) return false;
70300
- if (child !== functionBody && isFunctionLike$1(child)) return false;
70301
- if (!isNodeOfType(child, "ReturnStatement") || !child.argument) return;
70302
- hasReturnedJsx = containsJsxOutsideNestedFunctions(child.argument);
70303
- return false;
70304
- });
70305
- return hasReturnedJsx;
70306
- };
70307
69177
  const CREDENTIAL_MERGE_TAIL_TOKENS = {
70308
69178
  sign: new Set([
70309
69179
  "in",
@@ -70368,7 +69238,6 @@ const isCredentialEstablishingActionName = (actionName) => {
70368
69238
  const hasPublicNameToken = (actionName) => tokenizeIdentifierWords(actionName).includes("public");
70369
69239
  const inspectServerAction = (candidate, fileHasUseServerDirective, allowedFunctionNames, context) => {
70370
69240
  if (!(fileHasUseServerDirective || hasUseServerDirective(candidate.functionNode))) return;
70371
- if (isComponentLikeServerExport(candidate)) return;
70372
69241
  if (isCredentialEstablishingActionName(candidate.displayName)) return;
70373
69242
  if (hasPublicNameToken(candidate.displayName)) return;
70374
69243
  if (containsAuthCheck(getAuthScanRoots(candidate.functionNode), allowedFunctionNames, GENERIC_AUTH_METHOD_NAMES)) return;
@@ -70406,42 +69275,18 @@ const serverAuthActions = defineRule({
70406
69275
  title: "Unauthenticated server action can be called directly",
70407
69276
  severity: "error",
70408
69277
  recommendation: "Check auth before touching data because exported server actions can be called directly by unauthenticated clients.",
70409
- create: skipNonProductionFiles((context) => {
70410
- const shouldSkipTestAppSource = Boolean(context.filename && TEST_APP_SOURCE_PATH_PATTERN.test(context.filename));
69278
+ create: (context) => {
70411
69279
  let fileHasUseServerDirective = false;
70412
- let programNode = null;
70413
- const inspectedFunctions = /* @__PURE__ */ new Set();
70414
69280
  const customAuthFunctionNames = getReactDoctorStringArraySetting(context.settings, "serverAuthFunctionNames");
70415
69281
  const allowedFunctionNames = customAuthFunctionNames.length > 0 ? new Set([...AUTH_FUNCTION_NAMES, ...customAuthFunctionNames]) : AUTH_FUNCTION_NAMES;
70416
- const inspect = (candidate) => {
70417
- if (inspectedFunctions.has(candidate.functionNode)) return;
70418
- inspectedFunctions.add(candidate.functionNode);
70419
- inspectServerAction(candidate, fileHasUseServerDirective, allowedFunctionNames, context);
70420
- };
69282
+ const inspect = (candidate) => inspectServerAction(candidate, fileHasUseServerDirective, allowedFunctionNames, context);
70421
69283
  return {
70422
- Program(currentProgramNode) {
70423
- programNode = currentProgramNode;
70424
- fileHasUseServerDirective = hasDirective(currentProgramNode, "use server");
69284
+ Program(programNode) {
69285
+ fileHasUseServerDirective = hasDirective(programNode, "use server");
70425
69286
  },
70426
69287
  ExportNamedDeclaration(node) {
70427
- if (shouldSkipTestAppSource) return;
70428
69288
  const declaration = node.declaration;
70429
- if (!declaration) {
70430
- if (!programNode || node.source || node.exportKind === "type") return;
70431
- for (const specifier of node.specifiers ?? []) {
70432
- if (!isNodeOfType(specifier, "ExportSpecifier") || specifier.exportKind === "type") continue;
70433
- const exportedName = isNodeOfType(specifier.exported, "Identifier") ? specifier.exported.name : isNodeOfType(specifier.exported, "Literal") && typeof specifier.exported.value === "string" ? specifier.exported.value : null;
70434
- if (!exportedName) continue;
70435
- const exportedValue = findExportedValue(programNode, exportedName);
70436
- if (!isAsyncFunctionLikeNode(exportedValue)) continue;
70437
- inspect({
70438
- functionNode: exportedValue,
70439
- displayName: isNodeOfType(specifier.local, "Identifier") ? specifier.local.name : exportedName,
70440
- reportNode: specifier.local ?? specifier
70441
- });
70442
- }
70443
- return;
70444
- }
69289
+ if (!declaration) return;
70445
69290
  if (isAsyncFunctionLikeNode(declaration)) {
70446
69291
  if (!isNodeOfType(declaration, "FunctionDeclaration")) return;
70447
69292
  inspect({
@@ -70454,23 +69299,11 @@ const serverAuthActions = defineRule({
70454
69299
  if (isNodeOfType(declaration, "VariableDeclaration")) for (const candidate of collectCandidatesFromVariableDeclaration(declaration)) inspect(candidate);
70455
69300
  },
70456
69301
  ExportDefaultDeclaration(node) {
70457
- if (shouldSkipTestAppSource) return;
70458
- const directCandidate = getCandidateFromDefaultDeclaration(node);
70459
- if (directCandidate) {
70460
- inspect(directCandidate);
70461
- return;
70462
- }
70463
- if (!programNode) return;
70464
- const resolvedDefaultExport = findExportedValue(programNode, "default");
70465
- const candidate = isAsyncFunctionLikeNode(resolvedDefaultExport) ? {
70466
- functionNode: resolvedDefaultExport,
70467
- displayName: isNodeOfType(node.declaration, "Identifier") ? node.declaration.name : "default",
70468
- reportNode: node.declaration ?? node
70469
- } : null;
69302
+ const candidate = getCandidateFromDefaultDeclaration(node);
70470
69303
  if (candidate) inspect(candidate);
70471
69304
  }
70472
69305
  };
70473
- })
69306
+ }
70474
69307
  });
70475
69308
  //#endregion
70476
69309
  //#region src/plugin/rules/server/server-cache-with-object-literal.ts
@@ -70550,7 +69383,6 @@ const serverDedupProps = defineRule({
70550
69383
  for (const attr of node.attributes ?? []) {
70551
69384
  if (!isNodeOfType(attr, "JSXAttribute")) continue;
70552
69385
  if (!isNodeOfType(attr.name, "JSXIdentifier")) continue;
70553
- if (attr.name.name === "key") continue;
70554
69386
  if (!isNodeOfType(attr.value, "JSXExpressionContainer")) continue;
70555
69387
  const expression = attr.value.expression;
70556
69388
  if (!expression) continue;
@@ -71280,7 +70112,7 @@ const collectJsxRuntimeImports = (program) => {
71280
70112
  };
71281
70113
  //#endregion
71282
70114
  //#region src/plugin/rules/react-builtins/style-prop-object.ts
71283
- 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.";
71284
70116
  const resolveSettings = (settings) => {
71285
70117
  const reactDoctor = settings?.["react-doctor"];
71286
70118
  return { allow: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.stylePropObject ?? {} : {}).allow ?? [] };
@@ -71387,7 +70219,7 @@ const stylePropObject = defineRule({
71387
70219
  if (isNodeOfType(value, "Literal") && typeof value.value === "string") {
71388
70220
  context.report({
71389
70221
  node: attribute,
71390
- message: MESSAGE$2
70222
+ message: MESSAGE$1
71391
70223
  });
71392
70224
  return;
71393
70225
  }
@@ -71395,7 +70227,7 @@ const stylePropObject = defineRule({
71395
70227
  const innerExpression = value.expression;
71396
70228
  if (innerExpression && innerExpression.type !== "JSXEmptyExpression" && isInvalidStyleExpression(innerExpression)) context.report({
71397
70229
  node: attribute,
71398
- message: MESSAGE$2
70230
+ message: MESSAGE$1
71399
70231
  });
71400
70232
  }
71401
70233
  }
@@ -71418,7 +70250,7 @@ const stylePropObject = defineRule({
71418
70250
  if (!(isNodeOfType(key, "Identifier") && key.name === "style" || isNodeOfType(key, "Literal") && key.value === "style")) continue;
71419
70251
  if (isInvalidStyleExpression(property.value)) context.report({
71420
70252
  node: property,
71421
- message: MESSAGE$2
70253
+ message: MESSAGE$1
71422
70254
  });
71423
70255
  }
71424
70256
  }
@@ -71754,7 +70586,7 @@ const svgFilterClickjackingRisk = defineRule({
71754
70586
  });
71755
70587
  //#endregion
71756
70588
  //#region src/plugin/rules/a11y/tabindex-no-positive.ts
71757
- 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`.";
71758
70590
  const tabindexNoPositive = defineRule({
71759
70591
  id: "tabindex-no-positive",
71760
70592
  title: "Positive tabindex value",
@@ -71774,7 +70606,7 @@ const tabindexNoPositive = defineRule({
71774
70606
  } else if (tabIndex.value && isNodeOfType(tabIndex.value, "JSXExpressionContainer")) numericValue = parseJsxValue(tabIndex.value);
71775
70607
  if (numericValue !== null && numericValue > 0) context.report({
71776
70608
  node: tabIndex,
71777
- message: MESSAGE$1
70609
+ message: MESSAGE
71778
70610
  });
71779
70611
  } })
71780
70612
  });
@@ -72680,445 +71512,6 @@ const useLazyMotion = defineRule({
72680
71512
  } })
72681
71513
  });
72682
71514
  //#endregion
72683
- //#region src/plugin/rules/valtio/valtio-no-proxy-read-in-render.ts
72684
- const VALTIO_REACT_MODULE_SOURCES$1 = new Set(["valtio", "valtio/react"]);
72685
- const REACT_DEPENDENCY_ARRAY_HOOK_NAMES = new Set([
72686
- "useCallback",
72687
- "useEffect",
72688
- "useInsertionEffect",
72689
- "useLayoutEffect",
72690
- "useMemo"
72691
- ]);
72692
- const MESSAGE = "Read this Valtio value from the useSnapshot result during render. Keep proxy reads for callbacks so render uses the tracked, consistent snapshot.";
72693
- const isValtioImportSymbol = (symbol) => {
72694
- if (symbol.kind !== "import") return false;
72695
- const importDeclaration = symbol.declarationNode.parent;
72696
- return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && typeof importDeclaration.source.value === "string" && VALTIO_REACT_MODULE_SOURCES$1.has(importDeclaration.source.value));
72697
- };
72698
- const isValtioUseSnapshotCall = (node, scopes) => {
72699
- if (!isNodeOfType(node, "CallExpression")) return false;
72700
- const callee = stripParenExpression(node.callee);
72701
- if (isNodeOfType(callee, "Identifier")) {
72702
- const symbol = resolveConstIdentifierAlias(callee, scopes);
72703
- return Boolean(symbol && isValtioImportSymbol(symbol) && getImportedName(symbol.declarationNode) === "useSnapshot");
72704
- }
72705
- if (!isNodeOfType(callee, "MemberExpression") || getStaticPropertyName(callee) !== "useSnapshot") return false;
72706
- const receiver = stripParenExpression(callee.object);
72707
- if (!isNodeOfType(receiver, "Identifier")) return false;
72708
- const namespaceSymbol = resolveConstIdentifierAlias(receiver, scopes);
72709
- return Boolean(namespaceSymbol && isValtioImportSymbol(namespaceSymbol) && isNodeOfType(namespaceSymbol.declarationNode, "ImportNamespaceSpecifier"));
72710
- };
72711
- const collectPatternBindingIdentifiers = (pattern) => {
72712
- if (isNodeOfType(pattern, "Identifier")) return [pattern];
72713
- if (isNodeOfType(pattern, "AssignmentPattern")) return collectPatternBindingIdentifiers(pattern.left);
72714
- if (isNodeOfType(pattern, "RestElement")) return collectPatternBindingIdentifiers(pattern.argument);
72715
- if (isNodeOfType(pattern, "ArrayPattern")) return pattern.elements.flatMap((element) => element ? collectPatternBindingIdentifiers(element) : []);
72716
- if (isNodeOfType(pattern, "ObjectPattern")) return pattern.properties.flatMap((property) => {
72717
- if (isNodeOfType(property, "Property")) return collectPatternBindingIdentifiers(property.value);
72718
- return isNodeOfType(property, "RestElement") ? collectPatternBindingIdentifiers(property.argument) : [];
72719
- });
72720
- return [];
72721
- };
72722
- const collectAssignmentWriteTargets = (target) => {
72723
- const candidate = stripParenExpression(target);
72724
- if (isNodeOfType(candidate, "AssignmentPattern")) return collectAssignmentWriteTargets(candidate.left);
72725
- if (isNodeOfType(candidate, "RestElement")) return collectAssignmentWriteTargets(candidate.argument);
72726
- if (isNodeOfType(candidate, "ArrayPattern")) return candidate.elements.flatMap((element) => element ? collectAssignmentWriteTargets(element) : []);
72727
- if (isNodeOfType(candidate, "ObjectPattern")) return candidate.properties.flatMap((property) => isNodeOfType(property, "Property") ? collectAssignmentWriteTargets(property.value) : collectAssignmentWriteTargets(property.argument));
72728
- return [candidate];
72729
- };
72730
- const findPatternPathToBinding = (pattern, bindingIdentifier) => {
72731
- if (pattern === bindingIdentifier) return [];
72732
- if (isNodeOfType(pattern, "AssignmentPattern")) return findPatternPathToBinding(pattern.left, bindingIdentifier);
72733
- if (isNodeOfType(pattern, "RestElement")) return null;
72734
- if (isNodeOfType(pattern, "ArrayPattern")) {
72735
- for (const [elementIndex, element] of pattern.elements.entries()) {
72736
- if (!element) continue;
72737
- const nestedPath = findPatternPathToBinding(element, bindingIdentifier);
72738
- if (nestedPath) return [String(elementIndex), ...nestedPath];
72739
- }
72740
- return null;
72741
- }
72742
- if (!isNodeOfType(pattern, "ObjectPattern")) return null;
72743
- for (const property of pattern.properties) {
72744
- if (!isNodeOfType(property, "Property")) continue;
72745
- const nestedPath = findPatternPathToBinding(property.value, bindingIdentifier);
72746
- if (!nestedPath) continue;
72747
- const propertyName = getStaticPropertyKeyName(property);
72748
- return propertyName === null ? null : [propertyName, ...nestedPath];
72749
- }
72750
- return null;
72751
- };
72752
- const resolveProxyPath = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set(), allowedAliasSymbolIds = null, collectedAliasCaptures = null) => {
72753
- const candidate = stripParenExpression(expression);
72754
- if (isNodeOfType(candidate, "MemberExpression")) {
72755
- const receiverPath = resolveProxyPath(candidate.object, scopes, visitedSymbolIds, allowedAliasSymbolIds, collectedAliasCaptures);
72756
- if (!receiverPath) return null;
72757
- return {
72758
- ...receiverPath,
72759
- properties: [...receiverPath.properties, getStaticPropertyName(candidate)]
72760
- };
72761
- }
72762
- if (!isNodeOfType(candidate, "Identifier")) return null;
72763
- const symbol = scopes.symbolFor(candidate);
72764
- if (!symbol) return scopes.isGlobalReference(candidate) ? {
72765
- rootKey: `global:${candidate.name}`,
72766
- properties: []
72767
- } : null;
72768
- if (visitedSymbolIds.has(symbol.id)) return null;
72769
- if (symbol.kind === "const" && symbol.initializer && isNodeOfType(symbol.declarationNode, "VariableDeclarator")) {
72770
- const directAlias = symbol.declarationNode.id === symbol.bindingIdentifier;
72771
- const initializerNode = directAlias ? symbol.initializer : symbol.declarationNode.init;
72772
- if (!initializerNode) return {
72773
- rootKey: `symbol:${symbol.id}`,
72774
- properties: []
72775
- };
72776
- const initializer = stripParenExpression(initializerNode);
72777
- const canResolveDirectAlias = directAlias && (isNodeOfType(initializer, "Identifier") || isNodeOfType(initializer, "MemberExpression"));
72778
- const patternPath = directAlias ? null : findPatternPathToBinding(symbol.declarationNode.id, symbol.bindingIdentifier);
72779
- if ((canResolveDirectAlias || patternPath) && (allowedAliasSymbolIds === null || allowedAliasSymbolIds.has(symbol.id))) {
72780
- const nextVisitedSymbolIds = new Set(visitedSymbolIds);
72781
- nextVisitedSymbolIds.add(symbol.id);
72782
- const initializerPath = resolveProxyPath(initializer, scopes, nextVisitedSymbolIds, allowedAliasSymbolIds, collectedAliasCaptures);
72783
- if (initializerPath) {
72784
- const aliasPath = {
72785
- ...initializerPath,
72786
- properties: [...initializerPath.properties, ...patternPath ?? []]
72787
- };
72788
- const declarationEnd = symbol.declarationNode.range?.[1];
72789
- if (typeof declarationEnd === "number") collectedAliasCaptures?.set(symbol.id, {
72790
- declarationEnd,
72791
- path: aliasPath
72792
- });
72793
- return aliasPath;
72794
- }
72795
- }
72796
- }
72797
- return {
72798
- rootKey: `symbol:${symbol.id}`,
72799
- properties: []
72800
- };
72801
- };
72802
- const isScopeWithin = (candidateScope, ancestorScope) => {
72803
- let currentScope = candidateScope;
72804
- while (currentScope) {
72805
- if (currentScope === ancestorScope) return true;
72806
- currentScope = currentScope.parent;
72807
- }
72808
- return false;
72809
- };
72810
- const isPathPrefix = (prefix, candidate) => {
72811
- if (prefix.rootKey !== candidate.rootKey || prefix.properties.length > candidate.properties.length) return false;
72812
- return prefix.properties.every((propertyName, propertyIndex) => propertyName !== null && propertyName === candidate.properties[propertyIndex]);
72813
- };
72814
- const findOutermostMemberRead = (identifier) => {
72815
- let currentExpression = findTransparentExpressionRoot(identifier);
72816
- for (;;) {
72817
- const parent = currentExpression.parent;
72818
- if (!isNodeOfType(parent, "MemberExpression") || stripParenExpression(parent.object) !== stripParenExpression(currentExpression)) return currentExpression;
72819
- currentExpression = findTransparentExpressionRoot(parent);
72820
- }
72821
- };
72822
- const isReadPositionWithinAssignmentTarget = (expression) => {
72823
- let currentNode = expression;
72824
- let parentNode = currentNode.parent;
72825
- while (parentNode) {
72826
- 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;
72827
- if (isNodeOfType(parentNode, "AssignmentExpression") || isNodeOfType(parentNode, "UpdateExpression") || isNodeOfType(parentNode, "UnaryExpression") && parentNode.operator === "delete" || isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return false;
72828
- currentNode = parentNode;
72829
- parentNode = currentNode.parent;
72830
- }
72831
- return false;
72832
- };
72833
- const isSnapshotArgument = (expression, scopes) => {
72834
- const expressionRoot = findTransparentExpressionRoot(expression);
72835
- const parent = expressionRoot.parent;
72836
- return Boolean(parent && isNodeOfType(parent, "CallExpression") && isValtioUseSnapshotCall(parent, scopes) && parent.arguments[0] === expressionRoot);
72837
- };
72838
- const isStableProxyDependency = (expression, readPath, matchingTarget, scopes) => {
72839
- if (readPath.properties.length !== matchingTarget.path.properties.length || !isPathPrefix(matchingTarget.path, readPath)) return false;
72840
- const dependencyArray = findTransparentExpressionRoot(expression).parent;
72841
- if (!isNodeOfType(dependencyArray, "ArrayExpression")) return false;
72842
- const hookCall = dependencyArray.parent;
72843
- return Boolean(isNodeOfType(hookCall, "CallExpression") && hookCall.arguments[1] === dependencyArray && isReactApiCall(hookCall, REACT_DEPENDENCY_ARRAY_HOOK_NAMES, scopes, { allowGlobalReactNamespace: true }));
72844
- };
72845
- const getSnapshotTarget = (callExpression, context) => {
72846
- if (!isValtioUseSnapshotCall(callExpression, context.scopes)) return null;
72847
- const proxyArgument = callExpression.arguments[0];
72848
- if (!proxyArgument || isNodeOfType(proxyArgument, "SpreadElement")) return null;
72849
- const path = resolveProxyPath(proxyArgument, context.scopes);
72850
- if (!path || path.properties.some((propertyName) => propertyName === null)) return null;
72851
- const callExpressionRoot = findTransparentExpressionRoot(callExpression);
72852
- const declarator = callExpressionRoot.parent;
72853
- if (!isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== callExpressionRoot || !isNodeOfType(declarator.parent, "VariableDeclaration")) return null;
72854
- const ownerFunction = findRenderPhaseComponentOrHook(callExpression, context.scopes);
72855
- if (!ownerFunction) return null;
72856
- const snapshotBindingScopes = collectPatternBindingIdentifiers(declarator.id).flatMap((bindingIdentifier) => {
72857
- const bindingSymbol = context.scopes.symbolFor(bindingIdentifier);
72858
- return bindingSymbol ? [bindingSymbol.scope] : [];
72859
- });
72860
- const declarationEnd = declarator.range?.[1];
72861
- if (snapshotBindingScopes.length === 0 || typeof declarationEnd !== "number") return null;
72862
- return {
72863
- declarationEnd,
72864
- ownerFunction,
72865
- path,
72866
- snapshotBindingScopes
72867
- };
72868
- };
72869
- const wasTargetReplacedBeforeRead = (target, readPosition, writeTargets, readAliasCaptures, context) => writeTargets.some((writeTarget) => {
72870
- const writeNode = findTransparentExpressionRoot(writeTarget).parent;
72871
- const writePosition = writeNode?.range?.[0];
72872
- if (!writeNode || typeof writePosition !== "number" || writePosition <= target.declarationEnd || writePosition >= readPosition || findRenderPhaseComponentOrHook(writeNode, context.scopes) !== target.ownerFunction) return false;
72873
- const writePath = resolveProxyPath(writeTarget, context.scopes, /* @__PURE__ */ new Set(), null);
72874
- if (!writePath || !isPathPrefix(writePath, target.path)) return false;
72875
- return ![...readAliasCaptures.values()].some((aliasCapture) => aliasCapture.declarationEnd < writePosition && isPathPrefix(writePath, aliasCapture.path));
72876
- });
72877
- const valtioNoProxyReadInRender = defineRule({
72878
- id: "valtio-no-proxy-read-in-render",
72879
- title: "Valtio proxy read during render",
72880
- severity: "warn",
72881
- recommendation: "Read reactive render values from useSnapshot. Reserve the mutable proxy for event handlers, effects, and other callbacks that need the latest value.",
72882
- requires: ["valtio:1"],
72883
- create: (context) => {
72884
- const snapshotTargets = [];
72885
- const identifierCandidates = [];
72886
- const writeTargets = [];
72887
- return {
72888
- CallExpression(node) {
72889
- const snapshotTarget = getSnapshotTarget(node, context);
72890
- if (snapshotTarget) snapshotTargets.push(snapshotTarget);
72891
- },
72892
- Identifier(node) {
72893
- if (context.scopes.referenceFor(node)) identifierCandidates.push(node);
72894
- },
72895
- AssignmentExpression(node) {
72896
- writeTargets.push(...collectAssignmentWriteTargets(node.left));
72897
- },
72898
- UpdateExpression(node) {
72899
- writeTargets.push(node.argument);
72900
- },
72901
- "Program:exit"() {
72902
- const reportedExpressions = /* @__PURE__ */ new Set();
72903
- for (const identifier of identifierCandidates) {
72904
- const readExpression = findOutermostMemberRead(identifier);
72905
- if (reportedExpressions.has(readExpression) || isWithinAssignmentTarget(readExpression) && !isReadPositionWithinAssignmentTarget(readExpression) || isSnapshotArgument(readExpression, context.scopes)) continue;
72906
- const readAliasCaptures = /* @__PURE__ */ new Map();
72907
- const readPath = resolveProxyPath(readExpression, context.scopes, /* @__PURE__ */ new Set(), null, readAliasCaptures);
72908
- const readPosition = readExpression.range?.[0];
72909
- const ownerFunction = findRenderPhaseComponentOrHook(readExpression, context.scopes);
72910
- if (!readPath || typeof readPosition !== "number" || !ownerFunction) continue;
72911
- const readScope = context.scopes.scopeFor(readExpression);
72912
- 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));
72913
- if (!matchingTarget || isStableProxyDependency(readExpression, readPath, matchingTarget, context.scopes)) continue;
72914
- reportedExpressions.add(readExpression);
72915
- context.report({
72916
- node: readExpression,
72917
- message: MESSAGE
72918
- });
72919
- }
72920
- }
72921
- };
72922
- }
72923
- });
72924
- //#endregion
72925
- //#region src/plugin/rules/valtio/valtio-no-snapshot-in-callback.ts
72926
- const VALTIO_REACT_MODULE_SOURCES = new Set(["valtio", "valtio/react"]);
72927
- const DEFERRED_REACT_HOOK_NAMES = new Set([
72928
- "useEffect",
72929
- "useInsertionEffect",
72930
- "useLayoutEffect"
72931
- ]);
72932
- const PROMISE_CONTINUATION_METHOD_NAMES = new Set([
72933
- "catch",
72934
- "finally",
72935
- "then"
72936
- ]);
72937
- const DEFERRED_CONSTRUCTOR_NAMES = new Set([
72938
- "IntersectionObserver",
72939
- "MutationObserver",
72940
- "PerformanceObserver",
72941
- "ResizeObserver"
72942
- ]);
72943
- const isValtioImport = (symbol) => {
72944
- const source = getImportDeclarationForSymbol(symbol)?.source.value;
72945
- return typeof source === "string" && VALTIO_REACT_MODULE_SOURCES.has(source);
72946
- };
72947
- const resolvesToValtioNamespace = (expression, scopes, visitedSymbolIds) => {
72948
- const candidate = stripParenExpression(expression);
72949
- if (!isNodeOfType(candidate, "Identifier")) return false;
72950
- const symbol = scopes.symbolFor(candidate);
72951
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
72952
- if (symbol.kind === "import" && isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") && isValtioImport(symbol)) return true;
72953
- const initializer = getDirectUnreassignedInitializer(symbol);
72954
- if (!initializer) return false;
72955
- visitedSymbolIds.add(symbol.id);
72956
- return resolvesToValtioNamespace(initializer, scopes, visitedSymbolIds);
72957
- };
72958
- const isValtioUseSnapshotCallee = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
72959
- const candidate = stripParenExpression(expression);
72960
- if (isNodeOfType(candidate, "Identifier")) {
72961
- const symbol = scopes.symbolFor(candidate);
72962
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
72963
- if (symbol.kind === "import" && isValtioImport(symbol) && getImportedName(symbol.declarationNode) === "useSnapshot") return true;
72964
- const initializer = getDirectUnreassignedInitializer(symbol);
72965
- if (!initializer) return false;
72966
- visitedSymbolIds.add(symbol.id);
72967
- return isValtioUseSnapshotCallee(initializer, scopes, visitedSymbolIds);
72968
- }
72969
- if (!isNodeOfType(candidate, "MemberExpression") || getStaticPropertyName(candidate) !== "useSnapshot") return false;
72970
- return resolvesToValtioNamespace(candidate.object, scopes, visitedSymbolIds);
72971
- };
72972
- const getSnapshotOriginCall = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
72973
- const candidate = stripParenExpression(expression);
72974
- if (isNodeOfType(candidate, "CallExpression")) return isValtioUseSnapshotCallee(candidate.callee, context.scopes) ? candidate : null;
72975
- if (!isNodeOfType(candidate, "Identifier")) return null;
72976
- const symbol = context.scopes.symbolFor(candidate);
72977
- if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
72978
- const initializer = getDirectUnreassignedInitializer(symbol);
72979
- if (!initializer) return null;
72980
- visitedSymbolIds.add(symbol.id);
72981
- return getSnapshotOriginCall(initializer, context, visitedSymbolIds);
72982
- };
72983
- const isJsxEventHandlerValue = (expression) => {
72984
- const expressionContainer = expression.parent;
72985
- if (!isNodeOfType(expressionContainer, "JSXExpressionContainer")) return false;
72986
- const attribute = expressionContainer.parent;
72987
- return Boolean(isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && REACT_HANDLER_PROP_PATTERN.test(attribute.name.name));
72988
- };
72989
- const isGlobalDeferredFunctionCall = (callExpression, context) => {
72990
- const callee = stripParenExpression(callExpression.callee);
72991
- if (isNodeOfType(callee, "Identifier") && TIMER_AND_SCHEDULER_DIRECT_CALLEE_NAMES.has(callee.name)) return context.scopes.isGlobalReference(callee);
72992
- if (!isNodeOfType(callee, "MemberExpression")) return false;
72993
- const methodName = getStaticPropertyName(callee);
72994
- if (!methodName || !TIMER_AND_SCHEDULER_DIRECT_CALLEE_NAMES.has(methodName)) return false;
72995
- const receiver = stripParenExpression(callee.object);
72996
- return Boolean(isNodeOfType(receiver, "Identifier") && (receiver.name === "globalThis" || receiver.name === "window") && context.scopes.isGlobalReference(receiver));
72997
- };
72998
- const isDeferredCallbackArgument = (expression, context) => {
72999
- const parent = expression.parent;
73000
- if (isNodeOfType(parent, "NewExpression")) {
73001
- const callee = stripParenExpression(parent.callee);
73002
- return Boolean(parent.arguments?.[0] === expression && isNodeOfType(callee, "Identifier") && DEFERRED_CONSTRUCTOR_NAMES.has(callee.name) && context.scopes.isGlobalReference(callee));
73003
- }
73004
- if (!isNodeOfType(parent, "CallExpression")) return false;
73005
- if (!(parent.arguments ?? []).some((argument) => argument === expression)) return false;
73006
- if (parent.arguments?.[0] === expression && isReactApiCall(parent, DEFERRED_REACT_HOOK_NAMES, context.scopes, {
73007
- allowGlobalReactNamespace: true,
73008
- resolveNamedAliases: true
73009
- })) return true;
73010
- if (parent.arguments?.[0] === expression && isGlobalDeferredFunctionCall(parent, context)) return true;
73011
- const callee = stripParenExpression(parent.callee);
73012
- if (!isNodeOfType(callee, "MemberExpression")) return false;
73013
- const methodName = getStaticPropertyName(callee);
73014
- return Boolean(methodName && (SUBSCRIPTION_METHOD_NAMES.has(methodName) || PROMISE_CONTINUATION_METHOD_NAMES.has(methodName)));
73015
- };
73016
- const isReactEffectCallbackValue = (expression, context) => {
73017
- const expressionRoot = findTransparentExpressionRoot(expression);
73018
- const parent = expressionRoot.parent;
73019
- return Boolean(isNodeOfType(parent, "CallExpression") && parent.arguments?.[0] === expressionRoot && isReactApiCall(parent, DEFERRED_REACT_HOOK_NAMES, context.scopes, {
73020
- allowGlobalReactNamespace: true,
73021
- resolveNamedAliases: true
73022
- }));
73023
- };
73024
- const isSymbolUsedAsReactEffectCallback = (symbol, context, visitedSymbolIds) => {
73025
- if (visitedSymbolIds.has(symbol.id)) return false;
73026
- visitedSymbolIds.add(symbol.id);
73027
- return symbol.references.some((reference) => {
73028
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
73029
- if (isReactEffectCallbackValue(referenceRoot, context)) return true;
73030
- const aliasSymbol = getConstAliasSymbol(referenceRoot, context);
73031
- return Boolean(aliasSymbol && isSymbolUsedAsReactEffectCallback(aliasSymbol, context, visitedSymbolIds));
73032
- });
73033
- };
73034
- const isFunctionUsedAsReactEffectCallback = (functionNode, context) => {
73035
- if (isReactEffectCallbackValue(functionNode, context)) return true;
73036
- const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
73037
- const symbol = bindingIdentifier ? context.scopes.symbolFor(bindingIdentifier) : null;
73038
- return Boolean(symbol && isSymbolUsedAsReactEffectCallback(symbol, context, /* @__PURE__ */ new Set()));
73039
- };
73040
- const isReactEffectCleanupValue = (expression, context) => {
73041
- const expressionRoot = findTransparentExpressionRoot(expression);
73042
- const returnStatement = expressionRoot.parent;
73043
- if (isNodeOfType(returnStatement, "ArrowFunctionExpression") && returnStatement.body === expressionRoot) return isFunctionUsedAsReactEffectCallback(returnStatement, context);
73044
- if (!isNodeOfType(returnStatement, "ReturnStatement") || returnStatement.argument !== expressionRoot) return false;
73045
- const effectCallback = findEnclosingFunction$1(returnStatement);
73046
- return Boolean(effectCallback && isFunctionUsedAsReactEffectCallback(effectCallback, context));
73047
- };
73048
- const isDeferredCallbackValue = (expression, context) => isJsxEventHandlerValue(expression) || isDeferredCallbackArgument(expression, context) || isReactEffectCleanupValue(expression, context);
73049
- const getConstAliasSymbol = (expression, context) => {
73050
- const expressionRoot = findTransparentExpressionRoot(expression);
73051
- const declarator = expressionRoot.parent;
73052
- if (!isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== expressionRoot || !isNodeOfType(declarator.id, "Identifier")) return null;
73053
- const aliasSymbol = context.scopes.symbolFor(declarator.id);
73054
- return aliasSymbol?.kind === "const" ? aliasSymbol : null;
73055
- };
73056
- const isCallTarget = (expression) => {
73057
- const parent = expression.parent;
73058
- return isNodeOfType(parent, "CallExpression") && parent.callee === expression ? parent : null;
73059
- };
73060
- const getSynchronousCallbackInvocation = (expression, context) => {
73061
- if (!executesDuringRender(expression, context.scopes)) return null;
73062
- const parent = expression.parent;
73063
- return isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression") ? parent : null;
73064
- };
73065
- const isNodeExecutedFromDeferredCallback = (node, snapshotOwner, context, visitedFunctionSymbolIds) => {
73066
- const enclosingFunction = findEnclosingFunction$1(node);
73067
- if (!enclosingFunction || enclosingFunction === snapshotOwner) return false;
73068
- return isFunctionExecutedAsDeferredCallback(enclosingFunction, snapshotOwner, context, visitedFunctionSymbolIds);
73069
- };
73070
- const isSymbolExecutedAsDeferredCallback = (symbol, snapshotOwner, context, visitedFunctionSymbolIds) => {
73071
- if (visitedFunctionSymbolIds.has(symbol.id)) return false;
73072
- visitedFunctionSymbolIds.add(symbol.id);
73073
- return symbol.references.some((reference) => {
73074
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
73075
- if (isDeferredCallbackValue(referenceRoot, context)) return true;
73076
- const aliasSymbol = getConstAliasSymbol(referenceRoot, context);
73077
- if (aliasSymbol && isSymbolExecutedAsDeferredCallback(aliasSymbol, snapshotOwner, context, visitedFunctionSymbolIds)) return true;
73078
- const callTarget = isCallTarget(referenceRoot);
73079
- if (callTarget) return isNodeExecutedFromDeferredCallback(callTarget, snapshotOwner, context, visitedFunctionSymbolIds);
73080
- const callbackInvocation = getSynchronousCallbackInvocation(referenceRoot, context);
73081
- return Boolean(callbackInvocation && isNodeExecutedFromDeferredCallback(callbackInvocation, snapshotOwner, context, visitedFunctionSymbolIds));
73082
- });
73083
- };
73084
- const isFunctionExecutedAsDeferredCallback = (functionNode, snapshotOwner, context, visitedFunctionSymbolIds) => {
73085
- const functionRoot = findTransparentExpressionRoot(functionNode);
73086
- if (isDeferredCallbackValue(functionRoot, context)) return true;
73087
- const synchronousInvocation = getSynchronousCallbackInvocation(functionRoot, context);
73088
- if (synchronousInvocation) return isNodeExecutedFromDeferredCallback(synchronousInvocation, snapshotOwner, context, visitedFunctionSymbolIds);
73089
- const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
73090
- if (!bindingIdentifier) return false;
73091
- const symbol = context.scopes.symbolFor(bindingIdentifier);
73092
- return Boolean(symbol && isSymbolExecutedAsDeferredCallback(symbol, snapshotOwner, context, visitedFunctionSymbolIds));
73093
- };
73094
- const isDirectSnapshotAliasInitializer = (identifier) => {
73095
- const expressionRoot = findTransparentExpressionRoot(identifier);
73096
- const parent = expressionRoot.parent;
73097
- return Boolean(isNodeOfType(parent, "VariableDeclarator") && parent.init === expressionRoot && isNodeOfType(parent.id, "Identifier"));
73098
- };
73099
- const valtioNoSnapshotInCallback = defineRule({
73100
- id: "valtio-no-snapshot-in-callback",
73101
- title: "Valtio snapshot read in a callback",
73102
- severity: "warn",
73103
- requires: ["valtio", "valtio:1"],
73104
- 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.",
73105
- create: (context) => ({ Identifier(node) {
73106
- const reference = context.scopes.referenceFor(node);
73107
- if (!reference || reference.flag === "write") return;
73108
- const snapshotOriginCall = getSnapshotOriginCall(node, context);
73109
- if (!snapshotOriginCall || isDirectSnapshotAliasInitializer(node)) return;
73110
- const snapshotOwner = findEnclosingFunction$1(snapshotOriginCall);
73111
- if (!snapshotOwner) return;
73112
- const referenceOwner = findEnclosingFunction$1(node);
73113
- if (!referenceOwner || referenceOwner === snapshotOwner) return;
73114
- if (!isFunctionExecutedAsDeferredCallback(referenceOwner, snapshotOwner, context, /* @__PURE__ */ new Set())) return;
73115
- context.report({
73116
- node,
73117
- message: "This callback reads a Valtio snapshot. Read the original proxy instead so callback-only fields do not become tracked render dependencies."
73118
- });
73119
- } })
73120
- });
73121
- //#endregion
73122
71515
  //#region src/plugin/rules/react-builtins/void-dom-elements-no-children.ts
73123
71516
  const VOID_DOM_ELEMENTS = new Set([
73124
71517
  "area",
@@ -73652,453 +72045,6 @@ const zodV4PreferTopLevelStringFormats = defineRule({
73652
72045
  })
73653
72046
  });
73654
72047
  //#endregion
73655
- //#region src/plugin/utils/function-returns-collection-at-path.ts
73656
- const resolveImmutableInitializer = (expression, scopes, visitedSymbolIds) => {
73657
- const candidate = stripParenExpression(expression);
73658
- if (!isNodeOfType(candidate, "Identifier")) return candidate;
73659
- const symbol = scopes.symbolFor(candidate);
73660
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return candidate;
73661
- visitedSymbolIds.add(symbol.id);
73662
- return resolveImmutableInitializer(symbol.initializer, scopes, visitedSymbolIds);
73663
- };
73664
- const expressionAtPropertyPath = (expression, propertyPath, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73665
- const candidate = resolveImmutableInitializer(expression, scopes, visitedSymbolIds);
73666
- const [propertyName, ...remainingPath] = propertyPath;
73667
- if (!propertyName) return candidate;
73668
- if (!isNodeOfType(candidate, "ObjectExpression")) return null;
73669
- for (const property of candidate.properties) {
73670
- if (!isNodeOfType(property, "Property")) continue;
73671
- if (getStaticPropertyKeyName(property) !== propertyName) continue;
73672
- return expressionAtPropertyPath(property.value, remainingPath, scopes, visitedSymbolIds);
73673
- }
73674
- return null;
73675
- };
73676
- const isGlobalConstructor = (expression, constructorNames, scopes) => {
73677
- const candidate = stripParenExpression(expression);
73678
- return isNodeOfType(candidate, "Identifier") && constructorNames.has(candidate.name) && scopes.isGlobalReference(candidate);
73679
- };
73680
- const isCollectionExpression = (expression, collectionKind, scopes) => {
73681
- const candidate = stripParenExpression(expression);
73682
- if (collectionKind === "array" && isNodeOfType(candidate, "ArrayExpression")) return true;
73683
- if (!isNodeOfType(candidate, "NewExpression")) return false;
73684
- return isGlobalConstructor(candidate.callee, collectionKind === "array" ? new Set(["Array"]) : new Set(["Map", "Set"]), scopes);
73685
- };
73686
- const functionReturnsCollectionAtPath = ({ collectionKind, functionNode, propertyPath, scopes }) => {
73687
- if (!isFunctionLike$1(functionNode)) return false;
73688
- const returnedExpressions = isNodeOfType(functionNode.body, "BlockStatement") ? collectFunctionReturnStatements(functionNode).flatMap((returnStatement) => returnStatement.argument ? [returnStatement.argument] : []) : [functionNode.body];
73689
- return returnedExpressions.length > 0 && returnedExpressions.every((returnedExpression) => {
73690
- const collectionExpression = expressionAtPropertyPath(returnedExpression, propertyPath, scopes);
73691
- return Boolean(collectionExpression && isCollectionExpression(collectionExpression, collectionKind, scopes));
73692
- });
73693
- };
73694
- //#endregion
73695
- //#region src/plugin/utils/resolve-zustand-api.ts
73696
- const ZUSTAND_APIS_BY_MODULE = new Map([
73697
- ["zustand", new Set([
73698
- "create",
73699
- "createStore",
73700
- "useStore"
73701
- ])],
73702
- ["zustand/vanilla", new Set(["createStore"])],
73703
- ["zustand/react", new Set(["useStore"])],
73704
- ["zustand/traditional", new Set(["createWithEqualityFn", "useStoreWithEqualityFn"])],
73705
- ["zustand/shallow", new Set(["shallow", "useShallow"])],
73706
- ["zustand/react/shallow", new Set(["useShallow"])],
73707
- ["zustand/middleware", new Set([
73708
- "combine",
73709
- "createJSONStorage",
73710
- "devtools",
73711
- "persist",
73712
- "redux",
73713
- "subscribeWithSelector"
73714
- ])],
73715
- ["zustand/middleware/immer", new Set(["immer"])]
73716
- ]);
73717
- const STATE_CREATOR_MIDDLEWARE_ARGUMENT_INDEX = new Map([
73718
- ["combine", 1],
73719
- ["devtools", 0],
73720
- ["immer", 0],
73721
- ["persist", 0],
73722
- ["subscribeWithSelector", 0]
73723
- ]);
73724
- const toZustandApiName = (value) => {
73725
- switch (value) {
73726
- case "combine":
73727
- case "create":
73728
- case "createJSONStorage":
73729
- case "createStore":
73730
- case "createWithEqualityFn":
73731
- case "devtools":
73732
- case "immer":
73733
- case "persist":
73734
- case "redux":
73735
- case "shallow":
73736
- case "subscribeWithSelector":
73737
- case "useShallow":
73738
- case "useStore":
73739
- case "useStoreWithEqualityFn": return value;
73740
- default: return null;
73741
- }
73742
- };
73743
- const getImportSource = (symbol) => {
73744
- const declaration = getImportDeclarationForSymbol(symbol);
73745
- if (!declaration || isTypeOnlyImport(declaration) || isNodeOfType(symbol.declarationNode, "ImportSpecifier") && symbol.declarationNode.importKind === "type") return null;
73746
- return typeof declaration.source.value === "string" ? declaration.source.value : null;
73747
- };
73748
- const bindingFromImportSymbol = (symbol) => {
73749
- const moduleSource = getImportSource(symbol);
73750
- const supportedApiNames = moduleSource ? ZUSTAND_APIS_BY_MODULE.get(moduleSource) : null;
73751
- if (!moduleSource || !supportedApiNames) return null;
73752
- if (moduleSource === "zustand" && isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return {
73753
- apiName: "create",
73754
- moduleSource
73755
- };
73756
- const importedName = getImportedName(symbol.declarationNode);
73757
- const apiName = importedName ? toZustandApiName(importedName) : null;
73758
- return apiName && supportedApiNames.has(apiName) ? {
73759
- apiName,
73760
- moduleSource
73761
- } : null;
73762
- };
73763
- const namespaceImportSource = (expression, scopes) => {
73764
- if (!isNodeOfType(expression, "Identifier")) return null;
73765
- const symbol = resolveConstIdentifierAlias(expression, scopes);
73766
- if (!symbol || !isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) return null;
73767
- return getImportSource(symbol);
73768
- };
73769
- const resolveZustandApiBinding = (expression, scopes) => {
73770
- const candidate = stripParenExpression(expression);
73771
- if (isNodeOfType(candidate, "Identifier")) {
73772
- const symbol = resolveConstIdentifierAlias(candidate, scopes);
73773
- if (!symbol) return null;
73774
- if (symbol.kind === "import") return bindingFromImportSymbol(symbol);
73775
- if (symbol.kind !== "const" || !symbol.initializer) return null;
73776
- return resolveZustandApiBinding(symbol.initializer, scopes);
73777
- }
73778
- if (!isNodeOfType(candidate, "MemberExpression")) return null;
73779
- const propertyName = getStaticPropertyName(candidate);
73780
- const moduleSource = namespaceImportSource(stripParenExpression(candidate.object), scopes);
73781
- const supportedApiNames = moduleSource ? ZUSTAND_APIS_BY_MODULE.get(moduleSource) : null;
73782
- const apiName = propertyName ? toZustandApiName(propertyName) : null;
73783
- return apiName && moduleSource && supportedApiNames?.has(apiName) ? {
73784
- apiName,
73785
- moduleSource
73786
- } : null;
73787
- };
73788
- const resolveStateCreator = (expression, scopes, middlewareNames, visitedExpressions = /* @__PURE__ */ new Set()) => {
73789
- const candidate = stripParenExpression(expression);
73790
- if (visitedExpressions.has(candidate)) return null;
73791
- visitedExpressions.add(candidate);
73792
- const creatorFunction = resolveExactLocalFunction(expression, scopes);
73793
- if (isFunctionLike$1(creatorFunction)) return creatorFunction;
73794
- if (!isNodeOfType(candidate, "CallExpression")) return null;
73795
- const middleware = resolveZustandApiBinding(candidate.callee, scopes);
73796
- const creatorArgumentIndex = middleware ? STATE_CREATOR_MIDDLEWARE_ARGUMENT_INDEX.get(middleware.apiName) : null;
73797
- if (!middleware || creatorArgumentIndex === void 0 || creatorArgumentIndex === null) return null;
73798
- const creatorArgument = candidate.arguments[creatorArgumentIndex];
73799
- if (!creatorArgument || isNodeOfType(creatorArgument, "SpreadElement")) return null;
73800
- middlewareNames.add(middleware.apiName);
73801
- return resolveStateCreator(creatorArgument, scopes, middlewareNames, visitedExpressions);
73802
- };
73803
- const isStoreFactoryApi = (binding) => binding?.apiName === "create" || binding?.apiName === "createStore" || binding?.apiName === "createWithEqualityFn";
73804
- const resolveCurriedStoreFactoryBinding = (expression, scopes) => {
73805
- const candidate = stripParenExpression(expression);
73806
- if (isNodeOfType(candidate, "Identifier")) {
73807
- const symbol = resolveConstIdentifierAlias(candidate, scopes);
73808
- if (symbol?.kind !== "const" || !symbol.initializer) return null;
73809
- return resolveCurriedStoreFactoryBinding(symbol.initializer, scopes);
73810
- }
73811
- if (!isNodeOfType(candidate, "CallExpression") || candidate.arguments.length > 0) return null;
73812
- const factoryBinding = resolveZustandApiBinding(candidate.callee, scopes);
73813
- return isStoreFactoryApi(factoryBinding) ? factoryBinding : null;
73814
- };
73815
- const resolveZustandStoreCreator = (callExpression, scopes) => {
73816
- const factoryCall = resolveZustandStoreFactoryCall(callExpression, scopes);
73817
- if (!factoryCall) return null;
73818
- const middlewareNames = /* @__PURE__ */ new Set();
73819
- const creatorFunction = resolveStateCreator(factoryCall.creatorArgument, scopes, middlewareNames);
73820
- if (!creatorFunction) return null;
73821
- return {
73822
- creatorFunction,
73823
- factoryApiName: factoryCall.factoryApiName,
73824
- middlewareNames
73825
- };
73826
- };
73827
- const resolveZustandStoreFactoryCall = (callExpression, scopes) => {
73828
- let factoryBinding = resolveZustandApiBinding(callExpression.callee, scopes);
73829
- if (!isStoreFactoryApi(factoryBinding)) {
73830
- factoryBinding = resolveCurriedStoreFactoryBinding(callExpression.callee, scopes);
73831
- if (!isStoreFactoryApi(factoryBinding)) return null;
73832
- }
73833
- const creatorArgument = callExpression.arguments[0];
73834
- if (!creatorArgument || isNodeOfType(creatorArgument, "SpreadElement")) return null;
73835
- return {
73836
- callExpression,
73837
- creatorArgument,
73838
- factoryApiName: factoryBinding.apiName
73839
- };
73840
- };
73841
- //#endregion
73842
- //#region src/plugin/rules/state-and-effects/zustand-no-fresh-selector-result.ts
73843
- const ALLOCATING_ARRAY_METHODS = new Set([
73844
- "filter",
73845
- "flat",
73846
- "flatMap",
73847
- "map",
73848
- "toReversed",
73849
- "toSorted",
73850
- "toSpliced",
73851
- "with"
73852
- ]);
73853
- const SAME_REFERENCE_ARRAY_METHODS = new Set(["reverse", "sort"]);
73854
- const ALLOCATING_NAMESPACE_METHOD_KINDS = new Map([["Array", new Map([["from", "array"], ["of", "array"]])], ["Object", new Map([
73855
- ["create", "object"],
73856
- ["entries", "array"],
73857
- ["fromEntries", "object"],
73858
- ["keys", "array"],
73859
- ["values", "array"]
73860
- ])]]);
73861
- const isNullishEqualityArgument = (argument, scopes) => {
73862
- const candidate = stripParenExpression(argument);
73863
- return isNodeOfType(candidate, "Identifier") && candidate.name === "undefined" && scopes.isGlobalReference(candidate) || isNodeOfType(candidate, "Literal") && candidate.value === null || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "void";
73864
- };
73865
- const hasExplicitEqualityArgument = (argumentsList, equalityArgumentIndex, scopes) => {
73866
- const equalityArgument = argumentsList[equalityArgumentIndex];
73867
- if (!equalityArgument) return false;
73868
- if (isNodeOfType(equalityArgument, "SpreadElement")) return true;
73869
- return !isNullishEqualityArgument(equalityArgument, scopes);
73870
- };
73871
- const resolveZustandStoreCreation = (expression, scopes) => {
73872
- const candidate = stripParenExpression(expression);
73873
- if (!isNodeOfType(candidate, "CallExpression")) return null;
73874
- const factoryCall = resolveZustandStoreFactoryCall(candidate, scopes);
73875
- if (!factoryCall || factoryCall.factoryApiName !== "create" && factoryCall.factoryApiName !== "createWithEqualityFn") return null;
73876
- return {
73877
- creatorFunction: resolveZustandStoreCreator(candidate, scopes)?.creatorFunction ?? null,
73878
- hasDefaultEquality: factoryCall.factoryApiName === "createWithEqualityFn" && hasExplicitEqualityArgument(candidate.arguments, 1, scopes),
73879
- supportsEqualityArgument: factoryCall.factoryApiName === "createWithEqualityFn"
73880
- };
73881
- };
73882
- const resolveZustandBoundStore = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73883
- const candidate = stripParenExpression(expression);
73884
- if (!isNodeOfType(candidate, "Identifier")) return null;
73885
- const symbol = scopes.symbolFor(candidate);
73886
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73887
- visitedSymbolIds.add(symbol.id);
73888
- return resolveZustandStoreCreation(symbol.initializer, scopes) ?? resolveZustandBoundStore(symbol.initializer, scopes, visitedSymbolIds);
73889
- };
73890
- const getZustandSelectorCall = (callExpression, scopes) => {
73891
- const apiBinding = resolveZustandApiBinding(callExpression.callee, scopes);
73892
- if (apiBinding?.apiName === "useStore" || apiBinding?.apiName === "useStoreWithEqualityFn") {
73893
- const selector = callExpression.arguments[1];
73894
- if (!selector || isNodeOfType(selector, "SpreadElement")) return null;
73895
- if (apiBinding.apiName === "useStoreWithEqualityFn" && hasExplicitEqualityArgument(callExpression.arguments, 2, scopes)) return null;
73896
- return {
73897
- selector,
73898
- storeCreatorFunction: null
73899
- };
73900
- }
73901
- const boundStore = resolveZustandBoundStore(callExpression.callee, scopes);
73902
- const selector = callExpression.arguments[0];
73903
- if (!boundStore || !selector || isNodeOfType(selector, "SpreadElement")) return null;
73904
- if (boundStore.hasDefaultEquality || boundStore.supportsEqualityArgument && hasExplicitEqualityArgument(callExpression.arguments, 1, scopes)) return null;
73905
- return {
73906
- selector,
73907
- storeCreatorFunction: boundStore.creatorFunction
73908
- };
73909
- };
73910
- const isUseShallowCall = (callExpression, scopes) => resolveZustandApiBinding(callExpression.callee, scopes)?.apiName === "useShallow";
73911
- const resolveSelectorFunction = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73912
- const candidate = stripParenExpression(expression);
73913
- if (isFunctionLike$1(candidate)) return candidate;
73914
- if (isNodeOfType(candidate, "CallExpression")) {
73915
- if (isUseShallowCall(candidate, scopes)) return null;
73916
- if (!isReactApiCall(candidate, "useCallback", scopes, {
73917
- allowGlobalReactNamespace: true,
73918
- resolveNamedAliases: true
73919
- })) return null;
73920
- const callback = candidate.arguments[0];
73921
- if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
73922
- return resolveSelectorFunction(callback, scopes, visitedSymbolIds);
73923
- }
73924
- if (!isNodeOfType(candidate, "Identifier")) return null;
73925
- const symbol = scopes.symbolFor(candidate);
73926
- if (!symbol || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73927
- if (symbol.kind === "function" && isFunctionLike$1(symbol.declarationNode)) return symbol.declarationNode;
73928
- if (symbol.kind !== "const" || !symbol.initializer) return null;
73929
- visitedSymbolIds.add(symbol.id);
73930
- return resolveSelectorFunction(symbol.initializer, scopes, visitedSymbolIds);
73931
- };
73932
- const selectorStatePropertyPath = (expression, analysis, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73933
- const candidate = stripParenExpression(expression);
73934
- if (isNodeOfType(candidate, "Identifier")) {
73935
- const symbol = analysis.scopes.symbolFor(candidate);
73936
- const selectorParameter = isFunctionLike$1(analysis.selectorFunction) ? analysis.selectorFunction.params[0] : null;
73937
- if (selectorParameter && isNodeOfType(selectorParameter, "Identifier") && symbol?.id === analysis.scopes.symbolFor(selectorParameter)?.id) return [];
73938
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73939
- visitedSymbolIds.add(symbol.id);
73940
- return selectorStatePropertyPath(symbol.initializer, analysis, visitedSymbolIds);
73941
- }
73942
- if (!isNodeOfType(candidate, "MemberExpression")) return null;
73943
- const propertyName = getStaticPropertyName(candidate);
73944
- const objectPath = selectorStatePropertyPath(candidate.object, analysis, visitedSymbolIds);
73945
- return propertyName && objectPath ? [...objectPath, propertyName] : null;
73946
- };
73947
- const freshResultFromAllocatingCall = (expression, analysis, visitedSymbolIds) => {
73948
- const callee = stripParenExpression(expression.callee);
73949
- if (!isNodeOfType(callee, "MemberExpression")) return null;
73950
- const methodName = getStaticPropertyName(callee);
73951
- if (!methodName) return null;
73952
- const receiver = stripParenExpression(callee.object);
73953
- if (isNodeOfType(receiver, "Identifier") && analysis.scopes.isGlobalReference(receiver)) {
73954
- const resultKind = ALLOCATING_NAMESPACE_METHOD_KINDS.get(receiver.name)?.get(methodName);
73955
- if (resultKind) return {
73956
- kind: resultKind,
73957
- node: expression
73958
- };
73959
- if (receiver.name === "Object" && methodName === "assign") {
73960
- const target = expression.arguments[0];
73961
- if (!target || isNodeOfType(target, "SpreadElement")) return null;
73962
- const freshTarget = resolveFreshSelectorResult(target, analysis, new Set(visitedSymbolIds));
73963
- return freshTarget ? {
73964
- kind: freshTarget.kind,
73965
- node: expression
73966
- } : null;
73967
- }
73968
- }
73969
- if (ALLOCATING_ARRAY_METHODS.has(methodName)) {
73970
- if (resolveFreshSelectorResult(receiver, analysis, new Set(visitedSymbolIds))?.kind === "array") return {
73971
- kind: "array",
73972
- node: expression
73973
- };
73974
- const receiverPath = selectorStatePropertyPath(receiver, analysis);
73975
- if (receiverPath && analysis.storeCreatorFunction && functionReturnsCollectionAtPath({
73976
- collectionKind: "array",
73977
- functionNode: analysis.storeCreatorFunction,
73978
- propertyPath: receiverPath,
73979
- scopes: analysis.scopes
73980
- })) return {
73981
- kind: "array",
73982
- node: expression
73983
- };
73984
- return null;
73985
- }
73986
- if (!SAME_REFERENCE_ARRAY_METHODS.has(methodName)) return null;
73987
- return resolveFreshSelectorResult(receiver, analysis, new Set(visitedSymbolIds)) ? {
73988
- kind: "array",
73989
- node: expression
73990
- } : null;
73991
- };
73992
- const resolveFreshSelectorResult = (expression, analysis, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73993
- const freshRenderValue = resolveFreshRenderValue(expression, analysis.scopes);
73994
- if (freshRenderValue?.kind === "array" || freshRenderValue?.kind === "function" || freshRenderValue?.kind === "instance" || freshRenderValue?.kind === "object") return {
73995
- kind: freshRenderValue.kind,
73996
- node: expression
73997
- };
73998
- const candidate = stripParenExpression(expression);
73999
- if (isNodeOfType(candidate, "CallExpression")) return freshResultFromAllocatingCall(candidate, analysis, visitedSymbolIds);
74000
- if (isNodeOfType(candidate, "ConditionalExpression")) return resolveFreshSelectorResult(candidate.consequent, analysis, new Set(visitedSymbolIds)) ?? resolveFreshSelectorResult(candidate.alternate, analysis, new Set(visitedSymbolIds));
74001
- if (isNodeOfType(candidate, "LogicalExpression")) {
74002
- if (candidate.operator === "&&") return resolveFreshSelectorResult(candidate.right, analysis, visitedSymbolIds);
74003
- return resolveFreshSelectorResult(candidate.left, analysis, new Set(visitedSymbolIds)) ?? resolveFreshSelectorResult(candidate.right, analysis, new Set(visitedSymbolIds));
74004
- }
74005
- if (isNodeOfType(candidate, "SequenceExpression")) {
74006
- const returnedExpression = candidate.expressions[candidate.expressions.length - 1];
74007
- return returnedExpression ? resolveFreshSelectorResult(returnedExpression, analysis, visitedSymbolIds) : null;
74008
- }
74009
- if (!isNodeOfType(candidate, "Identifier")) return null;
74010
- const symbol = analysis.scopes.symbolFor(candidate);
74011
- if (symbol?.kind !== "const" || symbol.scope.kind === "module" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
74012
- visitedSymbolIds.add(symbol.id);
74013
- return resolveFreshSelectorResult(symbol.initializer, analysis, visitedSymbolIds);
74014
- };
74015
- const findFreshSelectorReturn = (selectorFunction, scopes, storeCreatorFunction) => {
74016
- if (!isFunctionLike$1(selectorFunction) || !selectorFunction.body) return null;
74017
- const analysis = {
74018
- scopes,
74019
- selectorFunction,
74020
- storeCreatorFunction
74021
- };
74022
- if (!isNodeOfType(selectorFunction.body, "BlockStatement")) return resolveFreshSelectorResult(selectorFunction.body, analysis);
74023
- let freshResult = null;
74024
- walkAst(selectorFunction.body, (candidate) => {
74025
- if (freshResult) return false;
74026
- if (candidate !== selectorFunction.body && isFunctionLike$1(candidate)) return false;
74027
- if (!isNodeOfType(candidate, "ReturnStatement") || !candidate.argument) return;
74028
- freshResult = resolveFreshSelectorResult(candidate.argument, analysis);
74029
- return freshResult ? false : void 0;
74030
- });
74031
- return freshResult;
74032
- };
74033
- const zustandNoFreshSelectorResult = defineRule({
74034
- id: "zustand-no-fresh-selector-result",
74035
- title: "Zustand selector returns a fresh value",
74036
- severity: "error",
74037
- category: "Performance",
74038
- requires: ["zustand", "zustand:5"],
74039
- recommendation: "Select a stable store field, split the selector, or wrap a collection selector with `useShallow`.",
74040
- create: (context) => ({ CallExpression(node) {
74041
- const selectorCall = getZustandSelectorCall(node, context.scopes);
74042
- if (!selectorCall) return;
74043
- const selectorFunction = resolveSelectorFunction(selectorCall.selector, context.scopes);
74044
- if (!selectorFunction) return;
74045
- const freshResult = findFreshSelectorReturn(selectorFunction, context.scopes, selectorCall.storeCreatorFunction);
74046
- if (!freshResult) return;
74047
- context.report({
74048
- node: freshResult.node,
74049
- 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`."
74050
- });
74051
- } })
74052
- });
74053
- //#endregion
74054
- //#region src/plugin/rules/state-and-effects/zustand-no-whole-store-destructure.ts
74055
- const BOUND_STORE_FACTORY_APIS = new Set(["create", "createWithEqualityFn"]);
74056
- const VANILLA_STORE_FACTORY_APIS = new Set(["createStore"]);
74057
- const isStoreFactoryValue = (rawValue, factoryApiNames, scopes) => {
74058
- const value = stripParenExpression(rawValue);
74059
- if (!isNodeOfType(value, "CallExpression")) return false;
74060
- const factoryCall = resolveZustandStoreFactoryCall(value, scopes);
74061
- return Boolean(factoryCall && factoryApiNames.has(factoryCall.factoryApiName));
74062
- };
74063
- const isStoreValue = (rawValue, factoryApiNames, scopes) => {
74064
- const value = stripParenExpression(rawValue);
74065
- if (isStoreFactoryValue(value, factoryApiNames, scopes)) return true;
74066
- if (!isNodeOfType(value, "Identifier")) return false;
74067
- const symbol = resolveConstIdentifierAlias(value, scopes);
74068
- return Boolean(symbol?.kind === "const" && symbol.initializer && isStoreFactoryValue(symbol.initializer, factoryApiNames, scopes));
74069
- };
74070
- const isBoundStoreCall = (node, scopes) => {
74071
- const callee = stripParenExpression(node.callee);
74072
- return isNodeOfType(callee, "Identifier") && isStoreValue(callee, BOUND_STORE_FACTORY_APIS, scopes);
74073
- };
74074
- const isVanillaStoreHookCall = (node, scopes) => {
74075
- if (node.arguments.length !== 1) return false;
74076
- const hook = resolveZustandApiBinding(node.callee, scopes);
74077
- if (hook?.apiName !== "useStore" && hook?.apiName !== "useStoreWithEqualityFn") return false;
74078
- return isStoreValue(node.arguments[0], VANILLA_STORE_FACTORY_APIS, scopes);
74079
- };
74080
- const isDirectReactRenderCall = (node, context) => {
74081
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
74082
- if (!renderFunction || findEnclosingFunction$1(node) !== renderFunction) return false;
74083
- const displayName = componentOrHookDisplayNameForFunction(renderFunction);
74084
- return Boolean(displayName && (isReactHookName(displayName) || functionHasReactComponentEvidence(renderFunction, context.scopes, context.cfg)));
74085
- };
74086
- const zustandNoWholeStoreDestructure = defineRule({
74087
- id: "zustand-no-whole-store-destructure",
74088
- title: "Whole Zustand store subscribed during render",
74089
- severity: "warn",
74090
- requires: ["zustand:1"],
74091
- recommendation: "Pass a selector to the Zustand hook so this component rerenders only when the state it reads changes.",
74092
- create: (context) => ({ CallExpression(node) {
74093
- if (!isDirectReactRenderCall(node, context)) return;
74094
- if (!(node.arguments.length === 0 && isBoundStoreCall(node, context.scopes)) && !isVanillaStoreHookCall(node, context.scopes)) return;
74095
- context.report({
74096
- node,
74097
- message: "This hook subscribes to the whole Zustand store, so every store update rerenders this component. Pass a selector for the state it reads."
74098
- });
74099
- } })
74100
- });
74101
- //#endregion
74102
72048
  //#region src/plugin/rule-registry.ts
74103
72049
  const reactDoctorRules = [
74104
72050
  {
@@ -75413,39 +73359,6 @@ const reactDoctorRules = [
75413
73359
  requires: [...new Set(["react", ...mediaHasCaption.requires ?? []])]
75414
73360
  }
75415
73361
  },
75416
- {
75417
- key: "react-doctor/mobx-no-make-auto-observable-in-inheritance",
75418
- id: "mobx-no-make-auto-observable-in-inheritance",
75419
- source: "react-doctor",
75420
- originallyExternal: false,
75421
- rule: {
75422
- ...mobxNoMakeAutoObservableInInheritance,
75423
- framework: "global",
75424
- category: "Bugs"
75425
- }
75426
- },
75427
- {
75428
- key: "react-doctor/mobx-no-observer-wrapped-memo",
75429
- id: "mobx-no-observer-wrapped-memo",
75430
- source: "react-doctor",
75431
- originallyExternal: false,
75432
- rule: {
75433
- ...mobxNoObserverWrappedMemo,
75434
- framework: "global",
75435
- category: "Bugs"
75436
- }
75437
- },
75438
- {
75439
- key: "react-doctor/mobx-reaction-disposer-discarded",
75440
- id: "mobx-reaction-disposer-discarded",
75441
- source: "react-doctor",
75442
- originallyExternal: false,
75443
- rule: {
75444
- ...mobxReactionDisposerDiscarded,
75445
- framework: "global",
75446
- category: "Bugs"
75447
- }
75448
- },
75449
73362
  {
75450
73363
  key: "react-doctor/mouse-events-have-key-events",
75451
73364
  id: "mouse-events-have-key-events",
@@ -77628,105 +75541,6 @@ const reactDoctorRules = [
77628
75541
  requires: [...new Set(["react", ...reduxUseselectorReturnsNewCollection.requires ?? []])]
77629
75542
  }
77630
75543
  },
77631
- {
77632
- key: "react-doctor/remotion-calculate-metadata-fetch-signal",
77633
- id: "remotion-calculate-metadata-fetch-signal",
77634
- source: "react-doctor",
77635
- originallyExternal: false,
77636
- rule: {
77637
- ...remotionCalculateMetadataFetchSignal,
77638
- framework: "global",
77639
- category: "Bugs"
77640
- }
77641
- },
77642
- {
77643
- key: "react-doctor/remotion-deterministic-randomness",
77644
- id: "remotion-deterministic-randomness",
77645
- source: "react-doctor",
77646
- originallyExternal: false,
77647
- rule: {
77648
- ...remotionDeterministicRandomness,
77649
- framework: "global",
77650
- category: "Bugs"
77651
- }
77652
- },
77653
- {
77654
- key: "react-doctor/remotion-no-css-animation",
77655
- id: "remotion-no-css-animation",
77656
- source: "react-doctor",
77657
- originallyExternal: false,
77658
- rule: {
77659
- ...remotionNoCssAnimation,
77660
- framework: "global",
77661
- category: "Bugs"
77662
- }
77663
- },
77664
- {
77665
- key: "react-doctor/remotion-no-css-transition",
77666
- id: "remotion-no-css-transition",
77667
- source: "react-doctor",
77668
- originallyExternal: false,
77669
- rule: {
77670
- ...remotionNoCssTransition,
77671
- framework: "global",
77672
- category: "Bugs"
77673
- }
77674
- },
77675
- {
77676
- key: "react-doctor/remotion-no-css-url-assets",
77677
- id: "remotion-no-css-url-assets",
77678
- source: "react-doctor",
77679
- originallyExternal: false,
77680
- rule: {
77681
- ...remotionNoCssUrlAssets,
77682
- framework: "global",
77683
- category: "Bugs"
77684
- }
77685
- },
77686
- {
77687
- key: "react-doctor/remotion-no-module-scope-delay-render",
77688
- id: "remotion-no-module-scope-delay-render",
77689
- source: "react-doctor",
77690
- originallyExternal: false,
77691
- rule: {
77692
- ...remotionNoModuleScopeDelayRender,
77693
- framework: "global",
77694
- category: "Bugs"
77695
- }
77696
- },
77697
- {
77698
- key: "react-doctor/remotion-no-native-media-elements",
77699
- id: "remotion-no-native-media-elements",
77700
- source: "react-doctor",
77701
- originallyExternal: false,
77702
- rule: {
77703
- ...remotionNoNativeMediaElements,
77704
- framework: "global",
77705
- category: "Bugs"
77706
- }
77707
- },
77708
- {
77709
- key: "react-doctor/remotion-no-next-image",
77710
- id: "remotion-no-next-image",
77711
- source: "react-doctor",
77712
- originallyExternal: false,
77713
- rule: {
77714
- ...remotionNoNextImage,
77715
- framework: "global",
77716
- category: "Bugs"
77717
- }
77718
- },
77719
- {
77720
- key: "react-doctor/remotion-stable-delay-render-handle",
77721
- id: "remotion-stable-delay-render-handle",
77722
- source: "react-doctor",
77723
- originallyExternal: false,
77724
- rule: {
77725
- ...remotionStableDelayRenderHandle,
77726
- framework: "global",
77727
- category: "Bugs"
77728
- }
77729
- },
77730
75544
  {
77731
75545
  key: "react-doctor/rendering-animate-svg-wrapper",
77732
75546
  id: "rendering-animate-svg-wrapper",
@@ -78850,30 +76664,6 @@ const reactDoctorRules = [
78850
76664
  category: "Performance"
78851
76665
  }
78852
76666
  },
78853
- {
78854
- key: "react-doctor/valtio-no-proxy-read-in-render",
78855
- id: "valtio-no-proxy-read-in-render",
78856
- source: "react-doctor",
78857
- originallyExternal: false,
78858
- rule: {
78859
- ...valtioNoProxyReadInRender,
78860
- framework: "global",
78861
- category: "Bugs",
78862
- requires: [...new Set(["react", ...valtioNoProxyReadInRender.requires ?? []])]
78863
- }
78864
- },
78865
- {
78866
- key: "react-doctor/valtio-no-snapshot-in-callback",
78867
- id: "valtio-no-snapshot-in-callback",
78868
- source: "react-doctor",
78869
- originallyExternal: false,
78870
- rule: {
78871
- ...valtioNoSnapshotInCallback,
78872
- framework: "global",
78873
- category: "Bugs",
78874
- requires: [...new Set(["react", ...valtioNoSnapshotInCallback.requires ?? []])]
78875
- }
78876
- },
78877
76667
  {
78878
76668
  key: "react-doctor/void-dom-elements-no-children",
78879
76669
  id: "void-dom-elements-no-children",
@@ -78941,30 +76731,6 @@ const reactDoctorRules = [
78941
76731
  framework: "global",
78942
76732
  category: "Maintainability"
78943
76733
  }
78944
- },
78945
- {
78946
- key: "react-doctor/zustand-no-fresh-selector-result",
78947
- id: "zustand-no-fresh-selector-result",
78948
- source: "react-doctor",
78949
- originallyExternal: false,
78950
- rule: {
78951
- ...zustandNoFreshSelectorResult,
78952
- framework: "global",
78953
- category: "Performance",
78954
- requires: [...new Set(["react", ...zustandNoFreshSelectorResult.requires ?? []])]
78955
- }
78956
- },
78957
- {
78958
- key: "react-doctor/zustand-no-whole-store-destructure",
78959
- id: "zustand-no-whole-store-destructure",
78960
- source: "react-doctor",
78961
- originallyExternal: false,
78962
- rule: {
78963
- ...zustandNoWholeStoreDestructure,
78964
- framework: "global",
78965
- category: "Bugs",
78966
- requires: [...new Set(["react", ...zustandNoWholeStoreDestructure.requires ?? []])]
78967
- }
78968
76734
  }
78969
76735
  ];
78970
76736
  const ruleRegistry = Object.fromEntries(reactDoctorRules.map((rule) => [rule.id, rule.rule]));
@@ -79234,13 +77000,6 @@ const CROSS_FILE_RULE_IDS = new Set([
79234
77000
  "no-unguarded-browser-global-in-render-or-hook-init",
79235
77001
  "prefer-dynamic-import",
79236
77002
  "rendering-hydration-mismatch-time",
79237
- "remotion-calculate-metadata-fetch-signal",
79238
- "remotion-deterministic-randomness",
79239
- "remotion-no-css-animation",
79240
- "remotion-no-css-transition",
79241
- "remotion-no-css-url-assets",
79242
- "remotion-no-native-media-elements",
79243
- "remotion-no-next-image",
79244
77003
  "rerender-memo-with-default-value",
79245
77004
  "rn-no-legacy-shadow-styles",
79246
77005
  "rn-no-raw-text",
@@ -79458,17 +77217,7 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
79458
77217
  * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
79459
77218
  * partition), forcing a conscious classification.
79460
77219
  */
79461
- const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set([
79462
- "nextjs-no-img-element",
79463
- "only-export-components",
79464
- "remotion-calculate-metadata-fetch-signal",
79465
- "remotion-deterministic-randomness",
79466
- "remotion-no-css-animation",
79467
- "remotion-no-css-transition",
79468
- "remotion-no-css-url-assets",
79469
- "remotion-no-native-media-elements",
79470
- "remotion-no-next-image"
79471
- ]);
77220
+ const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["nextjs-no-img-element", "only-export-components"]);
79472
77221
  /**
79473
77222
  * Runs the collectors for `ruleIds` over one file and returns every
79474
77223
  * filesystem probe they made — the file's cross-file dependency set.