UNPKG

rwsdk

Version:

Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime

90 lines (89 loc) 3.96 kB
import debug from "debug"; import { existsSync } from "node:fs"; import { mkdir, rm, writeFile } from "node:fs/promises"; import { dirname, resolve } from "node:path"; import { INTERMEDIATES_OUTPUT_DIR } from "../lib/constants.mjs"; import { runDirectivesScan } from "./runDirectivesScan.mjs"; const log = debug("rwsdk:vite:build-app"); /** * The build orchestrator is responsible for running the multi-phase build * process for production. It is designed to solve the circular dependency * between the worker, client, and SSR builds. * * @see docs/architecture/productionBuildProcess.md */ export async function buildApp({ builder, clientEntryPoints, clientFiles, serverFiles, projectRootDir, workerEntryPathname, }) { await rm(resolve(projectRootDir, "dist"), { recursive: true, force: true }); const workerEnv = builder.environments.worker; // Run a pre-scan build pass to allow plugins to set up and generate code // before scanning. console.log("Running plugin setup pass..."); process.env.RWSDK_BUILD_PASS = "plugin-setup"; const tempEntryPath = resolve(INTERMEDIATES_OUTPUT_DIR, "temp-entry.js"); try { if (!existsSync(dirname(tempEntryPath))) { await mkdir(dirname(tempEntryPath), { recursive: true }); } await writeFile(tempEntryPath, ""); const originalWorkerBuildConfig = workerEnv.config.build; workerEnv.config.build = { ...originalWorkerBuildConfig, write: false, rollupOptions: { ...originalWorkerBuildConfig?.rollupOptions, input: { index: tempEntryPath, }, }, }; await builder.build(workerEnv); // Restore the original config workerEnv.config.build = originalWorkerBuildConfig; } finally { await rm(tempEntryPath, { force: true }); } await runDirectivesScan({ rootConfig: builder.config, environments: builder.environments, clientFiles, serverFiles, entries: [workerEntryPathname], }); console.log("Building worker..."); process.env.RWSDK_BUILD_PASS = "worker"; await builder.build(workerEnv); log("Used client files after worker build & filtering: %O", Array.from(clientFiles)); console.log("Building SSR..."); await builder.build(builder.environments.ssr); log("Discovered clientEntryPoints: %O", Array.from(clientEntryPoints)); console.log("Building client..."); const clientEnv = builder.environments["client"]; clientEnv.config.build ??= {}; clientEnv.config.build.rollupOptions ??= {}; const clientEntryPointsArray = Array.from(clientEntryPoints); if (clientEntryPointsArray.length === 0) { log("No client entry points discovered, using default: src/client.tsx"); clientEnv.config.build.rollupOptions.input = ["src/client.tsx"]; } else { clientEnv.config.build.rollupOptions.input = clientEntryPointsArray; } await builder.build(clientEnv); console.log("Linking worker build..."); process.env.RWSDK_BUILD_PASS = "linker"; // Re-configure the worker environment for the linking pass const workerConfig = workerEnv.config; workerConfig.build.emptyOutDir = false; // context(justinvdm, 22 Sep 2025): This is a workaround to satisfy the // Cloudflare plugin's expectation of an entry chunk named `index`. The plugin // now manages the worker build, so we no longer set rollup options // directly. Instead, we re-point the original entry to the intermediate // worker bundle from the first pass. This allows the linker pass to re-use // the same plugin-driven configuration while bundling the final worker. workerConfig.build.rollupOptions.input = { index: resolve(projectRootDir, "dist", "worker", "index.js"), }; await builder.build(workerEnv); console.log("Build complete!"); }