/**
 * Static Renderer
 *
 * When `src/web/entry-server.tsx` exists, this module:
 * 1. Runs a Vite SSR build (as a subprocess to avoid in-process Bun/Vite issues)
 * 2. Imports the built entry-server.js
 * 3. Discovers routes to pre-render:
 *    - If `routeTree` is exported: auto-discovers all non-parameterized routes
 *    - If `getStaticPaths()` is exported: merges those paths in (for parameterized routes)
 *    - If neither: throws an error
 * 4. Calls render(url) for each route
 * 5. Replaces <!--app-html--> in the client template with rendered HTML
 * 6. Writes pre-rendered HTML files to .agentuity/client/
 */
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
    if (typeof path === "string" && /^\.\.?\//.test(path)) {
        return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
            return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
        });
    }
    return path;
};
import { join } from 'node:path';
import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'node:fs';
/**
 * Walks a TanStack Router route tree and extracts all non-parameterized paths.
 * Skips layout routes (no path) and parameterized routes (containing $).
 *
 * Accumulates the full URL path through the parent chain, since child routes
 * under layout routes have relative paths (e.g., '/key-value' under a
 * '/reference/api' layout should resolve to '/reference/api/key-value').
 */
export function extractRoutePaths(node) {
    const paths = new Set();
    function walk(route, parentPath) {
        const segment = route.path ?? route.options?.path;
        // Build the full path by accumulating segments from parent routes.
        // - Layout routes have no path (undefined) and don't contribute to the URL.
        // - Index routes have path '/' and resolve to the parent path itself
        //   (e.g., the '/' child of the root is the landing page; the '/' child
        //   of a '/docs' layout is '/docs'). They do not append anything.
        // - Leaf/layout routes have paths like '/reference/api' or '/key-value'.
        let currentPath = parentPath;
        let isIndexRoute = false;
        if (segment === '/') {
            isIndexRoute = true;
        }
        else if (segment) {
            // Non-root segment: append to parent path.
            // Segments always start with '/' (TanStack Router convention).
            currentPath = parentPath === '/' ? segment : parentPath + segment;
        }
        // Decide which URL, if any, to emit for this node.
        // - Index routes emit the parent URL, defaulting to '/' when the parent
        //   itself is pathless (i.e., the site root). Without this, a
        //   `createFileRoute('/')` route is never pre-rendered and the
        //   landing page ships with the unreplaced <!--app-html--> placeholder.
        // - Non-index routes emit their own accumulated path.
        const emit = isIndexRoute ? parentPath || '/' : currentPath;
        if (emit && !emit.includes('$')) {
            const normalized = emit === '/' ? '/' : emit.replace(/\/+$/, '');
            if (normalized) {
                paths.add(normalized);
            }
        }
        // Recurse into children, passing the accumulated path
        const children = route.children;
        if (children && typeof children === 'object') {
            for (const child of Object.values(children)) {
                if (child)
                    walk(child, currentPath);
            }
        }
    }
    walk(node, '');
    return [...paths].sort();
}
export async function runStaticRender(options) {
    const { rootDir, logger, dev = false } = options;
    const started = Date.now();
    const clientDir = join(rootDir, '.agentuity/client');
    const ssrOutDir = join(rootDir, '.agentuity/ssr');
    const entryServerPath = join(rootDir, 'src/web/entry-server.tsx');
    const templatePath = join(clientDir, 'index.html');
    // Verify prerequisites
    if (!existsSync(entryServerPath)) {
        throw new Error('Static rendering requires src/web/entry-server.tsx. ' +
            'This file must export a render(url: string) function and either ' +
            'a routeTree for auto-discovery or getStaticPaths() for explicit paths.');
    }
    if (!existsSync(templatePath)) {
        throw new Error('Client build must complete before static rendering. ' +
            'No index.html found in .agentuity/client/');
    }
    // Step 1: Vite SSR build (subprocess)
    // Run as a subprocess to avoid in-process Bun/Vite module resolution issues
    // that cause @mdx-js/rollup and other plugins to fail during SSR builds.
    // This matches the approach used for client builds in vite-builder.ts.
    logger.debug('Running Vite SSR build for static rendering (subprocess)...');
    const buildMode = dev ? 'development' : 'production';
    const viteProcess = Bun.spawn([
        'bun',
        'x',
        'vite',
        'build',
        '--ssr',
        entryServerPath,
        '--outDir',
        ssrOutDir,
        '--mode',
        buildMode,
    ], {
        cwd: rootDir,
        stdout: 'inherit',
        stderr: 'inherit',
    });
    const exitCode = await viteProcess.exited;
    if (exitCode !== 0) {
        throw new Error(`Vite SSR build exited with code ${exitCode}`);
    }
    // Steps 2–4: wrapped in try-finally so SSR artifacts are always cleaned up,
    // even if an exception is thrown during module import, validation, or rendering.
    let routeCount = 0;
    try {
        // Step 2: Import the built SSR entry
        const ssrEntryPath = join(ssrOutDir, 'entry-server.js');
        if (!existsSync(ssrEntryPath)) {
            throw new Error(`SSR build did not produce entry-server.js at ${ssrEntryPath}`);
        }
        const ssrModule = await import(__rewriteRelativeImportExtension(ssrEntryPath));
        if (typeof ssrModule.render !== 'function') {
            throw new Error('entry-server.tsx must export a render(url: string) function that returns HTML string');
        }
        // Step 3: Discover routes
        // Priority: auto-discover from routeTree + merge getStaticPaths() if present
        const discovered = new Set();
        // 3a. Auto-discover from exported routeTree (skips parameterized routes)
        if (ssrModule.routeTree) {
            const autoRoutes = extractRoutePaths(ssrModule.routeTree);
            for (const r of autoRoutes) {
                discovered.add(r);
            }
            logger.debug(`Auto-discovered ${autoRoutes.length} routes from route tree`);
        }
        // 3b. Merge paths from getStaticPaths() if exported (for parameterized routes, etc.)
        if (typeof ssrModule.getStaticPaths === 'function') {
            const extraRoutes = await ssrModule.getStaticPaths();
            if (!Array.isArray(extraRoutes)) {
                throw new Error('getStaticPaths() must return an array of URL paths (e.g., ["/", "/about"])');
            }
            for (const r of extraRoutes) {
                discovered.add(r);
            }
            logger.debug(`getStaticPaths() added ${extraRoutes.length} paths`);
        }
        // Must have at least one source of routes
        if (discovered.size === 0) {
            throw new Error('No routes to pre-render. Export routeTree from entry-server.tsx for auto-discovery, ' +
                'or export getStaticPaths() returning an array of URL paths.');
        }
        const routes = [...discovered].sort();
        routeCount = routes.length;
        logger.debug(`Total: ${routes.length} routes for pre-rendering`);
        // Step 4: Read template and pre-render each route
        const template = readFileSync(templatePath, 'utf-8');
        if (!template.includes('<!--app-html-->')) {
            logger.warn('index.html is missing the <!--app-html--> placeholder; ' +
                'pre-rendered content will not be injected into the page.');
        }
        for (const route of routes) {
            try {
                const html = await ssrModule.render(route);
                const page = template.replace('<!--app-html-->', html);
                let outPath;
                if (route === '/') {
                    outPath = join(clientDir, 'index.html');
                }
                else {
                    const dir = join(clientDir, route.slice(1));
                    mkdirSync(dir, { recursive: true });
                    outPath = join(dir, 'index.html');
                }
                writeFileSync(outPath, page, 'utf-8');
            }
            catch (err) {
                const message = err instanceof Error ? err.message : String(err);
                logger.warn(`Failed to render route ${route}: ${message}`);
                // Continue rendering other routes
            }
        }
    }
    finally {
        // Step 5: Clean up SSR build artifacts (always runs, even on error)
        rmSync(ssrOutDir, { recursive: true, force: true });
    }
    const duration = Date.now() - started;
    logger.debug(`Static rendering complete: ${routeCount} routes in ${duration}ms`);
    return { routes: routeCount, duration };
}
//# sourceMappingURL=static-renderer.js.map