vite-plugin-react-server 1.1.7 → 1.1.9

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 (143) hide show
  1. package/dist/package.json +10 -5
  2. package/dist/plugin/config/defaults.d.ts +1 -1
  3. package/dist/plugin/config/defaults.js +1 -1
  4. package/dist/plugin/config/defaults.js.map +1 -1
  5. package/dist/plugin/config/resolveAutoDiscover.d.ts +2 -0
  6. package/dist/plugin/config/resolveAutoDiscover.d.ts.map +1 -1
  7. package/dist/plugin/config/resolveAutoDiscover.js +15 -18
  8. package/dist/plugin/config/resolveAutoDiscover.js.map +1 -1
  9. package/dist/plugin/config/resolveOptions.d.ts.map +1 -1
  10. package/dist/plugin/config/resolveOptions.js +4 -1
  11. package/dist/plugin/config/resolveOptions.js.map +1 -1
  12. package/dist/plugin/config/resolveUserConfig.d.ts.map +1 -1
  13. package/dist/plugin/config/resolveUserConfig.js +64 -30
  14. package/dist/plugin/config/resolveUserConfig.js.map +1 -1
  15. package/dist/plugin/helpers/collectBundleManifestCss.d.ts +0 -6
  16. package/dist/plugin/helpers/collectBundleManifestCss.d.ts.map +1 -1
  17. package/dist/plugin/helpers/collectBundleManifestCss.js +2 -110
  18. package/dist/plugin/helpers/collectViteModuleGraphCss.d.ts +2 -1
  19. package/dist/plugin/helpers/collectViteModuleGraphCss.d.ts.map +1 -1
  20. package/dist/plugin/helpers/collectViteModuleGraphCss.js +19 -18
  21. package/dist/plugin/helpers/collectViteModuleGraphCss.js.map +1 -1
  22. package/dist/plugin/helpers/createCssProps.d.ts +3 -2
  23. package/dist/plugin/helpers/createCssProps.d.ts.map +1 -1
  24. package/dist/plugin/helpers/createCssProps.js +10 -6
  25. package/dist/plugin/helpers/createCssProps.js.map +1 -1
  26. package/dist/plugin/helpers/createRscStream.d.ts.map +1 -1
  27. package/dist/plugin/helpers/createRscStream.js +37 -43
  28. package/dist/plugin/helpers/createRscStream.js.map +1 -1
  29. package/dist/plugin/helpers/formatMetrics.d.ts +4 -0
  30. package/dist/plugin/helpers/formatMetrics.d.ts.map +1 -0
  31. package/dist/plugin/helpers/formatMetrics.js +24 -0
  32. package/dist/plugin/helpers/tryManifest.d.ts.map +1 -1
  33. package/dist/plugin/helpers/tryManifest.js +0 -8
  34. package/dist/plugin/helpers/tryManifest.js.map +1 -1
  35. package/dist/plugin/html.js +1 -1
  36. package/dist/plugin/html.js.map +1 -1
  37. package/dist/plugin/loader/createBuildLoader.d.ts.map +1 -1
  38. package/dist/plugin/loader/createBuildLoader.js +2 -0
  39. package/dist/plugin/loader/createBuildLoader.js.map +1 -1
  40. package/dist/plugin/metrics/formatMetrics.d.ts +4 -0
  41. package/dist/plugin/metrics/formatMetrics.d.ts.map +1 -0
  42. package/dist/plugin/metrics/formatMetrics.js +39 -0
  43. package/dist/plugin/metrics/formatMetrics.js.map +1 -0
  44. package/dist/plugin/metrics/index.d.ts +3 -0
  45. package/dist/plugin/metrics/index.d.ts.map +1 -0
  46. package/dist/plugin/metrics/index.js +1 -0
  47. package/dist/plugin/metrics.js +7 -0
  48. package/dist/plugin/metrics.js.map +1 -0
  49. package/dist/plugin/react-client/createWorkerStream.d.ts +16 -0
  50. package/dist/plugin/react-client/createWorkerStream.d.ts.map +1 -0
  51. package/dist/plugin/react-client/createWorkerStream.js +88 -0
  52. package/dist/plugin/react-client/createWorkerStream.js.map +1 -0
  53. package/dist/plugin/react-client/plugin.d.ts.map +1 -1
  54. package/dist/plugin/react-client/plugin.js +4 -1
  55. package/dist/plugin/react-client/plugin.js.map +1 -1
  56. package/dist/plugin/react-client/restartWorker.d.ts +6 -0
  57. package/dist/plugin/react-client/restartWorker.d.ts.map +1 -0
  58. package/dist/plugin/react-client/restartWorker.js +53 -0
  59. package/dist/plugin/react-client/restartWorker.js.map +1 -0
  60. package/dist/plugin/react-client/server.d.ts +5 -4
  61. package/dist/plugin/react-client/server.d.ts.map +1 -1
  62. package/dist/plugin/react-client/server.js +79 -110
  63. package/dist/plugin/react-client/server.js.map +1 -1
  64. package/dist/plugin/react-server/server.d.ts.map +1 -1
  65. package/dist/plugin/react-server/server.js +23 -28
  66. package/dist/plugin/react-server/server.js.map +1 -1
  67. package/dist/plugin/react-static/collectHtmlWorkerContent.js +1 -1
  68. package/dist/plugin/react-static/collectHtmlWorkerContent.js.map +1 -1
  69. package/dist/plugin/react-static/collectRscContent.js +1 -1
  70. package/dist/plugin/react-static/collectRscContent.js.map +1 -1
  71. package/dist/plugin/react-static/configurePreviewServer.d.ts.map +1 -1
  72. package/dist/plugin/react-static/configurePreviewServer.js +23 -4
  73. package/dist/plugin/react-static/configurePreviewServer.js.map +1 -1
  74. package/dist/plugin/react-static/fileWriter.d.ts.map +1 -1
  75. package/dist/plugin/react-static/fileWriter.js +5 -1
  76. package/dist/plugin/react-static/fileWriter.js.map +1 -1
  77. package/dist/plugin/react-static/plugin.d.ts.map +1 -1
  78. package/dist/plugin/react-static/plugin.js +50 -33
  79. package/dist/plugin/react-static/plugin.js.map +1 -1
  80. package/dist/plugin/types.d.ts +6 -7
  81. package/dist/plugin/types.d.ts.map +1 -1
  82. package/dist/plugin/utils/callServer.d.ts +2 -0
  83. package/dist/plugin/utils/callServer.d.ts.map +1 -0
  84. package/dist/plugin/utils/callServer.js +26 -0
  85. package/dist/plugin/utils/callServer.js.map +1 -0
  86. package/dist/plugin/utils/createReactFetcher.d.ts +7 -0
  87. package/dist/plugin/utils/createReactFetcher.d.ts.map +1 -0
  88. package/dist/plugin/utils/createReactFetcher.js +33 -0
  89. package/dist/plugin/utils/createReactFetcher.js.map +1 -0
  90. package/dist/plugin/utils/index.d.ts +4 -0
  91. package/dist/plugin/utils/index.d.ts.map +1 -0
  92. package/dist/plugin/utils/index.js +3 -0
  93. package/dist/plugin/utils/pageURL.d.ts +2 -0
  94. package/dist/plugin/utils/pageURL.d.ts.map +1 -0
  95. package/dist/plugin/utils/pageURL.js +21 -0
  96. package/dist/plugin/utils/pageURL.js.map +1 -0
  97. package/dist/plugin/utils.js +9 -0
  98. package/dist/plugin/utils.js.map +1 -0
  99. package/dist/plugin/worker/rsc/handleRender.d.ts +6 -2
  100. package/dist/plugin/worker/rsc/handleRender.d.ts.map +1 -1
  101. package/dist/plugin/worker/rsc/handleRender.js +26 -55
  102. package/dist/plugin/worker/rsc/handleRender.js.map +1 -1
  103. package/dist/plugin/worker/rsc/messageHandler.d.ts +1 -2
  104. package/dist/plugin/worker/rsc/messageHandler.d.ts.map +1 -1
  105. package/dist/plugin/worker/rsc/messageHandler.js +45 -2
  106. package/dist/plugin/worker/rsc/messageHandler.js.map +1 -1
  107. package/dist/plugin/worker/rsc/state.d.ts.map +1 -1
  108. package/dist/plugin/worker/rsc/state.js +1 -5
  109. package/dist/plugin/worker/rsc/state.js.map +1 -1
  110. package/dist/tsconfig.tsbuildinfo +1 -1
  111. package/package.json +10 -5
  112. package/plugin/config/defaults.tsx +1 -1
  113. package/plugin/config/resolveAutoDiscover.ts +17 -22
  114. package/plugin/config/resolveOptions.ts +5 -1
  115. package/plugin/config/resolveUserConfig.ts +72 -37
  116. package/plugin/helpers/collectBundleManifestCss.ts +1 -160
  117. package/plugin/helpers/collectViteModuleGraphCss.ts +31 -29
  118. package/plugin/helpers/createCssProps.tsx +22 -11
  119. package/plugin/helpers/createRscStream.tsx +42 -46
  120. package/plugin/helpers/formatMetrics.ts +37 -0
  121. package/plugin/helpers/tryManifest.ts +0 -9
  122. package/plugin/html.tsx +1 -1
  123. package/plugin/loader/createBuildLoader.ts +2 -0
  124. package/plugin/metrics/formatMetrics.ts +37 -0
  125. package/plugin/metrics/index.ts +2 -0
  126. package/plugin/react-client/createWorkerStream.ts +107 -0
  127. package/plugin/react-client/plugin.ts +3 -0
  128. package/plugin/react-client/restartWorker.ts +65 -0
  129. package/plugin/react-client/server.ts +97 -146
  130. package/plugin/react-server/server.ts +24 -29
  131. package/plugin/react-static/collectHtmlWorkerContent.ts +1 -1
  132. package/plugin/react-static/collectRscContent.ts +2 -2
  133. package/plugin/react-static/configurePreviewServer.ts +29 -6
  134. package/plugin/react-static/fileWriter.ts +5 -1
  135. package/plugin/react-static/plugin.ts +58 -38
  136. package/plugin/types.ts +11 -11
  137. package/plugin/utils/callServer.ts +25 -0
  138. package/plugin/utils/createReactFetcher.ts +31 -0
  139. package/plugin/utils/index.ts +3 -0
  140. package/plugin/utils/pageURL.ts +28 -0
  141. package/plugin/worker/rsc/handleRender.ts +33 -71
  142. package/plugin/worker/rsc/messageHandler.tsx +48 -6
  143. package/plugin/worker/rsc/state.ts +1 -5
@@ -28,7 +28,7 @@ export async function collectRscContent(
28
28
  handlerOptions: CreateHandlerOptions
29
29
  ): Promise<{ stream: PassThrough; metrics: StreamMetrics }> {
30
30
  const metrics = createStreamMetrics();
31
- const startTime = performance.now()
31
+ const startTime = performance.now();
32
32
 
33
33
  const outputPath = join(
34
34
  handlerOptions.build.outDir,
@@ -49,7 +49,7 @@ export async function collectRscContent(
49
49
  callback(null, chunk);
50
50
  },
51
51
  flush(callback) {
52
- metrics.duration = Date.now() - startTime;
52
+ metrics.duration = performance.now() - startTime;
53
53
  callback();
54
54
  }
55
55
  });
@@ -16,12 +16,15 @@ export async function configurePreviewServer({
16
16
  const staticHostDir = join(userOptions.projectRoot, userOptions.build.outDir, userOptions.build.static);
17
17
  server.middlewares.use(async (req, res, next) => {
18
18
  if(!req.url) {
19
+ console.log("no url")
19
20
  return next();
20
21
  }
21
- const [, value] = userOptions.normalizer(req.url);
22
+ const [withoutQuery] = req.url.split("?");
23
+ const [, value] = userOptions.normalizer(withoutQuery);
24
+ const ext = value.slice(value.lastIndexOf("."));
22
25
  // handle index.html
23
26
  const isHtml = userOptions.autoDiscover.htmlPattern(value)
24
- if (isHtml || req.headers.accept?.includes("text/html")) {
27
+ if (isHtml || (req.headers.accept?.includes("text/html"))) {
25
28
  const indexHtml = isHtml ? join(staticHostDir, value) : join(staticHostDir, value, userOptions.build.htmlOutputPath);
26
29
  try {
27
30
  const stats = await stat(indexHtml);
@@ -35,7 +38,7 @@ export async function configurePreviewServer({
35
38
  }
36
39
  }
37
40
  const isRsc = userOptions.autoDiscover.rscPattern(value)
38
- if (isRsc || req.headers.accept?.includes("text/x-component")) {
41
+ if (isRsc || (req.headers.accept?.includes("text/x-component"))) {
39
42
  const rsc = isRsc ? join(staticHostDir, value) : join(staticHostDir, value, userOptions.build.rscOutputPath);
40
43
  try {
41
44
  const stats = await stat(rsc);
@@ -48,14 +51,34 @@ export async function configurePreviewServer({
48
51
  // File doesn't exist, continue to next middleware
49
52
  }
50
53
  }
51
- const ext = value.slice(value.lastIndexOf("."));
54
+ const isCss = userOptions.autoDiscover.cssPattern(value)
55
+ if (isCss || (req.headers.accept?.includes("text/css") && (ext === ""))) {
56
+ const css = isCss ? join(staticHostDir, value) : join(staticHostDir, value);
57
+ try {
58
+ const stats = await stat(css);
59
+ if (stats.isFile()) {
60
+ res.setHeader("Content-Type", "text/css; charset=utf-8");
61
+ await pipeline(createReadStream(css), res);
62
+ return;
63
+ }
64
+ } catch {
65
+ // File doesn't exist, continue to next middleware
66
+ }
67
+ }
68
+ // Handle static files including CSS
52
69
  if (ext) {
53
70
  const filePath = join(staticHostDir, value);
54
71
  try {
55
72
  const stats = await stat(filePath);
56
73
  if (stats.isFile()) {
57
- const contentType = MIME_TYPES[ext] || "application/octet-stream";
58
- res.setHeader("Content-Type", `${contentType}; charset=utf-8`);
74
+ // Set proper MIME type based on file extension
75
+ const contentType = MIME_TYPES[ext];
76
+ // Ensure CSS files are served with the correct MIME type
77
+ if (contentType) {
78
+ res.setHeader("Content-Type", `${contentType}; charset=utf-8`);
79
+ } else {
80
+ res.setHeader("Content-Type", "application/octet-stream");
81
+ }
59
82
  await pipeline(createReadStream(filePath), res);
60
83
  return;
61
84
  }
@@ -43,7 +43,11 @@ export async function fileWriter(
43
43
  );
44
44
 
45
45
  // Ensure directory exists
46
- await mkdir(join(options.build.outDir, options.build.static, options.route), { recursive: true });
46
+ try {
47
+ await mkdir(join(options.build.outDir, options.build.static, options.route), { recursive: true });
48
+ } catch (error) {
49
+ console.error(`Error creating directory: ${error}`);
50
+ }
47
51
 
48
52
  // Create write stream
49
53
  const writeStream = createWriteStream(outputPath);
@@ -48,6 +48,7 @@ import {
48
48
  import { collectManifestCss } from "../helpers/collectManifestCss.js";
49
49
  import { createCssProps } from "../helpers/createCssProps.js";
50
50
  import { tryManifest } from "../helpers/tryManifest.js";
51
+ import { performance } from "node:perf_hooks";
51
52
 
52
53
  if (getCondition() !== "react-server") {
53
54
  throw new Error(
@@ -138,7 +139,6 @@ export function reactStaticPlugin(options: StreamPluginOptions): VitePlugin<{
138
139
  timing.renderStart = Date.now();
139
140
  },
140
141
 
141
-
142
142
  async writeBundle(options, bundle) {
143
143
  try {
144
144
  const bundleManifest = getBundleManifest<false>({
@@ -159,10 +159,7 @@ export function reactStaticPlugin(options: StreamPluginOptions): VitePlugin<{
159
159
 
160
160
  const clientManifestResult = await tryManifest({
161
161
  root: userOptions.projectRoot,
162
- outDir: join(
163
- userOptions.build.outDir,
164
- userOptions.build.client
165
- ),
162
+ outDir: join(userOptions.build.outDir, userOptions.build.client),
166
163
  ssrManifest: false,
167
164
  });
168
165
  if (clientManifestResult.type === "error") {
@@ -195,15 +192,29 @@ export function reactStaticPlugin(options: StreamPluginOptions): VitePlugin<{
195
192
  // Collect CSS files for each page and its props
196
193
  for (const [url, { page, props }] of autoDiscoveredFiles?.urlMap ??
197
194
  []) {
195
+ const transformedServerManifest = Object.fromEntries(
196
+ Object.entries(serverManifest).map(([key, value]) => {
197
+ if (!value.css?.length) {
198
+ return [key, value];
199
+ }
200
+ return [
201
+ key,
202
+ {
203
+ ...value,
204
+ css:
205
+ autoDiscoveredFiles?.staticManifest[key]?.css ?? value.css,
206
+ },
207
+ ];
208
+ })
209
+ );
198
210
  const cssInputs = collectManifestCss(
199
- serverManifest,
211
+ transformedServerManifest,
200
212
  props ? [page, props] : page,
201
213
  userOptions
202
214
  );
203
215
 
204
216
  // Create a map for this page's CSS files
205
217
  const pageCssMap: Map<string, CssContent> = new Map();
206
-
207
218
  // Add global styles if they exist
208
219
  if (Object.keys(globalCssInputs).length > 0) {
209
220
  for (const [, value] of Object.entries(globalCssInputs)) {
@@ -219,11 +230,7 @@ export function reactStaticPlugin(options: StreamPluginOptions): VitePlugin<{
219
230
  createCssProps({
220
231
  id: value,
221
232
  code: cssContent,
222
- css: userOptions.css,
223
- moduleBaseURL: userOptions.moduleBaseURL,
224
- moduleBasePath: userOptions.moduleBasePath,
225
- moduleRootPath: userOptions.moduleRootPath,
226
- projectRoot: userOptions.projectRoot,
233
+ userOptions: userOptions,
227
234
  })
228
235
  );
229
236
  }
@@ -232,25 +239,28 @@ export function reactStaticPlugin(options: StreamPluginOptions): VitePlugin<{
232
239
 
233
240
  // Add page-specific styles
234
241
  for (const [, value] of Object.entries(cssInputs)) {
235
- const { default: cssContent } = await buildLoader(
236
- value + "?inline"
237
- );
238
- if (typeof cssContent !== "string") {
239
- continue;
240
- }
241
- if (cssContent) {
242
- pageCssMap.set(
243
- value,
244
- createCssProps({
245
- id: value,
246
- code: cssContent,
247
- css: userOptions.css,
248
- moduleBaseURL: userOptions.moduleBaseURL,
249
- moduleBasePath: userOptions.moduleBasePath,
250
- moduleRootPath: userOptions.moduleRootPath,
251
- projectRoot: userOptions.projectRoot,
252
- })
242
+ try {
243
+ const { default: cssContent } = await buildLoader(
244
+ value + "?inline"
253
245
  );
246
+ if (typeof cssContent !== "string") {
247
+ continue;
248
+ }
249
+ if (cssContent) {
250
+ // Ensure the CSS file path is properly resolved
251
+ const cssPath = value.startsWith("/") ? value.slice(1) : value;
252
+ pageCssMap.set(
253
+ cssPath,
254
+ createCssProps({
255
+ id: cssPath,
256
+ code: cssContent,
257
+ userOptions: userOptions,
258
+ })
259
+ );
260
+ }
261
+ } catch (error) {
262
+ console.warn(`Failed to process CSS file ${value}:`, error);
263
+ continue;
254
264
  }
255
265
  }
256
266
  cssFilesByPage.set(url, pageCssMap);
@@ -269,17 +279,19 @@ export function reactStaticPlugin(options: StreamPluginOptions): VitePlugin<{
269
279
  }
270
280
  const staticManifest = autoDiscoveredFiles?.staticManifest ?? {};
271
281
  const indexHtml = staticManifest?.["index.html"]?.file;
282
+ const safeParseURL = (() => {
283
+ try {
284
+ return new URL(join(userOptions.moduleBasePath, indexHtml), userOptions.moduleBaseURL).href;
285
+ } catch (error) {
286
+ return userOptions.moduleBaseURL + join(userOptions.moduleBasePath, indexHtml);
287
+ }
288
+ })();
272
289
  const pipeableStreamOptions = {
273
290
  ...userOptions.pipeableStreamOptions,
274
291
  bootstrapModules: [
275
- ...(indexHtml
292
+ ...(safeParseURL
276
293
  ? [
277
- userOptions.moduleBaseURL !== ""
278
- ? new URL(
279
- join(userOptions.moduleBasePath, indexHtml),
280
- userOptions.moduleBaseURL
281
- ).href
282
- : join(userOptions.moduleBasePath, indexHtml),
294
+ safeParseURL,
283
295
  ]
284
296
  : []),
285
297
  ...(userOptions.pipeableStreamOptions?.bootstrapModules ?? []),
@@ -292,12 +304,20 @@ export function reactStaticPlugin(options: StreamPluginOptions): VitePlugin<{
292
304
  );
293
305
  // Create worker
294
306
  if (!worker) {
295
- const viteEnvPrefix = typeof resolvedConfig.envPrefix === 'string' ? resolvedConfig.envPrefix : Array.isArray(resolvedConfig.envPrefix) ? resolvedConfig.envPrefix[0] : 'VITE_'
307
+ const viteEnvPrefix =
308
+ typeof resolvedConfig.envPrefix === "string"
309
+ ? resolvedConfig.envPrefix
310
+ : Array.isArray(resolvedConfig.envPrefix)
311
+ ? resolvedConfig.envPrefix[0]
312
+ : "VITE_";
313
+ const routeCount = autoDiscoveredFiles?.urlMap.size ?? 0;
314
+ const maxListeners = routeCount + 1;
296
315
  const workerResult = await createWorker({
297
316
  projectRoot: userOptions.projectRoot,
298
317
  workerPath: userOptions.htmlWorkerPath,
299
318
  currentCondition: "react-server",
300
319
  reverseCondition: "react-client",
320
+ maxListeners: maxListeners,
301
321
  envPrefix: viteEnvPrefix,
302
322
  workerData: {
303
323
  resolvedConfig: serializeResolvedConfig(resolvedConfig),
package/plugin/types.ts CHANGED
@@ -1,25 +1,25 @@
1
+ import { Readable } from "node:stream";
2
+ import type { MessagePort } from "node:worker_threads";
3
+ import type React from "react";
4
+ import type { PropsWithChildren } from "react";
1
5
  import type {
2
6
  NormalizedOutputOptions,
3
7
  OutputBundle,
8
+ PreRenderedAsset,
4
9
  PreRenderedChunk,
5
10
  } from "rollup";
6
- import type { PreRenderedAsset } from "rollup";
11
+ import type { PassThrough, Transform } from "stream";
7
12
  import type {
8
- UserConfig,
9
- BuildOptions,
10
- InlineConfig,
11
13
  AliasOptions,
14
+ BuildOptions,
12
15
  Connect,
13
- ResolveOptions,
14
- Manifest,
16
+ InlineConfig,
15
17
  Logger,
18
+ Manifest,
19
+ ResolveOptions,
20
+ UserConfig,
16
21
  } from "vite";
17
22
  import type { ReactServerDomEsmOptions } from "./worker/types.js";
18
- import type React from "react";
19
- import type { PassThrough, Transform } from "stream";
20
- import type { MessagePort } from "node:worker_threads";
21
- import type { PropsWithChildren } from "react";
22
- import { Readable } from "node:stream";
23
23
 
24
24
  export type OnEvent = (event: PluginEvent) => void;
25
25
 
@@ -0,0 +1,25 @@
1
+ // @ts-nocheck
2
+ import { createFromFetch, encodeReply } from "react-server-dom-esm/client.browser";
3
+
4
+ type ServerResponse = { returnValue: unknown };
5
+
6
+ export const callServer = async (
7
+ id: string,
8
+ args: unknown[]
9
+ ): Promise<unknown> => {
10
+ let baseURL = import.meta.env.BASE_URL
11
+ const response = await createFromFetch(
12
+ fetch(baseURL, {
13
+ method: "POST",
14
+ body: await encodeReply(args),
15
+ headers: {
16
+ Accept: "application/json",
17
+ "Content-Type": "application/json",
18
+ },
19
+ }),
20
+ { callServer, moduleBaseURL: baseURL }
21
+ );
22
+ const returnValue = (response as ServerResponse).returnValue;
23
+ return returnValue;
24
+ };
25
+
@@ -0,0 +1,31 @@
1
+ import type { ReactNode } from "react";
2
+ // @ts-ignore
3
+ import { createFromFetch } from "react-server-dom-esm/client.browser";
4
+ import { callServer } from "./callServer.js";
5
+ import { pageURL } from "./pageURL.js";
6
+
7
+ export function createReactFetcher({
8
+ url = pageURL().pathname,
9
+ moduleBaseURL = new URL(import.meta.env.BASE_URL, window.location.href).href,
10
+ headers = {
11
+ Accept: "text/x-component",
12
+ },
13
+ }: {
14
+ url?: string;
15
+ moduleBaseURL?: string;
16
+ headers?: HeadersInit;
17
+ } = {}): Promise<ReactNode> {
18
+ if(moduleBaseURL.endsWith("/")) {
19
+ moduleBaseURL = moduleBaseURL.slice(0, -1)
20
+ }
21
+ console.log("createReactFetcher", {url, moduleBaseURL})
22
+ return createFromFetch(
23
+ fetch(url, {
24
+ headers: headers,
25
+ }),
26
+ {
27
+ callServer: callServer,
28
+ moduleBaseURL:moduleBaseURL,
29
+ }
30
+ ) as Promise<ReactNode>;
31
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./createReactFetcher.js";
2
+ export * from "./callServer.js";
3
+ export * from "./pageURL.js";
@@ -0,0 +1,28 @@
1
+ export const pageURL = () => {
2
+ const pathname = window.location.pathname;
3
+ const baseUrl = import.meta.env.BASE_URL;
4
+
5
+ // Remove base URL from pathname if it exists
6
+ const relativePath = pathname.startsWith(baseUrl)
7
+ ? pathname.slice(baseUrl.length)
8
+ : pathname;
9
+
10
+ // Handle root path and index.html
11
+ if (relativePath === '/' || relativePath === '' || relativePath === '/index.html' || relativePath === 'index.html') {
12
+ return new URL('/index.rsc', window.location.origin);
13
+ }
14
+
15
+ // Get the path without extension and remove any trailing index
16
+ const pathWithoutExt = relativePath
17
+ .replace(/\.(html|rsc)$/, '')
18
+ .replace(/\/index$/, '');
19
+
20
+ // Construct the RSC path
21
+ const rscPath = pathWithoutExt === '' ? '/index.rsc' : `${pathWithoutExt}/index.rsc`;
22
+
23
+ // Ensure no double slashes and ensure leading slash
24
+ const cleanPath = ('/' + rscPath.replace(/\/+/g, '/')).replace(/\/+/g, '/');
25
+
26
+ console.log("cleanPath", {cleanPath, rscPath, pathWithoutExt, relativePath, baseUrl, pathname})
27
+ return new URL(cleanPath, window.location.origin);
28
+ };
@@ -1,9 +1,3 @@
1
- import type {
2
- RscChunkOutputMessage,
3
- RscEndMessage,
4
- RscMetricsMessage,
5
- RscWorkerOutputMessage,
6
- } from "../types.js";
7
1
  import { resolvePageAndProps } from "../../helpers/resolvePageAndProps.js";
8
2
  import type { RscRenderMessage } from "../types.js";
9
3
  import { activeStreams, cssFiles } from "./state.js";
@@ -11,43 +5,20 @@ import { createRscStream } from "../../helpers/createRscStream.js";
11
5
  import { CssCollector } from "../../css-collector.js";
12
6
  import { PassThrough } from "node:stream";
13
7
  import { join } from "node:path";
14
- import { parentPort, workerData, type MessagePort } from "node:worker_threads";
8
+ import { workerData } from "node:worker_threads";
15
9
  import { React } from "../../vendor.server.js";
16
10
  import { hmrState } from "./state.js";
17
-
11
+ import { performance } from "node:perf_hooks";
18
12
 
19
13
  export async function handleRender(
20
14
  msg: RscRenderMessage,
21
- port = parentPort,
22
- _reactLoaderPort: MessagePort,
23
- _cssLoaderPort: MessagePort
15
+ handlers: {
16
+ onError: (error: any, errorInfo?: any) => void;
17
+ onData: (data: any) => void;
18
+ onEnd: () => void;
19
+ onMetrics: (metrics: any) => void;
20
+ }
24
21
  ) {
25
- const postError = process.env["DEV"]
26
- ? (error: any, errorInfo?: any) => {
27
- if (!(error instanceof Error)) {
28
- error = new Error(String(error));
29
- }
30
- port?.postMessage({
31
- type: "ERROR",
32
- id: msg.id,
33
- errorInfo,
34
- error: {
35
- message: error.message,
36
- stack: error.stack,
37
- name: error.name,
38
- cause: error.cause,
39
- },
40
- } satisfies RscWorkerOutputMessage);
41
- }
42
- : (error: Error, errorInfo?: any) => {
43
- port?.postMessage({
44
- type: "ERROR",
45
- id: msg.id,
46
- errorInfo,
47
- error: error.message,
48
- } satisfies RscWorkerOutputMessage);
49
- };
50
-
51
22
  let {
52
23
  id = workerData.id,
53
24
  route = workerData.route,
@@ -73,30 +44,30 @@ export async function handleRender(
73
44
  pageExportName,
74
45
  propsExportName,
75
46
  route,
76
- loader: async (id: string) => {
77
- // Check if module is invalidated
78
- if (hmrState.get(id)?.invalidated) {
79
- // Clear the HMR state for this module
80
- hmrState.delete(id);
81
- // Force a reload by using a unique query parameter
82
- return import(join(projectRoot, id) + `?t=${Date.now()}`);
47
+ loader: (id: string) => {
48
+ try {
49
+ if (hmrState.get(id)?.invalidated) {
50
+ // Clear the HMR state for this module
51
+ hmrState.delete(id);
52
+ // Force a reload by using a unique query parameter
53
+ return import(join(projectRoot, id) + `?t=${Date.now()}`);
54
+ }
55
+ return import(join(projectRoot, id));
56
+ } catch (error) {
57
+ return Promise.reject(error);
83
58
  }
84
- return import(join(projectRoot, id));
85
59
  },
86
60
  });
87
-
88
61
  if (pageAndPropsResult.type !== "success") {
89
- if (pageAndPropsResult.type === "error") {
90
- postError(pageAndPropsResult.error);
91
- }
92
- return;
62
+ const { error, ...rest } = pageAndPropsResult;
63
+ return handlers.onError(error, rest);
93
64
  }
94
65
 
95
66
  const { PageComponent, pageProps } = pageAndPropsResult;
96
67
 
97
68
  const adaptedOnEvent = (event: "error" | "postpone", data: any) => {
98
69
  if (event === "error") {
99
- postError(data.error, data.errorInfo);
70
+ handlers.onError(data.error, data.errorInfo);
100
71
  }
101
72
  };
102
73
 
@@ -128,7 +99,7 @@ export async function handleRender(
128
99
  });
129
100
 
130
101
  if (streamResult.type !== "success") {
131
- postError(streamResult.error);
102
+ handlers.onError(streamResult.error);
132
103
  return;
133
104
  }
134
105
 
@@ -143,38 +114,29 @@ export async function handleRender(
143
114
 
144
115
  // Handle data chunks
145
116
  passThrough.on("data", (chunk) => {
146
- port?.postMessage({
147
- type: "RSC_CHUNK",
148
- id,
149
- chunk,
150
- } satisfies RscChunkOutputMessage);
117
+ metrics.chunks++;
118
+ metrics.bytes += chunk.length;
119
+ metrics.duration = performance.now() - metrics.startTime;
120
+ handlers.onData(chunk);
151
121
  });
152
122
 
153
123
  // Handle stream end
154
124
  passThrough.on("end", () => {
155
- port?.postMessage({
156
- type: "RSC_END",
157
- id,
158
- } satisfies RscEndMessage);
125
+ metrics.duration = performance.now() - metrics.startTime;
126
+ handlers.onEnd();
159
127
  if (activeStreams.has(id)) {
160
- port?.postMessage({
161
- type: "RSC_METRICS",
162
- id,
163
- metrics,
164
- } satisfies RscMetricsMessage);
128
+ handlers.onMetrics(metrics);
165
129
  activeStreams.delete(id);
166
130
  }
167
131
  });
168
132
 
169
133
  // Handle errors
170
134
  passThrough.on("error", (error) => {
171
- postError(error as Error);
135
+ handlers.onError(error as Error, { reason: `${id} stream error` });
172
136
  activeStreams.delete(id);
173
137
  });
174
138
  } catch (error) {
175
- if (process.env["DEV"]) {
176
- console.error(`[Stream ${id}] Error:`, error);
177
- }
178
- postError(error as Error);
139
+ handlers.onError(error as Error, { reason: `${id} render error` });
140
+ return Promise.reject(error);
179
141
  }
180
142
  }
@@ -1,19 +1,60 @@
1
- import { parentPort, workerData, type MessagePort } from "node:worker_threads";
1
+ import { parentPort, workerData } from "node:worker_threads";
2
2
  import { addCssFileContent, hmrState } from "./state.js";
3
3
  import { handleRender } from "./handleRender.js";
4
+ import type { RscWorkerOutputMessage } from "../types.js";
4
5
 
5
- export function messageHandler(
6
+ export async function messageHandler(
6
7
  msg: any,
7
8
  port = parentPort,
8
- reactLoaderPort: MessagePort,
9
- cssLoaderPort: MessagePort
10
9
  ) {
11
10
  if (!port) {
12
11
  throw new Error("No port found");
13
12
  }
13
+ const handlers = {
14
+ onError: (error: any, errorInfo?: any) => {
15
+ if (!(error instanceof Error)) {
16
+ error = new Error(String(error));
17
+ }
18
+ port.postMessage({
19
+ type: "ERROR",
20
+ id: msg.id,
21
+ errorInfo,
22
+ error: {
23
+ message: error.message,
24
+ stack: error.stack,
25
+ name: error.name,
26
+ cause: error.cause,
27
+ },
28
+ } satisfies RscWorkerOutputMessage);
29
+ port.postMessage({
30
+ type: "RSC_END",
31
+ id: msg.id,
32
+ } satisfies RscWorkerOutputMessage);
33
+ },
34
+ onData: (data: any) => {
35
+ port.postMessage({
36
+ type: "RSC_CHUNK",
37
+ id: msg.id,
38
+ chunk: data,
39
+ } satisfies RscWorkerOutputMessage);
40
+ },
41
+ onEnd: () => {
42
+ port.postMessage({
43
+ type: "RSC_END",
44
+ id: msg.id,
45
+ } satisfies RscWorkerOutputMessage);
46
+ },
47
+ onMetrics: (metrics: any) => {
48
+ port.postMessage({
49
+ type: "RSC_METRICS",
50
+ id: msg.id,
51
+ metrics,
52
+ } satisfies RscWorkerOutputMessage);
53
+ },
54
+ };
14
55
  switch (msg.type) {
15
56
  case "RSC_RENDER":
16
- return handleRender(msg, port, reactLoaderPort, cssLoaderPort);
57
+ return await handleRender(msg, handlers);
17
58
  case "INITIALIZED_REACT_LOADER":
18
59
  return;
19
60
  case "INITIALIZED_CSS_LOADER":
@@ -46,7 +87,7 @@ export function messageHandler(
46
87
  const cssOptions = workerData.userOptions.css || {
47
88
  inlineThreshold: 1000,
48
89
  };
49
-
90
+
50
91
  addCssFileContent(msg.id, msg.content, {
51
92
  projectRoot: workerData.userOptions.projectRoot || process.cwd(),
52
93
  moduleBaseURL: workerData.userOptions.moduleBaseURL || "",
@@ -57,6 +98,7 @@ export function messageHandler(
57
98
  }
58
99
  return;
59
100
  default: {
101
+ console.log("messageHandler", { msg: msg.type });
60
102
  return;
61
103
  }
62
104
  }
@@ -70,11 +70,7 @@ export function addCssFileContent(id: string, code: string, userOptions: Pick<Re
70
70
  cssFiles.set(normalizeId, createCssProps({
71
71
  id,
72
72
  code,
73
- projectRoot: userOptions.projectRoot,
74
- moduleBaseURL: userOptions.moduleBaseURL,
75
- moduleBasePath: userOptions.moduleBasePath,
76
- moduleRootPath: userOptions.moduleRootPath,
77
- css: userOptions.css,
73
+ userOptions
78
74
  }));
79
75
  }
80
76