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.
- package/README.md +4 -4
- package/dist/package.json +1 -1
- package/dist/plugin/dev-server/configureReactServer.client.js +1 -1
- package/dist/plugin/dev-server/configureReactServer.server.d.ts.map +1 -1
- package/dist/plugin/dev-server/configureReactServer.server.js +5 -8
- package/dist/plugin/dev-server/plugin.client.d.ts.map +1 -1
- package/dist/plugin/dev-server/plugin.client.js +7 -14
- package/dist/plugin/dev-server/plugin.server.d.ts.map +1 -1
- package/dist/plugin/dev-server/plugin.server.js +2 -36
- package/dist/plugin/error/index.d.ts +1 -2
- package/dist/plugin/error/index.d.ts.map +1 -1
- package/dist/plugin/error/index.js +2 -3
- package/dist/plugin/error/panicThresholdHandler.js +14 -3
- package/dist/plugin/error/setupGlobalErrorHandler.d.ts.map +1 -1
- package/dist/plugin/error/setupGlobalErrorHandler.js +23 -16
- package/dist/plugin/helpers/createRscRenderHelpers.d.ts +0 -5
- package/dist/plugin/helpers/createRscRenderHelpers.d.ts.map +1 -1
- package/dist/plugin/helpers/createRscRenderHelpers.js +0 -22
- package/dist/plugin/helpers/headlessStreamState.d.ts +0 -38
- package/dist/plugin/helpers/headlessStreamState.d.ts.map +1 -1
- package/dist/plugin/helpers/headlessStreamState.js +0 -48
- package/dist/plugin/helpers/index.d.ts +0 -3
- package/dist/plugin/helpers/index.d.ts.map +1 -1
- package/dist/plugin/helpers/index.js +1 -4
- package/dist/plugin/helpers/requestInfo.d.ts.map +1 -1
- package/dist/plugin/helpers/requestInfo.js +5 -4
- package/dist/plugin/helpers/requestToRoute.d.ts.map +1 -1
- package/dist/plugin/helpers/requestToRoute.js +2 -2
- package/dist/plugin/helpers/workerCleanup.d.ts +1 -12
- package/dist/plugin/helpers/workerCleanup.d.ts.map +1 -1
- package/dist/plugin/helpers/workerCleanup.js +1 -1
- package/dist/plugin/loader/directives/index.d.ts +0 -1
- package/dist/plugin/loader/directives/index.d.ts.map +1 -1
- package/dist/plugin/loader/directives/index.js +1 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/plugin/dev-server/configureReactServer.client.ts +1 -1
- package/plugin/dev-server/configureReactServer.server.ts +7 -10
- package/plugin/dev-server/plugin.client.ts +14 -20
- package/plugin/dev-server/plugin.server.ts +2 -49
- package/plugin/error/index.ts +1 -2
- package/plugin/error/setupGlobalErrorHandler.ts +24 -25
- package/plugin/helpers/createRscRenderHelpers.ts +0 -29
- package/plugin/helpers/headlessStreamState.ts +0 -69
- package/plugin/helpers/index.ts +0 -3
- package/plugin/helpers/requestInfo.ts +6 -4
- package/plugin/helpers/requestToRoute.ts +3 -2
- package/plugin/helpers/workerCleanup.ts +1 -38
- package/plugin/loader/directives/index.ts +0 -1
- package/plugin/transformer/README.md +1 -1
- package/plugin/worker/html/README.md +1 -1
- package/plugin/worker/rsc/README.md +1 -1
- package/dist/plugin/error/assertPanic.d.ts +0 -2
- package/dist/plugin/error/assertPanic.d.ts.map +0 -1
- package/dist/plugin/error/assertPanic.js +0 -15
- package/dist/plugin/error/directiveError.d.ts +0 -13
- package/dist/plugin/error/directiveError.d.ts.map +0 -1
- package/dist/plugin/error/directiveError.js +0 -21
- package/dist/plugin/error/enhanceError.d.ts +0 -14
- package/dist/plugin/error/enhanceError.d.ts.map +0 -1
- package/dist/plugin/error/enhanceError.js +0 -24
- package/dist/plugin/helpers/createSafePageComponent.d.ts +0 -36
- package/dist/plugin/helpers/createSafePageComponent.d.ts.map +0 -1
- package/dist/plugin/helpers/createSafePageComponent.js +0 -50
- package/dist/plugin/helpers/moduleResolver.d.ts +0 -25
- package/dist/plugin/helpers/moduleResolver.d.ts.map +0 -1
- package/dist/plugin/helpers/moduleResolver.js +0 -64
- package/dist/plugin/helpers/stashReturnValue.d.ts +0 -3
- package/dist/plugin/helpers/stashReturnValue.d.ts.map +0 -1
- package/dist/plugin/helpers/stashReturnValue.js +0 -23
- package/dist/plugin/helpers/workerManager.d.ts +0 -5
- package/dist/plugin/helpers/workerManager.d.ts.map +0 -1
- package/dist/plugin/helpers/workerManager.js +0 -18
- package/dist/plugin/loader/directives/collectExportsFromModule.d.ts +0 -6
- package/dist/plugin/loader/directives/collectExportsFromModule.d.ts.map +0 -1
- package/dist/plugin/loader/directives/collectExportsFromModule.js +0 -24
- package/plugin/error/assertPanic.ts +0 -9
- package/plugin/error/directiveError.ts +0 -29
- package/plugin/error/enhanceError.ts +0 -41
- package/plugin/helpers/createSafePageComponent.ts +0 -64
- package/plugin/helpers/moduleResolver.ts +0 -91
- package/plugin/helpers/stashReturnValue.ts +0 -19
- package/plugin/helpers/workerManager.ts +0 -16
- package/plugin/loader/directives/collectExportsFromModule.ts +0 -25
package/package.json
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
115
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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 = {
|
package/plugin/error/index.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
}
|
package/plugin/helpers/index.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
11
|
-
|
|
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
|
|
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
|
-
}
|
|
@@ -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 [
|
|
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 [
|
|
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 [
|
|
5
|
+
📖 **For full documentation, see [`docs/maintenance/rsc-worker.md`](../../../docs/maintenance/rsc-worker.md)**
|
|
@@ -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"}
|