rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
170 lines (169 loc) • 7.54 kB
JavaScript
import path, { resolve } from "node:path";
import { builtinModules } from "node:module";
import enhancedResolve from "enhanced-resolve";
import { SSR_BRIDGE_PATH } from "../lib/constants.mjs";
// port(justinvdm, 09 Jun 2025):
// https://github.com/cloudflare/workers-sdk/blob/d533f5ee7da69c205d8d5e2a5f264d2370fc612b/packages/vite-plugin-cloudflare/src/cloudflare-environment.ts#L123-L128
export const cloudflareBuiltInModules = [
"cloudflare:email",
"cloudflare:sockets",
"cloudflare:workers",
"cloudflare:workflows",
];
export const externalModules = [
...cloudflareBuiltInModules,
...builtinModules,
...builtinModules.map((m) => `node:${m}`),
];
export const configPlugin = ({ silent, projectRootDir, clientEntryPathnames, workerEntryPathname, }) => ({
name: "rwsdk:config",
config: async (_) => {
const mode = process.env.NODE_ENV;
const baseConfig = {
appType: "custom",
mode,
logLevel: silent ? "silent" : "info",
build: {
minify: mode !== "development",
sourcemap: true,
},
define: {
"process.env.NODE_ENV": JSON.stringify(mode),
},
ssr: {
target: "webworker",
},
environments: {
client: {
consumer: "client",
build: {
outDir: resolve(projectRootDir, "dist", "client"),
manifest: true,
rollupOptions: {
input: clientEntryPathnames,
},
},
define: {
"import.meta.env.RWSDK_ENV": JSON.stringify("client"),
},
optimizeDeps: {
noDiscovery: false,
include: ["rwsdk/client"],
esbuildOptions: {
jsx: "automatic",
jsxImportSource: "react",
plugins: [],
define: {
"process.env.NODE_ENV": JSON.stringify(mode),
},
},
},
resolve: {
conditions: ["browser", "module"],
},
},
ssr: {
resolve: {
conditions: ["workerd", "module", "browser"],
noExternal: true,
},
define: {
"import.meta.env.RWSDK_ENV": JSON.stringify("ssr"),
},
optimizeDeps: {
noDiscovery: false,
entries: [workerEntryPathname],
exclude: externalModules,
include: ["rwsdk/__ssr", "rwsdk/__ssr_bridge"],
esbuildOptions: {
jsx: "automatic",
jsxImportSource: "react",
plugins: [],
define: {
"process.env.NODE_ENV": JSON.stringify(mode),
},
},
},
build: {
lib: {
entry: {
[path.basename(SSR_BRIDGE_PATH, ".js")]: enhancedResolve.sync(projectRootDir, "rwsdk/__ssr_bridge"),
},
formats: ["es"],
fileName: () => path.basename(SSR_BRIDGE_PATH),
},
outDir: path.dirname(SSR_BRIDGE_PATH),
},
},
worker: {
resolve: {
conditions: [
"workerd",
"react-server",
"module",
// context(justinvdm, 11 Jun 2025): Some packages meant for cloudflare workers, yet
// their deps have only node import conditions, e.g. `agents` package (meant for CF),
// has `pkce-challenge` package as a dep, which has only node import conditions.
// https://github.com/crouchcd/pkce-challenge/blob/master/package.json#L17
//
// @cloudflare/vite-plugin should take care of any relevant polyfills for deps with
// node builtins imports that can be polyfilled though, so it is worth us including this condition here.
// However, it does mean we will try to run packages meant for node that cannot be run on cloudflare workers.
// That's the trade-off, but arguably worth it.
"node",
],
noExternal: true,
},
define: {
"import.meta.env.RWSDK_ENV": JSON.stringify("worker"),
},
optimizeDeps: {
noDiscovery: false,
include: ["rwsdk/worker"],
exclude: [],
entries: [workerEntryPathname],
esbuildOptions: {
jsx: "automatic",
jsxImportSource: "react",
define: {
"process.env.NODE_ENV": JSON.stringify(mode),
},
},
},
build: {
outDir: resolve(projectRootDir, "dist", "worker"),
emitAssets: true,
ssr: true,
rollupOptions: {
output: {
inlineDynamicImports: true,
},
input: {
worker: workerEntryPathname,
},
},
},
},
},
server: {
hmr: true,
},
builder: {
buildApp: async (builder) => {
// note(justinvdm, 27 May 2025): **Ordering is important**:
// * When building, client needs to be build first, so that we have a
// manifest file to map to when looking at asset references in JSX
// (e.g. Document.tsx)
// * When bundling, the RSC build imports the SSR build - this way
// they each can have their own environments (e.g. with their own
// import conditions), while still having all worker-run code go
// through the processing done by `@cloudflare/vite-plugin`
await builder.build(builder.environments["client"]);
await builder.build(builder.environments["ssr"]);
await builder.build(builder.environments["worker"]);
},
},
};
return baseConfig;
},
});