oxlint-plugin-react-doctor 0.7.9-dev.61111f1 → 0.7.9-dev.6325f9c

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.d.ts CHANGED
@@ -18820,8 +18820,8 @@ type CrossFileDependencyCollector = (input: CrossFileDependencyCollectorInput) =
18820
18820
  declare const CROSS_FILE_DEPENDENCY_COLLECTORS: ReadonlyMap<string, CrossFileDependencyCollector>;
18821
18821
  /**
18822
18822
  * Cross-file rules whose dependency set CANNOT be soundly bounded — they are
18823
- * excluded from fingerprinting and re-lint every file on every scan. Empty
18824
- * today; a new cross-file rule must be added either here or to
18823
+ * excluded from fingerprinting and re-lint every file on every scan. A new
18824
+ * cross-file rule must be added either here or to
18825
18825
  * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
18826
18826
  * partition), forcing a conscious classification.
18827
18827
  */
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 [];
@@ -26486,6 +26523,489 @@ const hasEmailTemplateImport = (programRoot) => {
26486
26523
  return found;
26487
26524
  };
26488
26525
  //#endregion
26526
+ //#region src/plugin/utils/build-generated-image-project-index.ts
26527
+ const GENERATED_IMAGE_SOURCE_FILE_PATTERN = /\.[cm]?[jt]sx?$/i;
26528
+ const GENERATED_IMAGE_DECLARATION_FILE_PATTERN = /\.d\.[cm]?[jt]s$/i;
26529
+ const GENERATED_IMAGE_MDX_FILE_PATTERN = /\.mdx$/i;
26530
+ const GENERATED_IMAGE_IGNORED_DIRECTORY_NAMES = new Set([
26531
+ ".angular",
26532
+ ".astro",
26533
+ ".cache",
26534
+ ".contentlayer",
26535
+ ".docusaurus",
26536
+ ".expo",
26537
+ ".git",
26538
+ ".next",
26539
+ ".nuxt",
26540
+ ".output",
26541
+ ".svelte-kit",
26542
+ ".turbo",
26543
+ ".vercel",
26544
+ "build",
26545
+ "coverage",
26546
+ "dist",
26547
+ "node_modules",
26548
+ "out",
26549
+ "storybook-static"
26550
+ ]);
26551
+ const generatedImageScopeCache = /* @__PURE__ */ new WeakMap();
26552
+ const getGeneratedImageModuleScopes = (programNode) => {
26553
+ const cachedScopes = generatedImageScopeCache.get(programNode);
26554
+ if (cachedScopes) return cachedScopes;
26555
+ const scopes = analyzeScopes(programNode);
26556
+ generatedImageScopeCache.set(programNode, scopes);
26557
+ return scopes;
26558
+ };
26559
+ const listProductionSourceFiles = (rootDirectory) => {
26560
+ const sourceFilePaths = [];
26561
+ const pendingDirectories = [rootDirectory];
26562
+ let hasOpaqueMdxConsumerSurface = false;
26563
+ while (pendingDirectories.length > 0) {
26564
+ const currentDirectory = pendingDirectories.pop();
26565
+ if (!currentDirectory) continue;
26566
+ let entries;
26567
+ try {
26568
+ entries = fs.readdirSync(currentDirectory, { withFileTypes: true });
26569
+ } catch {
26570
+ return null;
26571
+ }
26572
+ for (const entry of entries) {
26573
+ const absolutePath = path.join(currentDirectory, entry.name);
26574
+ const isIgnoredDirectoryName = GENERATED_IMAGE_IGNORED_DIRECTORY_NAMES.has(entry.name) || entry.name.startsWith(".") && entry.name !== ".dumi" && entry.name !== ".storybook";
26575
+ if (entry.isSymbolicLink() && isIgnoredDirectoryName) continue;
26576
+ if (entry.isSymbolicLink()) return null;
26577
+ if (entry.isDirectory()) {
26578
+ if (isIgnoredDirectoryName) continue;
26579
+ pendingDirectories.push(absolutePath);
26580
+ continue;
26581
+ }
26582
+ if (!entry.isFile() || isTestlikeFilename(absolutePath)) continue;
26583
+ if (GENERATED_IMAGE_MDX_FILE_PATTERN.test(entry.name)) {
26584
+ hasOpaqueMdxConsumerSurface = true;
26585
+ continue;
26586
+ }
26587
+ if (!GENERATED_IMAGE_SOURCE_FILE_PATTERN.test(entry.name)) continue;
26588
+ if (GENERATED_IMAGE_DECLARATION_FILE_PATTERN.test(entry.name)) continue;
26589
+ sourceFilePaths.push(normalizeFilename(absolutePath));
26590
+ }
26591
+ }
26592
+ return {
26593
+ sourceFilePaths,
26594
+ hasOpaqueMdxConsumerSurface
26595
+ };
26596
+ };
26597
+ const getRuntimeModuleSource = (node) => {
26598
+ if (isNodeOfType(node, "ImportDeclaration")) {
26599
+ if (isTypeOnlyImport(node)) return null;
26600
+ return typeof node.source.value === "string" ? node.source.value : null;
26601
+ }
26602
+ if (isNodeOfType(node, "ExportNamedDeclaration")) {
26603
+ if (node.exportKind === "type" || isEverySpecifierInlineType(node.specifiers, "ExportSpecifier", "exportKind")) return null;
26604
+ return node.source && typeof node.source.value === "string" ? node.source.value : null;
26605
+ }
26606
+ if (isNodeOfType(node, "ExportAllDeclaration")) {
26607
+ if (node.exportKind === "type") return null;
26608
+ return typeof node.source.value === "string" ? node.source.value : null;
26609
+ }
26610
+ if (isNodeOfType(node, "ImportExpression")) return isNodeOfType(node.source, "Literal") && typeof node.source.value === "string" ? node.source.value : null;
26611
+ if (isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "require" && node.arguments.length === 1) {
26612
+ const source = node.arguments[0];
26613
+ return source && isNodeOfType(source, "Literal") && typeof source.value === "string" ? source.value : null;
26614
+ }
26615
+ return null;
26616
+ };
26617
+ const indexModuleSources = (module, consumerModulesByFilePath, resolvedSourcePathByNode, unresolvedRuntimeSources) => {
26618
+ walkAst(module.programNode, (node) => {
26619
+ const source = getRuntimeModuleSource(node);
26620
+ if (!source) return;
26621
+ const resolvedSourcePath = resolveModulePath(module.filePath, source);
26622
+ if (!resolvedSourcePath) {
26623
+ unresolvedRuntimeSources.add(source);
26624
+ return;
26625
+ }
26626
+ const normalizedSourcePath = normalizeFilename(resolvedSourcePath);
26627
+ resolvedSourcePathByNode.set(node, normalizedSourcePath);
26628
+ const consumerModules = consumerModulesByFilePath.get(normalizedSourcePath) ?? /* @__PURE__ */ new Set();
26629
+ consumerModules.add(module);
26630
+ consumerModulesByFilePath.set(normalizedSourcePath, consumerModules);
26631
+ });
26632
+ };
26633
+ const buildGeneratedImageProjectIndex = (rootDirectory, currentFilePath, currentProgramNode, currentScopes) => {
26634
+ const productionSourceFiles = listProductionSourceFiles(rootDirectory);
26635
+ if (!productionSourceFiles) return null;
26636
+ const modulesByFilePath = /* @__PURE__ */ new Map();
26637
+ for (const filePath of productionSourceFiles.sourceFilePaths) {
26638
+ if (filePath === currentFilePath) {
26639
+ modulesByFilePath.set(filePath, {
26640
+ filePath,
26641
+ programNode: currentProgramNode,
26642
+ scopes: currentScopes
26643
+ });
26644
+ continue;
26645
+ }
26646
+ const parsedProgram = parseSourceFile(filePath);
26647
+ if (!parsedProgram || !isNodeOfType(parsedProgram, "Program")) return null;
26648
+ modulesByFilePath.set(filePath, {
26649
+ filePath,
26650
+ programNode: parsedProgram,
26651
+ scopes: getGeneratedImageModuleScopes(parsedProgram)
26652
+ });
26653
+ }
26654
+ const consumerModulesByFilePath = /* @__PURE__ */ new Map();
26655
+ const resolvedSourcePathByNode = /* @__PURE__ */ new WeakMap();
26656
+ const unresolvedRuntimeSources = /* @__PURE__ */ new Set();
26657
+ for (const module of modulesByFilePath.values()) indexModuleSources(module, consumerModulesByFilePath, resolvedSourcePathByNode, unresolvedRuntimeSources);
26658
+ return {
26659
+ modulesByFilePath,
26660
+ consumerModulesByFilePath,
26661
+ resolvedSourcePathByNode,
26662
+ unresolvedRuntimeSources,
26663
+ hasOpaqueMdxConsumerSurface: productionSourceFiles.hasOpaqueMdxConsumerSurface
26664
+ };
26665
+ };
26666
+ //#endregion
26667
+ //#region src/plugin/utils/read-nearest-package-manifest.ts
26668
+ const cachedPackageDirectoryByFilename = /* @__PURE__ */ new Map();
26669
+ const cachedManifestByPackageDirectory = /* @__PURE__ */ new Map();
26670
+ const resetManifestCaches = () => {
26671
+ cachedPackageDirectoryByFilename.clear();
26672
+ cachedManifestByPackageDirectory.clear();
26673
+ };
26674
+ const findNearestPackageDirectory = (filename) => {
26675
+ if (!filename) return null;
26676
+ const fromCache = cachedPackageDirectoryByFilename.get(filename);
26677
+ if (fromCache !== void 0) {
26678
+ if (isProbeRecorderActive()) {
26679
+ let probedDirectory = path.dirname(filename);
26680
+ while (true) {
26681
+ recordExistenceProbe(path.join(probedDirectory, "package.json"));
26682
+ if (probedDirectory === fromCache) break;
26683
+ const parentDirectory = path.dirname(probedDirectory);
26684
+ if (parentDirectory === probedDirectory) break;
26685
+ probedDirectory = parentDirectory;
26686
+ }
26687
+ }
26688
+ return fromCache;
26689
+ }
26690
+ let currentDirectory = path.dirname(filename);
26691
+ while (true) {
26692
+ const candidatePackageJsonPath = path.join(currentDirectory, "package.json");
26693
+ recordExistenceProbe(candidatePackageJsonPath);
26694
+ let hasPackageJson = false;
26695
+ try {
26696
+ hasPackageJson = fs.statSync(candidatePackageJsonPath).isFile();
26697
+ } catch {
26698
+ hasPackageJson = false;
26699
+ }
26700
+ if (hasPackageJson) {
26701
+ cachedPackageDirectoryByFilename.set(filename, currentDirectory);
26702
+ return currentDirectory;
26703
+ }
26704
+ const parentDirectory = path.dirname(currentDirectory);
26705
+ if (parentDirectory === currentDirectory) {
26706
+ cachedPackageDirectoryByFilename.set(filename, null);
26707
+ return null;
26708
+ }
26709
+ currentDirectory = parentDirectory;
26710
+ }
26711
+ };
26712
+ const readNearestPackageManifest = (filename) => {
26713
+ const packageDirectory = findNearestPackageDirectory(filename);
26714
+ if (!packageDirectory) return null;
26715
+ return readPackageManifest(packageDirectory);
26716
+ };
26717
+ const readPackageManifest = (packageDirectory) => {
26718
+ const packageJsonPath = path.join(packageDirectory, "package.json");
26719
+ recordContentProbe(packageJsonPath);
26720
+ const cached = cachedManifestByPackageDirectory.get(packageDirectory);
26721
+ if (cached !== void 0) return cached;
26722
+ let manifest = null;
26723
+ try {
26724
+ const parsed = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
26725
+ if (typeof parsed === "object" && parsed !== null) manifest = parsed;
26726
+ } catch {
26727
+ manifest = null;
26728
+ }
26729
+ cachedManifestByPackageDirectory.set(packageDirectory, manifest);
26730
+ return manifest;
26731
+ };
26732
+ //#endregion
26733
+ //#region src/plugin/utils/is-exported-jsx-owned-by-generated-image-renderers.ts
26734
+ const getExportedSpecifierName = (specifier) => {
26735
+ const exported = specifier.exported;
26736
+ if (isNodeOfType(exported, "Identifier")) return exported.name;
26737
+ return isNodeOfType(exported, "Literal") && typeof exported.value === "string" ? exported.value : null;
26738
+ };
26739
+ const getImportedSpecifierName = (specifier) => {
26740
+ const local = specifier.local;
26741
+ if (isNodeOfType(local, "Identifier")) return local.name;
26742
+ return isNodeOfType(local, "Literal") && typeof local.value === "string" ? local.value : null;
26743
+ };
26744
+ const getImportSpecifierName = (specifier) => {
26745
+ if (isNodeOfType(specifier, "ImportDefaultSpecifier")) return "default";
26746
+ if (!isNodeOfType(specifier, "ImportSpecifier")) return null;
26747
+ const imported = specifier.imported;
26748
+ if (isNodeOfType(imported, "Identifier")) return imported.name;
26749
+ return isNodeOfType(imported, "Literal") && typeof imported.value === "string" ? imported.value : null;
26750
+ };
26751
+ const getDirectFunctionBindingIdentifier = (functionNode) => {
26752
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
26753
+ const functionValueRoot = findTransparentExpressionRoot(functionNode);
26754
+ const parent = functionValueRoot.parent;
26755
+ return isNodeOfType(parent, "VariableDeclarator") && parent.init === functionValueRoot && isNodeOfType(parent.id, "Identifier") ? parent.id : null;
26756
+ };
26757
+ const getExportNamesForFunction = (programNode, functionNode) => {
26758
+ const functionValueRoot = findTransparentExpressionRoot(functionNode);
26759
+ const bindingName = getDirectFunctionBindingIdentifier(functionNode)?.name ?? null;
26760
+ const exportedNames = /* @__PURE__ */ new Set();
26761
+ for (const statement of programNode.body) {
26762
+ if (isNodeOfType(statement, "ExportDefaultDeclaration")) {
26763
+ if (statement.declaration === functionValueRoot || bindingName && isNodeOfType(statement.declaration, "Identifier") && statement.declaration.name === bindingName) exportedNames.add("default");
26764
+ continue;
26765
+ }
26766
+ if (!isNodeOfType(statement, "ExportNamedDeclaration")) continue;
26767
+ const declaration = statement.declaration;
26768
+ if (declaration === functionValueRoot && bindingName) exportedNames.add(bindingName);
26769
+ if (declaration && isNodeOfType(declaration, "VariableDeclaration")) {
26770
+ for (const declarator of declaration.declarations) if (declarator.init === functionValueRoot && isNodeOfType(declarator.id, "Identifier")) exportedNames.add(declarator.id.name);
26771
+ }
26772
+ if (!bindingName || statement.source) continue;
26773
+ for (const specifier of statement.specifiers) {
26774
+ if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
26775
+ if (getImportedSpecifierName(specifier) !== bindingName) continue;
26776
+ const exportedName = getExportedSpecifierName(specifier);
26777
+ if (exportedName) exportedNames.add(exportedName);
26778
+ }
26779
+ }
26780
+ return [...exportedNames];
26781
+ };
26782
+ const isTransparentGeneratedImageValueFlow = (expression, target) => {
26783
+ let current = findTransparentExpressionRoot(expression);
26784
+ while (current !== target) {
26785
+ const parent = current.parent;
26786
+ if (!parent) return false;
26787
+ 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;
26788
+ current = findTransparentExpressionRoot(parent);
26789
+ }
26790
+ return true;
26791
+ };
26792
+ const isInsideGeneratedImageRendererArgument = (expression, scopes) => {
26793
+ let cursor = expression;
26794
+ while (cursor?.parent) {
26795
+ const parent = cursor.parent;
26796
+ if (isFunctionLike$1(parent)) return false;
26797
+ if (isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression")) {
26798
+ if (parent.arguments[0] && isTransparentGeneratedImageValueFlow(expression, parent.arguments[0]) && isGeneratedImageRendererCall(parent, scopes)) return true;
26799
+ }
26800
+ cursor = parent;
26801
+ }
26802
+ return false;
26803
+ };
26804
+ const getInvokedExpression = (identifier) => {
26805
+ const referenceExpression = findTransparentExpressionRoot(identifier);
26806
+ const parent = referenceExpression.parent;
26807
+ if (isNodeOfType(parent, "CallExpression") && parent.callee === referenceExpression) return parent;
26808
+ if (isNodeOfType(parent, "TaggedTemplateExpression") && parent.tag === referenceExpression) return parent;
26809
+ if ((isNodeOfType(parent, "JSXOpeningElement") || isNodeOfType(parent, "JSXClosingElement")) && parent.name === identifier) {
26810
+ const element = parent.parent;
26811
+ return isNodeOfType(element, "JSXElement") ? element : null;
26812
+ }
26813
+ return null;
26814
+ };
26815
+ const getForwardingFunction = (expression) => {
26816
+ const enclosingFunction = findEnclosingFunction$1(expression);
26817
+ if (!enclosingFunction) return null;
26818
+ if (isNodeOfType(enclosingFunction, "ArrowFunctionExpression") && !isNodeOfType(enclosingFunction.body, "BlockStatement") && isTransparentGeneratedImageValueFlow(expression, enclosingFunction.body)) return enclosingFunction;
26819
+ let cursor = expression.parent;
26820
+ while (cursor && cursor !== enclosingFunction) {
26821
+ if (isFunctionLike$1(cursor)) return null;
26822
+ if (isNodeOfType(cursor, "ReturnStatement") && cursor.argument && isTransparentGeneratedImageValueFlow(expression, cursor.argument)) return enclosingFunction;
26823
+ cursor = cursor.parent;
26824
+ }
26825
+ return null;
26826
+ };
26827
+ const enqueueExport = (state, filePath, exportedName) => {
26828
+ state.pendingExports.push({
26829
+ filePath: normalizeFilename(filePath),
26830
+ exportedName
26831
+ });
26832
+ };
26833
+ const classifyInvokedExpression = (module, expression, state) => {
26834
+ if (isInsideGeneratedImageRendererArgument(expression, module.scopes)) {
26835
+ state.didReachRenderer = true;
26836
+ return true;
26837
+ }
26838
+ const forwardingFunction = getForwardingFunction(expression);
26839
+ if (!forwardingFunction) return false;
26840
+ const exportedNames = getExportNamesForFunction(module.programNode, forwardingFunction);
26841
+ if (exportedNames.length === 0) return false;
26842
+ for (const exportedName of exportedNames) enqueueExport(state, module.filePath, exportedName);
26843
+ return true;
26844
+ };
26845
+ const classifySymbolReferences = (module, symbol, state, visitedSymbolIds) => {
26846
+ if (visitedSymbolIds.has(symbol.id)) return true;
26847
+ visitedSymbolIds.add(symbol.id);
26848
+ for (const reference of symbol.references) {
26849
+ if (reference.flag !== "read") return false;
26850
+ state.currentExportWasUsed = true;
26851
+ const identifier = reference.identifier;
26852
+ const invokedExpression = getInvokedExpression(identifier);
26853
+ if (invokedExpression) {
26854
+ if (!classifyInvokedExpression(module, invokedExpression, state)) return false;
26855
+ continue;
26856
+ }
26857
+ const parent = identifier.parent;
26858
+ if (isNodeOfType(parent, "ExportSpecifier") && parent.local === identifier) {
26859
+ const exportedName = getExportedSpecifierName(parent);
26860
+ if (!exportedName) return false;
26861
+ enqueueExport(state, module.filePath, exportedName);
26862
+ continue;
26863
+ }
26864
+ if (isNodeOfType(parent, "ExportDefaultDeclaration")) {
26865
+ enqueueExport(state, module.filePath, "default");
26866
+ continue;
26867
+ }
26868
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === identifier && isNodeOfType(parent.id, "Identifier") && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const") {
26869
+ const aliasSymbol = module.scopes.symbolFor(parent.id);
26870
+ if (!aliasSymbol || !classifySymbolReferences(module, aliasSymbol, state, visitedSymbolIds)) return false;
26871
+ continue;
26872
+ }
26873
+ return false;
26874
+ }
26875
+ return true;
26876
+ };
26877
+ const classifyNamespaceImportReferences = (module, symbol, exportedName, state) => {
26878
+ for (const reference of symbol.references) {
26879
+ if (reference.flag !== "read") return false;
26880
+ const identifier = reference.identifier;
26881
+ const parent = identifier.parent;
26882
+ if (!isNodeOfType(parent, "MemberExpression") || parent.object !== identifier) return false;
26883
+ const propertyName = getStaticPropertyName(parent);
26884
+ if (propertyName === null) return false;
26885
+ if (propertyName !== exportedName) continue;
26886
+ state.currentExportWasUsed = true;
26887
+ const invokedExpression = getInvokedExpression(parent);
26888
+ if (!invokedExpression || !classifyInvokedExpression(module, invokedExpression, state)) return false;
26889
+ }
26890
+ return true;
26891
+ };
26892
+ const classifyImportsFromExport = (module, exportIdentity, state) => {
26893
+ for (const statement of module.programNode.body) {
26894
+ if (isNodeOfType(statement, "ImportDeclaration")) {
26895
+ if (state.projectIndex.resolvedSourcePathByNode.get(statement) !== exportIdentity.filePath) continue;
26896
+ if (statement.importKind === "type") continue;
26897
+ for (const specifier of statement.specifiers) {
26898
+ if (isNodeOfType(specifier, "ImportSpecifier") && specifier.importKind === "type") continue;
26899
+ if (isNodeOfType(specifier, "ImportNamespaceSpecifier")) {
26900
+ const namespaceSymbol = module.scopes.symbolFor(specifier.local);
26901
+ if (!namespaceSymbol || !classifyNamespaceImportReferences(module, namespaceSymbol, exportIdentity.exportedName, state)) return false;
26902
+ continue;
26903
+ }
26904
+ if (getImportSpecifierName(specifier) !== exportIdentity.exportedName) continue;
26905
+ const symbol = module.scopes.symbolFor(specifier.local);
26906
+ if (!symbol || !classifySymbolReferences(module, symbol, state, /* @__PURE__ */ new Set())) return false;
26907
+ }
26908
+ continue;
26909
+ }
26910
+ if ((isNodeOfType(statement, "ExportNamedDeclaration") || isNodeOfType(statement, "ExportAllDeclaration")) && statement.source && state.projectIndex.resolvedSourcePathByNode.get(statement) === exportIdentity.filePath) {
26911
+ if (isNodeOfType(statement, "ExportAllDeclaration")) {
26912
+ if (statement.exported) return false;
26913
+ state.currentExportWasUsed = true;
26914
+ enqueueExport(state, module.filePath, exportIdentity.exportedName);
26915
+ continue;
26916
+ }
26917
+ for (const specifier of statement.specifiers) {
26918
+ if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
26919
+ if (getImportedSpecifierName(specifier) !== exportIdentity.exportedName) continue;
26920
+ const exportedName = getExportedSpecifierName(specifier);
26921
+ if (!exportedName) return false;
26922
+ state.currentExportWasUsed = true;
26923
+ enqueueExport(state, module.filePath, exportedName);
26924
+ }
26925
+ }
26926
+ }
26927
+ return true;
26928
+ };
26929
+ const hasOpaqueDynamicImportOfExport = (module, exportIdentity, projectIndex) => {
26930
+ let isOpaque = false;
26931
+ walkAst(module.programNode, (node) => {
26932
+ if (isOpaque) return false;
26933
+ if (isNodeOfType(node, "ImportExpression")) {
26934
+ if (projectIndex.resolvedSourcePathByNode.get(node) === exportIdentity.filePath) {
26935
+ isOpaque = true;
26936
+ return false;
26937
+ }
26938
+ }
26939
+ if (isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "require" && node.arguments.length === 1) {
26940
+ if (projectIndex.resolvedSourcePathByNode.get(node) === exportIdentity.filePath) {
26941
+ isOpaque = true;
26942
+ return false;
26943
+ }
26944
+ }
26945
+ });
26946
+ return isOpaque;
26947
+ };
26948
+ const classifyLocalExportReferences = (module, exportIdentity, state) => {
26949
+ const exportedValue = findExportedValue(module.programNode, exportIdentity.exportedName);
26950
+ if (!exportedValue || !isFunctionLike$1(exportedValue)) return true;
26951
+ const bindingIdentifier = getDirectFunctionBindingIdentifier(exportedValue);
26952
+ if (!bindingIdentifier) return true;
26953
+ const symbol = module.scopes.symbolFor(bindingIdentifier);
26954
+ return symbol ? classifySymbolReferences(module, symbol, state, /* @__PURE__ */ new Set()) : false;
26955
+ };
26956
+ const hasOpaqueWorkspacePackageConsumer = (projectIndex, exportIdentity) => {
26957
+ const packageName = readNearestPackageManifest(exportIdentity.filePath)?.name;
26958
+ if (typeof packageName !== "string" || packageName.length === 0) return false;
26959
+ for (const unresolvedSource of projectIndex.unresolvedRuntimeSources) if (unresolvedSource === packageName || unresolvedSource.startsWith(`${packageName}/`)) return true;
26960
+ return false;
26961
+ };
26962
+ const createExportedJsxGeneratedImageOwnershipAnalyzer = (context) => {
26963
+ const filename = context.filename ? normalizeFilename(context.filename) : "";
26964
+ const rootDirectorySetting = getReactDoctorStringSetting(context.settings, "rootDirectory");
26965
+ const rootDirectory = rootDirectorySetting ? normalizeFilename(rootDirectorySetting).replace(/\/$/, "") : "";
26966
+ const isFileInsideRoot = Boolean(filename && rootDirectory) && (filename === rootDirectory || filename.startsWith(`${rootDirectory}/`));
26967
+ let projectIndex;
26968
+ return (jsxNode) => {
26969
+ if (!isFileInsideRoot) return false;
26970
+ const programNode = findProgramRoot(jsxNode);
26971
+ const enclosingFunction = findEnclosingFunction$1(jsxNode);
26972
+ if (!programNode || !enclosingFunction) return false;
26973
+ const initialExportNames = getExportNamesForFunction(programNode, enclosingFunction);
26974
+ if (initialExportNames.length === 0) return false;
26975
+ if (projectIndex === void 0) projectIndex = buildGeneratedImageProjectIndex(rootDirectory, filename, programNode, context.scopes);
26976
+ if (!projectIndex || projectIndex.hasOpaqueMdxConsumerSurface) return false;
26977
+ const state = {
26978
+ projectIndex,
26979
+ pendingExports: initialExportNames.map((exportedName) => ({
26980
+ filePath: filename,
26981
+ exportedName
26982
+ })),
26983
+ visitedExportKeys: /* @__PURE__ */ new Set(),
26984
+ currentExportWasUsed: false,
26985
+ didReachRenderer: false
26986
+ };
26987
+ while (state.pendingExports.length > 0) {
26988
+ const exportIdentity = state.pendingExports.pop();
26989
+ if (!exportIdentity) continue;
26990
+ const exportKey = `${exportIdentity.filePath}\0${exportIdentity.exportedName}`;
26991
+ if (state.visitedExportKeys.has(exportKey)) continue;
26992
+ state.visitedExportKeys.add(exportKey);
26993
+ state.currentExportWasUsed = false;
26994
+ if (hasOpaqueWorkspacePackageConsumer(projectIndex, exportIdentity)) return false;
26995
+ const ownerModule = projectIndex.modulesByFilePath.get(exportIdentity.filePath);
26996
+ if (!ownerModule || !classifyLocalExportReferences(ownerModule, exportIdentity, state)) return false;
26997
+ const consumerModules = projectIndex.consumerModulesByFilePath.get(exportIdentity.filePath) ?? [];
26998
+ for (const module of consumerModules) {
26999
+ if (module.filePath === exportIdentity.filePath) continue;
27000
+ if (hasOpaqueDynamicImportOfExport(module, exportIdentity, projectIndex)) return false;
27001
+ if (!classifyImportsFromExport(module, exportIdentity, state)) return false;
27002
+ }
27003
+ if (!state.currentExportWasUsed) return false;
27004
+ }
27005
+ return state.didReachRenderer;
27006
+ };
27007
+ };
27008
+ //#endregion
26489
27009
  //#region src/plugin/rules/nextjs/nextjs-no-img-element.ts
26490
27010
  const NON_OPTIMIZABLE_SRC_PREFIX_PATTERN = /^\s*(data:|blob:)/i;
26491
27011
  const GENERATED_URL_NAME_PATTERN = /(data|object|blob)_?url/i;
@@ -26588,9 +27108,20 @@ const nextjsNoImgElement = defineRule({
26588
27108
  recommendation: "Use `next/image` so users get optimized formats, responsive srcsets, and lazy loading instead of oversized image downloads.",
26589
27109
  create: (context) => {
26590
27110
  if (isGeneratedImageRenderContext(context)) return {};
27111
+ const generatedImageOwnershipByFunction = /* @__PURE__ */ new WeakMap();
27112
+ const isExportedJsxGeneratedImageOwned = createExportedJsxGeneratedImageOwnershipAnalyzer(context);
26591
27113
  return { JSXOpeningElement(node) {
26592
27114
  if (resolveJsxElementType(node) !== "img") return;
26593
27115
  if (isGeneratedImageRenderContext(context, node)) return;
27116
+ const enclosingFunction = findEnclosingFunction$1(node);
27117
+ if (enclosingFunction) {
27118
+ let isGeneratedImageOwned = generatedImageOwnershipByFunction.get(enclosingFunction);
27119
+ if (isGeneratedImageOwned === void 0) {
27120
+ isGeneratedImageOwned = isExportedJsxGeneratedImageOwned(node);
27121
+ generatedImageOwnershipByFunction.set(enclosingFunction, isGeneratedImageOwned);
27122
+ }
27123
+ if (isGeneratedImageOwned) return;
27124
+ }
26594
27125
  const programRoot = findProgramRoot(node);
26595
27126
  if (programRoot && hasEmailTemplateImport(programRoot)) return;
26596
27127
  const srcAttribute = findJsxAttribute(node.attributes, "src");
@@ -31030,72 +31561,6 @@ const isReactNativeDependencyName = (dependencyName) => {
31030
31561
  return false;
31031
31562
  };
31032
31563
  //#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
31564
  //#region src/plugin/utils/classify-package-platform.ts
31100
31565
  const WEB_FRAMEWORK_DEPENDENCY_NAMES = new Set([
31101
31566
  "next",
@@ -42164,7 +42629,7 @@ const getRefBindingProvenance = (analysis, receiver, isReactUseRefCall) => {
42164
42629
  variables
42165
42630
  };
42166
42631
  };
42167
- const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactUseRefCall) => {
42632
+ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactUseRefCall, isReactUseEffectCall) => {
42168
42633
  const callee = stripParenExpression(callExpression.callee);
42169
42634
  if (!isNodeOfType(callee, "MemberExpression") || getStaticMemberPropertyName(callee) !== "current") return null;
42170
42635
  const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(callee.object), isReactUseRefCall);
@@ -42172,7 +42637,9 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
42172
42637
  const { refCall, variables } = bindingProvenance;
42173
42638
  const componentFunction = findEnclosingFunction$1(effectCall);
42174
42639
  if (!componentFunction || !isFunctionLike$1(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
42175
- if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
42640
+ const notificationEffectStatement = getDirectComponentBodyStatement(effectCall, componentFunction.body);
42641
+ const notificationEffectRoot = findTransparentExpressionRoot(effectCall);
42642
+ if (!notificationEffectStatement || !isNodeOfType(notificationEffectStatement, "ExpressionStatement") || notificationEffectRoot.parent !== notificationEffectStatement || !isReactUseEffectCall(effectCall)) return null;
42176
42643
  const callbackPropNames = /* @__PURE__ */ new Set();
42177
42644
  const initializer = refCall.arguments?.[0];
42178
42645
  const initializerCallbackName = initializer ? getParentCallbackPropName(analysis, initializer) : null;
@@ -42190,9 +42657,20 @@ const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactU
42190
42657
  const assignment = getRefMemberAssignment(identifier);
42191
42658
  if (!assignment) continue;
42192
42659
  const assignmentStatement = findTransparentExpressionRoot(assignment).parent;
42193
- if (assignment.operator !== "=" || !assignmentStatement || !isNodeOfType(assignmentStatement, "ExpressionStatement") || assignmentStatement.parent !== componentFunction.body) return null;
42660
+ if (assignment.operator !== "=" || !assignmentStatement || !isNodeOfType(assignmentStatement, "ExpressionStatement")) return null;
42194
42661
  const assignedCallbackName = getParentCallbackPropName(analysis, assignment.right);
42195
42662
  if (!assignedCallbackName) return null;
42663
+ if (assignmentStatement.parent !== componentFunction.body) {
42664
+ if (!initializerCallbackName || assignedCallbackName !== initializerCallbackName) return null;
42665
+ const assignmentEffectBody = assignmentStatement.parent;
42666
+ if (!assignmentEffectBody || !isNodeOfType(assignmentEffectBody, "BlockStatement")) return null;
42667
+ const assignmentEffectFunction = assignmentEffectBody.parent;
42668
+ if (!assignmentEffectFunction || !isFunctionLike$1(assignmentEffectFunction) || assignmentEffectFunction.body !== assignmentEffectBody) return null;
42669
+ const assignmentEffectCall = findTransparentExpressionRoot(assignmentEffectFunction).parent;
42670
+ if (!assignmentEffectCall || !isNodeOfType(assignmentEffectCall, "CallExpression") || !isReactUseEffectCall(assignmentEffectCall) || getEffectFn(analysis, assignmentEffectCall) !== assignmentEffectFunction) return null;
42671
+ const assignmentEffectStatement = getDirectComponentBodyStatement(assignmentEffectCall, componentFunction.body);
42672
+ if (!assignmentEffectStatement || !isNodeOfType(assignmentEffectStatement, "ExpressionStatement") || assignmentEffectCall.parent !== assignmentEffectStatement || assignmentEffectStatement.range[0] >= notificationEffectStatement.range[0]) return null;
42673
+ }
42196
42674
  callbackPropNames.add(assignedCallbackName);
42197
42675
  }
42198
42676
  return callbackPropNames.size > 0 ? { callbackPropNames } : null;
@@ -42405,6 +42883,10 @@ const noPassDataToParent = defineRule({
42405
42883
  allowGlobalReactNamespace: true,
42406
42884
  allowUnboundBareCalls: true
42407
42885
  });
42886
+ const isReactUseEffectCall = (node) => isReactApiCall(node, "useEffect", context.scopes, {
42887
+ allowGlobalReactNamespace: true,
42888
+ allowUnboundBareCalls: true
42889
+ });
42408
42890
  return { CallExpression(node) {
42409
42891
  if (!isUseEffect(node)) return;
42410
42892
  const analysis = getProgramAnalysis(node);
@@ -42417,7 +42899,7 @@ const noPassDataToParent = defineRule({
42417
42899
  for (const ref of effectFnRefs) {
42418
42900
  const callExpr = getCallExpr(ref);
42419
42901
  if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
42420
- const callbackRefProvenance = getCallbackRefProvenance(analysis, node, callExpr, isReactUseRefCall);
42902
+ const callbackRefProvenance = getCallbackRefProvenance(analysis, node, callExpr, isReactUseRefCall, isReactUseEffectCall);
42421
42903
  if (isRefCall(analysis, ref) && !callbackRefProvenance) continue;
42422
42904
  if (!isSynchronous(ref.identifier, effectFn)) continue;
42423
42905
  const calleeNode = unwrapChainExpression(callExpr.callee);
@@ -70079,6 +70561,7 @@ const CROSS_FILE_RULE_IDS = new Set([
70079
70561
  "exhaustive-deps",
70080
70562
  "no-barrel-import",
70081
70563
  "nextjs-missing-metadata",
70564
+ "nextjs-no-img-element",
70082
70565
  "nextjs-no-use-search-params-without-suspense",
70083
70566
  "no-dynamic-import-path",
70084
70567
  "no-full-lodash-import",
@@ -70310,12 +70793,12 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
70310
70793
  ]);
70311
70794
  /**
70312
70795
  * Cross-file rules whose dependency set CANNOT be soundly bounded — they are
70313
- * excluded from fingerprinting and re-lint every file on every scan. Empty
70314
- * today; a new cross-file rule must be added either here or to
70796
+ * excluded from fingerprinting and re-lint every file on every scan. A new
70797
+ * cross-file rule must be added either here or to
70315
70798
  * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
70316
70799
  * partition), forcing a conscious classification.
70317
70800
  */
70318
- const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["only-export-components"]);
70801
+ const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["nextjs-no-img-element", "only-export-components"]);
70319
70802
  /**
70320
70803
  * Runs the collectors for `ruleIds` over one file and returns every
70321
70804
  * filesystem probe they made — the file's cross-file dependency set.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.7.9-dev.61111f1",
3
+ "version": "0.7.9-dev.6325f9c",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",