From a0e10d014e313f5b3e3dfcb9eaa8449377a7c195 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 5 Jun 2024 12:18:49 +0900 Subject: [PATCH 1/8] wip: experiment with emitFile for server reference --- .../src/features/server-action/plugin.tsx | 26 +++++++++++++++++++ packages/react-server/src/plugin/index.ts | 1 + 2 files changed, 27 insertions(+) diff --git a/packages/react-server/src/features/server-action/plugin.tsx b/packages/react-server/src/features/server-action/plugin.tsx index 694f0ad2d..a04a4adb9 100644 --- a/packages/react-server/src/features/server-action/plugin.tsx +++ b/packages/react-server/src/features/server-action/plugin.tsx @@ -109,6 +109,12 @@ export function vitePluginServerUseServer({ id, outCode: output.toString(), }); + if (manager.buildType === "rsc") { + manager.serverReferenceMap[id] = this.emitFile({ + type: "chunk", + id, + }); + } return { code: output.toString(), map: output.generateMap(), @@ -121,6 +127,26 @@ export function vitePluginServerUseServer({ } return; }, + generateBundle(_options, bundle) { + if (manager.buildType === "rsc") { + let result = `export default {\n`; + for (const [id, refId] of Object.entries(manager.serverReferenceMap)) { + const fileName = this.getFileName(refId); + result += ` "${hashString(id)}": () => import("../${fileName}"),\n`; + } + result += "};\n"; + for (const [_k, v] of Object.entries(bundle)) { + if ( + v.type === "chunk" && + v.facadeModuleId === "\0virtual:server-references" + ) { + // TODO: how to content hash? + // server reference virtual is fine, but it's critical for client reference + v.code = result; + } + } + } + }, }; // expose server references for RSC build via virtual module diff --git a/packages/react-server/src/plugin/index.ts b/packages/react-server/src/plugin/index.ts index ced59211f..01b6778a6 100644 --- a/packages/react-server/src/plugin/index.ts +++ b/packages/react-server/src/plugin/index.ts @@ -65,6 +65,7 @@ class PluginStateManager { routeToClientReferences: Record = {}; routeManifest?: RouteManifest; + serverReferenceMap: Record = {}; // expose "use client" node modules to client via virtual modules // to avoid dual package due to deps optimization hash during dev From ec76bde4b44abed8776cd0efa2965a42051abbba Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 5 Jun 2024 12:21:59 +0900 Subject: [PATCH 2/8] chore: revert some --- packages/react-server/src/features/server-action/plugin.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-server/src/features/server-action/plugin.tsx b/packages/react-server/src/features/server-action/plugin.tsx index a04a4adb9..cb04d8b7f 100644 --- a/packages/react-server/src/features/server-action/plugin.tsx +++ b/packages/react-server/src/features/server-action/plugin.tsx @@ -155,6 +155,9 @@ export function vitePluginServerUseServer({ return `export default {}`; } tinyassert(manager.buildType === "rsc"); + if (1) { + return `export default "** PLACEHOLDER **"`; + } let result = `export default {\n`; for (const id of manager.rscUseServerIds) { result += `"${hashString(id)}": () => import("${id}"),\n`; From d3ce474de916fe8efe396dfc42b2e412ff7bb422 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 5 Jun 2024 12:22:46 +0900 Subject: [PATCH 3/8] chore: revert more --- packages/react-server/src/features/server-action/plugin.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-server/src/features/server-action/plugin.tsx b/packages/react-server/src/features/server-action/plugin.tsx index cb04d8b7f..8745e6250 100644 --- a/packages/react-server/src/features/server-action/plugin.tsx +++ b/packages/react-server/src/features/server-action/plugin.tsx @@ -160,7 +160,8 @@ export function vitePluginServerUseServer({ } let result = `export default {\n`; for (const id of manager.rscUseServerIds) { - result += `"${hashString(id)}": () => import("${id}"),\n`; + let key = manager.buildType ? hashString(id) : id; + result += `"${key}": () => import("${id}"),\n`; } result += "};\n"; debug("[virtual:server-references]", result); From bc35629c33d4805f35d50beae0a7adaf24a9aebf Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 5 Jun 2024 14:49:19 +0900 Subject: [PATCH 4/8] wip: wait for idle --- .../src/features/server-action/plugin.tsx | 93 +++++++++++-------- packages/react-server/src/plugin/index.ts | 4 +- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/packages/react-server/src/features/server-action/plugin.tsx b/packages/react-server/src/features/server-action/plugin.tsx index 8745e6250..e7fa52923 100644 --- a/packages/react-server/src/features/server-action/plugin.tsx +++ b/packages/react-server/src/features/server-action/plugin.tsx @@ -2,7 +2,12 @@ import { transformDirectiveProxyExport, transformServerActionServer, } from "@hiogawa/transforms"; -import { createDebug, tinyassert } from "@hiogawa/utils"; +import { + createDebug, + createManualPromise, + debounce, + tinyassert, +} from "@hiogawa/utils"; import { type Plugin, type PluginOption, parseAstAsync } from "vite"; import type { PluginStateManager } from "../../plugin"; import { @@ -127,46 +132,56 @@ export function vitePluginServerUseServer({ } return; }, - generateBundle(_options, bundle) { - if (manager.buildType === "rsc") { - let result = `export default {\n`; - for (const [id, refId] of Object.entries(manager.serverReferenceMap)) { - const fileName = this.getFileName(refId); - result += ` "${hashString(id)}": () => import("../${fileName}"),\n`; - } - result += "};\n"; - for (const [_k, v] of Object.entries(bundle)) { - if ( - v.type === "chunk" && - v.facadeModuleId === "\0virtual:server-references" - ) { - // TODO: how to content hash? - // server reference virtual is fine, but it's critical for client reference - v.code = result; - } - } - } - }, }; // expose server references for RSC build via virtual module - const virtualPlugin = createVirtualPlugin("server-references", async () => { - if (manager.buildType === "scan") { - return `export default {}`; - } - tinyassert(manager.buildType === "rsc"); - if (1) { - return `export default "** PLACEHOLDER **"`; - } - let result = `export default {\n`; - for (const id of manager.rscUseServerIds) { - let key = manager.buildType ? hashString(id) : id; - result += `"${key}": () => import("${id}"),\n`; - } - result += "};\n"; - debug("[virtual:server-references]", result); - return result; - }); + const virtualPlugin = createVirtualPlugin( + "server-references", + async function () { + if (manager.buildType === "scan") { + return `export default {}`; + } + tinyassert(manager.buildType === "rsc"); + await this.load({ id: "\0virtual:wait-for-idle" }); + let result = `export default {\n`; + for (const id of manager.rscUseServerIds) { + result += `"${hashString(id)}": () => import("${id}"),\n`; + } + result += "};\n"; + debug("[virtual:server-references]", result); + return result; + }, + ); + + return [transformPlugin, virtualPlugin, waitForIdlePlugin()]; +} - return [transformPlugin, virtualPlugin]; +// https://github.com/rollup/rollup/issues/4985#issuecomment-1936333388 +// https://github.com/ArnaudBarre/downwind/blob/1d47b6a3f1e7bc271d0bb5bd96cfbbea68445510/src/vitePlugin.ts#L164 +function waitForIdlePlugin(): Plugin[] { + const idlePromise = createManualPromise(); + const notIdle = debounce(() => { + idlePromise.resolve(); + }, 200); + + return [ + { + name: waitForIdlePlugin.name, + apply: "build", + enforce: "pre", + buildStart() { + this.emitFile({ + type: "chunk", + id: "virtual:wait-for-idle", + }); + }, + resolveId: () => notIdle(), + load: () => notIdle(), + transform: () => notIdle(), + }, + createVirtualPlugin("wait-for-idle", async () => { + await idlePromise; + return `export default "** wait-for-idle **"`; + }), + ]; } diff --git a/packages/react-server/src/plugin/index.ts b/packages/react-server/src/plugin/index.ts index 01b6778a6..64148e344 100644 --- a/packages/react-server/src/plugin/index.ts +++ b/packages/react-server/src/plugin/index.ts @@ -305,8 +305,8 @@ export function vitePluginReactServer(options?: { async buildStart(_options) { if (!manager.buildType) { console.log("▶▶▶ REACT SERVER BUILD (scan) [1/4]"); - manager.buildType = "scan"; - await build(reactServerViteConfig); + // manager.buildType = "scan"; + // await build(reactServerViteConfig); console.log("▶▶▶ REACT SERVER BUILD (server) [2/4]"); manager.buildType = "rsc"; From f15a79212b2af2880ca98d86915df18deae5aa42 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 5 Jun 2024 14:52:44 +0900 Subject: [PATCH 5/8] chore: revert some --- packages/react-server/src/features/server-action/plugin.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/react-server/src/features/server-action/plugin.tsx b/packages/react-server/src/features/server-action/plugin.tsx index e7fa52923..477a79064 100644 --- a/packages/react-server/src/features/server-action/plugin.tsx +++ b/packages/react-server/src/features/server-action/plugin.tsx @@ -114,12 +114,6 @@ export function vitePluginServerUseServer({ id, outCode: output.toString(), }); - if (manager.buildType === "rsc") { - manager.serverReferenceMap[id] = this.emitFile({ - type: "chunk", - id, - }); - } return { code: output.toString(), map: output.generateMap(), From 3aea25aa49490d14c09798d4cde36dc2d4c5c276 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 5 Jun 2024 14:53:38 +0900 Subject: [PATCH 6/8] chore: more revert --- packages/react-server/src/plugin/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-server/src/plugin/index.ts b/packages/react-server/src/plugin/index.ts index 64148e344..5db166edc 100644 --- a/packages/react-server/src/plugin/index.ts +++ b/packages/react-server/src/plugin/index.ts @@ -65,7 +65,6 @@ class PluginStateManager { routeToClientReferences: Record = {}; routeManifest?: RouteManifest; - serverReferenceMap: Record = {}; // expose "use client" node modules to client via virtual modules // to avoid dual package due to deps optimization hash during dev From c0aa5c722eec2f2f5cc172816641ac9db3bb7e63 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 5 Jun 2024 15:13:27 +0900 Subject: [PATCH 7/8] refactor: tweak --- .../src/features/server-action/plugin.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/react-server/src/features/server-action/plugin.tsx b/packages/react-server/src/features/server-action/plugin.tsx index 477a79064..af407f2d8 100644 --- a/packages/react-server/src/features/server-action/plugin.tsx +++ b/packages/react-server/src/features/server-action/plugin.tsx @@ -114,6 +114,12 @@ export function vitePluginServerUseServer({ id, outCode: output.toString(), }); + if (manager.buildType === "rsc") { + this.emitFile({ + type: "chunk", + id, + }); + } return { code: output.toString(), map: output.generateMap(), @@ -154,7 +160,10 @@ export function vitePluginServerUseServer({ // https://github.com/ArnaudBarre/downwind/blob/1d47b6a3f1e7bc271d0bb5bd96cfbbea68445510/src/vitePlugin.ts#L164 function waitForIdlePlugin(): Plugin[] { const idlePromise = createManualPromise(); - const notIdle = debounce(() => { + let done = false; + const notIdle = debounce((...args) => { + console.log("[wait-for-idle:done]", { args }); + done = true; idlePromise.resolve(); }, 200); @@ -169,9 +178,9 @@ function waitForIdlePlugin(): Plugin[] { id: "virtual:wait-for-idle", }); }, - resolveId: () => notIdle(), - load: () => notIdle(), - transform: () => notIdle(), + resolveId: notIdle, + load: notIdle, + transform: notIdle, }, createVirtualPlugin("wait-for-idle", async () => { await idlePromise; From afdf4ecf6448fd22910eb811d62ac5cd50e029e0 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 5 Jun 2024 15:18:53 +0900 Subject: [PATCH 8/8] wip --- packages/react-server/src/plugin/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-server/src/plugin/index.ts b/packages/react-server/src/plugin/index.ts index 5db166edc..f402c011f 100644 --- a/packages/react-server/src/plugin/index.ts +++ b/packages/react-server/src/plugin/index.ts @@ -6,6 +6,7 @@ import { type Plugin, type PluginOption, type ResolvedConfig, + Rollup, type ViteDevServer, build, createLogger, @@ -62,6 +63,8 @@ class PluginStateManager { configEnv!: ConfigEnv; buildType?: "scan" | "rsc" | "client" | "ssr"; + buildContextServer?: Rollup.PluginContext; + buildContextBrowser?: Rollup.PluginContext; routeToClientReferences: Record = {}; routeManifest?: RouteManifest; @@ -303,13 +306,12 @@ export function vitePluginReactServer(options?: { apply: "build", async buildStart(_options) { if (!manager.buildType) { - console.log("▶▶▶ REACT SERVER BUILD (scan) [1/4]"); + // console.log("▶▶▶ REACT SERVER BUILD (scan) [1/4]"); // manager.buildType = "scan"; // await build(reactServerViteConfig); console.log("▶▶▶ REACT SERVER BUILD (server) [2/4]"); manager.buildType = "rsc"; - manager.rscUseClientIds.clear(); await build(reactServerViteConfig); console.log("▶▶▶ REACT SERVER BUILD (browser) [3/4]");