@rangojs/router 0.11.0 → 0.12.1
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/dist/testing/vitest.js +23 -3
- package/dist/types/deps/rsc-client.d.ts +1 -0
- package/dist/types/deps/rsc.d.ts +1 -1
- package/dist/types/deps/ssr.d.ts +1 -1
- package/dist/types/rsc/types.d.ts +9 -9
- package/dist/types/ssr/index.d.ts +27 -4
- package/dist/types/testing/flight.d.ts +4 -3
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +2 -0
- package/dist/types/testing/vitest.d.ts +2 -1
- package/dist/types/vite/plugins/vercel-output.d.ts +22 -3
- package/dist/types/vite/plugins/virtual-entries.d.ts +3 -2
- package/dist/vite/index.js +148 -72
- package/package.json +8 -3
- package/skills/testing/setup.md +7 -7
- package/src/cache/cache-runtime.ts +1 -1
- package/src/cache/segment-codec.ts +2 -2
- package/src/deps/rsc-client.ts +8 -0
- package/src/deps/rsc.ts +4 -2
- package/src/deps/ssr.ts +1 -0
- package/src/rsc/handler.ts +3 -3
- package/src/rsc/types.ts +9 -9
- package/src/ssr/index.tsx +132 -51
- package/src/testing/flight.ts +4 -3
- package/src/testing/vitest-stubs/plugin-rsc.ts +13 -5
- package/src/testing/vitest.ts +43 -6
- package/src/vite/plugins/use-cache-transform.ts +65 -1
- package/src/vite/plugins/vercel-output.ts +146 -88
- package/src/vite/plugins/virtual-entries.ts +14 -11
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import type { Plugin } from "vite";
|
|
20
|
+
import type { ModuleExportMeta } from "@vitejs/plugin-rsc/transforms";
|
|
20
21
|
import path from "node:path";
|
|
21
22
|
import MagicString from "magic-string";
|
|
22
23
|
import { normalizePath, hashId } from "./expose-id-utils.js";
|
|
@@ -89,6 +90,13 @@ export function useCacheTransform(): Plugin {
|
|
|
89
90
|
return;
|
|
90
91
|
}
|
|
91
92
|
|
|
93
|
+
// plugin-rsc 0.5.34 `matchDirective` does `stmt.directive.match(...)`
|
|
94
|
+
// after `"directive" in node`. Vite/oxc parseAst now emits
|
|
95
|
+
// `directive: null` on ordinary ExpressionStatements, so a file that
|
|
96
|
+
// mixes a `"use cache"` function with a sibling handler whose first
|
|
97
|
+
// statement is an expression throws and the wrap is dropped.
|
|
98
|
+
stripNullDirectiveFields(ast);
|
|
99
|
+
|
|
92
100
|
const filePath = normalizePath(path.relative(projectRoot, id));
|
|
93
101
|
const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
|
|
94
102
|
|
|
@@ -101,6 +109,7 @@ export function useCacheTransform(): Plugin {
|
|
|
101
109
|
isBuild,
|
|
102
110
|
isLayoutOrTemplate,
|
|
103
111
|
transformWrapExport,
|
|
112
|
+
hasDirective,
|
|
104
113
|
);
|
|
105
114
|
}
|
|
106
115
|
|
|
@@ -131,6 +140,7 @@ function transformFileLevelUseCache(
|
|
|
131
140
|
isBuild: boolean,
|
|
132
141
|
isLayoutOrTemplate: boolean,
|
|
133
142
|
transformWrapExport: (typeof import("@vitejs/plugin-rsc/transforms"))["transformWrapExport"],
|
|
143
|
+
hasDirective: (typeof import("@vitejs/plugin-rsc/transforms"))["hasDirective"],
|
|
134
144
|
) {
|
|
135
145
|
const unconfirmedExports: string[] = [];
|
|
136
146
|
|
|
@@ -140,8 +150,18 @@ function transformFileLevelUseCache(
|
|
|
140
150
|
return `__rango_registerCachedFunction(${value}, ${JSON.stringify(funcId)}, "default")`;
|
|
141
151
|
},
|
|
142
152
|
rejectNonAsyncFunction: false,
|
|
143
|
-
filter: (name: string, meta:
|
|
153
|
+
filter: (name: string, meta: ModuleExportMeta) => {
|
|
144
154
|
if (name === "default" && isLayoutOrTemplate) return false;
|
|
155
|
+
// plugin-rsc 0.5.34 hoists mixed inline `"use server"` out of a
|
|
156
|
+
// file-level `"use cache"` module as `$$hoist_*` exports and rebinds the
|
|
157
|
+
// original name to `registerServerReference($$hoist_*, ...)`. Both are
|
|
158
|
+
// server references, not cached functions. The directive check covers
|
|
159
|
+
// the pre-hoist shape (this plugin seeing the source first).
|
|
160
|
+
if (name.startsWith("$$hoist_")) return false;
|
|
161
|
+
if (isHoistedServerReferenceRebind(meta.valueNode)) return false;
|
|
162
|
+
if (functionHasUseServerDirective(meta.valueNode, hasDirective)) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
145
165
|
// isFunction is boolean | undefined: true = confirmed function, false =
|
|
146
166
|
// confirmed non-function, undefined = cannot tell statically (e.g. a
|
|
147
167
|
// factory/HOF initializer `const x = makeCached(fn)`). Deliberate policy:
|
|
@@ -250,6 +270,50 @@ function transformFunctionLevelUseCache(
|
|
|
250
270
|
}
|
|
251
271
|
}
|
|
252
272
|
|
|
273
|
+
function stripNullDirectiveFields(node: unknown): void {
|
|
274
|
+
if (!node || typeof node !== "object") return;
|
|
275
|
+
const rec = node as Record<string, unknown>;
|
|
276
|
+
if (rec.type === "ExpressionStatement" && typeof rec.directive !== "string") {
|
|
277
|
+
delete rec.directive;
|
|
278
|
+
}
|
|
279
|
+
for (const value of Object.values(rec)) {
|
|
280
|
+
if (Array.isArray(value)) {
|
|
281
|
+
for (const item of value) stripNullDirectiveFields(item);
|
|
282
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
283
|
+
stripNullDirectiveFields(value);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function isHoistedServerReferenceRebind(
|
|
289
|
+
valueNode: ModuleExportMeta["valueNode"],
|
|
290
|
+
): boolean {
|
|
291
|
+
if (!valueNode || valueNode.type !== "CallExpression") return false;
|
|
292
|
+
const first = valueNode.arguments[0];
|
|
293
|
+
return (
|
|
294
|
+
first !== undefined &&
|
|
295
|
+
first.type === "Identifier" &&
|
|
296
|
+
first.name.startsWith("$$hoist_")
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function functionHasUseServerDirective(
|
|
301
|
+
valueNode: ModuleExportMeta["valueNode"],
|
|
302
|
+
hasDirective: (typeof import("@vitejs/plugin-rsc/transforms"))["hasDirective"],
|
|
303
|
+
): boolean {
|
|
304
|
+
if (!valueNode || !("body" in valueNode)) return false;
|
|
305
|
+
const { body } = valueNode;
|
|
306
|
+
if (!body || Array.isArray(body) || body.type !== "BlockStatement") {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
// plugin-rsc types valueNode with plain estree nodes but hasDirective with
|
|
310
|
+
// its oxc-flavored AST; the flavors differ only in position/extra fields.
|
|
311
|
+
return hasDirective(
|
|
312
|
+
body.body as Parameters<typeof hasDirective>[0],
|
|
313
|
+
"use server",
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
253
317
|
function findFileLevelDirective(
|
|
254
318
|
ast: any,
|
|
255
319
|
): { start: number; end: number } | null {
|
|
@@ -28,9 +28,11 @@
|
|
|
28
28
|
* type:module in scope the deployed (isolated) function loads them as
|
|
29
29
|
* CommonJS and fails on the first `import`.
|
|
30
30
|
*
|
|
31
|
-
* The launcher is bundled with
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* The launcher is bundled with rolldown (Vite 8's bundler — a production
|
|
32
|
+
* dependency of `vite`, resolved through the app's vite install) so a
|
|
33
|
+
* standalone consumer does not need `esbuild`. srvx (the Web->Node streaming
|
|
34
|
+
* bridge, a @rangojs/router dependency) and @vercel/functions (resolved from
|
|
35
|
+
* the app) are inlined; the RSC bundle stays a runtime-relative external.
|
|
34
36
|
*
|
|
35
37
|
* Timing: this runs in the `buildApp` hook (order "post"), which fires once
|
|
36
38
|
* after every environment has built, so dist/{client,rsc,ssr} all exist.
|
|
@@ -52,19 +54,30 @@ import type {
|
|
|
52
54
|
VercelPresetOptions,
|
|
53
55
|
} from "../plugin-types.js";
|
|
54
56
|
|
|
55
|
-
// Minimal structural types for the
|
|
56
|
-
// the app so @rangojs/router does not depend on
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
): void;
|
|
57
|
+
// Minimal structural types for the rolldown API we use. Resolved dynamically
|
|
58
|
+
// from the app's vite install so @rangojs/router does not depend on rolldown's
|
|
59
|
+
// type package (same stance the previous esbuild path took).
|
|
60
|
+
interface RolldownResolveResult {
|
|
61
|
+
id: string;
|
|
62
|
+
external?: boolean;
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
interface RolldownBundle {
|
|
65
|
+
write: (output: {
|
|
66
|
+
file: string;
|
|
67
|
+
format: string;
|
|
68
|
+
exports: string;
|
|
69
|
+
codeSplitting?: boolean;
|
|
70
|
+
}) => Promise<unknown>;
|
|
71
|
+
close: () => Promise<void>;
|
|
67
72
|
}
|
|
73
|
+
interface RolldownModule {
|
|
74
|
+
rolldown?: (options: Record<string, unknown>) => Promise<RolldownBundle>;
|
|
75
|
+
default?: {
|
|
76
|
+
rolldown?: (options: Record<string, unknown>) => Promise<RolldownBundle>;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const VIRTUAL_LAUNCHER_ID = "\0rango-vercel-launcher";
|
|
68
81
|
|
|
69
82
|
const LAUNCHER_SOURCE = `import { toNodeHandler } from "srvx/node";
|
|
70
83
|
import { waitUntil } from "@vercel/functions";
|
|
@@ -86,6 +99,122 @@ const fetchHandler = (request) =>
|
|
|
86
99
|
export default toNodeHandler(fetchHandler);
|
|
87
100
|
`;
|
|
88
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Resolve rolldown through the app's vite install (rolldown is a production
|
|
104
|
+
* dependency of Vite 8, unlike esbuild which is only an optional peer). Fall
|
|
105
|
+
* back to the app root, then the plugin's own vite, so a hoisted or
|
|
106
|
+
* workspace copy still works. Issue #785: the previous "esbuild ships with
|
|
107
|
+
* Vite" path broke standalone consumers on Vite 8.
|
|
108
|
+
*/
|
|
109
|
+
export function resolveRolldownPath(root: string): string {
|
|
110
|
+
const appRequire = createRequire(join(root, "package.json"));
|
|
111
|
+
const rangoRequire = createRequire(import.meta.url);
|
|
112
|
+
const attempts: Array<() => string> = [
|
|
113
|
+
() => createRequire(appRequire.resolve("vite")).resolve("rolldown"),
|
|
114
|
+
() => appRequire.resolve("rolldown"),
|
|
115
|
+
() => createRequire(rangoRequire.resolve("vite")).resolve("rolldown"),
|
|
116
|
+
() => rangoRequire.resolve("rolldown"),
|
|
117
|
+
];
|
|
118
|
+
for (const attempt of attempts) {
|
|
119
|
+
try {
|
|
120
|
+
return attempt();
|
|
121
|
+
} catch {
|
|
122
|
+
// Intentionally empty: try the next resolver.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
throw new Error(
|
|
126
|
+
'[rango] preset "vercel" requires "rolldown" to bundle the function launcher. Vite 8 depends on it; reinstall dependencies.',
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Bundle the Node launcher into `funcDir/index.mjs`: srvx + @vercel/functions
|
|
132
|
+
* inlined, `./rsc/index.js` left as a runtime-relative external.
|
|
133
|
+
*/
|
|
134
|
+
export async function bundleVercelLauncher(opts: {
|
|
135
|
+
root: string;
|
|
136
|
+
funcDir: string;
|
|
137
|
+
srvxNodePath: string;
|
|
138
|
+
}): Promise<void> {
|
|
139
|
+
const { root, funcDir, srvxNodePath } = opts;
|
|
140
|
+
const appRequire = createRequire(join(root, "package.json"));
|
|
141
|
+
let vercelFunctionsPath: string;
|
|
142
|
+
try {
|
|
143
|
+
vercelFunctionsPath = appRequire.resolve("@vercel/functions");
|
|
144
|
+
} catch {
|
|
145
|
+
throw new Error(
|
|
146
|
+
'[rango] preset "vercel": could not resolve "@vercel/functions". Add it to your app dependencies (it also backs VercelCacheStore).',
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
let rolldownModule: RolldownModule;
|
|
150
|
+
try {
|
|
151
|
+
rolldownModule = (await import(
|
|
152
|
+
pathToFileURL(resolveRolldownPath(root)).href
|
|
153
|
+
)) as RolldownModule;
|
|
154
|
+
} catch {
|
|
155
|
+
throw new Error(
|
|
156
|
+
'[rango] preset "vercel" requires "rolldown" to bundle the function launcher. Vite 8 depends on it; reinstall dependencies.',
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
const rolldownFn =
|
|
160
|
+
rolldownModule.rolldown ?? rolldownModule.default?.rolldown;
|
|
161
|
+
if (typeof rolldownFn !== "function") {
|
|
162
|
+
throw new Error('[rango] preset "vercel": could not load rolldown().');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let bundle: RolldownBundle | undefined;
|
|
166
|
+
try {
|
|
167
|
+
bundle = await rolldownFn({
|
|
168
|
+
input: VIRTUAL_LAUNCHER_ID,
|
|
169
|
+
cwd: root,
|
|
170
|
+
platform: "node",
|
|
171
|
+
logLevel: "silent",
|
|
172
|
+
resolve: {
|
|
173
|
+
alias: {
|
|
174
|
+
"srvx/node": srvxNodePath,
|
|
175
|
+
"@vercel/functions": vercelFunctionsPath,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
plugins: [
|
|
179
|
+
{
|
|
180
|
+
name: "rango-vercel-launcher",
|
|
181
|
+
resolveId(id: string): string | RolldownResolveResult | null {
|
|
182
|
+
if (id === VIRTUAL_LAUNCHER_ID) return VIRTUAL_LAUNCHER_ID;
|
|
183
|
+
if (id === "./rsc/index.js") {
|
|
184
|
+
return { id: "./rsc/index.js", external: true };
|
|
185
|
+
}
|
|
186
|
+
return null;
|
|
187
|
+
},
|
|
188
|
+
load(id: string): string | null {
|
|
189
|
+
if (id === VIRTUAL_LAUNCHER_ID) return LAUNCHER_SOURCE;
|
|
190
|
+
return null;
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
});
|
|
195
|
+
await bundle.write({
|
|
196
|
+
file: join(funcDir, "index.mjs"),
|
|
197
|
+
format: "esm",
|
|
198
|
+
exports: "default",
|
|
199
|
+
// @vercel/functions (and srvx) may contain dynamic import(); the
|
|
200
|
+
// launcher must stay a single index.mjs — Vercel's handler field
|
|
201
|
+
// points at that one file.
|
|
202
|
+
codeSplitting: false,
|
|
203
|
+
});
|
|
204
|
+
} catch (error) {
|
|
205
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
206
|
+
if (/@vercel\/functions/.test(message)) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
'[rango] preset "vercel": could not resolve "@vercel/functions". Add it to your app dependencies (it also backs VercelCacheStore).\n' +
|
|
209
|
+
message,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
} finally {
|
|
214
|
+
await bundle?.close();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
89
218
|
/**
|
|
90
219
|
* Reject a non-Node runtime for the vercel preset. The preset only emits a Node
|
|
91
220
|
* serverless function (launcherType "Nodejs", bundled Node APIs, response
|
|
@@ -237,7 +366,8 @@ async function assemble(
|
|
|
237
366
|
|
|
238
367
|
// 3. Bundle the Node launcher. srvx (a @rangojs/router dependency) is aliased
|
|
239
368
|
// to its resolved path; @vercel/functions resolves from the app; the RSC
|
|
240
|
-
// server bundle stays a runtime-relative external.
|
|
369
|
+
// server bundle stays a runtime-relative external. Rolldown (Vite 8's
|
|
370
|
+
// bundler) is resolved through the app's vite install — #785.
|
|
241
371
|
const rangoRequire = createRequire(import.meta.url);
|
|
242
372
|
let srvxNodePath: string;
|
|
243
373
|
try {
|
|
@@ -247,79 +377,7 @@ async function assemble(
|
|
|
247
377
|
'[rango] preset "vercel" requires "srvx" (a dependency of @rangojs/router). Reinstall dependencies.',
|
|
248
378
|
);
|
|
249
379
|
}
|
|
250
|
-
|
|
251
|
-
// esbuild ships with Vite, so we never add it as a @rangojs/router dependency.
|
|
252
|
-
// It is a DIRECT dependency of Vite but only a TRANSITIVE one from the app's
|
|
253
|
-
// view, so under strict pnpm it is NOT resolvable from the app root. Resolve it
|
|
254
|
-
// through Vite's module location (Vite is a direct app dependency, and esbuild
|
|
255
|
-
// is a direct dependency of Vite). Minimal structural types avoid coupling to
|
|
256
|
-
// esbuild's type package at compile time.
|
|
257
|
-
const appRequire = createRequire(join(root, "package.json"));
|
|
258
|
-
const resolveEsbuildPath = (): string => {
|
|
259
|
-
try {
|
|
260
|
-
const viteRequire = createRequire(appRequire.resolve("vite"));
|
|
261
|
-
return viteRequire.resolve("esbuild");
|
|
262
|
-
} catch {
|
|
263
|
-
// Intentionally empty: fall through to the app/rango fallbacks below.
|
|
264
|
-
}
|
|
265
|
-
try {
|
|
266
|
-
return appRequire.resolve("esbuild");
|
|
267
|
-
} catch {
|
|
268
|
-
// Intentionally empty: last resort is @rangojs/router's own resolver.
|
|
269
|
-
}
|
|
270
|
-
return rangoRequire.resolve("esbuild");
|
|
271
|
-
};
|
|
272
|
-
let esbuildModule: EsbuildModule;
|
|
273
|
-
try {
|
|
274
|
-
esbuildModule = (await import(
|
|
275
|
-
pathToFileURL(resolveEsbuildPath()).href
|
|
276
|
-
)) as EsbuildModule;
|
|
277
|
-
} catch {
|
|
278
|
-
throw new Error(
|
|
279
|
-
'[rango] preset "vercel" requires "esbuild" to bundle the function launcher. It ships with Vite; reinstall dependencies (or add esbuild to your app dependencies).',
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
const esbuildBuild = esbuildModule.build ?? esbuildModule.default?.build;
|
|
283
|
-
if (typeof esbuildBuild !== "function") {
|
|
284
|
-
throw new Error('[rango] preset "vercel": could not load esbuild.build.');
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
try {
|
|
288
|
-
await esbuildBuild({
|
|
289
|
-
stdin: {
|
|
290
|
-
contents: LAUNCHER_SOURCE,
|
|
291
|
-
resolveDir: root,
|
|
292
|
-
sourcefile: "func-entry.mjs",
|
|
293
|
-
loader: "js",
|
|
294
|
-
},
|
|
295
|
-
outfile: join(funcDir, "index.mjs"),
|
|
296
|
-
bundle: true,
|
|
297
|
-
format: "esm",
|
|
298
|
-
platform: "node",
|
|
299
|
-
target: "node18",
|
|
300
|
-
alias: { "srvx/node": srvxNodePath },
|
|
301
|
-
plugins: [
|
|
302
|
-
{
|
|
303
|
-
name: "external-rsc-entry",
|
|
304
|
-
setup(b: EsbuildPluginBuild) {
|
|
305
|
-
b.onResolve({ filter: /^\.\/rsc\/index\.js$/ }, () => ({
|
|
306
|
-
path: "./rsc/index.js",
|
|
307
|
-
external: true,
|
|
308
|
-
}));
|
|
309
|
-
},
|
|
310
|
-
},
|
|
311
|
-
],
|
|
312
|
-
});
|
|
313
|
-
} catch (error) {
|
|
314
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
315
|
-
if (/@vercel\/functions/.test(message)) {
|
|
316
|
-
throw new Error(
|
|
317
|
-
'[rango] preset "vercel": could not resolve "@vercel/functions". Add it to your app dependencies (it also backs VercelCacheStore).\n' +
|
|
318
|
-
message,
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
throw error;
|
|
322
|
-
}
|
|
380
|
+
await bundleVercelLauncher({ root, funcDir, srvxNodePath });
|
|
323
381
|
|
|
324
382
|
// 3b. Mark the function as ESM. The rsc/ssr bundles are .js ESM files with no
|
|
325
383
|
// package.json in scope on the deployed function (it is isolated at
|
|
@@ -63,18 +63,20 @@ function emitProgressiveChunkSize(value: number): string {
|
|
|
63
63
|
/**
|
|
64
64
|
* Generate the virtual SSR entry. `headScripts` mirrors the rango() plugin
|
|
65
65
|
* option: "preinit" (default) installs the client-reference preinit hook and
|
|
66
|
-
*
|
|
67
|
-
* "preload" omits the hook and
|
|
66
|
+
* threads `getClientEntryUrl` so Fizz emits `bootstrapModules`;
|
|
67
|
+
* "preload" omits the hook and uses the deprecated inline
|
|
68
|
+
* `loadBootstrapScriptContent` bootstrap.
|
|
68
69
|
*/
|
|
69
70
|
export function getVirtualEntrySSR(
|
|
70
71
|
headScripts: HeadScriptsOption = "preinit",
|
|
71
72
|
progressiveChunkSize?: number,
|
|
72
73
|
): string {
|
|
73
74
|
const preinit = headScripts !== "preload";
|
|
74
|
-
// The preload variant drops
|
|
75
|
-
// here so the template below stays a single
|
|
75
|
+
// The preload variant drops the preinit-only imports/install and swaps the
|
|
76
|
+
// bootstrap dep, all built here so the template below stays a single
|
|
77
|
+
// unconditional shape.
|
|
76
78
|
const depsImportNames = preinit
|
|
77
|
-
? "createFromReadableStream,\n setOnClientReference,"
|
|
79
|
+
? "createFromReadableStream,\n setOnClientReference,\n getClientEntryUrl,"
|
|
78
80
|
: "createFromReadableStream,";
|
|
79
81
|
const ssrImportNames = preinit ? "\n installClientReferencePreinit," : "";
|
|
80
82
|
const install = preinit
|
|
@@ -85,6 +87,10 @@ export function getVirtualEntrySSR(
|
|
|
85
87
|
installClientReferencePreinit(setOnClientReference);
|
|
86
88
|
`
|
|
87
89
|
: "";
|
|
90
|
+
const bootstrapDep = preinit
|
|
91
|
+
? "getClientEntryUrl,"
|
|
92
|
+
: `loadBootstrapScriptContent: () =>
|
|
93
|
+
import.meta.viteRsc.loadBootstrapScriptContent("index"),`;
|
|
88
94
|
const hs = JSON.stringify(headScripts);
|
|
89
95
|
// Emitted into all three handlers: live SSR and shell capture consume it
|
|
90
96
|
// directly; the resume handler receives it for dep-shape uniformity (resume()
|
|
@@ -114,8 +120,7 @@ export const renderHTML = createSSRHandler({
|
|
|
114
120
|
renderToReadableStream,
|
|
115
121
|
injectRSCPayload,
|
|
116
122
|
headScripts: ${hs},${pcs}
|
|
117
|
-
|
|
118
|
-
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
123
|
+
${bootstrapDep}
|
|
119
124
|
});
|
|
120
125
|
|
|
121
126
|
export const captureShellHTML = createShellCaptureHandler({
|
|
@@ -125,8 +130,7 @@ export const captureShellHTML = createShellCaptureHandler({
|
|
|
125
130
|
prerender,
|
|
126
131
|
resume,
|
|
127
132
|
headScripts: ${hs},${pcs}
|
|
128
|
-
|
|
129
|
-
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
133
|
+
${bootstrapDep}
|
|
130
134
|
});
|
|
131
135
|
|
|
132
136
|
export const resumeShellHTML = createShellResumeHandler({
|
|
@@ -136,8 +140,7 @@ export const resumeShellHTML = createShellResumeHandler({
|
|
|
136
140
|
prerender,
|
|
137
141
|
resume,
|
|
138
142
|
headScripts: ${hs},${pcs}
|
|
139
|
-
|
|
140
|
-
import.meta.viteRsc.loadBootstrapScriptContent("index"),
|
|
143
|
+
${bootstrapDep}
|
|
141
144
|
});
|
|
142
145
|
`.trim();
|
|
143
146
|
}
|