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

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
@@ -1819,8 +1819,70 @@ const flattenJsxName$1 = (node) => {
1819
1819
  return null;
1820
1820
  };
1821
1821
  //#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);
1822
+ //#region src/plugin/utils/get-static-property-name.ts
1823
+ const getStaticPropertyName = (memberExpression) => {
1824
+ const property = memberExpression.property;
1825
+ if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
1826
+ if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
1827
+ if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
1828
+ return null;
1829
+ };
1830
+ //#endregion
1831
+ //#region src/plugin/utils/is-generated-image-renderer-call.ts
1832
+ const GENERATED_IMAGE_RENDERER_MODULES = [
1833
+ "next/og",
1834
+ "@vercel/og",
1835
+ "satori"
1836
+ ];
1837
+ const IMAGE_RESPONSE_MODULES = new Set(["next/og", "@vercel/og"]);
1838
+ const getImportDeclaration$1 = (node) => {
1839
+ let current = node.parent;
1840
+ while (current) {
1841
+ if (isNodeOfType(current, "ImportDeclaration")) return current;
1842
+ if (isNodeOfType(current, "Program")) return null;
1843
+ current = current.parent;
1844
+ }
1845
+ return null;
1846
+ };
1847
+ const getImportSource = (declaration) => typeof declaration.source.value === "string" ? declaration.source.value : null;
1848
+ const isNamedImport = (symbol, importedName, moduleSources) => {
1849
+ if (symbol.kind !== "import") return false;
1850
+ const declaration = symbol.declarationNode;
1851
+ if (!isNodeOfType(declaration, "ImportSpecifier")) return false;
1852
+ const importDeclaration = getImportDeclaration$1(declaration);
1853
+ const source = importDeclaration ? getImportSource(importDeclaration) : null;
1854
+ if (!source || !moduleSources.has(source)) return false;
1855
+ const imported = declaration.imported;
1856
+ return isNodeOfType(imported, "Identifier") && imported.name === importedName || isNodeOfType(imported, "Literal") && imported.value === importedName;
1857
+ };
1858
+ const isSatoriImport = (symbol) => {
1859
+ if (symbol.kind !== "import") return false;
1860
+ const declaration = symbol.declarationNode;
1861
+ const importDeclaration = getImportDeclaration$1(declaration);
1862
+ if (!importDeclaration || getImportSource(importDeclaration) !== "satori") return false;
1863
+ if (isNodeOfType(declaration, "ImportDefaultSpecifier")) return true;
1864
+ if (!isNodeOfType(declaration, "ImportSpecifier")) return false;
1865
+ const imported = declaration.imported;
1866
+ return isNodeOfType(imported, "Identifier") && imported.name === "satori" || isNodeOfType(imported, "Literal") && imported.value === "satori";
1867
+ };
1868
+ const isGeneratedImageRendererCall = (node, scopes) => {
1869
+ if (!isNodeOfType(node, "CallExpression") && !isNodeOfType(node, "NewExpression")) return false;
1870
+ const callee = stripParenExpression(node.callee);
1871
+ if (isNodeOfType(callee, "Identifier")) {
1872
+ const symbol = scopes.referenceFor(callee)?.resolvedSymbol ?? null;
1873
+ return Boolean(symbol && (isNamedImport(symbol, "ImageResponse", IMAGE_RESPONSE_MODULES) || isSatoriImport(symbol)));
1874
+ }
1875
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
1876
+ if (getStaticPropertyName(callee) !== "ImageResponse") return false;
1877
+ if (!isNodeOfType(callee.object, "Identifier")) return false;
1878
+ const symbol = scopes.referenceFor(callee.object)?.resolvedSymbol ?? null;
1879
+ if (!symbol || symbol.kind !== "import") return false;
1880
+ const declaration = symbol.declarationNode;
1881
+ if (!isNodeOfType(declaration, "ImportNamespaceSpecifier")) return false;
1882
+ const importDeclaration = getImportDeclaration$1(declaration);
1883
+ const source = importDeclaration ? getImportSource(importDeclaration) : null;
1884
+ return Boolean(source && IMAGE_RESPONSE_MODULES.has(source));
1885
+ };
1824
1886
  //#endregion
1825
1887
  //#region src/plugin/constants/nextjs.ts
1826
1888
  const NEXTJS_SOURCE_FILE_EXTENSION_GROUP = "(?:tsx?|jsx?|mts|mjs)";
@@ -1892,42 +1954,22 @@ const isNextjsMetadataImageRouteFilename = (rawFilename) => {
1892
1954
  const normalizeFilename = (filename) => filename.includes("\\") ? filename.replaceAll("\\", "/") : filename;
1893
1955
  //#endregion
1894
1956
  //#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
1957
  const generatedImageJsxCache = /* @__PURE__ */ new WeakMap();
1899
1958
  const isGeneratedImageRenderFilename = (rawFilename) => {
1900
1959
  if (!rawFilename) return false;
1901
1960
  return isNextjsMetadataImageRouteFilename(normalizeFilename(rawFilename));
1902
1961
  };
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
1962
  const isComponentIdentifierName = (name) => {
1921
1963
  const firstCharacter = name[0];
1922
1964
  return Boolean(firstCharacter && firstCharacter === firstCharacter.toUpperCase());
1923
1965
  };
1924
1966
  const isFunctionLike = (node) => Boolean(node && (isNodeOfType(node, "FunctionDeclaration") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "ArrowFunctionExpression")));
1925
- const markFunctionReturnJsx = (functionNode, programRoot, generatedImageJsxNodes, visitedComponentNames) => {
1967
+ const markFunctionReturnJsx = (functionNode, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames) => {
1926
1968
  if (!isFunctionLike(functionNode)) return;
1927
1969
  if (isNodeOfType(functionNode, "ArrowFunctionExpression")) {
1928
1970
  const body = stripParenExpression(functionNode.body);
1929
1971
  if (!isNodeOfType(body, "BlockStatement")) {
1930
- markGeneratedImageExpression(body, programRoot, generatedImageJsxNodes, visitedComponentNames);
1972
+ markGeneratedImageExpression(body, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
1931
1973
  return;
1932
1974
  }
1933
1975
  }
@@ -1937,7 +1979,7 @@ const markFunctionReturnJsx = (functionNode, programRoot, generatedImageJsxNodes
1937
1979
  if (descendantNode !== body && isFunctionLike(descendantNode)) return false;
1938
1980
  if (!isNodeOfType(descendantNode, "ReturnStatement")) return;
1939
1981
  if (!descendantNode.argument) return;
1940
- markGeneratedImageExpression(stripParenExpression(descendantNode.argument), programRoot, generatedImageJsxNodes, visitedComponentNames);
1982
+ markGeneratedImageExpression(stripParenExpression(descendantNode.argument), programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
1941
1983
  });
1942
1984
  };
1943
1985
  const hasNormalJsxUsage = (programRoot, componentName, generatedImageJsxNodes) => {
@@ -1952,7 +1994,7 @@ const hasNormalJsxUsage = (programRoot, componentName, generatedImageJsxNodes) =
1952
1994
  });
1953
1995
  return hasNormalUsage;
1954
1996
  };
1955
- const markComponentRenderJsx = (programRoot, openingElement, generatedImageJsxNodes, visitedComponentNames) => {
1997
+ const markComponentRenderJsx = (programRoot, scopes, openingElement, generatedImageJsxNodes, visitedComponentNames) => {
1956
1998
  const tagName = flattenJsxName$1(openingElement.name);
1957
1999
  if (!tagName || tagName.includes(".") || !isComponentIdentifierName(tagName)) return;
1958
2000
  if (visitedComponentNames.has(tagName)) return;
@@ -1960,70 +2002,70 @@ const markComponentRenderJsx = (programRoot, openingElement, generatedImageJsxNo
1960
2002
  const binding = findVariableInitializer(openingElement, tagName);
1961
2003
  if (!binding?.initializer) return;
1962
2004
  visitedComponentNames.add(tagName);
1963
- markGeneratedImageExpression(stripParenExpression(binding.initializer), programRoot, generatedImageJsxNodes, visitedComponentNames);
2005
+ markGeneratedImageExpression(stripParenExpression(binding.initializer), programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
1964
2006
  };
1965
- const isInsideGeneratedImageRendererArgument = (node) => {
2007
+ const isInsideGeneratedImageRendererArgument$1 = (node, scopes) => {
1966
2008
  let cursor = node.parent;
1967
2009
  while (cursor) {
1968
- if (isGeneratedImageRendererCall(cursor)) return true;
2010
+ if (isGeneratedImageRendererCall(cursor, scopes)) return true;
1969
2011
  cursor = cursor.parent ?? null;
1970
2012
  }
1971
2013
  return false;
1972
2014
  };
1973
- const hasNormalFunctionCallUsage = (programRoot, functionName) => {
2015
+ const hasNormalFunctionCallUsage = (programRoot, functionName, scopes) => {
1974
2016
  let hasNormalUsage = false;
1975
2017
  walkAst(programRoot, (descendantNode) => {
1976
2018
  if (hasNormalUsage) return false;
1977
2019
  if (!isNodeOfType(descendantNode, "CallExpression")) return;
1978
2020
  if (!isNodeOfType(descendantNode.callee, "Identifier")) return;
1979
2021
  if (descendantNode.callee.name !== functionName) return;
1980
- if (isInsideGeneratedImageRendererArgument(descendantNode)) return;
2022
+ if (isInsideGeneratedImageRendererArgument$1(descendantNode, scopes)) return;
1981
2023
  hasNormalUsage = true;
1982
2024
  return false;
1983
2025
  });
1984
2026
  return hasNormalUsage;
1985
2027
  };
1986
- const markJsxSubtree = (node, programRoot, generatedImageJsxNodes, visitedComponentNames) => {
2028
+ const markJsxSubtree = (node, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames) => {
1987
2029
  walkAst(node, (descendantNode) => {
1988
2030
  if (!isNodeOfType(descendantNode, "JSXOpeningElement")) return;
1989
2031
  generatedImageJsxNodes.add(descendantNode);
1990
- markComponentRenderJsx(programRoot, descendantNode, generatedImageJsxNodes, visitedComponentNames);
2032
+ markComponentRenderJsx(programRoot, scopes, descendantNode, generatedImageJsxNodes, visitedComponentNames);
1991
2033
  });
1992
2034
  };
1993
- const markGeneratedImageExpression = (expression, programRoot, generatedImageJsxNodes, visitedComponentNames) => {
2035
+ const markGeneratedImageExpression = (expression, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames) => {
1994
2036
  const unwrappedExpression = stripParenExpression(expression);
1995
2037
  if (isNodeOfType(unwrappedExpression, "JSXElement") || isNodeOfType(unwrappedExpression, "JSXFragment")) {
1996
- markJsxSubtree(unwrappedExpression, programRoot, generatedImageJsxNodes, visitedComponentNames);
2038
+ markJsxSubtree(unwrappedExpression, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
1997
2039
  return;
1998
2040
  }
1999
2041
  if (isFunctionLike(unwrappedExpression)) {
2000
- markFunctionReturnJsx(unwrappedExpression, programRoot, generatedImageJsxNodes, visitedComponentNames);
2042
+ markFunctionReturnJsx(unwrappedExpression, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2001
2043
  return;
2002
2044
  }
2003
2045
  if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
2004
- markGeneratedImageExpression(unwrappedExpression.consequent, programRoot, generatedImageJsxNodes, visitedComponentNames);
2005
- markGeneratedImageExpression(unwrappedExpression.alternate, programRoot, generatedImageJsxNodes, visitedComponentNames);
2046
+ markGeneratedImageExpression(unwrappedExpression.consequent, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2047
+ markGeneratedImageExpression(unwrappedExpression.alternate, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2006
2048
  return;
2007
2049
  }
2008
2050
  if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
2009
- markGeneratedImageExpression(unwrappedExpression.left, programRoot, generatedImageJsxNodes, visitedComponentNames);
2010
- markGeneratedImageExpression(unwrappedExpression.right, programRoot, generatedImageJsxNodes, visitedComponentNames);
2051
+ markGeneratedImageExpression(unwrappedExpression.left, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2052
+ markGeneratedImageExpression(unwrappedExpression.right, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2011
2053
  return;
2012
2054
  }
2013
2055
  if (isNodeOfType(unwrappedExpression, "CallExpression")) {
2014
2056
  const callee = unwrappedExpression.callee;
2015
2057
  if (isFunctionLike(callee)) {
2016
- markFunctionReturnJsx(callee, programRoot, generatedImageJsxNodes, visitedComponentNames);
2058
+ markFunctionReturnJsx(callee, programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2017
2059
  return;
2018
2060
  }
2019
2061
  if (!isNodeOfType(callee, "Identifier")) return;
2020
2062
  if (visitedComponentNames.has(callee.name)) return;
2021
2063
  if (hasNormalJsxUsage(programRoot, callee.name, generatedImageJsxNodes)) return;
2022
- if (hasNormalFunctionCallUsage(programRoot, callee.name)) return;
2064
+ if (hasNormalFunctionCallUsage(programRoot, callee.name, scopes)) return;
2023
2065
  const binding = findVariableInitializer(callee, callee.name);
2024
2066
  if (!binding?.initializer || !isFunctionLike(stripParenExpression(binding.initializer))) return;
2025
2067
  visitedComponentNames.add(callee.name);
2026
- markFunctionReturnJsx(stripParenExpression(binding.initializer), programRoot, generatedImageJsxNodes, visitedComponentNames);
2068
+ markFunctionReturnJsx(stripParenExpression(binding.initializer), programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2027
2069
  return;
2028
2070
  }
2029
2071
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
@@ -2031,16 +2073,17 @@ const markGeneratedImageExpression = (expression, programRoot, generatedImageJsx
2031
2073
  visitedComponentNames.add(unwrappedExpression.name);
2032
2074
  const binding = findVariableInitializer(unwrappedExpression, unwrappedExpression.name);
2033
2075
  if (!binding?.initializer) return;
2034
- markGeneratedImageExpression(stripParenExpression(binding.initializer), programRoot, generatedImageJsxNodes, visitedComponentNames);
2076
+ markGeneratedImageExpression(stripParenExpression(binding.initializer), programRoot, scopes, generatedImageJsxNodes, visitedComponentNames);
2035
2077
  }
2036
2078
  };
2037
- const collectGeneratedImageJsxNodes = (programRoot) => {
2079
+ const collectGeneratedImageJsxNodes = (programRoot, scopes) => {
2038
2080
  const cached = generatedImageJsxCache.get(programRoot);
2039
2081
  if (cached) return cached;
2040
2082
  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());
2083
+ if (hasImportFromModules(programRoot, GENERATED_IMAGE_RENDERER_MODULES)) walkAst(programRoot, (descendantNode) => {
2084
+ if (!isNodeOfType(descendantNode, "CallExpression") && !isNodeOfType(descendantNode, "NewExpression")) return;
2085
+ if (!isGeneratedImageRendererCall(descendantNode, scopes)) return;
2086
+ for (const argument of descendantNode.arguments) markGeneratedImageExpression(argument, programRoot, scopes, generatedImageJsxNodes, /* @__PURE__ */ new Set());
2044
2087
  });
2045
2088
  generatedImageJsxCache.set(programRoot, generatedImageJsxNodes);
2046
2089
  return generatedImageJsxNodes;
@@ -2050,7 +2093,7 @@ const isGeneratedImageRenderContext = (context, node) => {
2050
2093
  if (!node) return false;
2051
2094
  const programRoot = findProgramRoot(node);
2052
2095
  if (!programRoot) return false;
2053
- const generatedImageJsxNodes = collectGeneratedImageJsxNodes(programRoot);
2096
+ const generatedImageJsxNodes = collectGeneratedImageJsxNodes(programRoot, context.scopes);
2054
2097
  if (generatedImageJsxNodes.has(node)) return true;
2055
2098
  if (isNodeOfType(node, "JSXElement")) return generatedImageJsxNodes.has(node.openingElement);
2056
2099
  return false;
@@ -4496,15 +4539,6 @@ const getDestructuredBindingPropertyName = (bindingIdentifier) => {
4496
4539
  return getStaticPropertyKeyName(property, { allowComputedString: true });
4497
4540
  };
4498
4541
  //#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
4542
  //#region src/plugin/utils/has-static-property-write-before.ts
4509
4543
  const equivalentSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
4510
4544
  const potentiallyAliasedSymbolsByAnalysis = /* @__PURE__ */ new WeakMap();
@@ -8200,6 +8234,9 @@ const createMethodMutationAnalysis = (context) => {
8200
8234
  };
8201
8235
  };
8202
8236
  //#endregion
8237
+ //#region src/plugin/utils/is-member-property.ts
8238
+ const isMemberProperty = (node, propertyName) => Boolean(node && isNodeOfType(node, "MemberExpression") && isNodeOfType(node.property, "Identifier") && node.property.name === propertyName);
8239
+ //#endregion
8203
8240
  //#region src/plugin/utils/collect-function-return-statements.ts
8204
8241
  const collectFunctionReturnStatements = (functionNode) => {
8205
8242
  if (!isFunctionLike$1(functionNode) || !isNodeOfType(functionNode.body, "BlockStatement")) return [];
@@ -14364,14 +14401,14 @@ const DEPENDENCY_HOOK_NAMES = new Set([
14364
14401
  "useImperativeHandle",
14365
14402
  "useInsertionEffect"
14366
14403
  ]);
14367
- const crossFileScopes = /* @__PURE__ */ new WeakMap();
14404
+ const crossFileScopes$1 = /* @__PURE__ */ new WeakMap();
14368
14405
  const crossFileControlFlow = /* @__PURE__ */ new WeakMap();
14369
14406
  const forwardedFreshDependencyCache = /* @__PURE__ */ new WeakMap();
14370
- const getCrossFileScopes = (resolved) => {
14371
- const cached = crossFileScopes.get(resolved.programNode);
14407
+ const getCrossFileScopes$1 = (resolved) => {
14408
+ const cached = crossFileScopes$1.get(resolved.programNode);
14372
14409
  if (cached) return cached;
14373
14410
  const scopes = analyzeScopes(resolved.programNode);
14374
- crossFileScopes.set(resolved.programNode, scopes);
14411
+ crossFileScopes$1.set(resolved.programNode, scopes);
14375
14412
  return scopes;
14376
14413
  };
14377
14414
  const getCrossFileControlFlow = (resolved) => {
@@ -14406,7 +14443,7 @@ const isCustomHookFunction = (functionNode, fallbackName) => {
14406
14443
  const displayName = componentOrHookDisplayNameForFunction(functionNode) ?? fallbackName ?? "";
14407
14444
  return /^use[A-Z0-9]/.test(displayName);
14408
14445
  };
14409
- const getImportedHookBinding = (callee, scopes) => {
14446
+ const getImportedHookBinding$1 = (callee, scopes) => {
14410
14447
  if (!isNodeOfType(callee, "Identifier")) return null;
14411
14448
  const importedSymbol = resolveConstIdentifierAlias(callee, scopes);
14412
14449
  if (importedSymbol?.kind !== "import" || !importedSymbol.initializer) return null;
@@ -14422,7 +14459,7 @@ const getImportedHookBinding = (callee, scopes) => {
14422
14459
  };
14423
14460
  const resolveImportedHookFunction = (callee, scopes, currentFilename) => {
14424
14461
  if (!currentFilename) return null;
14425
- const importedBinding = getImportedHookBinding(callee, scopes);
14462
+ const importedBinding = getImportedHookBinding$1(callee, scopes);
14426
14463
  if (!importedBinding) return null;
14427
14464
  const resolved = resolveCrossFileFunctionExportWithFilePath(currentFilename, importedBinding.source, importedBinding.exportedName);
14428
14465
  if (!resolved || !isCustomHookFunction(resolved.functionNode, importedBinding.exportedName)) return null;
@@ -14431,7 +14468,7 @@ const resolveImportedHookFunction = (callee, scopes, currentFilename) => {
14431
14468
  filePath: resolved.filePath,
14432
14469
  functionNode: resolved.functionNode,
14433
14470
  programNode: resolved.programNode,
14434
- scopes: getCrossFileScopes(resolved)
14471
+ scopes: getCrossFileScopes$1(resolved)
14435
14472
  };
14436
14473
  };
14437
14474
  const dependencyIndexForReactHookReference = (expression, scopes, dependencyHookNames, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
@@ -14462,11 +14499,11 @@ const dependencyIndexForReactHookReference = (expression, scopes, dependencyHook
14462
14499
  };
14463
14500
  const getImportedReactDependencyIndex = (callExpression, scopes, currentFilename, dependencyHookNames) => {
14464
14501
  if (!currentFilename) return null;
14465
- const importedBinding = getImportedHookBinding(stripParenExpression(callExpression.callee), scopes);
14502
+ const importedBinding = getImportedHookBinding$1(stripParenExpression(callExpression.callee), scopes);
14466
14503
  if (!importedBinding) return null;
14467
14504
  const resolved = resolveCrossFileValueExportWithFilePath(currentFilename, importedBinding.source, importedBinding.exportedName);
14468
14505
  if (!resolved) return null;
14469
- return dependencyIndexForReactHookReference(resolved.exportedNode, getCrossFileScopes(resolved), dependencyHookNames);
14506
+ return dependencyIndexForReactHookReference(resolved.exportedNode, getCrossFileScopes$1(resolved), dependencyHookNames);
14470
14507
  };
14471
14508
  const resolveHookFunction = (callExpression, scopes, cfg, currentFilename) => {
14472
14509
  const callee = stripParenExpression(callExpression.callee);
@@ -19181,10 +19218,10 @@ const isUncacheableOptionsMergeUtility = (node) => {
19181
19218
  return (argument.properties ?? []).some((property) => isNodeOfType(property, "SpreadElement") && isNodeOfType(property.argument, "Identifier") && parameterNames.has(property.argument.name));
19182
19219
  });
19183
19220
  };
19184
- const isIntlNewExpression = (node) => {
19221
+ const isIntlNewExpression = (node, context) => {
19185
19222
  if (!isNodeOfType(node, "NewExpression")) return false;
19186
19223
  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;
19224
+ 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
19225
  return false;
19189
19226
  };
19190
19227
  const jsHoistIntl = defineRule({
@@ -19194,7 +19231,7 @@ const jsHoistIntl = defineRule({
19194
19231
  severity: "warn",
19195
19232
  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
19233
  create: (context) => ({ NewExpression(node) {
19197
- if (!isIntlNewExpression(node)) return;
19234
+ if (!isIntlNewExpression(node, context)) return;
19198
19235
  let cursor = node.parent ?? null;
19199
19236
  let inFunctionBody = false;
19200
19237
  while (cursor) {
@@ -19959,6 +19996,28 @@ const jsLengthCheckFirst = defineRule({
19959
19996
  } })
19960
19997
  });
19961
19998
  //#endregion
19999
+ //#region src/plugin/utils/is-proven-global-namespace-reference.ts
20000
+ const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
20001
+ const strippedExpression = stripParenExpression(expression);
20002
+ if (!isNodeOfType(strippedExpression, "Identifier")) return false;
20003
+ if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
20004
+ const symbol = scopes.symbolFor(strippedExpression);
20005
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
20006
+ visitedSymbolIds.add(symbol.id);
20007
+ return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
20008
+ };
20009
+ const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
20010
+ const strippedExpression = stripParenExpression(expression);
20011
+ if (isNodeOfType(strippedExpression, "Identifier")) {
20012
+ if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
20013
+ const symbol = scopes.symbolFor(strippedExpression);
20014
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
20015
+ visitedSymbolIds.add(symbol.id);
20016
+ return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
20017
+ }
20018
+ return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
20019
+ };
20020
+ //#endregion
19962
20021
  //#region src/plugin/rules/js-performance/js-min-max-loop.ts
19963
20022
  const builtinMutationByProgram = /* @__PURE__ */ new WeakMap();
19964
20023
  const RUNTIMELESS_SYMBOL_KINDS = new Set(["ts-interface", "ts-type-alias"]);
@@ -20015,26 +20074,6 @@ const isSafeFreshNumericArray = (arrayExpression) => {
20015
20074
  }
20016
20075
  return !(didFindPositiveZero && didFindNegativeZero);
20017
20076
  };
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
20077
  const resolvesToGlobalMethod = (expression, namespaceName, methodNames, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
20039
20078
  const strippedExpression = stripParenExpression(expression);
20040
20079
  if (isNodeOfType(strippedExpression, "Identifier")) {
@@ -20043,7 +20082,7 @@ const resolvesToGlobalMethod = (expression, namespaceName, methodNames, scopes,
20043
20082
  visitedSymbols.add(symbol.id);
20044
20083
  return resolvesToGlobalMethod(symbol.initializer, namespaceName, methodNames, scopes, visitedSymbols);
20045
20084
  }
20046
- return isNodeOfType(strippedExpression, "MemberExpression") && methodNames.has(getStaticPropertyName(strippedExpression) ?? "") && resolvesToGlobalNamespace(strippedExpression.object, namespaceName, scopes);
20085
+ return isNodeOfType(strippedExpression, "MemberExpression") && methodNames.has(getStaticPropertyName(strippedExpression) ?? "") && isProvenGlobalNamespaceReference(strippedExpression.object, namespaceName, scopes);
20047
20086
  };
20048
20087
  const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /* @__PURE__ */ new Set()) => {
20049
20088
  const strippedExpression = stripParenExpression(expression);
@@ -20055,7 +20094,7 @@ const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /*
20055
20094
  }
20056
20095
  if (isNodeOfType(strippedExpression, "MemberExpression")) {
20057
20096
  const propertyName = getStaticPropertyName(strippedExpression);
20058
- if (propertyName === "prototype") return resolvesToGlobalNamespace(strippedExpression.object, "Array", scopes);
20097
+ if (propertyName === "prototype") return isProvenGlobalNamespaceReference(strippedExpression.object, "Array", scopes);
20059
20098
  return propertyName === "__proto__" && isNodeOfType(stripParenExpression(strippedExpression.object), "ArrayExpression");
20060
20099
  }
20061
20100
  if (!isNodeOfType(strippedExpression, "CallExpression")) return false;
@@ -20066,23 +20105,23 @@ const resolvesToNativeArrayPrototype = (expression, scopes, visitedSymbols = /*
20066
20105
  const isGlobalNamespaceReplacementTarget = (target, namespaceName, scopes) => {
20067
20106
  const strippedTarget = stripParenExpression(target);
20068
20107
  if (isNodeOfType(strippedTarget, "Identifier")) return strippedTarget.name === namespaceName && scopes.isGlobalReference(strippedTarget);
20069
- return isNodeOfType(strippedTarget, "MemberExpression") && getStaticPropertyName(strippedTarget) === namespaceName && isGlobalObjectReference(strippedTarget.object, scopes);
20108
+ return isNodeOfType(strippedTarget, "MemberExpression") && getStaticPropertyName(strippedTarget) === namespaceName && isProvenGlobalObjectReference(strippedTarget.object, scopes);
20070
20109
  };
20071
20110
  const isUnsafeBuiltinMemberTarget = (target, targetFunction, scopes) => {
20072
20111
  const strippedTarget = stripParenExpression(target);
20073
20112
  if (!isNodeOfType(strippedTarget, "MemberExpression")) return false;
20074
20113
  const propertyName = getStaticPropertyName(strippedTarget);
20075
20114
  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");
20115
+ if (isProvenGlobalNamespaceReference(strippedTarget.object, "Math", scopes)) return propertyName === null || propertyName === targetFunction;
20116
+ return isProvenGlobalObjectReference(strippedTarget.object, scopes) && (propertyName === null || propertyName === "Math");
20078
20117
  };
20079
20118
  const isUnsafeBuiltinMutationApiCall = (callExpression, targetFunction, scopes) => {
20080
20119
  const target = callExpression.arguments[0];
20081
20120
  if (!target) return false;
20082
20121
  let propertyName = null;
20083
20122
  if (resolvesToNativeArrayPrototype(target, scopes)) propertyName = "sort";
20084
- else if (resolvesToGlobalNamespace(target, "Math", scopes)) propertyName = targetFunction;
20085
- else if (isGlobalObjectReference(target, scopes)) propertyName = "Math";
20123
+ else if (isProvenGlobalNamespaceReference(target, "Math", scopes)) propertyName = targetFunction;
20124
+ else if (isProvenGlobalObjectReference(target, scopes)) propertyName = "Math";
20086
20125
  if (!propertyName) return false;
20087
20126
  const canObjectExpressionSetProperty = (properties) => {
20088
20127
  if (!isNodeOfType(properties, "ObjectExpression")) return true;
@@ -20133,7 +20172,7 @@ const hasUnsafeMathBinding = (node, scopes) => {
20133
20172
  let scope = scopes.scopeFor(node);
20134
20173
  while (scope) {
20135
20174
  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));
20175
+ if (symbol && !RUNTIMELESS_SYMBOL_KINDS.has(symbol.kind)) return !(symbol.kind === "const" && symbol.initializer && isProvenGlobalNamespaceReference(symbol.initializer, "Math", scopes));
20137
20176
  scope = scope.parent;
20138
20177
  }
20139
20178
  return false;
@@ -26486,6 +26525,489 @@ const hasEmailTemplateImport = (programRoot) => {
26486
26525
  return found;
26487
26526
  };
26488
26527
  //#endregion
26528
+ //#region src/plugin/utils/build-generated-image-project-index.ts
26529
+ const GENERATED_IMAGE_SOURCE_FILE_PATTERN = /\.[cm]?[jt]sx?$/i;
26530
+ const GENERATED_IMAGE_DECLARATION_FILE_PATTERN = /\.d\.[cm]?[jt]s$/i;
26531
+ const GENERATED_IMAGE_MDX_FILE_PATTERN = /\.mdx$/i;
26532
+ const GENERATED_IMAGE_IGNORED_DIRECTORY_NAMES = new Set([
26533
+ ".angular",
26534
+ ".astro",
26535
+ ".cache",
26536
+ ".contentlayer",
26537
+ ".docusaurus",
26538
+ ".expo",
26539
+ ".git",
26540
+ ".next",
26541
+ ".nuxt",
26542
+ ".output",
26543
+ ".svelte-kit",
26544
+ ".turbo",
26545
+ ".vercel",
26546
+ "build",
26547
+ "coverage",
26548
+ "dist",
26549
+ "node_modules",
26550
+ "out",
26551
+ "storybook-static"
26552
+ ]);
26553
+ const generatedImageScopeCache = /* @__PURE__ */ new WeakMap();
26554
+ const getGeneratedImageModuleScopes = (programNode) => {
26555
+ const cachedScopes = generatedImageScopeCache.get(programNode);
26556
+ if (cachedScopes) return cachedScopes;
26557
+ const scopes = analyzeScopes(programNode);
26558
+ generatedImageScopeCache.set(programNode, scopes);
26559
+ return scopes;
26560
+ };
26561
+ const listProductionSourceFiles = (rootDirectory) => {
26562
+ const sourceFilePaths = [];
26563
+ const pendingDirectories = [rootDirectory];
26564
+ let hasOpaqueMdxConsumerSurface = false;
26565
+ while (pendingDirectories.length > 0) {
26566
+ const currentDirectory = pendingDirectories.pop();
26567
+ if (!currentDirectory) continue;
26568
+ let entries;
26569
+ try {
26570
+ entries = fs.readdirSync(currentDirectory, { withFileTypes: true });
26571
+ } catch {
26572
+ return null;
26573
+ }
26574
+ for (const entry of entries) {
26575
+ const absolutePath = path.join(currentDirectory, entry.name);
26576
+ const isIgnoredDirectoryName = GENERATED_IMAGE_IGNORED_DIRECTORY_NAMES.has(entry.name) || entry.name.startsWith(".") && entry.name !== ".dumi" && entry.name !== ".storybook";
26577
+ if (entry.isSymbolicLink() && isIgnoredDirectoryName) continue;
26578
+ if (entry.isSymbolicLink()) return null;
26579
+ if (entry.isDirectory()) {
26580
+ if (isIgnoredDirectoryName) continue;
26581
+ pendingDirectories.push(absolutePath);
26582
+ continue;
26583
+ }
26584
+ if (!entry.isFile() || isTestlikeFilename(absolutePath)) continue;
26585
+ if (GENERATED_IMAGE_MDX_FILE_PATTERN.test(entry.name)) {
26586
+ hasOpaqueMdxConsumerSurface = true;
26587
+ continue;
26588
+ }
26589
+ if (!GENERATED_IMAGE_SOURCE_FILE_PATTERN.test(entry.name)) continue;
26590
+ if (GENERATED_IMAGE_DECLARATION_FILE_PATTERN.test(entry.name)) continue;
26591
+ sourceFilePaths.push(normalizeFilename(absolutePath));
26592
+ }
26593
+ }
26594
+ return {
26595
+ sourceFilePaths,
26596
+ hasOpaqueMdxConsumerSurface
26597
+ };
26598
+ };
26599
+ const getRuntimeModuleSource = (node) => {
26600
+ if (isNodeOfType(node, "ImportDeclaration")) {
26601
+ if (isTypeOnlyImport(node)) return null;
26602
+ return typeof node.source.value === "string" ? node.source.value : null;
26603
+ }
26604
+ if (isNodeOfType(node, "ExportNamedDeclaration")) {
26605
+ if (node.exportKind === "type" || isEverySpecifierInlineType(node.specifiers, "ExportSpecifier", "exportKind")) return null;
26606
+ return node.source && typeof node.source.value === "string" ? node.source.value : null;
26607
+ }
26608
+ if (isNodeOfType(node, "ExportAllDeclaration")) {
26609
+ if (node.exportKind === "type") return null;
26610
+ return typeof node.source.value === "string" ? node.source.value : null;
26611
+ }
26612
+ if (isNodeOfType(node, "ImportExpression")) return isNodeOfType(node.source, "Literal") && typeof node.source.value === "string" ? node.source.value : null;
26613
+ if (isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "require" && node.arguments.length === 1) {
26614
+ const source = node.arguments[0];
26615
+ return source && isNodeOfType(source, "Literal") && typeof source.value === "string" ? source.value : null;
26616
+ }
26617
+ return null;
26618
+ };
26619
+ const indexModuleSources = (module, consumerModulesByFilePath, resolvedSourcePathByNode, unresolvedRuntimeSources) => {
26620
+ walkAst(module.programNode, (node) => {
26621
+ const source = getRuntimeModuleSource(node);
26622
+ if (!source) return;
26623
+ const resolvedSourcePath = resolveModulePath(module.filePath, source);
26624
+ if (!resolvedSourcePath) {
26625
+ unresolvedRuntimeSources.add(source);
26626
+ return;
26627
+ }
26628
+ const normalizedSourcePath = normalizeFilename(resolvedSourcePath);
26629
+ resolvedSourcePathByNode.set(node, normalizedSourcePath);
26630
+ const consumerModules = consumerModulesByFilePath.get(normalizedSourcePath) ?? /* @__PURE__ */ new Set();
26631
+ consumerModules.add(module);
26632
+ consumerModulesByFilePath.set(normalizedSourcePath, consumerModules);
26633
+ });
26634
+ };
26635
+ const buildGeneratedImageProjectIndex = (rootDirectory, currentFilePath, currentProgramNode, currentScopes) => {
26636
+ const productionSourceFiles = listProductionSourceFiles(rootDirectory);
26637
+ if (!productionSourceFiles) return null;
26638
+ const modulesByFilePath = /* @__PURE__ */ new Map();
26639
+ for (const filePath of productionSourceFiles.sourceFilePaths) {
26640
+ if (filePath === currentFilePath) {
26641
+ modulesByFilePath.set(filePath, {
26642
+ filePath,
26643
+ programNode: currentProgramNode,
26644
+ scopes: currentScopes
26645
+ });
26646
+ continue;
26647
+ }
26648
+ const parsedProgram = parseSourceFile(filePath);
26649
+ if (!parsedProgram || !isNodeOfType(parsedProgram, "Program")) return null;
26650
+ modulesByFilePath.set(filePath, {
26651
+ filePath,
26652
+ programNode: parsedProgram,
26653
+ scopes: getGeneratedImageModuleScopes(parsedProgram)
26654
+ });
26655
+ }
26656
+ const consumerModulesByFilePath = /* @__PURE__ */ new Map();
26657
+ const resolvedSourcePathByNode = /* @__PURE__ */ new WeakMap();
26658
+ const unresolvedRuntimeSources = /* @__PURE__ */ new Set();
26659
+ for (const module of modulesByFilePath.values()) indexModuleSources(module, consumerModulesByFilePath, resolvedSourcePathByNode, unresolvedRuntimeSources);
26660
+ return {
26661
+ modulesByFilePath,
26662
+ consumerModulesByFilePath,
26663
+ resolvedSourcePathByNode,
26664
+ unresolvedRuntimeSources,
26665
+ hasOpaqueMdxConsumerSurface: productionSourceFiles.hasOpaqueMdxConsumerSurface
26666
+ };
26667
+ };
26668
+ //#endregion
26669
+ //#region src/plugin/utils/read-nearest-package-manifest.ts
26670
+ const cachedPackageDirectoryByFilename = /* @__PURE__ */ new Map();
26671
+ const cachedManifestByPackageDirectory = /* @__PURE__ */ new Map();
26672
+ const resetManifestCaches = () => {
26673
+ cachedPackageDirectoryByFilename.clear();
26674
+ cachedManifestByPackageDirectory.clear();
26675
+ };
26676
+ const findNearestPackageDirectory = (filename) => {
26677
+ if (!filename) return null;
26678
+ const fromCache = cachedPackageDirectoryByFilename.get(filename);
26679
+ if (fromCache !== void 0) {
26680
+ if (isProbeRecorderActive()) {
26681
+ let probedDirectory = path.dirname(filename);
26682
+ while (true) {
26683
+ recordExistenceProbe(path.join(probedDirectory, "package.json"));
26684
+ if (probedDirectory === fromCache) break;
26685
+ const parentDirectory = path.dirname(probedDirectory);
26686
+ if (parentDirectory === probedDirectory) break;
26687
+ probedDirectory = parentDirectory;
26688
+ }
26689
+ }
26690
+ return fromCache;
26691
+ }
26692
+ let currentDirectory = path.dirname(filename);
26693
+ while (true) {
26694
+ const candidatePackageJsonPath = path.join(currentDirectory, "package.json");
26695
+ recordExistenceProbe(candidatePackageJsonPath);
26696
+ let hasPackageJson = false;
26697
+ try {
26698
+ hasPackageJson = fs.statSync(candidatePackageJsonPath).isFile();
26699
+ } catch {
26700
+ hasPackageJson = false;
26701
+ }
26702
+ if (hasPackageJson) {
26703
+ cachedPackageDirectoryByFilename.set(filename, currentDirectory);
26704
+ return currentDirectory;
26705
+ }
26706
+ const parentDirectory = path.dirname(currentDirectory);
26707
+ if (parentDirectory === currentDirectory) {
26708
+ cachedPackageDirectoryByFilename.set(filename, null);
26709
+ return null;
26710
+ }
26711
+ currentDirectory = parentDirectory;
26712
+ }
26713
+ };
26714
+ const readNearestPackageManifest = (filename) => {
26715
+ const packageDirectory = findNearestPackageDirectory(filename);
26716
+ if (!packageDirectory) return null;
26717
+ return readPackageManifest(packageDirectory);
26718
+ };
26719
+ const readPackageManifest = (packageDirectory) => {
26720
+ const packageJsonPath = path.join(packageDirectory, "package.json");
26721
+ recordContentProbe(packageJsonPath);
26722
+ const cached = cachedManifestByPackageDirectory.get(packageDirectory);
26723
+ if (cached !== void 0) return cached;
26724
+ let manifest = null;
26725
+ try {
26726
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
26727
+ if (typeof parsed === "object" && parsed !== null) manifest = parsed;
26728
+ } catch {
26729
+ manifest = null;
26730
+ }
26731
+ cachedManifestByPackageDirectory.set(packageDirectory, manifest);
26732
+ return manifest;
26733
+ };
26734
+ //#endregion
26735
+ //#region src/plugin/utils/is-exported-jsx-owned-by-generated-image-renderers.ts
26736
+ const getExportedSpecifierName = (specifier) => {
26737
+ const exported = specifier.exported;
26738
+ if (isNodeOfType(exported, "Identifier")) return exported.name;
26739
+ return isNodeOfType(exported, "Literal") && typeof exported.value === "string" ? exported.value : null;
26740
+ };
26741
+ const getImportedSpecifierName = (specifier) => {
26742
+ const local = specifier.local;
26743
+ if (isNodeOfType(local, "Identifier")) return local.name;
26744
+ return isNodeOfType(local, "Literal") && typeof local.value === "string" ? local.value : null;
26745
+ };
26746
+ const getImportSpecifierName = (specifier) => {
26747
+ if (isNodeOfType(specifier, "ImportDefaultSpecifier")) return "default";
26748
+ if (!isNodeOfType(specifier, "ImportSpecifier")) return null;
26749
+ const imported = specifier.imported;
26750
+ if (isNodeOfType(imported, "Identifier")) return imported.name;
26751
+ return isNodeOfType(imported, "Literal") && typeof imported.value === "string" ? imported.value : null;
26752
+ };
26753
+ const getDirectFunctionBindingIdentifier = (functionNode) => {
26754
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
26755
+ const functionValueRoot = findTransparentExpressionRoot(functionNode);
26756
+ const parent = functionValueRoot.parent;
26757
+ return isNodeOfType(parent, "VariableDeclarator") && parent.init === functionValueRoot && isNodeOfType(parent.id, "Identifier") ? parent.id : null;
26758
+ };
26759
+ const getExportNamesForFunction = (programNode, functionNode) => {
26760
+ const functionValueRoot = findTransparentExpressionRoot(functionNode);
26761
+ const bindingName = getDirectFunctionBindingIdentifier(functionNode)?.name ?? null;
26762
+ const exportedNames = /* @__PURE__ */ new Set();
26763
+ for (const statement of programNode.body) {
26764
+ if (isNodeOfType(statement, "ExportDefaultDeclaration")) {
26765
+ if (statement.declaration === functionValueRoot || bindingName && isNodeOfType(statement.declaration, "Identifier") && statement.declaration.name === bindingName) exportedNames.add("default");
26766
+ continue;
26767
+ }
26768
+ if (!isNodeOfType(statement, "ExportNamedDeclaration")) continue;
26769
+ const declaration = statement.declaration;
26770
+ if (declaration === functionValueRoot && bindingName) exportedNames.add(bindingName);
26771
+ if (declaration && isNodeOfType(declaration, "VariableDeclaration")) {
26772
+ for (const declarator of declaration.declarations) if (declarator.init === functionValueRoot && isNodeOfType(declarator.id, "Identifier")) exportedNames.add(declarator.id.name);
26773
+ }
26774
+ if (!bindingName || statement.source) continue;
26775
+ for (const specifier of statement.specifiers) {
26776
+ if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
26777
+ if (getImportedSpecifierName(specifier) !== bindingName) continue;
26778
+ const exportedName = getExportedSpecifierName(specifier);
26779
+ if (exportedName) exportedNames.add(exportedName);
26780
+ }
26781
+ }
26782
+ return [...exportedNames];
26783
+ };
26784
+ const isTransparentGeneratedImageValueFlow = (expression, target) => {
26785
+ let current = findTransparentExpressionRoot(expression);
26786
+ while (current !== target) {
26787
+ const parent = current.parent;
26788
+ if (!parent) return false;
26789
+ 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;
26790
+ current = findTransparentExpressionRoot(parent);
26791
+ }
26792
+ return true;
26793
+ };
26794
+ const isInsideGeneratedImageRendererArgument = (expression, scopes) => {
26795
+ let cursor = expression;
26796
+ while (cursor?.parent) {
26797
+ const parent = cursor.parent;
26798
+ if (isFunctionLike$1(parent)) return false;
26799
+ if (isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression")) {
26800
+ if (parent.arguments[0] && isTransparentGeneratedImageValueFlow(expression, parent.arguments[0]) && isGeneratedImageRendererCall(parent, scopes)) return true;
26801
+ }
26802
+ cursor = parent;
26803
+ }
26804
+ return false;
26805
+ };
26806
+ const getInvokedExpression = (identifier) => {
26807
+ const referenceExpression = findTransparentExpressionRoot(identifier);
26808
+ const parent = referenceExpression.parent;
26809
+ if (isNodeOfType(parent, "CallExpression") && parent.callee === referenceExpression) return parent;
26810
+ if (isNodeOfType(parent, "TaggedTemplateExpression") && parent.tag === referenceExpression) return parent;
26811
+ if ((isNodeOfType(parent, "JSXOpeningElement") || isNodeOfType(parent, "JSXClosingElement")) && parent.name === identifier) {
26812
+ const element = parent.parent;
26813
+ return isNodeOfType(element, "JSXElement") ? element : null;
26814
+ }
26815
+ return null;
26816
+ };
26817
+ const getForwardingFunction = (expression) => {
26818
+ const enclosingFunction = findEnclosingFunction$1(expression);
26819
+ if (!enclosingFunction) return null;
26820
+ if (isNodeOfType(enclosingFunction, "ArrowFunctionExpression") && !isNodeOfType(enclosingFunction.body, "BlockStatement") && isTransparentGeneratedImageValueFlow(expression, enclosingFunction.body)) return enclosingFunction;
26821
+ let cursor = expression.parent;
26822
+ while (cursor && cursor !== enclosingFunction) {
26823
+ if (isFunctionLike$1(cursor)) return null;
26824
+ if (isNodeOfType(cursor, "ReturnStatement") && cursor.argument && isTransparentGeneratedImageValueFlow(expression, cursor.argument)) return enclosingFunction;
26825
+ cursor = cursor.parent;
26826
+ }
26827
+ return null;
26828
+ };
26829
+ const enqueueExport = (state, filePath, exportedName) => {
26830
+ state.pendingExports.push({
26831
+ filePath: normalizeFilename(filePath),
26832
+ exportedName
26833
+ });
26834
+ };
26835
+ const classifyInvokedExpression = (module, expression, state) => {
26836
+ if (isInsideGeneratedImageRendererArgument(expression, module.scopes)) {
26837
+ state.didReachRenderer = true;
26838
+ return true;
26839
+ }
26840
+ const forwardingFunction = getForwardingFunction(expression);
26841
+ if (!forwardingFunction) return false;
26842
+ const exportedNames = getExportNamesForFunction(module.programNode, forwardingFunction);
26843
+ if (exportedNames.length === 0) return false;
26844
+ for (const exportedName of exportedNames) enqueueExport(state, module.filePath, exportedName);
26845
+ return true;
26846
+ };
26847
+ const classifySymbolReferences = (module, symbol, state, visitedSymbolIds) => {
26848
+ if (visitedSymbolIds.has(symbol.id)) return true;
26849
+ visitedSymbolIds.add(symbol.id);
26850
+ for (const reference of symbol.references) {
26851
+ if (reference.flag !== "read") return false;
26852
+ state.currentExportWasUsed = true;
26853
+ const identifier = reference.identifier;
26854
+ const invokedExpression = getInvokedExpression(identifier);
26855
+ if (invokedExpression) {
26856
+ if (!classifyInvokedExpression(module, invokedExpression, state)) return false;
26857
+ continue;
26858
+ }
26859
+ const parent = identifier.parent;
26860
+ if (isNodeOfType(parent, "ExportSpecifier") && parent.local === identifier) {
26861
+ const exportedName = getExportedSpecifierName(parent);
26862
+ if (!exportedName) return false;
26863
+ enqueueExport(state, module.filePath, exportedName);
26864
+ continue;
26865
+ }
26866
+ if (isNodeOfType(parent, "ExportDefaultDeclaration")) {
26867
+ enqueueExport(state, module.filePath, "default");
26868
+ continue;
26869
+ }
26870
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === identifier && isNodeOfType(parent.id, "Identifier") && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const") {
26871
+ const aliasSymbol = module.scopes.symbolFor(parent.id);
26872
+ if (!aliasSymbol || !classifySymbolReferences(module, aliasSymbol, state, visitedSymbolIds)) return false;
26873
+ continue;
26874
+ }
26875
+ return false;
26876
+ }
26877
+ return true;
26878
+ };
26879
+ const classifyNamespaceImportReferences = (module, symbol, exportedName, state) => {
26880
+ for (const reference of symbol.references) {
26881
+ if (reference.flag !== "read") return false;
26882
+ const identifier = reference.identifier;
26883
+ const parent = identifier.parent;
26884
+ if (!isNodeOfType(parent, "MemberExpression") || parent.object !== identifier) return false;
26885
+ const propertyName = getStaticPropertyName(parent);
26886
+ if (propertyName === null) return false;
26887
+ if (propertyName !== exportedName) continue;
26888
+ state.currentExportWasUsed = true;
26889
+ const invokedExpression = getInvokedExpression(parent);
26890
+ if (!invokedExpression || !classifyInvokedExpression(module, invokedExpression, state)) return false;
26891
+ }
26892
+ return true;
26893
+ };
26894
+ const classifyImportsFromExport = (module, exportIdentity, state) => {
26895
+ for (const statement of module.programNode.body) {
26896
+ if (isNodeOfType(statement, "ImportDeclaration")) {
26897
+ if (state.projectIndex.resolvedSourcePathByNode.get(statement) !== exportIdentity.filePath) continue;
26898
+ if (statement.importKind === "type") continue;
26899
+ for (const specifier of statement.specifiers) {
26900
+ if (isNodeOfType(specifier, "ImportSpecifier") && specifier.importKind === "type") continue;
26901
+ if (isNodeOfType(specifier, "ImportNamespaceSpecifier")) {
26902
+ const namespaceSymbol = module.scopes.symbolFor(specifier.local);
26903
+ if (!namespaceSymbol || !classifyNamespaceImportReferences(module, namespaceSymbol, exportIdentity.exportedName, state)) return false;
26904
+ continue;
26905
+ }
26906
+ if (getImportSpecifierName(specifier) !== exportIdentity.exportedName) continue;
26907
+ const symbol = module.scopes.symbolFor(specifier.local);
26908
+ if (!symbol || !classifySymbolReferences(module, symbol, state, /* @__PURE__ */ new Set())) return false;
26909
+ }
26910
+ continue;
26911
+ }
26912
+ if ((isNodeOfType(statement, "ExportNamedDeclaration") || isNodeOfType(statement, "ExportAllDeclaration")) && statement.source && state.projectIndex.resolvedSourcePathByNode.get(statement) === exportIdentity.filePath) {
26913
+ if (isNodeOfType(statement, "ExportAllDeclaration")) {
26914
+ if (statement.exported) return false;
26915
+ state.currentExportWasUsed = true;
26916
+ enqueueExport(state, module.filePath, exportIdentity.exportedName);
26917
+ continue;
26918
+ }
26919
+ for (const specifier of statement.specifiers) {
26920
+ if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
26921
+ if (getImportedSpecifierName(specifier) !== exportIdentity.exportedName) continue;
26922
+ const exportedName = getExportedSpecifierName(specifier);
26923
+ if (!exportedName) return false;
26924
+ state.currentExportWasUsed = true;
26925
+ enqueueExport(state, module.filePath, exportedName);
26926
+ }
26927
+ }
26928
+ }
26929
+ return true;
26930
+ };
26931
+ const hasOpaqueDynamicImportOfExport = (module, exportIdentity, projectIndex) => {
26932
+ let isOpaque = false;
26933
+ walkAst(module.programNode, (node) => {
26934
+ if (isOpaque) return false;
26935
+ if (isNodeOfType(node, "ImportExpression")) {
26936
+ if (projectIndex.resolvedSourcePathByNode.get(node) === exportIdentity.filePath) {
26937
+ isOpaque = true;
26938
+ return false;
26939
+ }
26940
+ }
26941
+ if (isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "require" && node.arguments.length === 1) {
26942
+ if (projectIndex.resolvedSourcePathByNode.get(node) === exportIdentity.filePath) {
26943
+ isOpaque = true;
26944
+ return false;
26945
+ }
26946
+ }
26947
+ });
26948
+ return isOpaque;
26949
+ };
26950
+ const classifyLocalExportReferences = (module, exportIdentity, state) => {
26951
+ const exportedValue = findExportedValue(module.programNode, exportIdentity.exportedName);
26952
+ if (!exportedValue || !isFunctionLike$1(exportedValue)) return true;
26953
+ const bindingIdentifier = getDirectFunctionBindingIdentifier(exportedValue);
26954
+ if (!bindingIdentifier) return true;
26955
+ const symbol = module.scopes.symbolFor(bindingIdentifier);
26956
+ return symbol ? classifySymbolReferences(module, symbol, state, /* @__PURE__ */ new Set()) : false;
26957
+ };
26958
+ const hasOpaqueWorkspacePackageConsumer = (projectIndex, exportIdentity) => {
26959
+ const packageName = readNearestPackageManifest(exportIdentity.filePath)?.name;
26960
+ if (typeof packageName !== "string" || packageName.length === 0) return false;
26961
+ for (const unresolvedSource of projectIndex.unresolvedRuntimeSources) if (unresolvedSource === packageName || unresolvedSource.startsWith(`${packageName}/`)) return true;
26962
+ return false;
26963
+ };
26964
+ const createExportedJsxGeneratedImageOwnershipAnalyzer = (context) => {
26965
+ const filename = context.filename ? normalizeFilename(context.filename) : "";
26966
+ const rootDirectorySetting = getReactDoctorStringSetting(context.settings, "rootDirectory");
26967
+ const rootDirectory = rootDirectorySetting ? normalizeFilename(rootDirectorySetting).replace(/\/$/, "") : "";
26968
+ const isFileInsideRoot = Boolean(filename && rootDirectory) && (filename === rootDirectory || filename.startsWith(`${rootDirectory}/`));
26969
+ let projectIndex;
26970
+ return (jsxNode) => {
26971
+ if (!isFileInsideRoot) return false;
26972
+ const programNode = findProgramRoot(jsxNode);
26973
+ const enclosingFunction = findEnclosingFunction$1(jsxNode);
26974
+ if (!programNode || !enclosingFunction) return false;
26975
+ const initialExportNames = getExportNamesForFunction(programNode, enclosingFunction);
26976
+ if (initialExportNames.length === 0) return false;
26977
+ if (projectIndex === void 0) projectIndex = buildGeneratedImageProjectIndex(rootDirectory, filename, programNode, context.scopes);
26978
+ if (!projectIndex || projectIndex.hasOpaqueMdxConsumerSurface) return false;
26979
+ const state = {
26980
+ projectIndex,
26981
+ pendingExports: initialExportNames.map((exportedName) => ({
26982
+ filePath: filename,
26983
+ exportedName
26984
+ })),
26985
+ visitedExportKeys: /* @__PURE__ */ new Set(),
26986
+ currentExportWasUsed: false,
26987
+ didReachRenderer: false
26988
+ };
26989
+ while (state.pendingExports.length > 0) {
26990
+ const exportIdentity = state.pendingExports.pop();
26991
+ if (!exportIdentity) continue;
26992
+ const exportKey = `${exportIdentity.filePath}\0${exportIdentity.exportedName}`;
26993
+ if (state.visitedExportKeys.has(exportKey)) continue;
26994
+ state.visitedExportKeys.add(exportKey);
26995
+ state.currentExportWasUsed = false;
26996
+ if (hasOpaqueWorkspacePackageConsumer(projectIndex, exportIdentity)) return false;
26997
+ const ownerModule = projectIndex.modulesByFilePath.get(exportIdentity.filePath);
26998
+ if (!ownerModule || !classifyLocalExportReferences(ownerModule, exportIdentity, state)) return false;
26999
+ const consumerModules = projectIndex.consumerModulesByFilePath.get(exportIdentity.filePath) ?? [];
27000
+ for (const module of consumerModules) {
27001
+ if (module.filePath === exportIdentity.filePath) continue;
27002
+ if (hasOpaqueDynamicImportOfExport(module, exportIdentity, projectIndex)) return false;
27003
+ if (!classifyImportsFromExport(module, exportIdentity, state)) return false;
27004
+ }
27005
+ if (!state.currentExportWasUsed) return false;
27006
+ }
27007
+ return state.didReachRenderer;
27008
+ };
27009
+ };
27010
+ //#endregion
26489
27011
  //#region src/plugin/rules/nextjs/nextjs-no-img-element.ts
26490
27012
  const NON_OPTIMIZABLE_SRC_PREFIX_PATTERN = /^\s*(data:|blob:)/i;
26491
27013
  const GENERATED_URL_NAME_PATTERN = /(data|object|blob)_?url/i;
@@ -26588,9 +27110,20 @@ const nextjsNoImgElement = defineRule({
26588
27110
  recommendation: "Use `next/image` so users get optimized formats, responsive srcsets, and lazy loading instead of oversized image downloads.",
26589
27111
  create: (context) => {
26590
27112
  if (isGeneratedImageRenderContext(context)) return {};
27113
+ const generatedImageOwnershipByFunction = /* @__PURE__ */ new WeakMap();
27114
+ const isExportedJsxGeneratedImageOwned = createExportedJsxGeneratedImageOwnershipAnalyzer(context);
26591
27115
  return { JSXOpeningElement(node) {
26592
27116
  if (resolveJsxElementType(node) !== "img") return;
26593
27117
  if (isGeneratedImageRenderContext(context, node)) return;
27118
+ const enclosingFunction = findEnclosingFunction$1(node);
27119
+ if (enclosingFunction) {
27120
+ let isGeneratedImageOwned = generatedImageOwnershipByFunction.get(enclosingFunction);
27121
+ if (isGeneratedImageOwned === void 0) {
27122
+ isGeneratedImageOwned = isExportedJsxGeneratedImageOwned(node);
27123
+ generatedImageOwnershipByFunction.set(enclosingFunction, isGeneratedImageOwned);
27124
+ }
27125
+ if (isGeneratedImageOwned) return;
27126
+ }
26594
27127
  const programRoot = findProgramRoot(node);
26595
27128
  if (programRoot && hasEmailTemplateImport(programRoot)) return;
26596
27129
  const srcAttribute = findJsxAttribute(node.attributes, "src");
@@ -31030,72 +31563,6 @@ const isReactNativeDependencyName = (dependencyName) => {
31030
31563
  return false;
31031
31564
  };
31032
31565
  //#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
31566
  //#region src/plugin/utils/classify-package-platform.ts
31100
31567
  const WEB_FRAMEWORK_DEPENDENCY_NAMES = new Set([
31101
31568
  "next",
@@ -32148,6 +32615,24 @@ const isBuiltinNamespaceCallee = (callee) => {
32148
32615
  }
32149
32616
  return false;
32150
32617
  };
32618
+ const getReactUseCallbackSource = (reference, context) => {
32619
+ const identifier = reference.identifier;
32620
+ const symbol = resolveConstIdentifierAlias(identifier, context.scopes);
32621
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer) return null;
32622
+ const initializer = stripParenExpression(symbol.initializer);
32623
+ if (isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", context.scopes, {
32624
+ allowGlobalReactNamespace: true,
32625
+ resolveNamedAliases: true
32626
+ })) return initializer;
32627
+ return null;
32628
+ };
32629
+ const getDependencyStateRefs = (analysis, context, dependencyReference) => {
32630
+ const useCallbackCall = getReactUseCallbackSource(dependencyReference, context);
32631
+ if (!useCallbackCall) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
32632
+ const dependencyList = useCallbackCall.arguments?.[1];
32633
+ if (!dependencyList || !isNodeOfType(dependencyList, "ArrayExpression")) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
32634
+ return getDownstreamRefs(analysis, dependencyList).flatMap((reference) => getUpstreamRefs(analysis, reference)).filter((reference) => isState(analysis, reference));
32635
+ };
32151
32636
  const isSimpleExpression$1 = (analysis, expression, effectFn, visitedDeclarators) => {
32152
32637
  let isSimple = true;
32153
32638
  walkAst(expression, (child) => {
@@ -32195,7 +32680,7 @@ const noChainStateUpdates = defineRule({
32195
32680
  if (!effectFnRefs || !depsRefs) return;
32196
32681
  const effectFn = getEffectFn(analysis, node);
32197
32682
  if (!effectFn) return;
32198
- const stateDeps = depsRefs.flatMap((ref) => getUpstreamRefs(analysis, ref)).filter((ref) => isState(analysis, ref));
32683
+ const stateDeps = depsRefs.flatMap((reference) => getDependencyStateRefs(analysis, context, reference));
32199
32684
  if (stateDeps.length === 0) return;
32200
32685
  if (stateDeps.every((ref) => isExternallyDrivenState(analysis, ref))) return;
32201
32686
  const stateDepDeclarators = new Set(stateDeps.map((ref) => getUseStateDeclarator(ref)).filter((declarator) => declarator !== null));
@@ -35417,15 +35902,167 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
35417
35902
  walkInsideStatementBlocks(analysisFunction.body, visitor);
35418
35903
  }
35419
35904
  };
35420
- const collectWrittenStateNamesInEffect = (analysisFunctions, setterToStateName) => {
35421
- const writtenStateNames = /* @__PURE__ */ new Set();
35905
+ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
35906
+ const unwrappedExpression = stripParenExpression(expression);
35907
+ if (isNodeOfType(unwrappedExpression, "Literal")) {
35908
+ const literalValue = unwrappedExpression.value;
35909
+ if (literalValue === null || typeof literalValue === "boolean" || typeof literalValue === "number" || typeof literalValue === "string") return { value: literalValue };
35910
+ return null;
35911
+ }
35912
+ if (isNodeOfType(unwrappedExpression, "Identifier")) {
35913
+ if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
35914
+ if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
35915
+ const immutableSymbol = scopes.symbolFor(unwrappedExpression);
35916
+ 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;
35917
+ return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
35918
+ }
35919
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
35920
+ if (unwrappedExpression.operator === "void") return { value: void 0 };
35921
+ if (unwrappedExpression.operator !== "!") return null;
35922
+ const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35923
+ return argumentValue ? { value: !argumentValue.value } : null;
35924
+ }
35925
+ if (isNodeOfType(unwrappedExpression, "CallExpression")) {
35926
+ 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")) {
35927
+ const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
35928
+ return argumentValue ? { value: Boolean(argumentValue.value) } : null;
35929
+ }
35930
+ return null;
35931
+ }
35932
+ if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
35933
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35934
+ if (!leftValue) return null;
35935
+ if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
35936
+ if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
35937
+ if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
35938
+ return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35939
+ }
35940
+ if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
35941
+ const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35942
+ if (!testValue) return null;
35943
+ return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35944
+ }
35945
+ if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
35946
+ const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35947
+ if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
35948
+ return null;
35949
+ }
35950
+ if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
35951
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35952
+ const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
35953
+ if (!leftValue || !rightValue) return null;
35954
+ if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
35955
+ const areEqual = leftValue.value === rightValue.value;
35956
+ return { value: unwrappedExpression.operator === "===" ? areEqual : !areEqual };
35957
+ }
35958
+ if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
35959
+ const isLeftNullish = leftValue.value === null || leftValue.value === void 0;
35960
+ const isRightNullish = rightValue.value === null || rightValue.value === void 0;
35961
+ if (!isLeftNullish && !isRightNullish && typeof leftValue.value !== typeof rightValue.value) return null;
35962
+ const areEqual = isLeftNullish || isRightNullish ? isLeftNullish && isRightNullish : leftValue.value === rightValue.value;
35963
+ return { value: unwrappedExpression.operator === "==" ? areEqual : !areEqual };
35964
+ }
35965
+ }
35966
+ return null;
35967
+ };
35968
+ const readStaticUpdaterReturnValue = (updater, scopes) => {
35969
+ if (!isFunctionLike$1(updater) || updater.async || updater.generator) return null;
35970
+ if (!isNodeOfType(updater.body, "BlockStatement")) return readStaticEffectValue(updater.body, scopes, null, null);
35971
+ if (updater.body.body.length === 0) return { value: void 0 };
35972
+ if (updater.body.body.length !== 1) return null;
35973
+ const returnStatement = updater.body.body[0];
35974
+ if (!isNodeOfType(returnStatement, "ReturnStatement")) return null;
35975
+ if (!returnStatement.argument) return { value: void 0 };
35976
+ return readStaticEffectValue(returnStatement.argument, scopes, null, null);
35977
+ };
35978
+ const readStaticSetterValue = (setterCall, scopes) => {
35979
+ const argument = setterCall.arguments[0];
35980
+ if (!argument) return { value: void 0 };
35981
+ if (isNodeOfType(argument, "SpreadElement")) return null;
35982
+ const updater = resolveExactLocalFunction(argument, scopes);
35983
+ if (updater) return readStaticUpdaterReturnValue(updater, scopes);
35984
+ return readStaticEffectValue(argument, scopes, null, null);
35985
+ };
35986
+ const collectStateWritesInEffect = (analysisFunctions, setterToStateName, scopes) => {
35987
+ const stateWrites = /* @__PURE__ */ new Map();
35422
35988
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
35423
35989
  if (!isNodeOfType(child, "CallExpression")) return;
35424
35990
  if (!isNodeOfType(child.callee, "Identifier")) return;
35425
35991
  const stateName = setterToStateName.get(child.callee.name);
35426
- if (stateName) writtenStateNames.add(stateName);
35992
+ if (!stateName) return;
35993
+ const writeInfo = stateWrites.get(stateName) ?? {
35994
+ values: /* @__PURE__ */ new Set(),
35995
+ hasUnknownValue: false
35996
+ };
35997
+ const staticValue = readStaticSetterValue(child, scopes);
35998
+ if (staticValue) writeInfo.values.add(staticValue.value);
35999
+ else writeInfo.hasUnknownValue = true;
36000
+ stateWrites.set(stateName, writeInfo);
35427
36001
  });
35428
- return writtenStateNames;
36002
+ return stateWrites;
36003
+ };
36004
+ const isGlobalBooleanCall = (node, scopes) => {
36005
+ return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
36006
+ };
36007
+ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
36008
+ let currentNode = workNode;
36009
+ while (currentNode.parent) {
36010
+ const parentNode = currentNode.parent;
36011
+ if (isFunctionLike$1(parentNode)) break;
36012
+ if (isNodeOfType(parentNode, "IfStatement")) {
36013
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
36014
+ if (testValue) {
36015
+ if (currentNode === parentNode.consequent && !testValue.value) return false;
36016
+ if (currentNode === parentNode.alternate && testValue.value) return false;
36017
+ }
36018
+ }
36019
+ if (isNodeOfType(parentNode, "ConditionalExpression")) {
36020
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
36021
+ if (testValue) {
36022
+ if (currentNode === parentNode.consequent && !testValue.value) return false;
36023
+ if (currentNode === parentNode.alternate && testValue.value) return false;
36024
+ }
36025
+ }
36026
+ if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
36027
+ const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
36028
+ if (leftValue) {
36029
+ if (parentNode.operator === "&&" && !leftValue.value) return false;
36030
+ if (parentNode.operator === "||" && leftValue.value) return false;
36031
+ if (parentNode.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return false;
36032
+ }
36033
+ }
36034
+ if (isNodeOfType(parentNode, "BlockStatement")) {
36035
+ const statementIndex = parentNode.body.findIndex((statement) => statement === currentNode);
36036
+ if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
36037
+ const earlierStatement = parentNode.body[index];
36038
+ if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
36039
+ if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
36040
+ }
36041
+ }
36042
+ currentNode = parentNode;
36043
+ }
36044
+ return true;
36045
+ };
36046
+ const isReaderWorkNode = (node, analysisFunctions, scopes) => {
36047
+ if (isNodeOfType(node, "CallExpression")) {
36048
+ if (isGlobalBooleanCall(node, scopes)) return false;
36049
+ const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
36050
+ return !invokedFunction || !analysisFunctions.has(invokedFunction);
36051
+ }
36052
+ return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
36053
+ };
36054
+ const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
36055
+ if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
36056
+ for (const writtenValue of writeInfo.values) {
36057
+ const stateValue = { value: writtenValue };
36058
+ let didFindReachableWork = false;
36059
+ visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
36060
+ if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
36061
+ if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
36062
+ });
36063
+ if (didFindReachableWork) return true;
36064
+ }
36065
+ return false;
35429
36066
  };
35430
36067
  const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
35431
36068
  const isFunctionShapedReturn = (returnedValue, setterToStateName, isExplicitReturnStatement) => {
@@ -35514,18 +36151,29 @@ const noEffectChain = defineRule({
35514
36151
  const useStateBindings = collectUseStateBindings(componentBody);
35515
36152
  if (useStateBindings.length === 0) return;
35516
36153
  const setterToStateName = /* @__PURE__ */ new Map();
35517
- for (const binding of useStateBindings) setterToStateName.set(binding.setterName, binding.valueName);
36154
+ const stateSymbolIds = /* @__PURE__ */ new Map();
36155
+ for (const binding of useStateBindings) {
36156
+ setterToStateName.set(binding.setterName, binding.valueName);
36157
+ if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
36158
+ const stateIdentifier = binding.declarator.id.elements[0];
36159
+ if (isNodeOfType(stateIdentifier, "Identifier")) {
36160
+ const stateSymbol = context.scopes.symbolFor(stateIdentifier);
36161
+ if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
36162
+ }
36163
+ }
35518
36164
  const storageSetterNames = collectStorageHookSetterNames(componentBody);
35519
36165
  const effectInfos = [];
35520
36166
  for (const effectCall of findTopLevelEffectCalls(componentBody)) {
35521
36167
  const callback = getEffectCallback(effectCall, context.scopes);
35522
36168
  if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
35523
36169
  const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
35524
- const writtenStateNames = collectWrittenStateNamesInEffect(analysisFunctions, setterToStateName);
36170
+ const stateWrites = collectStateWritesInEffect(analysisFunctions, setterToStateName, context.scopes);
36171
+ const writtenStateNames = new Set(stateWrites.keys());
35525
36172
  effectInfos.push({
35526
36173
  node: effectCall,
35527
36174
  depNames: collectDepIdentifierNames(effectCall),
35528
- writtenStateNames,
36175
+ stateWrites,
36176
+ analysisFunctions,
35529
36177
  isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
35530
36178
  });
35531
36179
  }
@@ -35533,13 +36181,15 @@ const noEffectChain = defineRule({
35533
36181
  const reportedNodes = /* @__PURE__ */ new Set();
35534
36182
  for (const writerEffect of effectInfos) {
35535
36183
  if (writerEffect.isExternalSync) continue;
35536
- if (writerEffect.writtenStateNames.size === 0) continue;
36184
+ if (writerEffect.stateWrites.size === 0) continue;
35537
36185
  for (const readerEffect of effectInfos) {
35538
36186
  if (readerEffect === writerEffect) continue;
35539
36187
  if (readerEffect.isExternalSync) continue;
35540
36188
  if (readerEffect.depNames.size === 0) continue;
35541
36189
  let chainedStateName = null;
35542
- for (const writtenName of writerEffect.writtenStateNames) if (readerEffect.depNames.has(writtenName)) {
36190
+ for (const [writtenName, writeInfo] of writerEffect.stateWrites) {
36191
+ if (!readerEffect.depNames.has(writtenName)) continue;
36192
+ if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
35543
36193
  chainedStateName = writtenName;
35544
36194
  break;
35545
36195
  }
@@ -43886,11 +44536,110 @@ const isSameRefCurrentMember = (node, refSymbol, scopes) => {
43886
44536
  return isNodeOfType(receiver, "Identifier") && resolveConstIdentifierAlias(receiver, scopes)?.id === refSymbol.id;
43887
44537
  };
43888
44538
  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);
44539
+ const expression = stripParenExpression(node);
44540
+ if (isSameRefCurrentMember(expression, refSymbol, scopes)) return true;
44541
+ if (!isNodeOfType(expression, "Identifier")) return false;
44542
+ const aliasSymbol = scopes.symbolFor(expression);
43892
44543
  return aliasSymbol?.kind === "const" && aliasSymbol.initializer !== null && isSameRefCurrentMember(stripParenExpression(aliasSymbol.initializer), refSymbol, scopes);
43893
44544
  };
44545
+ const resolveImmutableInitializationValue = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
44546
+ const expression = stripParenExpression(node);
44547
+ if (!isNodeOfType(expression, "Identifier")) return expression;
44548
+ const symbol = scopes.symbolFor(expression);
44549
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(symbol.id)) return null;
44550
+ visitedSymbolIds.add(symbol.id);
44551
+ return resolveImmutableInitializationValue(symbol.initializer, scopes, visitedSymbolIds);
44552
+ };
44553
+ const isProvablyTruthyInitializationValue = (node, scopes) => {
44554
+ const expression = resolveImmutableInitializationValue(node, scopes);
44555
+ return Boolean(expression && (isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression")));
44556
+ };
44557
+ const getInitializationConstructorName = (node, scopes) => {
44558
+ const expression = resolveImmutableInitializationValue(node, scopes);
44559
+ if (!expression) return null;
44560
+ if (isNodeOfType(expression, "NewExpression")) {
44561
+ const callee = stripParenExpression(expression.callee);
44562
+ return isNodeOfType(callee, "Identifier") ? callee.name : null;
44563
+ }
44564
+ return null;
44565
+ };
44566
+ const isClosedTruthyTypeDomain = (typeNode, initializationValue, scopes) => {
44567
+ const initializationExpression = stripParenExpression(initializationValue);
44568
+ if (isNodeOfType(typeNode, "TSTypeLiteral")) return isNodeOfType(initializationExpression, "ObjectExpression");
44569
+ if (isNodeOfType(typeNode, "TSArrayType") || isNodeOfType(typeNode, "TSTupleType")) return isNodeOfType(initializationExpression, "ArrayExpression");
44570
+ if (isNodeOfType(typeNode, "TSFunctionType") || isNodeOfType(typeNode, "TSConstructorType")) return isNodeOfType(initializationExpression, "ArrowFunctionExpression") || isNodeOfType(initializationExpression, "FunctionExpression") || isNodeOfType(initializationExpression, "ClassExpression");
44571
+ if (isNodeOfType(typeNode, "TSObjectKeyword")) return true;
44572
+ if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
44573
+ const typeName = typeNode.typeName;
44574
+ return isNodeOfType(typeName, "Identifier") && typeName.name === getInitializationConstructorName(initializationExpression, scopes);
44575
+ };
44576
+ const refHasClosedFalsySentinelDomain = (refSymbol, initializationValue, scopes) => {
44577
+ const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
44578
+ if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
44579
+ const [initialValue] = initializer.arguments ?? [];
44580
+ if (!initialValue || isNodeOfType(initialValue, "SpreadElement") || !isEmptySentinel(initialValue, scopes)) return false;
44581
+ const [declaredType] = initializer.typeArguments?.params ?? [];
44582
+ if (!declaredType || !isNodeOfType(declaredType, "TSUnionType")) return false;
44583
+ let hasEmptySentinel = false;
44584
+ let hasTruthyDomain = false;
44585
+ for (const memberType of declaredType.types ?? []) {
44586
+ if (isNodeOfType(memberType, "TSNullKeyword") || isNodeOfType(memberType, "TSUndefinedKeyword")) {
44587
+ hasEmptySentinel = true;
44588
+ continue;
44589
+ }
44590
+ if (!isClosedTruthyTypeDomain(memberType, initializationValue, scopes)) return false;
44591
+ hasTruthyDomain = true;
44592
+ }
44593
+ return hasEmptySentinel && hasTruthyDomain;
44594
+ };
44595
+ const isSafeRefIdentifierUse = (identifier) => {
44596
+ const expressionRoot = findTransparentExpressionRoot(identifier);
44597
+ const parent = expressionRoot.parent;
44598
+ if (parent && isNodeOfType(parent, "VariableDeclarator") && parent.id === expressionRoot && parent.parent !== null && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const") return true;
44599
+ if (parent && isNodeOfType(parent, "MemberExpression") && parent.object === expressionRoot && getStaticPropertyName(parent) === "current") return true;
44600
+ if (!parent || !isNodeOfType(parent, "VariableDeclarator") || parent.init !== expressionRoot) return false;
44601
+ return isNodeOfType(parent.id, "Identifier") && parent.parent !== null && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const";
44602
+ };
44603
+ const refDoesNotEscape = (branchRoot, refSymbol, scopes) => {
44604
+ let didEscape = false;
44605
+ walkAst(branchRoot, (child) => {
44606
+ if (didEscape) return false;
44607
+ if (!isNodeOfType(child, "Identifier")) return;
44608
+ if (resolveConstIdentifierAlias(child, scopes)?.id !== refSymbol.id) return;
44609
+ if (child === refSymbol.bindingIdentifier || isSafeRefIdentifierUse(child)) return;
44610
+ didEscape = true;
44611
+ return false;
44612
+ });
44613
+ return !didEscape;
44614
+ };
44615
+ const expressionContainsRefCurrent = (expression, refSymbol, scopes) => {
44616
+ let didFindRefCurrent = false;
44617
+ walkAst(expression, (child) => {
44618
+ if (didFindRefCurrent) return false;
44619
+ if (resolveReactRefSymbol(child, scopes)?.id !== refSymbol.id) return;
44620
+ didFindRefCurrent = true;
44621
+ return false;
44622
+ });
44623
+ return didFindRefCurrent;
44624
+ };
44625
+ const hasNoCompetingRefCurrentWrite = (branchRoot, assignmentExpression, refSymbol, scopes) => {
44626
+ let writeCount = 0;
44627
+ walkAst(branchRoot, (child) => {
44628
+ if (writeCount > 1) return false;
44629
+ if (isNodeOfType(child, "AssignmentExpression")) {
44630
+ if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
44631
+ return;
44632
+ }
44633
+ if (isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
44634
+ if (expressionContainsRefCurrent(child.argument, refSymbol, scopes)) writeCount++;
44635
+ return;
44636
+ }
44637
+ if (isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement")) {
44638
+ if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
44639
+ }
44640
+ });
44641
+ return writeCount === 1 && expressionContainsRefCurrent(assignmentExpression.left, refSymbol, scopes);
44642
+ };
43894
44643
  const isEmptySentinel = (node, scopes) => isNodeOfType(node, "Literal") && node.value === null || isNodeOfType(node, "Identifier") && node.name === "undefined" && scopes.isGlobalReference(node);
43895
44644
  const hasRepeatedExecutionAncestor = (node, stop) => {
43896
44645
  let ancestor = node.parent;
@@ -43939,18 +44688,22 @@ const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol
43939
44688
  const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes) => {
43940
44689
  if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return true;
43941
44690
  if (assignmentExpression.operator !== "=") return false;
44691
+ const renderOwner = findRenderPhaseComponentOrHook(assignmentExpression, scopes);
44692
+ if (!renderOwner) return false;
43942
44693
  let descendant = assignmentExpression;
43943
44694
  let ancestor = descendant.parent;
43944
44695
  while (ancestor) {
43945
- if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(ancestor.test, "BinaryExpression") && [
44696
+ const test = isNodeOfType(ancestor, "IfStatement") ? stripParenExpression(ancestor.test) : null;
44697
+ 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;
44698
+ if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(test, "BinaryExpression") && [
43946
44699
  "===",
43947
44700
  "==",
43948
44701
  "!==",
43949
44702
  "!="
43950
- ].includes(ancestor.test.operator)) {
43951
- const { left, right } = ancestor.test;
44703
+ ].includes(test.operator)) {
44704
+ const { left, right } = test;
43952
44705
  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;
44706
+ const guardedBranch = test.operator === "===" || test.operator === "==" ? ancestor.consequent : ancestor.alternate;
43954
44707
  if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && hasNoPriorCoExecutableWrite(assignmentExpression, guardedBranch, refSymbol, scopes)) return true;
43955
44708
  }
43956
44709
  descendant = ancestor;
@@ -46585,6 +47338,185 @@ const noUnescapedEntities = defineRule({
46585
47338
  } })
46586
47339
  });
46587
47340
  //#endregion
47341
+ //#region src/plugin/utils/read-server-snapshot-boolean.ts
47342
+ const crossFileScopes = /* @__PURE__ */ new WeakMap();
47343
+ const getCrossFileScopes = (programNode) => {
47344
+ const cachedScopes = crossFileScopes.get(programNode);
47345
+ if (cachedScopes) return cachedScopes;
47346
+ const scopes = analyzeScopes(programNode);
47347
+ crossFileScopes.set(programNode, scopes);
47348
+ return scopes;
47349
+ };
47350
+ const symbolIsImmutable = (symbol) => (symbol.kind === "const" || symbol.kind === "function") && symbol.references.every((reference) => reference.flag === "read");
47351
+ const identifierAliasChainIsImmutable = (identifier, scopes, allowImportTerminal = false) => {
47352
+ if (!isNodeOfType(identifier, "Identifier")) return false;
47353
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
47354
+ let symbol = scopes.symbolFor(identifier);
47355
+ while (symbol) {
47356
+ if (symbol.kind === "import") return allowImportTerminal;
47357
+ if (visitedSymbolIds.has(symbol.id) || !symbolIsImmutable(symbol)) return false;
47358
+ visitedSymbolIds.add(symbol.id);
47359
+ if (symbol.kind !== "const" || !symbol.initializer) return symbol.kind === "function";
47360
+ const initializer = stripParenExpression(symbol.initializer);
47361
+ if (!isNodeOfType(initializer, "Identifier")) return true;
47362
+ symbol = scopes.symbolFor(initializer);
47363
+ }
47364
+ return false;
47365
+ };
47366
+ const getImportedHookBinding = (callee, scopes) => {
47367
+ if (!isNodeOfType(callee, "Identifier") || !identifierAliasChainIsImmutable(callee, scopes, true)) return null;
47368
+ const importedSymbol = resolveConstIdentifierAlias(callee, scopes);
47369
+ if (importedSymbol?.kind !== "import") return null;
47370
+ const importDeclaration = importedSymbol.declarationNode.parent;
47371
+ if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration")) return null;
47372
+ const source = importDeclaration.source.value;
47373
+ if (typeof source !== "string") return null;
47374
+ const exportedName = resolveImportedExportName(importedSymbol.declarationNode);
47375
+ return exportedName ? {
47376
+ exportedName,
47377
+ source
47378
+ } : null;
47379
+ };
47380
+ const functionBindingIsImmutable = (functionNode, scopes) => {
47381
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) {
47382
+ const symbol = scopes.symbolFor(functionNode.id);
47383
+ return Boolean(symbol && symbolIsImmutable(symbol));
47384
+ }
47385
+ const expressionRoot = findTransparentExpressionRoot(functionNode);
47386
+ const parentNode = expressionRoot.parent;
47387
+ if (isNodeOfType(parentNode, "ExportDefaultDeclaration")) return true;
47388
+ if (!isNodeOfType(parentNode, "VariableDeclarator") || parentNode.init !== expressionRoot || !isNodeOfType(parentNode.id, "Identifier")) return false;
47389
+ const symbol = scopes.symbolFor(parentNode.id);
47390
+ return Boolean(symbol && symbolIsImmutable(symbol));
47391
+ };
47392
+ const getExactFunctionResultExpression = (functionNode) => {
47393
+ if (!isFunctionLike$1(functionNode) || functionNode.async) return null;
47394
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.generator) return null;
47395
+ if (isNodeOfType(functionNode, "FunctionExpression") && functionNode.generator) return null;
47396
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return functionNode.body;
47397
+ const [returnStatement, additionalReturnStatement] = collectFunctionReturnStatements(functionNode);
47398
+ if (!returnStatement || additionalReturnStatement || !returnStatement.argument) return null;
47399
+ if ([...functionNode.body.body].pop() !== returnStatement) return null;
47400
+ return returnStatement.argument;
47401
+ };
47402
+ const functionHasNoParameters = (functionNode) => isFunctionLike$1(functionNode) && (functionNode.params?.length ?? 0) === 0;
47403
+ const readImmutableBooleanLiteral = (expression, scopes, visitedSymbolIds) => {
47404
+ const unwrappedExpression = stripParenExpression(expression);
47405
+ if (isNodeOfType(unwrappedExpression, "Literal") && typeof unwrappedExpression.value === "boolean") return unwrappedExpression.value;
47406
+ if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
47407
+ const symbol = scopes.symbolFor(unwrappedExpression);
47408
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || !symbolIsImmutable(symbol) || visitedSymbolIds.has(symbol.id)) return null;
47409
+ visitedSymbolIds.add(symbol.id);
47410
+ return readImmutableBooleanLiteral(symbol.initializer, scopes, visitedSymbolIds);
47411
+ };
47412
+ const readFunctionLiteralBoolean = (expression, scopes) => {
47413
+ const unwrappedExpression = stripParenExpression(expression);
47414
+ if (isNodeOfType(unwrappedExpression, "Identifier") && !identifierAliasChainIsImmutable(unwrappedExpression, scopes)) return null;
47415
+ const functionNode = resolveExactLocalFunction(unwrappedExpression, scopes);
47416
+ if (!functionNode) return null;
47417
+ const resultExpression = getExactFunctionResultExpression(functionNode);
47418
+ return resultExpression ? readImmutableBooleanLiteral(resultExpression, scopes, /* @__PURE__ */ new Set()) : null;
47419
+ };
47420
+ const readServerSnapshotBooleanInternal = (expression, scopes, visitedSymbolIds, visitedFunctionNodes, currentFilename) => {
47421
+ const unwrappedExpression = stripParenExpression(expression);
47422
+ if (isNodeOfType(unwrappedExpression, "Literal") && typeof unwrappedExpression.value === "boolean") return {
47423
+ hasUseSyncExternalStoreOrigin: false,
47424
+ value: unwrappedExpression.value
47425
+ };
47426
+ if (isNodeOfType(unwrappedExpression, "Identifier")) {
47427
+ const symbol = scopes.symbolFor(unwrappedExpression);
47428
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || !symbolIsImmutable(symbol) || visitedSymbolIds.has(symbol.id)) return null;
47429
+ visitedSymbolIds.add(symbol.id);
47430
+ return readServerSnapshotBooleanInternal(symbol.initializer, scopes, visitedSymbolIds, visitedFunctionNodes, currentFilename);
47431
+ }
47432
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
47433
+ const argumentResult = readServerSnapshotBooleanInternal(unwrappedExpression.argument, scopes, visitedSymbolIds, visitedFunctionNodes, currentFilename);
47434
+ return argumentResult ? {
47435
+ hasUseSyncExternalStoreOrigin: argumentResult.hasUseSyncExternalStoreOrigin,
47436
+ value: !argumentResult.value
47437
+ } : null;
47438
+ }
47439
+ if (isNodeOfType(unwrappedExpression, "LogicalExpression") && (unwrappedExpression.operator === "&&" || unwrappedExpression.operator === "||")) {
47440
+ const leftResult = readServerSnapshotBooleanInternal(unwrappedExpression.left, scopes, new Set(visitedSymbolIds), new Set(visitedFunctionNodes), currentFilename);
47441
+ if (leftResult && (unwrappedExpression.operator === "&&" && !leftResult.value || unwrappedExpression.operator === "||" && leftResult.value)) return leftResult;
47442
+ const rightResult = readServerSnapshotBooleanInternal(unwrappedExpression.right, scopes, new Set(visitedSymbolIds), new Set(visitedFunctionNodes), currentFilename);
47443
+ if (rightResult && (unwrappedExpression.operator === "&&" && !rightResult.value || unwrappedExpression.operator === "||" && rightResult.value)) return rightResult;
47444
+ return leftResult && rightResult ? {
47445
+ hasUseSyncExternalStoreOrigin: leftResult.hasUseSyncExternalStoreOrigin || rightResult.hasUseSyncExternalStoreOrigin,
47446
+ value: rightResult.value
47447
+ } : null;
47448
+ }
47449
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
47450
+ if (isReactApiCall(unwrappedExpression, "useSyncExternalStore", scopes, { resolveNamedAliases: true })) {
47451
+ const [, , serverSnapshotArgument] = unwrappedExpression.arguments ?? [];
47452
+ if (!serverSnapshotArgument || isNodeOfType(serverSnapshotArgument, "SpreadElement")) return null;
47453
+ const serverSnapshotValue = readFunctionLiteralBoolean(serverSnapshotArgument, scopes);
47454
+ return serverSnapshotValue === null ? null : {
47455
+ hasUseSyncExternalStoreOrigin: true,
47456
+ value: serverSnapshotValue
47457
+ };
47458
+ }
47459
+ if ((unwrappedExpression.arguments?.length ?? 0) !== 0) return null;
47460
+ const callee = stripParenExpression(unwrappedExpression.callee);
47461
+ const importedHookBinding = getImportedHookBinding(callee, scopes);
47462
+ if (importedHookBinding && currentFilename) {
47463
+ const resolvedHook = resolveCrossFileFunctionExportWithFilePath(path.resolve(currentFilename), importedHookBinding.source, importedHookBinding.exportedName);
47464
+ if (resolvedHook && !resolvedHook.filePath.split(path.sep).includes("node_modules")) {
47465
+ const resolvedScopes = getCrossFileScopes(resolvedHook.programNode);
47466
+ 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)) {
47467
+ visitedFunctionNodes.add(resolvedHook.functionNode);
47468
+ const resultExpression = getExactFunctionResultExpression(resolvedHook.functionNode);
47469
+ if (resultExpression) return readServerSnapshotBooleanInternal(resultExpression, resolvedScopes, /* @__PURE__ */ new Set(), visitedFunctionNodes, resolvedHook.filePath);
47470
+ }
47471
+ }
47472
+ return null;
47473
+ }
47474
+ if (!isNodeOfType(callee, "Identifier") || !isReactHookName(callee.name) || !identifierAliasChainIsImmutable(callee, scopes)) return null;
47475
+ const hookFunction = resolveExactLocalFunction(callee, scopes);
47476
+ if (!hookFunction || !functionHasNoParameters(hookFunction) || visitedFunctionNodes.has(hookFunction)) return null;
47477
+ visitedFunctionNodes.add(hookFunction);
47478
+ const resultExpression = getExactFunctionResultExpression(hookFunction);
47479
+ return resultExpression ? readServerSnapshotBooleanInternal(resultExpression, scopes, visitedSymbolIds, visitedFunctionNodes, currentFilename) : null;
47480
+ };
47481
+ const readServerSnapshotBoolean = (expression, scopes, currentFilename) => {
47482
+ const result = readServerSnapshotBooleanInternal(expression, scopes, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set(), currentFilename);
47483
+ return result?.hasUseSyncExternalStoreOrigin ? result.value : null;
47484
+ };
47485
+ //#endregion
47486
+ //#region src/plugin/utils/is-after-falsy-server-snapshot-early-return.ts
47487
+ const isAfterFalsyServerSnapshotEarlyReturn = (node, componentOrHookNode, scopes, filename) => {
47488
+ const enclosingFunction = findEnclosingFunction$1(node);
47489
+ if (!enclosingFunction || enclosingFunction !== componentOrHookNode && !executesDuringRender(enclosingFunction, scopes) || !isFunctionLike$1(enclosingFunction) || !isNodeOfType(enclosingFunction.body, "BlockStatement")) return false;
47490
+ let currentNode = node;
47491
+ while (currentNode !== enclosingFunction) {
47492
+ const parentNode = currentNode.parent;
47493
+ if (!parentNode) return false;
47494
+ if (isNodeOfType(parentNode, "BlockStatement")) for (const statement of parentNode.body) {
47495
+ if (statement === currentNode) break;
47496
+ if (!isNodeOfType(statement, "IfStatement")) continue;
47497
+ const serverResult = readServerSnapshotBoolean(statement.test, scopes, filename);
47498
+ if (serverResult === true && statementAlwaysExits(statement.consequent)) return true;
47499
+ if (serverResult === false && statement.alternate && statementAlwaysExits(statement.alternate)) return true;
47500
+ }
47501
+ currentNode = parentNode;
47502
+ }
47503
+ return false;
47504
+ };
47505
+ //#endregion
47506
+ //#region src/plugin/utils/is-gated-by-falsy-server-snapshot.ts
47507
+ const isGatedByFalsyServerSnapshot = (node, scopes, filename) => {
47508
+ let currentNode = node;
47509
+ let parentNode = node.parent;
47510
+ while (parentNode) {
47511
+ 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;
47512
+ if (isNodeOfType(parentNode, "ConditionalExpression") && (parentNode.consequent === currentNode && readServerSnapshotBoolean(parentNode.test, scopes, filename) === false || parentNode.alternate === currentNode && readServerSnapshotBoolean(parentNode.test, scopes, filename) === true)) return true;
47513
+ if (isNodeOfType(parentNode, "IfStatement") && (parentNode.consequent === currentNode && readServerSnapshotBoolean(parentNode.test, scopes, filename) === false || parentNode.alternate === currentNode && readServerSnapshotBoolean(parentNode.test, scopes, filename) === true)) return true;
47514
+ currentNode = parentNode;
47515
+ parentNode = parentNode.parent ?? null;
47516
+ }
47517
+ return false;
47518
+ };
47519
+ //#endregion
46588
47520
  //#region src/plugin/rules/performance/no-unguarded-browser-global-in-render-or-hook-init.ts
46589
47521
  const BROWSER_GLOBAL_NAMES = new Set([
46590
47522
  "window",
@@ -46691,7 +47623,9 @@ const noUnguardedBrowserGlobalInRenderOrHookInit = defineRule({
46691
47623
  if (fileIsEmailTemplate) return;
46692
47624
  if (isGeneratedImageRenderContext(context, findEnclosingJsxOpeningElement(node) ?? node)) return;
46693
47625
  if (isGatedByFalsyInitialState(node, context.scopes)) return;
47626
+ if (isGatedByFalsyServerSnapshot(node, context.scopes, context.filename)) return;
46694
47627
  if (isAfterClientOnlyEarlyReturn(node, componentOrHookNode, context.scopes)) return;
47628
+ if (isAfterFalsyServerSnapshotEarlyReturn(node, componentOrHookNode, context.scopes, context.filename)) return;
46695
47629
  if (isInsideAvailabilityGuard(node, browserGlobalName, context)) return;
46696
47630
  if (isAfterAvailabilityEarlyExit(node, componentOrHookNode, browserGlobalName, context)) return;
46697
47631
  reportedNodes.add(node);
@@ -50894,6 +51828,31 @@ const preferModuleScopePureFunction = defineRule({
50894
51828
  }
50895
51829
  });
50896
51830
  //#endregion
51831
+ //#region src/plugin/utils/get-require-call-source.ts
51832
+ const getRequireCallSource = (expression) => {
51833
+ const unwrappedExpression = stripParenExpression(expression);
51834
+ if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
51835
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
51836
+ if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
51837
+ const [firstArgument] = unwrappedExpression.arguments ?? [];
51838
+ if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
51839
+ return typeof firstArgument.value === "string" ? firstArgument.value : null;
51840
+ };
51841
+ //#endregion
51842
+ //#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
51843
+ const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
51844
+ const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
51845
+ const identifier = stripParenExpression(expression);
51846
+ if (!isNodeOfType(identifier, "Identifier")) return false;
51847
+ const symbol = resolveConstIdentifierAlias(identifier, scopes);
51848
+ if (!symbol) return false;
51849
+ if (symbol.kind === "import") {
51850
+ const importBinding = getImportBindingForName(identifier, symbol.name);
51851
+ return Boolean(importBinding && NODE_CRYPTO_MODULE_SOURCES.has(importBinding.source));
51852
+ }
51853
+ return Boolean(symbol.kind === "const" && symbol.initializer && NODE_CRYPTO_MODULE_SOURCES.has(getRequireCallSource(symbol.initializer) ?? ""));
51854
+ };
51855
+ //#endregion
50897
51856
  //#region src/plugin/rules/architecture/prefer-module-scope-static-value.ts
50898
51857
  const MUTATING_RECEIVER_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
50899
51858
  const isMutationContext = (referenceIdentifier) => {
@@ -51007,9 +51966,9 @@ const isImpureCall = (node, scopes) => {
51007
51966
  const callee = node.callee;
51008
51967
  if (isNodeOfType(callee, "Identifier")) return isImpureBareCallee(callee, scopes);
51009
51968
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return false;
51010
- if (!isNodeOfType(callee.object, "Identifier")) return false;
51011
51969
  if (!isNodeOfType(callee.property, "Identifier")) return false;
51012
- return Boolean(IMPURE_MEMBER_RECEIVERS.get(callee.object.name)?.has(callee.property.name));
51970
+ 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;
51971
+ return false;
51013
51972
  };
51014
51973
  const containsImpureExpression = (expression, scopes) => {
51015
51974
  let foundImpure = false;
@@ -55778,16 +56737,6 @@ const rnListCallbackPerRow = defineRule({
55778
56737
  }
55779
56738
  });
55780
56739
  //#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
56740
  //#region src/plugin/utils/get-initializer-module-source.ts
55792
56741
  const getInitializerModuleSource = (contextNode, initializer) => {
55793
56742
  const requireSource = getRequireCallSource(initializer);
@@ -62566,9 +63515,35 @@ const serverDedupProps = defineRule({
62566
63515
  });
62567
63516
  //#endregion
62568
63517
  //#region src/plugin/rules/server/server-fetch-without-revalidate.ts
62569
- const isFetchCall = (node) => {
63518
+ const isGlobalThisFetchMember = (node, context) => {
63519
+ const memberExpression = stripParenExpression(node);
63520
+ if (!isNodeOfType(memberExpression, "MemberExpression")) return false;
63521
+ const receiver = stripParenExpression(memberExpression.object);
63522
+ return getStaticPropertyName(memberExpression) === "fetch" && isNodeOfType(receiver, "Identifier") && receiver.name === "globalThis" && context.scopes.isGlobalReference(receiver);
63523
+ };
63524
+ const isGlobalThisIdentifier = (node, context) => {
63525
+ const expression = stripParenExpression(node);
63526
+ return isNodeOfType(expression, "Identifier") && expression.name === "globalThis" && context.scopes.isGlobalReference(expression);
63527
+ };
63528
+ 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));
63529
+ const isExactGlobalFetchValue = (node, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
63530
+ const expression = stripParenExpression(node);
63531
+ if (isGlobalThisFetchMember(expression, context)) return true;
63532
+ if (!isNodeOfType(expression, "Identifier")) return false;
63533
+ if (expression.name === "fetch" && context.scopes.isGlobalReference(expression)) return true;
63534
+ const symbol = context.scopes.symbolFor(expression);
63535
+ if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
63536
+ if (!isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
63537
+ if (isGlobalFetchDestructuringBinding(symbol.bindingIdentifier, symbol.declarationNode, context)) return true;
63538
+ if (symbol.declarationNode.id !== symbol.bindingIdentifier || !symbol.initializer) return false;
63539
+ visitedSymbolIds.add(symbol.id);
63540
+ return isExactGlobalFetchValue(symbol.initializer, context, visitedSymbolIds);
63541
+ };
63542
+ const isFetchCall = (node, context) => {
62570
63543
  if (!isNodeOfType(node, "CallExpression")) return false;
62571
- return isNodeOfType(node.callee, "Identifier") && node.callee.name === "fetch";
63544
+ const callee = stripParenExpression(node.callee);
63545
+ if (!isNodeOfType(callee, "Identifier") || callee.name !== "fetch") return false;
63546
+ return isExactGlobalFetchValue(callee, context);
62572
63547
  };
62573
63548
  const getPropertyKeyName$1 = (property) => {
62574
63549
  if (!isNodeOfType(property, "Property")) return null;
@@ -62614,7 +63589,7 @@ const serverFetchWithoutRevalidate = defineRule({
62614
63589
  },
62615
63590
  CallExpression(node) {
62616
63591
  if (!isServerSideFile) return;
62617
- if (!isFetchCall(node)) return;
63592
+ if (!isFetchCall(node, context)) return;
62618
63593
  if (isMutatingFetchCall(node)) return;
62619
63594
  const optionsArg = node.arguments?.[1];
62620
63595
  if (optionsArg) {
@@ -70096,6 +71071,7 @@ const CROSS_FILE_RULE_IDS = new Set([
70096
71071
  "exhaustive-deps",
70097
71072
  "no-barrel-import",
70098
71073
  "nextjs-missing-metadata",
71074
+ "nextjs-no-img-element",
70099
71075
  "nextjs-no-use-search-params-without-suspense",
70100
71076
  "no-dynamic-import-path",
70101
71077
  "no-full-lodash-import",
@@ -70327,12 +71303,12 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
70327
71303
  ]);
70328
71304
  /**
70329
71305
  * 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
71306
+ * excluded from fingerprinting and re-lint every file on every scan. A new
71307
+ * cross-file rule must be added either here or to
70332
71308
  * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
70333
71309
  * partition), forcing a conscious classification.
70334
71310
  */
70335
- const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["only-export-components"]);
71311
+ const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["nextjs-no-img-element", "only-export-components"]);
70336
71312
  /**
70337
71313
  * Runs the collectors for `ruleIds` over one file and returns every
70338
71314
  * filesystem probe they made — the file's cross-file dependency set.