rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
57 lines (56 loc) • 2.74 kB
text/typescript
/**
* 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;