oxlint-plugin-react-doctor 0.8.1-dev.e6a1557 → 0.8.1-dev.f4aa821

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 +1 -323
  2. package/dist/index.js +355 -2139
  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$67 = "Blind users can't follow this link because screen readers announce nothing, so add visible text, `aria-label`, or `aria-labelledby`.";
3176
+ const MESSAGE$63 = "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$67
3218
+ message: MESSAGE$63
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$66 = "Keyboard users can't focus this element with `aria-activedescendant` because it isn't tabbable, so add `tabIndex={0}`.";
3706
+ const MESSAGE$62 = "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$66
3724
+ message: MESSAGE$62
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$66
3732
+ message: MESSAGE$62
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$65 = "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$61 = "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$65
7002
+ message: MESSAGE$61
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$65
7013
+ message: MESSAGE$61
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$64 = "The build or install pipeline can execute package lifecycle code while CI secrets may be present.";
7152
+ const MESSAGE$60 = "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$64,
7197
+ message: MESSAGE$60,
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$64
7207
+ message: MESSAGE$60
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$63 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
7957
+ const MESSAGE$59 = "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$63
8168
+ message: MESSAGE$59
8169
8169
  });
8170
8170
  } };
8171
8171
  }
@@ -10255,7 +10255,7 @@ const getJsxAttributeStaticString = (attribute) => {
10255
10255
  };
10256
10256
  //#endregion
10257
10257
  //#region src/plugin/rules/a11y/control-has-associated-label.ts
10258
- const MESSAGE$62 = "Blind users can't tell what this control does because screen readers find no label, so add visible text, `aria-label`, or `aria-labelledby`.";
10258
+ const MESSAGE$58 = "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
10259
  const NON_OPERABLE_ELEMENTS = new Set([
10260
10260
  "td",
10261
10261
  "th",
@@ -10785,7 +10785,7 @@ const controlHasAssociatedLabel = defineRule({
10785
10785
  if (candidate.enclosingBindingName !== null && labelEmbeddedNames.has(candidate.enclosingBindingName)) continue;
10786
10786
  context.report({
10787
10787
  node: candidate.opening,
10788
- message: MESSAGE$62
10788
+ message: MESSAGE$58
10789
10789
  });
10790
10790
  }
10791
10791
  }
@@ -12592,7 +12592,7 @@ const noVagueButtonLabel = defineRule({
12592
12592
  });
12593
12593
  //#endregion
12594
12594
  //#region src/plugin/rules/a11y/dialog-has-accessible-name.ts
12595
- const MESSAGE$61 = "This dialog has no accessible name, so screen readers announce it as just “dialog.” Add `aria-label` or point `aria-labelledby` at its heading.";
12595
+ const MESSAGE$57 = "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
12596
  const DIALOG_ROLES = new Set(["dialog", "alertdialog"]);
12597
12597
  const NAME_PROVIDING_ATTRIBUTES = [
12598
12598
  "aria-label",
@@ -12617,7 +12617,7 @@ const dialogHasAccessibleName = defineRule({
12617
12617
  if (NAME_PROVIDING_ATTRIBUTES.some((attribute) => hasJsxPropIgnoreCase(node.attributes, attribute))) return;
12618
12618
  context.report({
12619
12619
  node: node.name,
12620
- message: MESSAGE$61
12620
+ message: MESSAGE$57
12621
12621
  });
12622
12622
  } };
12623
12623
  }
@@ -12657,7 +12657,7 @@ const isEs6Component = (node) => {
12657
12657
  };
12658
12658
  //#endregion
12659
12659
  //#region src/plugin/rules/react-builtins/display-name.ts
12660
- const MESSAGE$60 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
12660
+ const MESSAGE$56 = "This component shows up as Anonymous in React DevTools because it has no `displayName`.";
12661
12661
  const DEFAULT_ADDITIONAL_HOCS = [
12662
12662
  "observer",
12663
12663
  "lazy",
@@ -12850,7 +12850,7 @@ const displayName = defineRule({
12850
12850
  const reportAt = (node) => {
12851
12851
  context.report({
12852
12852
  node,
12853
- message: MESSAGE$60
12853
+ message: MESSAGE$56
12854
12854
  });
12855
12855
  };
12856
12856
  return {
@@ -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$59 = "The parent can't reach this component's node because the `forwardRef` wrapper ignores `ref`.";
19267
+ const MESSAGE$55 = "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$59
19290
+ message: MESSAGE$55
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$58 = "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$54 = "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$58
19365
+ message: MESSAGE$54
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$57 = "Screen readers may mispronounce this page because it doesn't declare a language, so add a `lang` attribute like `en`.";
19533
+ const MESSAGE$53 = "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$57
19580
+ message: MESSAGE$53
19581
19581
  });
19582
19582
  return;
19583
19583
  }
19584
19584
  if (evaluateLang(lang.value) === "empty") context.report({
19585
19585
  node: lang,
19586
- message: MESSAGE$57
19586
+ message: MESSAGE$53
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$56 = "Screen reader users cannot identify this `<iframe>` because it has no title. Add a `title` that describes its content.";
19859
+ const MESSAGE$52 = "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$56
19948
+ message: MESSAGE$52
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$56
19955
+ message: MESSAGE$52
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$55 = "Screen reader users hear \"image\" or \"photo\" twice because they already announce it, so describe what the image shows instead.";
20080
+ const MESSAGE$51 = "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$55
20145
+ message: MESSAGE$51
20146
20146
  });
20147
20147
  } };
20148
20148
  }
@@ -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$54 = "`new RegExp()` rebuilds the pattern on every loop pass. Move it to a constant outside the loop.";
22109
+ const MESSAGE$50 = "`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$54
22126
+ message: MESSAGE$50
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$53 = "Your users see this comment as text on the page because `//` & `/*` aren't hidden in JSX.";
24893
+ const MESSAGE$49 = "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$53
24963
+ message: MESSAGE$49
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$52 = "Every reader of this context redraws on each render because you build its `value` inline.";
24994
+ const MESSAGE$48 = "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$52
25097
+ message: MESSAGE$48
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$51 = "This child redraws every render because the prop gets a brand new array each time.";
25963
+ const MESSAGE$47 = "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$51
26050
+ message: MESSAGE$47
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$50 = "This child redraws every render because the prop gets a brand new function each time.";
26308
+ const MESSAGE$46 = "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$50
26515
+ message: MESSAGE$46
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$49 = "This child redraws every render because the prop gets a brand new object each time.";
26735
+ const MESSAGE$45 = "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$49
26824
+ message: MESSAGE$45
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$48 = "A `javascript:` URL is an XSS hole that runs injected input as code.";
26832
+ const MESSAGE$44 = "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$48
26870
+ message: MESSAGE$44
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$47 = "You can't tell what props reach this element when you spread them.";
27213
+ const MESSAGE$43 = "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$47
27256
+ message: MESSAGE$43
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$46 = "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$42 = "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$46
28012
+ message: MESSAGE$42
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$46
28021
+ message: MESSAGE$42
28022
28022
  });
28023
28023
  } })
28024
28024
  });
@@ -28069,7 +28069,7 @@ const mdxSsrExecutionRisk = defineRule({
28069
28069
  });
28070
28070
  //#endregion
28071
28071
  //#region src/plugin/rules/a11y/media-has-caption.ts
28072
- const MESSAGE$45 = "Deaf and hard-of-hearing users need captions for this media. Add a `<track kind=\"captions\">` inside the `<audio>` or `<video>`.";
28072
+ const MESSAGE$41 = "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$45
28162
+ message: MESSAGE$41
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$45
28181
+ message: MESSAGE$41
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$44 = "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$44
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$43 = "`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$43
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$42 = "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$42
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) {
@@ -30225,20 +29684,17 @@ const catchClauseRethrowsCaught = (handler) => {
30225
29684
  return didRethrow;
30226
29685
  };
30227
29686
  //#endregion
30228
- //#region src/plugin/utils/find-immediately-invoked-call-expression.ts
30229
- const findImmediatelyInvokedCallExpression = (functionNode) => {
29687
+ //#region src/plugin/utils/is-immediately-invoked-function.ts
29688
+ const isImmediatelyInvokedFunction = (functionNode) => {
30230
29689
  let wrappedCallee = functionNode;
30231
29690
  let enclosing = functionNode.parent;
30232
29691
  while (enclosing && stripParenExpression(enclosing) === functionNode) {
30233
29692
  wrappedCallee = enclosing;
30234
29693
  enclosing = enclosing.parent ?? null;
30235
29694
  }
30236
- return enclosing && isNodeOfType(enclosing, "CallExpression") && enclosing.callee === wrappedCallee ? enclosing : null;
29695
+ return Boolean(enclosing && isNodeOfType(enclosing, "CallExpression") && enclosing.callee === wrappedCallee);
30237
29696
  };
30238
29697
  //#endregion
30239
- //#region src/plugin/utils/is-immediately-invoked-function.ts
30240
- const isImmediatelyInvokedFunction = (functionNode) => Boolean(findImmediatelyInvokedCallExpression(functionNode));
30241
- //#endregion
30242
29698
  //#region src/plugin/utils/find-guarding-try-statement.ts
30243
29699
  const findGuardingTryStatement = (node) => {
30244
29700
  let child = node;
@@ -30789,7 +30245,7 @@ const nextjsNoVercelOgImport = defineRule({
30789
30245
  });
30790
30246
  //#endregion
30791
30247
  //#region src/plugin/rules/a11y/no-access-key.ts
30792
- const MESSAGE$41 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
30248
+ const MESSAGE$40 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
30793
30249
  const isUndefinedIdentifier$1 = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
30794
30250
  const noAccessKey = defineRule({
30795
30251
  id: "no-access-key",
@@ -30808,7 +30264,7 @@ const noAccessKey = defineRule({
30808
30264
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
30809
30265
  context.report({
30810
30266
  node: accessKey,
30811
- message: MESSAGE$41
30267
+ message: MESSAGE$40
30812
30268
  });
30813
30269
  return;
30814
30270
  }
@@ -30818,7 +30274,7 @@ const noAccessKey = defineRule({
30818
30274
  if (isUndefinedIdentifier$1(expression)) return;
30819
30275
  context.report({
30820
30276
  node: accessKey,
30821
- message: MESSAGE$41
30277
+ message: MESSAGE$40
30822
30278
  });
30823
30279
  }
30824
30280
  } };
@@ -33161,7 +32617,7 @@ const noAdjustStateOnPropChange = defineRule({
33161
32617
  });
33162
32618
  //#endregion
33163
32619
  //#region src/plugin/rules/a11y/no-aria-hidden-on-focusable.ts
33164
- const MESSAGE$40 = "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.";
32620
+ 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.";
33165
32621
  const ALWAYS_FOCUSABLE_TAGS = new Set([
33166
32622
  "button",
33167
32623
  "embed",
@@ -33273,7 +32729,7 @@ const noAriaHiddenOnFocusable = defineRule({
33273
32729
  const isImplicitlyFocusable = isNativelyFocusable(tag, node);
33274
32730
  if (isExplicitlyFocusable || isImplicitlyFocusable) context.report({
33275
32731
  node: ariaHidden,
33276
- message: MESSAGE$40
32732
+ message: MESSAGE$39
33277
32733
  });
33278
32734
  } })
33279
32735
  });
@@ -34320,7 +33776,7 @@ const isAllLiteralArrayExpression = (node) => {
34320
33776
  };
34321
33777
  //#endregion
34322
33778
  //#region src/plugin/rules/react-builtins/no-array-index-key.ts
34323
- const MESSAGE$39 = "Your users can see & submit the wrong data when this list reorders.";
33779
+ const MESSAGE$38 = "Your users can see & submit the wrong data when this list reorders.";
34324
33780
  const SECOND_INDEX_METHODS = new Set([
34325
33781
  "every",
34326
33782
  "filter",
@@ -34439,14 +33895,14 @@ const noArrayIndexKey = defineRule({
34439
33895
  if (propName !== "key") continue;
34440
33896
  if (expressionUsesIndex(property.value, indexBinding.name)) context.report({
34441
33897
  node: property,
34442
- message: MESSAGE$39
33898
+ message: MESSAGE$38
34443
33899
  });
34444
33900
  }
34445
33901
  } })
34446
33902
  });
34447
33903
  //#endregion
34448
33904
  //#region src/plugin/rules/state-and-effects/no-async-effect-callback.ts
34449
- const MESSAGE$38 = "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.";
33905
+ 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.";
34450
33906
  const noAsyncEffectCallback = defineRule({
34451
33907
  id: "no-async-effect-callback",
34452
33908
  title: "Async effect callback",
@@ -34464,13 +33920,13 @@ const noAsyncEffectCallback = defineRule({
34464
33920
  if (!callback.async) return;
34465
33921
  context.report({
34466
33922
  node: callback,
34467
- message: MESSAGE$38
33923
+ message: MESSAGE$37
34468
33924
  });
34469
33925
  } })
34470
33926
  });
34471
33927
  //#endregion
34472
33928
  //#region src/plugin/rules/a11y/no-autofocus.ts
34473
- const MESSAGE$37 = "`autoFocus` moves focus on load, which can disrupt screen reader and keyboard users. Remove it and let users choose where to focus.";
33929
+ 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.";
34474
33930
  const resolveSettings$21 = (settings) => {
34475
33931
  const reactDoctor = settings?.["react-doctor"];
34476
33932
  return { ignoreNonDOM: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noAutofocus ?? {} : {}).ignoreNonDOM ?? true };
@@ -34567,7 +34023,7 @@ const noAutofocus = defineRule({
34567
34023
  if (isConditionallyRendered(node)) return;
34568
34024
  context.report({
34569
34025
  node: autoFocusAttribute,
34570
- message: MESSAGE$37
34026
+ message: MESSAGE$36
34571
34027
  });
34572
34028
  } };
34573
34029
  }
@@ -35896,7 +35352,7 @@ const noChainStateUpdates = defineRule({
35896
35352
  });
35897
35353
  //#endregion
35898
35354
  //#region src/plugin/rules/react-builtins/no-children-prop.ts
35899
- const MESSAGE$36 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
35355
+ const MESSAGE$35 = "A `children` prop can override or hide nested children, so the component may render different content than the JSX shows.";
35900
35356
  const noChildrenProp = defineRule({
35901
35357
  id: "no-children-prop",
35902
35358
  title: "Children passed as a prop",
@@ -35908,7 +35364,7 @@ const noChildrenProp = defineRule({
35908
35364
  if (node.name.name !== "children") return;
35909
35365
  context.report({
35910
35366
  node: node.name,
35911
- message: MESSAGE$36
35367
+ message: MESSAGE$35
35912
35368
  });
35913
35369
  },
35914
35370
  CallExpression(node) {
@@ -35921,7 +35377,7 @@ const noChildrenProp = defineRule({
35921
35377
  const propertyKey = property.key;
35922
35378
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "children" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "children") context.report({
35923
35379
  node: propertyKey,
35924
- message: MESSAGE$36
35380
+ message: MESSAGE$35
35925
35381
  });
35926
35382
  }
35927
35383
  }
@@ -35929,7 +35385,7 @@ const noChildrenProp = defineRule({
35929
35385
  });
35930
35386
  //#endregion
35931
35387
  //#region src/plugin/rules/react-builtins/no-clone-element.ts
35932
- const MESSAGE$35 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
35388
+ const MESSAGE$34 = "`React.cloneElement` couples the parent to the child's prop shape, so child prop changes can silently break injected behavior.";
35933
35389
  const noCloneElement = defineRule({
35934
35390
  id: "no-clone-element",
35935
35391
  title: "cloneElement makes child props fragile",
@@ -35942,7 +35398,7 @@ const noCloneElement = defineRule({
35942
35398
  if (isNodeOfType(callee, "Identifier") && callee.name === "cloneElement") {
35943
35399
  if (isImportedFromModule(node, "cloneElement", "react")) context.report({
35944
35400
  node: callee,
35945
- message: MESSAGE$35
35401
+ message: MESSAGE$34
35946
35402
  });
35947
35403
  return;
35948
35404
  }
@@ -35955,7 +35411,7 @@ const noCloneElement = defineRule({
35955
35411
  if (!isImportedFromModule(node, callee.object.name, "react")) return;
35956
35412
  context.report({
35957
35413
  node: callee,
35958
- message: MESSAGE$35
35414
+ message: MESSAGE$34
35959
35415
  });
35960
35416
  }
35961
35417
  } })
@@ -35975,7 +35431,7 @@ const getCallMethodName = (callee) => {
35975
35431
  };
35976
35432
  //#endregion
35977
35433
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
35978
- const MESSAGE$34 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
35434
+ const MESSAGE$33 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
35979
35435
  const CONTEXT_MODULES = [
35980
35436
  "react",
35981
35437
  "use-context-selector",
@@ -36023,7 +35479,7 @@ const noCreateContextInRender = defineRule({
36023
35479
  if (!componentOrHookName) return;
36024
35480
  context.report({
36025
35481
  node,
36026
- message: `${MESSAGE$34} (called inside "${componentOrHookName}")`
35482
+ message: `${MESSAGE$33} (called inside "${componentOrHookName}")`
36027
35483
  });
36028
35484
  } })
36029
35485
  });
@@ -36770,7 +36226,7 @@ const isProvenOneShotTestingLibraryComponent = (functionNode, filename, scopes)
36770
36226
  };
36771
36227
  //#endregion
36772
36228
  //#region src/plugin/rules/react-builtins/no-create-ref-in-function-component.ts
36773
- const MESSAGE$33 = "`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.";
36229
+ 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.";
36774
36230
  const isUseMemoCallbackArgument = (functionNode, scopes) => {
36775
36231
  const parent = functionNode.parent;
36776
36232
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
@@ -36824,7 +36280,7 @@ const noCreateRefInFunctionComponent = defineRule({
36824
36280
  if (isCreateRefResultWriteOnly(node, context.filename, context.scopes)) return;
36825
36281
  context.report({
36826
36282
  node,
36827
- message: MESSAGE$33
36283
+ message: MESSAGE$32
36828
36284
  });
36829
36285
  } })
36830
36286
  });
@@ -36964,7 +36420,7 @@ const noCreateStoreInRender = defineRule({
36964
36420
  });
36965
36421
  //#endregion
36966
36422
  //#region src/plugin/rules/react-builtins/no-danger.ts
36967
- const MESSAGE$32 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
36423
+ const MESSAGE$31 = "`dangerouslySetInnerHTML` is an XSS hole that runs attacker-controlled HTML in your users' browsers.";
36968
36424
  const noDanger = defineRule({
36969
36425
  id: "no-danger",
36970
36426
  title: "Raw HTML injection can run unsafe markup",
@@ -36978,7 +36434,7 @@ const noDanger = defineRule({
36978
36434
  if (!propAttribute) return;
36979
36435
  context.report({
36980
36436
  node: propAttribute.name,
36981
- message: MESSAGE$32
36437
+ message: MESSAGE$31
36982
36438
  });
36983
36439
  },
36984
36440
  CallExpression(node) {
@@ -36990,7 +36446,7 @@ const noDanger = defineRule({
36990
36446
  const propertyKey = property.key;
36991
36447
  if (isNodeOfType(propertyKey, "Identifier") && propertyKey.name === "dangerouslySetInnerHTML" || isNodeOfType(propertyKey, "Literal") && propertyKey.value === "dangerouslySetInnerHTML") context.report({
36992
36448
  node: propertyKey,
36993
- message: MESSAGE$32
36449
+ message: MESSAGE$31
36994
36450
  });
36995
36451
  }
36996
36452
  }
@@ -37013,7 +36469,7 @@ const isMeaningfulJsxChild = (child) => {
37013
36469
  };
37014
36470
  //#endregion
37015
36471
  //#region src/plugin/rules/react-builtins/no-danger-with-children.ts
37016
- const MESSAGE$31 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
36472
+ const MESSAGE$30 = "React throws an error when you set both children & `dangerouslySetInnerHTML`.";
37017
36473
  const mergePropsShape = (target, source) => {
37018
36474
  target.hasDangerously ||= source.hasDangerously;
37019
36475
  target.hasChildren ||= source.hasChildren;
@@ -37088,7 +36544,7 @@ const noDangerWithChildren = defineRule({
37088
36544
  if (!hasChildrenProp && !hasNestedChildren) return;
37089
36545
  if (hasJsxPropIgnoreCase(opening.attributes, "dangerouslySetInnerHTML") || spreadPropsShape.hasDangerously) context.report({
37090
36546
  node: opening,
37091
- message: MESSAGE$31
36547
+ message: MESSAGE$30
37092
36548
  });
37093
36549
  },
37094
36550
  CallExpression(node) {
@@ -37101,7 +36557,7 @@ const noDangerWithChildren = defineRule({
37101
36557
  const positionalChildren = node.arguments.slice(2);
37102
36558
  if (positionalChildren.length > 1 || positionalChildren.some((argument) => !isNullishExpression(argument)) || propsShape.hasChildren) context.report({
37103
36559
  node,
37104
- message: MESSAGE$31
36560
+ message: MESSAGE$30
37105
36561
  });
37106
36562
  }
37107
36563
  })
@@ -38076,7 +37532,7 @@ const isSetStateCallInLifecycle = (setStateCall, lifecycleNames, options = {}) =
38076
37532
  //#endregion
38077
37533
  //#region src/plugin/rules/react-builtins/no-did-mount-set-state.ts
38078
37534
  const LIFECYCLE_NAMES$2 = new Set(["componentDidMount"]);
38079
- const MESSAGE$30 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
37535
+ const MESSAGE$29 = "Your users see an extra render right after mount when you call `setState` in `componentDidMount`.";
38080
37536
  const getEnclosingLifecycleFunction = (setStateCall) => {
38081
37537
  let ancestor = setStateCall.parent;
38082
37538
  while (ancestor) {
@@ -38203,7 +37659,7 @@ const noDidMountSetState = defineRule({
38203
37659
  }
38204
37660
  context.report({
38205
37661
  node: node.callee,
38206
- message: MESSAGE$30
37662
+ message: MESSAGE$29
38207
37663
  });
38208
37664
  } };
38209
37665
  }
@@ -38221,7 +37677,7 @@ const findEnclosingClass = (node) => {
38221
37677
  //#endregion
38222
37678
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
38223
37679
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
38224
- const MESSAGE$29 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
37680
+ const MESSAGE$28 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
38225
37681
  const DIFFERENCE_OPERATORS = new Set(["!=", "!=="]);
38226
37682
  const EQUALITY_OPERATORS = new Set([
38227
37683
  "==",
@@ -38615,7 +38071,7 @@ const noDidUpdateSetState = defineRule({
38615
38071
  if (isInsideDiffGuard(node, context.scopes)) return;
38616
38072
  context.report({
38617
38073
  node: node.callee,
38618
- message: MESSAGE$29
38074
+ message: MESSAGE$28
38619
38075
  });
38620
38076
  } };
38621
38077
  }
@@ -38638,7 +38094,7 @@ const isStateMemberExpression = (node) => {
38638
38094
  };
38639
38095
  //#endregion
38640
38096
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
38641
- const MESSAGE$28 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
38097
+ 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.";
38642
38098
  const shouldIgnoreMutation = (node) => {
38643
38099
  let isConstructor = false;
38644
38100
  let isInsideCallExpression = false;
@@ -38660,7 +38116,7 @@ const reportIfStateMutation = (context, reportNode, target) => {
38660
38116
  if (shouldIgnoreMutation(reportNode)) return;
38661
38117
  context.report({
38662
38118
  node: reportNode,
38663
- message: MESSAGE$28
38119
+ message: MESSAGE$27
38664
38120
  });
38665
38121
  };
38666
38122
  const noDirectMutationState = defineRule({
@@ -39011,7 +38467,7 @@ const noDocumentStartViewTransition = defineRule({
39011
38467
  });
39012
38468
  //#endregion
39013
38469
  //#region src/plugin/rules/js-performance/no-document-write.ts
39014
- const MESSAGE$27 = "`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.";
38470
+ 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.";
39015
38471
  const WRITE_METHODS = new Set(["write", "writeln"]);
39016
38472
  const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
39017
38473
  const noDocumentWrite = defineRule({
@@ -39028,7 +38484,7 @@ const noDocumentWrite = defineRule({
39028
38484
  if (methodName === null || !WRITE_METHODS.has(methodName)) return;
39029
38485
  context.report({
39030
38486
  node,
39031
- message: MESSAGE$27
38487
+ message: MESSAGE$26
39032
38488
  });
39033
38489
  } })
39034
38490
  });
@@ -42289,10 +41745,18 @@ const isCompletionSinkAfterCancellationEarlyExit = (completionSink, cancellation
42289
41745
  return false;
42290
41746
  };
42291
41747
  const isCompletionSinkGuardedByCancellationFlag = (completionSink, cancellationFlagKey, context) => isCompletionSinkInsideCancellationGuard(completionSink, cancellationFlagKey, context) || isCompletionSinkAfterCancellationEarlyExit(completionSink, cancellationFlagKey, context);
41748
+ const isDescendantOf = (node, ancestor) => {
41749
+ let currentNode = node;
41750
+ while (currentNode) {
41751
+ if (currentNode === ancestor) return true;
41752
+ currentNode = currentNode.parent ?? null;
41753
+ }
41754
+ return false;
41755
+ };
42292
41756
  const isPromiseContinuationForRequest = (functionNode, request) => {
42293
41757
  const callNode = functionNode.parent;
42294
41758
  if (!isNodeOfType(callNode, "CallExpression") || !callNode.arguments?.some((callArgument) => callArgument === functionNode) || !isNodeOfType(callNode.callee, "MemberExpression") || callNode.callee.computed || !isNodeOfType(callNode.callee.property, "Identifier") || !PROMISE_CONTINUATION_METHOD_NAMES$1.has(callNode.callee.property.name)) return false;
42295
- return isAstDescendant(request, callNode.callee.object);
41759
+ return isDescendantOf(request, callNode.callee.object);
42296
41760
  };
42297
41761
  const isAwaitedInFunction = (request, functionNode) => {
42298
41762
  let currentNode = request.parent;
@@ -42360,7 +41824,7 @@ const ALLOWED_NAMESPACES = new Set([
42360
41824
  "ReactDOM",
42361
41825
  "ReactDom"
42362
41826
  ]);
42363
- const MESSAGE$26 = "`findDOMNode` crashes your app in React 19 because it was removed.";
41827
+ const MESSAGE$25 = "`findDOMNode` crashes your app in React 19 because it was removed.";
42364
41828
  const noFindDomNode = defineRule({
42365
41829
  id: "no-find-dom-node",
42366
41830
  title: "findDOMNode breaks component encapsulation",
@@ -42371,7 +41835,7 @@ const noFindDomNode = defineRule({
42371
41835
  if (isNodeOfType(callee, "Identifier") && callee.name === "findDOMNode") {
42372
41836
  if (isImportedFromModule(node, callee.name, "react-dom")) context.report({
42373
41837
  node: callee,
42374
- message: MESSAGE$26
41838
+ message: MESSAGE$25
42375
41839
  });
42376
41840
  return;
42377
41841
  }
@@ -42382,7 +41846,7 @@ const noFindDomNode = defineRule({
42382
41846
  if (callee.property.name !== "findDOMNode") return;
42383
41847
  context.report({
42384
41848
  node: callee.property,
42385
- message: MESSAGE$26
41849
+ message: MESSAGE$25
42386
41850
  });
42387
41851
  }
42388
41852
  } })
@@ -43520,7 +42984,7 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
43520
42984
  });
43521
42985
  //#endregion
43522
42986
  //#region src/plugin/rules/performance/no-img-lazy-with-high-fetchpriority.ts
43523
- const MESSAGE$25 = "`<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.";
42987
+ 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.";
43524
42988
  const noImgLazyWithHighFetchpriority = defineRule({
43525
42989
  id: "no-img-lazy-with-high-fetchpriority",
43526
42990
  title: "Lazy image with high fetchPriority",
@@ -43534,7 +42998,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
43534
42998
  if (!fetchPriorityAttribute || getJsxPropStringValue(fetchPriorityAttribute)?.toLowerCase() !== "high") return;
43535
42999
  context.report({
43536
43000
  node: node.name,
43537
- message: MESSAGE$25
43001
+ message: MESSAGE$24
43538
43002
  });
43539
43003
  } })
43540
43004
  });
@@ -43723,7 +43187,7 @@ const noImpureStateUpdater = defineRule({
43723
43187
  });
43724
43188
  //#endregion
43725
43189
  //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
43726
- const MESSAGE$24 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
43190
+ const MESSAGE$23 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
43727
43191
  const REACT_USE_REF_OPTIONS = {
43728
43192
  allowGlobalReactNamespace: false,
43729
43193
  allowUnboundBareCalls: false
@@ -43824,7 +43288,7 @@ const noIndeterminateAttribute = defineRule({
43824
43288
  if (!inputTypeValues || !inputTypeValues.every((inputTypeValue) => inputTypeValue.toLowerCase() === "checkbox")) return;
43825
43289
  context.report({
43826
43290
  node: indeterminateAttribute,
43827
- message: MESSAGE$24
43291
+ message: MESSAGE$23
43828
43292
  });
43829
43293
  },
43830
43294
  CallExpression(node) {
@@ -43833,7 +43297,7 @@ const noIndeterminateAttribute = defineRule({
43833
43297
  if (!isProvenHtmlInputElement(receiver, context.scopes)) return;
43834
43298
  context.report({
43835
43299
  node,
43836
- message: MESSAGE$24
43300
+ message: MESSAGE$23
43837
43301
  });
43838
43302
  }
43839
43303
  };
@@ -44121,7 +43585,7 @@ const noIsMounted = defineRule({
44121
43585
  });
44122
43586
  //#endregion
44123
43587
  //#region src/plugin/rules/js-performance/no-json-parse-stringify-clone.ts
44124
- const MESSAGE$23 = "`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)`.";
43588
+ 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)`.";
44125
43589
  const isJsonMethodCall = (node, method) => {
44126
43590
  if (!isNodeOfType(node, "CallExpression")) return false;
44127
43591
  const callee = node.callee;
@@ -44185,13 +43649,13 @@ const noJsonParseStringifyClone = defineRule({
44185
43649
  if (isCatchParameterRoundTrip(firstArgument)) return;
44186
43650
  context.report({
44187
43651
  node,
44188
- message: MESSAGE$23
43652
+ message: MESSAGE$22
44189
43653
  });
44190
43654
  } })
44191
43655
  });
44192
43656
  //#endregion
44193
43657
  //#region src/plugin/rules/correctness/no-jsx-element-type.ts
44194
- const MESSAGE$22 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
43658
+ const MESSAGE$21 = "`JSX.Element` is too narrow: it excludes `null`, strings, numbers, and fragments that components commonly return. Use `React.ReactNode` instead.";
44195
43659
  const isJsxElementTypeReference = (node) => {
44196
43660
  if (!isNodeOfType(node, "TSTypeReference")) return false;
44197
43661
  const typeName = node.typeName;
@@ -44243,7 +43707,7 @@ const noJsxElementType = defineRule({
44243
43707
  if (isJsxImported) return;
44244
43708
  for (const typeAnnotation of flaggedAnnotations) context.report({
44245
43709
  node: typeAnnotation,
44246
- message: MESSAGE$22
43710
+ message: MESSAGE$21
44247
43711
  });
44248
43712
  }
44249
43713
  };
@@ -45524,7 +44988,7 @@ const noMoment = defineRule({
45524
44988
  });
45525
44989
  //#endregion
45526
44990
  //#region src/plugin/rules/react-builtins/no-multi-comp.ts
45527
- const MESSAGE$21 = "This file declares several components, so each component is harder to find, test, and change.";
44991
+ const MESSAGE$20 = "This file declares several components, so each component is harder to find, test, and change.";
45528
44992
  const resolveSettings$16 = (settings) => {
45529
44993
  const reactDoctor = settings?.["react-doctor"];
45530
44994
  return { ignoreStateless: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noMultiComp ?? {} : {}).ignoreStateless ?? false };
@@ -45876,7 +45340,7 @@ const noMultiComp = defineRule({
45876
45340
  if (isSmallFeatureModule || isLargeFeatureModule || isVeryLargeFeatureModule) return;
45877
45341
  for (const component of flagged.slice(1)) context.report({
45878
45342
  node: component.reportNode,
45879
- message: MESSAGE$21
45343
+ message: MESSAGE$20
45880
45344
  });
45881
45345
  } };
45882
45346
  }
@@ -45964,115 +45428,39 @@ const noMutableInDeps = defineRule({
45964
45428
  }
45965
45429
  });
45966
45430
  //#endregion
45967
- //#region src/plugin/utils/mutable-state-reference-analysis.ts
45968
- const REFLECT_MUTATION_METHODS = new Set(["deleteProperty", "set"]);
45969
- const isStaticMethodCallOnNamedObject = (node, objectName, methodNames) => {
45970
- const callee = stripParenExpression(node.callee);
45971
- if (!isNodeOfType(callee, "MemberExpression")) return false;
45972
- const calleeObject = stripParenExpression(callee.object);
45973
- if (!isNodeOfType(calleeObject, "Identifier") || calleeObject.name !== objectName) return false;
45974
- const methodName = getStaticPropertyName(callee);
45975
- if (!methodName || !methodNames.has(methodName)) return false;
45976
- return !findVariableInitializer(calleeObject, calleeObject.name);
45977
- };
45978
- const isExpressionRootedInMutableStateSource = (node, state) => {
45979
- let current = stripParenExpression(node);
45980
- while (current && isNodeOfType(current, "MemberExpression")) current = stripParenExpression(current.object);
45981
- return isNodeOfType(current, "Identifier") && state.mutableStateSourceNames.has(current.name) || Boolean(current && state.isAdditionalMutableStateSource?.(current));
45982
- };
45983
- const isExpressionReachableFromMutableState = (node, state) => {
45984
- if (!node) return false;
45985
- const expression = stripParenExpression(node);
45986
- return isNodeOfType(expression, "Identifier") && state.mutableStateSourceNames.has(expression.name) || isNodeOfType(expression, "MemberExpression") && isExpressionRootedInMutableStateSource(expression, state) || Boolean(state.isAdditionalMutableStateSource?.(expression));
45987
- };
45988
- const addMutableStateReferenceBindings = (pattern, state) => {
45989
- if (isNodeOfType(pattern, "Identifier")) {
45990
- state.mutableStateSourceNames.add(pattern.name);
45991
- return;
45992
- }
45993
- if (isNodeOfType(pattern, "ObjectPattern")) {
45994
- for (const property of pattern.properties) {
45995
- if (!isNodeOfType(property, "Property")) continue;
45996
- const value = property.value;
45997
- if (isNodeOfType(value, "Identifier")) state.mutableStateSourceNames.add(value.name);
45998
- else if (isNodeOfType(value, "AssignmentPattern") && isNodeOfType(value.left, "Identifier")) state.mutableStateSourceNames.add(value.left.name);
45999
- }
46000
- return;
46001
- }
46002
- if (!isNodeOfType(pattern, "ArrayPattern")) return;
46003
- for (const element of pattern.elements) if (isNodeOfType(element, "Identifier")) state.mutableStateSourceNames.add(element.name);
46004
- else if (isNodeOfType(element, "AssignmentPattern") && isNodeOfType(element.left, "Identifier")) state.mutableStateSourceNames.add(element.left.name);
46005
- };
46006
- const updateMutableStateReferencesForVariableDeclaration = (declaration, state) => {
46007
- for (const declarator of declaration.declarations) {
46008
- const bindingNames = /* @__PURE__ */ new Set();
46009
- collectPatternNames(declarator.id, bindingNames);
46010
- for (const bindingName of bindingNames) state.mutableStateSourceNames.delete(bindingName);
46011
- if (!isExpressionReachableFromMutableState(declarator.init, state)) continue;
46012
- if (isNodeOfType(declarator.id, "Identifier")) state.mutableStateSourceNames.add(declarator.id.name);
46013
- else addMutableStateReferenceBindings(declarator.id, state);
46014
- }
46015
- };
46016
- const updateMutableStateReferencesForIdentifierAssignment = (assignment, state) => {
46017
- if (!isNodeOfType(assignment.left, "Identifier")) return;
46018
- state.mutableStateSourceNames.delete(assignment.left.name);
46019
- if (isExpressionReachableFromMutableState(assignment.right, state)) state.mutableStateSourceNames.add(assignment.left.name);
46020
- };
46021
- const collectMutableStateReferenceMutations = (node, state, options = {}) => {
46022
- if (isFunctionLike$1(node)) return [];
46023
- const mutations = [];
46024
- walkAst(node, (child) => {
46025
- const candidate = stripParenExpression(child);
46026
- if (child !== node && isFunctionLike$1(candidate)) return false;
46027
- if (isNodeOfType(candidate, "AssignmentExpression")) {
46028
- const assignmentTarget = stripParenExpression(candidate.left);
46029
- if (isNodeOfType(assignmentTarget, "MemberExpression") && isExpressionRootedInMutableStateSource(candidate.left, state)) mutations.push({
46030
- node: candidate,
46031
- receiver: assignmentTarget.object
46032
- });
46033
- return;
46034
- }
46035
- if (isNodeOfType(candidate, "UpdateExpression")) {
46036
- const updateTarget = stripParenExpression(candidate.argument);
46037
- if (isNodeOfType(updateTarget, "MemberExpression") && isExpressionRootedInMutableStateSource(candidate.argument, state)) mutations.push({
46038
- node: candidate,
46039
- receiver: updateTarget.object
46040
- });
46041
- return;
45431
+ //#region src/plugin/utils/is-result-discarded-call.ts
45432
+ const isResultDiscardedCall = (callExpression) => {
45433
+ let node = callExpression;
45434
+ let parent = node.parent;
45435
+ while (parent) {
45436
+ if (isNodeOfType(parent, "ExpressionStatement")) return true;
45437
+ if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "void") return true;
45438
+ if (isNodeOfType(parent, "ArrowFunctionExpression") && parent.body === node) return true;
45439
+ if (isNodeOfType(parent, "ChainExpression")) {
45440
+ node = parent;
45441
+ parent = node.parent;
45442
+ continue;
46042
45443
  }
46043
- if (isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete") {
46044
- const deleteTarget = stripParenExpression(candidate.argument);
46045
- if (isNodeOfType(deleteTarget, "MemberExpression") && isExpressionRootedInMutableStateSource(candidate.argument, state)) mutations.push({
46046
- node: candidate,
46047
- receiver: deleteTarget.object
46048
- });
46049
- return;
45444
+ if (isNodeOfType(parent, "LogicalExpression") && parent.right === node) {
45445
+ node = parent;
45446
+ parent = node.parent;
45447
+ continue;
46050
45448
  }
46051
- if (!isNodeOfType(candidate, "CallExpression")) return;
46052
- const firstArgument = candidate.arguments[0];
46053
- if (firstArgument && !isNodeOfType(firstArgument, "SpreadElement") && isExpressionRootedInMutableStateSource(firstArgument, state) && (isStaticMethodCallOnNamedObject(candidate, "Object", OBJECT_PROPERTY_MUTATION_METHOD_NAMES) || isStaticMethodCallOnNamedObject(candidate, "Reflect", REFLECT_MUTATION_METHODS) || options.isAdditionalMutatingCall?.(candidate))) {
46054
- mutations.push({
46055
- node: candidate,
46056
- receiver: firstArgument
46057
- });
46058
- return;
45449
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === node || parent.alternate === node)) {
45450
+ node = parent;
45451
+ parent = node.parent;
45452
+ continue;
46059
45453
  }
46060
- const callee = stripParenExpression(candidate.callee);
46061
- if (!isNodeOfType(callee, "MemberExpression")) return;
46062
- const methodName = getStaticPropertyName(callee);
46063
- if (!methodName || !isExpressionRootedInMutableStateSource(callee.object, state)) return;
46064
- if (options.isProvenMutatingMethodCall) {
46065
- if (!options.isProvenMutatingMethodCall(candidate)) return;
46066
- } else {
46067
- if (!MUTATING_ARRAY_METHODS.has(methodName) && !MUTATING_COLLECTION_METHODS.has(methodName)) return;
46068
- if (MUTATING_COLLECTION_METHODS.has(methodName) && !MUTATING_ARRAY_METHODS.has(methodName) && !isResultDiscardedCall(candidate)) return;
45454
+ if (isNodeOfType(parent, "SequenceExpression")) {
45455
+ const expressions = parent.expressions ?? [];
45456
+ if (expressions[expressions.length - 1] !== node) return true;
45457
+ node = parent;
45458
+ parent = node.parent;
45459
+ continue;
46069
45460
  }
46070
- mutations.push({
46071
- node: candidate,
46072
- receiver: callee.object
46073
- });
46074
- });
46075
- return mutations;
45461
+ return false;
45462
+ }
45463
+ return false;
46076
45464
  };
46077
45465
  //#endregion
46078
45466
  //#region src/plugin/rules/state-and-effects/utils/lodash-mutator-call.ts
@@ -46165,13 +45553,14 @@ const resolveReducerFunction = (node, currentFilename) => {
46165
45553
  };
46166
45554
  //#endregion
46167
45555
  //#region src/plugin/rules/state-and-effects/no-mutating-reducer-state.ts
46168
- const MESSAGE$20 = "This reducer changes state in place, so your update is silently skipped.";
45556
+ const MESSAGE$19 = "This reducer changes state in place, so your update is silently skipped.";
46169
45557
  const SAME_REFERENCE_ARRAY_RETURN_METHODS = new Set([
46170
45558
  "copyWithin",
46171
45559
  "fill",
46172
45560
  "reverse",
46173
45561
  "sort"
46174
45562
  ]);
45563
+ const REFLECT_MUTATION_METHODS = new Set(["deleteProperty", "set"]);
46175
45564
  const cloneReducerPathState = (state) => ({
46176
45565
  originalStateReferenceNames: new Set(state.originalStateReferenceNames),
46177
45566
  mutableStateSourceNames: new Set(state.mutableStateSourceNames),
@@ -46203,11 +45592,33 @@ const isCallToImportedReactUseReducer = (node) => {
46203
45592
  const binding = findVariableInitializer(callee.object, callee.object.name);
46204
45593
  return Boolean(binding?.initializer && isReactNamespaceOrDefaultImportSpecifier(binding.initializer));
46205
45594
  };
45595
+ const isStaticMethodCallOnNamedObject = (node, objectName, methodNames) => {
45596
+ const unwrappedNode = stripParenExpression(node);
45597
+ if (!isNodeOfType(unwrappedNode, "CallExpression")) return false;
45598
+ if (!isNodeOfType(unwrappedNode.callee, "MemberExpression")) return false;
45599
+ const calleeObject = unwrappedNode.callee.object;
45600
+ if (!isNodeOfType(calleeObject, "Identifier")) return false;
45601
+ if (calleeObject.name !== objectName) return false;
45602
+ if (!methodNames.has(getStaticMemberPropertyName(unwrappedNode.callee) ?? "")) return false;
45603
+ if (findVariableInitializer(calleeObject, calleeObject.name)) return false;
45604
+ return true;
45605
+ };
45606
+ const isExpressionRootedInMutableReducerStateSource = (node, state) => {
45607
+ let current = stripParenExpression(node);
45608
+ while (current && isNodeOfType(current, "MemberExpression")) current = stripParenExpression(current.object);
45609
+ return isNodeOfType(current, "Identifier") && state.mutableStateSourceNames.has(current.name);
45610
+ };
46206
45611
  const isExpressionOriginalReducerStateReference = (node, state) => {
46207
45612
  if (!node) return false;
46208
45613
  const unwrappedNode = stripParenExpression(node);
46209
45614
  return isNodeOfType(unwrappedNode, "Identifier") && state.originalStateReferenceNames.has(unwrappedNode.name);
46210
45615
  };
45616
+ const isExpressionReachableFromOriginalReducerState = (node, state) => {
45617
+ if (!node) return false;
45618
+ if (isExpressionOriginalReducerStateReference(node, state)) return true;
45619
+ const unwrappedNode = stripParenExpression(node);
45620
+ return isNodeOfType(unwrappedNode, "MemberExpression") && isExpressionRootedInMutableReducerStateSource(unwrappedNode, state);
45621
+ };
46211
45622
  const canExpressionReturnOriginalReducerStateReference = (node, state) => {
46212
45623
  if (!node) return false;
46213
45624
  const unwrappedNode = stripParenExpression(node);
@@ -46224,7 +45635,42 @@ const canExpressionReturnOriginalReducerStateReference = (node, state) => {
46224
45635
  if (isNodeOfType(unwrappedNode, "SequenceExpression")) return canExpressionReturnOriginalReducerStateReference(unwrappedNode.expressions[unwrappedNode.expressions.length - 1], state);
46225
45636
  return false;
46226
45637
  };
46227
- const collectReducerStateMutationsInExpressionOrStatement = (node, state) => collectMutableStateReferenceMutations(node, state, { isAdditionalMutatingCall: isLodashMutatorCall });
45638
+ const collectReducerStateMutationsInExpressionOrStatement = (node, state) => {
45639
+ if (isFunctionLike$1(node)) return [];
45640
+ const mutations = [];
45641
+ walkAst(node, (child) => {
45642
+ const unwrappedChild = stripParenExpression(child);
45643
+ if (child !== node && isFunctionLike$1(unwrappedChild)) return false;
45644
+ if (isNodeOfType(unwrappedChild, "AssignmentExpression")) {
45645
+ if (isNodeOfType(stripParenExpression(unwrappedChild.left), "MemberExpression") && isExpressionRootedInMutableReducerStateSource(unwrappedChild.left, state)) mutations.push({ node: unwrappedChild });
45646
+ return;
45647
+ }
45648
+ if (isNodeOfType(unwrappedChild, "UpdateExpression")) {
45649
+ if (isNodeOfType(stripParenExpression(unwrappedChild.argument), "MemberExpression") && isExpressionRootedInMutableReducerStateSource(unwrappedChild.argument, state)) mutations.push({ node: unwrappedChild });
45650
+ return;
45651
+ }
45652
+ if (isNodeOfType(unwrappedChild, "UnaryExpression") && unwrappedChild.operator === "delete") {
45653
+ if (isNodeOfType(stripParenExpression(unwrappedChild.argument), "MemberExpression") && isExpressionRootedInMutableReducerStateSource(unwrappedChild.argument, state)) mutations.push({ node: unwrappedChild });
45654
+ return;
45655
+ }
45656
+ if (!isNodeOfType(unwrappedChild, "CallExpression")) return;
45657
+ const firstArgument = unwrappedChild.arguments?.[0];
45658
+ if (firstArgument && isExpressionRootedInMutableReducerStateSource(firstArgument, state) && (isStaticMethodCallOnNamedObject(unwrappedChild, "Object", OBJECT_PROPERTY_MUTATION_METHOD_NAMES) || isStaticMethodCallOnNamedObject(unwrappedChild, "Reflect", REFLECT_MUTATION_METHODS))) {
45659
+ mutations.push({ node: unwrappedChild });
45660
+ return;
45661
+ }
45662
+ if (firstArgument && isExpressionRootedInMutableReducerStateSource(firstArgument, state) && isLodashMutatorCall(unwrappedChild)) {
45663
+ mutations.push({ node: unwrappedChild });
45664
+ return;
45665
+ }
45666
+ if (!isNodeOfType(unwrappedChild.callee, "MemberExpression")) return;
45667
+ const methodName = getStaticMemberPropertyName(unwrappedChild.callee);
45668
+ if (!methodName || !MUTATING_ARRAY_METHODS.has(methodName) && !MUTATING_COLLECTION_METHODS.has(methodName)) return;
45669
+ if (MUTATING_COLLECTION_METHODS.has(methodName) && !MUTATING_ARRAY_METHODS.has(methodName) && !isResultDiscardedCall(unwrappedChild)) return;
45670
+ if (isExpressionRootedInMutableReducerStateSource(unwrappedChild.callee.object, state)) mutations.push({ node: unwrappedChild });
45671
+ });
45672
+ return mutations;
45673
+ };
46228
45674
  const collectBlockScopedBindingNames = (blockStatement) => {
46229
45675
  const blockScopedBindingNames = /* @__PURE__ */ new Set();
46230
45676
  for (const statement of blockStatement.body ?? []) {
@@ -46244,20 +45690,60 @@ const restoreOuterIdentityForBlockScopedNames = (blockState, outerState, blockSc
46244
45690
  }
46245
45691
  return nextState;
46246
45692
  };
45693
+ const recordDestructuredAliasNames = (pattern, state) => {
45694
+ if (isNodeOfType(pattern, "ObjectPattern")) {
45695
+ for (const property of pattern.properties ?? []) {
45696
+ if (!isNodeOfType(property, "Property")) continue;
45697
+ const valueNode = property.value;
45698
+ let leafIdentifier = null;
45699
+ if (isNodeOfType(valueNode, "Identifier")) leafIdentifier = valueNode;
45700
+ else if (isNodeOfType(valueNode, "AssignmentPattern") && isNodeOfType(valueNode.left, "Identifier")) leafIdentifier = valueNode.left;
45701
+ if (!leafIdentifier) continue;
45702
+ state.mutableStateSourceNames.add(leafIdentifier.name);
45703
+ }
45704
+ return;
45705
+ }
45706
+ if (isNodeOfType(pattern, "ArrayPattern")) for (const element of pattern.elements ?? []) {
45707
+ if (!element) continue;
45708
+ if (isNodeOfType(element, "Identifier")) {
45709
+ state.mutableStateSourceNames.add(element.name);
45710
+ continue;
45711
+ }
45712
+ if (isNodeOfType(element, "AssignmentPattern") && isNodeOfType(element.left, "Identifier")) {
45713
+ state.mutableStateSourceNames.add(element.left.name);
45714
+ continue;
45715
+ }
45716
+ if (isNodeOfType(element, "RestElement") && isNodeOfType(element.argument, "Identifier")) {}
45717
+ }
45718
+ };
46247
45719
  const updateReducerStateIdentityForVariableDeclaration = (declaration, state) => {
46248
- updateMutableStateReferencesForVariableDeclaration(declaration, state);
46249
- for (const declarator of declaration.declarations ?? []) if (isNodeOfType(declarator.id, "Identifier")) {
46250
- const name = declarator.id.name;
46251
- state.originalStateReferenceNames.delete(name);
46252
- if (isExpressionOriginalReducerStateReference(declarator.init, state)) state.originalStateReferenceNames.add(name);
45720
+ for (const declarator of declaration.declarations ?? []) {
45721
+ if (isNodeOfType(declarator.id, "Identifier")) {
45722
+ const name = declarator.id.name;
45723
+ state.originalStateReferenceNames.delete(name);
45724
+ state.mutableStateSourceNames.delete(name);
45725
+ if (isExpressionOriginalReducerStateReference(declarator.init, state)) {
45726
+ state.originalStateReferenceNames.add(name);
45727
+ state.mutableStateSourceNames.add(name);
45728
+ continue;
45729
+ }
45730
+ if (isExpressionReachableFromOriginalReducerState(declarator.init, state)) state.mutableStateSourceNames.add(name);
45731
+ continue;
45732
+ }
45733
+ if ((isNodeOfType(declarator.id, "ObjectPattern") || isNodeOfType(declarator.id, "ArrayPattern")) && isExpressionReachableFromOriginalReducerState(declarator.init, state)) recordDestructuredAliasNames(declarator.id, state);
46253
45734
  }
46254
45735
  };
46255
45736
  const updateReducerStateIdentityForIdentifierAssignment = (assignment, state) => {
46256
45737
  if (!isNodeOfType(assignment.left, "Identifier")) return;
46257
- updateMutableStateReferencesForIdentifierAssignment(assignment, state);
46258
45738
  const name = assignment.left.name;
46259
45739
  state.originalStateReferenceNames.delete(name);
46260
- if (isExpressionOriginalReducerStateReference(assignment.right, state)) state.originalStateReferenceNames.add(name);
45740
+ state.mutableStateSourceNames.delete(name);
45741
+ if (isExpressionOriginalReducerStateReference(assignment.right, state)) {
45742
+ state.originalStateReferenceNames.add(name);
45743
+ state.mutableStateSourceNames.add(name);
45744
+ return;
45745
+ }
45746
+ if (isExpressionReachableFromOriginalReducerState(assignment.right, state)) state.mutableStateSourceNames.add(name);
46261
45747
  };
46262
45748
  const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, reportedNodes, options) => {
46263
45749
  if (!isFunctionLike$1(functionNode) || !isNodeOfType(functionNode.body, "BlockStatement")) return;
@@ -46271,7 +45757,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
46271
45757
  reportedNodes.add(options.crossFileConsumerCallSite);
46272
45758
  context.report({
46273
45759
  node: options.crossFileConsumerCallSite,
46274
- message: `${MESSAGE$20} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
45760
+ message: `${MESSAGE$19} (mutation in imported reducer at \`${options.crossFileSourceDisplay}\`)`
46275
45761
  });
46276
45762
  return;
46277
45763
  }
@@ -46280,7 +45766,7 @@ const analyzeReactUseReducerFunctionForStateMutation = (context, functionNode, r
46280
45766
  reportedNodes.add(mutation.node);
46281
45767
  context.report({
46282
45768
  node: mutation.node,
46283
- message: MESSAGE$20
45769
+ message: MESSAGE$19
46284
45770
  });
46285
45771
  }
46286
45772
  };
@@ -46687,7 +46173,7 @@ const noNoninteractiveElementToInteractiveRole = defineRule({
46687
46173
  });
46688
46174
  //#endregion
46689
46175
  //#region src/plugin/rules/a11y/no-noninteractive-tabindex.ts
46690
- const MESSAGE$19 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
46176
+ const MESSAGE$18 = "Keyboard users get stuck focusing this element they can't act on because `tabIndex` makes it tabbable, so remove it.";
46691
46177
  const KEYBOARD_HANDLER_PROP_NAMES = [
46692
46178
  "onKeyDown",
46693
46179
  "onKeyUp",
@@ -46806,7 +46292,7 @@ const noNoninteractiveTabindex = defineRule({
46806
46292
  if (numeric === null) {
46807
46293
  if (isNodeOfType(tabIndexValue, "JSXExpressionContainer") && !settings.allowExpressionValues && !isKeyboardOperable(node) && !isFocusOperable(node) && !hasJsxSpreadAttribute$1(node.attributes)) context.report({
46808
46294
  node: tabIndex,
46809
- message: MESSAGE$19
46295
+ message: MESSAGE$18
46810
46296
  });
46811
46297
  return;
46812
46298
  }
@@ -46828,7 +46314,7 @@ const noNoninteractiveTabindex = defineRule({
46828
46314
  if (!roleAttribute) {
46829
46315
  context.report({
46830
46316
  node: tabIndex,
46831
- message: MESSAGE$19
46317
+ message: MESSAGE$18
46832
46318
  });
46833
46319
  return;
46834
46320
  }
@@ -46842,7 +46328,7 @@ const noNoninteractiveTabindex = defineRule({
46842
46328
  }
46843
46329
  context.report({
46844
46330
  node: tabIndex,
46845
- message: MESSAGE$19
46331
+ message: MESSAGE$18
46846
46332
  });
46847
46333
  } };
46848
46334
  }
@@ -49048,7 +48534,7 @@ const noRandomKey = defineRule({
49048
48534
  });
49049
48535
  //#endregion
49050
48536
  //#region src/plugin/rules/react-builtins/no-react-children.ts
49051
- const MESSAGE$18 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
48537
+ const MESSAGE$17 = "`React.Children` traversal depends on the runtime child shape, so wrapping or unwrapping a child can silently change what gets visited.";
49052
48538
  const isChildrenIdentifier = (node, contextNode) => {
49053
48539
  if (!isNodeOfType(node, "Identifier") || node.name !== "Children") return false;
49054
48540
  return isImportedFromModule(contextNode, "Children", "react");
@@ -49074,13 +48560,13 @@ const noReactChildren = defineRule({
49074
48560
  if (isChildrenIdentifier(memberObject, node)) {
49075
48561
  context.report({
49076
48562
  node: calleeOuter,
49077
- message: MESSAGE$18
48563
+ message: MESSAGE$17
49078
48564
  });
49079
48565
  return;
49080
48566
  }
49081
48567
  if (isReactNamespaceMember(memberObject, node)) context.report({
49082
48568
  node: calleeOuter,
49083
- message: MESSAGE$18
48569
+ message: MESSAGE$17
49084
48570
  });
49085
48571
  } })
49086
48572
  });
@@ -49962,7 +49448,7 @@ const noRenderPropChildren = defineRule({
49962
49448
  });
49963
49449
  //#endregion
49964
49450
  //#region src/plugin/rules/react-builtins/no-render-return-value.ts
49965
- const MESSAGE$17 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
49451
+ const MESSAGE$16 = "Your app breaks in React 19 because `ReactDOM.render` returns nothing there.";
49966
49452
  const isReactDomRenderCall = (node) => isGlobalMethodCall(node, "ReactDOM", "render");
49967
49453
  const isUsedAsReturnValue = (parent) => {
49968
49454
  if (!parent) return false;
@@ -49980,7 +49466,7 @@ const noRenderReturnValue = defineRule({
49980
49466
  if (!isUsedAsReturnValue(node.parent)) return;
49981
49467
  context.report({
49982
49468
  node: node.callee,
49983
- message: MESSAGE$17
49469
+ message: MESSAGE$16
49984
49470
  });
49985
49471
  } })
49986
49472
  });
@@ -51407,7 +50893,7 @@ const noSelfUpdatingEffect = defineRule({
51407
50893
  });
51408
50894
  //#endregion
51409
50895
  //#region src/plugin/rules/react-builtins/no-set-state.ts
51410
- const MESSAGE$16 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
50896
+ const MESSAGE$15 = "`this.setState` keeps local class state in a project that forbids it, so state ownership becomes harder to reason about.";
51411
50897
  const noSetState = defineRule({
51412
50898
  id: "no-set-state",
51413
50899
  title: "Local class state forbidden",
@@ -51422,7 +50908,7 @@ const noSetState = defineRule({
51422
50908
  if (!getParentComponent(node)) return;
51423
50909
  context.report({
51424
50910
  node: node.callee,
51425
- message: MESSAGE$16
50911
+ message: MESSAGE$15
51426
50912
  });
51427
50913
  } })
51428
50914
  });
@@ -51792,7 +51278,7 @@ const isAbstractRole = (openingElement, settings) => {
51792
51278
  };
51793
51279
  //#endregion
51794
51280
  //#region src/plugin/rules/a11y/no-static-element-interactions.ts
51795
- const MESSAGE$15 = "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.";
51281
+ 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.";
51796
51282
  const DEFAULT_HANDLERS = [
51797
51283
  "onClick",
51798
51284
  "onMouseDown",
@@ -51907,7 +51393,7 @@ const noStaticElementInteractions = defineRule({
51907
51393
  if (!roleAttribute || !roleAttribute.value) {
51908
51394
  context.report({
51909
51395
  node: node.name,
51910
- message: MESSAGE$15
51396
+ message: MESSAGE$14
51911
51397
  });
51912
51398
  return;
51913
51399
  }
@@ -51917,7 +51403,7 @@ const noStaticElementInteractions = defineRule({
51917
51403
  if (isRecognizedRoleString(attributeValue.value)) return;
51918
51404
  context.report({
51919
51405
  node: node.name,
51920
- message: MESSAGE$15
51406
+ message: MESSAGE$14
51921
51407
  });
51922
51408
  return;
51923
51409
  }
@@ -51925,7 +51411,7 @@ const noStaticElementInteractions = defineRule({
51925
51411
  if (!settings.allowExpressionValues) {
51926
51412
  context.report({
51927
51413
  node: node.name,
51928
- message: MESSAGE$15
51414
+ message: MESSAGE$14
51929
51415
  });
51930
51416
  return;
51931
51417
  }
@@ -51933,7 +51419,7 @@ const noStaticElementInteractions = defineRule({
51933
51419
  if (isStaticNullishExpression(expression)) {
51934
51420
  context.report({
51935
51421
  node: node.name,
51936
- message: MESSAGE$15
51422
+ message: MESSAGE$14
51937
51423
  });
51938
51424
  return;
51939
51425
  }
@@ -51943,7 +51429,7 @@ const noStaticElementInteractions = defineRule({
51943
51429
  if (branches.some((branch) => branch.role !== null && isRecognizedRoleString(branch.role))) return;
51944
51430
  context.report({
51945
51431
  node: node.name,
51946
- message: MESSAGE$15
51432
+ message: MESSAGE$14
51947
51433
  });
51948
51434
  return;
51949
51435
  }
@@ -51951,7 +51437,7 @@ const noStaticElementInteractions = defineRule({
51951
51437
  }
51952
51438
  context.report({
51953
51439
  node: node.name,
51954
- message: MESSAGE$15
51440
+ message: MESSAGE$14
51955
51441
  });
51956
51442
  } };
51957
51443
  }
@@ -52057,7 +51543,7 @@ const noStringRefs = defineRule({
52057
51543
  });
52058
51544
  //#endregion
52059
51545
  //#region src/plugin/rules/js-performance/no-sync-xhr.ts
52060
- const MESSAGE$14 = "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)`).";
51546
+ 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)`).";
52061
51547
  const isFalseLiteral = (node) => isNodeOfType(node, "Literal") && node.value === false;
52062
51548
  const PUBLIC_ASSET_PATH_PATTERN = /(?:^|\/)public\//i;
52063
51549
  const noSyncXhr = defineRule({
@@ -52078,7 +51564,7 @@ const noSyncXhr = defineRule({
52078
51564
  if (!asyncArgument || !isFalseLiteral(stripParenExpression(asyncArgument))) return;
52079
51565
  context.report({
52080
51566
  node,
52081
- message: MESSAGE$14
51567
+ message: MESSAGE$13
52082
51568
  });
52083
51569
  };
52084
51570
  return {
@@ -52103,7 +51589,7 @@ const noSyncXhr = defineRule({
52103
51589
  });
52104
51590
  //#endregion
52105
51591
  //#region src/plugin/rules/react-builtins/no-this-in-sfc.ts
52106
- const MESSAGE$13 = "This value is `undefined` because function components have no `this`.";
51592
+ const MESSAGE$12 = "This value is `undefined` because function components have no `this`.";
52107
51593
  const isInsideClassMethod = (node, customClassFactoryNames) => {
52108
51594
  let ancestor = node.parent;
52109
51595
  while (ancestor) {
@@ -52191,7 +51677,7 @@ const noThisInSfc = defineRule({
52191
51677
  if (functionHasOwnThisMemberWrite(enclosingFunction)) return;
52192
51678
  context.report({
52193
51679
  node,
52194
- message: MESSAGE$13
51680
+ message: MESSAGE$12
52195
51681
  });
52196
51682
  } };
52197
51683
  }
@@ -54561,7 +54047,7 @@ const noWideLetterSpacing = defineRule({
54561
54047
  //#endregion
54562
54048
  //#region src/plugin/rules/react-builtins/no-will-update-set-state.ts
54563
54049
  const LIFECYCLE_NAMES = new Set(["componentWillUpdate", "UNSAFE_componentWillUpdate"]);
54564
- const MESSAGE$12 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
54050
+ const MESSAGE$11 = "Calling setState in componentWillUpdate can trigger another update immediately, loop forever, and freeze the component.";
54565
54051
  const resolveSettings$7 = (settings) => {
54566
54052
  const reactDoctor = settings?.["react-doctor"];
54567
54053
  return { mode: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noWillUpdateSetState ?? {} : {}).mode ?? "allowed" };
@@ -54595,7 +54081,7 @@ const noWillUpdateSetState = defineRule({
54595
54081
  if (!isSetStateCallInLifecycle(node, activeLifecycleNames, { disallowInNestedFunctions: mode === "disallow-in-func" })) return;
54596
54082
  context.report({
54597
54083
  node: node.callee,
54598
- message: MESSAGE$12
54084
+ message: MESSAGE$11
54599
54085
  });
54600
54086
  } };
54601
54087
  }
@@ -56395,7 +55881,7 @@ const preactNoRenderArguments = defineRule({
56395
55881
  });
56396
55882
  //#endregion
56397
55883
  //#region src/plugin/rules/preact/preact-prefer-ondblclick.ts
56398
- const MESSAGE$11 = "Your users get no response from `onDoubleClick` in Preact core, where it never fires, so use `onDblClick` instead, which matches the DOM event name.";
55884
+ 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.";
56399
55885
  const preactPreferOndblclick = defineRule({
56400
55886
  id: "preact-prefer-ondblclick",
56401
55887
  title: "onDoubleClick instead of onDblClick",
@@ -56410,7 +55896,7 @@ const preactPreferOndblclick = defineRule({
56410
55896
  if (!onDoubleClickAttribute) return;
56411
55897
  context.report({
56412
55898
  node: onDoubleClickAttribute,
56413
- message: MESSAGE$11
55899
+ message: MESSAGE$10
56414
55900
  });
56415
55901
  } })
56416
55902
  });
@@ -56650,7 +56136,7 @@ const preferExplicitVariants = defineRule({
56650
56136
  });
56651
56137
  //#endregion
56652
56138
  //#region src/plugin/rules/react-builtins/prefer-function-component.ts
56653
- const MESSAGE$10 = "This class component keeps behavior in lifecycle methods, so state and effects are harder to follow than in a hook-based function component.";
56139
+ 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.";
56654
56140
  const resolveSettings$4 = (settings) => {
56655
56141
  const reactDoctor = settings?.["react-doctor"];
56656
56142
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.preferFunctionComponent ?? {} : {};
@@ -56689,7 +56175,7 @@ const preferFunctionComponent = defineRule({
56689
56175
  const reportNode = node.id ?? node;
56690
56176
  context.report({
56691
56177
  node: reportNode,
56692
- message: MESSAGE$10
56178
+ message: MESSAGE$9
56693
56179
  });
56694
56180
  };
56695
56181
  return {
@@ -58890,7 +58376,7 @@ const reactCompilerNoManualMemoization = defineRule({
58890
58376
  });
58891
58377
  //#endregion
58892
58378
  //#region src/plugin/rules/react-builtins/react-in-jsx-scope.ts
58893
- const MESSAGE$9 = "This JSX crashes because `React` isn't in scope.";
58379
+ const MESSAGE$8 = "This JSX crashes because `React` isn't in scope.";
58894
58380
  const reactInJsxScope = defineRule({
58895
58381
  id: "react-in-jsx-scope",
58896
58382
  title: "React not in scope for JSX",
@@ -58902,21 +58388,21 @@ const reactInJsxScope = defineRule({
58902
58388
  if (findVariableInitializer(node, "React")) return;
58903
58389
  context.report({
58904
58390
  node: node.name,
58905
- message: MESSAGE$9
58391
+ message: MESSAGE$8
58906
58392
  });
58907
58393
  },
58908
58394
  JSXFragment(node) {
58909
58395
  if (findVariableInitializer(node, "React")) return;
58910
58396
  context.report({
58911
58397
  node: node.openingFragment,
58912
- message: MESSAGE$9
58398
+ message: MESSAGE$8
58913
58399
  });
58914
58400
  }
58915
58401
  })
58916
58402
  });
58917
58403
  //#endregion
58918
58404
  //#region src/plugin/rules/security/react-markdown-unsanitized-raw-html.ts
58919
- const MESSAGE$8 = "React Markdown parses dynamic raw HTML when `rehype-raw` is enabled. Add `rehype-sanitize` to `rehypePlugins` or sanitize the markdown before rendering it.";
58405
+ 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.";
58920
58406
  const REACT_MARKDOWN_MODULE = "react-markdown";
58921
58407
  const REHYPE_RAW_MODULE = "rehype-raw";
58922
58408
  const REHYPE_SANITIZE_MODULE = "rehype-sanitize";
@@ -59068,7 +58554,7 @@ const reactMarkdownUnsanitizedRawHtml = defineRule({
59068
58554
  if (pluginEntries.some((entry) => isPluginFromModule(entry, REHYPE_SANITIZE_MODULE, context.scopes, /* @__PURE__ */ new Set())) || !hasDynamicUnsanitizedChildren(node, context.scopes)) return;
59069
58555
  context.report({
59070
58556
  node,
59071
- message: MESSAGE$8
58557
+ message: MESSAGE$7
59072
58558
  });
59073
58559
  } }))
59074
58560
  });
@@ -59124,7 +58610,7 @@ const inlineUseSelectorFunction = (callNode, aliases) => {
59124
58610
  };
59125
58611
  //#endregion
59126
58612
  //#region src/plugin/rules/state-and-effects/redux-useselector-inline-derivation.ts
59127
- const ALLOCATING_ARRAY_METHODS$1 = new Set([
58613
+ const ALLOCATING_ARRAY_METHODS = new Set([
59128
58614
  "filter",
59129
58615
  "map",
59130
58616
  "flatMap",
@@ -59160,7 +58646,7 @@ const getAllocatingCallSiteDescription = (expression) => {
59160
58646
  method: methodName
59161
58647
  };
59162
58648
  }
59163
- if (ALLOCATING_ARRAY_METHODS$1.has(methodName)) return {
58649
+ if (ALLOCATING_ARRAY_METHODS.has(methodName)) return {
59164
58650
  kind: "method",
59165
58651
  method: methodName
59166
58652
  };
@@ -59223,7 +58709,7 @@ const reduxUseselectorInlineDerivation = defineRule({
59223
58709
  });
59224
58710
  //#endregion
59225
58711
  //#region src/plugin/rules/state-and-effects/redux-useselector-returns-new-collection.ts
59226
- const MESSAGE$7 = "Your component re-renders on every dispatched action when useSelector returns a new object or array.";
58712
+ const MESSAGE$6 = "Your component re-renders on every dispatched action when useSelector returns a new object or array.";
59227
58713
  const isConciseBodyReturningCollection = (functionNode) => {
59228
58714
  if (!isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode, "FunctionExpression")) return false;
59229
58715
  const rawBody = functionNode.body;
@@ -59258,7 +58744,7 @@ const reduxUseselectorReturnsNewCollection = defineRule({
59258
58744
  if (!isConciseBodyReturningCollection(selectorArgument)) return;
59259
58745
  context.report({
59260
58746
  node,
59261
- message: MESSAGE$7
58747
+ message: MESSAGE$6
59262
58748
  });
59263
58749
  }
59264
58750
  };
@@ -60939,7 +60425,7 @@ const functionBodyHasReturnWithValue = (functionNode) => {
60939
60425
  //#endregion
60940
60426
  //#region src/plugin/rules/react-builtins/require-render-return.ts
60941
60427
  const RENDER_METHOD_NAME = "render";
60942
- const MESSAGE$6 = "Your users see nothing because this `render` method returns nothing.";
60428
+ const MESSAGE$5 = "Your users see nothing because this `render` method returns nothing.";
60943
60429
  const isStaticRenderKey = (key) => {
60944
60430
  if (isNodeOfType(key, "Identifier")) return key.name === RENDER_METHOD_NAME;
60945
60431
  if (isNodeOfType(key, "Literal")) return key.value === RENDER_METHOD_NAME;
@@ -60999,7 +60485,7 @@ const requireRenderReturn = defineRule({
60999
60485
  if (functionBodyHasReturnWithValue(functionNode)) return;
61000
60486
  context.report({
61001
60487
  node: host.reportNode,
61002
- message: MESSAGE$6
60488
+ message: MESSAGE$5
61003
60489
  });
61004
60490
  };
61005
60491
  return {
@@ -62248,85 +61734,6 @@ const isSetterCalledDuringRender = (root, setterName) => {
62248
61734
  return found;
62249
61735
  };
62250
61736
  //#endregion
62251
- //#region src/plugin/utils/is-descendant-without-function-boundary.ts
62252
- const isDescendantWithoutFunctionBoundary = (descendant, ancestor) => {
62253
- let current = descendant;
62254
- while (current && current !== ancestor) {
62255
- if (current !== descendant && isFunctionLike$1(current)) return false;
62256
- current = current.parent;
62257
- }
62258
- return current === ancestor;
62259
- };
62260
- //#endregion
62261
- //#region src/plugin/utils/can-execute-before-async-suspension.ts
62262
- const collectSuspensionNodes = (functionNode, options) => {
62263
- const suspensionNodes = new Set(options.suspensionNodes);
62264
- walkAst(functionNode, (node) => {
62265
- if (node !== functionNode && isFunctionLike$1(node)) return false;
62266
- if (!options.suspensionNodes && isNodeOfType(node, "AwaitExpression") || isNodeOfType(node, "ForOfStatement") && node.await) suspensionNodes.add(node);
62267
- });
62268
- return suspensionNodes;
62269
- };
62270
- const isInsideForAwaitPostSuspensionRegion = (node, functionNode) => {
62271
- let current = node;
62272
- while (current && current !== functionNode) {
62273
- const parent = current.parent;
62274
- if (parent && isNodeOfType(parent, "ForOfStatement") && parent.await && (parent.left === current || parent.body === current)) return true;
62275
- current = parent;
62276
- }
62277
- return false;
62278
- };
62279
- const suspensionBlockNode = (suspensionNode) => isNodeOfType(suspensionNode, "ForOfStatement") ? suspensionNode.right : suspensionNode;
62280
- const doesSuspensionOccurBeforeNode = (suspensionNode, node) => {
62281
- if (isNodeOfType(suspensionNode, "AwaitExpression")) {
62282
- if (isDescendantWithoutFunctionBoundary(node, suspensionNode.argument)) return false;
62283
- } else if (isNodeOfType(suspensionNode, "ForOfStatement") && isDescendantWithoutFunctionBoundary(node, suspensionNode.right)) return false;
62284
- if (isDescendantWithoutFunctionBoundary(suspensionNode, node)) return true;
62285
- const suspensionStart = getRangeStart(suspensionBlockNode(suspensionNode));
62286
- const nodeStart = getRangeStart(node);
62287
- return suspensionStart !== null && nodeStart !== null && suspensionStart < nodeStart;
62288
- };
62289
- const findExecutionNodeWithinFunction = (node, functionNode, context) => {
62290
- let executionNode = node;
62291
- let executionOwner = context.cfg.enclosingFunction(executionNode);
62292
- while (executionOwner && executionOwner !== functionNode) {
62293
- if (!isFunctionLike$1(executionOwner) || executionOwner.async || executionOwner.generator) return null;
62294
- const callExpression = findImmediatelyInvokedCallExpression(executionOwner);
62295
- if (!callExpression) return null;
62296
- executionNode = callExpression;
62297
- executionOwner = context.cfg.enclosingFunction(callExpression);
62298
- }
62299
- return executionOwner === functionNode ? executionNode : null;
62300
- };
62301
- const canExecuteBeforeAsyncSuspension = (node, functionNode, context, options = {}) => {
62302
- if (!isFunctionLike$1(functionNode) || !functionNode.async) return isNodeReachableWithinFunction(node, context);
62303
- const executionNode = findExecutionNodeWithinFunction(node, functionNode, context);
62304
- if (!executionNode || isInsideForAwaitPostSuspensionRegion(executionNode, functionNode)) return false;
62305
- const functionCfg = context.cfg.cfgFor(functionNode);
62306
- const targetBlock = functionCfg?.blockOf(executionNode);
62307
- if (!functionCfg || !targetBlock) return false;
62308
- const suspensionsByBlock = /* @__PURE__ */ new Map();
62309
- for (const suspensionNode of collectSuspensionNodes(functionNode, options)) {
62310
- const suspensionBlock = functionCfg.blockOf(suspensionBlockNode(suspensionNode));
62311
- if (!suspensionBlock) continue;
62312
- const blockSuspensions = suspensionsByBlock.get(suspensionBlock) ?? [];
62313
- blockSuspensions.push(suspensionNode);
62314
- suspensionsByBlock.set(suspensionBlock, blockSuspensions);
62315
- }
62316
- const visitedBlocks = /* @__PURE__ */ new Set();
62317
- const pendingBlocks = [functionCfg.entry];
62318
- while (pendingBlocks.length > 0) {
62319
- const block = pendingBlocks.pop();
62320
- if (!block || visitedBlocks.has(block)) continue;
62321
- visitedBlocks.add(block);
62322
- const blockSuspensions = suspensionsByBlock.get(block) ?? [];
62323
- if (block === targetBlock) return !blockSuspensions.some((suspensionNode) => doesSuspensionOccurBeforeNode(suspensionNode, executionNode));
62324
- if (blockSuspensions.length > 0) continue;
62325
- for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
62326
- }
62327
- return false;
62328
- };
62329
- //#endregion
62330
61737
  //#region src/plugin/rules/state-and-effects/utils/create-external-location-invalidation-checker.ts
62331
61738
  const HISTORY_LOCATION_MUTATION_METHOD_NAMES = new Set(["pushState", "replaceState"]);
62332
61739
  const AGGREGATE_MUTATION_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
@@ -62519,6 +61926,14 @@ const buildLocationInvalidationIndex = (componentBody, componentFunction, contex
62519
61926
  });
62520
61927
  return index;
62521
61928
  };
61929
+ const isDescendantWithoutFunctionBoundary = (descendant, ancestor) => {
61930
+ let current = descendant;
61931
+ while (current && current !== ancestor) {
61932
+ if (current !== descendant && isFunctionLike$1(current)) return false;
61933
+ current = current.parent;
61934
+ }
61935
+ return current === ancestor;
61936
+ };
62522
61937
  const areInMutuallyExclusiveConditionalBranches = (firstNode, secondNode) => {
62523
61938
  const firstBranches = /* @__PURE__ */ new Map();
62524
61939
  let current = firstNode;
@@ -62830,7 +62245,39 @@ const canNodeReachNormalFunctionExit = (node, functionNode, index) => {
62830
62245
  }
62831
62246
  return false;
62832
62247
  };
62833
- const canReactBatchMutationAfterExecution = (executionNode, mutationNode, owner, index) => (isExclusiveIntrinsicReactEventHandler(owner, index) || index.effectCallbacks.has(owner)) && canNodeReachNode(executionNode, mutationNode, index) && canExecuteBeforeAsyncSuspension(mutationNode, owner, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(owner) }) && canNodeReachNormalFunctionExit(mutationNode, owner, index);
62248
+ const canExecuteBeforeAsyncSuspension = (node, functionNode, index) => {
62249
+ if (!isFunctionLike$1(functionNode) || !functionNode.async) return isNodeReachableWithinFunction(node, index.context);
62250
+ const functionCfg = index.context.cfg.cfgFor(functionNode);
62251
+ const targetBlock = functionCfg?.blockOf(node);
62252
+ if (!functionCfg || !targetBlock) return false;
62253
+ const awaitsByBlock = /* @__PURE__ */ new Map();
62254
+ for (const awaitExpression of index.awaitExpressionsByOwner.get(functionNode) ?? []) {
62255
+ const awaitBlock = functionCfg.blockOf(awaitExpression);
62256
+ if (!awaitBlock) continue;
62257
+ const blockAwaits = awaitsByBlock.get(awaitBlock) ?? [];
62258
+ blockAwaits.push(awaitExpression);
62259
+ awaitsByBlock.set(awaitBlock, blockAwaits);
62260
+ }
62261
+ const visitedBlocks = /* @__PURE__ */ new Set();
62262
+ const pendingBlocks = [functionCfg.entry];
62263
+ const targetStart = getRangeStart(node);
62264
+ while (pendingBlocks.length > 0) {
62265
+ const block = pendingBlocks.pop();
62266
+ if (!block || visitedBlocks.has(block)) continue;
62267
+ visitedBlocks.add(block);
62268
+ const blockAwaits = awaitsByBlock.get(block) ?? [];
62269
+ if (block === targetBlock) return !blockAwaits.some((awaitExpression) => {
62270
+ if (isNodeOfType(awaitExpression, "AwaitExpression") && isDescendantWithoutFunctionBoundary(node, awaitExpression.argument)) return false;
62271
+ if (isDescendantWithoutFunctionBoundary(awaitExpression, node)) return true;
62272
+ const awaitStart = getRangeStart(awaitExpression);
62273
+ return awaitStart !== null && targetStart !== null && awaitStart < targetStart;
62274
+ });
62275
+ if (blockAwaits.length > 0) continue;
62276
+ for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
62277
+ }
62278
+ return false;
62279
+ };
62280
+ const canReactBatchMutationAfterExecution = (executionNode, mutationNode, owner, index) => (isExclusiveIntrinsicReactEventHandler(owner, index) || index.effectCallbacks.has(owner)) && canNodeReachNode(executionNode, mutationNode, index) && canExecuteBeforeAsyncSuspension(mutationNode, owner, index) && canNodeReachNormalFunctionExit(mutationNode, owner, index);
62834
62281
  const functionMaySynchronouslyMutateLocation = (functionNode, index, visitingFunctions, cycleAffectedFunctions) => {
62835
62282
  const cachedResult = index.synchronousMutationResultByFunction.get(functionNode);
62836
62283
  if (cachedResult !== void 0) return cachedResult;
@@ -62844,7 +62291,7 @@ const functionMaySynchronouslyMutateLocation = (functionNode, index, visitingFun
62844
62291
  return false;
62845
62292
  }
62846
62293
  visitingFunctions.add(functionNode);
62847
- const doesMutateSynchronously = [...collectLocationMutationExecutions(functionNode, index, visitingFunctions, cycleAffectedFunctions)].some((mutationExecution) => canExecuteBeforeAsyncSuspension(mutationExecution, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) }) && canNodeReachNormalFunctionExit(mutationExecution, functionNode, index));
62294
+ const doesMutateSynchronously = [...collectLocationMutationExecutions(functionNode, index, visitingFunctions, cycleAffectedFunctions)].some((mutationExecution) => canExecuteBeforeAsyncSuspension(mutationExecution, functionNode, index) && canNodeReachNormalFunctionExit(mutationExecution, functionNode, index));
62848
62295
  visitingFunctions.delete(functionNode);
62849
62296
  if (doesMutateSynchronously || !cycleAffectedFunctions.has(functionNode)) index.synchronousMutationResultByFunction.set(functionNode, doesMutateSynchronously);
62850
62297
  return doesMutateSynchronously;
@@ -62871,20 +62318,20 @@ const functionMustSynchronouslyRemoveLocationListener = (functionNode, registrat
62871
62318
  for (const removal of index.listenerRemovals) {
62872
62319
  if (!isDefinitelyMatchingLocationListenerRemoval(registration, removal)) continue;
62873
62320
  if (index.context.cfg.enclosingFunction(removal.callExpression) !== functionNode) continue;
62874
- if (canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) })) removalExecutions.push(removal.callExpression);
62321
+ if (canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.push(removal.callExpression);
62875
62322
  }
62876
62323
  for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
62877
62324
  const calledFunction = index.calledFunctionByExpression.get(expression);
62878
- if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) }) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, nextVisitingFunctions)) removalExecutions.push(expression);
62325
+ if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, nextVisitingFunctions)) removalExecutions.push(expression);
62879
62326
  }
62880
62327
  return doNodesCoverEveryPathFromFunctionEntry(functionNode, removalExecutions, index.context);
62881
62328
  };
62882
62329
  const collectSynchronousLocationListenerRemovalExecutions = (functionNode, registration, index) => {
62883
62330
  const removalExecutions = /* @__PURE__ */ new Set();
62884
- for (const removal of index.listenerRemovals) if (isDefinitelyMatchingLocationListenerRemoval(registration, removal) && index.context.cfg.enclosingFunction(removal.callExpression) === functionNode && canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) })) removalExecutions.add(removal.callExpression);
62331
+ for (const removal of index.listenerRemovals) if (isDefinitelyMatchingLocationListenerRemoval(registration, removal) && index.context.cfg.enclosingFunction(removal.callExpression) === functionNode && canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.add(removal.callExpression);
62885
62332
  for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
62886
62333
  const calledFunction = index.calledFunctionByExpression.get(expression);
62887
- if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index.context, { suspensionNodes: index.awaitExpressionsByOwner.get(functionNode) }) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, /* @__PURE__ */ new Set())) removalExecutions.add(expression);
62334
+ if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, /* @__PURE__ */ new Set())) removalExecutions.add(expression);
62888
62335
  }
62889
62336
  return removalExecutions;
62890
62337
  };
@@ -69880,7 +69327,7 @@ const rulesOfHooks = defineRule({
69880
69327
  });
69881
69328
  //#endregion
69882
69329
  //#region src/plugin/rules/a11y/scope.ts
69883
- const MESSAGE$5 = "The `scope` attribute only works on `<th>` cells, so screen readers get no table-header help from it here.";
69330
+ const MESSAGE$4 = "The `scope` attribute only works on `<th>` cells, so screen readers get no table-header help from it here.";
69884
69331
  const scope = defineRule({
69885
69332
  id: "scope",
69886
69333
  title: "scope attribute on non-th element",
@@ -69895,7 +69342,7 @@ const scope = defineRule({
69895
69342
  if (!HTML_TAGS.has(tag)) return;
69896
69343
  if (tag !== "th") context.report({
69897
69344
  node: scopeAttribute,
69898
- message: MESSAGE$5
69345
+ message: MESSAGE$4
69899
69346
  });
69900
69347
  } })
69901
69348
  });
@@ -69912,7 +69359,7 @@ const secretInFallback = defineRule({
69912
69359
  });
69913
69360
  //#endregion
69914
69361
  //#region src/plugin/rules/react-builtins/self-closing-comp.ts
69915
- const MESSAGE$4 = "This tag has no children, so the closing tag adds noise without changing output.";
69362
+ const MESSAGE$3 = "This tag has no children, so the closing tag adds noise without changing output.";
69916
69363
  const resolveSettings$2 = (settings) => {
69917
69364
  const reactDoctor = settings?.["react-doctor"];
69918
69365
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.selfClosingComp ?? {} : {};
@@ -69951,7 +69398,7 @@ const selfClosingComp = defineRule({
69951
69398
  if (isHtmlTag && !settings.html) return;
69952
69399
  context.report({
69953
69400
  node: node.openingElement,
69954
- message: MESSAGE$4
69401
+ message: MESSAGE$3
69955
69402
  });
69956
69403
  } };
69957
69404
  }
@@ -71335,7 +70782,7 @@ const collectJsxRuntimeImports = (program) => {
71335
70782
  };
71336
70783
  //#endregion
71337
70784
  //#region src/plugin/rules/react-builtins/style-prop-object.ts
71338
- const MESSAGE$3 = "Your styles don't render because you passed the `style` prop a string instead of an object.";
70785
+ const MESSAGE$2 = "Your styles don't render because you passed the `style` prop a string instead of an object.";
71339
70786
  const resolveSettings = (settings) => {
71340
70787
  const reactDoctor = settings?.["react-doctor"];
71341
70788
  return { allow: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.stylePropObject ?? {} : {}).allow ?? [] };
@@ -71442,7 +70889,7 @@ const stylePropObject = defineRule({
71442
70889
  if (isNodeOfType(value, "Literal") && typeof value.value === "string") {
71443
70890
  context.report({
71444
70891
  node: attribute,
71445
- message: MESSAGE$3
70892
+ message: MESSAGE$2
71446
70893
  });
71447
70894
  return;
71448
70895
  }
@@ -71450,7 +70897,7 @@ const stylePropObject = defineRule({
71450
70897
  const innerExpression = value.expression;
71451
70898
  if (innerExpression && innerExpression.type !== "JSXEmptyExpression" && isInvalidStyleExpression(innerExpression)) context.report({
71452
70899
  node: attribute,
71453
- message: MESSAGE$3
70900
+ message: MESSAGE$2
71454
70901
  });
71455
70902
  }
71456
70903
  }
@@ -71473,7 +70920,7 @@ const stylePropObject = defineRule({
71473
70920
  if (!(isNodeOfType(key, "Identifier") && key.name === "style" || isNodeOfType(key, "Literal") && key.value === "style")) continue;
71474
70921
  if (isInvalidStyleExpression(property.value)) context.report({
71475
70922
  node: property,
71476
- message: MESSAGE$3
70923
+ message: MESSAGE$2
71477
70924
  });
71478
70925
  }
71479
70926
  }
@@ -71809,7 +71256,7 @@ const svgFilterClickjackingRisk = defineRule({
71809
71256
  });
71810
71257
  //#endregion
71811
71258
  //#region src/plugin/rules/a11y/tabindex-no-positive.ts
71812
- const MESSAGE$2 = "Keyboard users get jumped out of the normal order by a positive `tabIndex`, so use `0` or `-1`.";
71259
+ const MESSAGE$1 = "Keyboard users get jumped out of the normal order by a positive `tabIndex`, so use `0` or `-1`.";
71813
71260
  const tabindexNoPositive = defineRule({
71814
71261
  id: "tabindex-no-positive",
71815
71262
  title: "Positive tabindex value",
@@ -71829,7 +71276,7 @@ const tabindexNoPositive = defineRule({
71829
71276
  } else if (tabIndex.value && isNodeOfType(tabIndex.value, "JSXExpressionContainer")) numericValue = parseJsxValue(tabIndex.value);
71830
71277
  if (numericValue !== null && numericValue > 0) context.report({
71831
71278
  node: tabIndex,
71832
- message: MESSAGE$2
71279
+ message: MESSAGE$1
71833
71280
  });
71834
71281
  } })
71835
71282
  });
@@ -72744,7 +72191,7 @@ const REACT_DEPENDENCY_ARRAY_HOOK_NAMES = new Set([
72744
72191
  "useLayoutEffect",
72745
72192
  "useMemo"
72746
72193
  ]);
72747
- const MESSAGE$1 = "Read this Valtio value from the useSnapshot result during render. Keep proxy reads for callbacks so render uses the tracked, consistent snapshot.";
72194
+ const MESSAGE = "Read this Valtio value from the useSnapshot result during render. Keep proxy reads for callbacks so render uses the tracked, consistent snapshot.";
72748
72195
  const isValtioImportSymbol = (symbol) => {
72749
72196
  if (symbol.kind !== "import") return false;
72750
72197
  const importDeclaration = symbol.declarationNode.parent;
@@ -72969,7 +72416,7 @@ const valtioNoProxyReadInRender = defineRule({
72969
72416
  reportedExpressions.add(readExpression);
72970
72417
  context.report({
72971
72418
  node: readExpression,
72972
- message: MESSAGE$1
72419
+ message: MESSAGE
72973
72420
  });
72974
72421
  }
72975
72422
  }
@@ -73707,1156 +73154,6 @@ const zodV4PreferTopLevelStringFormats = defineRule({
73707
73154
  })
73708
73155
  });
73709
73156
  //#endregion
73710
- //#region src/plugin/utils/function-returns-collection-at-path.ts
73711
- const resolveImmutableInitializer = (expression, scopes, visitedSymbolIds) => {
73712
- const candidate = stripParenExpression(expression);
73713
- if (!isNodeOfType(candidate, "Identifier")) return candidate;
73714
- const symbol = scopes.symbolFor(candidate);
73715
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return candidate;
73716
- visitedSymbolIds.add(symbol.id);
73717
- return resolveImmutableInitializer(symbol.initializer, scopes, visitedSymbolIds);
73718
- };
73719
- const expressionAtPropertyPath = (expression, propertyPath, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73720
- const candidate = resolveImmutableInitializer(expression, scopes, visitedSymbolIds);
73721
- const [propertyName, ...remainingPath] = propertyPath;
73722
- if (!propertyName) return candidate;
73723
- if (!isNodeOfType(candidate, "ObjectExpression")) return null;
73724
- for (const property of candidate.properties) {
73725
- if (!isNodeOfType(property, "Property")) continue;
73726
- if (getStaticPropertyKeyName(property) !== propertyName) continue;
73727
- return expressionAtPropertyPath(property.value, remainingPath, scopes, visitedSymbolIds);
73728
- }
73729
- return null;
73730
- };
73731
- const isGlobalConstructor = (expression, constructorNames, scopes) => {
73732
- const candidate = stripParenExpression(expression);
73733
- return isNodeOfType(candidate, "Identifier") && constructorNames.has(candidate.name) && scopes.isGlobalReference(candidate);
73734
- };
73735
- const isCollectionExpression = (expression, collectionKind, scopes) => {
73736
- const candidate = stripParenExpression(expression);
73737
- if (collectionKind === "array" && isNodeOfType(candidate, "ArrayExpression")) return true;
73738
- if (!isNodeOfType(candidate, "NewExpression")) return false;
73739
- return isGlobalConstructor(candidate.callee, collectionKind === "array" ? new Set(["Array"]) : new Set(["Map", "Set"]), scopes);
73740
- };
73741
- const functionReturnsCollectionAtPath = ({ collectionKind, functionNode, propertyPath, scopes }) => {
73742
- if (!isFunctionLike$1(functionNode)) return false;
73743
- const returnedExpressions = isNodeOfType(functionNode.body, "BlockStatement") ? collectFunctionReturnStatements(functionNode).flatMap((returnStatement) => returnStatement.argument ? [returnStatement.argument] : []) : [functionNode.body];
73744
- return returnedExpressions.length > 0 && returnedExpressions.every((returnedExpression) => {
73745
- const collectionExpression = expressionAtPropertyPath(returnedExpression, propertyPath, scopes);
73746
- return Boolean(collectionExpression && isCollectionExpression(collectionExpression, collectionKind, scopes));
73747
- });
73748
- };
73749
- //#endregion
73750
- //#region src/plugin/utils/resolve-zustand-api.ts
73751
- const ZUSTAND_APIS_BY_MODULE = new Map([
73752
- ["zustand", new Set([
73753
- "create",
73754
- "createStore",
73755
- "useStore"
73756
- ])],
73757
- ["zustand/vanilla", new Set(["createStore"])],
73758
- ["zustand/react", new Set(["useStore"])],
73759
- ["zustand/traditional", new Set(["createWithEqualityFn", "useStoreWithEqualityFn"])],
73760
- ["zustand/shallow", new Set(["shallow", "useShallow"])],
73761
- ["zustand/react/shallow", new Set(["useShallow"])],
73762
- ["zustand/middleware", new Set([
73763
- "combine",
73764
- "createJSONStorage",
73765
- "devtools",
73766
- "persist",
73767
- "redux",
73768
- "subscribeWithSelector"
73769
- ])],
73770
- ["zustand/middleware/immer", new Set(["immer"])]
73771
- ]);
73772
- const STATE_CREATOR_MIDDLEWARE_ARGUMENT_INDEX = new Map([
73773
- ["combine", 1],
73774
- ["devtools", 0],
73775
- ["immer", 0],
73776
- ["persist", 0],
73777
- ["subscribeWithSelector", 0]
73778
- ]);
73779
- const toZustandApiName = (value) => {
73780
- switch (value) {
73781
- case "combine":
73782
- case "create":
73783
- case "createJSONStorage":
73784
- case "createStore":
73785
- case "createWithEqualityFn":
73786
- case "devtools":
73787
- case "immer":
73788
- case "persist":
73789
- case "redux":
73790
- case "shallow":
73791
- case "subscribeWithSelector":
73792
- case "useShallow":
73793
- case "useStore":
73794
- case "useStoreWithEqualityFn": return value;
73795
- default: return null;
73796
- }
73797
- };
73798
- const getImportSource = (symbol) => {
73799
- const declaration = getImportDeclarationForSymbol(symbol);
73800
- if (!declaration || isTypeOnlyImport(declaration) || isNodeOfType(symbol.declarationNode, "ImportSpecifier") && symbol.declarationNode.importKind === "type") return null;
73801
- return typeof declaration.source.value === "string" ? declaration.source.value : null;
73802
- };
73803
- const bindingFromImportSymbol = (symbol) => {
73804
- const moduleSource = getImportSource(symbol);
73805
- const supportedApiNames = moduleSource ? ZUSTAND_APIS_BY_MODULE.get(moduleSource) : null;
73806
- if (!moduleSource || !supportedApiNames) return null;
73807
- if (moduleSource === "zustand" && isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) return {
73808
- apiName: "create",
73809
- moduleSource
73810
- };
73811
- const importedName = getImportedName(symbol.declarationNode);
73812
- const apiName = importedName ? toZustandApiName(importedName) : null;
73813
- return apiName && supportedApiNames.has(apiName) ? {
73814
- apiName,
73815
- moduleSource
73816
- } : null;
73817
- };
73818
- const namespaceImportSource = (expression, scopes) => {
73819
- if (!isNodeOfType(expression, "Identifier")) return null;
73820
- const symbol = resolveConstIdentifierAlias(expression, scopes);
73821
- if (!symbol || !isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) return null;
73822
- return getImportSource(symbol);
73823
- };
73824
- const resolveZustandApiBinding = (expression, scopes) => {
73825
- const candidate = stripParenExpression(expression);
73826
- if (isNodeOfType(candidate, "Identifier")) {
73827
- const symbol = resolveConstIdentifierAlias(candidate, scopes);
73828
- if (!symbol) return null;
73829
- if (symbol.kind === "import") return bindingFromImportSymbol(symbol);
73830
- if (symbol.kind !== "const" || !symbol.initializer) return null;
73831
- return resolveZustandApiBinding(symbol.initializer, scopes);
73832
- }
73833
- if (!isNodeOfType(candidate, "MemberExpression")) return null;
73834
- const propertyName = getStaticPropertyName(candidate);
73835
- const moduleSource = namespaceImportSource(stripParenExpression(candidate.object), scopes);
73836
- const supportedApiNames = moduleSource ? ZUSTAND_APIS_BY_MODULE.get(moduleSource) : null;
73837
- const apiName = propertyName ? toZustandApiName(propertyName) : null;
73838
- return apiName && moduleSource && supportedApiNames?.has(apiName) ? {
73839
- apiName,
73840
- moduleSource
73841
- } : null;
73842
- };
73843
- const resolveStateCreator = (expression, scopes, middlewareNames, visitedExpressions = /* @__PURE__ */ new Set()) => {
73844
- const candidate = stripParenExpression(expression);
73845
- if (visitedExpressions.has(candidate)) return null;
73846
- visitedExpressions.add(candidate);
73847
- const creatorFunction = resolveExactLocalFunction(expression, scopes);
73848
- if (isFunctionLike$1(creatorFunction)) return creatorFunction;
73849
- if (!isNodeOfType(candidate, "CallExpression")) return null;
73850
- const middleware = resolveZustandApiBinding(candidate.callee, scopes);
73851
- const creatorArgumentIndex = middleware ? STATE_CREATOR_MIDDLEWARE_ARGUMENT_INDEX.get(middleware.apiName) : null;
73852
- if (!middleware || creatorArgumentIndex === void 0 || creatorArgumentIndex === null) return null;
73853
- const creatorArgument = candidate.arguments[creatorArgumentIndex];
73854
- if (!creatorArgument || isNodeOfType(creatorArgument, "SpreadElement")) return null;
73855
- middlewareNames.add(middleware.apiName);
73856
- return resolveStateCreator(creatorArgument, scopes, middlewareNames, visitedExpressions);
73857
- };
73858
- const isStoreFactoryApi = (binding) => binding?.apiName === "create" || binding?.apiName === "createStore" || binding?.apiName === "createWithEqualityFn";
73859
- const resolveCurriedStoreFactoryBinding = (expression, scopes) => {
73860
- const candidate = stripParenExpression(expression);
73861
- if (isNodeOfType(candidate, "Identifier")) {
73862
- const symbol = resolveConstIdentifierAlias(candidate, scopes);
73863
- if (symbol?.kind !== "const" || !symbol.initializer) return null;
73864
- return resolveCurriedStoreFactoryBinding(symbol.initializer, scopes);
73865
- }
73866
- if (!isNodeOfType(candidate, "CallExpression") || candidate.arguments.length > 0) return null;
73867
- const factoryBinding = resolveZustandApiBinding(candidate.callee, scopes);
73868
- return isStoreFactoryApi(factoryBinding) ? factoryBinding : null;
73869
- };
73870
- const resolveZustandStoreCreator = (callExpression, scopes) => {
73871
- const factoryCall = resolveZustandStoreFactoryCall(callExpression, scopes);
73872
- if (!factoryCall) return null;
73873
- const middlewareNames = /* @__PURE__ */ new Set();
73874
- const creatorFunction = resolveStateCreator(factoryCall.creatorArgument, scopes, middlewareNames);
73875
- if (!creatorFunction) return null;
73876
- return {
73877
- creatorFunction,
73878
- factoryApiName: factoryCall.factoryApiName,
73879
- middlewareNames
73880
- };
73881
- };
73882
- const resolveZustandStoreFactoryCall = (callExpression, scopes) => {
73883
- let factoryBinding = resolveZustandApiBinding(callExpression.callee, scopes);
73884
- if (!isStoreFactoryApi(factoryBinding)) {
73885
- factoryBinding = resolveCurriedStoreFactoryBinding(callExpression.callee, scopes);
73886
- if (!isStoreFactoryApi(factoryBinding)) return null;
73887
- }
73888
- const creatorArgument = callExpression.arguments[0];
73889
- if (!creatorArgument || isNodeOfType(creatorArgument, "SpreadElement")) return null;
73890
- return {
73891
- callExpression,
73892
- creatorArgument,
73893
- factoryApiName: factoryBinding.apiName
73894
- };
73895
- };
73896
- //#endregion
73897
- //#region src/plugin/rules/state-and-effects/zustand-no-fresh-selector-result.ts
73898
- const ALLOCATING_ARRAY_METHODS = new Set([
73899
- "filter",
73900
- "flat",
73901
- "flatMap",
73902
- "map",
73903
- "toReversed",
73904
- "toSorted",
73905
- "toSpliced",
73906
- "with"
73907
- ]);
73908
- const SAME_REFERENCE_ARRAY_METHODS = new Set(["reverse", "sort"]);
73909
- const ALLOCATING_NAMESPACE_METHOD_KINDS = new Map([["Array", new Map([["from", "array"], ["of", "array"]])], ["Object", new Map([
73910
- ["create", "object"],
73911
- ["entries", "array"],
73912
- ["fromEntries", "object"],
73913
- ["keys", "array"],
73914
- ["values", "array"]
73915
- ])]]);
73916
- const isNullishEqualityArgument = (argument, scopes) => {
73917
- const candidate = stripParenExpression(argument);
73918
- return isNodeOfType(candidate, "Identifier") && candidate.name === "undefined" && scopes.isGlobalReference(candidate) || isNodeOfType(candidate, "Literal") && candidate.value === null || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "void";
73919
- };
73920
- const hasExplicitEqualityArgument = (argumentsList, equalityArgumentIndex, scopes) => {
73921
- const equalityArgument = argumentsList[equalityArgumentIndex];
73922
- if (!equalityArgument) return false;
73923
- if (isNodeOfType(equalityArgument, "SpreadElement")) return true;
73924
- return !isNullishEqualityArgument(equalityArgument, scopes);
73925
- };
73926
- const resolveZustandStoreCreation = (expression, scopes) => {
73927
- const candidate = stripParenExpression(expression);
73928
- if (!isNodeOfType(candidate, "CallExpression")) return null;
73929
- const factoryCall = resolveZustandStoreFactoryCall(candidate, scopes);
73930
- if (!factoryCall || factoryCall.factoryApiName !== "create" && factoryCall.factoryApiName !== "createWithEqualityFn") return null;
73931
- return {
73932
- creatorFunction: resolveZustandStoreCreator(candidate, scopes)?.creatorFunction ?? null,
73933
- hasDefaultEquality: factoryCall.factoryApiName === "createWithEqualityFn" && hasExplicitEqualityArgument(candidate.arguments, 1, scopes),
73934
- supportsEqualityArgument: factoryCall.factoryApiName === "createWithEqualityFn"
73935
- };
73936
- };
73937
- const resolveZustandBoundStore = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73938
- const candidate = stripParenExpression(expression);
73939
- if (!isNodeOfType(candidate, "Identifier")) return null;
73940
- const symbol = scopes.symbolFor(candidate);
73941
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73942
- visitedSymbolIds.add(symbol.id);
73943
- return resolveZustandStoreCreation(symbol.initializer, scopes) ?? resolveZustandBoundStore(symbol.initializer, scopes, visitedSymbolIds);
73944
- };
73945
- const getZustandSelectorCall = (callExpression, scopes) => {
73946
- const apiBinding = resolveZustandApiBinding(callExpression.callee, scopes);
73947
- if (apiBinding?.apiName === "useStore" || apiBinding?.apiName === "useStoreWithEqualityFn") {
73948
- const selector = callExpression.arguments[1];
73949
- if (!selector || isNodeOfType(selector, "SpreadElement")) return null;
73950
- if (apiBinding.apiName === "useStoreWithEqualityFn" && hasExplicitEqualityArgument(callExpression.arguments, 2, scopes)) return null;
73951
- return {
73952
- selector,
73953
- storeCreatorFunction: null
73954
- };
73955
- }
73956
- const boundStore = resolveZustandBoundStore(callExpression.callee, scopes);
73957
- const selector = callExpression.arguments[0];
73958
- if (!boundStore || !selector || isNodeOfType(selector, "SpreadElement")) return null;
73959
- if (boundStore.hasDefaultEquality || boundStore.supportsEqualityArgument && hasExplicitEqualityArgument(callExpression.arguments, 1, scopes)) return null;
73960
- return {
73961
- selector,
73962
- storeCreatorFunction: boundStore.creatorFunction
73963
- };
73964
- };
73965
- const isUseShallowCall = (callExpression, scopes) => resolveZustandApiBinding(callExpression.callee, scopes)?.apiName === "useShallow";
73966
- const resolveSelectorFunction = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73967
- const candidate = stripParenExpression(expression);
73968
- if (isFunctionLike$1(candidate)) return candidate;
73969
- if (isNodeOfType(candidate, "CallExpression")) {
73970
- if (isUseShallowCall(candidate, scopes)) return null;
73971
- if (!isReactApiCall(candidate, "useCallback", scopes, {
73972
- allowGlobalReactNamespace: true,
73973
- resolveNamedAliases: true
73974
- })) return null;
73975
- const callback = candidate.arguments[0];
73976
- if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
73977
- return resolveSelectorFunction(callback, scopes, visitedSymbolIds);
73978
- }
73979
- if (!isNodeOfType(candidate, "Identifier")) return null;
73980
- const symbol = scopes.symbolFor(candidate);
73981
- if (!symbol || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73982
- if (symbol.kind === "function" && isFunctionLike$1(symbol.declarationNode)) return symbol.declarationNode;
73983
- if (symbol.kind !== "const" || !symbol.initializer) return null;
73984
- visitedSymbolIds.add(symbol.id);
73985
- return resolveSelectorFunction(symbol.initializer, scopes, visitedSymbolIds);
73986
- };
73987
- const selectorStatePropertyPath = (expression, analysis, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
73988
- const candidate = stripParenExpression(expression);
73989
- if (isNodeOfType(candidate, "Identifier")) {
73990
- const symbol = analysis.scopes.symbolFor(candidate);
73991
- const selectorParameter = isFunctionLike$1(analysis.selectorFunction) ? analysis.selectorFunction.params[0] : null;
73992
- if (selectorParameter && isNodeOfType(selectorParameter, "Identifier") && symbol?.id === analysis.scopes.symbolFor(selectorParameter)?.id) return [];
73993
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
73994
- visitedSymbolIds.add(symbol.id);
73995
- return selectorStatePropertyPath(symbol.initializer, analysis, visitedSymbolIds);
73996
- }
73997
- if (!isNodeOfType(candidate, "MemberExpression")) return null;
73998
- const propertyName = getStaticPropertyName(candidate);
73999
- const objectPath = selectorStatePropertyPath(candidate.object, analysis, visitedSymbolIds);
74000
- return propertyName && objectPath ? [...objectPath, propertyName] : null;
74001
- };
74002
- const freshResultFromAllocatingCall = (expression, analysis, visitedSymbolIds) => {
74003
- const callee = stripParenExpression(expression.callee);
74004
- if (!isNodeOfType(callee, "MemberExpression")) return null;
74005
- const methodName = getStaticPropertyName(callee);
74006
- if (!methodName) return null;
74007
- const receiver = stripParenExpression(callee.object);
74008
- if (isNodeOfType(receiver, "Identifier") && analysis.scopes.isGlobalReference(receiver)) {
74009
- const resultKind = ALLOCATING_NAMESPACE_METHOD_KINDS.get(receiver.name)?.get(methodName);
74010
- if (resultKind) return {
74011
- kind: resultKind,
74012
- node: expression
74013
- };
74014
- if (receiver.name === "Object" && methodName === "assign") {
74015
- const target = expression.arguments[0];
74016
- if (!target || isNodeOfType(target, "SpreadElement")) return null;
74017
- const freshTarget = resolveFreshSelectorResult(target, analysis, new Set(visitedSymbolIds));
74018
- return freshTarget ? {
74019
- kind: freshTarget.kind,
74020
- node: expression
74021
- } : null;
74022
- }
74023
- }
74024
- if (ALLOCATING_ARRAY_METHODS.has(methodName)) {
74025
- if (resolveFreshSelectorResult(receiver, analysis, new Set(visitedSymbolIds))?.kind === "array") return {
74026
- kind: "array",
74027
- node: expression
74028
- };
74029
- const receiverPath = selectorStatePropertyPath(receiver, analysis);
74030
- if (receiverPath && analysis.storeCreatorFunction && functionReturnsCollectionAtPath({
74031
- collectionKind: "array",
74032
- functionNode: analysis.storeCreatorFunction,
74033
- propertyPath: receiverPath,
74034
- scopes: analysis.scopes
74035
- })) return {
74036
- kind: "array",
74037
- node: expression
74038
- };
74039
- return null;
74040
- }
74041
- if (!SAME_REFERENCE_ARRAY_METHODS.has(methodName)) return null;
74042
- return resolveFreshSelectorResult(receiver, analysis, new Set(visitedSymbolIds)) ? {
74043
- kind: "array",
74044
- node: expression
74045
- } : null;
74046
- };
74047
- const resolveFreshSelectorResult = (expression, analysis, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
74048
- const freshRenderValue = resolveFreshRenderValue(expression, analysis.scopes);
74049
- if (freshRenderValue?.kind === "array" || freshRenderValue?.kind === "function" || freshRenderValue?.kind === "instance" || freshRenderValue?.kind === "object") return {
74050
- kind: freshRenderValue.kind,
74051
- node: expression
74052
- };
74053
- const candidate = stripParenExpression(expression);
74054
- if (isNodeOfType(candidate, "CallExpression")) return freshResultFromAllocatingCall(candidate, analysis, visitedSymbolIds);
74055
- if (isNodeOfType(candidate, "ConditionalExpression")) return resolveFreshSelectorResult(candidate.consequent, analysis, new Set(visitedSymbolIds)) ?? resolveFreshSelectorResult(candidate.alternate, analysis, new Set(visitedSymbolIds));
74056
- if (isNodeOfType(candidate, "LogicalExpression")) {
74057
- if (candidate.operator === "&&") return resolveFreshSelectorResult(candidate.right, analysis, visitedSymbolIds);
74058
- return resolveFreshSelectorResult(candidate.left, analysis, new Set(visitedSymbolIds)) ?? resolveFreshSelectorResult(candidate.right, analysis, new Set(visitedSymbolIds));
74059
- }
74060
- if (isNodeOfType(candidate, "SequenceExpression")) {
74061
- const returnedExpression = candidate.expressions[candidate.expressions.length - 1];
74062
- return returnedExpression ? resolveFreshSelectorResult(returnedExpression, analysis, visitedSymbolIds) : null;
74063
- }
74064
- if (!isNodeOfType(candidate, "Identifier")) return null;
74065
- const symbol = analysis.scopes.symbolFor(candidate);
74066
- if (symbol?.kind !== "const" || symbol.scope.kind === "module" || !symbol.initializer || visitedSymbolIds.has(symbol.id) || symbol.references.some((reference) => reference.flag !== "read")) return null;
74067
- visitedSymbolIds.add(symbol.id);
74068
- return resolveFreshSelectorResult(symbol.initializer, analysis, visitedSymbolIds);
74069
- };
74070
- const findFreshSelectorReturn = (selectorFunction, scopes, storeCreatorFunction) => {
74071
- if (!isFunctionLike$1(selectorFunction) || !selectorFunction.body) return null;
74072
- const analysis = {
74073
- scopes,
74074
- selectorFunction,
74075
- storeCreatorFunction
74076
- };
74077
- if (!isNodeOfType(selectorFunction.body, "BlockStatement")) return resolveFreshSelectorResult(selectorFunction.body, analysis);
74078
- let freshResult = null;
74079
- walkAst(selectorFunction.body, (candidate) => {
74080
- if (freshResult) return false;
74081
- if (candidate !== selectorFunction.body && isFunctionLike$1(candidate)) return false;
74082
- if (!isNodeOfType(candidate, "ReturnStatement") || !candidate.argument) return;
74083
- freshResult = resolveFreshSelectorResult(candidate.argument, analysis);
74084
- return freshResult ? false : void 0;
74085
- });
74086
- return freshResult;
74087
- };
74088
- const zustandNoFreshSelectorResult = defineRule({
74089
- id: "zustand-no-fresh-selector-result",
74090
- title: "Zustand selector returns a fresh value",
74091
- severity: "error",
74092
- category: "Performance",
74093
- requires: ["zustand", "zustand:5"],
74094
- recommendation: "Select a stable store field, split the selector, or wrap a collection selector with `useShallow`.",
74095
- create: (context) => ({ CallExpression(node) {
74096
- const selectorCall = getZustandSelectorCall(node, context.scopes);
74097
- if (!selectorCall) return;
74098
- const selectorFunction = resolveSelectorFunction(selectorCall.selector, context.scopes);
74099
- if (!selectorFunction) return;
74100
- const freshResult = findFreshSelectorReturn(selectorFunction, context.scopes, selectorCall.storeCreatorFunction);
74101
- if (!freshResult) return;
74102
- context.report({
74103
- node: freshResult.node,
74104
- 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`."
74105
- });
74106
- } })
74107
- });
74108
- //#endregion
74109
- //#region src/plugin/rules/state-and-effects/zustand-no-get-during-initialization.ts
74110
- const findGetParameterIdentifier = (parameter) => {
74111
- if (!parameter) return null;
74112
- if (isNodeOfType(parameter, "Identifier")) return parameter;
74113
- if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
74114
- return null;
74115
- };
74116
- const isGetParameterCall = (callExpression, getParameterSymbol, context) => {
74117
- const callee = stripParenExpression(callExpression.callee);
74118
- if (!isNodeOfType(callee, "Identifier")) return false;
74119
- return resolveConstIdentifierAlias(callee, context.scopes)?.id === getParameterSymbol.id;
74120
- };
74121
- const reportEagerGetCalls = (creatorFunction, getParameterSymbol, context) => {
74122
- const visitedFunctions = /* @__PURE__ */ new Set();
74123
- const reportFunctionReads = (functionNode) => {
74124
- if (!isFunctionLike$1(functionNode) || functionNode.generator || visitedFunctions.has(functionNode)) return;
74125
- visitedFunctions.add(functionNode);
74126
- walkAst(functionNode, (node) => {
74127
- if (node !== functionNode && isFunctionLike$1(node)) return false;
74128
- if (!isNodeOfType(node, "CallExpression")) return;
74129
- if (isGetParameterCall(node, getParameterSymbol, context)) {
74130
- if (!canExecuteBeforeAsyncSuspension(node, functionNode, context)) return;
74131
- context.report({
74132
- node,
74133
- message: "`get()` runs before Zustand installs the initial state, so it returns `undefined` here."
74134
- });
74135
- return;
74136
- }
74137
- if (!canExecuteBeforeAsyncSuspension(node, functionNode, context)) return;
74138
- const calledFunction = resolveExactLocalFunction(node.callee, context.scopes);
74139
- if (!calledFunction || calledFunction === functionNode) return;
74140
- reportFunctionReads(calledFunction);
74141
- });
74142
- };
74143
- reportFunctionReads(creatorFunction);
74144
- };
74145
- const zustandNoGetDuringInitialization = defineRule({
74146
- id: "zustand-no-get-during-initialization",
74147
- title: "Zustand get() called during store initialization",
74148
- severity: "error",
74149
- category: "Correctness",
74150
- recommendation: "Move the read into a deferred store action or derive the initial value without calling `get()`.",
74151
- requires: ["zustand", "zustand:1"],
74152
- create: (context) => {
74153
- const creatorFunctions = /* @__PURE__ */ new Set();
74154
- return {
74155
- CallExpression(node) {
74156
- const creator = resolveZustandStoreCreator(node, context.scopes);
74157
- if (creator) creatorFunctions.add(creator.creatorFunction);
74158
- },
74159
- "Program:exit"() {
74160
- for (const creatorFunction of creatorFunctions) {
74161
- if (creatorFunction.generator) continue;
74162
- const parameterIdentifier = findGetParameterIdentifier(creatorFunction.params[1]);
74163
- const getParameterSymbol = parameterIdentifier ? context.scopes.symbolFor(parameterIdentifier) : null;
74164
- if (!getParameterSymbol) continue;
74165
- reportEagerGetCalls(creatorFunction, getParameterSymbol, context);
74166
- }
74167
- }
74168
- };
74169
- }
74170
- });
74171
- //#endregion
74172
- //#region src/plugin/rules/state-and-effects/zustand-no-mutating-state.ts
74173
- const MESSAGE = "This Zustand state reference is mutated and reused, so subscribers can miss the update.";
74174
- const FRESH_ARRAY_METHOD_NAMES = new Set([
74175
- "concat",
74176
- "filter",
74177
- "flat",
74178
- "flatMap",
74179
- "map",
74180
- "slice",
74181
- "toReversed",
74182
- "toSorted",
74183
- "toSpliced",
74184
- "with"
74185
- ]);
74186
- const UNSUPPORTED_CONTROL_FLOW_TYPES = new Set([
74187
- "DoWhileStatement",
74188
- "ForInStatement",
74189
- "ForOfStatement",
74190
- "ForStatement",
74191
- "IfStatement",
74192
- "LabeledStatement",
74193
- "BlockStatement",
74194
- "SwitchStatement",
74195
- "TryStatement",
74196
- "WhileStatement",
74197
- "WithStatement"
74198
- ]);
74199
- const UNSUPPORTED_SNAPSHOT_EXPRESSION_CONTROL_FLOW_TYPES = new Set(["ConditionalExpression", "LogicalExpression"]);
74200
- const findIdentifierParameter = (parameter) => {
74201
- if (!parameter) return null;
74202
- if (isNodeOfType(parameter, "Identifier")) return parameter;
74203
- if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
74204
- return null;
74205
- };
74206
- const symbolForParameter = (creatorFunction, parameterIndex, context) => {
74207
- const parameter = findIdentifierParameter(creatorFunction.params[parameterIndex]);
74208
- return parameter ? context.scopes.symbolFor(parameter) ?? null : null;
74209
- };
74210
- const isCallToSymbol = (callExpression, symbolIds, context) => {
74211
- const callee = stripParenExpression(callExpression.callee);
74212
- if (!isNodeOfType(callee, "Identifier")) return false;
74213
- const symbol = resolveConstIdentifierAlias(callee, context.scopes);
74214
- return Boolean(symbol && symbolIds.has(symbol.id));
74215
- };
74216
- const rootCallForExpression = (expression) => {
74217
- let current = stripParenExpression(expression);
74218
- while (isNodeOfType(current, "MemberExpression")) current = stripParenExpression(current.object);
74219
- return isNodeOfType(current, "CallExpression") ? current : null;
74220
- };
74221
- const storeSymbolIdForMethodCall = (callExpression, methodName, context) => {
74222
- const callee = stripParenExpression(callExpression.callee);
74223
- if (!isNodeOfType(callee, "MemberExpression")) return null;
74224
- if (getStaticPropertyName(callee) !== methodName) return null;
74225
- const receiver = stripParenExpression(callee.object);
74226
- if (!isNodeOfType(receiver, "Identifier")) return null;
74227
- return resolveConstIdentifierAlias(receiver, context.scopes)?.id ?? null;
74228
- };
74229
- const isStoreMethodCall = (callExpression, methodName, storeSymbolIds, context) => {
74230
- const storeSymbolId = storeSymbolIdForMethodCall(callExpression, methodName, context);
74231
- return storeSymbolId !== null && storeSymbolIds.has(storeSymbolId);
74232
- };
74233
- const isSnapshotExpression = (expression, getSymbolIds, storeSymbolIds, context) => {
74234
- if (!expression) return false;
74235
- const rootCall = rootCallForExpression(expression);
74236
- return Boolean(rootCall && (isCallToSymbol(rootCall, getSymbolIds, context) || isStoreMethodCall(rootCall, "getState", storeSymbolIds, context)));
74237
- };
74238
- const expressionKeyPreservesTarget = (expression, targetKey, context) => {
74239
- const expressionKey = resolveExpressionKey$1(expression, context);
74240
- return Boolean(expressionKey && (expressionKey === targetKey || targetKey.startsWith(`${expressionKey}.`)));
74241
- };
74242
- const expressionPreservesTarget = (expression, targetKey, mutationNode, context) => {
74243
- if (!expression) return false;
74244
- const candidate = stripParenExpression(expression);
74245
- if (candidate === mutationNode) return true;
74246
- if (isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "MemberExpression")) return expressionKeyPreservesTarget(candidate, targetKey, context);
74247
- if (isNodeOfType(candidate, "ObjectExpression")) return candidate.properties.some((property) => {
74248
- if (isNodeOfType(property, "SpreadElement")) {
74249
- const spreadKey = resolveExpressionKey$1(property.argument, context);
74250
- return Boolean(spreadKey && spreadKey !== targetKey && targetKey.startsWith(`${spreadKey}.`));
74251
- }
74252
- return isNodeOfType(property, "Property") && expressionPreservesTarget(property.value, targetKey, mutationNode, context);
74253
- });
74254
- if (isNodeOfType(candidate, "ArrayExpression")) return candidate.elements.some((element) => Boolean(element) && !isNodeOfType(element, "SpreadElement") && expressionPreservesTarget(element, targetKey, mutationNode, context));
74255
- if (isNodeOfType(candidate, "ConditionalExpression")) return expressionPreservesTarget(candidate.consequent, targetKey, mutationNode, context) || expressionPreservesTarget(candidate.alternate, targetKey, mutationNode, context);
74256
- if (isNodeOfType(candidate, "LogicalExpression")) return expressionPreservesTarget(candidate.left, targetKey, mutationNode, context) || expressionPreservesTarget(candidate.right, targetKey, mutationNode, context);
74257
- if (isNodeOfType(candidate, "SequenceExpression")) return expressionPreservesTarget(candidate.expressions[candidate.expressions.length - 1], targetKey, mutationNode, context);
74258
- return false;
74259
- };
74260
- const expressionContainsFreshCloneOfTarget = (expression, targetKey, context) => {
74261
- if (!expression) return false;
74262
- let didFindFreshClone = false;
74263
- walkAst(expression, (node) => {
74264
- const candidate = stripParenExpression(node);
74265
- if (isFunctionLike$1(candidate)) return false;
74266
- if (isNodeOfType(candidate, "SpreadElement")) {
74267
- if (resolveExpressionKey$1(candidate.argument, context) === targetKey) didFindFreshClone = true;
74268
- return false;
74269
- }
74270
- if (isNodeOfType(candidate, "NewExpression")) {
74271
- const callee = stripParenExpression(candidate.callee);
74272
- const firstArgument = candidate.arguments[0];
74273
- if (isNodeOfType(callee, "Identifier") && (callee.name === "Map" || callee.name === "Set") && firstArgument && !isNodeOfType(firstArgument, "SpreadElement") && resolveExpressionKey$1(firstArgument, context) === targetKey) didFindFreshClone = true;
74274
- return;
74275
- }
74276
- if (!isNodeOfType(candidate, "CallExpression")) return;
74277
- const callee = stripParenExpression(candidate.callee);
74278
- if (!isNodeOfType(callee, "MemberExpression")) return;
74279
- const methodName = getStaticPropertyName(callee);
74280
- if (methodName && FRESH_ARRAY_METHOD_NAMES.has(methodName) && resolveExpressionKey$1(callee.object, context) === targetKey) didFindFreshClone = true;
74281
- });
74282
- return didFindFreshClone;
74283
- };
74284
- const isDefinitelyNoUpdateExpression = (expression, context) => {
74285
- const candidate = stripParenExpression(expression);
74286
- return isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "void" || isNodeOfType(candidate, "Identifier") && candidate.name === "undefined" && context.scopes.isGlobalReference(candidate);
74287
- };
74288
- const returnedExpressionsForFunction = (functionNode) => {
74289
- if (!isFunctionLike$1(functionNode)) return [];
74290
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return [functionNode.body];
74291
- const returnedExpressions = [];
74292
- for (const statement of functionNode.body.body) if (isNodeOfType(statement, "ReturnStatement") && statement.argument) returnedExpressions.push(statement.argument);
74293
- return returnedExpressions;
74294
- };
74295
- const hasUnsupportedControlFlow = (statements) => statements.some((statement) => UNSUPPORTED_CONTROL_FLOW_TYPES.has(statement.type));
74296
- const hasAbruptCompletion = (node) => {
74297
- let didFindAbruptCompletion = false;
74298
- walkAst(node, (child) => {
74299
- if (child !== node && isFunctionLike$1(child)) return false;
74300
- if (isNodeOfType(child, "ReturnStatement") || isNodeOfType(child, "ThrowStatement")) {
74301
- didFindAbruptCompletion = true;
74302
- return false;
74303
- }
74304
- });
74305
- return didFindAbruptCompletion;
74306
- };
74307
- const hasUnsupportedSnapshotStatement = (statement) => {
74308
- if (!isNodeOfType(statement, "IfStatement")) return UNSUPPORTED_CONTROL_FLOW_TYPES.has(statement.type);
74309
- if (hasAbruptCompletion(statement)) return true;
74310
- const branchStatements = [];
74311
- for (const branch of [statement.consequent, statement.alternate]) {
74312
- if (!branch) continue;
74313
- branchStatements.push(...isNodeOfType(branch, "BlockStatement") ? branch.body : [branch]);
74314
- }
74315
- return branchStatements.some(hasUnsupportedSnapshotStatement);
74316
- };
74317
- const hasUnsupportedSnapshotControlFlow = (statements) => {
74318
- if (statements.some(hasUnsupportedSnapshotStatement)) return true;
74319
- let didFindUnsupportedExpressionControlFlow = false;
74320
- for (const statement of statements) walkAst(statement, (node) => {
74321
- if (node !== statement && isFunctionLike$1(node)) return false;
74322
- if (UNSUPPORTED_SNAPSHOT_EXPRESSION_CONTROL_FLOW_TYPES.has(node.type)) {
74323
- didFindUnsupportedExpressionControlFlow = true;
74324
- return false;
74325
- }
74326
- });
74327
- return didFindUnsupportedExpressionControlFlow;
74328
- };
74329
- const analyzeSetUpdater = (updaterFunction, getSymbolIds, storeSymbolIds, creatorFunction, context, reportedNodes) => {
74330
- if (!isFunctionLike$1(updaterFunction) || updaterFunction.async || updaterFunction.generator) return;
74331
- const stateParameter = findIdentifierParameter(updaterFunction.params[0]);
74332
- if (!stateParameter) return;
74333
- const state = { mutableStateSourceNames: new Set([stateParameter.name]) };
74334
- const returnedExpressions = returnedExpressionsForFunction(updaterFunction);
74335
- const mutations = [];
74336
- const mutationOptions = { isProvenMutatingMethodCall: (callExpression) => isProvenZustandMutatingMethodCall(callExpression, creatorFunction, context) };
74337
- if (isNodeOfType(updaterFunction.body, "BlockStatement")) {
74338
- if (hasUnsupportedControlFlow(updaterFunction.body.body)) return;
74339
- for (const statement of updaterFunction.body.body) {
74340
- mutations.push(...collectMutableStateReferenceMutations(statement, state, mutationOptions));
74341
- if (isNodeOfType(statement, "VariableDeclaration")) updateMutableStateReferencesForVariableDeclaration(statement, state);
74342
- else if (isNodeOfType(statement, "ExpressionStatement")) {
74343
- const assignment = stripParenExpression(statement.expression);
74344
- if (isNodeOfType(assignment, "AssignmentExpression")) updateMutableStateReferencesForIdentifierAssignment(assignment, state);
74345
- }
74346
- if (isNodeOfType(statement, "ReturnStatement")) break;
74347
- }
74348
- } else mutations.push(...collectMutableStateReferenceMutations(updaterFunction.body, state, mutationOptions));
74349
- for (const mutation of mutations) {
74350
- const mutationPath = staticPropertyPathForExpression(mutation.receiver, context);
74351
- const hasNoUpdateReturn = returnedExpressions.some((expression) => isDefinitelyNoUpdateExpression(expression, context));
74352
- const doesPreserveTarget = returnedExpressions.some((expression) => updateTargetReplacementDisposition(expression, mutation, context) === false || isSnapshotExpression(expression, getSymbolIds, storeSymbolIds, context) && mutationPath !== null && staticPathPreservesTarget(staticPropertyPathForExpression(expression, context), mutationPath));
74353
- if (returnedExpressions.length > 0 && !doesPreserveTarget && !hasNoUpdateReturn) continue;
74354
- if (reportedNodes.has(mutation.node)) continue;
74355
- reportedNodes.add(mutation.node);
74356
- context.report({
74357
- node: mutation.node,
74358
- message: MESSAGE
74359
- });
74360
- }
74361
- };
74362
- const collectNotifierCalls = (statement, setSymbolIds, storeSymbolIds, context) => {
74363
- const notifierCalls = [];
74364
- walkAst(statement, (node) => {
74365
- if (isFunctionLike$1(node)) return false;
74366
- if (!isNodeOfType(node, "CallExpression")) return;
74367
- if (isCallToSymbol(node, setSymbolIds, context) || isStoreMethodCall(node, "setState", storeSymbolIds, context)) notifierCalls.push(node);
74368
- });
74369
- return notifierCalls;
74370
- };
74371
- const staticPropertyPathForExpression = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set(), followMutableInitializer = false) => {
74372
- const candidate = stripParenExpression(expression);
74373
- if (isNodeOfType(candidate, "Identifier")) {
74374
- const symbol = context.scopes.symbolFor(candidate);
74375
- if (!symbol || visitedSymbolIds.has(symbol.id)) return [];
74376
- visitedSymbolIds.add(symbol.id);
74377
- const bindingProperty = symbol.bindingIdentifier.parent;
74378
- const bindingPattern = bindingProperty?.parent;
74379
- const variableDeclarator = bindingPattern?.parent;
74380
- if (isNodeOfType(bindingProperty, "Property") && isNodeOfType(bindingPattern, "ObjectPattern") && isNodeOfType(variableDeclarator, "VariableDeclarator")) {
74381
- const propertyName = getStaticPropertyKeyName(bindingProperty);
74382
- const objectPath = variableDeclarator.init ? staticPropertyPathForExpression(variableDeclarator.init, context, visitedSymbolIds, followMutableInitializer) : null;
74383
- return propertyName && objectPath ? [...objectPath, propertyName] : null;
74384
- }
74385
- if (!followMutableInitializer && symbol.kind !== "const" || !symbol.initializer) return [];
74386
- return staticPropertyPathForExpression(symbol.initializer, context, visitedSymbolIds, followMutableInitializer);
74387
- }
74388
- if (isNodeOfType(candidate, "MemberExpression")) {
74389
- const propertyName = getStaticPropertyName(candidate);
74390
- const objectPath = staticPropertyPathForExpression(candidate.object, context, visitedSymbolIds, followMutableInitializer);
74391
- return propertyName && objectPath ? [...objectPath, propertyName] : null;
74392
- }
74393
- if (isNodeOfType(candidate, "CallExpression")) return [];
74394
- return null;
74395
- };
74396
- const isProvenZustandMutatingMethodCall = (callExpression, creatorFunction, context) => {
74397
- const callee = stripParenExpression(callExpression.callee);
74398
- if (!isNodeOfType(callee, "MemberExpression")) return false;
74399
- const methodName = getStaticPropertyName(callee);
74400
- const receiverPath = staticPropertyPathForExpression(callee.object, context, /* @__PURE__ */ new Set(), true);
74401
- if (!methodName || !receiverPath) return false;
74402
- if (MUTATING_ARRAY_METHODS.has(methodName) && functionReturnsCollectionAtPath({
74403
- collectionKind: "array",
74404
- functionNode: creatorFunction,
74405
- propertyPath: receiverPath,
74406
- scopes: context.scopes
74407
- })) return true;
74408
- return MUTATING_COLLECTION_METHODS.has(methodName) && functionReturnsCollectionAtPath({
74409
- collectionKind: "map-or-set",
74410
- functionNode: creatorFunction,
74411
- propertyPath: receiverPath,
74412
- scopes: context.scopes
74413
- });
74414
- };
74415
- const isProvenFreshReplacementExpression = (expression, targetKey, context) => {
74416
- const candidate = stripParenExpression(expression);
74417
- return isNodeOfType(candidate, "ObjectExpression") || isNodeOfType(candidate, "ArrayExpression") || isNodeOfType(candidate, "NewExpression") || isNodeOfType(candidate, "Literal") || isNodeOfType(candidate, "TemplateLiteral") || expressionContainsFreshCloneOfTarget(candidate, targetKey, context);
74418
- };
74419
- const objectTargetReplacementDisposition = (objectExpression, targetPath, targetKey, ancestorKey, mutationNode, isPartialUpdateRoot, context, ancestorPath = []) => {
74420
- const propertyName = targetPath[0];
74421
- if (!propertyName) return true;
74422
- let disposition = isPartialUpdateRoot ? false : true;
74423
- for (const property of objectExpression.properties) {
74424
- if (isNodeOfType(property, "SpreadElement")) {
74425
- const spreadKey = resolveExpressionKey$1(property.argument, context);
74426
- const spreadPath = staticPropertyPathForExpression(property.argument, context);
74427
- disposition = spreadKey === ancestorKey || staticPathPreservesTarget(spreadPath, ancestorPath) ? false : null;
74428
- continue;
74429
- }
74430
- if (!isNodeOfType(property, "Property")) continue;
74431
- if (getStaticPropertyKeyName(property) !== propertyName) continue;
74432
- if (targetPath.length === 1) {
74433
- if (expressionPreservesTarget(property.value, targetKey, mutationNode, context)) disposition = false;
74434
- else disposition = isProvenFreshReplacementExpression(property.value, targetKey, context) ? true : null;
74435
- continue;
74436
- }
74437
- const propertyValue = stripParenExpression(property.value);
74438
- if (isNodeOfType(propertyValue, "ObjectExpression")) disposition = objectTargetReplacementDisposition(propertyValue, targetPath.slice(1), targetKey, `${ancestorKey}.${propertyName}`, mutationNode, false, context, [...ancestorPath, propertyName]);
74439
- else if (expressionKeyPreservesTarget(propertyValue, targetKey, context)) disposition = false;
74440
- else disposition = isProvenFreshReplacementExpression(propertyValue, targetKey, context) ? true : null;
74441
- }
74442
- return disposition;
74443
- };
74444
- const staticPathPreservesTarget = (candidatePath, targetPath) => Boolean(candidatePath && candidatePath.length <= targetPath.length && candidatePath.every((propertyName, index) => propertyName === targetPath[index]));
74445
- const objectTargetPathReplacementDisposition = (objectExpression, targetPath, isPartialUpdateRoot, context, ancestorPath = []) => {
74446
- const propertyName = targetPath[0];
74447
- if (!propertyName) return true;
74448
- let disposition = isPartialUpdateRoot ? false : true;
74449
- for (const property of objectExpression.properties) {
74450
- if (isNodeOfType(property, "SpreadElement")) {
74451
- disposition = null;
74452
- continue;
74453
- }
74454
- if (!isNodeOfType(property, "Property")) continue;
74455
- if (getStaticPropertyKeyName(property) !== propertyName) continue;
74456
- const propertyValue = stripParenExpression(property.value);
74457
- if (targetPath.length > 1 && isNodeOfType(propertyValue, "ObjectExpression")) {
74458
- disposition = objectTargetPathReplacementDisposition(propertyValue, targetPath.slice(1), false, context, [...ancestorPath, propertyName]);
74459
- continue;
74460
- }
74461
- if (staticPathPreservesTarget(staticPropertyPathForExpression(propertyValue, context), [...ancestorPath, ...targetPath])) {
74462
- disposition = false;
74463
- continue;
74464
- }
74465
- disposition = isProvenFreshReplacementExpression(propertyValue, "", context) ? true : null;
74466
- }
74467
- return disposition;
74468
- };
74469
- const objectExpressionPublishesSymbolAtPath = (objectExpression, targetPath, symbolId, context) => {
74470
- const propertyName = targetPath[0];
74471
- if (!propertyName) return false;
74472
- for (const property of objectExpression.properties) {
74473
- if (!isNodeOfType(property, "Property")) continue;
74474
- if (getStaticPropertyKeyName(property) !== propertyName) continue;
74475
- const propertyValue = stripParenExpression(property.value);
74476
- if (targetPath.length > 1) return isNodeOfType(propertyValue, "ObjectExpression") && objectExpressionPublishesSymbolAtPath(propertyValue, targetPath.slice(1), symbolId, context);
74477
- return isNodeOfType(propertyValue, "Identifier") && context.scopes.symbolFor(propertyValue)?.id === symbolId;
74478
- }
74479
- return false;
74480
- };
74481
- const branchPathCompatibility = (candidate, mutation) => {
74482
- let current = candidate;
74483
- const mutationFunction = findEnclosingFunction$1(mutation.node);
74484
- while (current.parent && current.parent !== mutationFunction) {
74485
- const parent = current.parent;
74486
- if (isNodeOfType(parent, "IfStatement") && (parent.consequent === current || parent.alternate === current)) {
74487
- if (isAstDescendant(mutation.node, current)) {
74488
- current = parent;
74489
- continue;
74490
- }
74491
- const otherBranch = parent.consequent === current ? parent.alternate : parent.consequent;
74492
- return otherBranch && isAstDescendant(mutation.node, otherBranch) ? false : null;
74493
- }
74494
- current = parent;
74495
- }
74496
- return true;
74497
- };
74498
- const rebindPathCompatibility = (assignment, mutation) => {
74499
- if (!isNodeOfType(assignment.parent, "ExpressionStatement")) return null;
74500
- return branchPathCompatibility(assignment.parent, mutation);
74501
- };
74502
- const targetRebindReplacementDisposition = (updateExpression, mutation, targetPath, targetKey, context) => {
74503
- const targetIdentifier = getRootIdentifier$1(mutation.receiver);
74504
- if (!targetIdentifier) return void 0;
74505
- const targetSymbol = context.scopes.symbolFor(targetIdentifier);
74506
- if (!targetSymbol || targetSymbol.kind === "const") return void 0;
74507
- const candidate = stripParenExpression(updateExpression);
74508
- if (!(targetPath.length === 0 ? isNodeOfType(candidate, "Identifier") && context.scopes.symbolFor(candidate)?.id === targetSymbol.id : isNodeOfType(candidate, "ObjectExpression") && objectExpressionPublishesSymbolAtPath(candidate, targetPath, targetSymbol.id, context))) return void 0;
74509
- const mutationStart = getRangeStart(mutation.node);
74510
- const updateStart = getRangeStart(updateExpression);
74511
- const mutationFunction = findEnclosingFunction$1(mutation.node);
74512
- if (mutationStart === null || updateStart === null) return void 0;
74513
- let latestAssignment = null;
74514
- let latestAssignmentStart = null;
74515
- let latestConditionalAssignmentStart = null;
74516
- for (const reference of targetSymbol.references) {
74517
- if (reference.flag === "read") continue;
74518
- const assignment = reference.identifier.parent;
74519
- if (!isNodeOfType(assignment, "AssignmentExpression") || assignment.operator !== "=" || assignment.left !== reference.identifier || findEnclosingFunction$1(assignment) !== mutationFunction) continue;
74520
- const assignmentStart = getRangeStart(assignment);
74521
- if (assignmentStart === null || assignmentStart <= mutationStart || assignmentStart >= updateStart) continue;
74522
- const pathCompatibility = rebindPathCompatibility(assignment, mutation);
74523
- if (pathCompatibility === false) continue;
74524
- if (pathCompatibility === null) {
74525
- if (latestConditionalAssignmentStart === null || assignmentStart > latestConditionalAssignmentStart) latestConditionalAssignmentStart = assignmentStart;
74526
- continue;
74527
- }
74528
- if (latestAssignmentStart !== null && assignmentStart <= latestAssignmentStart) continue;
74529
- latestAssignment = assignment;
74530
- latestAssignmentStart = assignmentStart;
74531
- }
74532
- if (latestConditionalAssignmentStart !== null && (latestAssignmentStart === null || latestConditionalAssignmentStart > latestAssignmentStart)) return null;
74533
- if (!latestAssignment) return void 0;
74534
- if (expressionKeyPreservesTarget(latestAssignment.right, targetKey, context)) return false;
74535
- return isProvenFreshReplacementExpression(latestAssignment.right, targetKey, context) ? true : null;
74536
- };
74537
- const updateTargetReplacementDisposition = (updateExpression, mutation, context) => {
74538
- const targetKey = resolveExpressionKey$1(mutation.receiver, context);
74539
- const targetPath = staticPropertyPathForExpression(mutation.receiver, context, /* @__PURE__ */ new Set(), true);
74540
- if (!targetPath) return null;
74541
- const candidate = stripParenExpression(updateExpression);
74542
- if (!targetKey) {
74543
- if (targetPath.length === 0) return null;
74544
- if (!isNodeOfType(candidate, "ObjectExpression")) return staticPathPreservesTarget(staticPropertyPathForExpression(candidate, context), targetPath) ? false : null;
74545
- return objectTargetPathReplacementDisposition(candidate, targetPath, true, context);
74546
- }
74547
- if (targetPath.length === 0) {
74548
- if (expressionPreservesTarget(candidate, targetKey, mutation.node, context)) {
74549
- const rebindDisposition = targetRebindReplacementDisposition(candidate, mutation, targetPath, targetKey, context);
74550
- return rebindDisposition === void 0 ? false : rebindDisposition;
74551
- }
74552
- return isProvenFreshReplacementExpression(candidate, targetKey, context) ? true : null;
74553
- }
74554
- if (!isNodeOfType(candidate, "ObjectExpression")) return expressionPreservesTarget(candidate, targetKey, mutation.node, context) ? false : null;
74555
- const ancestorKeySuffix = `.${targetPath.join(".")}`;
74556
- const disposition = objectTargetReplacementDisposition(candidate, targetPath, targetKey, targetKey.endsWith(ancestorKeySuffix) ? targetKey.slice(0, -ancestorKeySuffix.length) : targetKey, mutation.node, true, context);
74557
- if (disposition !== false) return disposition;
74558
- const rebindDisposition = targetRebindReplacementDisposition(candidate, mutation, targetPath, targetKey, context);
74559
- return rebindDisposition === void 0 ? false : rebindDisposition;
74560
- };
74561
- const notifierTargetReplacementDisposition = (notifierCall, mutation, context) => {
74562
- const updateArgument = notifierCall.arguments[0];
74563
- if (!updateArgument) return false;
74564
- if (isNodeOfType(updateArgument, "SpreadElement")) return null;
74565
- const updateFunction = resolveExactLocalFunction(updateArgument, context.scopes);
74566
- const dispositions = (updateFunction ? returnedExpressionsForFunction(updateFunction) : [updateArgument]).map((expression) => updateTargetReplacementDisposition(expression, mutation, context));
74567
- if (dispositions.some((disposition) => disposition === true)) return true;
74568
- if (dispositions.some((disposition) => disposition === null)) return null;
74569
- return false;
74570
- };
74571
- const sequentialNotifierTargetReplacementDisposition = (previousDisposition, nextDisposition) => {
74572
- if (previousDisposition === true || nextDisposition === true) return true;
74573
- if (previousDisposition === null || nextDisposition === null) return null;
74574
- return false;
74575
- };
74576
- const notifierFlowTargetReplacementDisposition = (statement, mutation, setSymbolIds, storeSymbolIds, context) => {
74577
- if (isNodeOfType(statement, "BlockStatement")) return statement.body.reduce((disposition, childStatement) => sequentialNotifierTargetReplacementDisposition(disposition, notifierFlowTargetReplacementDisposition(childStatement, mutation, setSymbolIds, storeSymbolIds, context)), false);
74578
- if (isNodeOfType(statement, "IfStatement")) {
74579
- const consequentDisposition = notifierFlowTargetReplacementDisposition(statement.consequent, mutation, setSymbolIds, storeSymbolIds, context);
74580
- const alternateDisposition = statement.alternate ? notifierFlowTargetReplacementDisposition(statement.alternate, mutation, setSymbolIds, storeSymbolIds, context) : false;
74581
- if (consequentDisposition === false || alternateDisposition === false) return false;
74582
- return consequentDisposition === true && alternateDisposition === true ? true : null;
74583
- }
74584
- return collectNotifierCalls(statement, setSymbolIds, storeSymbolIds, context).reduce((disposition, callExpression) => sequentialNotifierTargetReplacementDisposition(disposition, notifierTargetReplacementDisposition(callExpression, mutation, context)), false);
74585
- };
74586
- const collectConditionalNotifierGroups = (statement, statementIndex) => {
74587
- const groups = [];
74588
- walkAst(statement, (node) => {
74589
- if (isFunctionLike$1(node)) return false;
74590
- if (isNodeOfType(node, "IfStatement")) groups.push({
74591
- statement: node,
74592
- statementIndex
74593
- });
74594
- });
74595
- return groups;
74596
- };
74597
- const updateSnapshotStateForStatement = (statement, state) => {
74598
- if (isNodeOfType(statement, "VariableDeclaration")) {
74599
- updateMutableStateReferencesForVariableDeclaration(statement, state);
74600
- return;
74601
- }
74602
- if (!isNodeOfType(statement, "ExpressionStatement")) return;
74603
- const assignment = stripParenExpression(statement.expression);
74604
- if (!isNodeOfType(assignment, "AssignmentExpression")) return;
74605
- updateMutableStateReferencesForIdentifierAssignment(assignment, state);
74606
- };
74607
- const collectSequentialSnapshotMutations = (branchRoot, state, creatorFunction, context) => {
74608
- const branchState = { mutableStateSourceNames: new Set(state.mutableStateSourceNames) };
74609
- if (state.isAdditionalMutableStateSource) branchState.isAdditionalMutableStateSource = state.isAdditionalMutableStateSource;
74610
- const statements = isNodeOfType(branchRoot, "BlockStatement") ? branchRoot.body : [branchRoot];
74611
- const mutations = [];
74612
- const mutationOptions = { isProvenMutatingMethodCall: (callExpression) => isProvenZustandMutatingMethodCall(callExpression, creatorFunction, context) };
74613
- for (const statement of statements) {
74614
- if (isNodeOfType(statement, "IfStatement")) {
74615
- mutations.push(...collectSequentialSnapshotMutations(statement.consequent, branchState, creatorFunction, context));
74616
- if (statement.alternate) mutations.push(...collectSequentialSnapshotMutations(statement.alternate, branchState, creatorFunction, context));
74617
- continue;
74618
- }
74619
- mutations.push(...collectMutableStateReferenceMutations(statement, branchState, mutationOptions));
74620
- updateSnapshotStateForStatement(statement, branchState);
74621
- }
74622
- return mutations;
74623
- };
74624
- const analyzeSnapshotContainer = (statements, getSymbolIds, setSymbolIds, snapshotStoreSymbolIds, notifierStoreSymbolIds, creatorFunction, context, reportedNodes, returnedUpdateExpressions = []) => {
74625
- if (hasUnsupportedSnapshotControlFlow(statements)) return;
74626
- const state = {
74627
- isAdditionalMutableStateSource: (expression) => isSnapshotExpression(expression, getSymbolIds, snapshotStoreSymbolIds, context),
74628
- mutableStateSourceNames: /* @__PURE__ */ new Set()
74629
- };
74630
- const mutations = [];
74631
- const notifierCalls = [];
74632
- const conditionalNotifierGroups = [];
74633
- const mutationOptions = { isProvenMutatingMethodCall: (callExpression) => isProvenZustandMutatingMethodCall(callExpression, creatorFunction, context) };
74634
- for (let statementIndex = 0; statementIndex < statements.length; statementIndex += 1) {
74635
- const statement = statements[statementIndex];
74636
- if (isNodeOfType(statement, "IfStatement")) for (const branchRoot of [statement.consequent, statement.alternate]) {
74637
- if (!branchRoot) continue;
74638
- for (const mutation of collectSequentialSnapshotMutations(branchRoot, state, creatorFunction, context)) mutations.push({
74639
- branchRoot,
74640
- mutation,
74641
- statementIndex
74642
- });
74643
- const calls = collectNotifierCalls(branchRoot, setSymbolIds, notifierStoreSymbolIds, context);
74644
- for (const callExpression of calls) notifierCalls.push({
74645
- branchRoot,
74646
- callExpression,
74647
- statementIndex
74648
- });
74649
- }
74650
- else {
74651
- for (const mutation of collectMutableStateReferenceMutations(statement, state, mutationOptions)) mutations.push({
74652
- branchRoot: null,
74653
- mutation,
74654
- statementIndex
74655
- });
74656
- for (const callExpression of collectNotifierCalls(statement, setSymbolIds, notifierStoreSymbolIds, context)) notifierCalls.push({
74657
- branchRoot: null,
74658
- callExpression,
74659
- statementIndex
74660
- });
74661
- }
74662
- conditionalNotifierGroups.push(...collectConditionalNotifierGroups(statement, statementIndex));
74663
- updateSnapshotStateForStatement(statement, state);
74664
- if (isNodeOfType(statement, "ReturnStatement")) break;
74665
- }
74666
- for (const { branchRoot, mutation, statementIndex } of mutations) {
74667
- const replacementDispositions = [...notifierCalls.filter((notifier) => {
74668
- if (!notifier.branchRoot) {
74669
- if (notifier.statementIndex !== statementIndex) return notifier.statementIndex > statementIndex;
74670
- const mutationStart = getRangeStart(mutation.node);
74671
- const notifierStart = getRangeStart(notifier.callExpression);
74672
- return isAstDescendant(mutation.node, notifier.callExpression) || mutationStart !== null && notifierStart !== null && notifierStart >= mutationStart;
74673
- }
74674
- if (notifier.statementIndex !== statementIndex || notifier.branchRoot !== branchRoot) return false;
74675
- if (branchPathCompatibility(notifier.callExpression, mutation) !== true) return false;
74676
- const mutationStart = getRangeStart(mutation.node);
74677
- const notifierStart = getRangeStart(notifier.callExpression);
74678
- return isAstDescendant(mutation.node, notifier.callExpression) || mutationStart !== null && notifierStart !== null && notifierStart >= mutationStart;
74679
- }).map((notifier) => notifierTargetReplacementDisposition(notifier.callExpression, mutation, context)), ...returnedUpdateExpressions.map((expression) => updateTargetReplacementDisposition(expression, mutation, context))];
74680
- for (const group of conditionalNotifierGroups) {
74681
- if (group.statementIndex < statementIndex) continue;
74682
- const mutationStart = getRangeStart(mutation.node);
74683
- const groupStart = getRangeStart(group.statement);
74684
- if (mutationStart === null || groupStart === null || groupStart <= mutationStart || branchPathCompatibility(group.statement, mutation) !== true) continue;
74685
- replacementDispositions.push(notifierFlowTargetReplacementDisposition(group.statement, mutation, setSymbolIds, notifierStoreSymbolIds, context));
74686
- }
74687
- if (replacementDispositions.some((disposition) => disposition !== false)) continue;
74688
- if (reportedNodes.has(mutation.node)) continue;
74689
- reportedNodes.add(mutation.node);
74690
- context.report({
74691
- node: mutation.node,
74692
- message: MESSAGE
74693
- });
74694
- }
74695
- };
74696
- const zustandNoMutatingState = defineRule({
74697
- id: "zustand-no-mutating-state",
74698
- title: "Zustand state mutated in place",
74699
- severity: "error",
74700
- category: "Correctness",
74701
- recommendation: "Create a new object, array, Map, or Set before passing the updated value to Zustand.",
74702
- requires: ["zustand", "zustand:1"],
74703
- create: (context) => {
74704
- const creatorBindings = /* @__PURE__ */ new Map();
74705
- const bindingsWithBoundStoreNotifier = /* @__PURE__ */ new Set();
74706
- const functionContainers = /* @__PURE__ */ new Set();
74707
- const updaterFunctionNotifierSymbolIds = /* @__PURE__ */ new Map();
74708
- const recordUpdaterFunctionNotifier = (updaterFunction, notifierSymbolId) => {
74709
- const notifierSymbolIds = updaterFunctionNotifierSymbolIds.get(updaterFunction);
74710
- if (notifierSymbolIds) notifierSymbolIds.add(notifierSymbolId);
74711
- else updaterFunctionNotifierSymbolIds.set(updaterFunction, new Set([notifierSymbolId]));
74712
- };
74713
- const reportedNodes = /* @__PURE__ */ new WeakSet();
74714
- let programNode = null;
74715
- return {
74716
- Program(node) {
74717
- programNode = node;
74718
- },
74719
- CallExpression(node) {
74720
- const creator = resolveZustandStoreCreator(node, context.scopes);
74721
- if (!creator) return;
74722
- const hasNonImmerUsage = !creator.middlewareNames.has("immer");
74723
- let binding = creatorBindings.get(creator.creatorFunction);
74724
- if (binding) {
74725
- if (hasNonImmerUsage) binding.hasNonImmerUsage = true;
74726
- } else {
74727
- binding = {
74728
- creatorFunction: creator.creatorFunction,
74729
- getSymbol: symbolForParameter(creator.creatorFunction, 1, context),
74730
- hasNonImmerUsage,
74731
- nonImmerStoreSymbolIds: /* @__PURE__ */ new Set(),
74732
- setSymbol: symbolForParameter(creator.creatorFunction, 0, context),
74733
- storeSymbolIds: /* @__PURE__ */ new Set()
74734
- };
74735
- creatorBindings.set(creator.creatorFunction, binding);
74736
- }
74737
- const parent = node.parent;
74738
- if (isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) {
74739
- const storeSymbol = context.scopes.symbolFor(parent.id);
74740
- if (storeSymbol) {
74741
- binding.storeSymbolIds.add(storeSymbol.id);
74742
- if (hasNonImmerUsage) binding.nonImmerStoreSymbolIds.add(storeSymbol.id);
74743
- }
74744
- }
74745
- },
74746
- ArrowFunctionExpression(node) {
74747
- functionContainers.add(node);
74748
- },
74749
- FunctionDeclaration(node) {
74750
- functionContainers.add(node);
74751
- },
74752
- FunctionExpression(node) {
74753
- functionContainers.add(node);
74754
- },
74755
- "Program:exit"() {
74756
- for (const binding of creatorBindings.values()) {
74757
- if (binding.hasNonImmerUsage && binding.setSymbol) {
74758
- const setSymbolId = binding.setSymbol.id;
74759
- const creatorSetSymbolIds = new Set([setSymbolId]);
74760
- walkAst(binding.creatorFunction.body, (node) => {
74761
- if (!isNodeOfType(node, "CallExpression")) return;
74762
- if (!isCallToSymbol(node, creatorSetSymbolIds, context)) return;
74763
- const updaterArgument = node.arguments[0];
74764
- if (!updaterArgument || isNodeOfType(updaterArgument, "SpreadElement")) return;
74765
- const updaterFunction = resolveExactLocalFunction(updaterArgument, context.scopes);
74766
- if (!updaterFunction) return;
74767
- recordUpdaterFunctionNotifier(updaterFunction, setSymbolId);
74768
- analyzeSetUpdater(updaterFunction, binding.getSymbol ? new Set([binding.getSymbol.id]) : /* @__PURE__ */ new Set(), binding.storeSymbolIds, binding.creatorFunction, context, reportedNodes);
74769
- });
74770
- }
74771
- if (!programNode || binding.storeSymbolIds.size === 0) continue;
74772
- walkAst(programNode, (node) => {
74773
- if (!isNodeOfType(node, "CallExpression")) return;
74774
- const storeSymbolId = storeSymbolIdForMethodCall(node, "setState", context);
74775
- if (storeSymbolId === null || !binding.storeSymbolIds.has(storeSymbolId)) return;
74776
- bindingsWithBoundStoreNotifier.add(binding);
74777
- const updaterArgument = node.arguments[0];
74778
- if (!updaterArgument || isNodeOfType(updaterArgument, "SpreadElement")) return;
74779
- const updaterFunction = resolveExactLocalFunction(updaterArgument, context.scopes);
74780
- if (!updaterFunction) return;
74781
- recordUpdaterFunctionNotifier(updaterFunction, storeSymbolId);
74782
- if (!binding.nonImmerStoreSymbolIds.has(storeSymbolId)) return;
74783
- analyzeSetUpdater(updaterFunction, /* @__PURE__ */ new Set(), new Set([storeSymbolId]), binding.creatorFunction, context, reportedNodes);
74784
- });
74785
- }
74786
- const analyzeProvenance = (getSymbolIds, setSymbolIds, snapshotStoreSymbolIds, notifierStoreSymbolIds, creatorFunction) => {
74787
- if (programNode) analyzeSnapshotContainer(programNode.body, getSymbolIds, setSymbolIds, snapshotStoreSymbolIds, notifierStoreSymbolIds, creatorFunction, context, reportedNodes);
74788
- for (const functionContainer of functionContainers) {
74789
- if (!isFunctionLike$1(functionContainer)) continue;
74790
- if (!isNodeOfType(functionContainer.body, "BlockStatement")) continue;
74791
- const updaterNotifierSymbolIds = updaterFunctionNotifierSymbolIds.get(functionContainer);
74792
- const isUpdaterNotifierForProvenance = Boolean(updaterNotifierSymbolIds && Array.from(updaterNotifierSymbolIds).some((notifierSymbolId) => setSymbolIds.has(notifierSymbolId) || notifierStoreSymbolIds.has(notifierSymbolId)));
74793
- analyzeSnapshotContainer(functionContainer.body.body, getSymbolIds, setSymbolIds, snapshotStoreSymbolIds, notifierStoreSymbolIds, creatorFunction, context, reportedNodes, isUpdaterNotifierForProvenance ? returnedExpressionsForFunction(functionContainer) : []);
74794
- }
74795
- };
74796
- for (const binding of creatorBindings.values()) {
74797
- const getSymbolIds = /* @__PURE__ */ new Set();
74798
- const setSymbolIds = /* @__PURE__ */ new Set();
74799
- if (binding.getSymbol && binding.setSymbol && (binding.setSymbol.references.length > 0 || bindingsWithBoundStoreNotifier.has(binding))) getSymbolIds.add(binding.getSymbol.id);
74800
- if (binding.setSymbol) setSymbolIds.add(binding.setSymbol.id);
74801
- analyzeProvenance(getSymbolIds, setSymbolIds, /* @__PURE__ */ new Set(), binding.storeSymbolIds, binding.creatorFunction);
74802
- for (const storeSymbolId of binding.storeSymbolIds) {
74803
- const storeSymbolIds = new Set([storeSymbolId]);
74804
- analyzeProvenance(/* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), storeSymbolIds, storeSymbolIds, binding.creatorFunction);
74805
- }
74806
- }
74807
- }
74808
- };
74809
- }
74810
- });
74811
- //#endregion
74812
- //#region src/plugin/rules/state-and-effects/zustand-no-whole-store-destructure.ts
74813
- const BOUND_STORE_FACTORY_APIS = new Set(["create", "createWithEqualityFn"]);
74814
- const VANILLA_STORE_FACTORY_APIS = new Set(["createStore"]);
74815
- const isStoreFactoryValue = (rawValue, factoryApiNames, scopes) => {
74816
- const value = stripParenExpression(rawValue);
74817
- if (!isNodeOfType(value, "CallExpression")) return false;
74818
- const factoryCall = resolveZustandStoreFactoryCall(value, scopes);
74819
- return Boolean(factoryCall && factoryApiNames.has(factoryCall.factoryApiName));
74820
- };
74821
- const isStoreValue = (rawValue, factoryApiNames, scopes) => {
74822
- const value = stripParenExpression(rawValue);
74823
- if (isStoreFactoryValue(value, factoryApiNames, scopes)) return true;
74824
- if (!isNodeOfType(value, "Identifier")) return false;
74825
- const symbol = resolveConstIdentifierAlias(value, scopes);
74826
- return Boolean(symbol?.kind === "const" && symbol.initializer && isStoreFactoryValue(symbol.initializer, factoryApiNames, scopes));
74827
- };
74828
- const isBoundStoreCall = (node, scopes) => {
74829
- const callee = stripParenExpression(node.callee);
74830
- return isNodeOfType(callee, "Identifier") && isStoreValue(callee, BOUND_STORE_FACTORY_APIS, scopes);
74831
- };
74832
- const isVanillaStoreHookCall = (node, scopes) => {
74833
- if (node.arguments.length !== 1) return false;
74834
- const hook = resolveZustandApiBinding(node.callee, scopes);
74835
- if (hook?.apiName !== "useStore" && hook?.apiName !== "useStoreWithEqualityFn") return false;
74836
- return isStoreValue(node.arguments[0], VANILLA_STORE_FACTORY_APIS, scopes);
74837
- };
74838
- const isDirectReactRenderCall = (node, context) => {
74839
- const renderFunction = findRenderPhaseComponentOrHook(node, context.scopes);
74840
- if (!renderFunction || findEnclosingFunction$1(node) !== renderFunction) return false;
74841
- const displayName = componentOrHookDisplayNameForFunction(renderFunction);
74842
- return Boolean(displayName && (isReactHookName(displayName) || functionHasReactComponentEvidence(renderFunction, context.scopes, context.cfg)));
74843
- };
74844
- const zustandNoWholeStoreDestructure = defineRule({
74845
- id: "zustand-no-whole-store-destructure",
74846
- title: "Whole Zustand store subscribed during render",
74847
- severity: "warn",
74848
- requires: ["zustand:1"],
74849
- recommendation: "Pass a selector to the Zustand hook so this component rerenders only when the state it reads changes.",
74850
- create: (context) => ({ CallExpression(node) {
74851
- if (!isDirectReactRenderCall(node, context)) return;
74852
- if (!(node.arguments.length === 0 && isBoundStoreCall(node, context.scopes)) && !isVanillaStoreHookCall(node, context.scopes)) return;
74853
- context.report({
74854
- node,
74855
- message: "This hook subscribes to the whole Zustand store, so every store update rerenders this component. Pass a selector for the state it reads."
74856
- });
74857
- } })
74858
- });
74859
- //#endregion
74860
73157
  //#region src/plugin/rule-registry.ts
74861
73158
  const reactDoctorRules = [
74862
73159
  {
@@ -76171,39 +74468,6 @@ const reactDoctorRules = [
76171
74468
  requires: [...new Set(["react", ...mediaHasCaption.requires ?? []])]
76172
74469
  }
76173
74470
  },
76174
- {
76175
- key: "react-doctor/mobx-no-make-auto-observable-in-inheritance",
76176
- id: "mobx-no-make-auto-observable-in-inheritance",
76177
- source: "react-doctor",
76178
- originallyExternal: false,
76179
- rule: {
76180
- ...mobxNoMakeAutoObservableInInheritance,
76181
- framework: "global",
76182
- category: "Bugs"
76183
- }
76184
- },
76185
- {
76186
- key: "react-doctor/mobx-no-observer-wrapped-memo",
76187
- id: "mobx-no-observer-wrapped-memo",
76188
- source: "react-doctor",
76189
- originallyExternal: false,
76190
- rule: {
76191
- ...mobxNoObserverWrappedMemo,
76192
- framework: "global",
76193
- category: "Bugs"
76194
- }
76195
- },
76196
- {
76197
- key: "react-doctor/mobx-reaction-disposer-discarded",
76198
- id: "mobx-reaction-disposer-discarded",
76199
- source: "react-doctor",
76200
- originallyExternal: false,
76201
- rule: {
76202
- ...mobxReactionDisposerDiscarded,
76203
- framework: "global",
76204
- category: "Bugs"
76205
- }
76206
- },
76207
74471
  {
76208
74472
  key: "react-doctor/mouse-events-have-key-events",
76209
74473
  id: "mouse-events-have-key-events",
@@ -79699,54 +77963,6 @@ const reactDoctorRules = [
79699
77963
  framework: "global",
79700
77964
  category: "Maintainability"
79701
77965
  }
79702
- },
79703
- {
79704
- key: "react-doctor/zustand-no-fresh-selector-result",
79705
- id: "zustand-no-fresh-selector-result",
79706
- source: "react-doctor",
79707
- originallyExternal: false,
79708
- rule: {
79709
- ...zustandNoFreshSelectorResult,
79710
- framework: "global",
79711
- category: "Performance",
79712
- requires: [...new Set(["react", ...zustandNoFreshSelectorResult.requires ?? []])]
79713
- }
79714
- },
79715
- {
79716
- key: "react-doctor/zustand-no-get-during-initialization",
79717
- id: "zustand-no-get-during-initialization",
79718
- source: "react-doctor",
79719
- originallyExternal: false,
79720
- rule: {
79721
- ...zustandNoGetDuringInitialization,
79722
- framework: "global",
79723
- category: "Bugs",
79724
- requires: [...new Set(["react", ...zustandNoGetDuringInitialization.requires ?? []])]
79725
- }
79726
- },
79727
- {
79728
- key: "react-doctor/zustand-no-mutating-state",
79729
- id: "zustand-no-mutating-state",
79730
- source: "react-doctor",
79731
- originallyExternal: false,
79732
- rule: {
79733
- ...zustandNoMutatingState,
79734
- framework: "global",
79735
- category: "Bugs",
79736
- requires: [...new Set(["react", ...zustandNoMutatingState.requires ?? []])]
79737
- }
79738
- },
79739
- {
79740
- key: "react-doctor/zustand-no-whole-store-destructure",
79741
- id: "zustand-no-whole-store-destructure",
79742
- source: "react-doctor",
79743
- originallyExternal: false,
79744
- rule: {
79745
- ...zustandNoWholeStoreDestructure,
79746
- framework: "global",
79747
- category: "Bugs",
79748
- requires: [...new Set(["react", ...zustandNoWholeStoreDestructure.requires ?? []])]
79749
- }
79750
77966
  }
79751
77967
  ];
79752
77968
  const ruleRegistry = Object.fromEntries(reactDoctorRules.map((rule) => [rule.id, rule.rule]));