vite-plugin-react-server 1.1.18 → 1.1.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 (71) hide show
  1. package/README.md +7 -3
  2. package/dist/package.json +1 -1
  3. package/dist/plugin/config/defaults.d.ts +1 -1
  4. package/dist/plugin/config/defaults.d.ts.map +1 -1
  5. package/dist/plugin/config/defaults.js +2 -2
  6. package/dist/plugin/config/defaults.js.map +1 -1
  7. package/dist/plugin/config/resolveOptions.d.ts.map +1 -1
  8. package/dist/plugin/config/resolveOptions.js +4 -4
  9. package/dist/plugin/config/resolveOptions.js.map +1 -1
  10. package/dist/plugin/helpers/handleServerAction.d.ts +34 -0
  11. package/dist/plugin/helpers/handleServerAction.d.ts.map +1 -0
  12. package/dist/plugin/helpers/handleServerAction.js +48 -0
  13. package/dist/plugin/helpers/handleServerAction.js.map +1 -0
  14. package/dist/plugin/loader/handleExports.d.ts +16 -10
  15. package/dist/plugin/loader/handleExports.d.ts.map +1 -1
  16. package/dist/plugin/loader/handleExports.js +32 -16
  17. package/dist/plugin/loader/handleExports.js.map +1 -1
  18. package/dist/plugin/loader/transformModuleIfNeeded.d.ts.map +1 -1
  19. package/dist/plugin/loader/transformModuleIfNeeded.js +22 -11
  20. package/dist/plugin/loader/transformModuleIfNeeded.js.map +1 -1
  21. package/dist/plugin/loader/transformModuleWithPreservedFunctions.d.ts +21 -0
  22. package/dist/plugin/loader/transformModuleWithPreservedFunctions.d.ts.map +1 -1
  23. package/dist/plugin/loader/transformModuleWithPreservedFunctions.js +398 -73
  24. package/dist/plugin/loader/transformModuleWithPreservedFunctions.js.map +1 -1
  25. package/dist/plugin/loader/types.d.ts +9 -3
  26. package/dist/plugin/loader/types.d.ts.map +1 -1
  27. package/dist/plugin/react-client/configureWorkerRequestHandler.d.ts.map +1 -1
  28. package/dist/plugin/react-client/configureWorkerRequestHandler.js +12 -69
  29. package/dist/plugin/react-client/configureWorkerRequestHandler.js.map +1 -1
  30. package/dist/plugin/react-client/handleWorkerServerAction.d.ts +12 -0
  31. package/dist/plugin/react-client/handleWorkerServerAction.d.ts.map +1 -0
  32. package/dist/plugin/react-client/handleWorkerServerAction.js +47 -0
  33. package/dist/plugin/react-client/handleWorkerServerAction.js.map +1 -0
  34. package/dist/plugin/react-server/configureReactServer.js.map +1 -1
  35. package/dist/plugin/react-server/handleServerAction.d.ts +1 -1
  36. package/dist/plugin/react-server/handleServerAction.d.ts.map +1 -1
  37. package/dist/plugin/react-server/handleServerAction.js +42 -6
  38. package/dist/plugin/react-server/handleServerAction.js.map +1 -1
  39. package/dist/plugin/react-server/plugin.js +2 -2
  40. package/dist/plugin/react-server/plugin.js.map +1 -1
  41. package/dist/plugin/react-static/plugin.d.ts.map +1 -1
  42. package/dist/plugin/react-static/plugin.js +10 -2
  43. package/dist/plugin/react-static/plugin.js.map +1 -1
  44. package/dist/plugin/source-map/createMappingsSerializer.js +128 -157
  45. package/dist/plugin/source-map/createMappingsSerializer.js.map +1 -0
  46. package/dist/plugin/types.d.ts +5 -7
  47. package/dist/plugin/types.d.ts.map +1 -1
  48. package/dist/plugin/vendor/types.js +1 -0
  49. package/dist/plugin/worker/rsc/handleRender.d.ts.map +1 -1
  50. package/dist/plugin/worker/rsc/handleRender.js +0 -1
  51. package/dist/plugin/worker/rsc/handleRender.js.map +1 -1
  52. package/dist/plugin/worker/rsc/handlers.js.map +1 -1
  53. package/dist/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +1 -1
  55. package/plugin/config/defaults.tsx +5 -2
  56. package/plugin/config/resolveOptions.ts +9 -10
  57. package/plugin/helpers/handleServerAction.ts +79 -0
  58. package/plugin/loader/handleExports.ts +50 -40
  59. package/plugin/loader/transformModuleIfNeeded.ts +50 -15
  60. package/plugin/loader/transformModuleWithPreservedFunctions.ts +496 -126
  61. package/plugin/loader/types.ts +12 -4
  62. package/plugin/react-client/configureWorkerRequestHandler.ts +11 -87
  63. package/plugin/react-client/handleWorkerServerAction.ts +74 -0
  64. package/plugin/react-server/configureReactServer.ts +1 -1
  65. package/plugin/react-server/handleServerAction.ts +49 -12
  66. package/plugin/react-server/plugin.ts +2 -2
  67. package/plugin/react-static/plugin.ts +10 -2
  68. package/plugin/types.ts +6 -7
  69. package/plugin/vendor/types.ts +1 -0
  70. package/plugin/worker/rsc/handleRender.ts +0 -2
  71. package/plugin/worker/rsc/handlers.ts +3 -3
@@ -1,10 +1,18 @@
1
- import type { Program as AcornProgram, Statement } from "acorn";
1
+ import type { Program as AcornProgram, Statement, Node as AcornNode } from "acorn";
2
2
 
3
3
  // Directive is a special type in Acorn that extends ExpressionStatement
4
4
  export type Directive = Statement & {
5
5
  directive: string;
6
6
  };
7
7
 
8
- export interface Program extends AcornProgram {
9
- exports?: string[];
10
- }
8
+ export type Node = AcornNode;
9
+
10
+ export interface ExportInfo {
11
+ name: string;
12
+ localName?: string;
13
+ type: "function" | "variable" | "class" | "unknown";
14
+ isServerAction?: boolean;
15
+ node?: Node;
16
+ }
17
+
18
+ export type Program = AcornProgram;
@@ -18,8 +18,7 @@ import { getRouteFiles } from "../helpers/getRouteFiles.js";
18
18
  import type { RscWorkerInputMessage } from "../worker/types.js";
19
19
  import { Readable } from "node:stream";
20
20
  import type { ReadableStream } from "node:stream/web";
21
- import { PassThrough } from "node:stream";
22
- import { logError, toError } from "../error/toError.js";
21
+ import { handleWorkerServerAction } from "./handleWorkerServerAction.js";
23
22
 
24
23
  /**
25
24
  * Configures the worker request handler.
@@ -95,93 +94,18 @@ export async function configureWorkerRequestHandler<
95
94
 
96
95
  // Handle server action requests
97
96
  if (info.isServerActionRequest) {
98
- try {
99
- // Read request body
100
- const chunks: Buffer[] = [];
101
- for await (const chunk of req) {
102
- chunks.push(chunk);
103
- }
104
- const body = Buffer.concat(chunks).toString();
105
- const parsed = JSON.parse(body);
106
-
107
- // Get action ID and args from the request body
108
- let id: string;
109
- let args: unknown[];
110
- if (Array.isArray(parsed)) {
111
- // Format 1: Direct args array
112
- args = parsed;
113
- id = req.url?.split("?")[0] ?? "";
114
- } else if (parsed && typeof parsed === "object" && "id" in parsed) {
115
- // Format 2: Object with id and args
116
- id = parsed.id;
117
- args = parsed.args ?? [];
118
- } else {
119
- throw new Error("Invalid server action request format");
120
- }
121
-
122
- // Set up response headers for streaming
123
- res.setHeader("Content-Type", "text/x-component; charset=utf-8");
124
- res.setHeader("Transfer-Encoding", "chunked");
125
- res.setHeader("Connection", "keep-alive");
126
-
127
- if (!currentWorker) {
128
- currentWorker = await restartWorker({
129
- server,
130
- autoDiscoveredFiles,
131
- userOptions: serializedUserOptions,
132
- hmrChannel,
133
- });
134
- }
135
-
136
- // Send server action request to worker
137
- currentWorker!.postMessage({
138
- type: "SERVER_ACTION",
139
- id,
140
- args,
141
- } satisfies RscWorkerInputMessage);
142
-
143
- // Create a pass-through stream for the response
144
- const passThrough = new PassThrough();
145
- passThrough.pipe(res);
146
-
147
- // Handle worker messages
148
- const messageHandler = (message: any) => {
149
- if (message.type === "RSC_CHUNK") {
150
- passThrough.write(message.chunk);
151
- } else if (message.type === "RSC_END") {
152
- passThrough.end();
153
- currentWorker!.removeListener("message", messageHandler);
154
- } else if (message.type === "ERROR") {
155
- passThrough.end();
156
- currentWorker!.removeListener("message", messageHandler);
157
- logError(message.error, server.config.logger);
158
- }
159
- };
160
-
161
- currentWorker!.on("message", messageHandler);
162
-
163
- // Handle errors
164
- passThrough.on("error", (error) => {
165
- logError(error, server.config.logger);
166
- res.end();
97
+ if (!currentWorker) {
98
+ currentWorker = await restartWorker({
99
+ server,
100
+ autoDiscoveredFiles,
101
+ userOptions: serializedUserOptions,
102
+ hmrChannel,
167
103
  });
168
-
169
- return;
170
- } catch (error) {
171
- const err = toError(error);
172
- logError(err, server.config.logger);
173
- res.statusCode = 500;
174
- res.end(
175
- JSON.stringify({
176
- type: "server-action-response",
177
- returnValue: {
178
- success: false,
179
- error: err.message,
180
- },
181
- })
182
- );
183
- return;
184
104
  }
105
+ if (!currentWorker) {
106
+ throw new Error("Failed to start worker");
107
+ }
108
+ return handleWorkerServerAction(req, res, currentWorker, server.config.logger);
185
109
  }
186
110
 
187
111
  // Handle RSC requests
@@ -0,0 +1,74 @@
1
+ import type { ViteDevServer } from "vite";
2
+ import type { Worker } from "node:worker_threads";
3
+ import type { RscWorkerInputMessage } from "../worker/types.js";
4
+ import { logError } from "../error/toError.js";
5
+ import {
6
+ parseServerActionRequest,
7
+ setupServerActionHeaders,
8
+ createServerActionStream,
9
+ handleServerActionError
10
+ } from "../helpers/handleServerAction.js";
11
+
12
+ /**
13
+ * Handles server action requests in the worker scenario.
14
+ *
15
+ * @param req - The incoming request
16
+ * @param res - The response object
17
+ * @param worker - The worker thread
18
+ * @param logger - The Vite logger
19
+ */
20
+ export async function handleWorkerServerAction(
21
+ req: any,
22
+ res: any,
23
+ worker: Worker,
24
+ logger: ViteDevServer["config"]["logger"]
25
+ ) {
26
+ try {
27
+ // Read request body
28
+ const chunks: Buffer[] = [];
29
+ for await (const chunk of req) {
30
+ chunks.push(chunk);
31
+ }
32
+ const body = Buffer.concat(chunks).toString();
33
+
34
+ // Parse the server action request
35
+ const { id, args } = parseServerActionRequest(body, req.url);
36
+
37
+ // Set up response headers
38
+ setupServerActionHeaders(res);
39
+
40
+ // Send server action request to worker
41
+ worker.postMessage({
42
+ type: "SERVER_ACTION",
43
+ id,
44
+ args,
45
+ } satisfies RscWorkerInputMessage);
46
+
47
+ // Create a pass-through stream for the response
48
+ const passThrough = createServerActionStream(res);
49
+
50
+ // Handle worker messages
51
+ const messageHandler = (message: any) => {
52
+ if (message.type === "RSC_CHUNK") {
53
+ passThrough.write(message.chunk);
54
+ } else if (message.type === "RSC_END") {
55
+ passThrough.end();
56
+ worker.removeListener("message", messageHandler);
57
+ } else if (message.type === "ERROR") {
58
+ passThrough.end();
59
+ worker.removeListener("message", messageHandler);
60
+ logError(message.error, logger);
61
+ }
62
+ };
63
+
64
+ worker.on("message", messageHandler);
65
+
66
+ // Handle errors
67
+ passThrough.on("error", (error) => {
68
+ logError(error, logger);
69
+ res.end();
70
+ });
71
+ } catch (error) {
72
+ handleServerActionError(error, res, logger);
73
+ }
74
+ }
@@ -45,7 +45,7 @@ export async function configureReactServer<
45
45
  Html: React.Fragment,
46
46
  onEvent: createEventHandler(onEvent),
47
47
  css: handlerUserOptions.css
48
- };
48
+ } as ResolvedUserOptions<T, InlineCSS>;
49
49
 
50
50
  // Set environment-specific configuration
51
51
  const define = {
@@ -10,14 +10,15 @@ import { ReactDOMServer } from "../vendor/vendor.server.js";
10
10
  import type { IncomingMessage, ServerResponse } from "http";
11
11
 
12
12
  export async function handleServerAction<
13
- T extends PagePropOpt,
14
- InlineCss extends InlineCssOpt
13
+ T extends PagePropOpt = PagePropOpt,
14
+ InlineCss extends InlineCssOpt = InlineCssOpt
15
15
  >(
16
16
  req: IncomingMessage,
17
17
  res: ServerResponse,
18
18
  server: ViteDevServer,
19
19
  handlerOptions: ResolvedUserOptions<T, InlineCss>
20
20
  ) {
21
+ let id = req.url?.split("?")[0] ?? "";
21
22
  try {
22
23
  if (handlerOptions.verbose) {
23
24
  server.config.logger.info(
@@ -25,10 +26,7 @@ export async function handleServerAction<
25
26
  );
26
27
  }
27
28
 
28
- // Parse the request body - handle both formats:
29
- // 1. Direct args array: ["arg1", "arg2"]
30
- // 2. Object with id and args: { id: "path/to/action", args: ["arg1", "arg2"] }
31
- let id: string;
29
+ // Parse the request body
32
30
  let args: unknown[];
33
31
  try {
34
32
  const chunks: Buffer[] = [];
@@ -45,7 +43,6 @@ export async function handleServerAction<
45
43
  // Format 1: Direct args array
46
44
  args = parsed;
47
45
  // Get the action ID from the request URL
48
- id = req.url?.split("?")[0] ?? "";
49
46
  if (handlerOptions.verbose) {
50
47
  server.config.logger.info(
51
48
  `[react-server] Using action ID from URL: ${id}`
@@ -59,9 +56,8 @@ export async function handleServerAction<
59
56
  throw new Error("Invalid server action request format");
60
57
  }
61
58
  } catch (error: unknown) {
62
- const err = toError(error);
63
59
  throw new Error(`Failed to parse server action request`, {
64
- cause: err,
60
+ cause: toError(error),
65
61
  });
66
62
  }
67
63
 
@@ -134,13 +130,15 @@ export async function handleServerAction<
134
130
  );
135
131
  }
136
132
 
137
- // Send the response
133
+ // Send the response using RSC streaming
138
134
  res.setHeader("Content-Type", "text/x-component; charset=utf-8");
135
+ res.setHeader("Content-Length", "0"); // Will be updated after streaming
139
136
 
140
137
  const { pipe } = ReactDOMServer.renderToPipeableStream(
141
138
  {
142
139
  type: "server-action-response",
143
- returnValue: result
140
+ returnValue: result,
141
+ id
144
142
  },
145
143
  handlerOptions.moduleBasePath,
146
144
  {
@@ -149,11 +147,50 @@ export async function handleServerAction<
149
147
  res.statusCode = 500;
150
148
  res.end();
151
149
  },
150
+ onAllReady() {
151
+ // Update content length after streaming is complete
152
+ const contentLength = res.getHeader("Content-Length");
153
+ if (contentLength) {
154
+ res.setHeader("Content-Length", contentLength);
155
+ }
156
+ }
152
157
  }
153
158
  );
154
159
 
155
160
  pipe(res);
156
161
  } catch (error) {
157
- logError(error, server.config.logger);
162
+ const err = toError(error);
163
+ logError(err, server.config.logger);
164
+ res.statusCode = 500;
165
+ res.setHeader("Content-Type", "text/x-component; charset=utf-8");
166
+ res.setHeader("Content-Length", "0"); // Will be updated after streaming
167
+
168
+ const { pipe } = ReactDOMServer.renderToPipeableStream(
169
+ {
170
+ type: "server-action-response",
171
+ returnValue: null,
172
+ error: {
173
+ digest: err.message || "",
174
+ name: err.name || "Error",
175
+ },
176
+ id
177
+ },
178
+ handlerOptions.moduleBasePath,
179
+ {
180
+ onError(error: Error) {
181
+ logError(error, server.config.logger);
182
+ res.statusCode = 500;
183
+ res.end();
184
+ },
185
+ onAllReady() {
186
+ // Update content length after streaming is complete
187
+ const contentLength = res.getHeader("Content-Length");
188
+ if (contentLength) {
189
+ res.setHeader("Content-Length", contentLength);
190
+ }
191
+ }
192
+ }
193
+ );
194
+ pipe(res);
158
195
  }
159
196
  }
@@ -171,12 +171,12 @@ export function reactServerPlugin<
171
171
  const mod = server.moduleGraph.getModuleById(file);
172
172
  if (mod) {
173
173
  // Invalidate the parent module which will handle both client and SSR
174
- server.moduleGraph.invalidateModule(mod);
174
+ server.moduleGraph.invalidateModule(mod, undefined, undefined, true);
175
175
 
176
176
  // Force a reload of the module
177
177
  const newMod = await server.moduleGraph.ensureEntryFromUrl(file, false);
178
178
  if (newMod) {
179
- server.moduleGraph.invalidateModule(newMod);
179
+ server.moduleGraph.invalidateModule(newMod, undefined, undefined, true);
180
180
  }
181
181
  }
182
182
  }
@@ -195,10 +195,18 @@ export function reactStaticPlugin<
195
195
  const cssFilesByPage = new Map();
196
196
 
197
197
  // First collect global styles from index.html
198
- const globalCssInputs = collectManifestCss(
198
+ const indexHtmlCssInputs = collectManifestCss(
199
199
  autoDiscoveredFiles?.staticManifest ?? {},
200
- "index.html"
200
+ userOptions.clientEntry ?? "index.html"
201
201
  );
202
+ const clientEntryCssInputs = collectManifestCss(
203
+ autoDiscoveredFiles?.staticManifest ?? {},
204
+ userOptions.clientEntry
205
+ );
206
+ const globalCssInputs = {
207
+ ...indexHtmlCssInputs,
208
+ ...clientEntryCssInputs,
209
+ };
202
210
 
203
211
  const globalCss: Map<string, CssContent<InlineCSS>> = new Map();
204
212
  // Collect CSS files for each page and its props
package/plugin/types.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Readable } from "node:stream";
2
2
  import type { Worker } from "node:worker_threads";
3
3
  import type React from "react";
4
+ import type { ExoticComponent, FragmentProps } from "react";
4
5
  import type {
5
6
  NormalizedOutputOptions,
6
7
  OutputBundle,
@@ -19,8 +20,6 @@ import type {
19
20
  ViteDevServer,
20
21
  } from "vite";
21
22
  import type { ReactServerDomEsmOptions } from "./worker/types.js";
22
- import type { FragmentProps } from "react";
23
- import type { ExoticComponent } from "react";
24
23
 
25
24
  export type OnEvent = (event: PluginEvent) => void;
26
25
 
@@ -446,7 +445,8 @@ export type PluginEventType = PluginEvent["type"];
446
445
 
447
446
  export interface StreamPluginOptions<
448
447
  T extends PagePropOpt = PagePropOpt,
449
- InlineCSS extends InlineCssOpt = InlineCssOpt
448
+ InlineCSS extends InlineCssOpt = InlineCssOpt,
449
+ As extends AsOpt = AsOpt
450
450
  > {
451
451
  projectRoot?: string; // defaults to process.cwd()
452
452
  moduleBase: string; // defaults to 'src'
@@ -504,9 +504,9 @@ export interface StreamPluginOptions<
504
504
  // default: /\.rsc$/
505
505
  rscPattern?: RegExpOpt;
506
506
  // default serverDirective regex
507
- isServerFunction?: RegExpOpt;
507
+ isServerFunctionCode?: (code: string, moduleId?: string) => boolean;
508
508
  // default clientDirective regex
509
- isClientComponent?: RegExpOpt;
509
+ isClientComponentCode?: (code: string, moduleId?: string) => boolean;
510
510
  }
511
511
  | undefined;
512
512
  // Manual configuration
@@ -522,7 +522,7 @@ export interface StreamPluginOptions<
522
522
  loaderPath?: string;
523
523
  pageExportName?: string;
524
524
  propsExportName?: string;
525
- Html?: React.FC<HtmlProps<T, InlineCSS>>;
525
+ Html?: React.FC<HtmlProps<T, InlineCSS, As>> | typeof React.Fragment;
526
526
  CssCollector?: CssCollectorBoxedType<T, InlineCSS>;
527
527
  build?: BuildConfig;
528
528
  css?: CssCollectorOptions<InlineCSS>;
@@ -805,7 +805,6 @@ export type HtmlProps<
805
805
  manifest: Manifest;
806
806
  CssCollector: CssCollectorBoxedType<T, InlineCSS, As>;
807
807
  globalCss: Map<string, CssContent<InlineCSS>>;
808
- children?: React.ReactNode;
809
808
  as: As;
810
809
  };
811
810
 
@@ -0,0 +1 @@
1
+ //
@@ -46,8 +46,6 @@ export async function handleRender<
46
46
  if (hmrState.get(id)?.invalidated) {
47
47
  // Clear the HMR state for this module
48
48
  hmrState.delete(id);
49
- // Force a reload by using a unique query parameter
50
- console.log("LOADING", id);
51
49
  return import(join(projectRoot, id) + `?t=${Date.now()}`);
52
50
  }
53
51
  return import(join(projectRoot, id));
@@ -67,7 +67,7 @@ export const handlers: Required<StreamHandlers> = {
67
67
  const stream = ReactDOMServer.renderToPipeableStream(
68
68
  {
69
69
  type: "server-action-response",
70
- returnValue: result,
70
+ returnValue: result
71
71
  },
72
72
  userOptions.moduleBasePath,
73
73
  {
@@ -137,7 +137,7 @@ export const handlers: Required<StreamHandlers> = {
137
137
  const stream = ReactDOMServer.renderToPipeableStream(
138
138
  {
139
139
  type: "server-action-response",
140
- returnValue: result,
140
+ returnValue: result
141
141
  },
142
142
  userOptions.moduleBasePath,
143
143
  {
@@ -182,7 +182,7 @@ export const handlers: Required<StreamHandlers> = {
182
182
  const stream = ReactDOMServer.renderToPipeableStream(
183
183
  {
184
184
  type: "server-action-response",
185
- returnValue: { success: false, error: errorMessage },
185
+ returnValue: { success: false, error: errorMessage }
186
186
  },
187
187
  userOptions.moduleBasePath,
188
188
  {