vite-plugin-react-server 1.4.0 → 1.4.2

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 (84) hide show
  1. package/README.md +4 -4
  2. package/dist/package.json +1 -1
  3. package/dist/plugin/dev-server/configureReactServer.client.js +1 -1
  4. package/dist/plugin/dev-server/configureReactServer.server.d.ts.map +1 -1
  5. package/dist/plugin/dev-server/configureReactServer.server.js +5 -8
  6. package/dist/plugin/dev-server/plugin.client.d.ts.map +1 -1
  7. package/dist/plugin/dev-server/plugin.client.js +7 -14
  8. package/dist/plugin/dev-server/plugin.server.d.ts.map +1 -1
  9. package/dist/plugin/dev-server/plugin.server.js +2 -36
  10. package/dist/plugin/error/index.d.ts +1 -2
  11. package/dist/plugin/error/index.d.ts.map +1 -1
  12. package/dist/plugin/error/index.js +2 -3
  13. package/dist/plugin/error/panicThresholdHandler.js +14 -3
  14. package/dist/plugin/error/setupGlobalErrorHandler.d.ts.map +1 -1
  15. package/dist/plugin/error/setupGlobalErrorHandler.js +23 -16
  16. package/dist/plugin/helpers/createRscRenderHelpers.d.ts +0 -5
  17. package/dist/plugin/helpers/createRscRenderHelpers.d.ts.map +1 -1
  18. package/dist/plugin/helpers/createRscRenderHelpers.js +0 -22
  19. package/dist/plugin/helpers/headlessStreamState.d.ts +0 -38
  20. package/dist/plugin/helpers/headlessStreamState.d.ts.map +1 -1
  21. package/dist/plugin/helpers/headlessStreamState.js +0 -48
  22. package/dist/plugin/helpers/index.d.ts +0 -3
  23. package/dist/plugin/helpers/index.d.ts.map +1 -1
  24. package/dist/plugin/helpers/index.js +1 -4
  25. package/dist/plugin/helpers/requestInfo.d.ts.map +1 -1
  26. package/dist/plugin/helpers/requestInfo.js +5 -4
  27. package/dist/plugin/helpers/requestToRoute.d.ts.map +1 -1
  28. package/dist/plugin/helpers/requestToRoute.js +2 -2
  29. package/dist/plugin/helpers/workerCleanup.d.ts +1 -12
  30. package/dist/plugin/helpers/workerCleanup.d.ts.map +1 -1
  31. package/dist/plugin/helpers/workerCleanup.js +1 -1
  32. package/dist/plugin/loader/directives/index.d.ts +0 -1
  33. package/dist/plugin/loader/directives/index.d.ts.map +1 -1
  34. package/dist/plugin/loader/directives/index.js +1 -2
  35. package/dist/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +1 -1
  37. package/plugin/dev-server/configureReactServer.client.ts +1 -1
  38. package/plugin/dev-server/configureReactServer.server.ts +7 -10
  39. package/plugin/dev-server/plugin.client.ts +14 -20
  40. package/plugin/dev-server/plugin.server.ts +2 -49
  41. package/plugin/error/index.ts +1 -2
  42. package/plugin/error/setupGlobalErrorHandler.ts +24 -25
  43. package/plugin/helpers/createRscRenderHelpers.ts +0 -29
  44. package/plugin/helpers/headlessStreamState.ts +0 -69
  45. package/plugin/helpers/index.ts +0 -3
  46. package/plugin/helpers/requestInfo.ts +6 -4
  47. package/plugin/helpers/requestToRoute.ts +3 -2
  48. package/plugin/helpers/workerCleanup.ts +1 -38
  49. package/plugin/loader/directives/index.ts +0 -1
  50. package/plugin/transformer/README.md +1 -1
  51. package/plugin/worker/html/README.md +1 -1
  52. package/plugin/worker/rsc/README.md +1 -1
  53. package/dist/plugin/error/assertPanic.d.ts +0 -2
  54. package/dist/plugin/error/assertPanic.d.ts.map +0 -1
  55. package/dist/plugin/error/assertPanic.js +0 -15
  56. package/dist/plugin/error/directiveError.d.ts +0 -13
  57. package/dist/plugin/error/directiveError.d.ts.map +0 -1
  58. package/dist/plugin/error/directiveError.js +0 -21
  59. package/dist/plugin/error/enhanceError.d.ts +0 -14
  60. package/dist/plugin/error/enhanceError.d.ts.map +0 -1
  61. package/dist/plugin/error/enhanceError.js +0 -24
  62. package/dist/plugin/helpers/createSafePageComponent.d.ts +0 -36
  63. package/dist/plugin/helpers/createSafePageComponent.d.ts.map +0 -1
  64. package/dist/plugin/helpers/createSafePageComponent.js +0 -50
  65. package/dist/plugin/helpers/moduleResolver.d.ts +0 -25
  66. package/dist/plugin/helpers/moduleResolver.d.ts.map +0 -1
  67. package/dist/plugin/helpers/moduleResolver.js +0 -64
  68. package/dist/plugin/helpers/stashReturnValue.d.ts +0 -3
  69. package/dist/plugin/helpers/stashReturnValue.d.ts.map +0 -1
  70. package/dist/plugin/helpers/stashReturnValue.js +0 -23
  71. package/dist/plugin/helpers/workerManager.d.ts +0 -5
  72. package/dist/plugin/helpers/workerManager.d.ts.map +0 -1
  73. package/dist/plugin/helpers/workerManager.js +0 -18
  74. package/dist/plugin/loader/directives/collectExportsFromModule.d.ts +0 -6
  75. package/dist/plugin/loader/directives/collectExportsFromModule.d.ts.map +0 -1
  76. package/dist/plugin/loader/directives/collectExportsFromModule.js +0 -24
  77. package/plugin/error/assertPanic.ts +0 -9
  78. package/plugin/error/directiveError.ts +0 -29
  79. package/plugin/error/enhanceError.ts +0 -41
  80. package/plugin/helpers/createSafePageComponent.ts +0 -64
  81. package/plugin/helpers/moduleResolver.ts +0 -91
  82. package/plugin/helpers/stashReturnValue.ts +0 -19
  83. package/plugin/helpers/workerManager.ts +0 -16
  84. package/plugin/loader/directives/collectExportsFromModule.ts +0 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-react-server",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Vite plugin for React Server Components (RSC)",
5
5
  "type": "module",
6
6
  "main": "./dist/plugin/index.js",
@@ -74,7 +74,7 @@ export const configureReactServer: CreateReactWorkerServerFn =
74
74
  },
75
75
  });
76
76
 
77
- // Return object with restart function and HMR update sender for handleHotUpdate
77
+ // Return object with restart function and HMR update sender for hotUpdate
78
78
  return {
79
79
  restart: restartFn,
80
80
  sendHmrUpdate: (file: string, routes?: string[]) => {
@@ -78,16 +78,8 @@ export const configureReactServer: ConfigureReactServerFn =
78
78
  }
79
79
  activeStreams.clear();
80
80
  activeControllers.clear();
81
- });
82
-
83
- // Handle restart completion
84
- server.ws.on("full-reload", () => {
85
- isRestarting = false;
86
- logger.info("[vite-plugin-react-server] ✅ Server restart completed");
87
- });
88
81
 
89
- // Fallback: reset restart flag after a timeout
90
- server.ws.on("restart", () => {
82
+ // Fallback: reset restart flag after a timeout
91
83
  setTimeout(() => {
92
84
  if (isRestarting) {
93
85
  isRestarting = false;
@@ -98,6 +90,12 @@ export const configureReactServer: ConfigureReactServerFn =
98
90
  }, 5000); // 5 second timeout
99
91
  });
100
92
 
93
+ // Handle restart completion
94
+ server.ws.on("full-reload", () => {
95
+ isRestarting = false;
96
+ logger.info("[vite-plugin-react-server] ✅ Server restart completed");
97
+ });
98
+
101
99
  const loader = async (id: string) => {
102
100
  const [moduleID, exportName] = id.split("#");
103
101
 
@@ -550,7 +548,6 @@ export const configureReactServer: ConfigureReactServerFn =
550
548
 
551
549
  res.statusCode = 500;
552
550
  res.setHeader("Content-Type", "text/x-component; charset=utf-8");
553
- res.setHeader("Content-Length", "0"); // Will be updated after streaming
554
551
 
555
552
  // Note: Worker cleanup is handled by the response close handler
556
553
  }
@@ -35,8 +35,7 @@ export const vitePluginReactDevServer: VitePluginFn = function _vitePluginReactS
35
35
  return {
36
36
  name: "vite-plugin-react-server:dev-server-client",
37
37
  apply: "serve", // Only apply in dev server mode
38
- // Note: Removed applyToEnvironment - handleHotUpdate needs to run regardless
39
- // The plugin should apply to client environment, but handleHotUpdate is a dev server hook
38
+ // No applyToEnvironment hotUpdate needs to run for all environments
40
39
  // that should work regardless of environment filtering
41
40
  config(_config, viteConfigEnv) {
42
41
  configEnv = viteConfigEnv;
@@ -73,7 +72,13 @@ export const vitePluginReactDevServer: VitePluginFn = function _vitePluginReactS
73
72
  resolvedConfig: server.config,
74
73
  });
75
74
  },
76
- handleHotUpdate({ file, server }) {
75
+ hotUpdate(ctx: any) {
76
+ const { file, server } = ctx;
77
+ const envName = ctx.environment?.name ?? 'unknown';
78
+
79
+ // Only run worker invalidation from the client environment (once per change)
80
+ if (envName !== 'client') return;
81
+
77
82
  // Prevent recursive HMR updates
78
83
  if (isProcessingHmr) {
79
84
  return undefined;
@@ -90,6 +95,8 @@ export const vitePluginReactDevServer: VitePluginFn = function _vitePluginReactS
90
95
 
91
96
  // Skip client components — let @vitejs/plugin-react handle them with Fast Refresh
92
97
  const isClientFile = isSourceFile && (() => {
98
+ // Check filename pattern (.client.tsx, etc.) — matches isClientComponentByName
99
+ if (/\.client\.(js|ts|jsx|tsx)$/.test(file)) return true;
93
100
  try {
94
101
  const head = readFileSync(file, 'utf-8').slice(0, 200);
95
102
  return /^\s*["']use client["']/.test(head.split('\n')[0]);
@@ -98,9 +105,6 @@ export const vitePluginReactDevServer: VitePluginFn = function _vitePluginReactS
98
105
 
99
106
  const isServerFile = isSourceFile && !isClientFile;
100
107
 
101
- // Always log for debugging
102
- server.config.logger.info(`[vite-plugin-react-server] handleHotUpdate: file=${file}, normalized=${normalizedFile}, isServerFile=${isServerFile}, isClientFile=${isClientFile}, hasHandler=${!!hmrHandler}`);
103
-
104
108
  if (isServerFile && hmrHandler) {
105
109
  isProcessingHmr = true;
106
110
 
@@ -111,17 +115,8 @@ export const vitePluginReactDevServer: VitePluginFn = function _vitePluginReactS
111
115
  // This clears component caches, but Node.js's ES module cache persists
112
116
  hmrHandler.sendHmrUpdate(file);
113
117
 
114
- // CRITICAL: Send custom HMR message to client via WebSocket
115
- // Server components aren't in the client bundle, so vite:beforeUpdate doesn't fire
116
- // We need to manually notify the client to refetch the RSC stream
117
- server.ws.send({
118
- type: 'custom',
119
- event: 'vite-plugin-react-server:server-component-update',
120
- data: {
121
- file: normalizedFile,
122
- path: file,
123
- },
124
- });
118
+ // NOTE: The WS event to notify the client is sent by the hotUpdate hook
119
+ // in plugin.server.ts — don't duplicate it here.
125
120
 
126
121
  // CRITICAL: Node.js caches ES modules, so we need to restart the worker
127
122
  // to clear the module cache. Debounce restarts to prevent recursion.
@@ -150,7 +145,7 @@ export const vitePluginReactDevServer: VitePluginFn = function _vitePluginReactS
150
145
  }, 500); // 500ms debounce
151
146
  } else {
152
147
  // No restart function available yet - worker hasn't been created
153
- // This is expected if handleHotUpdate fires before the first request
148
+ // This is expected if hotUpdate fires before the first request
154
149
  server.config.logger.warn(`[vite-plugin-react-server] Restart function not available yet - worker will be created on next request`);
155
150
  setTimeout(() => {
156
151
  isProcessingHmr = false;
@@ -164,8 +159,7 @@ export const vitePluginReactDevServer: VitePluginFn = function _vitePluginReactS
164
159
  server.config.logger.warn(`[vite-plugin-react-server] Server file changed but HMR handler not available yet: ${file}`);
165
160
  }
166
161
 
167
- // Return undefined to allow other plugins to handle the update
168
- return undefined;
162
+ // Don't suppress plugin.server.ts hotUpdate handles page reload prevention
169
163
  },
170
164
  };
171
165
  };
@@ -44,6 +44,8 @@ export const vitePluginReactDevServer = function _vitePluginReactServerDevServer
44
44
  if (envName === 'client') {
45
45
  // Check if it's a client component — let Fast Refresh handle it
46
46
  const isClient = (file.endsWith('.tsx') || file.endsWith('.ts') || file.endsWith('.jsx') || file.endsWith('.js')) && (() => {
47
+ // Check filename pattern (.client.tsx, .client.ts, etc.) — matches isClientComponentByName
48
+ if (/\.client\.(js|ts|jsx|tsx)$/.test(file)) return true;
47
49
  try {
48
50
  const head = readFileSync(file, 'utf-8').slice(0, 200);
49
51
  return /^\s*["']use client["']/.test(head.split('\n')[0]);
@@ -77,55 +79,6 @@ export const vitePluginReactDevServer = function _vitePluginReactServerDevServer
77
79
  }
78
80
  return [];
79
81
  },
80
- handleHotUpdate({ file, server }: { file: string; server: ViteDevServer }) {
81
- const moduleBase = userOptions.moduleBase || "src";
82
- const projectRoot = userOptions.projectRoot || server.config.root;
83
- const normalizedFile = file.replace(projectRoot, '').replace(/^\/+/, '');
84
- const isSourceFile = normalizedFile.startsWith(moduleBase + '/') &&
85
- (file.endsWith('.tsx') || file.endsWith('.ts') || file.endsWith('.jsx') || file.endsWith('.js'));
86
-
87
- // Skip client components — let @vitejs/plugin-react handle them
88
- // with Fast Refresh (preserves component-level state).
89
- const isClientFile = isSourceFile && (() => {
90
- try {
91
- const head = readFileSync(file, 'utf-8').slice(0, 200);
92
- return /^\s*["']use client["']/.test(head.split('\n')[0]);
93
- } catch { return false; }
94
- })();
95
-
96
- if (isSourceFile && !isClientFile) {
97
- server.config.logger.info(`[vite-plugin-react-server] File changed (RSC refetch): ${normalizedFile}`);
98
-
99
- // Send custom HMR event so client can refetch RSC stream
100
- server.ws.send({
101
- type: 'custom',
102
- event: 'vite-plugin-react-server:server-component-update',
103
- data: {
104
- file: normalizedFile,
105
- path: file,
106
- },
107
- });
108
-
109
- // Invalidate the server module so next request gets fresh content
110
- const mod = server.environments['server']?.moduleGraph?.getModulesByFile(file);
111
- if (mod) {
112
- for (const m of mod) {
113
- server.environments['server']?.moduleGraph?.invalidateModule(m);
114
- }
115
- }
116
-
117
- // Return empty array to prevent Vite's default full-page reload
118
- // The client will refetch the RSC stream via the custom event
119
- return [];
120
- }
121
-
122
- if (isClientFile) {
123
- // Client components are handled by @vitejs/plugin-react (Fast Refresh)
124
- // or Vite's client-side HMR. Return empty to prevent the server
125
- // environment from triggering a full page reload.
126
- return [];
127
- }
128
- },
129
82
  };
130
83
 
131
84
  const serverPlugin = {
@@ -1,6 +1,5 @@
1
1
  export { toError } from "./toError.js";
2
2
  export { logError } from "./logError.js";
3
- export { enhanceError, createContextualError } from "./enhanceError.js";
4
3
  export { handleError } from "./handleError.js";
5
4
  export { shouldPanic, PANIC_SYMBOL, isPanic } from "./shouldPanic.js";
6
- export { assertPanic } from "./assertPanic.js";
5
+ export { shouldCausePanic, handlePanicThreshold, isPanicError } from "./panicThresholdHandler.js";
@@ -4,12 +4,13 @@ import type {
4
4
  } from "./types.js";
5
5
 
6
6
  let isGlobalHandlerSetup = false;
7
+ let uncaughtExceptionHandler: ((error: Error) => void) | null = null;
8
+ let unhandledRejectionHandler: ((reason: unknown, promise: Promise<unknown>) => void) | null = null;
7
9
 
8
10
  export const setupGlobalErrorHandler: SetupGlobalErrorHandlerFn =
9
11
  function _setupGlobalErrorHandler(options) {
10
12
  const { panicThreshold, logger, verbose = false } = options;
11
13
 
12
- // Set up global error handling for all panic threshold levels
13
14
  if (isGlobalHandlerSetup) {
14
15
  return;
15
16
  }
@@ -20,39 +21,32 @@ export const setupGlobalErrorHandler: SetupGlobalErrorHandlerFn =
20
21
  );
21
22
  }
22
23
 
23
- // Set up our error handlers
24
- process.on("uncaughtException", (error: Error) => {
24
+ uncaughtExceptionHandler = (error: Error) => {
25
25
  if (verbose) {
26
26
  logger.info(
27
27
  `Global error handler caught uncaught exception: ${error.message}`
28
28
  );
29
29
  }
30
30
 
31
- // Handle the error gracefully based on panic threshold
32
31
  logger.warn(
33
32
  `Uncaught exception handled by panic threshold (${panicThreshold}): ${error.message}`
34
33
  );
34
+ };
35
35
 
36
- // Don't call process.exit - let the error be handled gracefully
37
- });
38
-
39
- process.on(
40
- "unhandledRejection",
41
- (reason: unknown, _promise: Promise<unknown>) => {
42
- if (verbose) {
43
- logger.info(
44
- `Global error handler caught unhandled rejection: ${reason}`
45
- );
46
- }
47
-
48
- // Handle the rejection gracefully based on panic threshold
49
- logger.warn(
50
- `Unhandled rejection handled by panic threshold (${panicThreshold}): ${reason}`
36
+ unhandledRejectionHandler = (reason: unknown, _promise: Promise<unknown>) => {
37
+ if (verbose) {
38
+ logger.info(
39
+ `Global error handler caught unhandled rejection: ${reason}`
51
40
  );
52
-
53
- // Don't call process.exit - let the rejection be handled gracefully
54
41
  }
55
- );
42
+
43
+ logger.warn(
44
+ `Unhandled rejection handled by panic threshold (${panicThreshold}): ${reason}`
45
+ );
46
+ };
47
+
48
+ process.on("uncaughtException", uncaughtExceptionHandler);
49
+ process.on("unhandledRejection", unhandledRejectionHandler);
56
50
 
57
51
  isGlobalHandlerSetup = true;
58
52
  };
@@ -63,9 +57,14 @@ export const cleanupGlobalErrorHandler: CleanupGlobalErrorHandlerFn =
63
57
  return;
64
58
  }
65
59
 
66
- // Remove our handlers
67
- process.removeAllListeners("uncaughtException");
68
- process.removeAllListeners("unhandledRejection");
60
+ if (uncaughtExceptionHandler) {
61
+ process.removeListener("uncaughtException", uncaughtExceptionHandler);
62
+ uncaughtExceptionHandler = null;
63
+ }
64
+ if (unhandledRejectionHandler) {
65
+ process.removeListener("unhandledRejection", unhandledRejectionHandler);
66
+ unhandledRejectionHandler = null;
67
+ }
69
68
 
70
69
  isGlobalHandlerSetup = false;
71
70
  };
@@ -46,32 +46,3 @@ export function createReactElement(
46
46
  /**
47
47
  * Pure function for storing headless stream data
48
48
  */
49
- export function storeHeadlessStreamData(
50
- context: RscRenderContext,
51
- finalOptions: CreateHandlerOptions
52
- ): void {
53
- // Determine if this is a headless stream by checking if htmlPath is empty
54
- const isHeadless = finalOptions.htmlPath === "";
55
-
56
- if (
57
- context.headlessStreamElements &&
58
- isHeadless &&
59
- !context.headlessStreamErrors?.has(context.route)
60
- ) {
61
- // Store the rendered elements for reuse
62
- const element = createElementWithReact(React, {
63
- ...finalOptions,
64
- Html: React.Fragment,
65
- as: React.Fragment,
66
- });
67
- context.headlessStreamElements.set(context.id, {
68
- elements: element,
69
- errored: false,
70
- });
71
- }
72
- }
73
-
74
- export const createRscRenderHelpers = {
75
- createReactElement,
76
- storeHeadlessStreamData,
77
- };
@@ -51,72 +51,3 @@ export function hasHeadlessStreamError(
51
51
  ): boolean {
52
52
  return state.errors.has(route);
53
53
  }
54
-
55
- /**
56
- * Gets the headless stream error for a route.
57
- *
58
- * @param state - The headless stream state
59
- * @param route - The route to get the error for
60
- * @returns The error if it exists, undefined otherwise
61
- */
62
- export function getHeadlessStreamError(
63
- state: HeadlessStreamState,
64
- route: string
65
- ): Error | undefined {
66
- return state.errors.get(route);
67
- }
68
-
69
- /**
70
- * Stores headless stream elements for reuse.
71
- *
72
- * @param state - The headless stream state
73
- * @param streamId - The stream ID
74
- * @param data - The stream data to store
75
- */
76
- export function storeHeadlessStreamElements(
77
- state: HeadlessStreamState,
78
- streamId: string,
79
- data: HeadlessStreamData
80
- ): void {
81
- state.elements.set(streamId, data);
82
- }
83
-
84
- /**
85
- * Gets headless stream elements for reuse.
86
- *
87
- * @param state - The headless stream state
88
- * @param streamId - The stream ID
89
- * @returns The stream data if it exists, undefined otherwise
90
- */
91
- export function getHeadlessStreamElements(
92
- state: HeadlessStreamState,
93
- streamId: string
94
- ): HeadlessStreamData | undefined {
95
- return state.elements.get(streamId);
96
- }
97
-
98
- /**
99
- * Cleans up headless stream elements after use.
100
- *
101
- * @param state - The headless stream state
102
- * @param streamId - The stream ID to clean up
103
- */
104
- export function cleanupHeadlessStreamElements(
105
- state: HeadlessStreamState,
106
- streamId: string
107
- ): void {
108
- state.elements.delete(streamId);
109
- }
110
-
111
- /**
112
- * Clears all headless stream errors for a route.
113
- *
114
- * @param state - The headless stream state
115
- * @param route - The route to clear errors for
116
- */
117
- export function clearHeadlessStreamErrors(
118
- state: HeadlessStreamState,
119
- route: string
120
- ): void {
121
- state.errors.delete(route);
122
- }
@@ -35,11 +35,8 @@ export * from "./getBundleManifest.js";
35
35
 
36
36
  // Module handling
37
37
  export * from "./moduleRefs.js";
38
- export * from "./moduleResolver.js";
39
38
 
40
39
  // Utility functions
41
- export * from "./stashReturnValue.js";
42
- export * from "./workerManager.js";
43
40
 
44
41
  // Unified render helpers
45
42
  export * from "./validateRscRenderMessage.js";
@@ -76,6 +76,8 @@ export function requestInfo(
76
76
  const hasJsonHeader = req.headers["accept"]?.includes("application/json");
77
77
  const hasHtmlHeader = req.headers.accept?.includes("text/html");
78
78
  const hasRscHeader = req.headers.accept?.includes("text/x-component");
79
+ // Support ?_rsc query param (e.g. /?_rsc for browser debugging) as alternative to Accept header (useful for browser debugging)
80
+ const hasRscQueryParam = /[?&]_rsc\b/.test(req.url || "");
79
81
  const hasCssHeader = req.headers.accept?.includes("text/css");
80
82
  const isFolder = !ext;
81
83
  const isFormContentType =
@@ -112,15 +114,15 @@ export function requestInfo(
112
114
  // Form action detection
113
115
 
114
116
  const isHtmlRequest =
115
- isHtml ||
117
+ !hasRscQueryParam && (isHtml ||
116
118
  hasHtmlHeader ||
117
119
  (isFolder &&
118
120
  !hasRscHeader &&
119
121
  !isRsc &&
120
122
  !isJsRequest &&
121
- !isFormActionRequest);
123
+ !isFormActionRequest));
122
124
  const isRscRequest =
123
- !isJsRequest && !isHtmlRequest && (isRsc || hasRscHeader);
125
+ hasRscQueryParam || (!isJsRequest && !isHtmlRequest && (isRsc || hasRscHeader));
124
126
  const isCssRequest =
125
127
  !isHtmlRequest &&
126
128
  !isRscRequest &&
@@ -178,7 +180,7 @@ export function requestInfo(
178
180
  if (mimeType) {
179
181
  contentType = mimeType + "; charset=utf-8";
180
182
  } else {
181
- contentType = "application/octet-stream; charset=utf-8";
183
+ contentType = "application/octet-stream";
182
184
  }
183
185
  }
184
186
 
@@ -7,8 +7,9 @@ export function requestToRoute(
7
7
  "moduleBasePath" | "moduleBaseURL" | "build"
8
8
  >
9
9
  ) {
10
- let route = req.url
11
- ?.replace(
10
+ // Strip query string before route matching
11
+ let route = req.url?.split("?")[0]
12
+ .replace(
12
13
  handlerOptions.moduleBaseURL + handlerOptions.build.rscOutputPath,
13
14
  ""
14
15
  )
@@ -1,4 +1,4 @@
1
- import type { Worker, MessagePort } from "node:worker_threads";
1
+ import type { Worker } from "node:worker_threads";
2
2
 
3
3
  /**
4
4
  * Centralized worker cleanup utilities
@@ -45,41 +45,4 @@ export function cleanupWorker(
45
45
  }
46
46
  }
47
47
 
48
- /**
49
- * Safely cleans up MessagePorts with consistent error handling
50
- */
51
- export function cleanupMessagePorts(
52
- ports: (MessagePort | null | undefined)[],
53
- options: { logErrors?: boolean } = {}
54
- ): void {
55
- const { logErrors = false } = options;
56
-
57
- for (const port of ports) {
58
- if (!port) continue;
59
-
60
- try {
61
- // Remove onmessage handlers (property assignment cleanup)
62
- (port as any).onmessage = null;
63
- // Close the port
64
- port.close();
65
- } catch (error) {
66
- if (logErrors) {
67
- console.warn('[workerCleanup] Error during MessagePort cleanup:', error);
68
- }
69
- // Always ignore cleanup errors
70
- }
71
- }
72
- }
73
48
 
74
- /**
75
- * Comprehensive cleanup for worker + MessagePorts combination
76
- * This is the most common cleanup pattern in the codebase
77
- */
78
- export function cleanupWorkerAndPorts(
79
- worker: Worker | null | undefined,
80
- ports: (MessagePort | null | undefined)[],
81
- options: WorkerCleanupOptions = {}
82
- ): void {
83
- cleanupMessagePorts(ports, { logErrors: options.logErrors });
84
- cleanupWorker(worker, options);
85
- }
@@ -1,6 +1,5 @@
1
1
  export * from "./addLocalExportedNames.js";
2
2
  export * from "./getExports.js";
3
- export * from "./collectExportsFromModule.js";
4
3
  export * from "./analyzeDirectives.js";
5
4
  export * from "./analyzeModule.js";
6
5
  export * from "./collectExports.js";
@@ -2,4 +2,4 @@
2
2
 
3
3
  This directory contains the core transformation plugin for React Server Components (RSC) in Vite.
4
4
 
5
- 📖 **For full documentation, see [`../../docs/transformer-plugin.md`](../../docs/transformer-plugin.md)**
5
+ 📖 **For full documentation, see [`docs/maintenance/transformer-plugin.md`](../../docs/maintenance/transformer-plugin.md)**
@@ -2,4 +2,4 @@
2
2
 
3
3
  This directory contains the HTML worker implementation for the Vite React Server Plugin.
4
4
 
5
- 📖 **For full documentation, see [`../../docs/html-worker.md`](../../docs/html-worker.md)**
5
+ 📖 **For full documentation, see [`docs/maintenance/rsc-worker.md`](../../../docs/maintenance/rsc-worker.md)**
@@ -2,4 +2,4 @@
2
2
 
3
3
  This directory contains the RSC worker implementation for the Vite React Server Plugin.
4
4
 
5
- 📖 **For full documentation, see [`../../docs/rsc-worker.md`](../../docs/rsc-worker.md)**
5
+ 📖 **For full documentation, see [`docs/maintenance/rsc-worker.md`](../../../docs/maintenance/rsc-worker.md)**
@@ -1,2 +0,0 @@
1
- export declare function assertPanic<T>(error: T): asserts error is NonNullable<T>;
2
- //# sourceMappingURL=assertPanic.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"assertPanic.d.ts","sourceRoot":"","sources":["../../../plugin/error/assertPanic.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAIjC"}
@@ -1,15 +0,0 @@
1
- /**
2
- * vite-plugin-react-server
3
- * Copyright (c) Nico Brinkkemper
4
- * MIT License
5
- */
6
- import { isPanic } from './shouldPanic.js';
7
-
8
- function assertPanic(error) {
9
- if (isPanic(error)) {
10
- throw error;
11
- }
12
- }
13
-
14
- export { assertPanic };
15
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXJ0UGFuaWMuanMiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3BsdWdpbi9lcnJvci9hc3NlcnRQYW5pYy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBpc1BhbmljIH0gZnJvbSBcIi4vc2hvdWxkUGFuaWMuanNcIjtcblxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydFBhbmljPFQ+KFxuICBlcnJvcjogVFxuKTogYXNzZXJ0cyBlcnJvciBpcyBOb25OdWxsYWJsZTxUPiB7XG4gIGlmIChpc1BhbmljKGVycm9yKSkge1xuICAgIHRocm93IGVycm9yO1xuICB9XG59XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7OztBQUVPLFNBQVMsWUFDZCxLQUNpQyxFQUFBO0FBQ2pDLEVBQUksSUFBQSxPQUFBLENBQVEsS0FBSyxDQUFHLEVBQUE7QUFDbEIsSUFBTSxNQUFBLEtBQUE7QUFBQTtBQUVWOzs7OyJ9
@@ -1,13 +0,0 @@
1
- import type { DirectiveMatch } from "../loader/directives/types.js";
2
- export declare const DIRECTIVE_ERRORS: {
3
- readonly FILE_LEVEL: {
4
- readonly NOT_AT_TOP: "'use {directive}' directive must be at the top of the file, before any other statements.";
5
- readonly MIXED_DIRECTIVES: "Cannot use both 'use client' and 'use server' directives in the same file";
6
- };
7
- readonly FUNCTION_LEVEL: {
8
- readonly CLIENT_NOT_ALLOWED: "Directive 'use client' is not allowed at function level. Only 'use server' is allowed at the start of async functions.";
9
- readonly SERVER_NOT_ASYNC: "'use server' directive is only allowed at the start of async functions";
10
- };
11
- };
12
- export declare function isFileLevelWarning(warning: DirectiveMatch): boolean;
13
- //# sourceMappingURL=directiveError.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"directiveError.d.ts","sourceRoot":"","sources":["../../../plugin/error/directiveError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,eAAO,MAAM,gBAAgB;;;;;;;;;CAanB,CAAC;AAGX,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CASnE"}
@@ -1,21 +0,0 @@
1
- // Error types for better maintainability
2
- export const DIRECTIVE_ERRORS = {
3
- FILE_LEVEL: {
4
- NOT_AT_TOP: "'use {directive}' directive must be at the top of the file, before any other statements.",
5
- MIXED_DIRECTIVES: "Cannot use both 'use client' and 'use server' directives in the same file",
6
- },
7
- FUNCTION_LEVEL: {
8
- CLIENT_NOT_ALLOWED: "Directive 'use client' is not allowed at function level. Only 'use server' is allowed at the start of async functions.",
9
- SERVER_NOT_ASYNC: "'use server' directive is only allowed at the start of async functions",
10
- },
11
- };
12
- // Helper to check if a warning is about file-level issues
13
- export function isFileLevelWarning(warning) {
14
- if (!warning.message)
15
- return false;
16
- return (warning.message ===
17
- DIRECTIVE_ERRORS.FILE_LEVEL.NOT_AT_TOP.replace("{directive}", "client") ||
18
- warning.message ===
19
- DIRECTIVE_ERRORS.FILE_LEVEL.NOT_AT_TOP.replace("{directive}", "server") ||
20
- warning.message === DIRECTIVE_ERRORS.FILE_LEVEL.MIXED_DIRECTIVES);
21
- }
@@ -1,14 +0,0 @@
1
- /**
2
- * Creates a new error with context from any input.
3
- * Always creates a fresh stack trace and sets the original as the cause.
4
- * This is the preferred way to wrap errors with context.
5
- */
6
- export declare function enhanceError(originalError: Error | string | {
7
- message: string;
8
- }, captureStackTraceFunction: Function, context?: string): Error;
9
- /**
10
- * Creates a new error with context and captured stack trace
11
- * Useful for creating contextual errors at specific points in the code
12
- */
13
- export declare function createContextualError(message: string, context?: string, captureStackTraceFunction?: Function): Error;
14
- //# sourceMappingURL=enhanceError.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"enhanceError.d.ts","sourceRoot":"","sources":["../../../plugin/error/enhanceError.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,KAAK,GAAG,MAAM,GAAG;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,EACjD,yBAAyB,EAAE,QAAQ,EACnC,OAAO,GAAE,MAAuC,GAC/C,KAAK,CASP;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAW,EACpB,yBAAyB,CAAC,EAAE,QAAQ,GACnC,KAAK,CAYP"}