vite-plugin-react-server 0.3.18 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +220 -141
  2. package/bin/patch.mjs +8 -2
  3. package/dist/package.json +15 -8
  4. package/dist/plugin/checkFilesExist.d.ts.map +1 -1
  5. package/dist/plugin/checkFilesExist.js +6 -2
  6. package/dist/plugin/checkFilesExist.js.map +1 -1
  7. package/dist/plugin/collect-manifest-client-files.d.ts +23 -0
  8. package/dist/plugin/collect-manifest-client-files.d.ts.map +1 -0
  9. package/dist/plugin/collect-manifest-client-files.js +131 -0
  10. package/dist/plugin/collect-manifest-client-files.js.map +1 -0
  11. package/dist/plugin/components.d.ts +3 -13
  12. package/dist/plugin/components.d.ts.map +1 -1
  13. package/dist/plugin/components.js +3 -13
  14. package/dist/plugin/config/defaults.d.ts +14 -6
  15. package/dist/plugin/config/defaults.d.ts.map +1 -1
  16. package/dist/plugin/config/defaults.js +9 -5
  17. package/dist/plugin/config/defaults.js.map +1 -1
  18. package/dist/plugin/config/getPaths.d.ts +0 -1
  19. package/dist/plugin/config/getPaths.d.ts.map +1 -1
  20. package/dist/plugin/config/getPaths.js +2 -7
  21. package/dist/plugin/config/getPaths.js.map +1 -1
  22. package/dist/plugin/config/mimeTypes.d.ts +2 -0
  23. package/dist/plugin/config/mimeTypes.d.ts.map +1 -0
  24. package/dist/plugin/config/mimeTypes.js +24 -0
  25. package/dist/plugin/config/mimeTypes.js.map +1 -0
  26. package/dist/plugin/config/resolveOptions.d.ts +2 -2
  27. package/dist/plugin/config/resolveOptions.d.ts.map +1 -1
  28. package/dist/plugin/config/resolveOptions.js +47 -28
  29. package/dist/plugin/config/resolveOptions.js.map +1 -1
  30. package/dist/plugin/config/resolvePages.d.ts +1 -0
  31. package/dist/plugin/config/resolvePages.d.ts.map +1 -1
  32. package/dist/plugin/config/resolvePages.js +9 -5
  33. package/dist/plugin/config/resolvePages.js.map +1 -1
  34. package/dist/plugin/config/resolveUserConfig.d.ts +2 -1
  35. package/dist/plugin/config/resolveUserConfig.d.ts.map +1 -1
  36. package/dist/plugin/config/resolveUserConfig.js +181 -147
  37. package/dist/plugin/config/resolveUserConfig.js.map +1 -1
  38. package/dist/plugin/copy-dir.js +23 -18
  39. package/dist/plugin/copy-dir.js.map +1 -0
  40. package/dist/plugin/css-collector-inline.d.ts +10 -0
  41. package/dist/plugin/css-collector-inline.d.ts.map +1 -0
  42. package/dist/plugin/css-collector-inline.js +55 -0
  43. package/dist/plugin/css-collector-inline.js.map +1 -0
  44. package/dist/plugin/css-collector.d.ts +14 -0
  45. package/dist/plugin/css-collector.d.ts.map +1 -0
  46. package/dist/plugin/css-collector.js +49 -0
  47. package/dist/plugin/css-collector.js.map +1 -0
  48. package/dist/plugin/helpers/createHandler.d.ts +17 -0
  49. package/dist/plugin/helpers/createHandler.d.ts.map +1 -0
  50. package/dist/plugin/helpers/createHandler.js +111 -0
  51. package/dist/plugin/helpers/createHandler.js.map +1 -0
  52. package/dist/plugin/helpers/createRscStream.d.ts +27 -0
  53. package/dist/plugin/helpers/createRscStream.d.ts.map +1 -0
  54. package/dist/plugin/helpers/createRscStream.js +80 -0
  55. package/dist/plugin/helpers/createRscStream.js.map +1 -0
  56. package/dist/plugin/helpers/getBundleManifest.d.ts.map +1 -1
  57. package/dist/plugin/helpers/getBundleManifest.js +12 -4
  58. package/dist/plugin/helpers/getBundleManifest.js.map +1 -1
  59. package/dist/plugin/html.d.ts +5 -0
  60. package/dist/plugin/html.d.ts.map +1 -0
  61. package/dist/plugin/html.js +11 -0
  62. package/dist/plugin/html.js.map +1 -0
  63. package/dist/plugin/loader/createBuildLoader.d.ts +1 -1
  64. package/dist/plugin/loader/createBuildLoader.d.ts.map +1 -1
  65. package/dist/plugin/loader/createBuildLoader.js +8 -5
  66. package/dist/plugin/loader/createBuildLoader.js.map +1 -1
  67. package/dist/plugin/loader/css-loader.d.ts.map +1 -1
  68. package/dist/plugin/loader/css-loader.js.map +1 -1
  69. package/dist/plugin/loader/react-loader.js +2 -2
  70. package/dist/plugin/loader/react-loader.js.map +1 -1
  71. package/dist/plugin/preserver/plugin.d.ts.map +1 -1
  72. package/dist/plugin/preserver/plugin.js +49 -14
  73. package/dist/plugin/preserver/plugin.js.map +1 -1
  74. package/dist/plugin/react-client/plugin.d.ts.map +1 -1
  75. package/dist/plugin/react-client/plugin.js +21 -78
  76. package/dist/plugin/react-client/plugin.js.map +1 -1
  77. package/dist/plugin/react-server/index.d.ts.map +1 -1
  78. package/dist/plugin/react-server/index.js +2 -0
  79. package/dist/plugin/react-server/index.js.map +1 -1
  80. package/dist/plugin/react-server/plugin.d.ts +2 -1
  81. package/dist/plugin/react-server/plugin.d.ts.map +1 -1
  82. package/dist/plugin/react-server/plugin.js +92 -225
  83. package/dist/plugin/react-server/plugin.js.map +1 -1
  84. package/dist/plugin/react-static/index.d.ts +2 -0
  85. package/dist/plugin/react-static/index.d.ts.map +1 -0
  86. package/dist/plugin/react-static/index.js +1 -0
  87. package/dist/plugin/react-static/plugin.d.ts +7 -0
  88. package/dist/plugin/react-static/plugin.d.ts.map +1 -0
  89. package/dist/plugin/react-static/plugin.js +207 -0
  90. package/dist/plugin/react-static/plugin.js.map +1 -0
  91. package/dist/plugin/react-static/types.d.ts +2 -0
  92. package/dist/plugin/react-static/types.d.ts.map +1 -0
  93. package/dist/plugin/resolvePage.d.ts.map +1 -1
  94. package/dist/plugin/resolvePage.js +9 -0
  95. package/dist/plugin/resolvePage.js.map +1 -1
  96. package/dist/plugin/root.d.ts +2 -0
  97. package/dist/plugin/root.d.ts.map +1 -0
  98. package/dist/plugin/root.js +12 -0
  99. package/dist/plugin/root.js.map +1 -0
  100. package/dist/plugin/transformer/plugin.d.ts.map +1 -1
  101. package/dist/plugin/transformer/plugin.js +32 -23
  102. package/dist/plugin/transformer/plugin.js.map +1 -1
  103. package/dist/plugin/transformer/types.d.ts +1 -18
  104. package/dist/plugin/transformer/types.d.ts.map +1 -1
  105. package/dist/plugin/types.d.ts +70 -15
  106. package/dist/plugin/types.d.ts.map +1 -1
  107. package/dist/plugin/worker/createWorker.js +0 -1
  108. package/dist/plugin/worker/createWorker.js.map +1 -1
  109. package/dist/plugin/worker/html/html-worker.development.d.ts +30 -0
  110. package/dist/plugin/worker/html/html-worker.development.d.ts.map +1 -1
  111. package/dist/plugin/worker/html/html-worker.development.js +30 -2
  112. package/dist/plugin/worker/html/html-worker.development.js.map +1 -1
  113. package/dist/plugin/worker/html/html-worker.production.js +3 -5
  114. package/dist/plugin/worker/html/html-worker.production.js.map +1 -1
  115. package/dist/plugin/worker/html/messageHandler.d.ts.map +1 -1
  116. package/dist/plugin/worker/html/messageHandler.js +12 -3
  117. package/dist/plugin/worker/html/messageHandler.js.map +1 -1
  118. package/dist/plugin/worker/html/renderPages.d.ts +13 -26
  119. package/dist/plugin/worker/html/renderPages.d.ts.map +1 -1
  120. package/dist/plugin/worker/html/renderPages.js +138 -86
  121. package/dist/plugin/worker/html/renderPages.js.map +1 -1
  122. package/dist/plugin/worker/rsc/messageHandler.d.ts.map +1 -1
  123. package/dist/plugin/worker/rsc/messageHandler.js +104 -84
  124. package/dist/plugin/worker/rsc/messageHandler.js.map +1 -1
  125. package/dist/plugin/worker/rsc/rsc-worker.development.js +13 -18
  126. package/dist/plugin/worker/rsc/rsc-worker.development.js.map +1 -1
  127. package/dist/plugin/worker/rsc/rsc-worker.production.js +4 -1
  128. package/dist/plugin/worker/rsc/rsc-worker.production.js.map +1 -1
  129. package/dist/plugin/worker/rsc/state.d.ts.map +1 -1
  130. package/dist/plugin/worker/rsc/state.js.map +1 -1
  131. package/dist/plugin/worker/types.d.ts +3 -0
  132. package/dist/plugin/worker/types.d.ts.map +1 -1
  133. package/dist/tsconfig.tsbuildinfo +1 -1
  134. package/package.json +14 -6
  135. package/plugin/checkFilesExist.ts +7 -3
  136. package/plugin/collect-manifest-client-files.ts +167 -0
  137. package/plugin/components.ts +3 -0
  138. package/plugin/config/defaults.tsx +70 -0
  139. package/plugin/config/getPaths.ts +1 -7
  140. package/plugin/config/mimeTypes.ts +17 -0
  141. package/plugin/config/resolveOptions.ts +58 -45
  142. package/plugin/config/resolvePages.ts +8 -4
  143. package/plugin/config/resolveUserConfig.ts +220 -176
  144. package/plugin/css-collector-inline.tsx +60 -0
  145. package/plugin/css-collector.tsx +62 -0
  146. package/plugin/helpers/createHandler.ts +135 -0
  147. package/plugin/helpers/createRscStream.ts +109 -0
  148. package/plugin/helpers/getBundleManifest.ts +14 -5
  149. package/plugin/html.tsx +9 -0
  150. package/plugin/loader/createBuildLoader.ts +9 -6
  151. package/plugin/loader/css-loader.ts +0 -2
  152. package/plugin/loader/react-loader.ts +2 -2
  153. package/plugin/preserver/plugin.ts +64 -17
  154. package/plugin/react-client/plugin.ts +23 -93
  155. package/plugin/react-server/index.ts +2 -0
  156. package/plugin/react-server/plugin.ts +111 -302
  157. package/plugin/react-static/index.ts +1 -0
  158. package/plugin/react-static/plugin.ts +256 -0
  159. package/plugin/react-static/types.ts +3 -0
  160. package/plugin/resolvePage.ts +9 -0
  161. package/plugin/root.ts +4 -0
  162. package/plugin/transformer/plugin.ts +40 -31
  163. package/plugin/transformer/types.ts +0 -19
  164. package/plugin/types.ts +77 -16
  165. package/plugin/worker/createWorker.ts +1 -1
  166. package/plugin/worker/html/README.md +63 -0
  167. package/plugin/worker/html/html-worker.development.tsx +89 -2
  168. package/plugin/worker/html/html-worker.production.tsx +8 -10
  169. package/plugin/worker/html/messageHandler.ts +12 -3
  170. package/plugin/worker/html/renderPages.ts +178 -138
  171. package/plugin/worker/rsc/README.md +58 -0
  172. package/plugin/worker/rsc/messageHandler.tsx +112 -113
  173. package/plugin/worker/rsc/rsc-worker.development.ts +12 -22
  174. package/plugin/worker/rsc/rsc-worker.production.ts +5 -1
  175. package/plugin/worker/rsc/state.ts +0 -3
  176. package/plugin/worker/types.ts +3 -0
  177. package/scripts/react+0.0.0-experimental-eda36a1c-20250228.patch +114 -12
  178. package/scripts/react-dom+0.0.0-experimental-eda36a1c-20250228.patch +10571 -121
  179. package/tsconfig.json +2 -2
  180. package/dist/plugin/collect-css-manifest.d.ts +0 -4
  181. package/dist/plugin/collect-css-manifest.d.ts.map +0 -1
  182. package/dist/plugin/collect-css-manifest.js +0 -65
  183. package/dist/plugin/collect-css-manifest.js.map +0 -1
  184. package/dist/plugin/config/createModuleIdGenerator.d.ts +0 -11
  185. package/dist/plugin/config/createModuleIdGenerator.d.ts.map +0 -1
  186. package/dist/plugin/config/createModuleIdGenerator.js +0 -44
  187. package/dist/plugin/config/createModuleIdGenerator.js.map +0 -1
  188. package/dist/plugin/getEnv.d.ts +0 -19
  189. package/dist/plugin/getEnv.d.ts.map +0 -1
  190. package/dist/plugin/getEnv.js +0 -107
  191. package/dist/plugin/loader/createCssLoader.d.ts +0 -30
  192. package/dist/plugin/loader/createCssLoader.d.ts.map +0 -1
  193. package/dist/plugin/loader/createCssLoader.js +0 -35
  194. package/dist/plugin/loader/createPageLoader.d.ts +0 -24
  195. package/dist/plugin/loader/createPageLoader.d.ts.map +0 -1
  196. package/dist/plugin/loader/createPageLoader.js +0 -50
  197. package/dist/plugin/loader/rsc/messageHandler.d.ts +0 -2
  198. package/dist/plugin/loader/rsc/messageHandler.d.ts.map +0 -1
  199. package/dist/plugin/loader/rsc/rsc-worker.development.d.ts +0 -2
  200. package/dist/plugin/loader/rsc/rsc-worker.development.d.ts.map +0 -1
  201. package/dist/plugin/loader/rsc/rsc-worker.development.js +0 -1
  202. package/dist/plugin/module-graph.d.ts +0 -10
  203. package/dist/plugin/module-graph.d.ts.map +0 -1
  204. package/dist/plugin/module-graph.js +0 -35
  205. package/dist/plugin/react-server/createHandler.d.ts +0 -17
  206. package/dist/plugin/react-server/createHandler.d.ts.map +0 -1
  207. package/dist/plugin/react-server/createHandler.js +0 -126
  208. package/dist/plugin/react-server/createHandler.js.map +0 -1
  209. package/dist/plugin/react-server/createRscStream.d.ts +0 -16
  210. package/dist/plugin/react-server/createRscStream.d.ts.map +0 -1
  211. package/dist/plugin/react-server/createRscStream.js +0 -70
  212. package/dist/plugin/react-server/createRscStream.js.map +0 -1
  213. package/dist/plugin/react-server/createSsrHandler.d.ts +0 -4
  214. package/dist/plugin/react-server/createSsrHandler.d.ts.map +0 -1
  215. package/dist/plugin/react-server/createSsrHandler.js +0 -95
  216. package/dist/plugin/utils/logger.d.ts +0 -9
  217. package/dist/plugin/utils/logger.d.ts.map +0 -1
  218. package/dist/plugin/utils/logger.js +0 -68
  219. package/dist/plugin/utils/logger.js.map +0 -1
  220. package/dist/plugin/worker/html/plugin.d.ts +0 -4
  221. package/dist/plugin/worker/html/plugin.d.ts.map +0 -1
  222. package/dist/plugin/worker/html/plugin.js +0 -94
  223. package/dist/plugin/worker/plugin.d.ts +0 -19
  224. package/dist/plugin/worker/plugin.d.ts.map +0 -1
  225. package/dist/plugin/worker/plugin.js +0 -23
  226. package/dist/plugin/worker/rsc/plugin.d.ts +0 -4
  227. package/dist/plugin/worker/rsc/plugin.d.ts.map +0 -1
  228. package/dist/plugin/worker/rsc/plugin.js +0 -76
  229. package/plugin/collect-css-manifest.ts +0 -82
  230. package/plugin/components.tsx +0 -14
  231. package/plugin/config/createModuleIdGenerator.ts +0 -52
  232. package/plugin/config/defaults.ts +0 -51
  233. package/plugin/getEnv.ts +0 -135
  234. package/plugin/loader/createCssLoader.ts +0 -73
  235. package/plugin/loader/createPageLoader.ts +0 -103
  236. package/plugin/loader/rsc/messageHandler.tsx +0 -1
  237. package/plugin/loader/rsc/rsc-worker.development.ts +0 -1
  238. package/plugin/module-graph.ts +0 -48
  239. package/plugin/react-server/createHandler.ts +0 -162
  240. package/plugin/react-server/createRscStream.ts +0 -86
  241. package/plugin/react-server/createSsrHandler.ts +0 -125
  242. package/plugin/utils/logger.ts +0 -52
  243. package/plugin/worker/html/plugin.ts +0 -101
  244. package/plugin/worker/plugin.ts +0 -26
  245. package/plugin/worker/rsc/plugin.ts +0 -84
  246. /package/dist/plugin/{loader/rsc/messageHandler.js → react-static/types.js} +0 -0
@@ -1,8 +1,95 @@
1
+ // no offical types for node:module available yet (23.7.0)
2
+ declare module "node:module" {
3
+ export interface ImportAttributes {
4
+ [key: string]: string | undefined;
5
+ }
6
+
7
+ export interface ResolveHookContext {
8
+ conditions: string[];
9
+ parentURL: string | undefined;
10
+ importAttributes: ImportAttributes;
11
+ }
12
+
13
+ export interface LoadHookContext {
14
+ conditions: string[];
15
+ format: ModuleFormat | null | undefined;
16
+ importAttributes: ImportAttributes;
17
+ shortCircuit?: boolean;
18
+ }
19
+
20
+ export interface ResolveResult {
21
+ url: string;
22
+ shortCircuit: boolean;
23
+ }
24
+
25
+ export interface LoadResult {
26
+ format: string;
27
+ source: string | SharedArrayBuffer | Uint8Array;
28
+ shortCircuit: boolean;
29
+ }
30
+
31
+ export interface HooksAPI {
32
+ resolve?: (
33
+ specifier: string,
34
+ context: ResolveHookContext,
35
+ nextResolve: (
36
+ specifier: string,
37
+ context: ResolveHookContext
38
+ ) => ResolveResult
39
+ ) => ResolveResult;
40
+
41
+ load?: (
42
+ url: string,
43
+ context: LoadHookContext,
44
+ nextLoad: (url: string, context: LoadHookContext) => LoadResult
45
+ ) => LoadResult;
46
+ }
47
+
48
+ export function registerHooks(hooks: HooksAPI): void;
49
+ }
50
+ import { join } from "node:path";
51
+ import { pluginRoot } from "../../root.js";
1
52
  import { messageHandler } from "./messageHandler.js";
2
53
  import { parentPort } from "node:worker_threads";
54
+ import { register } from "node:module";
55
+ import { register as registerTsx } from "tsx/esm/api";
56
+ import { MessageChannel } from "node:worker_threads";
3
57
 
4
58
  if (!parentPort) throw new Error("This module must be run as a worker");
5
- parentPort?.on("message", messageHandler);
6
59
 
60
+ // Create channels for each loader
61
+ const reactLoaderChannel = new MessageChannel();
62
+ const cssLoaderChannel = new MessageChannel();
63
+
64
+ // Listen for messages from loaders
65
+ reactLoaderChannel.port2.on("message", messageHandler);
66
+ cssLoaderChannel.port2.on("message", messageHandler);
67
+
68
+ const loaderPath = "file://" + join(pluginRoot, "loader/react-loader.js");
69
+ const cssLoaderPath = "file://" + join(pluginRoot, "loader/css-loader.js");
70
+
71
+ // Register react-loader
72
+ register(loaderPath, {
73
+ parentURL: pluginRoot,
74
+ data: { port: reactLoaderChannel.port1 },
75
+ transferList: [reactLoaderChannel.port1],
76
+ });
77
+ register(cssLoaderPath, {
78
+ parentURL: pluginRoot,
79
+ data: { port: cssLoaderChannel.port1 },
80
+ transferList: [cssLoaderChannel.port1],
81
+ });
82
+
83
+ // Register loaders
84
+ registerTsx();
7
85
  // Signal ready with environment
8
- parentPort?.postMessage({ type: "READY", env: process.env["NODE_ENV"] });
86
+ parentPort?.on("message", messageHandler);
87
+ parentPort?.postMessage({
88
+ type: "READY",
89
+ env: process.env["NODE_ENV"],
90
+ pid: process.pid,
91
+ });
92
+
93
+ if (process.env["NODE_ENV"] !== "development") {
94
+ throw new Error("This module must be run in development mode");
95
+ }
@@ -1,18 +1,16 @@
1
1
  import { messageHandler } from "./messageHandler.js";
2
2
  import { parentPort } from "node:worker_threads";
3
- import { Window } from 'happy-dom';
4
- const window = new Window({ url: 'https://localhost:8080' });
5
- const document = window.document;
6
- globalThis.window = window as any;
7
- globalThis.document = document as any;
8
3
 
9
- let ready = false;
10
4
  if (!parentPort) throw new Error("This module must be run as a worker");
11
5
 
12
6
  // Signal ready with environment
13
7
  parentPort?.on("message", messageHandler);
14
- parentPort?.postMessage({
15
- type: "READY",
16
- env: process.env["NODE_ENV"],
17
- pid: process.pid
8
+ parentPort?.postMessage({
9
+ type: "READY",
10
+ env: process.env["NODE_ENV"],
11
+ pid: process.pid,
18
12
  });
13
+
14
+ if (process.env["NODE_ENV"] !== "production") {
15
+ throw new Error("This module must be run in development mode");
16
+ }
@@ -7,7 +7,6 @@ import {
7
7
  createFromNodeStream,
8
8
  // @ts-ignore
9
9
  } from "react-server-dom-esm/client.node";
10
- import { join } from "path";
11
10
 
12
11
  // Track active renders and streams
13
12
  const activeRenders = new Map<string, HtmlRenderState>();
@@ -29,7 +28,7 @@ export const messageHandler = async (message: HtmlWorkerMessage) => {
29
28
  moduleRootPath,
30
29
  moduleBaseURL,
31
30
  outDir: '',
32
- htmlOutputPath: htmlOutputPath ?? join(process.cwd(), 'index.html'),
31
+ htmlOutputPath: htmlOutputPath,
33
32
  pipableStreamOptions: pipableStreamOptions,
34
33
  });
35
34
  } else {
@@ -61,7 +60,7 @@ export const messageHandler = async (message: HtmlWorkerMessage) => {
61
60
  const reactElements = await createFromNodeStream(
62
61
  rscStream,
63
62
  render.moduleRootPath,
64
- 'localhost'
63
+ render.moduleBaseURL
65
64
  );
66
65
 
67
66
  // Create a promise that resolves when HTML is complete
@@ -88,6 +87,16 @@ export const messageHandler = async (message: HtmlWorkerMessage) => {
88
87
  reactElements as React.ReactNode,
89
88
  {
90
89
  ...render.pipableStreamOptions,
90
+ // Calculate relative paths based on route depth
91
+ bootstrapModules: render.pipableStreamOptions?.bootstrapModules?.map(path => {
92
+ if (!path) return path;
93
+ if(render.moduleBaseURL && render.moduleBaseURL !== '') {
94
+ return new URL(path, render.moduleBaseURL).toString();
95
+ }
96
+ const depth = id.split('/').filter(Boolean).length;
97
+ const prefix = depth > 0 ? '../'.repeat(depth) : '/';
98
+ return path.startsWith('/') ? prefix + path.slice(1) : prefix + path;
99
+ }),
91
100
  onShellReady() {
92
101
  parentPort?.postMessage({ type: "SHELL_READY", id });
93
102
  }
@@ -1,89 +1,116 @@
1
- import { mkdirSync } from "node:fs";
2
1
  import { mkdir, writeFile } from "node:fs/promises";
3
- import { dirname, join, resolve as resolvePath } from "node:path";
2
+ import { dirname, join } from "node:path";
4
3
  import { Transform } from "node:stream";
5
4
  import type { Worker } from "node:worker_threads";
6
- import { createHandler } from "../../react-server/createHandler.js";
7
- import type { CheckFilesExistReturn, ResolvedUserConfig, ResolvedUserOptions } from "../../types.js";
5
+ import { createHandler } from "../../helpers/createHandler.js";
8
6
  import type {
9
- HtmlWorkerResponse,
10
- WorkerRscChunkMessage,
11
- } from "../types.js";
12
- import type { Manifest } from "vite";
13
- import type {
14
- PluginContext,
15
- } from "rollup";
7
+ CheckFilesExistReturn,
8
+ CreateHandlerOptions,
9
+ PageData,
10
+ } from "../../types.js";
11
+ import type { HtmlWorkerResponse } from "../types.js";
12
+ import { type Manifest, type IndexHtmlTransformHook, createLogger } from "vite";
16
13
  import React from "react";
17
- import { collectManifestCss } from "../../collect-css-manifest.js";
18
-
19
- interface PipeableStreamOptions {
20
- bootstrapModules?: string[];
21
- bootstrapScripts?: string[];
22
- bootstrapScriptContent?: string;
23
- signal?: AbortSignal;
24
- identifierPrefix?: string;
25
- namespaceURI?: string;
26
- nonce?: string;
27
- progressiveChunkSize?: number;
28
- onShellReady?: () => void;
29
- onAllReady?: () => void;
30
- onError?: (error: unknown) => void;
31
- importMap?: {
32
- imports?: Record<string, string>;
33
- };
34
- }
14
+ import { collectManifestClientFiles } from "../../collect-manifest-client-files.js";
35
15
 
36
- type RenderPagesOptions = {
37
- pluginOptions: ResolvedUserOptions;
38
- userConfig: ResolvedUserConfig;
16
+ type RenderPagesOptions<T = any> = Omit<CreateHandlerOptions<T>, "url" | "route" | "getCss" | "propsPath" | "pagePath"> & {
39
17
  clientManifest: Manifest;
40
18
  serverManifest: Manifest;
41
19
  worker: Worker;
42
- pipableStreamOptions?: PipeableStreamOptions;
43
20
  loader: (id: string) => Promise<Record<string, any>>;
44
21
  onCssFile?: (url: string, parentUrl: string) => void;
22
+ onClientJSFile?: (url: string, parentUrl: string) => void;
23
+ onPage?: (pageData: PageData) => Promise<void>;
45
24
  clientCss?: string[];
46
- moduleBasePath: string;
47
- moduleBaseURL: string;
25
+ transformIndexHtml: IndexHtmlTransformHook;
26
+ outDir: string;
27
+ htmlOutputPath: string;
28
+ server?: any;
29
+ bundle?: any;
30
+ chunk?: any;
31
+ originalUrl?: string;
48
32
  };
49
33
 
50
- export async function renderPages(
51
- pluginContext: PluginContext,
34
+ export async function renderPages<T = any>(
52
35
  routes: string[],
53
36
  files: CheckFilesExistReturn,
54
- options: RenderPagesOptions,
37
+ options: RenderPagesOptions<T>
55
38
  ) {
56
- const root = pluginContext.environment.config.root;
57
- const outDir = pluginContext.environment.config.build.outDir;
58
39
  const failedRoutes = new Map<string, Error>();
59
40
  const completedRoutes = new Set<string>();
60
- const writePromises = new Map<string, Promise<void>>();
61
41
  const clientCss = options.clientCss ?? [];
42
+ const partialPageData = new Map<string, Partial<PageData>>();
43
+ const moduleRootPath =
44
+ options.moduleBasePath !== "" &&
45
+ !options.moduleRootPath.endsWith(options.moduleBasePath)
46
+ ? join(options.moduleRootPath, options.moduleBasePath)
47
+ : options.moduleRootPath;
48
+ const mergeAndSendPageData = async (route: string, resolve: () => void) => {
49
+ const partial = partialPageData.get(route);
50
+ if (!partial?.html || !partial.rsc) {
51
+ return; // Wait for both parts
52
+ }
53
+
54
+ const pageData: PageData = {
55
+ route,
56
+ html: partial.html,
57
+ rsc: partial.rsc,
58
+ };
59
+
60
+ // Write HTML file
61
+ let routeHtmlPath =
62
+ route === "/"
63
+ ? options.htmlOutputPath
64
+ : options.htmlOutputPath.replace(
65
+ "index.html",
66
+ join(route, "index.html")
67
+ );
68
+ if (routeHtmlPath.startsWith("/")) {
69
+ routeHtmlPath = routeHtmlPath.slice(1);
70
+ }
71
+ const routeRscPath = routeHtmlPath.slice(0, -5) + ".rsc";
72
+ await mkdir(dirname(routeHtmlPath), { recursive: true });
73
+ await writeFile(routeRscPath, partial.rsc.content);
74
+ await writeFile(routeHtmlPath, partial.html.raw);
75
+
76
+ await options.onPage?.(pageData);
77
+ completedRoutes.add(route);
78
+ if (completedRoutes.size === routes.length) {
79
+ resolve();
80
+ }
81
+ };
82
+
62
83
  try {
63
84
  // Set up worker message handling
64
85
  const allRoutesComplete = new Promise<void>((resolve, reject) => {
65
- options.worker.on("message", (msg: HtmlWorkerResponse) => {
86
+ options.worker.on("message", async (msg: HtmlWorkerResponse) => {
66
87
  switch (msg.type) {
67
88
  case "ALL_READY": {
68
- const { id, html, outputPath } = msg;
69
- mkdirSync(dirname(outputPath), { recursive: true });
70
-
71
- writeFile(outputPath, html)
72
- .then(() => {
73
- completedRoutes.add(id);
74
- if (completedRoutes.size === routes.length) {
75
- resolve();
76
- }
77
- })
78
- .catch((error) => {
79
- console.error('Write error for route:', id, error);
80
- failedRoutes.set(id, error as Error);
81
- reject(error);
82
- });
89
+ const { id, html } = msg;
90
+ try {
91
+ const partial = partialPageData.get(id) || { route: id };
92
+
93
+ partial.html = {
94
+ raw: html,
95
+ transformed:
96
+ typeof options.transformIndexHtml === "function"
97
+ ? String(await options.transformIndexHtml(id, {
98
+ path: id,
99
+ filename: join(id, "index.html"),
100
+ }) || "")
101
+ : "", // Will be set by main thread transform
102
+ assets: [],
103
+ };
104
+ partialPageData.set(id, partial);
105
+ await mergeAndSendPageData(id, resolve);
106
+ } catch (error) {
107
+ failedRoutes.set(id, error as Error);
108
+ reject(error);
109
+ }
83
110
  break;
84
111
  }
85
112
  case "ERROR": {
86
- console.error('Worker error for route:', msg.id, msg.error);
113
+ console.error("Worker error for route:", msg.id, msg.error);
87
114
  failedRoutes.set(msg.id, new Error(msg.error));
88
115
  reject(new Error(msg.error));
89
116
  break;
@@ -92,124 +119,138 @@ export async function renderPages(
92
119
  });
93
120
  });
94
121
 
95
- const newCssFiles = collectManifestCss(
96
- options.clientManifest,
97
- options.moduleBasePath,
98
- 'index.html',
99
- (url, parentUrl)=>{
100
- options?.onCssFile?.(url, parentUrl);
101
- if(!clientCss.includes(url)){
102
- clientCss.push(url);
103
- }
104
- },
105
- join(root, options.pluginOptions.build.outDir, options.pluginOptions.build.client)
106
- );
107
-
108
122
  // Process routes sequentially
109
123
  for (const route of routes) {
110
124
  const routeFiles = files.urlMap.get(route);
111
125
  if (!routeFiles) {
112
- console.error('No files found for route:', route);
126
+ console.error("No files found for route:", route);
113
127
  failedRoutes.set(route, new Error(`No files found for ${route}`));
114
128
  continue;
115
129
  }
116
130
 
117
- collectManifestCss(
118
- options.serverManifest,
119
- options.moduleBasePath,
120
- routeFiles.page,
121
- (url, parentUrl)=>{
122
- options.onCssFile?.(url, parentUrl);
123
- if(!clientCss.includes(url)){
124
- clientCss.push(url);
125
- }
131
+ if (options.pipableStreamOptions?.importMap?.imports) {
132
+ for (let [, value] of Object.entries(
133
+ options.pipableStreamOptions?.importMap?.imports
134
+ )) {
135
+ options.onClientJSFile?.(value, route);
126
136
  }
127
- );
137
+ }
138
+ const getCss = async (id: string) => {
139
+ const cssFiles = collectManifestClientFiles({
140
+ manifest: options.serverManifest,
141
+ root: options.root,
142
+ pagePath: id,
143
+ }).cssFiles;
144
+ return cssFiles;
145
+ }
146
+ const pagePath = files.urlMap.get(route)?.page;
147
+ const propsPath = files.urlMap.get(route)?.props;
148
+ if(!pagePath){
149
+ throw new Error(`No page path found for ${route}`);
150
+ }
128
151
  // Create handler for pure RSC output
129
- const rscResult = await createHandler(route, {
130
- ...options.pluginOptions,
131
- Html: React.Fragment // Use Fragment for pure RSC output
132
- }, {
152
+ const rscResult = await createHandler({
153
+ root: options.root,
154
+ url: route,
155
+ route: route,
156
+ getCss: getCss,
133
157
  loader: options.loader,
134
- clientManifest: options.clientManifest,
135
- serverManifest: options.serverManifest,
136
- cssFiles: Array.from(newCssFiles.values()).concat(clientCss),
137
- pipableStreamOptions: {
138
- ...options.pipableStreamOptions,
139
- importMap: {
140
- imports: {
141
- ...options.pipableStreamOptions?.importMap?.imports,
142
- }
143
- }
144
- },
158
+ cssFiles: clientCss,
159
+ moduleBase: options.moduleBase,
160
+ moduleBasePath: options.moduleBasePath,
161
+ moduleRootPath: moduleRootPath,
162
+ moduleBaseURL: options.moduleBaseURL,
163
+ pipableStreamOptions: options.pipableStreamOptions ?? {},
164
+ inlineCss: options.inlineCss,
165
+ Html: React.Fragment,
166
+ CssCollector: options.CssCollector,
167
+ pagePath: pagePath,
168
+ propsPath: propsPath,
169
+ pageExportName: options.pageExportName,
170
+ propsExportName: options.propsExportName,
171
+ logger: createLogger(),
145
172
  });
146
-
147
173
  // Create handler for HTML output
148
- const htmlResult = await createHandler(route, options.pluginOptions, {
174
+ const htmlResult = await createHandler({
175
+ root: options.root,
176
+ url: route,
177
+ route: route,
178
+ getCss: getCss,
149
179
  loader: options.loader,
150
- clientManifest: options.clientManifest,
151
- serverManifest: options.serverManifest,
152
180
  cssFiles: clientCss,
153
- pipableStreamOptions: {
154
- ...options.pipableStreamOptions,
155
- importMap: {
156
- imports: {
157
- ...options.pipableStreamOptions?.importMap?.imports
158
- }
159
- }
160
- },
181
+ moduleBase: options.moduleBase,
182
+ moduleBasePath: options.moduleBasePath,
183
+ moduleRootPath: moduleRootPath,
184
+ moduleBaseURL: options.moduleBaseURL,
185
+ pipableStreamOptions: options.pipableStreamOptions,
186
+ inlineCss: options.inlineCss,
187
+ Html: options.Html,
188
+ CssCollector: options.CssCollector,
189
+ pagePath: pagePath,
190
+ propsPath: propsPath,
191
+ pageExportName: options.pageExportName,
192
+ propsExportName: options.propsExportName,
193
+ logger: createLogger(),
161
194
  });
162
195
 
163
196
  if (rscResult.type !== "success" || htmlResult.type !== "success") {
164
- console.error('Handler failed for route:', route);
197
+ if (rscResult.type !== "success") {
198
+ if (rscResult.type !== "skip") {
199
+ console.error("Handler failed for route:", route, rscResult.error);
200
+ }
201
+ }
202
+ if (htmlResult.type !== "success") {
203
+ if (htmlResult.type !== "skip") {
204
+ console.error("Handler failed for route:", route, htmlResult.error);
205
+ }
206
+ }
165
207
  failedRoutes.set(route, new Error(`Handler failed for ${route}`));
166
208
  continue;
167
209
  }
168
210
 
169
211
  // Process both streams
170
212
  await Promise.all([
171
- // Save RSC stream to .rsc file in client directory
213
+ // Handle RSC stream
172
214
  new Promise<void>((resolve, reject) => {
173
215
  const chunks: Buffer[] = [];
174
216
  const rscTransform = new Transform({
175
217
  transform(chunk, _encoding, callback) {
176
218
  try {
177
- chunks.push(Buffer.from(chunk));
178
- callback(null, chunk);
219
+ if (chunk) {
220
+ chunks.push(Buffer.from(chunk));
221
+ callback(null, chunk);
222
+ }
179
223
  } catch (error) {
180
224
  callback(error as Error);
181
225
  }
182
226
  },
183
- flush(callback) {
227
+ async flush(callback) {
184
228
  try {
185
- const rscPath = join(options.pluginOptions.build.outDir, options.pluginOptions.build.client, route, 'index.rsc');
186
-
187
- // Ensure directory exists
188
- mkdirSync(dirname(rscPath), { recursive: true });
189
-
190
- // Write complete file
191
- writeFile(rscPath, Buffer.concat(chunks))
192
- .then(() => {
193
- callback();
194
- resolve();
195
- })
196
- .catch(error => {
197
- console.error('RSC write error:', error);
198
- callback(error as Error);
199
- reject(error);
200
- });
229
+ const rscContent = Buffer.concat(chunks).toString("utf-8");
230
+
231
+ // Update partial page data with raw RSC content
232
+ const partial = partialPageData.get(route) || { route };
233
+ partial.rsc = {
234
+ modules: [], // Will be parsed by the client
235
+ content: rscContent,
236
+ };
237
+ partialPageData.set(route, partial);
238
+ await mergeAndSendPageData(route, resolve);
239
+
240
+ callback();
241
+ resolve();
201
242
  } catch (error) {
202
243
  callback(error as Error);
203
244
  reject(error);
204
245
  }
205
- }
246
+ },
206
247
  });
207
248
 
208
249
  rscResult.stream.pipe(rscTransform);
209
250
  }),
210
251
 
211
252
  // Send HTML stream to worker
212
- new Promise<void>((resolve, reject) => {
253
+ new Promise<void>((resolve) => {
213
254
  const htmlTransform = new Transform({
214
255
  transform(chunk, _encoding, callback) {
215
256
  try {
@@ -217,9 +258,9 @@ export async function renderPages(
217
258
  type: "RSC_CHUNK",
218
259
  id: route,
219
260
  chunk: chunk.toString(),
220
- moduleRootPath: join(root, options.pluginOptions.build.outDir, options.pluginOptions.build.client),
261
+ moduleRootPath: moduleRootPath,
221
262
  moduleBaseURL: options.moduleBaseURL,
222
- htmlOutputPath: join(options.pluginOptions.build.outDir, options.pluginOptions.build.client, route, 'index.html'),
263
+ htmlOutputPath: options.htmlOutputPath,
223
264
  pipableStreamOptions: options.pipableStreamOptions,
224
265
  });
225
266
  callback(null, chunk);
@@ -234,18 +275,17 @@ export async function renderPages(
234
275
  });
235
276
  callback();
236
277
  resolve();
237
- }
278
+ },
238
279
  });
239
280
 
240
281
  htmlResult.stream.pipe(htmlTransform);
241
- })
282
+ }),
242
283
  ]);
243
284
  }
244
285
 
245
286
  await allRoutesComplete;
246
-
247
287
  } catch (error) {
248
- console.error('Render error:', error);
288
+ console.error("Render error:", error);
249
289
  throw error;
250
290
  }
251
291
 
@@ -0,0 +1,58 @@
1
+ # RSC Worker
2
+
3
+ The RSC worker is used by the client plugin to handle React Server Components when the main thread is running without the `react-server` condition. It provides the server-condition capabilities that the client plugin needs but can't access directly.
4
+
5
+ ## Purpose
6
+
7
+ The RSC worker serves several key functions:
8
+
9
+ 1. **Server Condition Access**: Enables RSC processing in a client-focused environment
10
+ 2. **Message-Based Communication**: Handles RSC streaming through a message passing interface
11
+
12
+ ## Implementation Details
13
+
14
+ The worker comes in two variants:
15
+
16
+ - `rsc-worker.development.ts`: Full implementation with detailed logging and error handling
17
+ - `rsc-worker.production.ts`: Optimized implementation for production builds
18
+
19
+ ## Extending the Worker
20
+
21
+ Users can create their own RSC workers for application-level use. This allows for:
22
+
23
+ 1. Custom RSC processing logic
24
+ 2. Application-specific message handling
25
+ 3. Specialized worker architectures
26
+
27
+ Example of creating a custom RSC worker:
28
+
29
+ ```typescript
30
+ import { messageHandler } from 'vite-plugin-react-server/worker/rsc/messageHandler'
31
+ import type { RscWorkerMessage } from 'vite-plugin-react-server/worker/types'
32
+
33
+ // Create your custom message handler
34
+ const customMessageHandler = async (msg: RscWorkerMessage) => {
35
+ // Add your custom logic here
36
+ return messageHandler(msg)
37
+ }
38
+
39
+ // Initialize your worker
40
+ if (typeof WorkerGlobalScope !== 'undefined') {
41
+ parentPort.on('message', customMessageHandler)
42
+ }
43
+ ```
44
+
45
+ ## Future Possibilities
46
+
47
+ The RSC worker architecture is designed to be extensible. Future enhancements could include:
48
+
49
+ - Custom streaming strategies
50
+ - Advanced caching mechanisms
51
+ - Specialized RSC processing pipelines
52
+ - Integration with other build tools
53
+
54
+ ## Notes
55
+
56
+ - The worker must be initialized with appropriate Node conditions
57
+ - Message handling must account for streaming data
58
+ - Consider memory usage when processing large RSC payloads