vite-plugin-react-server 0.3.18 → 0.3.19

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 (208) hide show
  1. package/README.md +119 -118
  2. package/bin/patch.mjs +8 -2
  3. package/dist/package.json +3 -3
  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 +117 -0
  10. package/dist/plugin/collect-manifest-client-files.js.map +1 -0
  11. package/dist/plugin/components.d.ts +9 -9
  12. package/dist/plugin/components.d.ts.map +1 -1
  13. package/dist/plugin/components.js +50 -9
  14. package/dist/plugin/components.js.map +1 -0
  15. package/dist/plugin/config/defaults.d.ts +7 -6
  16. package/dist/plugin/config/defaults.d.ts.map +1 -1
  17. package/dist/plugin/config/defaults.js +8 -5
  18. package/dist/plugin/config/defaults.js.map +1 -1
  19. package/dist/plugin/config/getPaths.d.ts +0 -1
  20. package/dist/plugin/config/getPaths.d.ts.map +1 -1
  21. package/dist/plugin/config/getPaths.js +2 -7
  22. package/dist/plugin/config/getPaths.js.map +1 -1
  23. package/dist/plugin/config/mimeTypes.d.ts +2 -0
  24. package/dist/plugin/config/mimeTypes.d.ts.map +1 -0
  25. package/dist/plugin/config/mimeTypes.js +24 -0
  26. package/dist/plugin/config/mimeTypes.js.map +1 -0
  27. package/dist/plugin/config/resolveOptions.d.ts +1 -1
  28. package/dist/plugin/config/resolveOptions.d.ts.map +1 -1
  29. package/dist/plugin/config/resolveOptions.js +41 -28
  30. package/dist/plugin/config/resolveOptions.js.map +1 -1
  31. package/dist/plugin/config/resolvePages.d.ts +1 -0
  32. package/dist/plugin/config/resolvePages.d.ts.map +1 -1
  33. package/dist/plugin/config/resolvePages.js +9 -5
  34. package/dist/plugin/config/resolvePages.js.map +1 -1
  35. package/dist/plugin/config/resolveUserConfig.d.ts +2 -1
  36. package/dist/plugin/config/resolveUserConfig.d.ts.map +1 -1
  37. package/dist/plugin/config/resolveUserConfig.js +10 -5
  38. package/dist/plugin/config/resolveUserConfig.js.map +1 -1
  39. package/dist/plugin/copy-dir.js +23 -18
  40. package/dist/plugin/copy-dir.js.map +1 -0
  41. package/dist/plugin/helpers/createHandler.d.ts +22 -0
  42. package/dist/plugin/helpers/createHandler.d.ts.map +1 -0
  43. package/dist/plugin/{react-server → helpers}/createHandler.js +36 -48
  44. package/dist/plugin/helpers/createHandler.js.map +1 -0
  45. package/dist/plugin/{react-server → helpers}/createRscStream.d.ts +2 -1
  46. package/dist/plugin/helpers/createRscStream.d.ts.map +1 -0
  47. package/dist/plugin/helpers/createRscStream.js +71 -0
  48. package/dist/plugin/helpers/createRscStream.js.map +1 -0
  49. package/dist/plugin/helpers/getBundleManifest.d.ts.map +1 -1
  50. package/dist/plugin/helpers/getBundleManifest.js +12 -4
  51. package/dist/plugin/helpers/getBundleManifest.js.map +1 -1
  52. package/dist/plugin/loader/createBuildLoader.d.ts +1 -1
  53. package/dist/plugin/loader/createBuildLoader.d.ts.map +1 -1
  54. package/dist/plugin/loader/createBuildLoader.js +8 -5
  55. package/dist/plugin/loader/createBuildLoader.js.map +1 -1
  56. package/dist/plugin/loader/css-loader.d.ts.map +1 -1
  57. package/dist/plugin/loader/css-loader.js.map +1 -1
  58. package/dist/plugin/loader/react-loader.js +2 -2
  59. package/dist/plugin/loader/react-loader.js.map +1 -1
  60. package/dist/plugin/preserver/plugin.d.ts.map +1 -1
  61. package/dist/plugin/preserver/plugin.js +49 -14
  62. package/dist/plugin/preserver/plugin.js.map +1 -1
  63. package/dist/plugin/react-client/plugin.d.ts.map +1 -1
  64. package/dist/plugin/react-client/plugin.js +18 -76
  65. package/dist/plugin/react-client/plugin.js.map +1 -1
  66. package/dist/plugin/react-server/index.d.ts.map +1 -1
  67. package/dist/plugin/react-server/index.js +2 -0
  68. package/dist/plugin/react-server/index.js.map +1 -1
  69. package/dist/plugin/react-server/plugin.d.ts +2 -1
  70. package/dist/plugin/react-server/plugin.d.ts.map +1 -1
  71. package/dist/plugin/react-server/plugin.js +53 -217
  72. package/dist/plugin/react-server/plugin.js.map +1 -1
  73. package/dist/plugin/react-static/index.d.ts +2 -0
  74. package/dist/plugin/react-static/index.d.ts.map +1 -0
  75. package/dist/plugin/react-static/index.js +1 -0
  76. package/dist/plugin/react-static/plugin.d.ts +7 -0
  77. package/dist/plugin/react-static/plugin.d.ts.map +1 -0
  78. package/dist/plugin/react-static/plugin.js +199 -0
  79. package/dist/plugin/react-static/plugin.js.map +1 -0
  80. package/dist/plugin/resolvePage.d.ts.map +1 -1
  81. package/dist/plugin/resolvePage.js +9 -0
  82. package/dist/plugin/resolvePage.js.map +1 -1
  83. package/dist/plugin/root.d.ts +2 -0
  84. package/dist/plugin/root.d.ts.map +1 -0
  85. package/dist/plugin/root.js +12 -0
  86. package/dist/plugin/root.js.map +1 -0
  87. package/dist/plugin/transformer/plugin.d.ts.map +1 -1
  88. package/dist/plugin/transformer/plugin.js +32 -23
  89. package/dist/plugin/transformer/plugin.js.map +1 -1
  90. package/dist/plugin/transformer/types.d.ts +1 -18
  91. package/dist/plugin/transformer/types.d.ts.map +1 -1
  92. package/dist/plugin/types.d.ts +24 -6
  93. package/dist/plugin/types.d.ts.map +1 -1
  94. package/dist/plugin/worker/createWorker.js +0 -1
  95. package/dist/plugin/worker/createWorker.js.map +1 -1
  96. package/dist/plugin/worker/html/html-worker.development.d.ts +30 -0
  97. package/dist/plugin/worker/html/html-worker.development.d.ts.map +1 -1
  98. package/dist/plugin/worker/html/html-worker.development.js +30 -2
  99. package/dist/plugin/worker/html/html-worker.development.js.map +1 -1
  100. package/dist/plugin/worker/html/html-worker.production.js +3 -5
  101. package/dist/plugin/worker/html/html-worker.production.js.map +1 -1
  102. package/dist/plugin/worker/html/messageHandler.d.ts.map +1 -1
  103. package/dist/plugin/worker/html/messageHandler.js +8 -2
  104. package/dist/plugin/worker/html/messageHandler.js.map +1 -1
  105. package/dist/plugin/worker/html/plugin.d.ts.map +1 -1
  106. package/dist/plugin/worker/html/plugin.js +2 -3
  107. package/dist/plugin/worker/html/renderPages.d.ts +8 -4
  108. package/dist/plugin/worker/html/renderPages.d.ts.map +1 -1
  109. package/dist/plugin/worker/html/renderPages.js +118 -83
  110. package/dist/plugin/worker/html/renderPages.js.map +1 -1
  111. package/dist/plugin/worker/rsc/messageHandler.d.ts.map +1 -1
  112. package/dist/plugin/worker/rsc/messageHandler.js +89 -84
  113. package/dist/plugin/worker/rsc/messageHandler.js.map +1 -1
  114. package/dist/plugin/worker/rsc/plugin.d.ts.map +1 -1
  115. package/dist/plugin/worker/rsc/plugin.js +1 -2
  116. package/dist/plugin/worker/rsc/rsc-worker.development.js +13 -18
  117. package/dist/plugin/worker/rsc/rsc-worker.development.js.map +1 -1
  118. package/dist/plugin/worker/rsc/rsc-worker.production.js +4 -1
  119. package/dist/plugin/worker/rsc/rsc-worker.production.js.map +1 -1
  120. package/dist/plugin/worker/rsc/state.d.ts.map +1 -1
  121. package/dist/plugin/worker/rsc/state.js.map +1 -1
  122. package/dist/tsconfig.tsbuildinfo +1 -1
  123. package/package.json +3 -3
  124. package/plugin/checkFilesExist.ts +7 -3
  125. package/plugin/collect-manifest-client-files.ts +152 -0
  126. package/plugin/components.tsx +55 -10
  127. package/plugin/config/defaults.tsx +69 -0
  128. package/plugin/config/getPaths.ts +1 -7
  129. package/plugin/config/mimeTypes.ts +17 -0
  130. package/plugin/config/resolveOptions.ts +48 -40
  131. package/plugin/config/resolvePages.ts +8 -4
  132. package/plugin/config/resolveUserConfig.ts +12 -9
  133. package/plugin/{react-server → helpers}/createHandler.ts +46 -63
  134. package/plugin/helpers/createRscStream.ts +81 -0
  135. package/plugin/helpers/getBundleManifest.ts +14 -5
  136. package/plugin/loader/createBuildLoader.ts +9 -6
  137. package/plugin/loader/css-loader.ts +0 -2
  138. package/plugin/loader/react-loader.ts +2 -2
  139. package/plugin/preserver/plugin.ts +64 -17
  140. package/plugin/react-client/plugin.ts +20 -91
  141. package/plugin/react-server/index.ts +2 -0
  142. package/plugin/react-server/plugin.ts +66 -293
  143. package/plugin/react-static/index.ts +1 -0
  144. package/plugin/react-static/plugin.ts +247 -0
  145. package/plugin/resolvePage.ts +9 -0
  146. package/plugin/root.ts +4 -0
  147. package/plugin/transformer/plugin.ts +40 -31
  148. package/plugin/transformer/types.ts +0 -19
  149. package/plugin/types.ts +25 -6
  150. package/plugin/worker/createWorker.ts +1 -1
  151. package/plugin/worker/html/README.md +63 -0
  152. package/plugin/worker/html/html-worker.development.tsx +89 -2
  153. package/plugin/worker/html/html-worker.production.tsx +8 -10
  154. package/plugin/worker/html/messageHandler.ts +8 -2
  155. package/plugin/worker/html/plugin.ts +2 -3
  156. package/plugin/worker/html/renderPages.ts +150 -114
  157. package/plugin/worker/rsc/README.md +58 -0
  158. package/plugin/worker/rsc/messageHandler.tsx +95 -111
  159. package/plugin/worker/rsc/plugin.ts +1 -2
  160. package/plugin/worker/rsc/rsc-worker.development.ts +12 -22
  161. package/plugin/worker/rsc/rsc-worker.production.ts +5 -1
  162. package/plugin/worker/rsc/state.ts +0 -3
  163. package/scripts/react+0.0.0-experimental-eda36a1c-20250228.patch +114 -12
  164. package/scripts/react-dom+0.0.0-experimental-eda36a1c-20250228.patch +10571 -121
  165. package/tsconfig.json +2 -2
  166. package/dist/plugin/collect-css-manifest.d.ts +0 -4
  167. package/dist/plugin/collect-css-manifest.d.ts.map +0 -1
  168. package/dist/plugin/collect-css-manifest.js +0 -65
  169. package/dist/plugin/collect-css-manifest.js.map +0 -1
  170. package/dist/plugin/config/createModuleIdGenerator.d.ts +0 -11
  171. package/dist/plugin/config/createModuleIdGenerator.d.ts.map +0 -1
  172. package/dist/plugin/config/createModuleIdGenerator.js +0 -44
  173. package/dist/plugin/config/createModuleIdGenerator.js.map +0 -1
  174. package/dist/plugin/loader/createCssLoader.d.ts +0 -30
  175. package/dist/plugin/loader/createCssLoader.d.ts.map +0 -1
  176. package/dist/plugin/loader/createCssLoader.js +0 -35
  177. package/dist/plugin/loader/createPageLoader.d.ts +0 -24
  178. package/dist/plugin/loader/createPageLoader.d.ts.map +0 -1
  179. package/dist/plugin/loader/createPageLoader.js +0 -50
  180. package/dist/plugin/loader/rsc/messageHandler.d.ts +0 -2
  181. package/dist/plugin/loader/rsc/messageHandler.d.ts.map +0 -1
  182. package/dist/plugin/loader/rsc/messageHandler.js +0 -1
  183. package/dist/plugin/loader/rsc/rsc-worker.development.d.ts +0 -2
  184. package/dist/plugin/loader/rsc/rsc-worker.development.d.ts.map +0 -1
  185. package/dist/plugin/loader/rsc/rsc-worker.development.js +0 -1
  186. package/dist/plugin/react-server/createHandler.d.ts +0 -17
  187. package/dist/plugin/react-server/createHandler.d.ts.map +0 -1
  188. package/dist/plugin/react-server/createHandler.js.map +0 -1
  189. package/dist/plugin/react-server/createRscStream.d.ts.map +0 -1
  190. package/dist/plugin/react-server/createRscStream.js +0 -70
  191. package/dist/plugin/react-server/createRscStream.js.map +0 -1
  192. package/dist/plugin/react-server/createSsrHandler.d.ts +0 -4
  193. package/dist/plugin/react-server/createSsrHandler.d.ts.map +0 -1
  194. package/dist/plugin/react-server/createSsrHandler.js +0 -95
  195. package/dist/plugin/utils/logger.d.ts +0 -9
  196. package/dist/plugin/utils/logger.d.ts.map +0 -1
  197. package/dist/plugin/utils/logger.js +0 -68
  198. package/dist/plugin/utils/logger.js.map +0 -1
  199. package/plugin/collect-css-manifest.ts +0 -82
  200. package/plugin/config/createModuleIdGenerator.ts +0 -52
  201. package/plugin/config/defaults.ts +0 -51
  202. package/plugin/loader/createCssLoader.ts +0 -73
  203. package/plugin/loader/createPageLoader.ts +0 -103
  204. package/plugin/loader/rsc/messageHandler.tsx +0 -1
  205. package/plugin/loader/rsc/rsc-worker.development.ts +0 -1
  206. package/plugin/react-server/createRscStream.ts +0 -86
  207. package/plugin/react-server/createSsrHandler.ts +0 -125
  208. package/plugin/utils/logger.ts +0 -52
@@ -0,0 +1,247 @@
1
+ import { join, dirname } from "node:path";
2
+ import { Worker } from "node:worker_threads";
3
+ import {
4
+ type ResolvedConfig,
5
+ type UserConfig,
6
+ type Manifest,
7
+ type IndexHtmlTransformHook,
8
+ type Plugin as VitePlugin,
9
+ } from "vite";
10
+ import { checkFilesExist } from "../checkFilesExist.js";
11
+ import { resolveOptions } from "../config/resolveOptions.js";
12
+ import { resolvePages } from "../config/resolvePages.js";
13
+ import { resolveUserConfig } from "../config/resolveUserConfig.js";
14
+ import { tryManifest } from "../helpers/tryManifest.js";
15
+ import { createBuildLoader } from "../loader/createBuildLoader.js";
16
+ import type {
17
+ BuildTiming,
18
+ CheckFilesExistReturn,
19
+ ReactStreamPluginMeta,
20
+ ResolvedUserConfig,
21
+ ResolvedUserOptions,
22
+ } from "../types.js";
23
+ import { type StreamPluginOptions } from "../types.js";
24
+ import { createWorker } from "../worker/createWorker.js";
25
+ import { renderPages } from "../worker/html/renderPages.js";
26
+ import { mkdir } from "node:fs/promises";
27
+ import { collectManifestClientFiles } from "../collect-manifest-client-files.js";
28
+ import { mkdirSync, copyFileSync } from "node:fs";
29
+ import { copyDir } from "../copy-dir.js";
30
+
31
+ let resolvedConfig: ResolvedConfig | null = null;
32
+ let loader: ((id: string) => Promise<Record<string, any>>) | null = null;
33
+ let worker: Worker;
34
+ let htmlTransform: IndexHtmlTransformHook | null = null;
35
+ let clientAssets = new Set<string>();
36
+
37
+ export function reactStaticPlugin(options: StreamPluginOptions): VitePlugin<{
38
+ meta: ReactStreamPluginMeta;
39
+ }> {
40
+ const timing: BuildTiming = {
41
+ start: Date.now(),
42
+ };
43
+
44
+ let files: CheckFilesExistReturn;
45
+ let root: string = process.cwd();
46
+ let userConfig: ResolvedUserConfig;
47
+ let userOptions: ResolvedUserOptions;
48
+ let resolvedPages: string[];
49
+ let serverManifest: Manifest = {};
50
+ let clientManifest: Manifest = {};
51
+
52
+ const resolvedOptions = resolveOptions(options, false);
53
+ if (resolvedOptions.type === "error") {
54
+ throw resolvedOptions.error;
55
+ }
56
+ userOptions = resolvedOptions.userOptions;
57
+ if (
58
+ userOptions.projectRoot != root &&
59
+ typeof userOptions.projectRoot === "string" &&
60
+ userOptions.projectRoot !== process.cwd() &&
61
+ userOptions.projectRoot !== ""
62
+ ) {
63
+ root = userOptions.projectRoot;
64
+ console.log(
65
+ "[vite:plugin-react-server] Root dir changed in plugin",
66
+ userOptions.projectRoot,
67
+ root
68
+ );
69
+ }
70
+
71
+ return {
72
+ name: "vite:plugin-react-server/static",
73
+ enforce: "post",
74
+ api: {
75
+ meta: { timing },
76
+ },
77
+ async config(config, configEnv): Promise<UserConfig> {
78
+ if (
79
+ typeof config.root === "string" &&
80
+ config.root !== root &&
81
+ config.root !== process.cwd() &&
82
+ config.root !== ""
83
+ ) {
84
+ root = config.root;
85
+ }
86
+ const resolvedPagesResult = await resolvePages(userOptions.build.pages);
87
+ if (resolvedPagesResult.type === "error") {
88
+ throw resolvedPagesResult.error;
89
+ }
90
+ resolvedPages = resolvedPagesResult.pages;
91
+ files = await checkFilesExist(resolvedPages, userOptions, root);
92
+
93
+ const resolvedConfig = resolveUserConfig({
94
+ isStatic: true,
95
+ config,
96
+ configEnv,
97
+ userOptions,
98
+ files,
99
+ });
100
+
101
+ if (resolvedConfig.type === "error") {
102
+ throw resolvedConfig.error;
103
+ }
104
+
105
+ userConfig = resolvedConfig.userConfig;
106
+ timing.configResolved = Date.now();
107
+ return {};
108
+ },
109
+ async buildStart() {
110
+ timing.buildStart = Date.now();
111
+ },
112
+ async closeBundle() {
113
+ timing.renderStart = Date.now();
114
+
115
+ // Create the loader
116
+ const serverManifestResult = tryManifest({
117
+ root: root,
118
+ outDir: join(userOptions.build.outDir, userOptions.build.server),
119
+ ssrManifest: false,
120
+ });
121
+ if (serverManifestResult.type === "error") {
122
+ throw serverManifestResult.error;
123
+ }
124
+ serverManifest = serverManifestResult.manifest;
125
+
126
+ // Get the client manifest
127
+ const clientManifestResult = tryManifest({
128
+ root: root,
129
+ outDir: join(userOptions.build.outDir, userOptions.build.client),
130
+ ssrManifest: false,
131
+ });
132
+
133
+ if (clientManifestResult.type === "error") {
134
+ throw clientManifestResult.error;
135
+ }
136
+ clientManifest = clientManifestResult.manifest;
137
+
138
+ // Ensure static directory exists
139
+ const staticDir = join(root, userOptions.build.outDir, userOptions.build.static);
140
+ await mkdir(staticDir, { recursive: true });
141
+
142
+ worker = await createWorker({
143
+ projectRoot: root,
144
+ workerPath: userOptions.htmlWorkerPath,
145
+ condition: "react-server",
146
+ reverseCondition: true,
147
+ mode: (resolvedConfig?.mode ?? "production") as "production" | "development",
148
+ });
149
+
150
+ if (typeof loader !== "function") {
151
+ loader = createBuildLoader({
152
+ root: root,
153
+ userConfig,
154
+ userOptions,
155
+ pluginContext: this,
156
+ serverManifest,
157
+ clientManifest,
158
+ });
159
+ }
160
+
161
+ // Collect CSS files per route
162
+ const routeCssMap = new Map<string, Set<string>>();
163
+ const globalCss = new Set<string>();
164
+ // copy whole client directory to static directory
165
+ await mkdir(staticDir, { recursive: true });
166
+ await copyDir(join(root, userOptions.build.outDir, userOptions.build.client), join(root, userOptions.build.outDir, userOptions.build.static));
167
+ // Add global CSS from index.html - use client manifest
168
+ const {cssFiles: indexCss} = collectManifestClientFiles({
169
+ manifest: clientManifest,
170
+ root: root,
171
+ pagePath: 'index.html',
172
+ moduleBase: userOptions.moduleBase,
173
+ preserveModulesRoot: userOptions.build.preserveModulesRoot,
174
+ testClient: ()=>true,
175
+ });
176
+ indexCss.forEach((css) => globalCss.add(css));
177
+
178
+ // Add CSS for each route's page component - use server manifest
179
+ for (const route of resolvedPages) {
180
+ const routeFiles = files.urlMap.get(route);
181
+ if (routeFiles) {
182
+ const pageCss = collectManifestClientFiles({
183
+ manifest: serverManifest,
184
+ root: root,
185
+ pagePath: routeFiles.page,
186
+ moduleBase: userOptions.moduleBase,
187
+ preserveModulesRoot: userOptions.build.preserveModulesRoot,
188
+ onClientModule(path) {
189
+ // copy the css file to the static directory
190
+ const targetPath = join(root, userOptions.build.outDir, userOptions.build.server, path);
191
+ const destinationPath = join(root, userOptions.build.outDir, userOptions.build.static, path);
192
+ mkdirSync(dirname(destinationPath), { recursive: true });
193
+ copyFileSync(targetPath, destinationPath);
194
+ },
195
+ testClient: userOptions.autoDiscover.cssPattern,
196
+ testJson: userOptions.autoDiscover.jsonPattern,
197
+ });
198
+ routeCssMap.set(route, new Set([...globalCss, ...pageCss.cssFiles.keys()]));
199
+ }
200
+ }
201
+ const bootstrapModules = clientManifest["index.html"]?.file
202
+ ? [clientManifest["index.html"].file.startsWith("/")
203
+ ? clientManifest["index.html"].file.slice(1)
204
+ : clientManifest["index.html"].file]
205
+ : [];
206
+
207
+ const { failedRoutes, completedRoutes} = await renderPages(
208
+ resolvedPages,
209
+ files,
210
+ {
211
+ outDir: userOptions.build.outDir,
212
+ htmlOutputPath: join( userOptions.build.outDir, userOptions.build.static, "index.html"),
213
+ pipableStreamOptions: {
214
+ bootstrapModules: bootstrapModules,
215
+ },
216
+ moduleBasePath: join(root, userOptions.build.outDir, userOptions.build.client),
217
+ moduleBaseURL: userOptions.moduleBaseURL,
218
+ userConfig,
219
+ pluginOptions: userOptions,
220
+ worker,
221
+ clientManifest,
222
+ serverManifest,
223
+ loader,
224
+ transformIndexHtml: htmlTransform!,
225
+ onClientJSFile: (url) => {
226
+ if (!clientAssets.has(url)) {
227
+ const clientPath = join(root, userOptions.build.outDir, userOptions.build.client, url);
228
+ const targetPath = join(root, userOptions.build.outDir, userOptions.build.static, url);
229
+ mkdirSync(dirname(targetPath), { recursive: true });
230
+ copyFileSync(clientPath, targetPath);
231
+ clientAssets.add(url);
232
+ }
233
+ }
234
+ }
235
+ );
236
+
237
+ if (failedRoutes.size > 0) {
238
+ console.error(
239
+ "[vite-plugin-react-server] Failed to render routes:",
240
+ failedRoutes
241
+ );
242
+ }
243
+ console.log(`Rendered ${completedRoutes.size} unique routes to ${join(userOptions.build.outDir, userOptions.build.static)}`);
244
+ await worker.terminate();
245
+ },
246
+ };
247
+ }
@@ -1,3 +1,5 @@
1
+ import { stashedPages } from "./config/resolvePages.js";
2
+
1
3
  type ResolvePageOptions = {
2
4
  pageModule: Record<string, any>;
3
5
  path: string;
@@ -16,6 +18,13 @@ export async function resolvePage({
16
18
  url,
17
19
  exportName,
18
20
  }: ResolvePageOptions): Promise<ResolvePageResult> {
21
+ if(stashedPages.length > 0 && stashedPages.includes(path)){
22
+ return {
23
+ type: "success",
24
+ key: path,
25
+ Page: pageModule,
26
+ }
27
+ }
19
28
  if (!pageModule) {
20
29
  return {
21
30
  type: "error",
package/plugin/root.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { dirname } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ export const pluginRoot = dirname(fileURLToPath(import.meta.url));
4
+
@@ -1,6 +1,6 @@
1
1
  import { resolveOptions } from "../config/resolveOptions.js";
2
- import type { StreamPluginOptions } from "../types.js";
3
- import { type Plugin } from "vite";
2
+ import type { InputNormalizer, ResolvedUserOptions, StreamPluginOptions } from "../types.js";
3
+ import { type Manifest, type Plugin } from "vite";
4
4
  import { transformModuleIfNeeded } from "../loader/react-loader.js";
5
5
  import { DEFAULT_CONFIG } from "../config/defaults.js";
6
6
  import { createInputNormalizer } from "../helpers/inputNormalizer.js";
@@ -32,37 +32,47 @@ import { join } from "node:path";
32
32
  */
33
33
 
34
34
  export function reactTransformPlugin(options: StreamPluginOptions): Plugin {
35
- const resolvedOptions = resolveOptions(options);
36
- let isDev = false;
37
- if (resolvedOptions.type === "error") throw resolvedOptions.error;
38
- const normalizer = createInputNormalizer({
39
- root: resolvedOptions.userOptions.projectRoot,
40
- preserveModulesRoot: undefined,
41
- removeExtension: false,
42
- });
43
-
44
- // Get the client manifest
45
- const clientManifestResult = tryManifest({
46
- root: resolvedOptions.userOptions.projectRoot,
47
- outDir: join(
48
- resolvedOptions.userOptions.build.outDir,
49
- resolvedOptions.userOptions.build.client
50
- ),
51
- ssrManifest: false,
52
- });
53
-
35
+ let normalizer: InputNormalizer;
36
+ let clientManifest: Manifest;
37
+ let isDev:boolean;
38
+ let userOptions: ResolvedUserOptions
54
39
  return {
55
40
  name: "vite:react-transform",
56
41
  enforce: "pre", // Run before Vite's transforms
57
42
  config(config, configEnv) {
58
- isDev = configEnv.mode === "development" && configEnv.command === "serve";
43
+ const resolvedOptionsResult = resolveOptions(
44
+ options,
45
+ config.build?.outDir?.startsWith(
46
+ join(options.build?.outDir ?? DEFAULT_CONFIG.BUILD.outDir, options.build?.client ?? DEFAULT_CONFIG.BUILD.client)
47
+ ) ?? false
48
+ );
49
+ isDev = configEnv.mode === "development" && configEnv.command === "serve"
50
+ if (resolvedOptionsResult.type === "error") throw resolvedOptionsResult.error;
51
+ userOptions = resolvedOptionsResult.userOptions;
52
+ normalizer = createInputNormalizer({
53
+ root: resolvedOptionsResult.userOptions.projectRoot,
54
+ preserveModulesRoot: undefined,
55
+ removeExtension: false,
56
+ });
57
+
59
58
  },
60
59
  async transform(code, id, options) {
61
60
  const ssr = options?.ssr ?? false;
62
61
  if (!ssr) return null;
63
62
  if (!id.match(DEFAULT_CONFIG.FILE_REGEX)) return null;
64
- if (!code.match('"use client"'))
65
- return null;
63
+ if (!code.match('"use client"')) return null;
64
+
65
+ // Get the client manifest
66
+ const clientManifestResult = tryManifest({
67
+ root: userOptions.projectRoot,
68
+ outDir: join(
69
+ userOptions.build.outDir,
70
+ userOptions.build.client
71
+ ),
72
+ ssrManifest: false,
73
+ });
74
+ if (clientManifestResult.type === "error") throw clientManifestResult.error;
75
+ clientManifest = clientManifestResult.manifest;
66
76
  const [key, value] = normalizer(id);
67
77
  const transformed = await transformModuleIfNeeded(
68
78
  code,
@@ -87,16 +97,15 @@ export function reactTransformPlugin(options: StreamPluginOptions): Plugin {
87
97
  transformed,
88
98
  }
89
99
  );
90
- return null
91
- }
92
- if (clientManifestResult.type === "error") {
93
- throw clientManifestResult.error;
100
+ return null;
94
101
  }
95
- const clientPath = clientManifestResult.manifest[key]?.file;
102
+ const clientPath = clientManifest[key]?.file;
96
103
 
97
104
  if (!clientPath) {
98
- console.warn(`[vite-plugin-react-server] Could not find client path for ${value}. Ignoring.`)
99
- return null
105
+ console.warn(
106
+ `[vite-plugin-react-server] Could not find client path for ${value}. Ignoring.`
107
+ );
108
+ return null;
100
109
  }
101
110
  return {
102
111
  code: transformed.replace(key, clientPath),
@@ -1,19 +0,0 @@
1
- export interface ViteReactClientTransformOptions {
2
- projectRoot?: string;
3
- moduleId?: (path: string, ssr: boolean) => string;
4
- validateModuleId?: (moduleId: string) => boolean;
5
- include?: string | RegExp | (string | RegExp)[];
6
- exclude?: string | RegExp | (string | RegExp)[];
7
- }
8
-
9
- export interface TransformerOptions {
10
- moduleId: (path: string, ssr: boolean) => string;
11
- /**
12
- * Optional validation function for module IDs
13
- */
14
- validateModuleId?: (moduleId: string) => boolean;
15
- /**
16
- * The directory to use for the module IDs
17
- */
18
- moduleBase: string;
19
- }
package/plugin/types.ts CHANGED
@@ -1,5 +1,3 @@
1
- import type { ComponentType } from "react";
2
- import type { RenderToPipeableStreamOptions } from "react-dom/server";
3
1
  import type { PreRenderedChunk } from "rollup";
4
2
  import type { PreRenderedAsset } from "rollup";
5
3
  import type {
@@ -25,7 +23,7 @@ export type ResolvedUserConfig = Required<
25
23
  Pick<UserConfig, "root" | "mode" | "build" | "resolve">
26
24
  > &
27
25
  Omit<UserConfig, "root" | "mode" | "build" | "resolve"> & {
28
- resolve: {alias:AliasOptions} & ResolveOptions;
26
+ resolve: ResolveOptions;
29
27
  } & {
30
28
  build: NonNullable<
31
29
  Required<
@@ -89,7 +87,6 @@ export type ResolvedUserOptions = Required<
89
87
  | "serverEntry"
90
88
  | "moduleBaseExceptions"
91
89
  | "pipableStreamOptions"
92
- | "moduleId"
93
90
  >
94
91
  > & {
95
92
  build: NonNullable<Required<StreamPluginOptions["build"]>>;
@@ -172,15 +169,17 @@ export interface StreamPluginOptions {
172
169
  build?: BuildConfig;
173
170
  moduleBaseExceptions?: string[];
174
171
  pipableStreamOptions?: PipeableStreamOptions;
175
- moduleId?: (id: string, ssr: boolean) => string;
176
172
  }
177
173
 
178
174
  export interface CreateHandlerOptions<T = any> {
179
175
  loader: (id: string) => Promise<T>;
180
176
  clientManifest?: import("vite").Manifest;
181
177
  serverManifest?: import("vite").Manifest;
178
+ moduleBasePath: string;
179
+ moduleBaseURL: string;
182
180
  moduleGraph?: import("vite").ModuleGraph;
183
181
  cssFiles?: string[];
182
+ cssModules?: Set<string>;
184
183
  onCssFile?: (path: string, parentUrl: string) => void;
185
184
  logger?: import("vite").Logger;
186
185
  pipableStreamOptions?: PipeableStreamOptions;
@@ -245,7 +244,7 @@ export interface BuildOutput {
245
244
  }
246
245
 
247
246
  export interface BuildConfig {
248
- pages: string[] | (() => Promise<string[]> | string[]);
247
+ pages: string[] | (() => Promise<string[]> | string[]) | Promise<string[]>;
249
248
  assetsDir?: string;
250
249
  client?: string; // Output directory for client files
251
250
  server?: string; // Output directory for server files
@@ -359,3 +358,23 @@ export type HtmlProps = {
359
358
  url: string;
360
359
  cssFiles: string[];
361
360
  };
361
+
362
+ export interface PageAsset {
363
+ type: 'css' | 'js';
364
+ path: string;
365
+ parentUrl: string;
366
+ }
367
+
368
+ export interface PageData {
369
+ route: string;
370
+ clientComponents?: string[];
371
+ html?: {
372
+ raw: string;
373
+ transformed?: string;
374
+ assets: PageAsset[];
375
+ };
376
+ rsc?: {
377
+ content: string;
378
+ modules: Array<[string, string]>; // [modulePath, exportName]
379
+ };
380
+ }
@@ -1,7 +1,7 @@
1
1
  import { Worker, type ResourceLimits } from "node:worker_threads";
2
2
  import { getMode, getNodePath } from "../config/getPaths.js";
3
3
  import { getCondition } from "../config/getCondition.js";
4
- import { join } from "node:path";
4
+
5
5
  type CreateWorkerOptions = {
6
6
  projectRoot?: string;
7
7
  condition?: "react-server" | "react-client";
@@ -0,0 +1,63 @@
1
+ # HTML Worker
2
+
3
+ The HTML worker handles HTML generation and transformation as part of the server plugin (which runs under the `react-server` condition). Since the main thread is already in the correct condition for RSC, the HTML worker can work directly with RSC streams without needing a separate RSC worker.
4
+
5
+ The worker receives RSC streams directly from the main thread (which is running under `react-server` condition) and transforms them into HTML. This is different from the client plugin, which needs a separate RSC worker to handle server-condition tasks.
6
+
7
+ ## Extending the Worker
8
+
9
+ Users can create their own HTML workers for application-level use. This allows for:
10
+
11
+ 1. Custom HTML processing
12
+ 2. Specialized asset handling
13
+ 3. Custom build pipelines
14
+
15
+ Example of creating a custom HTML worker:
16
+
17
+ ```typescript
18
+ import { messageHandler } from 'vite-plugin-react-server/worker/html/messageHandler'
19
+ import type { HtmlWorkerMessage } from 'vite-plugin-react-server/worker/types'
20
+
21
+ // Create your custom message handler
22
+ const customMessageHandler = async (msg: HtmlWorkerMessage) => {
23
+ // Add your custom logic here
24
+ return messageHandler(msg)
25
+ }
26
+
27
+ // Initialize your worker
28
+ if (typeof WorkerGlobalScope !== 'undefined') {
29
+ parentPort.on('message', customMessageHandler)
30
+ }
31
+ ```
32
+
33
+ ## Messages
34
+ You have two options, either directly write the files from the worker or pass them back to the main thread through messages.
35
+
36
+ ```ts
37
+
38
+ ```
39
+
40
+ ## Client Dev Mode Integration
41
+
42
+ When running in client dev mode, you can use your custom HTML worker as part of your application:
43
+
44
+ 1. The worker runs in the client environment
45
+ 2. It can handle HTML transformations in real-time
46
+ 3. Provides immediate feedback during development
47
+ 4. Can be integrated with other development tools
48
+
49
+ ## Future Possibilities
50
+
51
+ The HTML worker architecture supports various potential enhancements:
52
+
53
+ - Custom HTML transformation pipelines
54
+ - Advanced asset optimization strategies
55
+ - Integration with other build tools
56
+ - Real-time HTML processing in development
57
+
58
+ ## Notes
59
+
60
+ - The worker must handle streaming HTML content
61
+ - Asset paths need to be properly resolved
62
+ - Consider memory usage when processing large HTML files
63
+ - Ensure proper coordination with the RSC worker
@@ -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
+ }