A tool for people curious about the React Server Components protocol

improve bundle

Changed files
+47 -27
src
+4 -4
src/client/workspace-session.ts
··· 49 49 const worker = new WorkerClient(signal); 50 50 51 51 try { 52 - const clientExports = parseClientModule(clientCode); 52 + const clientExports = await parseClientModule(clientCode); 53 53 const manifest = buildManifest("client", clientExports); 54 - const compiledClient = compileToCommonJS(clientCode); 54 + const compiledClient = await compileToCommonJS(clientCode); 55 55 const clientModule = evaluateClientModule(compiledClient); 56 56 registerClientModule("client", clientModule); 57 57 58 - const actionNames = parseServerActions(serverCode); 59 - const compiledServer = compileToCommonJS(serverCode); 58 + const actionNames = await parseServerActions(serverCode); 59 + const compiledServer = await compileToCommonJS(serverCode); 60 60 61 61 await worker.deploy(compiledServer, manifest, actionNames); 62 62 const renderRaw = await worker.render();
+30 -13
src/shared/compiler.ts
··· 1 - import { transform } from "@babel/standalone"; 2 1 import type * as t from "@babel/types"; 2 + 3 + // Babel is lazy-loaded and preloaded via <link rel="modulepreload"> in HTML 4 + let babelPromise: Promise<typeof import("@babel/standalone")> | null = null; 5 + 6 + function getBabel(): Promise<typeof import("@babel/standalone")> { 7 + if (!babelPromise) { 8 + babelPromise = import("@babel/standalone"); 9 + } 10 + return babelPromise; 11 + } 12 + 13 + async function transform( 14 + code: string, 15 + options: Parameters<typeof import("@babel/standalone").transform>[1], 16 + ): Promise<ReturnType<typeof import("@babel/standalone").transform>> { 17 + const babel = await getBabel(); 18 + return babel.transform(code, options); 19 + } 3 20 4 21 type BabelProgram = t.Program & { 5 22 directives?: Array<{ value?: { value?: string } }>; ··· 23 40 code?: string; 24 41 }; 25 42 26 - export function parseExports(code: string): string[] { 43 + export async function parseExports(code: string): Promise<string[]> { 27 44 const exports: string[] = []; 28 - const result = transform(code, { 45 + const result = (await transform(code, { 29 46 presets: ["react"], 30 47 ast: true, 31 - }) as TransformResult; 48 + })) as TransformResult; 32 49 33 50 const ast = result.ast; 34 51 if (!ast) return exports; ··· 99 116 return hasDirective(body, "use server"); 100 117 } 101 118 102 - export function parseServerActions(code: string): string[] { 103 - const result = transform(code, { 119 + export async function parseServerActions(code: string): Promise<string[]> { 120 + const result = (await transform(code, { 104 121 presets: ["react"], 105 122 ast: true, 106 - }) as TransformResult; 123 + })) as TransformResult; 107 124 108 125 const ast = result.ast; 109 126 if (!ast) return []; ··· 204 221 return actions; 205 222 } 206 223 207 - export function parseClientModule(code: string): string[] { 208 - const result = transform(code, { 224 + export async function parseClientModule(code: string): Promise<string[]> { 225 + const result = (await transform(code, { 209 226 presets: ["react"], 210 227 ast: true, 211 - }) as TransformResult; 228 + })) as TransformResult; 212 229 213 230 const ast = result.ast; 214 231 if (!ast) return []; ··· 293 310 return exports; 294 311 } 295 312 296 - export function compileToCommonJS(code: string): string { 297 - const result = transform(code, { 313 + export async function compileToCommonJS(code: string): Promise<string> { 314 + const result = (await transform(code, { 298 315 presets: ["react"], 299 316 sourceType: "module", 300 317 plugins: [["transform-modules-commonjs", { loose: true }]], 301 - }) as TransformResult; 318 + })) as TransformResult; 302 319 return result.code ?? ""; 303 320 } 304 321
+13 -10
vite.config.js
··· 68 68 }; 69 69 } 70 70 71 - function preloadCodemirrorPlugin() { 71 + function preloadChunksPlugin() { 72 72 return { 73 - name: "preload-codemirror", 73 + name: "preload-chunks", 74 74 transformIndexHtml(html, { bundle, filename }) { 75 75 if (!bundle) return; // dev mode 76 76 const tags = []; 77 77 78 - const cmChunk = Object.keys(bundle).find((k) => k.includes("codemirror")); 79 - if (cmChunk) { 80 - tags.push({ 81 - tag: "link", 82 - attrs: { rel: "modulepreload", href: "/" + cmChunk }, 83 - injectTo: "head", 84 - }); 78 + // Preload codemirror and babel chunks 79 + for (const name of ["codemirror", "babel"]) { 80 + const chunk = Object.keys(bundle).find((k) => k.includes(name)); 81 + if (chunk) { 82 + tags.push({ 83 + tag: "link", 84 + attrs: { rel: "modulepreload", href: "/" + chunk }, 85 + injectTo: "head", 86 + }); 87 + } 85 88 } 86 89 87 90 // From index.html, prefetch embed resources for the iframe ··· 104 107 } 105 108 106 109 export default defineConfig(({ mode }) => ({ 107 - plugins: [react(), rolldownWorkerPlugin(), serveEmbedPlugin(), preloadCodemirrorPlugin()], 110 + plugins: [react(), rolldownWorkerPlugin(), serveEmbedPlugin(), preloadChunksPlugin()], 108 111 server: { port: 3333 }, 109 112 define: { 110 113 "process.env.NODE_ENV": JSON.stringify(mode === "development" ? "development" : "production"),