vite-plugin-react-server 0.3.11 → 0.3.12

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 (220) hide show
  1. package/dist/package.json +15 -11
  2. package/dist/plugin/checkFilesExist.d.ts +2 -2
  3. package/dist/plugin/checkFilesExist.d.ts.map +1 -1
  4. package/dist/plugin/checkFilesExist.js +39 -57
  5. package/dist/plugin/checkFilesExist.js.map +1 -1
  6. package/dist/plugin/collect-css-manifest.d.ts.map +1 -1
  7. package/dist/plugin/collect-css-manifest.js +5 -0
  8. package/dist/plugin/collect-css-manifest.js.map +1 -1
  9. package/dist/plugin/components.js +10 -15
  10. package/dist/plugin/config/createModuleIdGenerator.js +1 -1
  11. package/dist/plugin/config/createModuleIdGenerator.js.map +1 -1
  12. package/dist/plugin/config/defaults.d.ts +5 -14
  13. package/dist/plugin/config/defaults.d.ts.map +1 -1
  14. package/dist/plugin/config/defaults.js +12 -12
  15. package/dist/plugin/config/defaults.js.map +1 -1
  16. package/dist/plugin/config/getPaths.js +1 -1
  17. package/dist/plugin/config/resolveOptions.d.ts.map +1 -1
  18. package/dist/plugin/config/resolveOptions.js +73 -52
  19. package/dist/plugin/config/resolveOptions.js.map +1 -1
  20. package/dist/plugin/config/resolveUserConfig.d.ts.map +1 -1
  21. package/dist/plugin/config/resolveUserConfig.js +53 -65
  22. package/dist/plugin/config/resolveUserConfig.js.map +1 -1
  23. package/dist/plugin/helpers/getBundleManifest.d.ts +6 -1
  24. package/dist/plugin/helpers/getBundleManifest.d.ts.map +1 -1
  25. package/dist/plugin/helpers/getBundleManifest.js +48 -19
  26. package/dist/plugin/helpers/getBundleManifest.js.map +1 -1
  27. package/dist/plugin/helpers/inputNormalizer.d.ts +1 -2
  28. package/dist/plugin/helpers/inputNormalizer.d.ts.map +1 -1
  29. package/dist/plugin/helpers/inputNormalizer.js +52 -46
  30. package/dist/plugin/helpers/inputNormalizer.js.map +1 -1
  31. package/dist/plugin/helpers/tryManifest.d.ts +1 -1
  32. package/dist/plugin/helpers/tryManifest.d.ts.map +1 -1
  33. package/dist/plugin/helpers/tryManifest.js.map +1 -1
  34. package/dist/plugin/loader/createBuildLoader.d.ts +1 -1
  35. package/dist/plugin/loader/createBuildLoader.d.ts.map +1 -1
  36. package/dist/plugin/loader/createBuildLoader.js +29 -26
  37. package/dist/plugin/loader/createBuildLoader.js.map +1 -1
  38. package/dist/plugin/loader/css-loader.d.ts +16 -0
  39. package/dist/plugin/loader/css-loader.d.ts.map +1 -0
  40. package/dist/plugin/loader/css-loader.js +70 -0
  41. package/dist/plugin/loader/css-loader.js.map +1 -0
  42. package/dist/plugin/loader/react-loader.d.ts +17 -0
  43. package/dist/plugin/loader/react-loader.d.ts.map +1 -0
  44. package/dist/plugin/loader/react-loader.js +647 -0
  45. package/dist/plugin/loader/react-loader.js.map +1 -0
  46. package/dist/plugin/loader/rsc/messageHandler.d.ts +2 -0
  47. package/dist/plugin/loader/rsc/messageHandler.d.ts.map +1 -0
  48. package/dist/plugin/loader/rsc/messageHandler.js +1 -0
  49. package/dist/plugin/loader/rsc/rsc-worker.development.d.ts +2 -0
  50. package/dist/plugin/loader/rsc/rsc-worker.development.d.ts.map +1 -0
  51. package/dist/plugin/loader/rsc/rsc-worker.development.js +1 -0
  52. package/dist/plugin/react-client/index.js +2 -2
  53. package/dist/plugin/react-client/index.js.map +1 -1
  54. package/dist/plugin/react-client/plugin.d.ts.map +1 -1
  55. package/dist/plugin/react-client/plugin.js +202 -25
  56. package/dist/plugin/react-client/plugin.js.map +1 -1
  57. package/dist/plugin/react-server/createHandler.d.ts.map +1 -1
  58. package/dist/plugin/react-server/createHandler.js +10 -4
  59. package/dist/plugin/react-server/createHandler.js.map +1 -1
  60. package/dist/plugin/react-server/createRscStream.d.ts +15 -3
  61. package/dist/plugin/react-server/createRscStream.d.ts.map +1 -1
  62. package/dist/plugin/react-server/createRscStream.js +52 -49
  63. package/dist/plugin/react-server/createRscStream.js.map +1 -1
  64. package/dist/plugin/react-server/plugin.d.ts.map +1 -1
  65. package/dist/plugin/react-server/plugin.js +24 -20
  66. package/dist/plugin/react-server/plugin.js.map +1 -1
  67. package/dist/plugin/transformer/plugin.d.ts.map +1 -1
  68. package/dist/plugin/transformer/plugin.js +65 -52
  69. package/dist/plugin/transformer/plugin.js.map +1 -1
  70. package/dist/plugin/types.d.ts +5 -0
  71. package/dist/plugin/types.d.ts.map +1 -1
  72. package/dist/plugin/utils/logger.d.ts +9 -0
  73. package/dist/plugin/utils/logger.d.ts.map +1 -0
  74. package/dist/plugin/utils/logger.js +68 -0
  75. package/dist/plugin/utils/logger.js.map +1 -0
  76. package/dist/plugin/worker/createWorker.d.ts +1 -0
  77. package/dist/plugin/worker/createWorker.d.ts.map +1 -1
  78. package/dist/plugin/worker/createWorker.js +23 -36
  79. package/dist/plugin/worker/createWorker.js.map +1 -1
  80. package/dist/plugin/worker/html/html-worker.production.js +5 -1
  81. package/dist/plugin/worker/html/html-worker.production.js.map +1 -1
  82. package/dist/plugin/worker/html/messageHandler.d.ts.map +1 -1
  83. package/dist/plugin/worker/html/messageHandler.js +10 -19
  84. package/dist/plugin/worker/html/messageHandler.js.map +1 -1
  85. package/dist/plugin/worker/html/renderPages.d.ts +2 -2
  86. package/dist/plugin/worker/html/renderPages.d.ts.map +1 -1
  87. package/dist/plugin/worker/html/renderPages.js +130 -131
  88. package/dist/plugin/worker/html/renderPages.js.map +1 -1
  89. package/dist/plugin/worker/rsc/index.d.ts +1 -3
  90. package/dist/plugin/worker/rsc/index.d.ts.map +1 -1
  91. package/dist/plugin/worker/rsc/index.js +1 -9
  92. package/dist/plugin/worker/rsc/index.js.map +1 -1
  93. package/dist/plugin/worker/rsc/messageHandler.d.ts +3 -0
  94. package/dist/plugin/worker/rsc/messageHandler.d.ts.map +1 -0
  95. package/dist/plugin/worker/rsc/messageHandler.js +107 -0
  96. package/dist/plugin/worker/rsc/messageHandler.js.map +1 -0
  97. package/dist/plugin/worker/rsc/plugin.d.ts.map +1 -1
  98. package/dist/plugin/worker/rsc/plugin.js +74 -80
  99. package/dist/plugin/worker/rsc/rsc-worker.development.d.ts +32 -0
  100. package/dist/plugin/worker/rsc/rsc-worker.development.d.ts.map +1 -0
  101. package/dist/plugin/worker/rsc/rsc-worker.development.js +43 -0
  102. package/dist/plugin/worker/rsc/rsc-worker.development.js.map +1 -0
  103. package/dist/plugin/worker/rsc/rsc-worker.js +4 -106
  104. package/dist/plugin/worker/rsc/rsc-worker.production.d.ts +2 -0
  105. package/dist/plugin/worker/rsc/rsc-worker.production.d.ts.map +1 -0
  106. package/dist/plugin/worker/rsc/rsc-worker.production.js +14 -0
  107. package/dist/plugin/worker/rsc/rsc-worker.production.js.map +1 -0
  108. package/dist/plugin/worker/rsc/state.d.ts +11 -0
  109. package/dist/plugin/worker/rsc/state.d.ts.map +1 -0
  110. package/dist/plugin/worker/rsc/state.js +12 -0
  111. package/dist/plugin/worker/rsc/state.js.map +1 -0
  112. package/dist/plugin/worker/types.d.ts +60 -46
  113. package/dist/plugin/worker/types.d.ts.map +1 -1
  114. package/dist/tsconfig.tsbuildinfo +1 -1
  115. package/package.json +15 -11
  116. package/plugin/checkFilesExist.ts +42 -62
  117. package/plugin/collect-css-manifest.ts +5 -1
  118. package/plugin/config/createModuleIdGenerator.ts +1 -1
  119. package/plugin/config/defaults.ts +13 -15
  120. package/plugin/config/resolveOptions.ts +134 -76
  121. package/plugin/config/resolveUserConfig.ts +75 -76
  122. package/plugin/helpers/getBundleManifest.ts +69 -31
  123. package/plugin/helpers/inputNormalizer.ts +82 -70
  124. package/plugin/helpers/tryManifest.ts +1 -1
  125. package/plugin/loader/createBuildLoader.ts +38 -41
  126. package/plugin/loader/css-loader.ts +96 -0
  127. package/plugin/loader/react-loader.ts +945 -0
  128. package/plugin/loader/rsc/messageHandler.tsx +1 -0
  129. package/plugin/loader/rsc/rsc-worker.development.ts +1 -0
  130. package/plugin/react-client/index.ts +1 -1
  131. package/plugin/react-client/plugin.ts +266 -41
  132. package/plugin/react-server/createHandler.ts +9 -5
  133. package/plugin/react-server/createRscStream.ts +75 -54
  134. package/plugin/react-server/plugin.ts +26 -21
  135. package/plugin/transformer/plugin.ts +67 -76
  136. package/plugin/types/global.d.ts +8 -0
  137. package/plugin/types.ts +2 -0
  138. package/plugin/utils/logger.ts +52 -0
  139. package/plugin/worker/createWorker.ts +43 -44
  140. package/plugin/worker/html/html-worker.production.tsx +7 -2
  141. package/plugin/worker/html/messageHandler.ts +13 -21
  142. package/plugin/worker/html/renderPages.ts +146 -179
  143. package/plugin/worker/rsc/index.ts +4 -13
  144. package/plugin/worker/rsc/messageHandler.tsx +143 -0
  145. package/plugin/worker/rsc/plugin.ts +38 -37
  146. package/plugin/worker/rsc/rsc-worker.development.ts +107 -0
  147. package/plugin/worker/rsc/rsc-worker.production.ts +13 -0
  148. package/plugin/worker/rsc/rsc-worker.tsx +5 -128
  149. package/plugin/worker/rsc/state.ts +37 -0
  150. package/plugin/worker/types.ts +79 -55
  151. package/scripts/check-react-version.mjs +17 -7
  152. package/scripts/react+0.0.0-experimental-b3a95caf-20250113.patch +143 -4170
  153. package/scripts/react-dom+0.0.0-experimental-b3a95caf-20250113.patch +14271 -90079
  154. package/dist/plugin/components.js.map +0 -1
  155. package/dist/plugin/helpers/createClientInputNormalizer.d.ts +0 -8
  156. package/dist/plugin/helpers/createClientInputNormalizer.d.ts.map +0 -1
  157. package/dist/plugin/helpers/createClientInputNormalizer.js +0 -35
  158. package/dist/plugin/helpers/createServerInputNormalizer.d.ts +0 -9
  159. package/dist/plugin/helpers/createServerInputNormalizer.d.ts.map +0 -1
  160. package/dist/plugin/helpers/createServerInputNormalizer.js +0 -37
  161. package/dist/plugin/helpers/createStaticInputNormalizer.d.ts +0 -7
  162. package/dist/plugin/helpers/createStaticInputNormalizer.d.ts.map +0 -1
  163. package/dist/plugin/helpers/createStaticInputNormalizer.js +0 -18
  164. package/dist/plugin/helpers/getModuleManifest.d.ts +0 -17
  165. package/dist/plugin/helpers/getModuleManifest.d.ts.map +0 -1
  166. package/dist/plugin/helpers/getModuleManifest.js +0 -23
  167. package/dist/plugin/helpers/inputNormalizerWorker.d.ts +0 -12
  168. package/dist/plugin/helpers/inputNormalizerWorker.d.ts.map +0 -1
  169. package/dist/plugin/helpers/inputNormalizerWorker.js +0 -33
  170. package/dist/plugin/helpers/normalizedRelativePath.d.ts +0 -11
  171. package/dist/plugin/helpers/normalizedRelativePath.d.ts.map +0 -1
  172. package/dist/plugin/helpers/normalizedRelativePath.js +0 -36
  173. package/dist/plugin/helpers/resolveFilePath.d.ts +0 -13
  174. package/dist/plugin/helpers/resolveFilePath.d.ts.map +0 -1
  175. package/dist/plugin/helpers/resolveFilePath.js +0 -74
  176. package/dist/plugin/helpers/resolveWorkerModule.d.ts +0 -6
  177. package/dist/plugin/helpers/resolveWorkerModule.d.ts.map +0 -1
  178. package/dist/plugin/helpers/resolveWorkerModule.js +0 -24
  179. package/dist/plugin/helpers/validateModuleBase.d.ts +0 -3
  180. package/dist/plugin/helpers/validateModuleBase.d.ts.map +0 -1
  181. package/dist/plugin/helpers/validateModuleBase.js +0 -16
  182. package/dist/plugin/helpers/validateResolvedConfig.d.ts +0 -3
  183. package/dist/plugin/helpers/validateResolvedConfig.d.ts.map +0 -1
  184. package/dist/plugin/helpers/validateResolvedConfig.js +0 -17
  185. package/dist/plugin/transformer/transformer-client-components.d.ts +0 -30
  186. package/dist/plugin/transformer/transformer-client-components.d.ts.map +0 -1
  187. package/dist/plugin/transformer/transformer-client-components.js +0 -122
  188. package/dist/plugin/transformer/transformer-client-components.js.map +0 -1
  189. package/dist/plugin/transformer/transformer-server-actions.d.ts +0 -29
  190. package/dist/plugin/transformer/transformer-server-actions.d.ts.map +0 -1
  191. package/dist/plugin/transformer/transformer-server-actions.js +0 -90
  192. package/dist/plugin/worker/rsc/createRscStream.d.ts +0 -5
  193. package/dist/plugin/worker/rsc/createRscStream.d.ts.map +0 -1
  194. package/dist/plugin/worker/rsc/createRscStream.js +0 -39
  195. package/dist/plugin/worker/rsc/createRscStream.js.map +0 -1
  196. package/dist/plugin/worker/rsc/development.d.ts +0 -5
  197. package/dist/plugin/worker/rsc/development.d.ts.map +0 -1
  198. package/dist/plugin/worker/rsc/development.js +0 -13
  199. package/dist/plugin/worker/rsc/development.js.map +0 -1
  200. package/dist/plugin/worker/rsc/plugin.js.map +0 -1
  201. package/dist/plugin/worker/rsc/production.d.ts +0 -5
  202. package/dist/plugin/worker/rsc/production.d.ts.map +0 -1
  203. package/dist/plugin/worker/rsc/production.js +0 -13
  204. package/dist/plugin/worker/rsc/production.js.map +0 -1
  205. package/plugin/helpers/createClientInputNormalizer.ts +0 -48
  206. package/plugin/helpers/createServerInputNormalizer.ts +0 -52
  207. package/plugin/helpers/createStaticInputNormalizer.ts +0 -26
  208. package/plugin/helpers/getModuleManifest.ts +0 -36
  209. package/plugin/helpers/inputNormalizerWorker.ts +0 -52
  210. package/plugin/helpers/normalizedRelativePath.ts +0 -59
  211. package/plugin/helpers/resolveFilePath.ts +0 -108
  212. package/plugin/helpers/resolveWorkerModule.ts +0 -41
  213. package/plugin/helpers/validateModuleBase.ts +0 -30
  214. package/plugin/helpers/validateResolvedConfig.ts +0 -21
  215. package/plugin/transformer/transformer-client-components.ts +0 -168
  216. package/plugin/transformer/transformer-server-actions.ts +0 -125
  217. package/plugin/worker/rsc/createRscStream.ts +0 -42
  218. package/plugin/worker/rsc/development.ts +0 -6
  219. package/plugin/worker/rsc/production.ts +0 -6
  220. package/scripts/react-server-dom-esm+0.0.1.patch +0 -24775
@@ -5,8 +5,8 @@ import { reactClientPlugin } from "./plugin.js";
5
5
 
6
6
  export function vitePluginReactClient(options = {} as StreamPluginOptions): import("vite").Plugin[] {
7
7
  return [
8
- reactPreservePlugin(options),
9
8
  reactClientPlugin(options),
10
9
  reactTransformPlugin(options),
10
+ reactPreservePlugin(options),
11
11
  ];
12
12
  }
@@ -1,5 +1,6 @@
1
1
  import { type Manifest, type Plugin, type ResolvedConfig } from "vite";
2
2
  import type {
3
+ CheckFilesExistReturn,
3
4
  ResolvedUserConfig,
4
5
  ResolvedUserOptions,
5
6
  StreamPluginOptions,
@@ -7,16 +8,34 @@ import type {
7
8
  import { resolveOptions } from "../config/resolveOptions.js";
8
9
  import { resolveUserConfig } from "../config/resolveUserConfig.js";
9
10
  import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
10
- import { dirname, join, resolve } from "node:path";
11
+ import { dirname, join } from "node:path";
11
12
  import { getBundleManifest } from "../helpers/getBundleManifest.js";
12
13
  import { checkFilesExist } from "../checkFilesExist.js";
13
14
  import { resolvePages } from "../config/resolvePages.js";
15
+ import { tryManifest } from "../helpers/tryManifest.js";
16
+ import { createInputNormalizer } from "../helpers/inputNormalizer.js";
17
+ import { createWorker } from "../worker/createWorker.js";
18
+ import type { Worker } from "node:worker_threads";
19
+ import { getPluginRoot } from "../config/getPaths.js";
20
+ import { DEFAULT_CONFIG } from "../config/defaults.js";
21
+ import type {
22
+ RscRenderMessage,
23
+ RscWorkerMessage,
24
+ RscWorkerResponse,
25
+ } from "../worker/types.js";
26
+ import { createLogger } from "../utils/logger.js";
27
+
28
+ const log = createLogger("react-client");
14
29
  let userOptions: ResolvedUserOptions;
15
30
  let userConfig: ResolvedUserConfig;
16
31
  let clientManifest: Manifest = {};
17
32
  let resolvedConfig: ResolvedConfig;
18
33
  let root: string;
19
- let loader: ((id: string) => Promise<Record<string, any>>) = (id: string) => import(id);
34
+ let loader: (id: string) => Promise<Record<string, any>> = (id: string) =>
35
+ import(id);
36
+ let worker: Worker;
37
+ let files: CheckFilesExistReturn;
38
+
20
39
  export function reactClientPlugin(options: StreamPluginOptions): Plugin {
21
40
  const resolvedOptions = resolveOptions(options, true);
22
41
  if (resolvedOptions.type === "error") {
@@ -24,40 +43,54 @@ export function reactClientPlugin(options: StreamPluginOptions): Plugin {
24
43
  }
25
44
  userOptions = resolvedOptions.userOptions;
26
45
  root = userOptions.projectRoot;
46
+ const rscWorkerPath = join(getPluginRoot(), DEFAULT_CONFIG.RSC_WORKER_PATH);
27
47
 
28
- console.log("[vite:react-client] Initial setup:", {
29
- projectRoot: userOptions.projectRoot,
30
- root: root,
31
- clientEntry: userOptions.clientEntry
32
- });
48
+ log.info("RSC worker path:" + rscWorkerPath);
33
49
 
34
50
  return {
35
51
  name: "vite:react-client",
36
52
 
37
53
  async config(config, configEnv) {
38
-
39
- if(typeof config.root === "string" &&
54
+ if (
55
+ typeof config.root === "string" &&
40
56
  config.root !== root &&
41
57
  config.root !== process.cwd() &&
42
- config.root !== "") {
58
+ config.root !== ""
59
+ ) {
43
60
  root = config.root;
44
61
  console.log("[vite:react-client] Root updated:", root);
45
62
  }
46
-
63
+ if (configEnv.command === "serve" && !configEnv.isPreview && !worker) {
64
+ worker = await createWorker({
65
+ projectRoot: root,
66
+ workerPath: rscWorkerPath,
67
+ reverseCondition: true,
68
+ });
69
+ }
47
70
  const pages = await resolvePages(userOptions.build.pages);
48
- if(pages.type === "error") {
71
+ if (pages.type === "error") {
49
72
  throw pages.error;
50
73
  }
51
74
 
52
- const files = await checkFilesExist(pages.pages, userOptions, root);
53
-
75
+ if (pages.pages.length > 0) {
76
+ files = await checkFilesExist(pages.pages, userOptions, root);
77
+ } else {
78
+ files = {
79
+ pageMap: new Map(),
80
+ propsMap: new Map(),
81
+ propsSet: new Set(),
82
+ pageSet: new Set(),
83
+ urlMap: new Map(),
84
+ errors: [],
85
+ };
86
+ }
54
87
 
55
88
  const resolvedConfig = resolveUserConfig({
56
89
  isClient: true,
57
90
  config,
58
91
  configEnv,
59
92
  userOptions,
60
- files
93
+ files,
61
94
  });
62
95
 
63
96
  if (resolvedConfig.type === "error") {
@@ -74,13 +107,18 @@ export function reactClientPlugin(options: StreamPluginOptions): Plugin {
74
107
 
75
108
  async generateBundle(options, bundle) {
76
109
  // Create manifest entries for each chunk
77
- clientManifest = getBundleManifest(this, bundle, undefined);
110
+ clientManifest = getBundleManifest({
111
+ pluginContext: this,
112
+ bundle,
113
+ moduleBase: userOptions.moduleBase,
114
+ preserveModulesRoot: userOptions.build.preserveModulesRoot,
115
+ });
78
116
 
79
117
  // Write manifest immediately after generation
80
118
  const manifestPath = join(
81
119
  root,
82
- resolvedConfig.environments['client'].build.outDir as string,
83
- resolvedConfig.environments['client'].build.manifest as string
120
+ resolvedConfig.environments["client"].build.outDir as string,
121
+ resolvedConfig.environments["client"].build.manifest as string
84
122
  );
85
123
  await mkdir(dirname(manifestPath), { recursive: true });
86
124
 
@@ -89,27 +127,83 @@ export function reactClientPlugin(options: StreamPluginOptions): Plugin {
89
127
  JSON.stringify(clientManifest, null, 2)
90
128
  );
91
129
  },
92
-
130
+
93
131
  async configurePreviewServer(server) {
132
+ if (root !== server.config.root) {
133
+ root = server.config.root;
134
+ }
94
135
  if (typeof loader !== "function") {
95
136
  loader = (id: string) => import(id);
96
137
  }
138
+ const normalize = createInputNormalizer({
139
+ root,
140
+ removeExtension: false,
141
+ preserveModulesRoot: userOptions.build.preserveModulesRoot
142
+ ? userOptions.moduleBase
143
+ : undefined,
144
+ });
97
145
  server.middlewares.use(async (req, res, next) => {
98
- console.log('req.url', req.url);
99
- if (req.url && req.url.endsWith('.js')) {
100
- const nodeRoot = process.env['NODE_PATH'] ?? process.cwd();
101
-
146
+ const [key, value] = normalize(req.url);
147
+ const fileRoot = key.startsWith("node_modules")
148
+ ? root
149
+ : join(root, userOptions.build.outDir, userOptions.build.client);
150
+ if (value.endsWith(".js")) {
102
151
  try {
103
- const stats = await stat(req.url);
152
+ const stats = await stat(join(fileRoot, value));
104
153
  if (stats.isFile()) {
105
- const content = await readFile(req.url, 'utf-8');
106
- res.setHeader('Content-Type', 'application/javascript');
154
+ const content = await readFile(join(fileRoot, value), "utf-8");
155
+ res.setHeader("Content-Type", "application/javascript");
107
156
  res.end(content);
157
+ return;
108
158
  } else {
109
159
  next();
110
160
  }
111
161
  } catch (error) {
112
- console.error('error', error);
162
+ const { manifest: clientManifest } = tryManifest({
163
+ root,
164
+ outDir: join(userOptions.build.outDir, userOptions.build.client),
165
+ });
166
+ const { manifest: serverManifest } = tryManifest({
167
+ root,
168
+ outDir: join(userOptions.build.outDir, userOptions.build.server),
169
+ });
170
+ if (clientManifest && value in clientManifest) {
171
+ res.setHeader("Content-Type", "application/javascript");
172
+ res.end(clientManifest[value]);
173
+ return;
174
+ } else if (serverManifest && value in serverManifest) {
175
+ res.setHeader("Content-Type", "application/javascript");
176
+ res.end(serverManifest[value]);
177
+ return;
178
+ }
179
+ const foundClient =
180
+ clientManifest &&
181
+ Object.entries(clientManifest).find(
182
+ ([key, value]) =>
183
+ value === key ||
184
+ value === value.file ||
185
+ value === value.src ||
186
+ value === value.name
187
+ );
188
+ if (foundClient) {
189
+ res.setHeader("Content-Type", "application/javascript");
190
+ res.end(foundClient);
191
+ }
192
+ const foundServer =
193
+ serverManifest &&
194
+ Object.entries(serverManifest).find(
195
+ ([key, value]) =>
196
+ value === key ||
197
+ value === value.file ||
198
+ value === value.src ||
199
+ value === value.name
200
+ );
201
+
202
+ if (foundServer) {
203
+ res.setHeader("Content-Type", "application/javascript");
204
+ res.end(foundServer);
205
+ return;
206
+ }
113
207
  next();
114
208
  }
115
209
  } else {
@@ -122,27 +216,158 @@ export function reactClientPlugin(options: StreamPluginOptions): Plugin {
122
216
  if (typeof loader !== "function") {
123
217
  loader = server.ssrLoadModule;
124
218
  }
219
+ if (!worker) {
220
+ log.info("Creating RSC worker...");
221
+ worker = await createWorker({
222
+ projectRoot: root,
223
+ workerPath: rscWorkerPath,
224
+ condition: "react-client",
225
+ });
226
+ log.info("RSC worker created");
227
+ }
228
+ const normalize = createInputNormalizer({
229
+ root,
230
+ removeExtension: false,
231
+ preserveModulesRoot: userOptions.build.preserveModulesRoot
232
+ ? userOptions.moduleBase
233
+ : undefined,
234
+ });
125
235
  server.middlewares.use(async (req, res, next) => {
126
- console.log('req.url', req.url);
127
- if (req.url && req.url.endsWith('.js')) {
128
- const filePath = join(root, req.url);
236
+ if (!req.url) {
237
+ next();
238
+ return;
239
+ }
240
+ if (
241
+ req.url.endsWith(".rsc") ||
242
+ req.headers.accept?.includes("text/x-component")
243
+ ) {
129
244
  try {
130
- const stats = await stat(filePath);
131
- if (stats.isFile()) {
132
- const content = await readFile(filePath, 'utf-8');
133
- res.setHeader('Content-Type', 'application/javascript');
134
- res.end(content);
245
+ const path = req.url?.includes("index.rsc")
246
+ ? req.url.replace("index.rsc", "")
247
+ : req.url?.replace(".rsc", "");
248
+ let [key, value] = normalize(path);
249
+
250
+ let pageImport = DEFAULT_CONFIG.PAGE as string;
251
+ let propsImport = DEFAULT_CONFIG.PROPS as string;
252
+ // PAGE
253
+ // no trailing slash
254
+ const pathNoTrailing = path?.replace(/\/$/, '');
255
+ if (files.urlMap.has(req.url)) {
256
+ pageImport = files.urlMap.get(req.url)!.page;
257
+ propsImport = files.urlMap.get(req.url)!.props;
258
+ } else if (files.urlMap.has(pathNoTrailing)) {
259
+ pageImport = files.urlMap.get(pathNoTrailing)!.page;
260
+ propsImport = files.urlMap.get(pathNoTrailing)!.props;
261
+ } else if (files.urlMap.has(path)) {
262
+ pageImport = files.urlMap.get(path)!.page;
263
+ propsImport = files.urlMap.get(path)!.props;
264
+ } else if (files.urlMap.has(value)) {
265
+ pageImport = files.urlMap.get(value)!.page;
266
+ propsImport = files.urlMap.get(value)!.props;
267
+ } else if (files.urlMap.has(key)) {
268
+ pageImport = files.urlMap.get(key)!.page;
269
+ propsImport = files.urlMap.get(key)!.props;
135
270
  } else {
136
- next();
271
+ console.warn(`Page/props import not found for any of the following (in order of priority): ${[req.url, pathNoTrailing, path, value, key].filter(Boolean).join(', ')} available pages:${Array.from(files.urlMap.keys()).join(', ')}`);
137
272
  }
273
+ // Set headers early
274
+ res.setHeader("Content-Type", "text/x-component");
275
+ res.setHeader("Transfer-Encoding", "chunked");
276
+ res.setHeader("Connection", "keep-alive");
277
+
278
+ let hasError = false;
279
+ const timeout = setTimeout(() => {
280
+ if (!hasError) {
281
+ hasError = true;
282
+ res.statusCode = 500;
283
+ res.end("RSC render timeout");
284
+ }
285
+ }, 5000);
286
+
287
+ const messageHandler = (
288
+ message: RscWorkerMessage | RscWorkerResponse
289
+ ) => {
290
+ try {
291
+ switch (message.type) {
292
+ case "RSC_CHUNK":
293
+ // Write chunk directly to response
294
+ if (!hasError) {
295
+ res.write(message.chunk);
296
+ }
297
+ break;
298
+
299
+ case "RSC_END":
300
+ clearTimeout(timeout);
301
+ if (!hasError) {
302
+ res.end();
303
+ }
304
+ worker.off("message", messageHandler);
305
+ break;
306
+
307
+ case "ERROR":
308
+ clearTimeout(timeout);
309
+ log.error("Render error", message);
310
+ if (!hasError) {
311
+ hasError = true;
312
+ res.statusCode = 500;
313
+ res.end(message.error);
314
+ }
315
+ worker.off("message", messageHandler);
316
+ break;
317
+ }
318
+ } catch (error) {
319
+ clearTimeout(timeout);
320
+ if (!hasError) {
321
+ hasError = true;
322
+ res.statusCode = 500;
323
+ res.end(
324
+ error instanceof Error ? error.message : String(error)
325
+ );
326
+ }
327
+ worker.off("message", messageHandler);
328
+ }
329
+ };
330
+
331
+ worker.on("message", messageHandler);
332
+ worker.once("error", (error) => {
333
+ clearTimeout(timeout);
334
+ if (!hasError) {
335
+ hasError = true;
336
+ res.statusCode = 500;
337
+ res.end(error instanceof Error ? error.message : String(error));
338
+ }
339
+ worker.off("message", messageHandler);
340
+ });
341
+ worker.postMessage({
342
+ type: "RSC_RENDER",
343
+ id: value,
344
+ pageImport,
345
+ propsImport,
346
+ url: req.url ?? "/",
347
+ pageExportName:
348
+ userOptions.pageExportName ?? DEFAULT_CONFIG.PAGE_EXPORT_NAME,
349
+ propsExportName:
350
+ userOptions.propsExportName ?? DEFAULT_CONFIG.PROPS_EXPORT_NAME,
351
+ outDir: userOptions.build.outDir,
352
+ projectRoot: root,
353
+ moduleRootPath:
354
+ userOptions.build.preserveModulesRoot === true
355
+ ? userOptions.moduleBase
356
+ : "",
357
+ moduleBaseURL: ``,
358
+ moduleBasePath: '/',
359
+ pipableStreamOptions: userOptions.pipableStreamOptions,
360
+ cssFiles: []
361
+ } satisfies RscRenderMessage);
138
362
  } catch (error) {
139
- next();
363
+ log.error("Middleware error:", error);
364
+ res.statusCode = 500;
365
+ res.end(error instanceof Error ? error.message : String(error));
140
366
  }
141
- } else if(req.url && req.url.endsWith('.rsc')) {
142
- // stream the rsc stream
143
- throw new Error('Not implemented');
367
+ } else {
368
+ next();
144
369
  }
145
370
  });
146
- }
371
+ },
147
372
  };
148
373
  }
@@ -29,9 +29,9 @@ export async function createHandler<T>(
29
29
 
30
30
  const Html = pluginOptions.Html ?? DEFAULT_CONFIG.HTML;
31
31
  const pageExportName =
32
- pluginOptions.pageExportName ?? DEFAULT_CONFIG.PAGE_EXPORT;
32
+ pluginOptions.pageExportName ?? DEFAULT_CONFIG.PAGE_EXPORT_NAME;
33
33
  const propsExportName =
34
- pluginOptions.propsExportName ?? DEFAULT_CONFIG.PROPS_EXPORT;
34
+ pluginOptions.propsExportName ?? DEFAULT_CONFIG.PROPS_EXPORT_NAME;
35
35
  const controller = new AbortController();
36
36
 
37
37
  const cssFiles = streamOptions.cssFiles;
@@ -63,7 +63,13 @@ export async function createHandler<T>(
63
63
  try {
64
64
  const mod = await streamOptions.loader(id);
65
65
  const pageCss = await Promise.resolve(getCss(id));
66
- Array.from(pageCss.keys()).forEach((css) => cssModules.add(css));
66
+ Array.from(pageCss.keys()).forEach((css) => {
67
+ cssModules.add(css);
68
+ // Notify about new CSS file if callback exists
69
+ if (streamOptions.onCssFile) {
70
+ streamOptions.onCssFile(css);
71
+ }
72
+ });
67
73
  return mod as Record<string, any>;
68
74
  } catch (e: any) {
69
75
  if (e.message?.includes("module runner has been closed")) {
@@ -120,7 +126,6 @@ export async function createHandler<T>(
120
126
  if (streamOptions.cssFiles) {
121
127
  streamOptions.cssFiles.forEach((css) => cssModules.add(css));
122
128
  }
123
-
124
129
  const stream = createRscStream({
125
130
  Html: Html,
126
131
  Page: Page,
@@ -139,7 +144,6 @@ export async function createHandler<T>(
139
144
  });
140
145
 
141
146
  if (!stream) {
142
- console.log("[createHandler] No stream created for route:", url);
143
147
  return { type: "skip" as const };
144
148
  }
145
149
 
@@ -1,61 +1,82 @@
1
1
  import * as React from "react";
2
- import type { PipeableStream } from "react-dom/server";
3
2
  // @ts-ignore
4
3
  import { renderToPipeableStream } from "react-server-dom-esm/server.node";
5
- import { CssCollector } from "../components.js";
6
- import type { RscStreamOptions } from "../types.js";
4
+ import type { PipeableStreamOptions } from "../worker/types.js";
5
+ import type { Logger } from "vite";
7
6
 
8
- export function createRscStream(
9
- streamOptions: RscStreamOptions
10
- ): PipeableStream {
11
- const {
12
- Html,
13
- Page,
14
- props,
15
- logger,
16
- cssFiles,
17
- moduleBasePath,
18
- pipableStreamOptions,
19
- htmlProps,
20
- } = streamOptions;
21
- const css = Array.isArray(cssFiles)
22
- ? cssFiles.map((css, index) =>
23
- React.createElement(CssCollector, {
24
- key: `css-${index}`,
25
- url: css.startsWith("/") ? css : `/${css}`,
26
- moduleBasePath,
27
- })
28
- )
29
- : [];
7
+
8
+ // CSS collector component
9
+ function CssCollector({
10
+ children,
11
+ cssFiles,
12
+ }: {
13
+ children?: React.ReactNode;
14
+ cssFiles: string[];
15
+ }) {
16
+ return React.createElement(
17
+ React.Fragment,
18
+ null,
19
+ ...cssFiles.map((css) => {
20
+ const url = css.startsWith('/') || css.startsWith('http') || css.startsWith('./') ? css : '/'+css
21
+ return React.createElement('link', {
22
+ key: css,
23
+ rel: 'stylesheet',
24
+ href: url
25
+ })
26
+ }),
27
+ children
28
+ );
29
+ }
30
+
31
+ export function createRscStream({
32
+ Html,
33
+ Page,
34
+ props,
35
+ moduleBasePath,
36
+ logger,
37
+ cssFiles = [],
38
+ route,
39
+ url,
40
+ pipableStreamOptions,
41
+ htmlProps,
42
+ }: {
43
+ Html: React.ComponentType<any>;
44
+ Page: React.ComponentType<any>;
45
+ props: any;
46
+ moduleBasePath: string;
47
+ logger: Logger;
48
+ cssFiles?: string[];
49
+ route: string;
50
+ url: string;
51
+ pipableStreamOptions?: PipeableStreamOptions;
52
+ htmlProps?: any;
53
+ }) {
54
+
30
55
  const htmlIsFragment = Html == React.Fragment;
31
- console.log("[vite:plugin-react-server] Creating RSC stream with options:", {
32
- moduleBasePath,
33
- importMap: pipableStreamOptions?.importMap,
34
- moduleRootPath: moduleBasePath // Make sure this matches client component paths
35
- });
36
- return renderToPipeableStream(
37
- React.createElement(
38
- Html,
56
+ const withCss = React.createElement(
57
+ CssCollector,
58
+ { cssFiles },
59
+ React.createElement(Page, props)
60
+ )
61
+ // Otherwise wrap with Html component
62
+ const content = htmlIsFragment
63
+ ? withCss
64
+ : React.createElement(Html, htmlProps, withCss);
65
+ try {
66
+ return renderToPipeableStream(
67
+ content,
68
+ moduleBasePath,
39
69
  {
40
- key: "html",
41
- ...(htmlIsFragment ? {} : htmlProps)
42
- },
43
- React.createElement(Page, { key: "page", ...props }),
44
- ...css
45
- ),
46
- moduleBasePath,
47
- {
48
- onError: logger?.error ?? console.error,
49
- onPostpone: logger?.info ?? console.info,
50
- environmentName: "Server",
51
- importMap: {
52
- imports: {
53
- ...pipableStreamOptions?.importMap?.imports,
54
- // Make sure paths are absolute and match module resolution
55
- '/': moduleBasePath
56
- }
57
- },
58
- ...pipableStreamOptions
59
- }
60
- );
70
+ onError: (error: Error) => {
71
+ logger.error(`Stream error at ${route}.`, {error});
72
+ },
73
+ onPostpone: logger.info ?? console.info,
74
+ environmentName: "Server",
75
+ ...pipableStreamOptions,
76
+ }
77
+ );
78
+ } catch (error) {
79
+ logger.error(`Failed to create stream for ${route}.`, {error: error as Error});
80
+ return null;
81
+ }
61
82
  }