UNPKG

rwsdk

Version:

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

57 lines (56 loc) 2.74 kB
/** * context(justinvdm, 2025-05-14): * * ## Problem * React Server Components (RSC) and traditional SSR require different module * resolution: * - RSC modules must resolve with the "react-server" export condition * - SSR modules must resolve without it * * This presents a challenge for our framework, where the same modules often * need to run in both modes — within a single Cloudflare Worker runtime. We * can't split execution contexts or afford duplicated builds. * * Vite provides an elegant way to manage distinct resolution graphs via its * `environments` feature (`client`, `ssr`, `worker`, etc.). Each environment * can use different export conditions, plugins, and optimizeDeps configs. * * However, using separate environments implies separate output bundles. In our * case, that would nearly double the final bundle size — which is not viable * given Cloudflare Workers' strict 3MB limit. * * ## Solution * We run both RSC and SSR from a single Vite `worker` environment. To simulate * distinct resolution graphs, we virtualize SSR imports using a prefix. * * How it works: * - Maintain an SSR subgraph as part of the worker environment's module graph. * Any time we see "use client", we enter the subgraph. * - We keep the graphs separate by rewriting imports in SSR graph to map to virtual files. * - Bare imports to deps get resolved using a custom resolver so that we use * import conditions relevant to SSR - note the lack of "react-server" * condition: ["workerd", "edge", "import", "default"] * - All imports within the subgraph get their path rewritten with the SSR * module namespace prefix so that we stay within the subgraph. */ import { Plugin } from "vite"; export declare const SSR_NAMESPACE = "virtual:rwsdk:ssr"; export declare const SSR_NAMESPACE_PREFIX: string; export declare const SSR_URL_PREFIX: string; export declare const SSR_ESBUILD_NAMESPACE = "__rwsdk_ssr_esbuild_namespace__"; export declare const SSR_RESOLVER_CONDITION_NAMES: string[]; export declare const createSSRDepResolver: ({ projectRootDir, }: { projectRootDir: string; }) => (request: string, importer?: string) => string | false; export type VirtualizedSSRContext = { projectRootDir: string; config: any; resolveModule: (request: string, importer: string) => string | false | Promise<string | false>; resolveDep: (request: string) => string | false; }; export declare const isSSRPath: (filePath: string) => boolean; export declare const ensureNoSSRNamespace: (filePath: string) => string; export declare const ensureSSRNamespace: (filePath: string) => string; export declare function virtualizedSSRPlugin({ projectRootDir, }: { projectRootDir: string; }): Plugin;