@getcronit/pylon
Version:

2,560 lines • 91.3 kB
JavaScript
import "./chunk-R6NNLVZJ.js";
// src/plugins/use-pages/build/index.ts
import chokidar from "chokidar";
import esbuild from "esbuild";
import fs6 from "fs/promises";
import path4 from "path";
// src/plugins/use-pages/build/app-utils.ts
import fs from "fs";
import path from "path";
var PAGES_DIR = "./pages";
function formatSegment(segment) {
let sanitized = segment;
if (sanitized.startsWith("[...") && sanitized.endsWith("]")) {
const param = sanitized.slice(4, -1);
sanitized = "CatchAll" + param.charAt(0).toUpperCase() + param.slice(1);
} else if (sanitized.startsWith("[") && sanitized.endsWith("]")) {
sanitized = sanitized.slice(1, -1);
}
return sanitized.charAt(0).toUpperCase() + sanitized.slice(1);
}
function getLayoutComponentName(filePath) {
const segments = filePath.replace(PAGES_DIR, "").replace(/\\/g, "/").replace(/layout\.tsx$/, "").split("/").filter(Boolean);
return segments.map(formatSegment).join("") + "Layout";
}
function getPageComponentName(filePath) {
const segments = filePath.replace(PAGES_DIR, "").replace(/\\/g, "/").replace(/page\.tsx$/, "").split("/").filter(Boolean);
return segments.map(formatSegment).join("") + "Page";
}
function convertToDynamicRoute(segment) {
if (segment.startsWith("[...") && segment.endsWith("]")) return "*";
if (segment.startsWith("[") && segment.endsWith("]"))
return `:${segment.slice(1, -1)}`;
return segment;
}
function processLayoutItem(relativePath, importPath, route, context) {
const layoutComponentName = getLayoutComponentName(relativePath);
context.imports.push(`import ${layoutComponentName} from ${importPath};`);
const componentName = layoutComponentName === "Layout" ? `RootLayout` : `${layoutComponentName}`;
const catchAllParam = relativePath.match(/\[\.\.\.(.+)\]/)?.[1];
const paramMatches = [...relativePath.matchAll(/\[(.+?)\]/g)].map(
(m) => m[1].replace("...", "")
);
route.id = componentName;
route.Component = `withRouteData((props) => <${componentName} children={<Outlet />} {...props} />, "${componentName}", ${catchAllParam ? `"${catchAllParam}"` : "undefined"})`;
route.shouldRevalidate = `({ currentParams, nextParams, formData, defaultShouldRevalidate }) => {
// Revalidate if a form was submitted (standard behavior)
if (formData) return true;
// List of params this layout segment depends on
const relevantKeys = ${JSON.stringify(paramMatches)};
// Check if any relevant URL parameter changed
const hasParamChanged = relevantKeys.some(key =>
JSON.stringify(currentParams[key]) !== JSON.stringify(nextParams[key])
);
// If it's the RootLayout, we might only want to revalidate on hard refreshes
// or specific global triggers. Otherwise, follow param changes.
return hasParamChanged || (relevantKeys.length === 0 && defaultShouldRevalidate);
}`;
if (route.path === "/") {
route.errorElement = "<ErrorElement standalone={true} />";
}
route.HydrateFallback = "HydrateFallback";
}
function processPageItem(relativePath, importPath, route) {
const catchAllParam = relativePath.match(/\[\.\.\.(.+)\]/)?.[1];
const pageComponentName = getPageComponentName(relativePath);
route.children.push({
id: pageComponentName,
path: void 0,
index: true,
errorElement: "<ErrorElement standalone={false} />",
lazy: `async () => {const i = await import(${importPath}).catch(() => {window.location.reload()}); return {Component: withRouteData(i.default, "${pageComponentName}", ${catchAllParam ? `"${catchAllParam}"` : "undefined"})}}`,
HydrateFallback: "HydrateFallback"
});
}
function optimizeRouteStructure(route, hasLayout) {
if (!hasLayout && route.children?.length === 1 && route.children[0].path === "*") {
const child = route.children[0];
const currentPath = route.path === "/" ? "" : route.path;
Object.assign(route, child);
route.path = currentPath ? `${currentPath}/*` : "*";
delete route.children;
}
if (route.path === "*" && !hasLayout && route.children?.length === 1) {
const child = route.children[0];
const currentPath = route.path;
Object.assign(route, child);
route.path = currentPath;
delete route.index;
delete route.children;
}
if (route.path === "*" && hasLayout && route.children) {
const pageChild = route.children.find((child) => child.index);
if (pageChild) {
delete pageChild.index;
pageChild.path = "*";
}
}
}
function scanDirectory(directory, context, basePath = "") {
const items = fs.readdirSync(directory, { withFileTypes: true });
const route = { path: basePath || "/", children: [] };
let hasLayout = false;
let pageFound = false;
for (const item of items) {
const itemPath = path.join(directory, item.name);
const relativePath = path.join(basePath, item.name).replace(/\\/g, "/");
const importPath = `"./${path.join("..", PAGES_DIR, relativePath).replace(/\.tsx$/, "")}"`;
if (item.isDirectory()) {
const childRoute = scanDirectory(itemPath, context, relativePath);
if (childRoute) {
route.children.push(childRoute);
}
} else if (item.name === "layout.tsx") {
processLayoutItem(relativePath, importPath, route, context);
hasLayout = true;
} else if (item.name === "page.tsx") {
processPageItem(relativePath, importPath, route);
pageFound = true;
}
}
if (route.path) {
const segments = route.path.split("/").map((segment) => convertToDynamicRoute(segment)).filter(Boolean);
const fullPath = segments.length > 0 ? `/${segments.join("/")}` : "/";
route.path = segments[segments.length - 1] || "/";
if (hasLayout || pageFound) {
context.routeSlugs.push(fullPath);
}
}
if (hasLayout) {
const childNotFoundRoute = {
id: `${route.id}/NotFound`,
path: "*",
element: "<NotFoundPage standalone={false} />",
loader: '() => { console.log("EXECUTED LOADER 404"); return new Response("Not Found", { status: 404 }) }'
};
if (!route.children) {
route.children = [];
}
route.children.push(childNotFoundRoute);
}
optimizeRouteStructure(route, hasLayout);
if (hasLayout || route.lazy || route.children && route.children.length > 0) {
return route;
}
return null;
}
function serialize(obj, parentKey) {
if (Array.isArray(obj)) {
return `[${obj.map(serialize).join(", ")}]`;
} else if (obj && typeof obj === "object") {
const entries = Object.entries(obj).map(
([key, value]) => `${JSON.stringify(key)}: ${serialize(value, key)}`
);
return `{${entries.join(", ")}}`;
} else if (typeof obj === "string") {
if (parentKey === "lazy" || parentKey === "loader" || parentKey === "shouldRevalidate" || parentKey === "Component" || parentKey === "element" || parentKey === "errorElement" || parentKey === "HydrateFallback") {
return obj;
}
return JSON.stringify(obj);
} else {
return String(obj);
}
}
function generateRouteFileContent(context, rootRoute, notFoundRoute) {
return `${context.imports.join("\n")}
import {useMemo, Suspense} from 'react'
import {__PYLON_ROUTER_INTERNALS_DO_NOT_USE, __PYLON_INTERNALS_DO_NOT_USE, GlobalErrorPage, StatusPage} from '@getcronit/pylon/pages'
const Outlet = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.Outlet
const ErrorElement: React.FC<{standalone: boolean}> = ({standalone}) => {
// Destructure for cleaner code
const { useRouteError, isRouteErrorResponse, Navigate } = __PYLON_ROUTER_INTERNALS_DO_NOT_USE;
const error = useRouteError();
console.error(error);
let message = 'An unexpected error occurred.';
// 1. Handle raw Response redirects (e.g., thrown directly during client render)
if (
error instanceof Response &&
error.status >= 300 &&
error.status < 400 &&
error.headers.get('Location')
) {
return <Navigate to={error.headers.get('Location')!} replace />;
}
// 2. Use the official router check for handled data errors (404, 401, etc.)
// We check \`error.internal\` as a fallback just in case the server formatting is slightly off
const isRouteError = isRouteErrorResponse(error)
if (isRouteError) {
try {
const errorData = (error as any).data;
const rawMessage = typeof errorData === 'string' ? errorData : errorData?.message;
if (rawMessage) {
try {
const parsed = JSON.parse(rawMessage);
message = parsed.message || rawMessage;
} catch (e) {
message = rawMessage;
}
}
} catch (e) {}
// Only render the StatusPage for non-500 HTTP errors
if ((error as any).status !== 500) {
return (
<StatusPage
code={(error as any).status}
message={message}
error={error}
standalone={standalone}
/>
);
}
}
// 3. Fallback for standard code crashes (e.g., TypeError) and explicit 500s
const displayError = error instanceof Error
? error
: new Error(
message ||
(error && typeof error === 'object' && ((error as any).message || (error as any).statusText)) ||
'A critical error occurred'
);
return <GlobalErrorPage error={displayError as any} standalone={standalone} />;
}
const HydrateFallback = () => {
return <div>Loading...</div>
}
// Replaced pageClientCache with central cache in DataClientProvider
function withRouteData(Component: React.ComponentType<any>, id?: string, catchAllParam?: string) {
return function WithRouteDataWrapper(props: any) {
const dataClient = __PYLON_INTERNALS_DO_NOT_USE.useDataClient()
const pagesContext = dataClient.pagesContext
const location = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useLocation()
const [searchParams] = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useSearchParams()
const searchParamsObject = useMemo(() => Object.fromEntries(searchParams.entries()), [searchParams])
const reactRouterParams = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.useParams()
const params = useMemo(() => {
const params: Record<string, string | string[] | undefined> = reactRouterParams
if (catchAllParam && reactRouterParams['*']) {
params[catchAllParam] = reactRouterParams['*']?.split('/')
}
return params
}, [reactRouterParams, catchAllParam])
const pageProps = useMemo(() => {
return {
path: location.pathname,
params,
searchParams: searchParamsObject,
context: pagesContext,
}
}, [location.pathname, params, searchParamsObject, pagesContext])
return (
<__PYLON_INTERNALS_DO_NOT_USE.RouteDataProvider props={pageProps} name={id}>
<Component {...(props as any)} {...pageProps} children={<Outlet />} />
</__PYLON_INTERNALS_DO_NOT_USE.RouteDataProvider>
)
};
}
const RootLayout = (props: { children: React.ReactNode; [key: string]: any }) => {
const manifest = (globalThis as any).__PYLON_MANIFEST__;
return (
<Layout {...props}>
<meta charSet="utf-8" />
{manifest?.['index.css'] && <link rel="stylesheet" href={manifest['index.css']} precedence="high" />}
{manifest?.['app.css'] && <link rel="stylesheet" href={manifest['app.css']} precedence="high" />}
{props.children}
</Layout>
)
}
const NotFoundPage: React.FC<{standalone: boolean}> = ({standalone = false}) => {
return <StatusPage code={404} message="The page you are looking for does not exist." standalone={standalone} />
}
const routes = ${serialize([rootRoute, notFoundRoute].filter(Boolean))}
export default routes
`;
}
function makeAppFiles() {
const context = { imports: [], routeSlugs: [] };
const rootRoute = scanDirectory(PAGES_DIR, context);
const notFoundRoute = {
id: "NotFound",
path: "*",
element: "<NotFoundPage standalone={true} />",
loader: '() => { return new Response("Not Found", { status: 404 }) }'
};
const routes = generateRouteFileContent(context, rootRoute, notFoundRoute);
const slugs = `export default ${JSON.stringify(context.routeSlugs, null, 2)}`;
return {
routes,
slugs
};
}
// src/plugins/use-pages/build/plugins/external-esm-plugin.ts
import escapeStringRegexp from "escape-string-regexp";
var NAME = "esm-externals";
var NAMESPACE = NAME;
function makeFilter(externals) {
return new RegExp(
"^(" + externals.map(escapeStringRegexp).join("|") + ")(\\/.*)?$"
// TODO support for query strings?
);
}
var esmExternalsPlugin = (externals) => {
return {
name: NAME,
setup(build2) {
const filter = makeFilter(externals);
build2.onResolve({ filter: /.*/, namespace: NAMESPACE }, (args) => {
return {
path: args.path,
external: true
};
});
build2.onResolve({ filter }, (args) => {
return {
path: args.path,
namespace: NAMESPACE
};
});
build2.onLoad({ filter: /.*/, namespace: NAMESPACE }, (args) => {
return {
contents: `export * as default from ${JSON.stringify(
args.path
)}; export * from ${JSON.stringify(args.path)};`
};
});
}
};
};
// src/plugins/use-pages/build/plugins/image-plugin.ts
import { createHash } from "crypto";
import path2 from "path";
import fs2 from "fs/promises";
var imagePlugin = {
name: "image-plugin",
setup(build2) {
const outdir = build2.initialOptions.outdir;
const publicPath = build2.initialOptions.publicPath;
if (!outdir || !publicPath) {
throw new Error("outdir and publicPath must be set in esbuild options");
}
build2.onResolve({ filter: /\.(png|jpe?g)$/ }, async (args) => {
const filePath = path2.resolve(args.resolveDir, args.path);
const fileName = path2.basename(filePath);
const extname = path2.extname(filePath);
const hash = createHash("md5").update(filePath + await fs2.readFile(filePath)).digest("hex").slice(0, 8);
const newFilename = `${fileName}-${hash}${extname}`;
const newFilePath = path2.join(outdir, "media", newFilename);
await fs2.mkdir(path2.dirname(newFilePath), { recursive: true });
await fs2.copyFile(filePath, newFilePath);
return {
path: newFilePath,
namespace: "image"
};
});
build2.onLoad({ filter: /\.png$|\.jpg$/ }, async (args) => {
const sharp = (await import("sharp")).default;
const image = sharp(args.path);
const metadata = await image.metadata();
const url = `${publicPath}/media/${path2.basename(args.path)}`;
const output = image.resize({
width: Math.min(metadata.width ?? 16, 16),
height: Math.min(metadata.height ?? 16, 16),
fit: "inside"
}).toFormat("webp", {
quality: 30,
alphaQuality: 20,
smartSubsample: true
});
const { data, info } = await output.toBuffer({ resolveWithObject: true });
const dataURIBase64 = `data:image/${info.format};base64,${data.toString(
"base64"
)}`;
return {
contents: JSON.stringify({
url,
width: metadata.width,
height: metadata.height,
blurDataURL: dataURIBase64
}),
loader: "json"
};
});
}
};
// src/plugins/use-pages/build/plugins/inject-app-hydration.ts
import fs3 from "fs/promises";
import path3 from "path";
var injectAppHydrationPlugin = (version) => ({
name: "inject-hydration",
setup(build2) {
build2.onLoad({ filter: /.*/, namespace: "file" }, async (args) => {
if (args.path === path3.resolve(process.cwd(), ".pylon", "app.tsx")) {
let contents = await fs3.readFile(args.path, "utf-8");
const clientPath = path3.resolve(process.cwd(), ".pylon/client");
const pathToClient = path3.relative(path3.dirname(args.path), clientPath);
contents += `
import {hydrateRoot} from 'react-dom/client'
import * as client from './${pathToClient}'
import { __PYLON_ROUTER_INTERNALS_DO_NOT_USE, __PYLON_INTERNALS_DO_NOT_USE, DevOverlay, onCaughtErrorProd, onRecoverableErrorProd, onUncaughtErrorProd } from '@getcronit/pylon/pages';
import React, {startTransition} from 'react'
import * as Sentry from '@sentry/react'
// @ts-ignore
window.__PYLON_VERSION__ = "${version}"
async function hydrate() {
// Determine if any of the initial routes are lazy
const lazyMatches = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.matchRoutes(routes, window.location)?.filter(
(m) => m.route.lazy
);
// Load the lazy matches and update the routes before creating your router
// so we can hydrate the SSR-rendered content synchronously
if (lazyMatches && lazyMatches?.length > 0) {
await Promise.all(
lazyMatches.map(async (m) => {
const routeModule = await m.route.lazy!();
Object.assign(m.route, { ...routeModule, lazy: undefined });
})
);
}
const payload = (window as any).__pylonStaticData
if (payload?.cache) {
const coreClient = (client as any).client || client
if (coreClient && coreClient.cache) {
console.log('Hydrating cache with payload', payload.cache, coreClient)
coreClient.hydrateCache({cacheSnapshot: payload.cache, shouldRefetch: false})
}
}
// @ts-ignore
const router = __PYLON_ROUTER_INTERNALS_DO_NOT_USE.createBrowserRouter(routes)
// @ts-ignore
window.__PYLON_NAVIGATE__ = router.navigate
startTransition(() => {
hydrateRoot(
document,
<__PYLON_INTERNALS_DO_NOT_USE.DataClientProvider client={client}>
<__PYLON_ROUTER_INTERNALS_DO_NOT_USE.RouterProvider router={router} />
</__PYLON_INTERNALS_DO_NOT_USE.DataClientProvider>
, {
// Callback called when an error is thrown and not caught by an ErrorBoundary.
onUncaughtError: Sentry.reactErrorHandler((error, errorInfo) => {
console.warn('Uncaught error', error, errorInfo.componentStack);
}),
// Callback called when React catches an error in an ErrorBoundary.
onCaughtError: Sentry.reactErrorHandler(),
// Callback called when React automatically recovers from errors.
onRecoverableError: Sentry.reactErrorHandler(),
})
})
}
hydrate()
`;
return {
loader: "tsx",
contents
};
}
});
}
});
// src/plugins/use-pages/build/plugins/postcss-plugin.ts
import fs4 from "fs/promises";
import loadConfig from "postcss-load-config";
import postcss from "postcss";
var postcssPlugin = {
name: "postcss-plugin",
setup(build2) {
build2.onLoad({ filter: /.css$/, namespace: "file" }, async (args) => {
const { plugins, options } = await loadConfig();
const css = await fs4.readFile(args.path, "utf-8");
const result = await postcss(plugins).process(css, {
...options,
from: args.path
}).then((result2) => result2);
return {
contents: result.css,
loader: "css"
};
});
}
};
// src/plugins/use-pages/build/plugins/use-data-static-analyzer/index.ts
import * as fs5 from "fs";
import { Node as Node2, SyntaxKind as SyntaxKind2 } from "ts-morph";
// src/plugins/use-pages/build/plugins/use-data-static-analyzer/analyze.ts
import {
Node,
Project,
SyntaxKind,
ts
} from "ts-morph";
function pathKey(p) {
let key = "";
for (let i = 0; i < p.length; i++) {
if (i > 0) key += ".";
key += p[i].name;
if (p[i].args !== void 0) key += "(" + p[i].args + ")";
}
return key;
}
var JS_ARRAY_ONLY_METHODS = /* @__PURE__ */ new Set([
"push",
"pop",
"shift",
"unshift",
"splice",
"join",
"flat",
"flatMap",
"reverse",
"sort",
"toReversed",
"toSorted",
"toSpliced"
]);
var JS_SHARED_METHODS = /* @__PURE__ */ new Set(["slice", "concat", "indexOf", "includes"]);
var JS_ARRAY_METHODS = /* @__PURE__ */ new Set([
...JS_ARRAY_ONLY_METHODS,
...JS_SHARED_METHODS
]);
var JS_INTERNALS = /* @__PURE__ */ new Set([
...JS_ARRAY_METHODS,
"length",
"toString",
"toLocaleString",
"toLocaleDateString",
"toLocaleTimeString",
"toJSON",
"valueOf",
"hasOwnProperty",
"trim",
"trimStart",
"trimEnd",
"toLowerCase",
"toUpperCase",
"split",
"substring",
"substr",
"replace",
"replaceAll",
"match",
"matchAll",
"startsWith",
"endsWith",
"charAt",
"charCodeAt",
"codePointAt",
"repeat",
"padStart",
"padEnd",
"toFixed",
"toPrecision",
"toExponential",
"bind",
"call",
"apply"
]);
var ITERATOR_METHODS = /* @__PURE__ */ new Set([
"map",
"filter",
"forEach",
"reduce",
"some",
"every",
"find",
"findIndex",
"reverse",
"sort",
"slice",
"concat",
"flat",
"flatMap",
"toReversed",
"toSorted",
"toSpliced"
]);
function coreAnalyze(sourceFile, options) {
const result = {};
let _checker = void 0;
const getChecker = () => {
if (!_checker) _checker = sourceFile.getProject().getTypeChecker();
return _checker;
};
const symbolCache = /* @__PURE__ */ new WeakMap();
function getSymbol(node) {
let sym = symbolCache.get(node);
if (sym !== void 0) return sym;
sym = node.getSymbol?.() || getChecker().getSymbolAtLocation(node);
symbolCache.set(node, sym ?? null);
return sym ?? void 0;
}
const typeCache = /* @__PURE__ */ new WeakMap();
function getNodeType(node) {
let t = typeCache.get(node);
if (t !== void 0) return t;
t = node.getType();
typeCache.set(node, t);
return t;
}
let importedIdentifiers;
const getImportedIdentifiers = () => {
if (importedIdentifiers) return importedIdentifiers;
importedIdentifiers = /* @__PURE__ */ new Set();
sourceFile.getImportDeclarations().forEach((imp) => {
const namedBindings = imp.getImportClause()?.getNamedBindings();
if (namedBindings && Node.isNamedImports(namedBindings)) {
namedBindings.getElements().forEach((el) => {
importedIdentifiers.add(el.getAliasNode()?.getText() || el.getName());
});
}
const namespaceImport = imp.getImportClause()?.getNamespaceImport();
if (namespaceImport) {
importedIdentifiers.add(namespaceImport.getText());
}
});
return importedIdentifiers;
};
const WELL_KNOWN_GLOBALS = /* @__PURE__ */ new Set([
"console",
"Math",
"JSON",
"Array",
"Object",
"Promise",
"Error",
"Map",
"Set",
"Number",
"String",
"Boolean",
"Date",
"RegExp",
"Intl",
"Uint8Array",
"Buffer",
"React",
"document",
"window"
]);
function needsTypeChecker(node) {
if (!Node.isIdentifier(node)) return true;
const name = node.getText();
if (WELL_KNOWN_GLOBALS.has(name)) return false;
if (functionRegistry.has(name)) return false;
const paths = resolveBinding(name);
if (paths.length === 0) return true;
return false;
}
const trackedNames = /* @__PURE__ */ new Set();
if (options.rootObjectName) {
trackedNames.add(options.rootObjectName);
}
function textMentionsTracked(node) {
if (trackedNames.size === 0) return true;
let found = false;
const walkNative = (n) => {
if (found) return;
if (ts.isIdentifier(n) && trackedNames.has(n.text)) {
found = true;
return;
}
ts.forEachChild(n, walkNative);
};
walkNative(node.compilerNode);
return found;
}
function mergePathAndArgs(tree, path5, isList = false) {
if (path5.length === 0) return;
if (path5.some((p) => p.name === "__decl")) return;
let current = tree;
for (let i = 0; i < path5.length; i++) {
const step = path5[i];
const key = step.name;
if (key === "__element") continue;
const args = step.args;
const isLast = i === path5.length - 1;
const segmentIsList = (!!step.__isList || isLast && isList) && !step.__isVirtual;
if (current[key] === true && !isLast) {
current[key] = {};
}
let node = current[key];
if (node === void 0 || node === true) {
if (args !== void 0) {
node = { __args: args };
current[key] = node;
} else {
if (isLast && !segmentIsList) {
current[key] = true;
node = true;
} else {
node = {};
current[key] = node;
}
}
}
if (node === true) {
if (isLast && !segmentIsList && args === void 0) return;
node = {};
current[key] = node;
}
if (Array.isArray(node)) {
let branch = node.find((n) => n.__args === args);
if (!branch) {
branch = args !== void 0 ? { __args: args } : {};
node.push(branch);
}
node = branch;
} else if (args !== void 0 && node.__args !== void 0 && node.__args !== args) {
const oldNode = node;
const newNode = { __args: args };
current[key] = [oldNode, newNode];
node = newNode;
} else if (args === void 0 && node.__args !== void 0) {
const oldNode = node;
const newNode = {};
current[key] = [oldNode, newNode];
node = newNode;
} else if (args !== void 0 && node.__args === void 0) {
node.__args = args;
}
if (segmentIsList) {
if (typeof node === "object" && node !== null && !Array.isArray(node)) {
node.__isList = true;
} else if (node === true) {
current[key] = { __isList: true };
node = current[key];
}
}
if (!isLast) {
current = node;
}
}
}
function stringifyArgument(node) {
if (Node.isObjectLiteralExpression(node)) {
const props = node.getProperties().map((prop) => {
if (Node.isPropertyAssignment(prop)) {
const keyNode = prop.getNameNode();
const key = Node.isComputedPropertyName(keyNode) ? `[${stringifyArgument(keyNode.getExpression())}]` : prop.getName();
const value = stringifyArgument(prop.getInitializer());
return `${key}: ${value}`;
}
if (Node.isShorthandPropertyAssignment(prop)) {
const name = prop.getName();
const value = stringifyArgument(prop.getNameNode());
if (value === name) return name;
return `${name}: ${value}`;
}
if (Node.isSpreadAssignment(prop)) {
return `...${stringifyArgument(prop.getExpression())}`;
}
return prop.getText();
});
if (props.length === 0) return "{}";
return `{ ${props.join(", ")} }`;
}
if (Node.isArrayLiteralExpression(node)) {
return `[${node.getElements().map(stringifyArgument).join(", ")}]`;
}
if (Node.isIdentifier(node)) {
const name = node.getText();
const binding = resolveBindingInfo(name);
if (binding && binding.paths.length > 0) {
const path5 = binding.paths[0];
const first = path5[0];
if (first?.sourceName) {
if (first.name.startsWith("__target_")) return name;
if (path5.some((p) => p.isElement)) return name;
let result2 = first.sourceName;
for (let i = 1; i < path5.length; i++) {
const seg = path5[i];
if (seg.name.startsWith("__")) continue;
result2 += "." + seg.name;
if (seg.args !== void 0) result2 += "(" + seg.args + ")";
}
return result2;
}
if (first?.name.startsWith("__literal_")) {
return first.name.replace("__literal_", "");
}
}
return name;
}
const kind = node.getKind();
if (kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.TrueKeyword || kind === SyntaxKind.FalseKeyword || kind === SyntaxKind.NullKeyword || kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
return node.getText();
}
if (Node.isConditionalExpression(node)) {
const condition = stringifyArgument(node.getCondition());
const whenTrue = stringifyArgument(node.getWhenTrue());
const whenFalse = stringifyArgument(node.getWhenFalse());
if (condition === "true" || condition.startsWith('"') && condition !== '""' || !isNaN(Number(condition)) && Number(condition) !== 0) {
return whenTrue;
}
if (condition === "false" || condition === "null" || condition === "undefined" || condition === "0" || condition === '""') {
return whenFalse;
}
return `${condition} ? ${whenTrue} : ${whenFalse}`;
}
return node.getText();
}
let scopes = [{ bindings: /* @__PURE__ */ new Map() }];
function currentScope() {
return scopes[scopes.length - 1];
}
let branchDepth = 0;
const exportedFunctionReturns = /* @__PURE__ */ new Map();
function setBinding(identifier, paths, isDeclaration = false, isParam = false) {
let finalPaths = paths;
if (isDeclaration) {
if (options.rootObjectName && identifier === options.rootObjectName && !finalPaths.some((p) => p.length > 0 && p[0].sourceName)) {
currentScope().bindings.set(identifier, { paths: [[]], isParam });
trackedNames.add(identifier);
return;
}
finalPaths = paths.map((p) => {
if (p.length > 0) {
if (!p[0].sourceName) {
return [{ ...p[0], sourceName: identifier }, ...p.slice(1)];
}
}
return p;
});
}
if (!isDeclaration) {
for (let i = scopes.length - 1; i >= 0; i--) {
const existingBinding = scopes[i].bindings.get(identifier);
if (existingBinding) {
if (branchDepth > 0) {
const existing = existingBinding.paths;
const combined = [...existing];
const seen = new Set(combined.map(pathKey));
for (const p of finalPaths) {
const pk = pathKey(p);
if (!seen.has(pk)) {
seen.add(pk);
combined.push(p);
}
}
scopes[i].bindings.set(identifier, {
paths: combined,
isParam: existingBinding.isParam
});
} else {
scopes[i].bindings.set(identifier, { paths: finalPaths, isParam });
}
return;
}
}
}
currentScope().bindings.set(identifier, { paths: finalPaths, isParam });
if (finalPaths.length > 0) trackedNames.add(identifier);
}
function hasBinding(identifier) {
for (let i = scopes.length - 1; i >= 0; i--) {
if (scopes[i].bindings.has(identifier)) return true;
}
if (options.rootObjectName && identifier === options.rootObjectName)
return true;
return false;
}
function resolveBinding(identifier) {
for (let i = scopes.length - 1; i >= 0; i--) {
const b = scopes[i].bindings.get(identifier);
if (b) return b.paths;
}
return [];
}
function resolveBindingInfo(identifier) {
for (let i = scopes.length - 1; i >= 0; i--) {
const b = scopes[i].bindings.get(identifier);
if (b) return b;
}
return void 0;
}
let lastReturnedPaths = [];
function markAsList(paths) {
paths.forEach((path5) => {
mergePathAndArgs(result, path5, true);
if (path5.length > 0) {
const lastSegment = path5[path5.length - 1];
if (!lastSegment.__isVirtual) {
lastSegment.__isList = true;
}
}
});
}
function bindParam(paramNameNode, paths, isDeclaration = false) {
if (Node.isIdentifier(paramNameNode)) {
setBinding(paramNameNode.getText(), paths, isDeclaration, true);
} else if (Node.isObjectBindingPattern(paramNameNode) || Node.isArrayBindingPattern(paramNameNode)) {
handleDestructuring(paramNameNode, paths, isDeclaration);
}
}
function executeFunctionBody(body, bindParams, isolate = false) {
if (!body) return [];
const oldScopes = scopes;
if (isolate) {
scopes = [oldScopes[0], { bindings: /* @__PURE__ */ new Map() }];
} else {
scopes.push({ bindings: /* @__PURE__ */ new Map() });
}
if (bindParams) bindParams();
let paths = [];
if (Node.isBlock(body)) {
const prevReturned = lastReturnedPaths;
lastReturnedPaths = [];
body.getStatements().forEach(analyzeStatement);
paths = lastReturnedPaths;
lastReturnedPaths = prevReturned;
} else {
paths = evaluateExpression(body);
}
if (isolate) {
scopes = oldScopes;
} else {
scopes.pop();
}
return paths;
}
function bindIteratorParam(params, basePaths, methodName) {
const paramIndex = methodName === "reduce" ? 1 : 0;
if (params.length > paramIndex) {
const elementPaths = basePaths.map((p) => [
...p,
{ name: "__element", isElement: true }
]);
bindParam(params[paramIndex].getNameNode(), elementPaths);
}
}
function withRecursionGuard(decl, fn) {
const count = visitedDecls.get(decl) || 0;
if (count >= MAX_RECURSION_PER_DECL || currentDepth >= MAX_DEPTH) {
return void 0;
}
visitedDecls.set(decl, count + 1);
currentDepth++;
const sourceFile2 = decl.getSourceFile();
if (options.onFileAccess) options.onFileAccess(sourceFile2);
try {
return fn();
} finally {
currentDepth--;
const newCount = (visitedDecls.get(decl) || 1) - 1;
if (newCount <= 0) visitedDecls.delete(decl);
else visitedDecls.set(decl, newCount);
}
}
const functionRegistry = /* @__PURE__ */ new Map();
function handleDestructuring(pattern, paths, isDeclaration = false) {
if (Node.isObjectBindingPattern(pattern)) {
for (const element of pattern.getElements()) {
let propName = "";
const propertyNameNode = element.getPropertyNameNode();
const nameNode = element.getNameNode();
if (element.getDotDotDotToken()) {
if (Node.isIdentifier(nameNode)) {
setBinding(nameNode.getText(), paths, isDeclaration);
}
continue;
}
if (propertyNameNode) {
propName = propertyNameNode.getText();
} else if (Node.isIdentifier(nameNode)) {
propName = nameNode.getText();
}
if (propName) {
if (propName.startsWith("$") && propName !== "$on") continue;
const propPrefix = `__prop_${propName}`;
const matches = paths.filter((p) => p[0]?.name === propPrefix);
let nextPaths;
if (matches.length > 0) {
nextPaths = matches.map((p) => p.slice(1));
} else {
nextPaths = paths.map((p) => [...p, { name: propName }]);
}
nextPaths.forEach((p) => mergePathAndArgs(result, p));
if (Node.isIdentifier(nameNode)) {
setBinding(nameNode.getText(), nextPaths, isDeclaration);
} else if (Node.isObjectBindingPattern(nameNode) || Node.isArrayBindingPattern(nameNode)) {
handleDestructuring(nameNode, nextPaths, isDeclaration);
}
}
}
} else if (Node.isArrayBindingPattern(pattern)) {
const elements = pattern.getElements();
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
if (!Node.isOmittedExpression(element)) {
const nameNode = element.getNameNode();
const indexPrefix = `__index_${i}`;
const elementPaths = paths.filter((p) => p[0]?.name === indexPrefix).map((p) => p.slice(1));
const finalPaths = elementPaths.length > 0 ? elementPaths : paths;
if (Node.isIdentifier(nameNode)) {
setBinding(nameNode.getText(), finalPaths, isDeclaration);
} else if (Node.isObjectBindingPattern(nameNode) || Node.isArrayBindingPattern(nameNode)) {
handleDestructuring(nameNode, finalPaths, isDeclaration);
}
}
}
}
}
const visitedDecls = /* @__PURE__ */ new Map();
let currentDepth = 0;
const MAX_DEPTH = 10;
const MAX_RECURSION_PER_DECL = 2;
function resolveFunctionDefinition(symbol, onFileAccess) {
if (!symbol) return void 0;
let currentSym = symbol;
const visitedSyms = /* @__PURE__ */ new Set();
while (currentSym && !visitedSyms.has(currentSym)) {
visitedSyms.add(currentSym);
const prevSym = currentSym;
const decls = currentSym.getDeclarations();
if (onFileAccess && decls) {
decls.forEach((d) => onFileAccess(d.getSourceFile()));
}
let decl = decls?.find(
(d) => Node.isFunctionDeclaration(d) || Node.isArrowFunction(d) || Node.isFunctionExpression(d) || Node.isMethodDeclaration(d)
);
if (!decl) {
const varDecl = decls?.find(
(d) => Node.isVariableDeclaration(d) || Node.isPropertyAssignment(d)
);
if (varDecl && (Node.isVariableDeclaration(varDecl) || Node.isPropertyAssignment(varDecl))) {
let initializer = varDecl.getInitializer();
while (initializer) {
if (Node.isAsExpression(initializer) || Node.isParenthesizedExpression(initializer)) {
initializer = initializer.getExpression();
continue;
}
if (Node.isCallExpression(initializer)) {
const args = initializer.getArguments();
let foundSubDecl = false;
for (const arg of args) {
const argSym = getSymbol(arg);
let subDecl;
if (argSym) {
subDecl = resolveFunctionDefinition(argSym, onFileAccess);
}
if (subDecl) {
initializer = subDecl;
foundSubDecl = true;
break;
} else if (Node.isArrowFunction(arg) || Node.isFunctionExpression(arg)) {
initializer = arg;
foundSubDecl = true;
break;
}
}
if (foundSubDecl) continue;
}
if (Node.isIdentifier(initializer)) {
const sym = getSymbol(initializer);
if (sym && !visitedSyms.has(sym)) {
currentSym = sym;
break;
}
}
break;
}
if (initializer && !Node.isIdentifier(initializer)) {
if (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer)) {
decl = initializer;
}
}
if (decl) return decl;
if (currentSym !== prevSym) continue;
}
}
if (decl) return decl;
try {
const aliased = currentSym.getAliasedSymbol();
if (aliased && aliased !== currentSym) {
currentSym = aliased;
continue;
}
} catch (e) {
}
break;
}
return void 0;
}
function executeIfFunction(paths, argsPaths, isolate = false) {
let allRetPaths = [];
let found = false;
for (const p of paths) {
for (const segment of p) {
if (segment.name === "__decl" && segment.decl) {
const fnDef = segment.decl;
const retPaths = withRecursionGuard(fnDef, () => {
return executeFunctionBody(
fnDef.getBody(),
() => {
fnDef.getParameters().forEach((param, i) => {
if (i < argsPaths.length) {
bindParam(param.getNameNode(), argsPaths[i], true);
}
});
},
isolate
);
});
if (retPaths !== void 0) {
allRetPaths.push(...retPaths);
found = true;
}
}
}
}
return found ? allRetPaths : void 0;
}
function evaluateExpression(originalNode) {
if (!originalNode) return [];
let node = originalNode;
let k = node.getKind();
while (k === SyntaxKind.ParenthesizedExpression || k === SyntaxKind.AsExpression || k === SyntaxKind.TypeAssertionExpression || k === SyntaxKind.NonNullExpression) {
node = node.getExpression();
k = node.getKind();
}
if (options.targetNodes) {
const idx = options.targetNodes.indexOf(node);
if (idx !== -1) {
return [[{ name: `__target_${idx}` }]];
}
}
const kind = node.getKind();
if (kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.TrueKeyword || kind === SyntaxKind.FalseKeyword || kind === SyntaxKind.NullKeyword || kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.Identifier && node.getText() === "undefined") {
return [[{ name: `__literal_${node.getText()}` }]];
}
if (Node.isIdentifier(node)) {
const name = node.getText();
if (hasBinding(name)) {
return resolveBinding(name);
}
if (needsTypeChecker(node)) {
const sym = getSymbol(node);
const decl = resolveFunctionDefinition(sym, options.onFileAccess);
if (decl) {
return [[{ name: "__decl", decl }]];
}
}
return [];
}
if (Node.isPropertyAccessExpression(node)) {
const basePaths = evaluateExpression(node.getExpression());
const name = node.getName();
const propPrefix = `__prop_${name}`;
const matchingPaths = basePaths.filter((p) => p[0]?.name === propPrefix);
if (matchingPaths.length > 0) {
return matchingPaths.map((p) => p.slice(1));
}
if (JS_INTERNALS.has(name) || name.startsWith("$") && name !== "$on") {
if (JS_ARRAY_METHODS.has(name)) markAsList(basePaths);
return name === "bind" ? basePaths : [];
}
const parent = node.getParent();
const isCallExpr = parent && parent.getKind() === SyntaxKind.CallExpression && parent.getExpression() === node;
const newPaths = [];
for (const path5 of basePaths) {
const nextPath = [...path5, { name }];
if (!isCallExpr) {
mergePathAndArgs(result, nextPath);
}
newPaths.push(nextPath);
}
return newPaths;
}
if (Node.isObjectLiteralExpression(node)) {
const paths = [];
node.getProperties().forEach((prop) => {
if (Node.isPropertyAssignment(prop)) {
const nameNode = prop.getNameNode();
let name = "";
if (Node.isComputedPropertyName(nameNode)) {
const expr = nameNode.getExpression();
if (Node.isStringLiteral(expr)) {
name = expr.getLiteralText();
}
} else {
name = prop.getName();
}
if (!name) return;
const init = prop.getInitializer();
if (init) {
if (Node.isArrowFunction(init) || Node.isFunctionExpression(init)) {
functionRegistry.set(name, init);
}
const initPaths = evaluateExpression(init);
if (initPaths.length === 0) {
paths.push([{ name: `__prop_${name}` }]);
} else {
for (const ip of initPaths) {
paths.push([{ name: `__prop_${name}` }, ...ip]);
}
}
}
} else if (Node.isShorthandPropertyAssignment(prop)) {
const name = prop.getName();
const initPaths = resolveBinding(name);
if (initPaths.length === 0) {
paths.push([{ name: `__prop_${name}` }]);
} else {
for (const ip of initPaths) {
paths.push([{ name: `__prop_${name}` }, ...ip]);
}
}
} else if (Node.isMethodDeclaration(prop)) {
const name = prop.getName();
functionRegistry.set(name, prop);
paths.push([{ name: `__prop_${name}` }]);
} else if (Node.isSpreadAssignment(prop)) {
const spreadPaths = evaluateExpression(prop.getExpression());
paths.push(...spreadPaths);
}
});
return paths;
}
if (Node.isArrayLiteralExpression(node)) {
const paths = [];
node.getElements().forEach((el) => {
if (Node.isSpreadElement(el)) {
paths.push(...evaluateExpression(el.getExpression()));
} else {
paths.push(...evaluateExpression(el));
}
});
return paths;
}
if (Node.isSpreadElement(node)) {
return evaluateExpression(node.getExpression());
}
if (Node.isElementAccessExpression(node)) {
const basePaths = evaluateExpression(node.getExpression());
const argExpr = node.getArgumentExpression();
if (argExpr) {
if (Node.isStringLiteral(argExpr) || Node.isNoSubstitutionTemplateLiteral(argExpr)) {
const propName = argExpr.getLiteralText();
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName)) {
const newPaths = [];
for (const path5 of basePaths) {
const nextPath = [...path5, { name: propName }];
mergePathAndArgs(result, nextPath);
newPaths.push(nextPath);
}
return newPaths;
}
}
const baseType = getNodeType(node.getExpression());
const argType = getNodeType(argExpr);
if (baseType.isArray() || baseType.isTuple() || argType.isNumber() || argType.isNumberLiteral() || Node.isNumericLiteral(argExpr)) {
markAsList(basePaths);
}
}
if (argExpr) evaluateExpression(argExpr);
return basePaths;
}
if (Node.isCallExpression(node)) {
const args = node.getArguments();
let argsString;
const getArgsString = () => {
if (argsString === void 0) {
argsString = args.length > 0 ? args.map((arg) => stringifyArgument(arg)).join(", ") : "";
}
return argsString;
};
const expr = node.getExpression();
const hookName = Node.isIdentifier(expr) ? expr.getText() : Node.isPropertyAccessExpression(expr) ? expr.getName() : "";
if (hookName === "useMemo") {
const firstArg = args[0];
if (firstArg) {
const paths = evaluateExpression(firstArg);
const retPaths = executeIfFunction(paths, []);
return retPaths !== void 0 ? retPaths : paths;
}
}
if (hookName === "useCallback") {
const firstArg = args[0];
if (firstArg) {
return evaluateExpression(firstArg);
}
}
if (hookName === "useState") {
const firstArg = args[0];
const initPaths = firstArg ? evaluateExpression(firstArg) : [];
const statePaths = initPaths.length > 0 ? initPaths.map((p) => [
{ name: "__index_0" },
{ name: "__state" },
...p
]) : [[{ name: "__index_0" }, { name: "__state" }]];
return [...statePaths, [{ name: "__index_1" }]];
}
if (Node.isPropertyAccessExpression(expr)) {
const methodName = expr.getName();
const baseExpr = expr.getExpression();
const basePaths = evaluateExpression(baseExpr);
if (ITERATOR_METHODS.has(methodName)) {
let baseType;
if (needsTypeChecker(baseExpr)) {
baseType = getNodeType(baseExpr);
}
if (!JS_SHARED_METHODS.has(methodName) || baseType && !(baseType.isString() || baseType.isStringLiteral())) {
markAsList(basePaths);
}
let callbackPaths = [];
node.getArguments().forEach((originalArg) => {
let arg = originalArg;
while (Node.isParenthesizedExpression(arg)) {
arg = arg.getExpression();
}
if (Node.isArrowFunction(arg) || Node.isFunctionExpression(arg)) {
callbackPaths = executeFunctionBody(arg.getBody(), () => {
bindIteratorParam(arg.getParameters(), basePaths, methodName);
});
} else if (Node.isIdentifier(arg)) {
const fnName = arg.getText();
let fnDef = functionRegistry.get(fnName);
if (!fnDef) {
const paths = resolveBinding(fnName);
for (const p of paths) {
for (const s of p) {
if (s.name === "__decl" && s.decl) {
fnDef = s.decl;
break;
}
}
if (fnDef) break;
}
}
if (!fnDef) {
let symbol;
if (needsTypeChecker(arg)) {
symbol = getSymbol(arg) || getNodeType(arg).getSymbol();
}
fnDef = resolveFunctionDefinition(symbol, options.onFileAccess);
}
if (fnDef) {
const result2 = withRecursionGuard(fnDef, () => {
return executeFunctionBody(fnDef.getBody(), () => {
bindIteratorParam(
fnDef.getParameters(),
basePaths,
methodName
);
});
});
if (result2) callbackPaths = result2;
}
} else {
evaluateExpression(arg);
}
});
return methodName === "map" && callbackPaths.length > 0 ? callbackPaths.map(
(p) => p.map(
(s, idx) => idx === p.length - 1 && (s.name === "__element" || s.name.startsWith("__index_") || !p.some((s2) => s2.name.startsWith("__prop_"))) ? { ...s, __isVirtual: true } : s
)
) : basePaths;
}
if (JS_INTERNALS.has(methodName) || methodName.startsWith("$")) {
if (JS_ARRAY_ONLY_METHODS.has(methodName)) {
markAsList(basePaths);
} else if (JS_SHARED_METHODS.has(methodName)) {
if (needsTypeChecker(baseExpr)) {
const baseType = getNodeType(baseExpr);
if (!(baseType.isString() || baseType.isStringLiteral())) {
markAsList(basePaths);
}
}
}
if (methodName === "push" || methodName === "unshift" || methodName === "splice") {
node.getArguments().forEach((arg, i) => {
if (methodName === "splice" && i < 2) {
evaluateExpression(arg);
return;
}
const argPaths = evaluateExpression(arg);
basePaths.push(...argPaths);
});
} else {
node.getArguments().forEach((arg) => evaluateExpression(arg) || []);
}
return methodName === "bind" ? basePaths : [];
}
let methodFnDef = functionRegistry.get(methodName);
if (!methodFnDef) {
if (needsTypeChecker(expr)) {
const methodSymbol = getSymbol(expr);
methodFnDef = resolveFunctionDefinition(
methodSymbol,
options.onFileAccess
);
}
}
if (!methodFnDef) {
const nameNode = expr.getNameNode();
if (nameNode && needsTypeChecker(nameNode)) {
const nameSym = getSymbol(nameNode);
if (nameSym) {
methodFnDef = resolveFunctionDefinition(
nameSym,
options.onFileAccess
);
}
}
}
const methodArgsPaths = node.getArguments().map((arg) => evaluateExpression(arg));
const callPaths = evaluateExpression(expr);
const retPaths = executeIfFunction(callPaths, methodArgsPaths);
if (retPaths !== void 0) return retPaths;
const newPaths = [];
for (const path5 of basePaths) {
const nextPath = [...path5, { name: methodName, args: getArgsString() }];
mergePathAndArgs(result, nextPath);
newPaths.push(nextPath);
}
return newPaths;
}
let basePathsForCall = [];
if (Node.isIdentifier(expr)) {
const fnName = expr.getText();
let fnDef = functionRegistry.get(fnName);
if (!fnDef) {
if (needsTypeChecker(expr)) {
const symbol = getSymbol(expr) || getNodeType(expr).getSymbol();
fnDef = resolveFunctionDefinition(symbol, options.onFileAccess);
}
}
const argsPaths = node.getArguments().map((arg) => evaluateExpression(arg));
const callPaths = evaluateExpression(expr);
const retPaths = executeIfFunction(callPaths, argsPaths);
if (retPaths !== void 0) return retPaths;
if (fnDef) {
const retPaths2 = withRecursionGuard(fnDef, () => {
return executeFunctionBody(fnDef.getBody(), () => {
fnDef.getParameters().forEach((param, i) => {
if (i < argsPaths.length) {
bindParam(param.getNameNode(), argsPaths[i], true);
}
});
});
});
if (retPaths2) return retPaths2;
}
basePathsForCall = resolveBinding(fnName);
} else {
basePathsForCall = evaluateExpression(expr);
}
if (basePathsForCall.length > 0) {
const newPaths = [];
for (const path5 of basePathsForCall) {
if (path5.length > 0) {
const last = path5[path5.length - 1];
const nextPath = [
...path5.slice(0, -1),
{ ...last, args: getArgsString() }
];
mergePathAndArgs(result, nextPath);
newPaths.push(nextPath);
}
}
if (newPaths.length > 0) return newPaths;
}
return [];
}
if (Node.isJsxOpeningElement(node) || Node.isJsxSelfClosingElement(node)) {
const tagNameNode = node.getTagNameNode();
const tagNameString = tagNameNode.getText();
let decl;
if (!/^[a-z]/.test(tagNameString)) {
if (Node.isIdentifier(tagNameNode)) {
decl = functionRegistry.get(tagNameString);
if (!decl) {
const paths = resolveBinding(tagNameString);
for (const p of paths) {
for (const s of p) {
if (s.name === "__decl" && s.decl) {
decl = s.decl;
break;
}
}
if (decl) break;
}
}
}
if (!decl) {
let symbol;
if (needsTypeChecker(tagNameNode)) {
symbol = getSymbol(tagNameNode) || getNodeType(tagNameNode).getSymbol();
}
decl = resolveFunctionDefinition(symbol, options.onFileAccess);
}
}
const attributes = node.getAttributes();
const propsPaths = /* @__PURE__ */ new Map();
attributes.forEach((attr) => {
if (Node.isJsxAttribute(attr)) {
const name = attr.getNameNode().getText();
const initializer = attr.getInitializer();
if (initializer) {
const valPaths = Node.isJsxExpression(initializer) ? evaluateExpression(initializer.getExpression()) : evaluateExpression(initializer);
propsPaths.set(name, valPaths);
executeIfFunction(valPaths, []);
}
} else if (Node.isJsxSpreadAttribute(attr)) {
const expr = attr.getExpression();
const spreadPaths = evaluateExpression(expr);
for (const path5 of spreadPaths) {
if (path5[0]?.name.startsWith("__prop_")) {
const propName = path5[0].name.replace("__prop_", "");
const existing = propsPaths.get(propName) || [];
propsPaths.set(propName, [...existing, path5.slice(1)]);
}
}
}
});
if (decl) {
withRecursionGuard(decl, () => {
return executeFunctionBody(
decl.getBody(),
() => {
const params = decl.getParameters();
if (params.length > 0) {
const propsParam = params[0].getNameNode();
if (Node.isObjectBindingPattern(propsParam)) {
for (const element of propsParam.getElements()) {
const name = element.getPropertyNameNode()?.getText() || element.getName();
const paths = propsPaths.get(name) || [];
bindParam(element.getNameNode(), paths, true);
}
} else {
const allPaths = [];
propsPaths.forEach((paths, name) => {
paths.forEach((p) => {
allPaths.push([{ name: `__prop_${name}` }, ...p]);
});
});
bindParam(propsParam, allPaths, true);
}
}
},
true
);
});
}
return [];
}
if (Node.isConditionalExpression(node)) {
evaluateExpression(node.getCondition());
branchDepth++;
const truePaths = evaluateExpression(node.getWhenTrue());
const falsePaths = evaluateExpression(node.getWhenFalse());
branchDepth--;
return [...truePaths, ...falsePaths];
}
if (Node.isBinaryExpression(node) && node.getOperatorToken().getKind() === SyntaxKind.EqualsToken) {
const right = node.getRight();
const left = node.getLeft();
const paths = evaluateExpression(right);
if (Node.isIdentifier(left)) {
setBinding(left.getText(), paths);
} else {
evaluateExpression(left);
}
return paths;
}
if (Node.isArrowFunction(node) || Node.isFunctionExpression(node)) {
const resultPaths = executeFunctionBody(node.getBody());
return [[{ name: "__decl", decl: node }], ...resultPaths];
}
if (Node.isBinaryExpression(node)) {
const left = evaluateExpression(node.getLeft());
const right = evaluateExpression(node.getRight());
return [...left, ...right];
}
node.forEachChild((child) => {
evaluateExpression(child);
});
return [];
}
function analyzeStatement(stmt) {
if (Node.isVariableStatement(stmt)) {
for (const decl of stmt.getDeclarations()) {
const initializer = decl.getInitializer();
const paths = initializer ? evaluateExpression(initializer) : [];
const nameNode = decl.getNameNode();
const name = nameNode.getText();
if (initializer && (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer))) {
functionRegistry.set(name, initializer);
const containsTarget = !options.targetNodes || options.targetNodes.some(
(target) => initializer.getStart() <= target.getStart() && initializer.getEnd() >= target.getEnd()
);
if (containsTarget) {
executeFunctionBody(initializer.getBody());
}
}
bindParam(nameNode, paths, true);
}
} else if (Node.isExpressionStatement(stmt)) {
evaluateExpression(stmt.getExpression());
} else if (Node.isIfStatement(stmt)) {
evaluateExpression(stmt.getExpression());
branchDepth++;
analyzeStatement(stmt.getThenStatement());
const elseStmt = stmt.getElseStatement();
if (elseStmt) analyzeStatement(elseStmt);
branchDepth--;
} else if (Node.isSwitchStatement(stmt)) {
evaluateExpression(stmt.getExpression());
branchDepth++;
stmt.getClauses().forEach((clause) => {
clause.getStatements().forEach(analyzeStatement);
});
branchDepth--;
} else if (Node.isForStatement(stmt)) {
const initializer = stmt.getInitializer();
if (initializer && Node.isVariableDeclarationList(initializer)) {
scopes.push({ bindings: /* @__PURE__ */ new Map() });
for (const decl of initializer.getDeclarations()) {
const init = decl.getInitializer();
if (init) {
const paths = evaluateExpression(init);
bindParam(decl.getNameNode(), paths);
}
}
}
const condition = stmt.getCondition();
if (condition) evaluateExpression(condition);
const incrementor = stmt.getIncrementor();
if (incrementor) evaluateExpression(incrementor);
analyzeStatement(stmt.getStatement());
if (initializer && Node.isVariableDeclarationList(initializer)) {
scopes.pop();
}
} else if (Node.isForOfStatement(stmt)) {
scopes.push({ bindings: /* @__PURE__ */ new Map() });
const paths = evaluateExpression(stmt.getExpression());
markAsList(paths);
const initializer = stmt.getInitializer();
if (Node.isVariableDeclarationList(initializer)) {
for (const decl of initializer.getDeclarations()) {
bindParam(decl.getNameNode(), paths);
}
}
analyzeStatement(stmt.getStatement());
scopes.pop();
} else if (Node.isBlock(stmt)) {
const stmts = stmt.getStatements();
if (stmts.length > 5 && !textMentionsTracked(stmt)) return;
scopes.push({ bindings: /* @__PURE__ */ new Map() });
stmts.forEach(analyzeStatement);
scopes.pop();
} else if (Node.isReturnStatement(stmt)) {
const expr = stmt.getExpression();
if (expr) {
lastReturnedPaths = evaluateExpression(expr);
if (lastReturnedPaths.length > 0) {
let parent = stmt.getParent();
while (parent && !Node.isFunctionDeclaration(parent) && !Node.isArrowFunction(parent) && !Node.isFunctionExpression(parent)) {
parent = parent.getParent();
}
if (parent && (Node.isFunctionDeclaration(parent) || Node.isArrowFunction(parent) || Node.isFunctionExpression(parent))) {
const existing = exportedFunctionReturns.get(parent) || [];
exportedFunctionReturns.set(parent, [
...existing,
...lastReturnedPaths
]);
}
}
}
} else if (Node.isFunctionDeclaration(stmt)) {
const name = stmt.getName();
if (name) functionRegistry.set(name, stmt);
const containsTarget = !options.targetNodes || options.targetNodes.some(
(target) => stmt.getStart() <= target.getStart() && stmt.getEnd() >= target.getEnd()
);
if (containsTarget) {
scopes.push({ bindings: /* @__PURE__ */ new Map() });
executeFunctionBody(stmt.getBody());
scopes.pop();
}
} else {
stmt.forEachChild(analyzeStatement);
}
}
const walkOuter = (node) => {
if (Node.isFunctionDeclaration(node)) {
const name = node.getName();
if (name) functionRegistry.set(name, node);
const params = node.getParameters();
if (params.length > 0 && options.rootObjectName) {
const paramNameNode = params[0].getNameNode();
if (Node.isObjectBindingPattern(paramNameNode)) {
for (const el of paramNameNode.getElements()) {
const propName = el.getPropertyNameNode()?.getText() || el.getName();
if (propName === options.rootObjectName) {
scopes.push({ bindings: /* @__PURE__ */ new Map() });
bindParam(el.getNameNode(), [[]]);
const body = node.getBody();
if (body && Node.isBlock(body)) {
body.getStatements().forEach(analyzeStatement);
}
scopes.pop();
}
}
}
}
}
if (Node.isFunctionDeclaration(node) && !options.targetNodes) {
const body = node.getBody();
if (body && !textMentionsTracked(body)) return;
}
if (Node.isStatement(node)) {
analyzeStatement(node);
} else {
node.forEachChild(walkOuter);
}
};
walkOuter(sourceFile);
return { result, exportedFunctionReturns };
}
var _sharedProject;
function clearAnalyzeCache() {
_sharedProject = void 0;
projectImporterGraphCache = /* @__PURE__ */ new WeakMap();
}
var projectImporterGraphCache = /* @__PURE__ */ new WeakMap();
function extractQueries(filePath, project, options = {}) {
const accessedFiles = /* @__PURE__ */ new Set();
accessedFiles.add(filePath);
const { pylonPackage = "@getcronit/pylon/pages", hookName = "useData" } = options;
const sourceFile = project.getSourceFileOrThrow(filePath);
if (!options.skipDependencyResolution) {
project.resolveSourceFileDependencies();
}
const targetNodes = findUseQueries(sourceFile, pylonPackage, hookName);
if (targetNodes.length === 0) {
return { queries: [], dependencies: Array.from(accessedFiles) };
}
const { result, exportedFunctionReturns } = coreAnalyze(sourceFile, {
targetNodes,
onFileAccess: (sf) => accessedFiles.add(sf.getFilePath())
});
const processedCallSites = /* @__PURE__ */ new Set();
const functionQueue = Array.from(
exportedFunctionReturns.entries()
);
const processedFunctions = /* @__PURE__ */ new Set();
let directImporterGraph = projectImporterGraphCache.get(project);
function buildImporterGraph() {
if (directImporterGraph) return;
directImporterGraph = /* @__PURE__ */ new Map();
function addToGraph(importedPath, importer) {
let set = directImporterGraph.get(importedPath);
if (!set) {
set = /* @__PURE__ */ new Set();
directImporterGraph.set(importedPath, set);
}
set.add(importer);
}
project.getSourceFiles().forEach((sf) => {
const text = sf.compilerNode.text;
if (!text.includes("import") && !text.includes("export")) return;
sf.getImportDeclarations().forEach((imp) => {
const moduleSF = imp.getModuleSpecifierSourceFile();
if (moduleSF) addToGraph(moduleSF.getFilePath(), sf);
});
sf.getExportDeclarations().forEach((exp) => {
const moduleSF = exp.getModuleSpecifierSourceFile();
if (moduleSF) addToGraph(moduleSF.getFilePath(), sf);
});
});
projectImporterGraphCache.set(project, directImporterGraph);
}
function getTransitiveImporters(sfPath, visited = /* @__PURE__ */ new Set()) {
const result2 = /* @__PURE__ */ new Set();
const direct = directImporterGraph?.get(sfPath);
if (!direct) return result2;
visited.add(sfPath);
for (const importer of direct) {
result2.add(importer);
const impPath = importer.getFilePath();
if (!visited.has(impPath)) {
const sub = getTransitiveImporters(impPath, visited);
sub.forEach((s) => result2.add(s));
}
}
return result2;
}
function findCallSitesInFile(sf, fnName) {
if (!sf.compilerNode.text.includes(fnName)) return [];
return sf.getDescendantsOfKind(SyntaxKind.CallExpression).filter((call) => {
const expr = call.getExpression();
const name = Node.isIdentifier(expr) ? expr.getText() : Node.isPropertyAccessExpression(expr) ? expr.getName() : "";
return name === fnName;
});
}
const referencesCache = /* @__PURE__ */ new Map();
function getCachedReferences(fn) {
let refs = referencesCache.get(fn);
if (refs !== void 0) return refs;
refs = fn.findReferences?.() || [];
referencesCache.set(fn, refs);
return refs;
}
const analysisCache = /* @__PURE__ */ new Map();
function getCachedAnalysis(sf, targets) {
const cacheKey = sf.getFilePath() + ":" + targets.map((t) => t.getStart() + "-" + t.getEnd()).join(",");
let cached = analysisCache.get(cacheKey);
if (cached) return cached;
cached = coreAnalyze(sf, {
targetNodes: targets,
onFileAccess: (s) => accessedFiles.add(s.getFilePath())
});
analysisCache.set(cacheKey, cached);
return cached;
}
while (functionQueue.length > 0) {
const [fn, paths] = functionQueue.shift();
if (processedFunctions.has(fn)) continue;
processedFunctions.add(fn);
const targetPaths = paths.filter(
(p) => p.some((step) => step.name.startsWith("__target_"))
);
if (targetPaths.length === 0) continue;
buildImporterGraph();
const callNodes = [];
if (Node.isFunctionDeclaration(fn) || Node.isArrowFunction(fn) || Node.isFunctionExpression(fn)) {
const name = fn.getName?.();
const sf = fn.getSourceFile();
const sfPath = sf.getFilePath();
if (name) {
callNodes.push(...findCallSitesInFile(sf, name));
}
const importers = getTransitiveImporters(sfPath);
if (importers.size > 0 && name) {
for (const importer of importers) {
callNodes.push(...findCallSitesInFile(importer, name));
}
}
}
if (callNodes.length === 0) {
const references = getCachedReferences(fn);
for (const refSymbol of references) {
for (const match of refSymbol.getReferences()) {
callNodes.push(match.getNode());
}
}
}
for (const node of callNodes) {
if (processedCallSites.has(node)) continue;
processedCallSites.add(node);
let call = node;
while (call && !Node.isCallExpression(call)) {
call = call.getParent();
}
if (call && Node.isCallExpression(call)) {
accessedFiles.add(call.getSourceFile().getFilePath());
const callerAnalysis = getCachedAnalysis(call.getSourceFile(), [call]);
for (const [
newFn,
newPaths
] of callerAnalysis.exportedFunctionReturns.entries()) {
functionQueue.push([newFn, newPaths]);
}
const externalSelectors = callerAnalysis.result["__target_0"] || {};
const shadowedProperties = /* @__PURE__ */ new Set();
for (const p of paths) {
if (p[0]?.name.startsWith("__prop_")) {
shadowedProperties.add(p[0].name.replace(/^__prop_/, ""));
}
}
for (const tp of targetPaths) {
const targetIdx = tp.findIndex(
(step) => step.name.startsWith("__target_")
);
if (targetIdx === -1) continue;
const prefixes = tp.slice(0, targetIdx);
const suffixes = tp.slice(targetIdx + 1);
let currentExt = externalSelectors;
let skip = false;
for (const pref of prefixes) {
const cleanName = pref.name.replace(/^__prop_/, "");
if (currentExt && typeof currentExt === "object" && cleanName in currentExt) {
currentExt = currentExt[cleanName];
} else {
skip = true;
break;
}
}
if (skip || !currentExt) continue;
if (suffixes.length > 0 && typeof currentExt === "object") {
currentExt = { ...currentExt };
delete currentExt.__args;
}
if (prefixes.length === 0 && typeof currentExt === "object") {
const filtered = { ...currentExt };
for (const shadow of shadowedProperties) {
delete filtered[shadow];
}
currentExt = filtered;
}
const targetKey = tp[targetIdx].name;
const subPath = tp.slice(targetIdx + 1);
let currentLevel = result[targetKey] || (result[targetKey] = {});
let parent = result;
let lastKey = targetKey;
for (let i = 0; i < subPath.length; i++) {
const step = subPath[i];
if (step.name === "__element" || step.name === "__decl" || step.name.startsWith("__prop_")) {
continue;
}
const isLast = i === subPath.length - 1;
if (currentLevel[step.name] === true && !isLast) {
currentLevel[step.name] = {};
}
parent = currentLevel;
lastKey = step.name;
if (isLast) {
if (currentExt === true) {
if (!currentLevel[step.name] || currentLevel[step.name] === true) {
currentLevel[step.name] = true;
}
} else if (typeof currentExt === "object" && Object.keys(currentExt).length > 0) {
if (!currentLevel[step.name] || currentLevel[step.name] === true) {
currentLevel[step.name] = {};
}
deepMerge(currentLevel[step.name], currentExt);
} else {
if (currentLevel[step.name] === void 0) {
currentLevel[step.name] = true;
}
}
break;
}
currentLevel = currentLevel[step.name] || (currentLevel[step.name] = {});
}
continue;
}
}
}
}
return {
queries: targetNodes.map((node, idx) => ({
start: node.getStart(),
end: node.getEnd(),
selectors: result[`__target_${idx}`] || {},
node
})),
dependencies: Array.from(accessedFiles)
};
}
function deepMerge(target, source) {
if (!source || typeof source !== "object") return;
for (const key in source) {
if (key === "__element" || key.startsWith("__prop_") || key === "__decl") {
continue;
}
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
if (!target[key] || typeof target[key] !== "object") {
target[key] = {};
}
deepMerge(target[key], source[key]);
} else {
if (source[key] === true && typeof target[key] === "object") {
continue;
}
target[key] = source[key];
}
}
}
function findUseQueries(sourceFile, pylonPackage, hookName) {
if (!sourceFile.compilerNode.text.includes(hookName)) return [];
const useQueryAliases = /* @__PURE__ */ new Set();
const targetNodes = [];
const visit = (node) => {
if (Node.isImportDeclaration(node)) {
const moduleSpecifier = node.getModuleSpecifierValue();
if (moduleSpecifier === pylonPackage) {
const importClause = node.getImportClause();
if (importClause) {
const namedBindings = importClause.getNamedBindings();
if (namedBindings && Node.isNamedImports(namedBindings)) {
for (const el of namedBindings.getElements()) {
const originalName = el.getNameNode().getText();
const aliasNode = el.getAliasNode();
const localName = aliasNode ? aliasNode.getText() : originalName;
if (originalName === hookName) {
useQueryAliases.add(localName);
}
}
}
}
}
} else if (Node.isCallExpression(node)) {
const expression = node.getExpression();
if (Node.isIdentifier(expression) && useQueryAliases.has(expression.getText())) {
targetNodes.push(node);
}
}
node.forEachChild(visit);
};
visit(sourceFile);
return targetNodes;
}
// src/plugins/use-pages/build/plugins/use-data-static-analyzer/manager.ts
import * as crypto from "crypto";
import { Project as Project2 } from "ts-morph";
var StaticAnalysisManager = class {
project;
cache = /* @__PURE__ */ new Map();
sessionResults = /* @__PURE__ */ new Map();
lastSessionReset = 0;
constructor(options) {
this.project = new Project2({
tsConfigFilePath: options.tsConfigFilePath,
skipAddingFilesFromTsConfig: true,
compilerOptions: {
allowJs: true,
jsx: 4,
// ReactJSX
moduleResolution: 2,
// Node
esModuleInterop: true,
target: 9
// ESNext
}
});
}
/**
* Resets the session cache if enough time has passed since the last reset.
* This allows client and server builds starting together to share results.
*/
resetSession() {
const now = Date.now();
if (now - this.lastSessionReset > 500) {
this.sessionResults.clear();
this.lastSessionReset = now;
}
}
getProject() {
return this.project;
}
getCachedResult(path5, content) {
const hash = this.computeHash(content);
const sessionResult = this.sessionResults.get(path5);
if (sessionResult && sessionResult.hash === hash) {
return sessionResult;
}
const cached = this.cache.get(path5);
if (cached && cached.hash === hash) {
return cached;
}
return null;
}
setCache(path5, result) {
this.cache.set(path5, result);
this.sessionResults.set(path5, result);
}
updateSourceFile(path5, content) {
const existing = this.project.getSourceFile(path5);
if (existing) {
if (existing.getFullText() !== content) {
existing.replaceWithText(content);
}
return existing;
}
return this.project.createSourceFile(path5, content, { overwrite: true });
}
computeHash(content) {
return crypto.createHash("md5").update(content).digest("hex");
}
};
// src/plugins/use-pages/build/plugins/use-data-static-analyzer/selectors-to-prepare.ts
function generatePrepare(selectors) {
let depth = 0;
let varCount = 0;
function compileNode(node, accessPath) {
const lines = [];
for (const [key, value] of Object.entries(node)) {
if (key === "__args" || key === "__isList") continue;
const isIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key);
const base = isIdentifier ? `${accessPath}?.${key}` : `${accessPath}?.[${JSON.stringify(key)}]`;
if (value === true) {
lines.push(`${base};`);
continue;
}
const branches = Array.isArray(value) ? value : [value];
for (const branch of branches) {
let nodeAccess = base;
if (typeof branch === "object" && branch !== null) {
if (branch.__args !== void 0) {
nodeAccess += `?.(${branch.__args})`;
}
if (branch.__isList) {
depth++;
const iter = `i${depth}`;
const sub = compileNode(branch, iter);
if (sub.length === 0) {
lines.push(`${nodeAccess};`);
} else {
lines.push(`${nodeAccess}?.map(${iter} => { ${sub.join(" ")} });`);
}
depth--;
continue;
}
const childKeys = Object.keys(branch).filter(
(k) => k !== "__args" && k !== "__isList"
);
if (childKeys.length > 1 || childKeys.length > 0 && branch.__args !== void 0) {
const varName = `v${++varCount}`;
lines.push(`const ${varName} = ${nodeAccess};`);
lines.push(...compileNode(branch, varName));
continue;
} else if (childKeys.length > 0) {
lines.push(...compileNode(branch, nodeAccess));
continue;
}
}
lines.push(`${nodeAccess};`);
}
}
return lines;
}
const internals = compileNode(selectors, "query");
if (internals.length === 0) return `({ query }) => {}`;
return `({ query }) => { ${internals.join(" ")} }`;
}
// src/plugins/use-pages/build/plugins/use-data-static-analyzer/index.ts
function useDataStaticAnalyzer(options = {}) {
const {
filter = /\.(ts|tsx)$/,
pylonPackage = "@getcronit/pylon/pages",
hookName = "useData",
debug = false
} = options;
return {
name: "pylon-use-data-static-analyzer",
async setup(build2) {
const manager = options.manager || new StaticAnalysisManager({
tsConfigFilePath: build2.initialOptions.tsconfig
});
const project = manager.getProject();
build2.onStart(() => {
manager.resetSession();
clearAnalyzeCache();
});
const entries = build2.initialOptions.entryPoints;
if (entries) {
const entryPaths = [];
if (Array.isArray(entries)) {
for (const entry of entries) {
entryPaths.push(
typeof entry === "string" ? entry : entry.in
);
}
} else if (entries && typeof entries === "object") {
for (const key in entries) {
entryPaths.push(entries[key]);
}
}
if (entryPaths.length > 0) {
project.addSourceFilesAtPaths(entryPaths);
project.resolveSourceFileDependencies();
}
}
build2.onLoad({ filter }, async (args) => {
const contents = await fs5.promises.readFile(args.path, "utf8");
const cached = manager.getCachedResult(args.path, contents);
if (cached) {
return {
contents: cached.contents,
loader: args.path.endsWith(".tsx") ? "tsx" : "ts",
watchFiles: cached.dependencies
};
}
if (!contents.includes(pylonPackage) || !contents.includes(hookName) && !contents.includes("from")) {
manager.setCache(args.path, {
contents,
dependencies: [args.path],
hash: manager.computeHash(contents)
});
return null;
}
manager.updateSourceFile(args.path, contents);
if (debug) {
console.log(`[Pylon] Analyzing ${args.path}`);
}
try {
const { queries, dependencies } = extractQueries(args.path, project, {
pylonPackage,
hookName,
skipDependencyResolution: true
});
let outputContents = contents;
if (queries.length > 0) {
const sortedQueries = [...queries].sort((a, b) => b.start - a.start);
for (const query of sortedQueries) {
const node = query.node;
const args2 = node.getArguments();
const prepareFn = generatePrepare(query.selectors);
if (args2.length === 0) {
const closeParen = node.getLastChildByKind(
SyntaxKind2.CloseParenToken
);
const pos = closeParen ? closeParen.getStart() : node.getEnd() - 1;
outputContents = outputContents.slice(0, pos) + `{
prepare: ${prepareFn}
}` + outputContents.slice(pos);
} else {
const firstArg = args2[0];
if (Node2.isObjectLiteralExpression(firstArg)) {
const startPos = firstArg.getStart() + 1;
const text = firstArg.getText();
const isEmpty = text.replace(/\s/g, "") === "{}";
const injection = isEmpty ? `
prepare: ${prepareFn}
` : `
prepare: ${prepareFn},`;
outputContents = outputContents.slice(0, startPos) + injection + outputContents.slice(startPos);
} else {
const startPos = firstArg.getStart();
const endPos = firstArg.getEnd();
const existingText = outputContents.slice(startPos, endPos);
outputContents = outputContents.slice(0, startPos) + `{
...${existingText},
prepare: ${prepareFn}
}` + outputContents.slice(endPos);
}
}
}
if (debug) {
console.log(
`[Pylon] Effectively injected selectors into ${queries.length} calls in ${args.path}`
);
}
}
manager.setCache(args.path, {
contents: outputContents,
dependencies,
hash: manager.computeHash(contents)
});
const loader = args.path.endsWith(".tsx") ? "tsx" : "ts";
return {
contents: outputContents,
loader,
watchFiles: dependencies
};
} catch (err) {
console.error(`[Pylon] Error analyzing ${args.path}:`, err);
return null;
}
});
}
};
}
// src/plugins/use-pages/build/index.ts
var DIST_STATIC_DIR = path4.join(process.cwd(), ".pylon/__pylon/static");
var DIST_PAGES_DIR = path4.join(process.cwd(), ".pylon/__pylon/pages");
async function updateFileIfChanged(filePath, newContent) {
await fs6.mkdir(path4.dirname(filePath), { recursive: true });
try {
const currentContent = await fs6.readFile(filePath);
if (currentContent.equals(newContent)) {
return false;
}
} catch (err) {
if (err.code !== "ENOENT") throw err;
}
await fs6.writeFile(filePath, newContent);
return true;
}
var build = async ({ onBuild }) => {
const version = Math.random().toString(36).substring(7);
const buildAppFile = async () => {
const appFiles = makeAppFiles();
await updateFileIfChanged(
path4.resolve(process.cwd(), ".pylon", "app.tsx"),
Buffer.from(appFiles.routes)
);
};
const copyPublicDir = async () => {
const publicDir = path4.resolve(process.cwd(), "public");
const pylonPublicDir = path4.resolve(
process.cwd(),
".pylon",
"__pylon",
"public"
);
try {
await fs6.access(publicDir);
await fs6.mkdir(pylonPublicDir, { recursive: true });
await fs6.cp(publicDir, pylonPublicDir, { recursive: true, force: true });
} catch (err) {
if (err.code !== "ENOENT") throw err;
}
};
const pylonCssPath = path4.join(
process.cwd(),
"node_modules",
"@getcronit/pylon/dist/pages/index.css"
);
const buildAppFilePlugin = {
name: "build-app-file",
setup(build2) {
build2.onStart(async () => {
await buildAppFile();
});
}
};
const writeOnEndPlugin = {
name: "write-on-end",
setup(build2) {
build2.initialOptions.metafile = true;
build2.initialOptions.write = false;
build2.onEnd(async (result) => {
const manifest = {};
for (const [key, value] of Object.entries(
result.metafile?.outputs || {}
)) {
if (value.entryPoint === ".pylon/app.tsx") {
manifest["app.js"] = key;
if (value.cssBundle) {
manifest["app.css"] = value.cssBundle;
}
} else if (value.entryPoint?.endsWith("pylon/dist/pages/index.css")) {
manifest["index.css"] = key;
} else if (value.entryPoint?.endsWith("pages/sitemap.ts")) {
manifest["sitemap.js"] = key;
}
}
if (build2.initialOptions.publicPath) {
const publicPath = build2.initialOptions.publicPath;
for (const [key, value] of Object.entries(manifest)) {
const index = value.indexOf(publicPath);
if (index !== -1) {
manifest[key] = value.slice(index);
}
}
}
manifest["version"] = version;
await updateFileIfChanged(
path4.join(build2.initialOptions.outdir, "manifest.json"),
Buffer.from(JSON.stringify(manifest, null, 2))
);
await Promise.all(
result.outputFiles.map(async (file) => {
await fs6.mkdir(path4.dirname(file.path), { recursive: true });
await updateFileIfChanged(file.path, file.contents);
})
);
if (result.errors.length === 0) {
onBuild();
}
});
}
};
const nodePaths = [
path4.join(process.cwd(), "node_modules"),
path4.join(process.cwd(), "node_modules", "@getcronit/pylon/node_modules")
];
let pagesWatcher = null;
const timePlugin = (name) => ({
name: "rebuild-log",
setup({ onStart, onEnd }) {
var t;
onStart(() => {
t = Date.now();
});
onEnd(() => {
console.log(`Pages [${name}] Rebuild took ${Date.now() - t}ms`);
});
}
});
const sitemapExists = await fs6.access(path4.join(process.cwd(), "pages/sitemap.ts")).then(() => true).catch(() => false);
const tsConfigPath = path4.join(process.cwd(), "tsconfig.json");
const tsConfigExists = await fs6.access(tsConfigPath).then(() => true).catch(() => false);
const analysisManager = new StaticAnalysisManager({
tsConfigFilePath: tsConfigExists ? tsConfigPath : void 0
});
const clientCtx = await esbuild.context({
sourcemap: "linked",
write: false,
metafile: true,
nodePaths,
absWorkingDir: process.cwd(),
plugins: [
buildAppFilePlugin,
injectAppHydrationPlugin(version),
useDataStaticAnalyzer({ debug: true, manager: analysisManager }),
imagePlugin,
postcssPlugin,
writeOnEndPlugin,
timePlugin("client")
],
publicPath: "/__pylon/static",
assetNames: "assets/[name]-[hash]",
chunkNames: "chunks/[name]-[hash]",
entryNames: "./[name]-[hash]",
format: "esm",
platform: "browser",
entryPoints: [".pylon/app.tsx", pylonCssPath],
outdir: DIST_STATIC_DIR,
bundle: true,
splitting: true,
minify: false,
loader: {
// Map file extensions to the file loader
".svg": "file",
".woff": "file",
".woff2": "file",
".ttf": "file",
".otf": "file"
},
define: {
"process.env.NODE_ENV": JSON.stringify(
process.env.NODE_ENV || "development"
)
},
mainFields: ["browser", "module", "main"]
});
const serverCtx = await esbuild.context({
sourcemap: "inline",
write: false,
metafile: true,
absWorkingDir: process.cwd(),
nodePaths,
plugins: [
buildAppFilePlugin,
useDataStaticAnalyzer({ debug: true, manager: analysisManager }),
imagePlugin,
postcssPlugin,
writeOnEndPlugin,
timePlugin("server"),
esmExternalsPlugin([
"@getcronit/pylon",
"react",
"react-dom",
"gqty",
"@gqty/react"
])
],
publicPath: "/__pylon/static",
assetNames: "assets/[name]-[hash]",
chunkNames: "chunks/[name]-[hash]",
entryNames: "./[name]-[hash]",
format: "esm",
platform: "node",
entryPoints: [
".pylon/app.tsx",
pylonCssPath,
...sitemapExists ? ["./pages/sitemap.ts"] : []
],
outdir: DIST_PAGES_DIR,
bundle: true,
splitting: false,
external: ["@getcronit/pylon", "react", "react-dom", "gqty", "@gqty/react"],
minify: true,
loader: {
// Map file extensions to the file loader
".svg": "file",
".woff": "file",
".woff2": "file",
".ttf": "file",
".otf": "file"
},
define: {
"process.env.NODE_ENV": JSON.stringify(
process.env.NODE_ENV || "development"
)
},
mainFields: ["module", "main"]
});
return {
watch: async () => {
await buildAppFile();
await copyPublicDir();
pagesWatcher = chokidar.watch("pages", { ignoreInitial: true });
pagesWatcher.on("all", async (event, path5) => {
if (["add", "change", "unlink"].includes(event)) {
await copyPublicDir();
}
});
await Promise.all([clientCtx.watch(), serverCtx.watch()]);
},
dispose: async () => {
if (pagesWatcher) {
pagesWatcher.close();
}
Promise.all([clientCtx.dispose(), serverCtx.dispose()]);
},
rebuild: async () => {
await copyPublicDir();
await Promise.all([clientCtx.rebuild(), serverCtx.rebuild()]);
return {};
},
cancel: async () => {
if (pagesWatcher) {
await pagesWatcher.close();
}
await Promise.all([clientCtx.cancel(), serverCtx.cancel()]);
}
};
};
export {
build
};
//# sourceMappingURL=build-TGNP3US3.js.map