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.d.ts +2 -2
- package/dist/index.js +1175 -199
- package/package.json +1 -1
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/
|
|
1823
|
-
const
|
|
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,
|
|
2042
|
-
if (!
|
|
2043
|
-
|
|
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) ?? "") &&
|
|
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
|
|
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 &&
|
|
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 (
|
|
20077
|
-
return
|
|
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 (
|
|
20085
|
-
else if (
|
|
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 &&
|
|
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((
|
|
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
|
|
35421
|
-
const
|
|
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)
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
43890
|
-
if (
|
|
43891
|
-
|
|
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
|
-
|
|
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(
|
|
43951
|
-
const { left, right } =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
70331
|
-
*
|
|
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.
|