oxlint-plugin-react-doctor 0.7.9-dev.b30fb80 → 0.7.9-dev.c7f61cd

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.
package/dist/index.js CHANGED
@@ -579,6 +579,22 @@ const EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS = new Set([
579
579
  "ResizeObserver",
580
580
  "PerformanceObserver"
581
581
  ]);
582
+ const EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES = new Set([
583
+ "blur",
584
+ "focus",
585
+ "getBoundingClientRect",
586
+ "getClientRects",
587
+ "measure",
588
+ "measureInWindow",
589
+ "measureLayout",
590
+ "scroll",
591
+ "scrollBy",
592
+ "scrollIntoView",
593
+ "scrollTo",
594
+ "select",
595
+ "setRangeText",
596
+ "setSelectionRange"
597
+ ]);
582
598
  const STORAGE_OBJECTS = new Set(["localStorage", "sessionStorage"]);
583
599
  //#endregion
584
600
  //#region src/plugin/constants/react.ts
@@ -1819,8 +1835,70 @@ const flattenJsxName$1 = (node) => {
1819
1835
  return null;
1820
1836
  };
1821
1837
  //#endregion
1822
- //#region src/plugin/utils/is-member-property.ts
1823
- const isMemberProperty = (node, propertyName) => Boolean(node && isNodeOfType(node, "MemberExpression") && isNodeOfType(node.property, "Identifier") && node.property.name === propertyName);
1838
+ //#region src/plugin/utils/get-static-property-name.ts
1839
+ const getStaticPropertyName = (memberExpression) => {
1840
+ const property = memberExpression.property;
1841
+ if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
1842
+ if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
1843
+ if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
1844
+ return null;
1845
+ };
1846
+ //#endregion
1847
+ //#region src/plugin/utils/is-generated-image-renderer-call.ts
1848
+ const GENERATED_IMAGE_RENDERER_MODULES = [
1849
+ "next/og",
1850
+ "@vercel/og",
1851
+ "satori"
1852
+ ];
1853
+ const IMAGE_RESPONSE_MODULES = new Set(["next/og", "@vercel/og"]);
1854
+ const getImportDeclaration$1 = (node) => {
1855
+ let current = node.parent;
1856
+ while (current) {
1857
+ if (isNodeOfType(current, "ImportDeclaration")) return current;
1858
+ if (isNodeOfType(current, "Program")) return null;
1859
+ current = current.parent;
1860
+ }
1861
+ return null;
1862
+ };
1863
+ const getImportSource = (declaration) => typeof declaration.source.value === "string" ? declaration.source.value : null;
1864
+ const isNamedImport = (symbol, importedName, moduleSources) => {
1865
+ if (symbol.kind !== "import") return false;
1866
+ const declaration = symbol.declarationNode;
1867
+ if (!isNodeOfType(declaration, "ImportSpecifier")) return false;
1868
+ const importDeclaration = getImportDeclaration$1(declaration);
1869
+ const source = importDeclaration ? getImportSource(importDeclaration) : null;
1870
+ if (!source || !moduleSources.has(source)) return false;
1871
+ const imported = declaration.imported;
1872
+ return isNodeOfType(imported, "Identifier") && imported.name === importedName || isNodeOfType(imported, "Literal") && imported.value === importedName;
1873
+ };
1874
+ const isSatoriImport = (symbol) => {
1875
+ if (symbol.kind !== "import") return false;
1876
+ const declaration = symbol.declarationNode;
1877
+ const importDeclaration = getImportDeclaration$1(declaration);
1878
+ if (!importDeclaration || getImportSource(importDeclaration) !== "satori") return false;
1879
+ if (isNodeOfType(declaration, "ImportDefaultSpecifier")) return true;
1880
+ if (!isNodeOfType(declaration, "ImportSpecifier")) return false;
1881
+ const imported = declaration.imported;
1882
+ return isNodeOfType(imported, "Identifier") && imported.name === "satori" || isNodeOfType(imported, "Literal") && imported.value === "satori";
1883
+ };
1884
+ const isGeneratedImageRendererCall = (node, scopes) => {
1885
+ if (!isNodeOfType(node, "CallExpression") && !isNodeOfType(node, "NewExpression")) return false;
1886
+ const callee = stripParenExpression(node.callee);
1887
+ if (isNodeOfType(callee, "Identifier")) {
1888
+ const symbol = scopes.referenceFor(callee)?.resolvedSymbol ?? null;
1889
+ return Boolean(symbol && (isNamedImport(symbol, "ImageResponse", IMAGE_RESPONSE_MODULES) || isSatoriImport(symbol)));
1890
+ }
1891
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
1892
+ if (getStaticPropertyName(callee) !== "ImageResponse") return false;
1893
+ if (!isNodeOfType(callee.object, "Identifier")) return false;
1894
+ const symbol = scopes.referenceFor(callee.object)?.resolvedSymbol ?? null;
1895
+ if (!symbol || symbol.kind !== "import") return false;
1896
+ const declaration = symbol.declarationNode;
1897
+ if (!isNodeOfType(declaration, "ImportNamespaceSpecifier")) return false;
1898
+ const importDeclaration = getImportDeclaration$1(declaration);
1899
+ const source = importDeclaration ? getImportSource(importDeclaration) : null;
1900
+ return Boolean(source && IMAGE_RESPONSE_MODULES.has(source));
1901
+ };
1824
1902
  //#endregion
1825
1903
  //#region src/plugin/constants/nextjs.ts
1826
1904
  const NEXTJS_SOURCE_FILE_EXTENSION_GROUP = "(?:tsx?|jsx?|mts|mjs)";
@@ -1892,42 +1970,22 @@ const isNextjsMetadataImageRouteFilename = (rawFilename) => {
1892
1970
  const normalizeFilename = (filename) => filename.includes("\\") ? filename.replaceAll("\\", "/") : filename;
1893
1971
  //#endregion
1894
1972
  //#region src/plugin/utils/is-generated-image-render-context.ts
1895
- const IMAGE_RESPONSE_MODULES = ["next/og", "@vercel/og"];
1896
- const SATORI_MODULE = "satori";
1897
- const GENERATED_IMAGE_MODULES = [...IMAGE_RESPONSE_MODULES, SATORI_MODULE];
1898
1973
  const generatedImageJsxCache = /* @__PURE__ */ new WeakMap();
1899
1974
  const isGeneratedImageRenderFilename = (rawFilename) => {
1900
1975
  if (!rawFilename) return false;
1901
1976
  return isNextjsMetadataImageRouteFilename(normalizeFilename(rawFilename));
1902
1977
  };
1903
- const isImageResponseCallee = (contextNode, callee) => {
1904
- if (isNodeOfType(callee, "Identifier")) return IMAGE_RESPONSE_MODULES.some((moduleSource) => getImportedNameFromModule(contextNode, callee.name, moduleSource) === "ImageResponse");
1905
- if (!isMemberProperty(callee, "ImageResponse")) return false;
1906
- if (!isNodeOfType(callee.object, "Identifier")) return false;
1907
- const namespaceIdentifierName = callee.object.name;
1908
- return IMAGE_RESPONSE_MODULES.some((moduleSource) => isNamespaceImportFromModule(contextNode, namespaceIdentifierName, moduleSource));
1909
- };
1910
- const isSatoriCallee = (contextNode, callee) => {
1911
- if (!isNodeOfType(callee, "Identifier")) return false;
1912
- if (getImportedNameFromModule(contextNode, callee.name, SATORI_MODULE) === "satori") return true;
1913
- return isDefaultImportFromModule(contextNode, callee.name, SATORI_MODULE);
1914
- };
1915
- const isGeneratedImageRendererCall = (node) => {
1916
- if (!isNodeOfType(node, "CallExpression") && !isNodeOfType(node, "NewExpression")) return false;
1917
- if (!isNodeOfType(node.callee, "Identifier") && !isNodeOfType(node.callee, "MemberExpression")) return false;
1918
- return isImageResponseCallee(node, node.callee) || isSatoriCallee(node, node.callee);
1919
- };
1920
1978
  const isComponentIdentifierName = (name) => {
1921
1979
  const firstCharacter = name[0];
1922
1980
  return Boolean(firstCharacter && firstCharacter === firstCharacter.toUpperCase());
1923
1981
  };
1924
1982
  const isFunctionLike = (node) => Boolean(node && (isNodeOfType(node, "FunctionDeclaration") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "ArrowFunctionExpression")));
1925
- const markFunctionReturnJsx = (functionNode, programRoot, generatedImageJsxNodes, visitedComponentNames) => {
1983
+ const markFunctionReturnJsx = (functionNode, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames) => {
1926
1984
  if (!isFunctionLike(functionNode)) return;
1927
1985
  if (isNodeOfType(functionNode, "ArrowFunctionExpression")) {
1928
1986
  const body = stripParenExpression(functionNode.body);
1929
1987
  if (!isNodeOfType(body, "BlockStatement")) {
1930
- markGeneratedImageExpression(body, programRoot, generatedImageJsxNodes, visitedComponentNames);
1988
+ markGeneratedImageExpression(body, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
1931
1989
  return;
1932
1990
  }
1933
1991
  }
@@ -1937,7 +1995,7 @@ const markFunctionReturnJsx = (functionNode, programRoot, generatedImageJsxNodes
1937
1995
  if (descendantNode !== body && isFunctionLike(descendantNode)) return false;
1938
1996
  if (!isNodeOfType(descendantNode, "ReturnStatement")) return;
1939
1997
  if (!descendantNode.argument) return;
1940
- markGeneratedImageExpression(stripParenExpression(descendantNode.argument), programRoot, generatedImageJsxNodes, visitedComponentNames);
1998
+ markGeneratedImageExpression(stripParenExpression(descendantNode.argument), programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
1941
1999
  });
1942
2000
  };
1943
2001
  const hasNormalJsxUsage = (programRoot, componentName, generatedImageJsxNodes) => {
@@ -1952,7 +2010,7 @@ const hasNormalJsxUsage = (programRoot, componentName, generatedImageJsxNodes) =
1952
2010
  });
1953
2011
  return hasNormalUsage;
1954
2012
  };
1955
- const markComponentRenderJsx = (programRoot, openingElement, generatedImageJsxNodes, visitedComponentNames) => {
2013
+ const markComponentRenderJsx = (programRoot, scopes, openingElement, generatedImageJsxNodes, visitedComponentNames) => {
1956
2014
  const tagName = flattenJsxName$1(openingElement.name);
1957
2015
  if (!tagName || tagName.includes(".") || !isComponentIdentifierName(tagName)) return;
1958
2016
  if (visitedComponentNames.has(tagName)) return;
@@ -1960,70 +2018,70 @@ const markComponentRenderJsx = (programRoot, openingElement, generatedImageJsxNo
1960
2018
  const binding = findVariableInitializer(openingElement, tagName);
1961
2019
  if (!binding?.initializer) return;
1962
2020
  visitedComponentNames.add(tagName);
1963
- markGeneratedImageExpression(stripParenExpression(binding.initializer), programRoot, generatedImageJsxNodes, visitedComponentNames);
2021
+ markGeneratedImageExpression(stripParenExpression(binding.initializer), programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
1964
2022
  };
1965
- const isInsideGeneratedImageRendererArgument = (node) => {
2023
+ const isInsideGeneratedImageRendererArgument$1 = (node, scopes) => {
1966
2024
  let cursor = node.parent;
1967
2025
  while (cursor) {
1968
- if (isGeneratedImageRendererCall(cursor)) return true;
2026
+ if (isGeneratedImageRendererCall(cursor, scopes)) return true;
1969
2027
  cursor = cursor.parent ?? null;
1970
2028
  }
1971
2029
  return false;
1972
2030
  };
1973
- const hasNormalFunctionCallUsage = (programRoot, functionName) => {
2031
+ const hasNormalFunctionCallUsage = (programRoot, functionName, scopes) => {
1974
2032
  let hasNormalUsage = false;
1975
2033
  walkAst(programRoot, (descendantNode) => {
1976
2034
  if (hasNormalUsage) return false;
1977
2035
  if (!isNodeOfType(descendantNode, "CallExpression")) return;
1978
2036
  if (!isNodeOfType(descendantNode.callee, "Identifier")) return;
1979
2037
  if (descendantNode.callee.name !== functionName) return;
1980
- if (isInsideGeneratedImageRendererArgument(descendantNode)) return;
2038
+ if (isInsideGeneratedImageRendererArgument$1(descendantNode, scopes)) return;
1981
2039
  hasNormalUsage = true;
1982
2040
  return false;
1983
2041
  });
1984
2042
  return hasNormalUsage;
1985
2043
  };
1986
- const markJsxSubtree = (node, programRoot, generatedImageJsxNodes, visitedComponentNames) => {
2044
+ const markJsxSubtree = (node, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames) => {
1987
2045
  walkAst(node, (descendantNode) => {
1988
2046
  if (!isNodeOfType(descendantNode, "JSXOpeningElement")) return;
1989
2047
  generatedImageJsxNodes.add(descendantNode);
1990
- markComponentRenderJsx(programRoot, descendantNode, generatedImageJsxNodes, visitedComponentNames);
2048
+ markComponentRenderJsx(programRoot, scopes, descendantNode, generatedImageJsxNodes, visitedComponentNames);
1991
2049
  });
1992
2050
  };
1993
- const markGeneratedImageExpression = (expression, programRoot, generatedImageJsxNodes, visitedComponentNames) => {
2051
+ const markGeneratedImageExpression = (expression, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames) => {
1994
2052
  const unwrappedExpression = stripParenExpression(expression);
1995
2053
  if (isNodeOfType(unwrappedExpression, "JSXElement") || isNodeOfType(unwrappedExpression, "JSXFragment")) {
1996
- markJsxSubtree(unwrappedExpression, programRoot, generatedImageJsxNodes, visitedComponentNames);
2054
+ markJsxSubtree(unwrappedExpression, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
1997
2055
  return;
1998
2056
  }
1999
2057
  if (isFunctionLike(unwrappedExpression)) {
2000
- markFunctionReturnJsx(unwrappedExpression, programRoot, generatedImageJsxNodes, visitedComponentNames);
2058
+ markFunctionReturnJsx(unwrappedExpression, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2001
2059
  return;
2002
2060
  }
2003
2061
  if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
2004
- markGeneratedImageExpression(unwrappedExpression.consequent, programRoot, generatedImageJsxNodes, visitedComponentNames);
2005
- markGeneratedImageExpression(unwrappedExpression.alternate, programRoot, generatedImageJsxNodes, visitedComponentNames);
2062
+ markGeneratedImageExpression(unwrappedExpression.consequent, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2063
+ markGeneratedImageExpression(unwrappedExpression.alternate, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2006
2064
  return;
2007
2065
  }
2008
2066
  if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
2009
- markGeneratedImageExpression(unwrappedExpression.left, programRoot, generatedImageJsxNodes, visitedComponentNames);
2010
- markGeneratedImageExpression(unwrappedExpression.right, programRoot, generatedImageJsxNodes, visitedComponentNames);
2067
+ markGeneratedImageExpression(unwrappedExpression.left, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2068
+ markGeneratedImageExpression(unwrappedExpression.right, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2011
2069
  return;
2012
2070
  }
2013
2071
  if (isNodeOfType(unwrappedExpression, "CallExpression")) {
2014
2072
  const callee = unwrappedExpression.callee;
2015
2073
  if (isFunctionLike(callee)) {
2016
- markFunctionReturnJsx(callee, programRoot, generatedImageJsxNodes, visitedComponentNames);
2074
+ markFunctionReturnJsx(callee, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2017
2075
  return;
2018
2076
  }
2019
2077
  if (!isNodeOfType(callee, "Identifier")) return;
2020
2078
  if (visitedComponentNames.has(callee.name)) return;
2021
2079
  if (hasNormalJsxUsage(programRoot, callee.name, generatedImageJsxNodes)) return;
2022
- if (hasNormalFunctionCallUsage(programRoot, callee.name)) return;
2080
+ if (hasNormalFunctionCallUsage(programRoot, callee.name, scopes)) return;
2023
2081
  const binding = findVariableInitializer(callee, callee.name);
2024
2082
  if (!binding?.initializer || !isFunctionLike(stripParenExpression(binding.initializer))) return;
2025
2083
  visitedComponentNames.add(callee.name);
2026
- markFunctionReturnJsx(stripParenExpression(binding.initializer), programRoot, generatedImageJsxNodes, visitedComponentNames);
2084
+ markFunctionReturnJsx(stripParenExpression(binding.initializer), programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2027
2085
  return;
2028
2086
  }
2029
2087
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
@@ -2031,16 +2089,17 @@ const markGeneratedImageExpression = (expression, programRoot, generatedImageJsx
2031
2089
  visitedComponentNames.add(unwrappedExpression.name);
2032
2090
  const binding = findVariableInitializer(unwrappedExpression, unwrappedExpression.name);
2033
2091
  if (!binding?.initializer) return;
2034
- markGeneratedImageExpression(stripParenExpression(binding.initializer), programRoot, generatedImageJsxNodes, visitedComponentNames);
2092
+ markGeneratedImageExpression(stripParenExpression(binding.initializer), programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2035
2093
  }
2036
2094
  };
2037
- const collectGeneratedImageJsxNodes = (programRoot) => {
2095
+ const collectGeneratedImageJsxNodes = (programRoot, scopes) => {
2038
2096
  const cached = generatedImageJsxCache.get(programRoot);
2039
2097
  if (cached) return cached;
2040
2098
  const generatedImageJsxNodes = /* @__PURE__ */ new WeakSet();
2041
- if (hasImportFromModules(programRoot, GENERATED_IMAGE_MODULES)) walkAst(programRoot, (descendantNode) => {
2042
- if (!isGeneratedImageRendererCall(descendantNode)) return;
2043
- for (const argument of descendantNode.arguments) markGeneratedImageExpression(argument, programRoot, generatedImageJsxNodes, /* @__PURE__ */ new Set());
2099
+ if (hasImportFromModules(programRoot, GENERATED_IMAGE_RENDERER_MODULES)) walkAst(programRoot, (descendantNode) => {
2100
+ if (!isNodeOfType(descendantNode, "CallExpression") && !isNodeOfType(descendantNode, "NewExpression")) return;
2101
+ if (!isGeneratedImageRendererCall(descendantNode, scopes)) return;
2102
+ for (const argument of descendantNode.arguments) markGeneratedImageExpression(argument, programRoot, scopes, generatedImageJsxNodes, /* @__PURE__ */ new Set());
2044
2103
  });
2045
2104
  generatedImageJsxCache.set(programRoot, generatedImageJsxNodes);
2046
2105
  return generatedImageJsxNodes;
@@ -2050,7 +2109,7 @@ const isGeneratedImageRenderContext = (context, node) => {
2050
2109
  if (!node) return false;
2051
2110
  const programRoot = findProgramRoot(node);
2052
2111
  if (!programRoot) return false;
2053
- const generatedImageJsxNodes = collectGeneratedImageJsxNodes(programRoot);
2112
+ const generatedImageJsxNodes = collectGeneratedImageJsxNodes(programRoot, context.scopes);
2054
2113
  if (generatedImageJsxNodes.has(node)) return true;
2055
2114
  if (isNodeOfType(node, "JSXElement")) return generatedImageJsxNodes.has(node.openingElement);
2056
2115
  return false;
@@ -4496,15 +4555,6 @@ const getDestructuredBindingPropertyName = (bindingIdentifier) => {
4496
4555
  return getStaticPropertyKeyName(property, { allowComputedString: true });
4497
4556
  };
4498
4557
  //#endregion
4499
- //#region src/plugin/utils/get-static-property-name.ts
4500
- const getStaticPropertyName = (memberExpression) => {
4501
- const property = memberExpression.property;
4502
- if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
4503
- if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
4504
- if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
4505
- return null;
4506
- };
4507
- //#endregion
4508
4558
  //#region src/plugin/utils/has-static-property-write-before.ts
4509
4559
  const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
4510
4560
  const potentiallyAliasedSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
@@ -8200,6 +8250,9 @@ const createMethodMutationAnalysis = (context) => {
8200
8250
  };
8201
8251
  };
8202
8252
  //#endregion
8253
+ //#region src/plugin/utils/is-member-property.ts
8254
+ const isMemberProperty = (node, propertyName) => Boolean(node && isNodeOfType(node, "MemberExpression") && isNodeOfType(node.property, "Identifier") && node.property.name === propertyName);
8255
+ //#endregion
8203
8256
  //#region src/plugin/utils/collect-function-return-statements.ts
8204
8257
  const collectFunctionReturnStatements = (functionNode) => {
8205
8258
  if (!isFunctionLike$1(functionNode) || !isNodeOfType(functionNode.body, "BlockStatement")) return [];
@@ -14364,14 +14417,14 @@ const DEPENDENCY_HOOK_NAMES = new Set([
14364
14417
  "useImperativeHandle",
14365
14418
  "useInsertionEffect"
14366
14419
  ]);
14367
- const crossFileScopes = /* @__PURE__ */ new WeakMap();
14420
+ const crossFileScopes$1 = /* @__PURE__ */ new WeakMap();
14368
14421
  const crossFileControlFlow = /* @__PURE__ */ new WeakMap();
14369
14422
  const forwardedFreshDependencyCache = /* @__PURE__ */ new WeakMap();
14370
- const getCrossFileScopes = (resolved) => {
14371
- const cached = crossFileScopes.get(resolved.programNode);
14423
+ const getCrossFileScopes$1 = (resolved) => {
14424
+ const cached = crossFileScopes$1.get(resolved.programNode);
14372
14425
  if (cached) return cached;
14373
14426
  const scopes = analyzeScopes(resolved.programNode);
14374
- crossFileScopes.set(resolved.programNode, scopes);
14427
+ crossFileScopes$1.set(resolved.programNode, scopes);
14375
14428
  return scopes;
14376
14429
  };
14377
14430
  const getCrossFileControlFlow = (resolved) => {
@@ -14406,7 +14459,7 @@ const isCustomHookFunction = (functionNode, fallbackName) => {
14406
14459
  const displayName = componentOrHookDisplayNameForFunction(functionNode) ?? fallbackName ?? "";
14407
14460
  return /^use[A-Z0-9]/.test(displayName);
14408
14461
  };
14409
- const getImportedHookBinding = (callee, scopes) => {
14462
+ const getImportedHookBinding$1 = (callee, scopes) => {
14410
14463
  if (!isNodeOfType(callee, "Identifier")) return null;
14411
14464
  const importedSymbol = resolveConstIdentifierAlias(callee, scopes);
14412
14465
  if (importedSymbol?.kind !== "import" || !importedSymbol.initializer) return null;
@@ -14422,7 +14475,7 @@ const getImportedHookBinding = (callee, scopes) => {
14422
14475
  };
14423
14476
  const resolveImportedHookFunction = (callee, scopes, currentFilename) => {
14424
14477
  if (!currentFilename) return null;
14425
- const importedBinding = getImportedHookBinding(callee, scopes);
14478
+ const importedBinding = getImportedHookBinding$1(callee, scopes);
14426
14479
  if (!importedBinding) return null;
14427
14480
  const resolved = resolveCrossFileFunctionExportWithFilePath(currentFilename, importedBinding.source, importedBinding.exportedName);
14428
14481
  if (!resolved || !isCustomHookFunction(resolved.functionNode, importedBinding.exportedName)) return null;
@@ -14431,7 +14484,7 @@ const resolveImportedHookFunction = (callee, scopes, currentFilename) => {
14431
14484
  filePath: resolved.filePath,
14432
14485
  functionNode: resolved.functionNode,
14433
14486
  programNode: resolved.programNode,
14434
- scopes: getCrossFileScopes(resolved)
14487
+ scopes: getCrossFileScopes$1(resolved)
14435
14488
  };
14436
14489
  };
14437
14490
  const dependencyIndexForReactHookReference = (expression, scopes, dependencyHookNames, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
@@ -14462,11 +14515,11 @@ const dependencyIndexForReactHookReference = (expression, scopes, dependencyHook
14462
14515
  };
14463
14516
  const getImportedReactDependencyIndex = (callExpression, scopes, currentFilename, dependencyHookNames) => {
14464
14517
  if (!currentFilename) return null;
14465
- const importedBinding = getImportedHookBinding(stripParenExpression(callExpression.callee), scopes);
14518
+ const importedBinding = getImportedHookBinding$1(stripParenExpression(callExpression.callee), scopes);
14466
14519
  if (!importedBinding) return null;
14467
14520
  const resolved = resolveCrossFileValueExportWithFilePath(currentFilename, importedBinding.source, importedBinding.exportedName);
14468
14521
  if (!resolved) return null;
14469
- return dependencyIndexForReactHookReference(resolved.exportedNode, getCrossFileScopes(resolved), dependencyHookNames);
14522
+ return dependencyIndexForReactHookReference(resolved.exportedNode, getCrossFileScopes$1(resolved), dependencyHookNames);
14470
14523
  };
14471
14524
  const resolveHookFunction = (callExpression, scopes, cfg, currentFilename) => {
14472
14525
  const callee = stripParenExpression(callExpression.callee);
@@ -19181,10 +19234,10 @@ const isUncacheableOptionsMergeUtility = (node) => {
19181
19234
  return (argument.properties ?? []).some((property) => isNodeOfType(property, "SpreadElement") && isNodeOfType(property.argument, "Identifier") && parameterNames.has(property.argument.name));
19182
19235
  });
19183
19236
  };
19184
- const isIntlNewExpression = (node) => {
19237
+ const isIntlNewExpression = (node, context) => {
19185
19238
  if (!isNodeOfType(node, "NewExpression")) return false;
19186
19239
  const callee = node.callee;
19187
- if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.object, "Identifier") && callee.object.name === "Intl" && isNodeOfType(callee.property, "Identifier") && INTL_CLASSES.has(callee.property.name)) return true;
19240
+ if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.object, "Identifier") && callee.object.name === "Intl" && context.scopes.isGlobalReference(callee.object) && isNodeOfType(callee.property, "Identifier") && INTL_CLASSES.has(callee.property.name)) return true;
19188
19241
  return false;
19189
19242
  };
19190
19243
  const jsHoistIntl = defineRule({
@@ -19194,7 +19247,7 @@ const jsHoistIntl = defineRule({
19194
19247
  severity: "warn",
19195
19248
  recommendation: "Move `new Intl.NumberFormat(...)` to the top of the file or wrap it in `useMemo`. Building one is slow, so don't redo it on every call",
19196
19249
  create: (context) => ({ NewExpression(node) {
19197
- if (!isIntlNewExpression(node)) return;
19250
+ if (!isIntlNewExpression(node, context)) return;
19198
19251
  let cursor = node.parent ?? null;
19199
19252
  let inFunctionBody = false;
19200
19253
  while (cursor) {
@@ -19959,6 +20012,28 @@ const jsLengthCheckFirst = defineRule({
19959
20012
  } })
19960
20013
  });
19961
20014
  //#endregion
20015
+ //#region src/plugin/utils/is-proven-global-namespace-reference.ts
20016
+ const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
20017
+ const strippedExpression = stripParenExpression(expression);
20018
+ if (!isNodeOfType(strippedExpression, "Identifier")) return false;
20019
+ if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
20020
+ const symbol = scopes.symbolFor(strippedExpression);
20021
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
20022
+ visitedSymbolIds.add(symbol.id);
20023
+ return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
20024
+ };
20025
+ const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
20026
+ const strippedExpression = stripParenExpression(expression);
20027
+ if (isNodeOfType(strippedExpression, "Identifier")) {
20028
+ if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
20029
+ const symbol = scopes.symbolFor(strippedExpression);
20030
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
20031
+ visitedSymbolIds.add(symbol.id);
20032
+ return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
20033
+ }
20034
+ return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
20035
+ };
20036
+ //#endregion
19962
20037
  //#region src/plugin/rules/js-performance/js-min-max-loop.ts
19963
20038
  const builtinMutationByProgram = /* @__PURE__ */ new WeakMap();
19964
20039
  const RUNTIMELESS_SYMBOL_KINDS = new Set(["ts-interface", "ts-type-alias"]);
@@ -20015,26 +20090,6 @@ const isSafeFreshNumericArray = (arrayExpression) => {
20015
20090
  }
20016
20091
  return !(didFindPositiveZero && didFindNegativeZero);
20017
20092
  };
20018
- const isGlobalObjectReference = (expression, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
20019
- const strippedExpression = stripParenExpression(expression);
20020
- if (!isNodeOfType(strippedExpression, "Identifier")) return false;
20021
- if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
20022
- const symbol = scopes.symbolFor(strippedExpression);
20023
- if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbols.has(symbol.id)) return false;
20024
- visitedSymbols.add(symbol.id);
20025
- return isGlobalObjectReference(symbol.initializer, scopes, visitedSymbols);
20026
- };
20027
- const resolvesToGlobalNamespace = (expression, namespaceName, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
20028
- const strippedExpression = stripParenExpression(expression);
20029
- if (isNodeOfType(strippedExpression, "Identifier")) {
20030
- if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
20031
- const symbol = scopes.symbolFor(strippedExpression);
20032
- if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbols.has(symbol.id)) return false;
20033
- visitedSymbols.add(symbol.id);
20034
- return resolvesToGlobalNamespace(symbol.initializer, namespaceName, scopes, visitedSymbols);
20035
- }
20036
- return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isGlobalObjectReference(strippedExpression.object, scopes);
20037
- };
20038
20093
  const resolvesToGlobalMethod = (expression, namespaceName, methodNames, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
20039
20094
  const strippedExpression = stripParenExpression(expression);
20040
20095
  if (isNodeOfType(strippedExpression, "Identifier")) {
@@ -20043,7 +20098,7 @@ const resolvesToGlobalMethod = (expression, namespaceName, methodNames, scopes,
20043
20098
  visitedSymbols.add(symbol.id);
20044
20099
  return resolvesToGlobalMethod(symbol.initializer, namespaceName, methodNames, scopes, visitedSymbols);
20045
20100
  }
20046
- return isNodeOfType(strippedExpression, "MemberExpression") && methodNames.has(getStaticPropertyName(strippedExpression) ?? "") && resolvesToGlobalNamespace(strippedExpression.object, namespaceName, scopes);
20101
+ return isNodeOfType(strippedExpression, "MemberExpression") && methodNames.has(getStaticPropertyName(strippedExpression) ?? "") && isProvenGlobalNamespaceReference(strippedExpression.object, namespaceName, scopes);
20047
20102
  };
20048
20103
  const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
20049
20104
  const strippedExpression = stripParenExpression(expression);
@@ -20055,7 +20110,7 @@ const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /*
20055
20110
  }
20056
20111
  if (isNodeOfType(strippedExpression, "MemberExpression")) {
20057
20112
  const propertyName = getStaticPropertyName(strippedExpression);
20058
- if (propertyName === "prototype") return resolvesToGlobalNamespace(strippedExpression.object, "Array", scopes);
20113
+ if (propertyName === "prototype") return isProvenGlobalNamespaceReference(strippedExpression.object, "Array", scopes);
20059
20114
  return propertyName === "__proto__" && isNodeOfType(stripParenExpression(strippedExpression.object), "ArrayExpression");
20060
20115
  }
20061
20116
  if (!isNodeOfType(strippedExpression, "CallExpression")) return false;
@@ -20066,23 +20121,23 @@ const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /*
20066
20121
  const isGlobalNamespaceReplacementTarget = (target, namespaceName, scopes) => {
20067
20122
  const strippedTarget = stripParenExpression(target);
20068
20123
  if (isNodeOfType(strippedTarget, "Identifier")) return strippedTarget.name === namespaceName && scopes.isGlobalReference(strippedTarget);
20069
- return isNodeOfType(strippedTarget, "MemberExpression") && getStaticPropertyName(strippedTarget) === namespaceName && isGlobalObjectReference(strippedTarget.object, scopes);
20124
+ return isNodeOfType(strippedTarget, "MemberExpression") && getStaticPropertyName(strippedTarget) === namespaceName && isProvenGlobalObjectReference(strippedTarget.object, scopes);
20070
20125
  };
20071
20126
  const isUnsafeBuiltinMemberTarget = (target, targetFunction, scopes) => {
20072
20127
  const strippedTarget = stripParenExpression(target);
20073
20128
  if (!isNodeOfType(strippedTarget, "MemberExpression")) return false;
20074
20129
  const propertyName = getStaticPropertyName(strippedTarget);
20075
20130
  if (resolvesToNativeArrayPrototype(strippedTarget.object, scopes)) return propertyName === null || propertyName === "sort";
20076
- if (resolvesToGlobalNamespace(strippedTarget.object, "Math", scopes)) return propertyName === null || propertyName === targetFunction;
20077
- return isGlobalObjectReference(strippedTarget.object, scopes) && (propertyName === null || propertyName === "Math");
20131
+ if (isProvenGlobalNamespaceReference(strippedTarget.object, "Math", scopes)) return propertyName === null || propertyName === targetFunction;
20132
+ return isProvenGlobalObjectReference(strippedTarget.object, scopes) && (propertyName === null || propertyName === "Math");
20078
20133
  };
20079
20134
  const isUnsafeBuiltinMutationApiCall = (callExpression, targetFunction, scopes) => {
20080
20135
  const target = callExpression.arguments[0];
20081
20136
  if (!target) return false;
20082
20137
  let propertyName = null;
20083
20138
  if (resolvesToNativeArrayPrototype(target, scopes)) propertyName = "sort";
20084
- else if (resolvesToGlobalNamespace(target, "Math", scopes)) propertyName = targetFunction;
20085
- else if (isGlobalObjectReference(target, scopes)) propertyName = "Math";
20139
+ else if (isProvenGlobalNamespaceReference(target, "Math", scopes)) propertyName = targetFunction;
20140
+ else if (isProvenGlobalObjectReference(target, scopes)) propertyName = "Math";
20086
20141
  if (!propertyName) return false;
20087
20142
  const canObjectExpressionSetProperty = (properties) => {
20088
20143
  if (!isNodeOfType(properties, "ObjectExpression")) return true;
@@ -20133,7 +20188,7 @@ const hasUnsafeMathBinding = (node, scopes) => {
20133
20188
  let scope = scopes.scopeFor(node);
20134
20189
  while (scope) {
20135
20190
  const symbol = scope.symbolsByName.get("Math");
20136
- if (symbol && !RUNTIMELESS_SYMBOL_KINDS.has(symbol.kind)) return !(symbol.kind === "const" && symbol.initializer && resolvesToGlobalNamespace(symbol.initializer, "Math", scopes));
20191
+ if (symbol && !RUNTIMELESS_SYMBOL_KINDS.has(symbol.kind)) return !(symbol.kind === "const" && symbol.initializer && isProvenGlobalNamespaceReference(symbol.initializer, "Math", scopes));
20137
20192
  scope = scope.parent;
20138
20193
  }
20139
20194
  return false;
@@ -26486,6 +26541,489 @@ const hasEmailTemplateImport = (programRoot) => {
26486
26541
  return found;
26487
26542
  };
26488
26543
  //#endregion
26544
+ //#region src/plugin/utils/build-generated-image-project-index.ts
26545
+ const GENERATED_IMAGE_SOURCE_FILE_PATTERN = /\.[cm]?[jt]sx?$/i;
26546
+ const GENERATED_IMAGE_DECLARATION_FILE_PATTERN = /\.d\.[cm]?[jt]s$/i;
26547
+ const GENERATED_IMAGE_MDX_FILE_PATTERN = /\.mdx$/i;
26548
+ const GENERATED_IMAGE_IGNORED_DIRECTORY_NAMES = new Set([
26549
+ ".angular",
26550
+ ".astro",
26551
+ ".cache",
26552
+ ".contentlayer",
26553
+ ".docusaurus",
26554
+ ".expo",
26555
+ ".git",
26556
+ ".next",
26557
+ ".nuxt",
26558
+ ".output",
26559
+ ".svelte-kit",
26560
+ ".turbo",
26561
+ ".vercel",
26562
+ "build",
26563
+ "coverage",
26564
+ "dist",
26565
+ "node_modules",
26566
+ "out",
26567
+ "storybook-static"
26568
+ ]);
26569
+ const generatedImageScopeCache = /* @__PURE__ */ new WeakMap();
26570
+ const getGeneratedImageModuleScopes = (programNode) => {
26571
+ const cachedScopes = generatedImageScopeCache.get(programNode);
26572
+ if (cachedScopes) return cachedScopes;
26573
+ const scopes = analyzeScopes(programNode);
26574
+ generatedImageScopeCache.set(programNode, scopes);
26575
+ return scopes;
26576
+ };
26577
+ const listProductionSourceFiles = (rootDirectory) => {
26578
+ const sourceFilePaths = [];
26579
+ const pendingDirectories = [rootDirectory];
26580
+ let hasOpaqueMdxConsumerSurface = false;
26581
+ while (pendingDirectories.length > 0) {
26582
+ const currentDirectory = pendingDirectories.pop();
26583
+ if (!currentDirectory) continue;
26584
+ let entries;
26585
+ try {
26586
+ entries = fs.readdirSync(currentDirectory, { withFileTypes: true });
26587
+ } catch {
26588
+ return null;
26589
+ }
26590
+ for (const entry of entries) {
26591
+ const absolutePath = path.join(currentDirectory, entry.name);
26592
+ const isIgnoredDirectoryName = GENERATED_IMAGE_IGNORED_DIRECTORY_NAMES.has(entry.name) || entry.name.startsWith(".") && entry.name !== ".dumi" && entry.name !== ".storybook";
26593
+ if (entry.isSymbolicLink() && isIgnoredDirectoryName) continue;
26594
+ if (entry.isSymbolicLink()) return null;
26595
+ if (entry.isDirectory()) {
26596
+ if (isIgnoredDirectoryName) continue;
26597
+ pendingDirectories.push(absolutePath);
26598
+ continue;
26599
+ }
26600
+ if (!entry.isFile() || isTestlikeFilename(absolutePath)) continue;
26601
+ if (GENERATED_IMAGE_MDX_FILE_PATTERN.test(entry.name)) {
26602
+ hasOpaqueMdxConsumerSurface = true;
26603
+ continue;
26604
+ }
26605
+ if (!GENERATED_IMAGE_SOURCE_FILE_PATTERN.test(entry.name)) continue;
26606
+ if (GENERATED_IMAGE_DECLARATION_FILE_PATTERN.test(entry.name)) continue;
26607
+ sourceFilePaths.push(normalizeFilename(absolutePath));
26608
+ }
26609
+ }
26610
+ return {
26611
+ sourceFilePaths,
26612
+ hasOpaqueMdxConsumerSurface
26613
+ };
26614
+ };
26615
+ const getRuntimeModuleSource = (node) => {
26616
+ if (isNodeOfType(node, "ImportDeclaration")) {
26617
+ if (isTypeOnlyImport(node)) return null;
26618
+ return typeof node.source.value === "string" ? node.source.value : null;
26619
+ }
26620
+ if (isNodeOfType(node, "ExportNamedDeclaration")) {
26621
+ if (node.exportKind === "type" || isEverySpecifierInlineType(node.specifiers, "ExportSpecifier", "exportKind")) return null;
26622
+ return node.source && typeof node.source.value === "string" ? node.source.value : null;
26623
+ }
26624
+ if (isNodeOfType(node, "ExportAllDeclaration")) {
26625
+ if (node.exportKind === "type") return null;
26626
+ return typeof node.source.value === "string" ? node.source.value : null;
26627
+ }
26628
+ if (isNodeOfType(node, "ImportExpression")) return isNodeOfType(node.source, "Literal") && typeof node.source.value === "string" ? node.source.value : null;
26629
+ if (isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "require" && node.arguments.length === 1) {
26630
+ const source = node.arguments[0];
26631
+ return source && isNodeOfType(source, "Literal") && typeof source.value === "string" ? source.value : null;
26632
+ }
26633
+ return null;
26634
+ };
26635
+ const indexModuleSources = (module, consumerModulesByFilePath, resolvedSourcePathByNode, unresolvedRuntimeSources) => {
26636
+ walkAst(module.programNode, (node) => {
26637
+ const source = getRuntimeModuleSource(node);
26638
+ if (!source) return;
26639
+ const resolvedSourcePath = resolveModulePath(module.filePath, source);
26640
+ if (!resolvedSourcePath) {
26641
+ unresolvedRuntimeSources.add(source);
26642
+ return;
26643
+ }
26644
+ const normalizedSourcePath = normalizeFilename(resolvedSourcePath);
26645
+ resolvedSourcePathByNode.set(node, normalizedSourcePath);
26646
+ const consumerModules = consumerModulesByFilePath.get(normalizedSourcePath) ?? /* @__PURE__ */ new Set();
26647
+ consumerModules.add(module);
26648
+ consumerModulesByFilePath.set(normalizedSourcePath, consumerModules);
26649
+ });
26650
+ };
26651
+ const buildGeneratedImageProjectIndex = (rootDirectory, currentFilePath, currentProgramNode, currentScopes) => {
26652
+ const productionSourceFiles = listProductionSourceFiles(rootDirectory);
26653
+ if (!productionSourceFiles) return null;
26654
+ const modulesByFilePath = /* @__PURE__ */ new Map();
26655
+ for (const filePath of productionSourceFiles.sourceFilePaths) {
26656
+ if (filePath === currentFilePath) {
26657
+ modulesByFilePath.set(filePath, {
26658
+ filePath,
26659
+ programNode: currentProgramNode,
26660
+ scopes: currentScopes
26661
+ });
26662
+ continue;
26663
+ }
26664
+ const parsedProgram = parseSourceFile(filePath);
26665
+ if (!parsedProgram || !isNodeOfType(parsedProgram, "Program")) return null;
26666
+ modulesByFilePath.set(filePath, {
26667
+ filePath,
26668
+ programNode: parsedProgram,
26669
+ scopes: getGeneratedImageModuleScopes(parsedProgram)
26670
+ });
26671
+ }
26672
+ const consumerModulesByFilePath = /* @__PURE__ */ new Map();
26673
+ const resolvedSourcePathByNode = /* @__PURE__ */ new WeakMap();
26674
+ const unresolvedRuntimeSources = /* @__PURE__ */ new Set();
26675
+ for (const module of modulesByFilePath.values()) indexModuleSources(module, consumerModulesByFilePath, resolvedSourcePathByNode, unresolvedRuntimeSources);
26676
+ return {
26677
+ modulesByFilePath,
26678
+ consumerModulesByFilePath,
26679
+ resolvedSourcePathByNode,
26680
+ unresolvedRuntimeSources,
26681
+ hasOpaqueMdxConsumerSurface: productionSourceFiles.hasOpaqueMdxConsumerSurface
26682
+ };
26683
+ };
26684
+ //#endregion
26685
+ //#region src/plugin/utils/read-nearest-package-manifest.ts
26686
+ const cachedPackageDirectoryByFilename = /* @__PURE__ */ new Map();
26687
+ const cachedManifestByPackageDirectory = /* @__PURE__ */ new Map();
26688
+ const resetManifestCaches = () => {
26689
+ cachedPackageDirectoryByFilename.clear();
26690
+ cachedManifestByPackageDirectory.clear();
26691
+ };
26692
+ const findNearestPackageDirectory = (filename) => {
26693
+ if (!filename) return null;
26694
+ const fromCache = cachedPackageDirectoryByFilename.get(filename);
26695
+ if (fromCache !== void 0) {
26696
+ if (isProbeRecorderActive()) {
26697
+ let probedDirectory = path.dirname(filename);
26698
+ while (true) {
26699
+ recordExistenceProbe(path.join(probedDirectory, "package.json"));
26700
+ if (probedDirectory === fromCache) break;
26701
+ const parentDirectory = path.dirname(probedDirectory);
26702
+ if (parentDirectory === probedDirectory) break;
26703
+ probedDirectory = parentDirectory;
26704
+ }
26705
+ }
26706
+ return fromCache;
26707
+ }
26708
+ let currentDirectory = path.dirname(filename);
26709
+ while (true) {
26710
+ const candidatePackageJsonPath = path.join(currentDirectory, "package.json");
26711
+ recordExistenceProbe(candidatePackageJsonPath);
26712
+ let hasPackageJson = false;
26713
+ try {
26714
+ hasPackageJson = fs.statSync(candidatePackageJsonPath).isFile();
26715
+ } catch {
26716
+ hasPackageJson = false;
26717
+ }
26718
+ if (hasPackageJson) {
26719
+ cachedPackageDirectoryByFilename.set(filename, currentDirectory);
26720
+ return currentDirectory;
26721
+ }
26722
+ const parentDirectory = path.dirname(currentDirectory);
26723
+ if (parentDirectory === currentDirectory) {
26724
+ cachedPackageDirectoryByFilename.set(filename, null);
26725
+ return null;
26726
+ }
26727
+ currentDirectory = parentDirectory;
26728
+ }
26729
+ };
26730
+ const readNearestPackageManifest = (filename) => {
26731
+ const packageDirectory = findNearestPackageDirectory(filename);
26732
+ if (!packageDirectory) return null;
26733
+ return readPackageManifest(packageDirectory);
26734
+ };
26735
+ const readPackageManifest = (packageDirectory) => {
26736
+ const packageJsonPath = path.join(packageDirectory, "package.json");
26737
+ recordContentProbe(packageJsonPath);
26738
+ const cached = cachedManifestByPackageDirectory.get(packageDirectory);
26739
+ if (cached !== void 0) return cached;
26740
+ let manifest = null;
26741
+ try {
26742
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
26743
+ if (typeof parsed === "object" && parsed !== null) manifest = parsed;
26744
+ } catch {
26745
+ manifest = null;
26746
+ }
26747
+ cachedManifestByPackageDirectory.set(packageDirectory, manifest);
26748
+ return manifest;
26749
+ };
26750
+ //#endregion
26751
+ //#region src/plugin/utils/is-exported-jsx-owned-by-generated-image-renderers.ts
26752
+ const getExportedSpecifierName = (specifier) => {
26753
+ const exported = specifier.exported;
26754
+ if (isNodeOfType(exported, "Identifier")) return exported.name;
26755
+ return isNodeOfType(exported, "Literal") && typeof exported.value === "string" ? exported.value : null;
26756
+ };
26757
+ const getImportedSpecifierName = (specifier) => {
26758
+ const local = specifier.local;
26759
+ if (isNodeOfType(local, "Identifier")) return local.name;
26760
+ return isNodeOfType(local, "Literal") && typeof local.value === "string" ? local.value : null;
26761
+ };
26762
+ const getImportSpecifierName = (specifier) => {
26763
+ if (isNodeOfType(specifier, "ImportDefaultSpecifier")) return "default";
26764
+ if (!isNodeOfType(specifier, "ImportSpecifier")) return null;
26765
+ const imported = specifier.imported;
26766
+ if (isNodeOfType(imported, "Identifier")) return imported.name;
26767
+ return isNodeOfType(imported, "Literal") && typeof imported.value === "string" ? imported.value : null;
26768
+ };
26769
+ const getDirectFunctionBindingIdentifier = (functionNode) => {
26770
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
26771
+ const functionValueRoot = findTransparentExpressionRoot(functionNode);
26772
+ const parent = functionValueRoot.parent;
26773
+ return isNodeOfType(parent, "VariableDeclarator") && parent.init === functionValueRoot && isNodeOfType(parent.id, "Identifier") ? parent.id : null;
26774
+ };
26775
+ const getExportNamesForFunction = (programNode, functionNode) => {
26776
+ const functionValueRoot = findTransparentExpressionRoot(functionNode);
26777
+ const bindingName = getDirectFunctionBindingIdentifier(functionNode)?.name ?? null;
26778
+ const exportedNames = /* @__PURE__ */ new Set();
26779
+ for (const statement of programNode.body) {
26780
+ if (isNodeOfType(statement, "ExportDefaultDeclaration")) {
26781
+ if (statement.declaration === functionValueRoot || bindingName && isNodeOfType(statement.declaration, "Identifier") && statement.declaration.name === bindingName) exportedNames.add("default");
26782
+ continue;
26783
+ }
26784
+ if (!isNodeOfType(statement, "ExportNamedDeclaration")) continue;
26785
+ const declaration = statement.declaration;
26786
+ if (declaration === functionValueRoot && bindingName) exportedNames.add(bindingName);
26787
+ if (declaration && isNodeOfType(declaration, "VariableDeclaration")) {
26788
+ for (const declarator of declaration.declarations) if (declarator.init === functionValueRoot && isNodeOfType(declarator.id, "Identifier")) exportedNames.add(declarator.id.name);
26789
+ }
26790
+ if (!bindingName || statement.source) continue;
26791
+ for (const specifier of statement.specifiers) {
26792
+ if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
26793
+ if (getImportedSpecifierName(specifier) !== bindingName) continue;
26794
+ const exportedName = getExportedSpecifierName(specifier);
26795
+ if (exportedName) exportedNames.add(exportedName);
26796
+ }
26797
+ }
26798
+ return [...exportedNames];
26799
+ };
26800
+ const isTransparentGeneratedImageValueFlow = (expression, target) => {
26801
+ let current = findTransparentExpressionRoot(expression);
26802
+ while (current !== target) {
26803
+ const parent = current.parent;
26804
+ if (!parent) return false;
26805
+ if (!((isNodeOfType(parent, "JSXExpressionContainer") || isNodeOfType(parent, "JSXSpreadChild")) && parent.expression === current || (isNodeOfType(parent, "JSXElement") || isNodeOfType(parent, "JSXFragment")) && parent.children.some((child) => child === current) || isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === current || parent.alternate === current) || isNodeOfType(parent, "LogicalExpression") && (parent.left === current || parent.right === current) || isNodeOfType(parent, "ArrayExpression") && parent.elements.some((element) => element === current) || isNodeOfType(parent, "SequenceExpression") && parent.expressions.at(-1) === current || isNodeOfType(parent, "AwaitExpression") && parent.argument === current)) return false;
26806
+ current = findTransparentExpressionRoot(parent);
26807
+ }
26808
+ return true;
26809
+ };
26810
+ const isInsideGeneratedImageRendererArgument = (expression, scopes) => {
26811
+ let cursor = expression;
26812
+ while (cursor?.parent) {
26813
+ const parent = cursor.parent;
26814
+ if (isFunctionLike$1(parent)) return false;
26815
+ if (isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression")) {
26816
+ if (parent.arguments[0] && isTransparentGeneratedImageValueFlow(expression, parent.arguments[0]) && isGeneratedImageRendererCall(parent, scopes)) return true;
26817
+ }
26818
+ cursor = parent;
26819
+ }
26820
+ return false;
26821
+ };
26822
+ const getInvokedExpression = (identifier) => {
26823
+ const referenceExpression = findTransparentExpressionRoot(identifier);
26824
+ const parent = referenceExpression.parent;
26825
+ if (isNodeOfType(parent, "CallExpression") && parent.callee === referenceExpression) return parent;
26826
+ if (isNodeOfType(parent, "TaggedTemplateExpression") && parent.tag === referenceExpression) return parent;
26827
+ if ((isNodeOfType(parent, "JSXOpeningElement") || isNodeOfType(parent, "JSXClosingElement")) && parent.name === identifier) {
26828
+ const element = parent.parent;
26829
+ return isNodeOfType(element, "JSXElement") ? element : null;
26830
+ }
26831
+ return null;
26832
+ };
26833
+ const getForwardingFunction = (expression) => {
26834
+ const enclosingFunction = findEnclosingFunction$1(expression);
26835
+ if (!enclosingFunction) return null;
26836
+ if (isNodeOfType(enclosingFunction, "ArrowFunctionExpression") && !isNodeOfType(enclosingFunction.body, "BlockStatement") && isTransparentGeneratedImageValueFlow(expression, enclosingFunction.body)) return enclosingFunction;
26837
+ let cursor = expression.parent;
26838
+ while (cursor && cursor !== enclosingFunction) {
26839
+ if (isFunctionLike$1(cursor)) return null;
26840
+ if (isNodeOfType(cursor, "ReturnStatement") && cursor.argument && isTransparentGeneratedImageValueFlow(expression, cursor.argument)) return enclosingFunction;
26841
+ cursor = cursor.parent;
26842
+ }
26843
+ return null;
26844
+ };
26845
+ const enqueueExport = (state, filePath, exportedName) => {
26846
+ state.pendingExports.push({
26847
+ filePath: normalizeFilename(filePath),
26848
+ exportedName
26849
+ });
26850
+ };
26851
+ const classifyInvokedExpression = (module, expression, state) => {
26852
+ if (isInsideGeneratedImageRendererArgument(expression, module.scopes)) {
26853
+ state.didReachRenderer = true;
26854
+ return true;
26855
+ }
26856
+ const forwardingFunction = getForwardingFunction(expression);
26857
+ if (!forwardingFunction) return false;
26858
+ const exportedNames = getExportNamesForFunction(module.programNode, forwardingFunction);
26859
+ if (exportedNames.length === 0) return false;
26860
+ for (const exportedName of exportedNames) enqueueExport(state, module.filePath, exportedName);
26861
+ return true;
26862
+ };
26863
+ const classifySymbolReferences = (module, symbol, state, visitedSymbolIds) => {
26864
+ if (visitedSymbolIds.has(symbol.id)) return true;
26865
+ visitedSymbolIds.add(symbol.id);
26866
+ for (const reference of symbol.references) {
26867
+ if (reference.flag !== "read") return false;
26868
+ state.currentExportWasUsed = true;
26869
+ const identifier = reference.identifier;
26870
+ const invokedExpression = getInvokedExpression(identifier);
26871
+ if (invokedExpression) {
26872
+ if (!classifyInvokedExpression(module, invokedExpression, state)) return false;
26873
+ continue;
26874
+ }
26875
+ const parent = identifier.parent;
26876
+ if (isNodeOfType(parent, "ExportSpecifier") && parent.local === identifier) {
26877
+ const exportedName = getExportedSpecifierName(parent);
26878
+ if (!exportedName) return false;
26879
+ enqueueExport(state, module.filePath, exportedName);
26880
+ continue;
26881
+ }
26882
+ if (isNodeOfType(parent, "ExportDefaultDeclaration")) {
26883
+ enqueueExport(state, module.filePath, "default");
26884
+ continue;
26885
+ }
26886
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === identifier && isNodeOfType(parent.id, "Identifier") && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const") {
26887
+ const aliasSymbol = module.scopes.symbolFor(parent.id);
26888
+ if (!aliasSymbol || !classifySymbolReferences(module, aliasSymbol, state, visitedSymbolIds)) return false;
26889
+ continue;
26890
+ }
26891
+ return false;
26892
+ }
26893
+ return true;
26894
+ };
26895
+ const classifyNamespaceImportReferences = (module, symbol, exportedName, state) => {
26896
+ for (const reference of symbol.references) {
26897
+ if (reference.flag !== "read") return false;
26898
+ const identifier = reference.identifier;
26899
+ const parent = identifier.parent;
26900
+ if (!isNodeOfType(parent, "MemberExpression") || parent.object !== identifier) return false;
26901
+ const propertyName = getStaticPropertyName(parent);
26902
+ if (propertyName === null) return false;
26903
+ if (propertyName !== exportedName) continue;
26904
+ state.currentExportWasUsed = true;
26905
+ const invokedExpression = getInvokedExpression(parent);
26906
+ if (!invokedExpression || !classifyInvokedExpression(module, invokedExpression, state)) return false;
26907
+ }
26908
+ return true;
26909
+ };
26910
+ const classifyImportsFromExport = (module, exportIdentity, state) => {
26911
+ for (const statement of module.programNode.body) {
26912
+ if (isNodeOfType(statement, "ImportDeclaration")) {
26913
+ if (state.projectIndex.resolvedSourcePathByNode.get(statement) !== exportIdentity.filePath) continue;
26914
+ if (statement.importKind === "type") continue;
26915
+ for (const specifier of statement.specifiers) {
26916
+ if (isNodeOfType(specifier, "ImportSpecifier") && specifier.importKind === "type") continue;
26917
+ if (isNodeOfType(specifier, "ImportNamespaceSpecifier")) {
26918
+ const namespaceSymbol = module.scopes.symbolFor(specifier.local);
26919
+ if (!namespaceSymbol || !classifyNamespaceImportReferences(module, namespaceSymbol, exportIdentity.exportedName, state)) return false;
26920
+ continue;
26921
+ }
26922
+ if (getImportSpecifierName(specifier) !== exportIdentity.exportedName) continue;
26923
+ const symbol = module.scopes.symbolFor(specifier.local);
26924
+ if (!symbol || !classifySymbolReferences(module, symbol, state, /* @__PURE__ */ new Set())) return false;
26925
+ }
26926
+ continue;
26927
+ }
26928
+ if ((isNodeOfType(statement, "ExportNamedDeclaration") || isNodeOfType(statement, "ExportAllDeclaration")) && statement.source && state.projectIndex.resolvedSourcePathByNode.get(statement) === exportIdentity.filePath) {
26929
+ if (isNodeOfType(statement, "ExportAllDeclaration")) {
26930
+ if (statement.exported) return false;
26931
+ state.currentExportWasUsed = true;
26932
+ enqueueExport(state, module.filePath, exportIdentity.exportedName);
26933
+ continue;
26934
+ }
26935
+ for (const specifier of statement.specifiers) {
26936
+ if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
26937
+ if (getImportedSpecifierName(specifier) !== exportIdentity.exportedName) continue;
26938
+ const exportedName = getExportedSpecifierName(specifier);
26939
+ if (!exportedName) return false;
26940
+ state.currentExportWasUsed = true;
26941
+ enqueueExport(state, module.filePath, exportedName);
26942
+ }
26943
+ }
26944
+ }
26945
+ return true;
26946
+ };
26947
+ const hasOpaqueDynamicImportOfExport = (module, exportIdentity, projectIndex) => {
26948
+ let isOpaque = false;
26949
+ walkAst(module.programNode, (node) => {
26950
+ if (isOpaque) return false;
26951
+ if (isNodeOfType(node, "ImportExpression")) {
26952
+ if (projectIndex.resolvedSourcePathByNode.get(node) === exportIdentity.filePath) {
26953
+ isOpaque = true;
26954
+ return false;
26955
+ }
26956
+ }
26957
+ if (isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "require" && node.arguments.length === 1) {
26958
+ if (projectIndex.resolvedSourcePathByNode.get(node) === exportIdentity.filePath) {
26959
+ isOpaque = true;
26960
+ return false;
26961
+ }
26962
+ }
26963
+ });
26964
+ return isOpaque;
26965
+ };
26966
+ const classifyLocalExportReferences = (module, exportIdentity, state) => {
26967
+ const exportedValue = findExportedValue(module.programNode, exportIdentity.exportedName);
26968
+ if (!exportedValue || !isFunctionLike$1(exportedValue)) return true;
26969
+ const bindingIdentifier = getDirectFunctionBindingIdentifier(exportedValue);
26970
+ if (!bindingIdentifier) return true;
26971
+ const symbol = module.scopes.symbolFor(bindingIdentifier);
26972
+ return symbol ? classifySymbolReferences(module, symbol, state, /* @__PURE__ */ new Set()) : false;
26973
+ };
26974
+ const hasOpaqueWorkspacePackageConsumer = (projectIndex, exportIdentity) => {
26975
+ const packageName = readNearestPackageManifest(exportIdentity.filePath)?.name;
26976
+ if (typeof packageName !== "string" || packageName.length === 0) return false;
26977
+ for (const unresolvedSource of projectIndex.unresolvedRuntimeSources) if (unresolvedSource === packageName || unresolvedSource.startsWith(`${packageName}/`)) return true;
26978
+ return false;
26979
+ };
26980
+ const createExportedJsxGeneratedImageOwnershipAnalyzer = (context) => {
26981
+ const filename = context.filename ? normalizeFilename(context.filename) : "";
26982
+ const rootDirectorySetting = getReactDoctorStringSetting(context.settings, "rootDirectory");
26983
+ const rootDirectory = rootDirectorySetting ? normalizeFilename(rootDirectorySetting).replace(/\/$/, "") : "";
26984
+ const isFileInsideRoot = Boolean(filename && rootDirectory) && (filename === rootDirectory || filename.startsWith(`${rootDirectory}/`));
26985
+ let projectIndex;
26986
+ return (jsxNode) => {
26987
+ if (!isFileInsideRoot) return false;
26988
+ const programNode = findProgramRoot(jsxNode);
26989
+ const enclosingFunction = findEnclosingFunction$1(jsxNode);
26990
+ if (!programNode || !enclosingFunction) return false;
26991
+ const initialExportNames = getExportNamesForFunction(programNode, enclosingFunction);
26992
+ if (initialExportNames.length === 0) return false;
26993
+ if (projectIndex === void 0) projectIndex = buildGeneratedImageProjectIndex(rootDirectory, filename, programNode, context.scopes);
26994
+ if (!projectIndex || projectIndex.hasOpaqueMdxConsumerSurface) return false;
26995
+ const state = {
26996
+ projectIndex,
26997
+ pendingExports: initialExportNames.map((exportedName) => ({
26998
+ filePath: filename,
26999
+ exportedName
27000
+ })),
27001
+ visitedExportKeys: /* @__PURE__ */ new Set(),
27002
+ currentExportWasUsed: false,
27003
+ didReachRenderer: false
27004
+ };
27005
+ while (state.pendingExports.length > 0) {
27006
+ const exportIdentity = state.pendingExports.pop();
27007
+ if (!exportIdentity) continue;
27008
+ const exportKey = `${exportIdentity.filePath}\0${exportIdentity.exportedName}`;
27009
+ if (state.visitedExportKeys.has(exportKey)) continue;
27010
+ state.visitedExportKeys.add(exportKey);
27011
+ state.currentExportWasUsed = false;
27012
+ if (hasOpaqueWorkspacePackageConsumer(projectIndex, exportIdentity)) return false;
27013
+ const ownerModule = projectIndex.modulesByFilePath.get(exportIdentity.filePath);
27014
+ if (!ownerModule || !classifyLocalExportReferences(ownerModule, exportIdentity, state)) return false;
27015
+ const consumerModules = projectIndex.consumerModulesByFilePath.get(exportIdentity.filePath) ?? [];
27016
+ for (const module of consumerModules) {
27017
+ if (module.filePath === exportIdentity.filePath) continue;
27018
+ if (hasOpaqueDynamicImportOfExport(module, exportIdentity, projectIndex)) return false;
27019
+ if (!classifyImportsFromExport(module, exportIdentity, state)) return false;
27020
+ }
27021
+ if (!state.currentExportWasUsed) return false;
27022
+ }
27023
+ return state.didReachRenderer;
27024
+ };
27025
+ };
27026
+ //#endregion
26489
27027
  //#region src/plugin/rules/nextjs/nextjs-no-img-element.ts
26490
27028
  const NON_OPTIMIZABLE_SRC_PREFIX_PATTERN = /^\s*(data:|blob:)/i;
26491
27029
  const GENERATED_URL_NAME_PATTERN = /(data|object|blob)_?url/i;
@@ -26588,9 +27126,20 @@ const nextjsNoImgElement = defineRule({
26588
27126
  recommendation: "Use `next/image` so users get optimized formats, responsive srcsets, and lazy loading instead of oversized image downloads.",
26589
27127
  create: (context) => {
26590
27128
  if (isGeneratedImageRenderContext(context)) return {};
27129
+ const generatedImageOwnershipByFunction = /* @__PURE__ */ new WeakMap();
27130
+ const isExportedJsxGeneratedImageOwned = createExportedJsxGeneratedImageOwnershipAnalyzer(context);
26591
27131
  return { JSXOpeningElement(node) {
26592
27132
  if (resolveJsxElementType(node) !== "img") return;
26593
27133
  if (isGeneratedImageRenderContext(context, node)) return;
27134
+ const enclosingFunction = findEnclosingFunction$1(node);
27135
+ if (enclosingFunction) {
27136
+ let isGeneratedImageOwned = generatedImageOwnershipByFunction.get(enclosingFunction);
27137
+ if (isGeneratedImageOwned === void 0) {
27138
+ isGeneratedImageOwned = isExportedJsxGeneratedImageOwned(node);
27139
+ generatedImageOwnershipByFunction.set(enclosingFunction, isGeneratedImageOwned);
27140
+ }
27141
+ if (isGeneratedImageOwned) return;
27142
+ }
26594
27143
  const programRoot = findProgramRoot(node);
26595
27144
  if (programRoot && hasEmailTemplateImport(programRoot)) return;
26596
27145
  const srcAttribute = findJsxAttribute(node.attributes, "src");
@@ -27936,7 +28485,11 @@ const isProvenNativeReadMethod = (ref, methodName) => Boolean(ref.resolved?.defs
27936
28485
  }));
27937
28486
  //#endregion
27938
28487
  //#region src/plugin/rules/state-and-effects/utils/effect/react.ts
27939
- const KNOWN_PURE_HOC_NAMES = new Set(["memo", "forwardRef"]);
28488
+ const KNOWN_COMPONENT_WRAPPER_NAMES = new Set([
28489
+ "memo",
28490
+ "forwardRef",
28491
+ "observer"
28492
+ ]);
27940
28493
  const startsWithUppercase = (name) => Boolean(name && name.length > 0 && name[0] >= "A" && name[0] <= "Z");
27941
28494
  const isReactFunctionalComponent = (node) => {
27942
28495
  if (!node) return false;
@@ -27958,7 +28511,7 @@ const isReactFunctionalHOC = (analysis, node) => {
27958
28511
  const isWrappedInline = () => {
27959
28512
  if (!isNodeOfType(init, "CallExpression")) return false;
27960
28513
  if (!isNodeOfType(init.callee, "Identifier")) return false;
27961
- if (KNOWN_PURE_HOC_NAMES.has(init.callee.name)) return false;
28514
+ if (KNOWN_COMPONENT_WRAPPER_NAMES.has(init.callee.name)) return false;
27962
28515
  const firstArg = init.arguments?.[0];
27963
28516
  if (!firstArg) return false;
27964
28517
  return isNodeOfType(firstArg, "ArrowFunctionExpression") || isNodeOfType(firstArg, "FunctionExpression");
@@ -27978,7 +28531,7 @@ const isReactFunctionalHOC = (analysis, node) => {
27978
28531
  if (!args.includes(refId)) continue;
27979
28532
  const callee = parent.callee;
27980
28533
  const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "CallExpression") && isNodeOfType(callee.callee, "Identifier") ? callee.callee.name : null;
27981
- if (calleeName != null && !KNOWN_PURE_HOC_NAMES.has(calleeName)) return true;
28534
+ if (calleeName != null && !KNOWN_COMPONENT_WRAPPER_NAMES.has(calleeName)) return true;
27982
28535
  }
27983
28536
  return false;
27984
28537
  };
@@ -31030,72 +31583,6 @@ const isReactNativeDependencyName = (dependencyName) => {
31030
31583
  return false;
31031
31584
  };
31032
31585
  //#endregion
31033
- //#region src/plugin/utils/read-nearest-package-manifest.ts
31034
- const cachedPackageDirectoryByFilename = /* @__PURE__ */ new Map();
31035
- const cachedManifestByPackageDirectory = /* @__PURE__ */ new Map();
31036
- const resetManifestCaches = () => {
31037
- cachedPackageDirectoryByFilename.clear();
31038
- cachedManifestByPackageDirectory.clear();
31039
- };
31040
- const findNearestPackageDirectory = (filename) => {
31041
- if (!filename) return null;
31042
- const fromCache = cachedPackageDirectoryByFilename.get(filename);
31043
- if (fromCache !== void 0) {
31044
- if (isProbeRecorderActive()) {
31045
- let probedDirectory = path.dirname(filename);
31046
- while (true) {
31047
- recordExistenceProbe(path.join(probedDirectory, "package.json"));
31048
- if (probedDirectory === fromCache) break;
31049
- const parentDirectory = path.dirname(probedDirectory);
31050
- if (parentDirectory === probedDirectory) break;
31051
- probedDirectory = parentDirectory;
31052
- }
31053
- }
31054
- return fromCache;
31055
- }
31056
- let currentDirectory = path.dirname(filename);
31057
- while (true) {
31058
- const candidatePackageJsonPath = path.join(currentDirectory, "package.json");
31059
- recordExistenceProbe(candidatePackageJsonPath);
31060
- let hasPackageJson = false;
31061
- try {
31062
- hasPackageJson = fs.statSync(candidatePackageJsonPath).isFile();
31063
- } catch {
31064
- hasPackageJson = false;
31065
- }
31066
- if (hasPackageJson) {
31067
- cachedPackageDirectoryByFilename.set(filename, currentDirectory);
31068
- return currentDirectory;
31069
- }
31070
- const parentDirectory = path.dirname(currentDirectory);
31071
- if (parentDirectory === currentDirectory) {
31072
- cachedPackageDirectoryByFilename.set(filename, null);
31073
- return null;
31074
- }
31075
- currentDirectory = parentDirectory;
31076
- }
31077
- };
31078
- const readNearestPackageManifest = (filename) => {
31079
- const packageDirectory = findNearestPackageDirectory(filename);
31080
- if (!packageDirectory) return null;
31081
- return readPackageManifest(packageDirectory);
31082
- };
31083
- const readPackageManifest = (packageDirectory) => {
31084
- const packageJsonPath = path.join(packageDirectory, "package.json");
31085
- recordContentProbe(packageJsonPath);
31086
- const cached = cachedManifestByPackageDirectory.get(packageDirectory);
31087
- if (cached !== void 0) return cached;
31088
- let manifest = null;
31089
- try {
31090
- const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
31091
- if (typeof parsed === "object" && parsed !== null) manifest = parsed;
31092
- } catch {
31093
- manifest = null;
31094
- }
31095
- cachedManifestByPackageDirectory.set(packageDirectory, manifest);
31096
- return manifest;
31097
- };
31098
- //#endregion
31099
31586
  //#region src/plugin/utils/classify-package-platform.ts
31100
31587
  const WEB_FRAMEWORK_DEPENDENCY_NAMES = new Set([
31101
31588
  "next",
@@ -32148,6 +32635,24 @@ const isBuiltinNamespaceCallee = (callee) => {
32148
32635
  }
32149
32636
  return false;
32150
32637
  };
32638
+ const getReactUseCallbackSource = (reference, context) => {
32639
+ const identifier = reference.identifier;
32640
+ const symbol = resolveConstIdentifierAlias(identifier, context.scopes);
32641
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer) return null;
32642
+ const initializer = stripParenExpression(symbol.initializer);
32643
+ if (isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", context.scopes, {
32644
+ allowGlobalReactNamespace: true,
32645
+ resolveNamedAliases: true
32646
+ })) return initializer;
32647
+ return null;
32648
+ };
32649
+ const getDependencyStateRefs = (analysis, context, dependencyReference) => {
32650
+ const useCallbackCall = getReactUseCallbackSource(dependencyReference, context);
32651
+ if (!useCallbackCall) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
32652
+ const dependencyList = useCallbackCall.arguments?.[1];
32653
+ if (!dependencyList || !isNodeOfType(dependencyList, "ArrayExpression")) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
32654
+ return getDownstreamRefs(analysis, dependencyList).flatMap((reference) => getUpstreamRefs(analysis, reference)).filter((reference) => isState(analysis, reference));
32655
+ };
32151
32656
  const isSimpleExpression$1 = (analysis, expression, effectFn, visitedDeclarators) => {
32152
32657
  let isSimple = true;
32153
32658
  walkAst(expression, (child) => {
@@ -32195,7 +32700,7 @@ const noChainStateUpdates = defineRule({
32195
32700
  if (!effectFnRefs || !depsRefs) return;
32196
32701
  const effectFn = getEffectFn(analysis, node);
32197
32702
  if (!effectFn) return;
32198
- const stateDeps = depsRefs.flatMap((ref) => getUpstreamRefs(analysis, ref)).filter((ref) => isState(analysis, ref));
32703
+ const stateDeps = depsRefs.flatMap((reference) => getDependencyStateRefs(analysis, context, reference));
32199
32704
  if (stateDeps.length === 0) return;
32200
32705
  if (stateDeps.every((ref) => isExternallyDrivenState(analysis, ref))) return;
32201
32706
  const stateDepDeclarators = new Set(stateDeps.map((ref) => getUseStateDeclarator(ref)).filter((declarator) => declarator !== null));
@@ -32454,16 +32959,7 @@ const isProvenIntrinsicJsxElement = (openingElement, scopes) => {
32454
32959
  return isIntrinsicValue(openingElement.name);
32455
32960
  };
32456
32961
  //#endregion
32457
- //#region src/plugin/rules/react-builtins/is-safe-create-ref-callback-current-write.ts
32458
- const pathStartsWith$1 = (propertyPath, prefix) => prefix.every((propertyName, index) => propertyPath[index] === propertyName);
32459
- const collectMemberExpression = (identifier) => {
32460
- let expression = findTransparentExpressionRoot(identifier);
32461
- while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) {
32462
- if (!getStaticPropertyName(expression.parent)) return null;
32463
- expression = findTransparentExpressionRoot(expression.parent);
32464
- }
32465
- return expression;
32466
- };
32962
+ //#region src/plugin/utils/is-inline-intrinsic-ref-callback.ts
32467
32963
  const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
32468
32964
  const functionExpression = findTransparentExpressionRoot(functionNode);
32469
32965
  if (!isFunctionLike$1(functionExpression) || functionExpression.async || functionExpression.generator) return false;
@@ -32474,6 +32970,17 @@ const isInlineIntrinsicRefCallback = (functionNode, scopes) => {
32474
32970
  const openingElement = attribute.parent;
32475
32971
  return Boolean(openingElement && isNodeOfType(openingElement, "JSXOpeningElement") && isProvenIntrinsicJsxElement(openingElement, scopes));
32476
32972
  };
32973
+ //#endregion
32974
+ //#region src/plugin/rules/react-builtins/is-safe-create-ref-callback-current-write.ts
32975
+ const pathStartsWith$1 = (propertyPath, prefix) => prefix.every((propertyName, index) => propertyPath[index] === propertyName);
32976
+ const collectMemberExpression = (identifier) => {
32977
+ let expression = findTransparentExpressionRoot(identifier);
32978
+ while (expression.parent && isNodeOfType(expression.parent, "MemberExpression") && expression.parent.object === expression) {
32979
+ if (!getStaticPropertyName(expression.parent)) return null;
32980
+ expression = findTransparentExpressionRoot(expression.parent);
32981
+ }
32982
+ return expression;
32983
+ };
32477
32984
  const isSafeCreateRefCallbackCurrentWrite = (referenceNode, accessedPropertyPath, targetPropertyPath, scopes) => {
32478
32985
  if (accessedPropertyPath.length !== targetPropertyPath.length + 1 || !pathStartsWith$1(accessedPropertyPath, targetPropertyPath) || accessedPropertyPath[targetPropertyPath.length] !== "current") return false;
32479
32986
  const memberExpression = collectMemberExpression(referenceNode);
@@ -35417,17 +35924,178 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
35417
35924
  walkInsideStatementBlocks(analysisFunction.body, visitor);
35418
35925
  }
35419
35926
  };
35420
- const collectWrittenStateNamesInEffect = (analysisFunctions, setterToStateName) => {
35421
- const writtenStateNames = /* @__PURE__ */ new Set();
35927
+ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
35928
+ const unwrappedExpression = stripParenExpression(expression);
35929
+ if (isNodeOfType(unwrappedExpression, "Literal")) {
35930
+ const literalValue = unwrappedExpression.value;
35931
+ if (literalValue === null || typeof literalValue === "boolean" || typeof literalValue === "number" || typeof literalValue === "string") return { value: literalValue };
35932
+ return null;
35933
+ }
35934
+ if (isNodeOfType(unwrappedExpression, "Identifier")) {
35935
+ if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
35936
+ if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
35937
+ const immutableSymbol = scopes.symbolFor(unwrappedExpression);
35938
+ if (immutableSymbol?.kind !== "const" || !immutableSymbol.initializer || !isNodeOfType(immutableSymbol.declarationNode, "VariableDeclarator") || immutableSymbol.declarationNode.id !== immutableSymbol.bindingIdentifier || immutableSymbol.declarationNode.init !== immutableSymbol.initializer || immutableSymbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(immutableSymbol.id)) return null;
35939
+ return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
35940
+ }
35941
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
35942
+ if (unwrappedExpression.operator === "void") return { value: void 0 };
35943
+ if (unwrappedExpression.operator !== "!") return null;
35944
+ const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35945
+ return argumentValue ? { value: !argumentValue.value } : null;
35946
+ }
35947
+ if (isNodeOfType(unwrappedExpression, "CallExpression")) {
35948
+ if (isNodeOfType(unwrappedExpression.callee, "Identifier") && unwrappedExpression.callee.name === "Boolean" && scopes.isGlobalReference(unwrappedExpression.callee) && unwrappedExpression.arguments.length === 1 && unwrappedExpression.arguments[0] && !isNodeOfType(unwrappedExpression.arguments[0], "SpreadElement")) {
35949
+ const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
35950
+ return argumentValue ? { value: Boolean(argumentValue.value) } : null;
35951
+ }
35952
+ return null;
35953
+ }
35954
+ if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
35955
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35956
+ if (!leftValue) return null;
35957
+ if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
35958
+ if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
35959
+ if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
35960
+ return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35961
+ }
35962
+ if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
35963
+ const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35964
+ if (!testValue) return null;
35965
+ return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35966
+ }
35967
+ if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
35968
+ const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35969
+ if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
35970
+ return null;
35971
+ }
35972
+ if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
35973
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35974
+ const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35975
+ if (!leftValue || !rightValue) return null;
35976
+ if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
35977
+ const areEqual = leftValue.value === rightValue.value;
35978
+ return { value: unwrappedExpression.operator === "===" ? areEqual : !areEqual };
35979
+ }
35980
+ if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
35981
+ const isLeftNullish = leftValue.value === null || leftValue.value === void 0;
35982
+ const isRightNullish = rightValue.value === null || rightValue.value === void 0;
35983
+ if (!isLeftNullish && !isRightNullish && typeof leftValue.value !== typeof rightValue.value) return null;
35984
+ const areEqual = isLeftNullish || isRightNullish ? isLeftNullish && isRightNullish : leftValue.value === rightValue.value;
35985
+ return { value: unwrappedExpression.operator === "==" ? areEqual : !areEqual };
35986
+ }
35987
+ }
35988
+ return null;
35989
+ };
35990
+ const readStaticUpdaterReturnValue = (updater, scopes) => {
35991
+ if (!isFunctionLike$1(updater) || updater.async || updater.generator) return null;
35992
+ if (!isNodeOfType(updater.body, "BlockStatement")) return readStaticEffectValue(updater.body, scopes, null, null);
35993
+ if (updater.body.body.length === 0) return { value: void 0 };
35994
+ if (updater.body.body.length !== 1) return null;
35995
+ const returnStatement = updater.body.body[0];
35996
+ if (!isNodeOfType(returnStatement, "ReturnStatement")) return null;
35997
+ if (!returnStatement.argument) return { value: void 0 };
35998
+ return readStaticEffectValue(returnStatement.argument, scopes, null, null);
35999
+ };
36000
+ const readStaticSetterValue = (setterCall, scopes) => {
36001
+ const argument = setterCall.arguments[0];
36002
+ if (!argument) return { value: void 0 };
36003
+ if (isNodeOfType(argument, "SpreadElement")) return null;
36004
+ const updater = resolveExactLocalFunction(argument, scopes);
36005
+ if (updater) return readStaticUpdaterReturnValue(updater, scopes);
36006
+ return readStaticEffectValue(argument, scopes, null, null);
36007
+ };
36008
+ const collectStateWritesInEffect = (analysisFunctions, setterToStateName, scopes) => {
36009
+ const stateWrites = /* @__PURE__ */ new Map();
35422
36010
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
35423
36011
  if (!isNodeOfType(child, "CallExpression")) return;
35424
36012
  if (!isNodeOfType(child.callee, "Identifier")) return;
35425
36013
  const stateName = setterToStateName.get(child.callee.name);
35426
- if (stateName) writtenStateNames.add(stateName);
36014
+ if (!stateName) return;
36015
+ const writeInfo = stateWrites.get(stateName) ?? {
36016
+ values: /* @__PURE__ */ new Set(),
36017
+ hasUnknownValue: false
36018
+ };
36019
+ const staticValue = readStaticSetterValue(child, scopes);
36020
+ if (staticValue) writeInfo.values.add(staticValue.value);
36021
+ else writeInfo.hasUnknownValue = true;
36022
+ stateWrites.set(stateName, writeInfo);
35427
36023
  });
35428
- return writtenStateNames;
36024
+ return stateWrites;
36025
+ };
36026
+ const isGlobalBooleanCall = (node, scopes) => {
36027
+ return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
36028
+ };
36029
+ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
36030
+ let currentNode = workNode;
36031
+ while (currentNode.parent) {
36032
+ const parentNode = currentNode.parent;
36033
+ if (isFunctionLike$1(parentNode)) break;
36034
+ if (isNodeOfType(parentNode, "IfStatement")) {
36035
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
36036
+ if (testValue) {
36037
+ if (currentNode === parentNode.consequent && !testValue.value) return false;
36038
+ if (currentNode === parentNode.alternate && testValue.value) return false;
36039
+ }
36040
+ }
36041
+ if (isNodeOfType(parentNode, "ConditionalExpression")) {
36042
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
36043
+ if (testValue) {
36044
+ if (currentNode === parentNode.consequent && !testValue.value) return false;
36045
+ if (currentNode === parentNode.alternate && testValue.value) return false;
36046
+ }
36047
+ }
36048
+ if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
36049
+ const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
36050
+ if (leftValue) {
36051
+ if (parentNode.operator === "&&" && !leftValue.value) return false;
36052
+ if (parentNode.operator === "||" && leftValue.value) return false;
36053
+ if (parentNode.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return false;
36054
+ }
36055
+ }
36056
+ if (isNodeOfType(parentNode, "BlockStatement")) {
36057
+ const statementIndex = parentNode.body.findIndex((statement) => statement === currentNode);
36058
+ if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
36059
+ const earlierStatement = parentNode.body[index];
36060
+ if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
36061
+ if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
36062
+ }
36063
+ }
36064
+ currentNode = parentNode;
36065
+ }
36066
+ return true;
36067
+ };
36068
+ const isReaderWorkNode = (node, analysisFunctions, scopes) => {
36069
+ if (isNodeOfType(node, "CallExpression")) {
36070
+ if (isGlobalBooleanCall(node, scopes)) return false;
36071
+ const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
36072
+ return !invokedFunction || !analysisFunctions.has(invokedFunction);
36073
+ }
36074
+ return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
36075
+ };
36076
+ const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
36077
+ if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
36078
+ for (const writtenValue of writeInfo.values) {
36079
+ const stateValue = { value: writtenValue };
36080
+ let didFindReachableWork = false;
36081
+ visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
36082
+ if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
36083
+ if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
36084
+ });
36085
+ if (didFindReachableWork) return true;
36086
+ }
36087
+ return false;
35429
36088
  };
35430
36089
  const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
36090
+ const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
36091
+ "clear",
36092
+ "delete",
36093
+ "entries",
36094
+ "get",
36095
+ "has",
36096
+ "keys",
36097
+ "values"
36098
+ ]);
35431
36099
  const isFunctionShapedReturn = (returnedValue, setterToStateName, isExplicitReturnStatement) => {
35432
36100
  if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
35433
36101
  if (isNodeOfType(returnedValue, "CallExpression")) {
@@ -35478,27 +36146,135 @@ const callsOpaqueExternalSetter = (analysisFunctions, setterToStateName) => {
35478
36146
  });
35479
36147
  return didFindOpaqueSetterCall;
35480
36148
  };
36149
+ const isReactRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && (isReactApiCall(expression, "useRef", scopes, {
36150
+ allowGlobalReactNamespace: true,
36151
+ allowUnboundBareCalls: true,
36152
+ resolveNamedAliases: true
36153
+ }) || isReactApiCall(expression, "createRef", scopes, {
36154
+ allowGlobalReactNamespace: true,
36155
+ allowUnboundBareCalls: true,
36156
+ resolveNamedAliases: true
36157
+ }));
36158
+ const getDirectReactRefSymbol = (rawExpression, scopes) => {
36159
+ const expression = stripParenExpression(rawExpression);
36160
+ if (!isNodeOfType(expression, "Identifier")) return null;
36161
+ const symbol = scopes.symbolFor(expression);
36162
+ if (!symbol) return null;
36163
+ const initializer = getDirectUnreassignedInitializer(symbol);
36164
+ return initializer && isReactRefCall(stripParenExpression(initializer), scopes) ? symbol : null;
36165
+ };
36166
+ const isReactNativeJsxElement = (openingElement, scopes) => {
36167
+ if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
36168
+ const symbol = scopes.symbolFor(openingElement.name);
36169
+ const importDeclaration = symbol?.declarationNode.parent;
36170
+ return Boolean(symbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react-native");
36171
+ };
36172
+ const isDirectHostJsxRef = (symbol, scopes) => {
36173
+ let hostRefCount = 0;
36174
+ for (const reference of symbol.references) {
36175
+ const expression = findTransparentExpressionRoot(reference.identifier);
36176
+ const container = expression.parent;
36177
+ if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
36178
+ if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
36179
+ const attribute = container.parent;
36180
+ if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
36181
+ const openingElement = attribute.parent;
36182
+ if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
36183
+ hostRefCount += 1;
36184
+ }
36185
+ return hostRefCount > 0;
36186
+ };
36187
+ const isIntrinsicRefCallbackParameter = (expression, scopes) => {
36188
+ const identifier = stripParenExpression(expression);
36189
+ if (!isNodeOfType(identifier, "Identifier")) return false;
36190
+ const callback = findEnclosingFunction$1(identifier);
36191
+ if (!callback || !isFunctionLike$1(callback) || !isInlineIntrinsicRefCallback(callback, scopes)) return false;
36192
+ const rawFirstParameter = callback.params?.[0];
36193
+ const firstParameter = isNodeOfType(rawFirstParameter, "AssignmentPattern") ? rawFirstParameter.left : rawFirstParameter;
36194
+ const symbol = scopes.symbolFor(identifier);
36195
+ return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter);
36196
+ };
36197
+ const getDirectReactRefCall = (symbol, scopes) => {
36198
+ const initializer = getDirectUnreassignedInitializer(symbol);
36199
+ if (!initializer) return null;
36200
+ const expression = stripParenExpression(initializer);
36201
+ return isNodeOfType(expression, "CallExpression") && isReactRefCall(expression, scopes) ? expression : null;
36202
+ };
36203
+ const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
36204
+ const initialValue = getDirectReactRefCall(symbol, scopes)?.arguments?.[0];
36205
+ if (!initialValue || !isNodeOfType(initialValue, "NewExpression") || !isNodeOfType(initialValue.callee, "Identifier") || initialValue.callee.name !== "Map" || !scopes.isGlobalReference(initialValue.callee) || initialValue.arguments.length !== 0) return false;
36206
+ let intrinsicValueWriteCount = 0;
36207
+ for (const reference of symbol.references) {
36208
+ const identifier = findTransparentExpressionRoot(reference.identifier);
36209
+ const currentMember = identifier.parent;
36210
+ if (!isNodeOfType(currentMember, "MemberExpression") || currentMember.object !== identifier || getStaticPropertyName(currentMember) !== "current") return false;
36211
+ const currentExpression = findTransparentExpressionRoot(currentMember);
36212
+ const methodMember = currentExpression.parent;
36213
+ if (!isNodeOfType(methodMember, "MemberExpression") || methodMember.object !== currentExpression) return false;
36214
+ const methodName = getStaticPropertyName(methodMember);
36215
+ if (methodName === "size") continue;
36216
+ const call = methodMember.parent;
36217
+ if (!isNodeOfType(call, "CallExpression") || call.callee !== methodMember) return false;
36218
+ if (methodName && NON_CONTAMINATING_MAP_METHOD_NAMES.has(methodName)) continue;
36219
+ if (methodName !== "set") return false;
36220
+ const storedValue = call.arguments[1];
36221
+ if (!storedValue || isNodeOfType(storedValue, "SpreadElement") || !isIntrinsicRefCallbackParameter(storedValue, scopes)) return false;
36222
+ intrinsicValueWriteCount += 1;
36223
+ }
36224
+ return intrinsicValueWriteCount > 0;
36225
+ };
36226
+ const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollectionValue = false, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
36227
+ const expression = stripParenExpression(rawExpression);
36228
+ if (isNodeOfType(expression, "Identifier")) {
36229
+ const symbol = scopes.symbolFor(expression);
36230
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
36231
+ const initializer = getDirectUnreassignedInitializer(symbol);
36232
+ if (!initializer) return false;
36233
+ visitedSymbolIds.add(symbol.id);
36234
+ return isDerivedFromProvenDomRefCurrent(initializer, scopes, didReadCollectionValue, visitedSymbolIds);
36235
+ }
36236
+ if (isNodeOfType(expression, "MemberExpression")) {
36237
+ if (getStaticPropertyName(expression) === "current") {
36238
+ const symbol = getDirectReactRefSymbol(expression.object, scopes);
36239
+ return Boolean(symbol && (isDirectHostJsxRef(symbol, scopes) || didReadCollectionValue && storesOnlyIntrinsicRefCallbackValues(symbol, scopes)));
36240
+ }
36241
+ return isDerivedFromProvenDomRefCurrent(expression.object, scopes, didReadCollectionValue, visitedSymbolIds);
36242
+ }
36243
+ if (!isNodeOfType(expression, "CallExpression")) return false;
36244
+ const callee = stripParenExpression(expression.callee);
36245
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
36246
+ return isDerivedFromProvenDomRefCurrent(callee.object, scopes, didReadCollectionValue || getStaticPropertyName(callee) === "get", visitedSymbolIds);
36247
+ };
36248
+ const isCommittedDomSyncNode = (node, scopes) => {
36249
+ if (!isNodeOfType(node, "CallExpression")) return false;
36250
+ const callee = stripParenExpression(node.callee);
36251
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
36252
+ const propertyName = getStaticPropertyName(callee);
36253
+ if (propertyName === null || !EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES.has(propertyName)) return false;
36254
+ return isDerivedFromProvenDomRefCurrent(callee.object, scopes) || isProvenBrowserApiReceiver(callee.object, "dom-event-target", scopes);
36255
+ };
35481
36256
  const isExternalSyncNode = (node) => {
35482
36257
  if (isNodeOfType(node, "NewExpression")) return isNodeOfType(node.callee, "Identifier") && EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS.has(node.callee.name);
35483
36258
  if (isNodeOfType(node, "AssignmentExpression")) return isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "current";
35484
36259
  if (!isNodeOfType(node, "CallExpression")) return false;
35485
36260
  if (isNodeOfType(node.callee, "Identifier")) return EXTERNAL_SYNC_DIRECT_CALLEE_NAMES.has(node.callee.name);
35486
- if (!isNodeOfType(node.callee, "MemberExpression") || !isNodeOfType(node.callee.property, "Identifier")) return false;
35487
- const propertyName = node.callee.property.name;
36261
+ if (!isNodeOfType(node.callee, "MemberExpression")) return false;
36262
+ const propertyName = getStaticPropertyName(node.callee);
36263
+ if (propertyName === null) return false;
35488
36264
  if (EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName)) return true;
35489
36265
  if (isBrowserStorageReceiver(node.callee.object)) return true;
35490
36266
  if (!EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES.has(propertyName)) return false;
35491
36267
  const receiverRootName = getRootIdentifierName(node.callee.object);
35492
36268
  return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
35493
36269
  };
35494
- const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName) => {
36270
+ const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, scopes, allowCommittedDomSync) => {
35495
36271
  if (!isFunctionLike$1(effectCallback)) return false;
35496
36272
  if (!isNodeOfType(effectCallback.body, "BlockStatement")) {
35497
36273
  if (isFunctionShapedReturn(effectCallback.body, setterToStateName, false)) return true;
35498
36274
  } else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, true)) return true;
35499
36275
  let didFindExternalCall = false;
35500
36276
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
35501
- if (isExternalSyncNode(child)) didFindExternalCall = true;
36277
+ if (isExternalSyncNode(child) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
35502
36278
  });
35503
36279
  return didFindExternalCall;
35504
36280
  };
@@ -35514,32 +36290,45 @@ const noEffectChain = defineRule({
35514
36290
  const useStateBindings = collectUseStateBindings(componentBody);
35515
36291
  if (useStateBindings.length === 0) return;
35516
36292
  const setterToStateName = /* @__PURE__ */ new Map();
35517
- for (const binding of useStateBindings) setterToStateName.set(binding.setterName, binding.valueName);
36293
+ const stateSymbolIds = /* @__PURE__ */ new Map();
36294
+ for (const binding of useStateBindings) {
36295
+ setterToStateName.set(binding.setterName, binding.valueName);
36296
+ if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
36297
+ const stateIdentifier = binding.declarator.id.elements[0];
36298
+ if (isNodeOfType(stateIdentifier, "Identifier")) {
36299
+ const stateSymbol = context.scopes.symbolFor(stateIdentifier);
36300
+ if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
36301
+ }
36302
+ }
35518
36303
  const storageSetterNames = collectStorageHookSetterNames(componentBody);
35519
36304
  const effectInfos = [];
35520
36305
  for (const effectCall of findTopLevelEffectCalls(componentBody)) {
35521
36306
  const callback = getEffectCallback(effectCall, context.scopes);
35522
36307
  if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
35523
36308
  const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
35524
- const writtenStateNames = collectWrittenStateNamesInEffect(analysisFunctions, setterToStateName);
36309
+ const stateWrites = collectStateWritesInEffect(analysisFunctions, setterToStateName, context.scopes);
36310
+ const writtenStateNames = new Set(stateWrites.keys());
35525
36311
  effectInfos.push({
35526
36312
  node: effectCall,
35527
36313
  depNames: collectDepIdentifierNames(effectCall),
35528
- writtenStateNames,
35529
- isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
36314
+ stateWrites,
36315
+ analysisFunctions,
36316
+ isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName, context.scopes, writtenStateNames.size === 0) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
35530
36317
  });
35531
36318
  }
35532
36319
  if (effectInfos.length < 2) return;
35533
36320
  const reportedNodes = /* @__PURE__ */ new Set();
35534
36321
  for (const writerEffect of effectInfos) {
35535
36322
  if (writerEffect.isExternalSync) continue;
35536
- if (writerEffect.writtenStateNames.size === 0) continue;
36323
+ if (writerEffect.stateWrites.size === 0) continue;
35537
36324
  for (const readerEffect of effectInfos) {
35538
36325
  if (readerEffect === writerEffect) continue;
35539
36326
  if (readerEffect.isExternalSync) continue;
35540
36327
  if (readerEffect.depNames.size === 0) continue;
35541
36328
  let chainedStateName = null;
35542
- for (const writtenName of writerEffect.writtenStateNames) if (readerEffect.depNames.has(writtenName)) {
36329
+ for (const [writtenName, writeInfo] of writerEffect.stateWrites) {
36330
+ if (!readerEffect.depNames.has(writtenName)) continue;
36331
+ if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
35543
36332
  chainedStateName = writtenName;
35544
36333
  break;
35545
36334
  }
@@ -39206,6 +39995,18 @@ const functionContainsProvenReactHookCall = (functionNode, scopes) => {
39206
39995
  return containsReactHookCall;
39207
39996
  };
39208
39997
  //#endregion
39998
+ //#region src/plugin/utils/function-returns-only-null.ts
39999
+ const isNullExpression = (expression) => {
40000
+ const candidate = stripParenExpression(expression);
40001
+ return isNodeOfType(candidate, "Literal") && candidate.value === null;
40002
+ };
40003
+ const functionReturnsOnlyNull = (functionNode) => {
40004
+ if (!isFunctionLike$1(functionNode)) return false;
40005
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
40006
+ const returnStatements = collectFunctionReturnStatements(functionNode);
40007
+ return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
40008
+ };
40009
+ //#endregion
39209
40010
  //#region src/plugin/utils/function-returns-props-children.ts
39210
40011
  const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
39211
40012
  if (!isFunctionLike$1(functionNode) || functionNode.params.length === 0) return false;
@@ -39238,17 +40039,8 @@ const functionReturnsPropsChildren = (functionNode, scopes, controlFlow) => {
39238
40039
  }, controlFlow);
39239
40040
  };
39240
40041
  //#endregion
39241
- //#region src/plugin/utils/function-returns-only-null.ts
39242
- const isNullExpression = (expression) => {
39243
- const candidate = stripParenExpression(expression);
39244
- return isNodeOfType(candidate, "Literal") && candidate.value === null;
39245
- };
39246
- const functionReturnsOnlyNull = (functionNode) => {
39247
- if (!isFunctionLike$1(functionNode)) return false;
39248
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNullExpression(functionNode.body);
39249
- const returnStatements = collectFunctionReturnStatements(functionNode);
39250
- return returnStatements.length > 0 && returnStatements.every((returnStatement) => Boolean(returnStatement.argument && isNullExpression(returnStatement.argument)));
39251
- };
40042
+ //#region src/plugin/utils/function-has-react-component-evidence.ts
40043
+ const functionHasReactComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
39252
40044
  //#endregion
39253
40045
  //#region src/plugin/utils/is-proven-styled-component-expression.ts
39254
40046
  const findFactoryRoot = (node) => {
@@ -39281,17 +40073,16 @@ const isProvenStyledComponentExpression = (expression, scopes) => {
39281
40073
  //#region src/plugin/utils/is-proven-react-component-symbol.ts
39282
40074
  const REACT_COMPONENT_HOC_NAMES = new Set(["memo", "forwardRef"]);
39283
40075
  const LEGACY_REACT_COMPONENT_FACTORY_NAMES = new Set(["createClass", "createReactClass"]);
39284
- const functionHasComponentEvidence = (functionNode, scopes, controlFlow) => functionContainsReactRenderOutput(functionNode, scopes, controlFlow) || functionReturnsPropsChildren(functionNode, scopes, controlFlow) || functionContainsProvenReactHookCall(functionNode, scopes) && functionReturnsOnlyNull(functionNode);
39285
40076
  const isProvenReactComponentExpression = (expression, scopes, controlFlow, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39286
40077
  const candidate = stripParenExpression(expression);
39287
- if (isInlineFunctionExpression(candidate)) return functionHasComponentEvidence(candidate, scopes, controlFlow);
40078
+ if (isInlineFunctionExpression(candidate)) return functionHasReactComponentEvidence(candidate, scopes, controlFlow);
39288
40079
  if (isNodeOfType(candidate, "ClassExpression")) return isProvenReactClassComponent(candidate, scopes);
39289
40080
  if (isProvenStyledComponentExpression(candidate, scopes)) return true;
39290
40081
  if (isNodeOfType(candidate, "Identifier")) {
39291
40082
  const symbol = scopes.symbolFor(candidate);
39292
40083
  if (!symbol || visitedSymbolIds.has(symbol.id) || hasSymbolWriteBefore(symbol, candidate, scopes)) return false;
39293
40084
  visitedSymbolIds.add(symbol.id);
39294
- if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasComponentEvidence(symbol.declarationNode, scopes, controlFlow);
40085
+ if (isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return functionHasReactComponentEvidence(symbol.declarationNode, scopes, controlFlow);
39295
40086
  if (isNodeOfType(symbol.declarationNode, "ClassDeclaration") || isNodeOfType(symbol.declarationNode, "ClassExpression")) return isProvenReactClassComponent(symbol.declarationNode, scopes);
39296
40087
  return Boolean(symbol.initializer && isProvenReactComponentExpression(symbol.initializer, scopes, controlFlow, visitedSymbolIds));
39297
40088
  }
@@ -39318,7 +40109,7 @@ const isProvenReactComponentSymbol = (symbol, scopes, controlFlow, componentRefe
39318
40109
  for (const candidateSymbol of candidateSymbols) {
39319
40110
  if (hasSymbolWriteBefore(candidateSymbol, componentReference, scopes)) continue;
39320
40111
  if (isComponentDeclaration(candidateSymbol.declarationNode)) {
39321
- if (functionHasComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
40112
+ if (functionHasReactComponentEvidence(candidateSymbol.declarationNode, scopes, controlFlow)) return true;
39322
40113
  continue;
39323
40114
  }
39324
40115
  const initializer = candidateSymbol.initializer ? stripParenExpression(candidateSymbol.initializer) : null;
@@ -43104,6 +43895,53 @@ const noPropCallbackInEffect = defineRule({
43104
43895
  });
43105
43896
  //#endregion
43106
43897
  //#region src/plugin/rules/state-and-effects/no-prop-callback-in-render.ts
43898
+ const functionBindingSymbols = (functionNode, scopes) => {
43899
+ let bindingIdentifier = null;
43900
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) bindingIdentifier = functionNode.id;
43901
+ else {
43902
+ let bindingExpression = findTransparentExpressionRoot(functionNode);
43903
+ let parent = bindingExpression.parent;
43904
+ while (isNodeOfType(parent, "CallExpression") && parent.arguments[0] === bindingExpression) {
43905
+ const callee = parent.callee;
43906
+ const wrapperName = isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") ? callee.property.name : null;
43907
+ if (!isReactApiCall(parent, REACT_HOC_NAMES, scopes, {
43908
+ allowGlobalReactNamespace: true,
43909
+ resolveNamedAliases: true
43910
+ }) && (!wrapperName || REACT_HOC_NAMES.has(wrapperName) || !COMPONENT_HOC_WRAPPER_NAMES.has(wrapperName))) break;
43911
+ bindingExpression = findTransparentExpressionRoot(parent);
43912
+ parent = bindingExpression.parent;
43913
+ }
43914
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === bindingExpression && isNodeOfType(parent.id, "Identifier")) bindingIdentifier = parent.id;
43915
+ }
43916
+ if (!bindingIdentifier) return [];
43917
+ let scope = scopes.scopeFor(functionNode);
43918
+ while (scope) {
43919
+ const symbols = scope.symbols.filter((symbol) => symbol.bindingIdentifier === bindingIdentifier);
43920
+ if (symbols.length > 0) return symbols;
43921
+ scope = scope.parent;
43922
+ }
43923
+ return [];
43924
+ };
43925
+ const symbolHasReactComponentUse = (symbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
43926
+ if (visitedSymbolIds.has(symbol.id)) return false;
43927
+ visitedSymbolIds.add(symbol.id);
43928
+ for (const reference of symbol.references) {
43929
+ const identifier = reference.identifier;
43930
+ if (hasSymbolWriteBefore(symbol, identifier, scopes)) continue;
43931
+ const parent = identifier.parent;
43932
+ if (isNodeOfType(parent, "JSXOpeningElement") && isNodeOfType(parent.name, "JSXIdentifier") && parent.name === identifier) return true;
43933
+ const expression = findTransparentExpressionRoot(identifier);
43934
+ const expressionParent = expression.parent;
43935
+ if (isNodeOfType(expressionParent, "CallExpression") && expressionParent.arguments[0] === expression && isReactApiCall(expressionParent, "createElement", scopes, { resolveNamedAliases: true })) return true;
43936
+ if (!isNodeOfType(expressionParent, "VariableDeclarator") || expressionParent.init !== expression || !isNodeOfType(expressionParent.id, "Identifier") || !isNodeOfType(expressionParent.parent, "VariableDeclaration") || expressionParent.parent.kind !== "const") continue;
43937
+ const aliasSymbol = scopes.symbolFor(expressionParent.id);
43938
+ if (aliasSymbol && symbolHasReactComponentUse(aliasSymbol, scopes, visitedSymbolIds)) return true;
43939
+ }
43940
+ return false;
43941
+ };
43942
+ const functionHasReactComponentUse = (functionNode, scopes) => {
43943
+ return functionBindingSymbols(functionNode, scopes).some((symbol) => symbolHasReactComponentUse(symbol, scopes));
43944
+ };
43107
43945
  const isPreservedThroughConciseArrow = (callExpression, scopes) => {
43108
43946
  let node = callExpression;
43109
43947
  let parent = node.parent;
@@ -43150,7 +43988,10 @@ const noPropCallbackInRender = defineRule({
43150
43988
  create: (context) => ({ CallExpression(node) {
43151
43989
  if (!isResultDiscardedCall(node)) return;
43152
43990
  if (isPreservedThroughConciseArrow(node, context.scopes)) return;
43153
- if (!findRenderPhaseComponentOrHook(node, context.scopes)) return;
43991
+ const renderPhaseOwner = findRenderPhaseComponentOrHook(node, context.scopes);
43992
+ if (!renderPhaseOwner) return;
43993
+ const renderPhaseOwnerName = componentOrHookDisplayNameForFunction(renderPhaseOwner);
43994
+ if (!renderPhaseOwnerName || !isReactHookName(renderPhaseOwnerName) && !functionHasReactComponentEvidence(renderPhaseOwner, context.scopes, context.cfg) && !functionHasReactComponentUse(renderPhaseOwner, context.scopes)) return;
43154
43995
  const analysis = getProgramAnalysis(node);
43155
43996
  if (!analysis) return;
43156
43997
  const callee = stripParenExpression(node.callee);
@@ -43886,11 +44727,110 @@ const isSameRefCurrentMember = (node, refSymbol, scopes) => {
43886
44727
  return isNodeOfType(receiver, "Identifier") && resolveConstIdentifierAlias(receiver, scopes)?.id === refSymbol.id;
43887
44728
  };
43888
44729
  const isSameRefCurrentAlias = (node, refSymbol, scopes) => {
43889
- if (isSameRefCurrentMember(node, refSymbol, scopes)) return true;
43890
- if (!isNodeOfType(node, "Identifier")) return false;
43891
- const aliasSymbol = scopes.symbolFor(node);
44730
+ const expression = stripParenExpression(node);
44731
+ if (isSameRefCurrentMember(expression, refSymbol, scopes)) return true;
44732
+ if (!isNodeOfType(expression, "Identifier")) return false;
44733
+ const aliasSymbol = scopes.symbolFor(expression);
43892
44734
  return aliasSymbol?.kind === "const" && aliasSymbol.initializer !== null && isSameRefCurrentMember(stripParenExpression(aliasSymbol.initializer), refSymbol, scopes);
43893
44735
  };
44736
+ const resolveImmutableInitializationValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
44737
+ const expression = stripParenExpression(node);
44738
+ if (!isNodeOfType(expression, "Identifier")) return expression;
44739
+ const symbol = scopes.symbolFor(expression);
44740
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(symbol.id)) return null;
44741
+ visitedSymbolIds.add(symbol.id);
44742
+ return resolveImmutableInitializationValue(symbol.initializer, scopes, visitedSymbolIds);
44743
+ };
44744
+ const isProvablyTruthyInitializationValue = (node, scopes) => {
44745
+ const expression = resolveImmutableInitializationValue(node, scopes);
44746
+ return Boolean(expression && (isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression")));
44747
+ };
44748
+ const getInitializationConstructorName = (node, scopes) => {
44749
+ const expression = resolveImmutableInitializationValue(node, scopes);
44750
+ if (!expression) return null;
44751
+ if (isNodeOfType(expression, "NewExpression")) {
44752
+ const callee = stripParenExpression(expression.callee);
44753
+ return isNodeOfType(callee, "Identifier") ? callee.name : null;
44754
+ }
44755
+ return null;
44756
+ };
44757
+ const isClosedTruthyTypeDomain = (typeNode, initializationValue, scopes) => {
44758
+ const initializationExpression = stripParenExpression(initializationValue);
44759
+ if (isNodeOfType(typeNode, "TSTypeLiteral")) return isNodeOfType(initializationExpression, "ObjectExpression");
44760
+ if (isNodeOfType(typeNode, "TSArrayType") || isNodeOfType(typeNode, "TSTupleType")) return isNodeOfType(initializationExpression, "ArrayExpression");
44761
+ if (isNodeOfType(typeNode, "TSFunctionType") || isNodeOfType(typeNode, "TSConstructorType")) return isNodeOfType(initializationExpression, "ArrowFunctionExpression") || isNodeOfType(initializationExpression, "FunctionExpression") || isNodeOfType(initializationExpression, "ClassExpression");
44762
+ if (isNodeOfType(typeNode, "TSObjectKeyword")) return true;
44763
+ if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
44764
+ const typeName = typeNode.typeName;
44765
+ return isNodeOfType(typeName, "Identifier") && typeName.name === getInitializationConstructorName(initializationExpression, scopes);
44766
+ };
44767
+ const refHasClosedFalsySentinelDomain = (refSymbol, initializationValue, scopes) => {
44768
+ const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
44769
+ if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
44770
+ const [initialValue] = initializer.arguments ?? [];
44771
+ if (!initialValue || isNodeOfType(initialValue, "SpreadElement") || !isEmptySentinel(initialValue, scopes)) return false;
44772
+ const [declaredType] = initializer.typeArguments?.params ?? [];
44773
+ if (!declaredType || !isNodeOfType(declaredType, "TSUnionType")) return false;
44774
+ let hasEmptySentinel = false;
44775
+ let hasTruthyDomain = false;
44776
+ for (const memberType of declaredType.types ?? []) {
44777
+ if (isNodeOfType(memberType, "TSNullKeyword") || isNodeOfType(memberType, "TSUndefinedKeyword")) {
44778
+ hasEmptySentinel = true;
44779
+ continue;
44780
+ }
44781
+ if (!isClosedTruthyTypeDomain(memberType, initializationValue, scopes)) return false;
44782
+ hasTruthyDomain = true;
44783
+ }
44784
+ return hasEmptySentinel && hasTruthyDomain;
44785
+ };
44786
+ const isSafeRefIdentifierUse = (identifier) => {
44787
+ const expressionRoot = findTransparentExpressionRoot(identifier);
44788
+ const parent = expressionRoot.parent;
44789
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.id === expressionRoot && parent.parent !== null && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const") return true;
44790
+ if (parent && isNodeOfType(parent, "MemberExpression") && parent.object === expressionRoot && getStaticPropertyName(parent) === "current") return true;
44791
+ if (!parent || !isNodeOfType(parent, "VariableDeclarator") || parent.init !== expressionRoot) return false;
44792
+ return isNodeOfType(parent.id, "Identifier") && parent.parent !== null && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const";
44793
+ };
44794
+ const refDoesNotEscape = (branchRoot, refSymbol, scopes) => {
44795
+ let didEscape = false;
44796
+ walkAst(branchRoot, (child) => {
44797
+ if (didEscape) return false;
44798
+ if (!isNodeOfType(child, "Identifier")) return;
44799
+ if (resolveConstIdentifierAlias(child, scopes)?.id !== refSymbol.id) return;
44800
+ if (child === refSymbol.bindingIdentifier || isSafeRefIdentifierUse(child)) return;
44801
+ didEscape = true;
44802
+ return false;
44803
+ });
44804
+ return !didEscape;
44805
+ };
44806
+ const expressionContainsRefCurrent = (expression, refSymbol, scopes) => {
44807
+ let didFindRefCurrent = false;
44808
+ walkAst(expression, (child) => {
44809
+ if (didFindRefCurrent) return false;
44810
+ if (resolveReactRefSymbol(child, scopes)?.id !== refSymbol.id) return;
44811
+ didFindRefCurrent = true;
44812
+ return false;
44813
+ });
44814
+ return didFindRefCurrent;
44815
+ };
44816
+ const hasNoCompetingRefCurrentWrite = (branchRoot, assignmentExpression, refSymbol, scopes) => {
44817
+ let writeCount = 0;
44818
+ walkAst(branchRoot, (child) => {
44819
+ if (writeCount > 1) return false;
44820
+ if (isNodeOfType(child, "AssignmentExpression")) {
44821
+ if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
44822
+ return;
44823
+ }
44824
+ if (isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
44825
+ if (expressionContainsRefCurrent(child.argument, refSymbol, scopes)) writeCount++;
44826
+ return;
44827
+ }
44828
+ if (isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement")) {
44829
+ if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
44830
+ }
44831
+ });
44832
+ return writeCount === 1 && expressionContainsRefCurrent(assignmentExpression.left, refSymbol, scopes);
44833
+ };
43894
44834
  const isEmptySentinel = (node, scopes) => isNodeOfType(node, "Literal") && node.value === null || isNodeOfType(node, "Identifier") && node.name === "undefined" && scopes.isGlobalReference(node);
43895
44835
  const hasRepeatedExecutionAncestor = (node, stop) => {
43896
44836
  let ancestor = node.parent;
@@ -43939,18 +44879,22 @@ const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol
43939
44879
  const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes) => {
43940
44880
  if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return true;
43941
44881
  if (assignmentExpression.operator !== "=") return false;
44882
+ const renderOwner = findRenderPhaseComponentOrHook(assignmentExpression, scopes);
44883
+ if (!renderOwner) return false;
43942
44884
  let descendant = assignmentExpression;
43943
44885
  let ancestor = descendant.parent;
43944
44886
  while (ancestor) {
43945
- if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(ancestor.test, "BinaryExpression") && [
44887
+ const test = isNodeOfType(ancestor, "IfStatement") ? stripParenExpression(ancestor.test) : null;
44888
+ if (isNodeOfType(ancestor, "IfStatement") && test && isNodeOfType(test, "UnaryExpression") && test.operator === "!" && isSameRefCurrentAlias(test.argument, refSymbol, scopes) && ancestor.consequent === descendant && isProvablyTruthyInitializationValue(assignmentExpression.right, scopes) && refHasClosedFalsySentinelDomain(refSymbol, assignmentExpression.right, scopes) && !hasRepeatedExecutionAncestor(assignmentExpression, ancestor.consequent) && !hasRepeatedExecutionAncestor(ancestor, renderOwner) && hasNoPriorCoExecutableWrite(assignmentExpression, ancestor.consequent, refSymbol, scopes) && hasNoCompetingRefCurrentWrite(renderOwner, assignmentExpression, refSymbol, scopes) && refDoesNotEscape(renderOwner, refSymbol, scopes)) return true;
44889
+ if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(test, "BinaryExpression") && [
43946
44890
  "===",
43947
44891
  "==",
43948
44892
  "!==",
43949
44893
  "!="
43950
- ].includes(ancestor.test.operator)) {
43951
- const { left, right } = ancestor.test;
44894
+ ].includes(test.operator)) {
44895
+ const { left, right } = test;
43952
44896
  const comparesEmptySentinel = isSameRefCurrentAlias(left, refSymbol, scopes) && isEmptySentinel(right, scopes) || isSameRefCurrentAlias(right, refSymbol, scopes) && isEmptySentinel(left, scopes);
43953
- const guardedBranch = ancestor.test.operator === "===" || ancestor.test.operator === "==" ? ancestor.consequent : ancestor.alternate;
44897
+ const guardedBranch = test.operator === "===" || test.operator === "==" ? ancestor.consequent : ancestor.alternate;
43954
44898
  if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && hasNoPriorCoExecutableWrite(assignmentExpression, guardedBranch, refSymbol, scopes)) return true;
43955
44899
  }
43956
44900
  descendant = ancestor;
@@ -46585,6 +47529,185 @@ const noUnescapedEntities = defineRule({
46585
47529
  } })
46586
47530
  });
46587
47531
  //#endregion
47532
+ //#region src/plugin/utils/read-server-snapshot-boolean.ts
47533
+ const crossFileScopes = /* @__PURE__ */ new WeakMap();
47534
+ const getCrossFileScopes = (programNode) => {
47535
+ const cachedScopes = crossFileScopes.get(programNode);
47536
+ if (cachedScopes) return cachedScopes;
47537
+ const scopes = analyzeScopes(programNode);
47538
+ crossFileScopes.set(programNode, scopes);
47539
+ return scopes;
47540
+ };
47541
+ const symbolIsImmutable = (symbol) => (symbol.kind === "const" || symbol.kind === "function") && symbol.references.every((reference) => reference.flag === "read");
47542
+ const identifierAliasChainIsImmutable = (identifier, scopes, allowImportTerminal = false) => {
47543
+ if (!isNodeOfType(identifier, "Identifier")) return false;
47544
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
47545
+ let symbol = scopes.symbolFor(identifier);
47546
+ while (symbol) {
47547
+ if (symbol.kind === "import") return allowImportTerminal;
47548
+ if (visitedSymbolIds.has(symbol.id) || !symbolIsImmutable(symbol)) return false;
47549
+ visitedSymbolIds.add(symbol.id);
47550
+ if (symbol.kind !== "const" || !symbol.initializer) return symbol.kind === "function";
47551
+ const initializer = stripParenExpression(symbol.initializer);
47552
+ if (!isNodeOfType(initializer, "Identifier")) return true;
47553
+ symbol = scopes.symbolFor(initializer);
47554
+ }
47555
+ return false;
47556
+ };
47557
+ const getImportedHookBinding = (callee, scopes) => {
47558
+ if (!isNodeOfType(callee, "Identifier") || !identifierAliasChainIsImmutable(callee, scopes, true)) return null;
47559
+ const importedSymbol = resolveConstIdentifierAlias(callee, scopes);
47560
+ if (importedSymbol?.kind !== "import") return null;
47561
+ const importDeclaration = importedSymbol.declarationNode.parent;
47562
+ if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration")) return null;
47563
+ const source = importDeclaration.source.value;
47564
+ if (typeof source !== "string") return null;
47565
+ const exportedName = resolveImportedExportName(importedSymbol.declarationNode);
47566
+ return exportedName ? {
47567
+ exportedName,
47568
+ source
47569
+ } : null;
47570
+ };
47571
+ const functionBindingIsImmutable = (functionNode, scopes) => {
47572
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) {
47573
+ const symbol = scopes.symbolFor(functionNode.id);
47574
+ return Boolean(symbol && symbolIsImmutable(symbol));
47575
+ }
47576
+ const expressionRoot = findTransparentExpressionRoot(functionNode);
47577
+ const parentNode = expressionRoot.parent;
47578
+ if (isNodeOfType(parentNode, "ExportDefaultDeclaration")) return true;
47579
+ if (!isNodeOfType(parentNode, "VariableDeclarator") || parentNode.init !== expressionRoot || !isNodeOfType(parentNode.id, "Identifier")) return false;
47580
+ const symbol = scopes.symbolFor(parentNode.id);
47581
+ return Boolean(symbol && symbolIsImmutable(symbol));
47582
+ };
47583
+ const getExactFunctionResultExpression = (functionNode) => {
47584
+ if (!isFunctionLike$1(functionNode) || functionNode.async) return null;
47585
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.generator) return null;
47586
+ if (isNodeOfType(functionNode, "FunctionExpression") && functionNode.generator) return null;
47587
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return functionNode.body;
47588
+ const [returnStatement, additionalReturnStatement] = collectFunctionReturnStatements(functionNode);
47589
+ if (!returnStatement || additionalReturnStatement || !returnStatement.argument) return null;
47590
+ if ([...functionNode.body.body].pop() !== returnStatement) return null;
47591
+ return returnStatement.argument;
47592
+ };
47593
+ const functionHasNoParameters = (functionNode) => isFunctionLike$1(functionNode) && (functionNode.params?.length ?? 0) === 0;
47594
+ const readImmutableBooleanLiteral = (expression, scopes, visitedSymbolIds) => {
47595
+ const unwrappedExpression = stripParenExpression(expression);
47596
+ if (isNodeOfType(unwrappedExpression, "Literal") && typeof unwrappedExpression.value === "boolean") return unwrappedExpression.value;
47597
+ if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
47598
+ const symbol = scopes.symbolFor(unwrappedExpression);
47599
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || !symbolIsImmutable(symbol) || visitedSymbolIds.has(symbol.id)) return null;
47600
+ visitedSymbolIds.add(symbol.id);
47601
+ return readImmutableBooleanLiteral(symbol.initializer, scopes, visitedSymbolIds);
47602
+ };
47603
+ const readFunctionLiteralBoolean = (expression, scopes) => {
47604
+ const unwrappedExpression = stripParenExpression(expression);
47605
+ if (isNodeOfType(unwrappedExpression, "Identifier") && !identifierAliasChainIsImmutable(unwrappedExpression, scopes)) return null;
47606
+ const functionNode = resolveExactLocalFunction(unwrappedExpression, scopes);
47607
+ if (!functionNode) return null;
47608
+ const resultExpression = getExactFunctionResultExpression(functionNode);
47609
+ return resultExpression ? readImmutableBooleanLiteral(resultExpression, scopes, /* @__PURE__ */ new Set()) : null;
47610
+ };
47611
+ const readServerSnapshotBooleanInternal = (expression, scopes, visitedSymbolIds, visitedFunctionNodes, currentFilename) => {
47612
+ const unwrappedExpression = stripParenExpression(expression);
47613
+ if (isNodeOfType(unwrappedExpression, "Literal") && typeof unwrappedExpression.value === "boolean") return {
47614
+ hasUseSyncExternalStoreOrigin: false,
47615
+ value: unwrappedExpression.value
47616
+ };
47617
+ if (isNodeOfType(unwrappedExpression, "Identifier")) {
47618
+ const symbol = scopes.symbolFor(unwrappedExpression);
47619
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || !symbolIsImmutable(symbol) || visitedSymbolIds.has(symbol.id)) return null;
47620
+ visitedSymbolIds.add(symbol.id);
47621
+ return readServerSnapshotBooleanInternal(symbol.initializer, scopes, visitedSymbolIds, visitedFunctionNodes, currentFilename);
47622
+ }
47623
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
47624
+ const argumentResult = readServerSnapshotBooleanInternal(unwrappedExpression.argument, scopes, visitedSymbolIds, visitedFunctionNodes, currentFilename);
47625
+ return argumentResult ? {
47626
+ hasUseSyncExternalStoreOrigin: argumentResult.hasUseSyncExternalStoreOrigin,
47627
+ value: !argumentResult.value
47628
+ } : null;
47629
+ }
47630
+ if (isNodeOfType(unwrappedExpression, "LogicalExpression") && (unwrappedExpression.operator === "&&" || unwrappedExpression.operator === "||")) {
47631
+ const leftResult = readServerSnapshotBooleanInternal(unwrappedExpression.left, scopes, new Set(visitedSymbolIds), new Set(visitedFunctionNodes), currentFilename);
47632
+ if (leftResult && (unwrappedExpression.operator === "&&" && !leftResult.value || unwrappedExpression.operator === "||" && leftResult.value)) return leftResult;
47633
+ const rightResult = readServerSnapshotBooleanInternal(unwrappedExpression.right, scopes, new Set(visitedSymbolIds), new Set(visitedFunctionNodes), currentFilename);
47634
+ if (rightResult && (unwrappedExpression.operator === "&&" && !rightResult.value || unwrappedExpression.operator === "||" && rightResult.value)) return rightResult;
47635
+ return leftResult && rightResult ? {
47636
+ hasUseSyncExternalStoreOrigin: leftResult.hasUseSyncExternalStoreOrigin || rightResult.hasUseSyncExternalStoreOrigin,
47637
+ value: rightResult.value
47638
+ } : null;
47639
+ }
47640
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
47641
+ if (isReactApiCall(unwrappedExpression, "useSyncExternalStore", scopes, { resolveNamedAliases: true })) {
47642
+ const [, , serverSnapshotArgument] = unwrappedExpression.arguments ?? [];
47643
+ if (!serverSnapshotArgument || isNodeOfType(serverSnapshotArgument, "SpreadElement")) return null;
47644
+ const serverSnapshotValue = readFunctionLiteralBoolean(serverSnapshotArgument, scopes);
47645
+ return serverSnapshotValue === null ? null : {
47646
+ hasUseSyncExternalStoreOrigin: true,
47647
+ value: serverSnapshotValue
47648
+ };
47649
+ }
47650
+ if ((unwrappedExpression.arguments?.length ?? 0) !== 0) return null;
47651
+ const callee = stripParenExpression(unwrappedExpression.callee);
47652
+ const importedHookBinding = getImportedHookBinding(callee, scopes);
47653
+ if (importedHookBinding && currentFilename) {
47654
+ const resolvedHook = resolveCrossFileFunctionExportWithFilePath(path.resolve(currentFilename), importedHookBinding.source, importedHookBinding.exportedName);
47655
+ if (resolvedHook && !resolvedHook.filePath.split(path.sep).includes("node_modules")) {
47656
+ const resolvedScopes = getCrossFileScopes(resolvedHook.programNode);
47657
+ if (isReactHookName(componentOrHookDisplayNameForFunction(resolvedHook.functionNode) ?? (importedHookBinding.exportedName === "default" && isNodeOfType(callee, "Identifier") ? callee.name : importedHookBinding.exportedName)) && functionHasNoParameters(resolvedHook.functionNode) && functionBindingIsImmutable(resolvedHook.functionNode, resolvedScopes) && !visitedFunctionNodes.has(resolvedHook.functionNode)) {
47658
+ visitedFunctionNodes.add(resolvedHook.functionNode);
47659
+ const resultExpression = getExactFunctionResultExpression(resolvedHook.functionNode);
47660
+ if (resultExpression) return readServerSnapshotBooleanInternal(resultExpression, resolvedScopes, /* @__PURE__ */ new Set(), visitedFunctionNodes, resolvedHook.filePath);
47661
+ }
47662
+ }
47663
+ return null;
47664
+ }
47665
+ if (!isNodeOfType(callee, "Identifier") || !isReactHookName(callee.name) || !identifierAliasChainIsImmutable(callee, scopes)) return null;
47666
+ const hookFunction = resolveExactLocalFunction(callee, scopes);
47667
+ if (!hookFunction || !functionHasNoParameters(hookFunction) || visitedFunctionNodes.has(hookFunction)) return null;
47668
+ visitedFunctionNodes.add(hookFunction);
47669
+ const resultExpression = getExactFunctionResultExpression(hookFunction);
47670
+ return resultExpression ? readServerSnapshotBooleanInternal(resultExpression, scopes, visitedSymbolIds, visitedFunctionNodes, currentFilename) : null;
47671
+ };
47672
+ const readServerSnapshotBoolean = (expression, scopes, currentFilename) => {
47673
+ const result = readServerSnapshotBooleanInternal(expression, scopes, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), currentFilename);
47674
+ return result?.hasUseSyncExternalStoreOrigin ? result.value : null;
47675
+ };
47676
+ //#endregion
47677
+ //#region src/plugin/utils/is-after-falsy-server-snapshot-early-return.ts
47678
+ const isAfterFalsyServerSnapshotEarlyReturn = (node, componentOrHookNode, scopes, filename) => {
47679
+ const enclosingFunction = findEnclosingFunction$1(node);
47680
+ if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, scopes) || !isFunctionLike$1(enclosingFunction) || !isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
47681
+ let currentNode = node;
47682
+ while (currentNode !== enclosingFunction) {
47683
+ const parentNode = currentNode.parent;
47684
+ if (!parentNode) return false;
47685
+ if (isNodeOfType(parentNode, "BlockStatement")) for (const statement of parentNode.body) {
47686
+ if (statement === currentNode) break;
47687
+ if (!isNodeOfType(statement, "IfStatement")) continue;
47688
+ const serverResult = readServerSnapshotBoolean(statement.test, scopes, filename);
47689
+ if (serverResult === true && statementAlwaysExits(statement.consequent)) return true;
47690
+ if (serverResult === false && statement.alternate && statementAlwaysExits(statement.alternate)) return true;
47691
+ }
47692
+ currentNode = parentNode;
47693
+ }
47694
+ return false;
47695
+ };
47696
+ //#endregion
47697
+ //#region src/plugin/utils/is-gated-by-falsy-server-snapshot.ts
47698
+ const isGatedByFalsyServerSnapshot = (node, scopes, filename) => {
47699
+ let currentNode = node;
47700
+ let parentNode = node.parent;
47701
+ while (parentNode) {
47702
+ if (isNodeOfType(parentNode, "LogicalExpression") && parentNode.right === currentNode && (parentNode.operator === "&&" && readServerSnapshotBoolean(parentNode.left, scopes, filename) === false || parentNode.operator === "||" && readServerSnapshotBoolean(parentNode.left, scopes, filename) === true)) return true;
47703
+ if (isNodeOfType(parentNode, "ConditionalExpression") && (parentNode.consequent === currentNode && readServerSnapshotBoolean(parentNode.test, scopes, filename) === false || parentNode.alternate === currentNode && readServerSnapshotBoolean(parentNode.test, scopes, filename) === true)) return true;
47704
+ if (isNodeOfType(parentNode, "IfStatement") && (parentNode.consequent === currentNode && readServerSnapshotBoolean(parentNode.test, scopes, filename) === false || parentNode.alternate === currentNode && readServerSnapshotBoolean(parentNode.test, scopes, filename) === true)) return true;
47705
+ currentNode = parentNode;
47706
+ parentNode = parentNode.parent ?? null;
47707
+ }
47708
+ return false;
47709
+ };
47710
+ //#endregion
46588
47711
  //#region src/plugin/rules/performance/no-unguarded-browser-global-in-render-or-hook-init.ts
46589
47712
  const BROWSER_GLOBAL_NAMES = new Set([
46590
47713
  "window",
@@ -46691,7 +47814,9 @@ const noUnguardedBrowserGlobalInRenderOrHookInit = defineRule({
46691
47814
  if (fileIsEmailTemplate) return;
46692
47815
  if (isGeneratedImageRenderContext(context, findEnclosingJsxOpeningElement(node) ?? node)) return;
46693
47816
  if (isGatedByFalsyInitialState(node, context.scopes)) return;
47817
+ if (isGatedByFalsyServerSnapshot(node, context.scopes, context.filename)) return;
46694
47818
  if (isAfterClientOnlyEarlyReturn(node, componentOrHookNode, context.scopes)) return;
47819
+ if (isAfterFalsyServerSnapshotEarlyReturn(node, componentOrHookNode, context.scopes, context.filename)) return;
46695
47820
  if (isInsideAvailabilityGuard(node, browserGlobalName, context)) return;
46696
47821
  if (isAfterAvailabilityEarlyExit(node, componentOrHookNode, browserGlobalName, context)) return;
46697
47822
  reportedNodes.add(node);
@@ -50894,6 +52019,31 @@ const preferModuleScopePureFunction = defineRule({
50894
52019
  }
50895
52020
  });
50896
52021
  //#endregion
52022
+ //#region src/plugin/utils/get-require-call-source.ts
52023
+ const getRequireCallSource = (expression) => {
52024
+ const unwrappedExpression = stripParenExpression(expression);
52025
+ if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
52026
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
52027
+ if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
52028
+ const [firstArgument] = unwrappedExpression.arguments ?? [];
52029
+ if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
52030
+ return typeof firstArgument.value === "string" ? firstArgument.value : null;
52031
+ };
52032
+ //#endregion
52033
+ //#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
52034
+ const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
52035
+ const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
52036
+ const identifier = stripParenExpression(expression);
52037
+ if (!isNodeOfType(identifier, "Identifier")) return false;
52038
+ const symbol = resolveConstIdentifierAlias(identifier, scopes);
52039
+ if (!symbol) return false;
52040
+ if (symbol.kind === "import") {
52041
+ const importBinding = getImportBindingForName(identifier, symbol.name);
52042
+ return Boolean(importBinding && NODE_CRYPTO_MODULE_SOURCES.has(importBinding.source));
52043
+ }
52044
+ return Boolean(symbol.kind === "const" && symbol.initializer && NODE_CRYPTO_MODULE_SOURCES.has(getRequireCallSource(symbol.initializer) ?? ""));
52045
+ };
52046
+ //#endregion
50897
52047
  //#region src/plugin/rules/architecture/prefer-module-scope-static-value.ts
50898
52048
  const MUTATING_RECEIVER_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
50899
52049
  const isMutationContext = (referenceIdentifier) => {
@@ -51007,9 +52157,9 @@ const isImpureCall = (node, scopes) => {
51007
52157
  const callee = node.callee;
51008
52158
  if (isNodeOfType(callee, "Identifier")) return isImpureBareCallee(callee, scopes);
51009
52159
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return false;
51010
- if (!isNodeOfType(callee.object, "Identifier")) return false;
51011
52160
  if (!isNodeOfType(callee.property, "Identifier")) return false;
51012
- return Boolean(IMPURE_MEMBER_RECEIVERS.get(callee.object.name)?.has(callee.property.name));
52161
+ for (const [receiverName, receiverMethodNames] of IMPURE_MEMBER_RECEIVERS) if (receiverMethodNames.has(callee.property.name) && (isProvenGlobalNamespaceReference(callee.object, receiverName, scopes) || receiverName === "crypto" && isProvenNodeCryptoNamespaceReference(callee.object, scopes))) return true;
52162
+ return false;
51013
52163
  };
51014
52164
  const containsImpureExpression = (expression, scopes) => {
51015
52165
  let foundImpure = false;
@@ -55778,16 +56928,6 @@ const rnListCallbackPerRow = defineRule({
55778
56928
  }
55779
56929
  });
55780
56930
  //#endregion
55781
- //#region src/plugin/utils/get-require-call-source.ts
55782
- const getRequireCallSource = (expression) => {
55783
- if (isNodeOfType(expression, "MemberExpression")) return getRequireCallSource(expression.object);
55784
- if (!isNodeOfType(expression, "CallExpression")) return null;
55785
- if (!isNodeOfType(expression.callee, "Identifier") || expression.callee.name !== "require") return null;
55786
- const [firstArgument] = expression.arguments ?? [];
55787
- if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
55788
- return typeof firstArgument.value === "string" ? firstArgument.value : null;
55789
- };
55790
- //#endregion
55791
56931
  //#region src/plugin/utils/get-initializer-module-source.ts
55792
56932
  const getInitializerModuleSource = (contextNode, initializer) => {
55793
56933
  const requireSource = getRequireCallSource(initializer);
@@ -62566,9 +63706,35 @@ const serverDedupProps = defineRule({
62566
63706
  });
62567
63707
  //#endregion
62568
63708
  //#region src/plugin/rules/server/server-fetch-without-revalidate.ts
62569
- const isFetchCall = (node) => {
63709
+ const isGlobalThisFetchMember = (node, context) => {
63710
+ const memberExpression = stripParenExpression(node);
63711
+ if (!isNodeOfType(memberExpression, "MemberExpression")) return false;
63712
+ const receiver = stripParenExpression(memberExpression.object);
63713
+ return getStaticPropertyName(memberExpression) === "fetch" && isNodeOfType(receiver, "Identifier") && receiver.name === "globalThis" && context.scopes.isGlobalReference(receiver);
63714
+ };
63715
+ const isGlobalThisIdentifier = (node, context) => {
63716
+ const expression = stripParenExpression(node);
63717
+ return isNodeOfType(expression, "Identifier") && expression.name === "globalThis" && context.scopes.isGlobalReference(expression);
63718
+ };
63719
+ const isGlobalFetchDestructuringBinding = (symbolBinding, declaration, context) => isNodeOfType(declaration.id, "ObjectPattern") && Boolean(declaration.id.properties.some((property) => isNodeOfType(property, "Property") && property.value === symbolBinding && getStaticPropertyKeyName(property, { allowComputedString: true }) === "fetch") && declaration.init && isGlobalThisIdentifier(declaration.init, context));
63720
+ const isExactGlobalFetchValue = (node, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
63721
+ const expression = stripParenExpression(node);
63722
+ if (isGlobalThisFetchMember(expression, context)) return true;
63723
+ if (!isNodeOfType(expression, "Identifier")) return false;
63724
+ if (expression.name === "fetch" && context.scopes.isGlobalReference(expression)) return true;
63725
+ const symbol = context.scopes.symbolFor(expression);
63726
+ if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
63727
+ if (!isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
63728
+ if (isGlobalFetchDestructuringBinding(symbol.bindingIdentifier, symbol.declarationNode, context)) return true;
63729
+ if (symbol.declarationNode.id !== symbol.bindingIdentifier || !symbol.initializer) return false;
63730
+ visitedSymbolIds.add(symbol.id);
63731
+ return isExactGlobalFetchValue(symbol.initializer, context, visitedSymbolIds);
63732
+ };
63733
+ const isFetchCall = (node, context) => {
62570
63734
  if (!isNodeOfType(node, "CallExpression")) return false;
62571
- return isNodeOfType(node.callee, "Identifier") && node.callee.name === "fetch";
63735
+ const callee = stripParenExpression(node.callee);
63736
+ if (!isNodeOfType(callee, "Identifier") || callee.name !== "fetch") return false;
63737
+ return isExactGlobalFetchValue(callee, context);
62572
63738
  };
62573
63739
  const getPropertyKeyName$1 = (property) => {
62574
63740
  if (!isNodeOfType(property, "Property")) return null;
@@ -62614,7 +63780,7 @@ const serverFetchWithoutRevalidate = defineRule({
62614
63780
  },
62615
63781
  CallExpression(node) {
62616
63782
  if (!isServerSideFile) return;
62617
- if (!isFetchCall(node)) return;
63783
+ if (!isFetchCall(node, context)) return;
62618
63784
  if (isMutatingFetchCall(node)) return;
62619
63785
  const optionsArg = node.arguments?.[1];
62620
63786
  if (optionsArg) {
@@ -70096,6 +71262,7 @@ const CROSS_FILE_RULE_IDS = new Set([
70096
71262
  "exhaustive-deps",
70097
71263
  "no-barrel-import",
70098
71264
  "nextjs-missing-metadata",
71265
+ "nextjs-no-img-element",
70099
71266
  "nextjs-no-use-search-params-without-suspense",
70100
71267
  "no-dynamic-import-path",
70101
71268
  "no-full-lodash-import",
@@ -70327,12 +71494,12 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
70327
71494
  ]);
70328
71495
  /**
70329
71496
  * Cross-file rules whose dependency set CANNOT be soundly bounded — they are
70330
- * excluded from fingerprinting and re-lint every file on every scan. Empty
70331
- * today; a new cross-file rule must be added either here or to
71497
+ * excluded from fingerprinting and re-lint every file on every scan. A new
71498
+ * cross-file rule must be added either here or to
70332
71499
  * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
70333
71500
  * partition), forcing a conscious classification.
70334
71501
  */
70335
- const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["only-export-components"]);
71502
+ const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["nextjs-no-img-element", "only-export-components"]);
70336
71503
  /**
70337
71504
  * Runs the collectors for `ruleIds` over one file and returns every
70338
71505
  * filesystem probe they made — the file's cross-file dependency set.