UNPKG

polen

Version:

A framework for delightful GraphQL developer portals

102 lines (86 loc) 3.81 kB
import { Hono } from '#dep/hono/index' import type { ReactRouter } from '#dep/react-router/index' import { asyncParallel, chunk } from '#lib/kit-temp' import { debugPolen } from '#singletons/debug' import * as NodeFs from 'node:fs/promises' import PROJECT_DATA from 'virtual:polen/project/data.jsonsuper' import viteClientAssetManifest from 'virtual:polen/vite/client/manifest' import { createPageHtmlResponse } from '../create-page-html-response.js' import { injectManifestIntoHtml } from '../manifest.js' import { createPolenDataInjector } from '../transformers/inject-polen-data.js' import { createThemeInitInjector } from '../transformers/inject-theme-init.js' import { getRoutesPaths } from './get-route-paths.js' export const generate = async (view: ReactRouter.StaticHandler) => { const debug = debugPolen.sub(`ssg`) const routePaths = getRoutesPaths() const totalPaths = routePaths.length debug(`start`, { totalPaths }) // Process routes in batches using the new utilities const batchSize = 50 const batches = chunk(routePaths, batchSize) const result = await asyncParallel( batches, async (batchPaths, batchIndex) => { const batchApp = new Hono.Hono() // Create a custom handler for batch processing that includes base path handling const batchHandler: Hono.Handler = async (ctx) => { // For SSG, we need to create a request with the base path prepended const url = new URL(ctx.req.raw.url) const basePath = PROJECT_DATA.basePath === `/` ? `` : PROJECT_DATA.basePath.slice(0, -1) // Create a new request with the base path prepended to the pathname const modifiedRequest = new Request( `${url.protocol}//${url.host}${basePath}${url.pathname}${url.search}`, { method: ctx.req.raw.method, headers: ctx.req.raw.headers, body: ctx.req.raw.body, }, ) const staticHandlerContext = await view.query(modifiedRequest) if (staticHandlerContext instanceof Response) { return staticHandlerContext } // SSG needs Polen data injection, theme init, and manifest transformation const polenDataInjector = createPolenDataInjector() const themeInitInjector = createThemeInitInjector() const transformHtml = async (html: string) => { // First inject Polen data html = await polenDataInjector(html, ctx) // Then inject theme initialization html = await themeInitInjector(html, ctx) // Finally inject manifest return injectManifestIntoHtml(html, viteClientAssetManifest, PROJECT_DATA.basePath) } // Pass ctx for SSG to set up Polen data return createPageHtmlResponse(staticHandlerContext, { transformHtml }, ctx) } // Register only the routes for this batch for (const routePath of batchPaths) { batchApp.get(routePath, batchHandler) } debug(`batch:start`, { batchIndex: batchIndex + 1, totalBatches: batches.length, pagesInBatch: batchPaths.length, }) const ssgResult = await Hono.SSG.toSSG(batchApp, NodeFs, { concurrency: 5, // Reduced concurrency for memory efficiency dir: PROJECT_DATA.paths.relative.build.root, }) if (!ssgResult.success) { throw new Error(`Failed to generate static site at batch ${batchIndex + 1}`, { cause: ssgResult.error, }) } return ssgResult }, { concurrency: 1, // Process batches sequentially to avoid memory issues failFast: true, // Stop on first batch failure }, ) if (!result.success) { throw new Error(`SSG generation failed`, { cause: result.errors[0] }) } debug(`complete`, { totalPaths }) }