@rangojs/router 0.0.0-experimental.20 → 0.0.0-experimental.21
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/vite/index.js +129 -82
- package/package.json +1 -1
- package/src/prerender/store.ts +56 -15
- package/src/vite/discovery/bundle-postprocess.ts +31 -56
- package/src/vite/discovery/prerender-collection.ts +34 -14
- package/src/vite/discovery/state.ts +4 -7
- package/src/vite/router-discovery.ts +4 -0
- package/src/vite/utils/prerender-utils.ts +60 -0
package/dist/vite/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/vite/rango.ts
|
|
2
2
|
import { readFileSync as readFileSync7 } from "node:fs";
|
|
3
|
-
import { resolve as
|
|
3
|
+
import { resolve as resolve9 } from "node:path";
|
|
4
4
|
|
|
5
5
|
// src/vite/plugins/expose-action-id.ts
|
|
6
6
|
import MagicString from "magic-string";
|
|
@@ -1745,7 +1745,7 @@ import { resolve } from "node:path";
|
|
|
1745
1745
|
// package.json
|
|
1746
1746
|
var package_default = {
|
|
1747
1747
|
name: "@rangojs/router",
|
|
1748
|
-
version: "0.0.0-experimental.
|
|
1748
|
+
version: "0.0.0-experimental.21",
|
|
1749
1749
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
1750
1750
|
keywords: [
|
|
1751
1751
|
"react",
|
|
@@ -3024,7 +3024,7 @@ function createCjsToEsmPlugin() {
|
|
|
3024
3024
|
|
|
3025
3025
|
// src/vite/router-discovery.ts
|
|
3026
3026
|
import { createServer as createViteServer } from "vite";
|
|
3027
|
-
import { resolve as
|
|
3027
|
+
import { resolve as resolve8 } from "node:path";
|
|
3028
3028
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3029
3029
|
|
|
3030
3030
|
// src/vite/plugins/virtual-stub-plugin.ts
|
|
@@ -3207,8 +3207,8 @@ function createDiscoveryState(entryPath, opts) {
|
|
|
3207
3207
|
perRouterTrieMap: /* @__PURE__ */ new Map(),
|
|
3208
3208
|
perRouterPrecomputedMap: /* @__PURE__ */ new Map(),
|
|
3209
3209
|
perRouterManifestDataMap: /* @__PURE__ */ new Map(),
|
|
3210
|
-
|
|
3211
|
-
|
|
3210
|
+
prerenderManifestEntries: null,
|
|
3211
|
+
staticManifestEntries: null,
|
|
3212
3212
|
handlerChunkInfo: null,
|
|
3213
3213
|
staticHandlerChunkInfo: null,
|
|
3214
3214
|
rscEntryFileName: null,
|
|
@@ -3308,6 +3308,16 @@ function contextSet(variables, keyOrVar, value) {
|
|
|
3308
3308
|
}
|
|
3309
3309
|
|
|
3310
3310
|
// src/vite/utils/prerender-utils.ts
|
|
3311
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3312
|
+
import {
|
|
3313
|
+
copyFileSync,
|
|
3314
|
+
existsSync as existsSync4,
|
|
3315
|
+
mkdirSync,
|
|
3316
|
+
rmSync,
|
|
3317
|
+
statSync,
|
|
3318
|
+
writeFileSync as writeFileSync2
|
|
3319
|
+
} from "node:fs";
|
|
3320
|
+
import { resolve as resolve5 } from "node:path";
|
|
3311
3321
|
function escapeRegExp2(str) {
|
|
3312
3322
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3313
3323
|
}
|
|
@@ -3386,6 +3396,37 @@ function notifyOnError(registry, error, phase, routeKey, pathname, skipped) {
|
|
|
3386
3396
|
break;
|
|
3387
3397
|
}
|
|
3388
3398
|
}
|
|
3399
|
+
function getStagedAssetDir(projectRoot) {
|
|
3400
|
+
return resolve5(projectRoot, "node_modules/.rangojs-router-build/rsc-assets");
|
|
3401
|
+
}
|
|
3402
|
+
function resetStagedBuildAssets(projectRoot) {
|
|
3403
|
+
rmSync(getStagedAssetDir(projectRoot), { recursive: true, force: true });
|
|
3404
|
+
}
|
|
3405
|
+
function stageBuildAssetModule(projectRoot, prefix, exportValue) {
|
|
3406
|
+
const stagedDir = getStagedAssetDir(projectRoot);
|
|
3407
|
+
mkdirSync(stagedDir, { recursive: true });
|
|
3408
|
+
const contentHash = createHash4("sha256").update(exportValue).digest("hex").slice(0, 8);
|
|
3409
|
+
const fileName = `${prefix}-${contentHash}.js`;
|
|
3410
|
+
const filePath = resolve5(stagedDir, fileName);
|
|
3411
|
+
if (!existsSync4(filePath)) {
|
|
3412
|
+
writeFileSync2(filePath, `export default ${exportValue};
|
|
3413
|
+
`);
|
|
3414
|
+
}
|
|
3415
|
+
return fileName;
|
|
3416
|
+
}
|
|
3417
|
+
function copyStagedBuildAssets(projectRoot, fileNames) {
|
|
3418
|
+
const stagedDir = getStagedAssetDir(projectRoot);
|
|
3419
|
+
const distAssetsDir = resolve5(projectRoot, "dist/rsc/assets");
|
|
3420
|
+
mkdirSync(distAssetsDir, { recursive: true });
|
|
3421
|
+
let totalBytes = 0;
|
|
3422
|
+
for (const fileName of new Set(fileNames)) {
|
|
3423
|
+
const stagedPath = resolve5(stagedDir, fileName);
|
|
3424
|
+
const distPath = resolve5(distAssetsDir, fileName);
|
|
3425
|
+
copyFileSync(stagedPath, distPath);
|
|
3426
|
+
totalBytes += statSync(stagedPath).size;
|
|
3427
|
+
}
|
|
3428
|
+
return totalBytes;
|
|
3429
|
+
}
|
|
3389
3430
|
|
|
3390
3431
|
// src/vite/discovery/prerender-collection.ts
|
|
3391
3432
|
async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
@@ -3487,7 +3528,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3487
3528
|
`[rsc-router] Pre-rendering ${entries.length} URL(s)${concurrencyNote}...`
|
|
3488
3529
|
);
|
|
3489
3530
|
const { hashParams } = await rscEnv.runner.import("@rangojs/router/build");
|
|
3490
|
-
const
|
|
3531
|
+
const manifestEntries = {};
|
|
3491
3532
|
let doneCount = 0;
|
|
3492
3533
|
let skipCount = 0;
|
|
3493
3534
|
const startTotal = performance.now();
|
|
@@ -3517,18 +3558,30 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3517
3558
|
break;
|
|
3518
3559
|
}
|
|
3519
3560
|
const paramHash = hashParams(result.params || {});
|
|
3520
|
-
|
|
3561
|
+
const mainKey = `${result.routeName}/${paramHash}`;
|
|
3562
|
+
const mainValue = JSON.stringify({
|
|
3521
3563
|
segments: result.segments,
|
|
3522
3564
|
handles: result.handles
|
|
3523
|
-
};
|
|
3565
|
+
});
|
|
3566
|
+
manifestEntries[mainKey] = stageBuildAssetModule(
|
|
3567
|
+
state.projectRoot,
|
|
3568
|
+
"__pr",
|
|
3569
|
+
mainValue
|
|
3570
|
+
);
|
|
3524
3571
|
if (result.interceptSegments?.length) {
|
|
3525
|
-
|
|
3572
|
+
const interceptKey = `${result.routeName}/${paramHash}/i`;
|
|
3573
|
+
const interceptValue = JSON.stringify({
|
|
3526
3574
|
segments: [...result.segments, ...result.interceptSegments],
|
|
3527
3575
|
handles: {
|
|
3528
3576
|
...result.handles,
|
|
3529
3577
|
...result.interceptHandles || {}
|
|
3530
3578
|
}
|
|
3531
|
-
};
|
|
3579
|
+
});
|
|
3580
|
+
manifestEntries[interceptKey] = stageBuildAssetModule(
|
|
3581
|
+
state.projectRoot,
|
|
3582
|
+
"__pr",
|
|
3583
|
+
interceptValue
|
|
3584
|
+
);
|
|
3532
3585
|
}
|
|
3533
3586
|
const elapsed = (performance.now() - startUrl).toFixed(0);
|
|
3534
3587
|
console.log(
|
|
@@ -3572,7 +3625,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3572
3625
|
}
|
|
3573
3626
|
const totalElapsed = (performance.now() - startTotal).toFixed(0);
|
|
3574
3627
|
if (doneCount > 0) {
|
|
3575
|
-
state.
|
|
3628
|
+
state.prerenderManifestEntries = manifestEntries;
|
|
3576
3629
|
}
|
|
3577
3630
|
const parts = [`${doneCount} done`];
|
|
3578
3631
|
if (skipCount > 0) parts.push(`${skipCount} skipped`);
|
|
@@ -3583,7 +3636,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
|
|
|
3583
3636
|
async function renderStaticHandlers(state, rscEnv, registry) {
|
|
3584
3637
|
if (!state.opts?.enableBuildPrerender || !state.isBuildMode || !state.resolvedStaticModules?.size)
|
|
3585
3638
|
return;
|
|
3586
|
-
const
|
|
3639
|
+
const manifestEntries = {};
|
|
3587
3640
|
let staticDone = 0;
|
|
3588
3641
|
let staticSkip = 0;
|
|
3589
3642
|
let totalStaticCount = 0;
|
|
@@ -3620,7 +3673,13 @@ async function renderStaticHandlers(state, rscEnv, registry) {
|
|
|
3620
3673
|
def.$$routePrefix
|
|
3621
3674
|
);
|
|
3622
3675
|
if (result) {
|
|
3623
|
-
|
|
3676
|
+
const hasHandles = Object.keys(result.handles).length > 0;
|
|
3677
|
+
const exportValue = hasHandles ? JSON.stringify(result) : JSON.stringify(result.encoded);
|
|
3678
|
+
manifestEntries[def.$$id] = stageBuildAssetModule(
|
|
3679
|
+
state.projectRoot,
|
|
3680
|
+
"__st",
|
|
3681
|
+
exportValue
|
|
3682
|
+
);
|
|
3624
3683
|
const elapsed = (performance.now() - startHandler).toFixed(0);
|
|
3625
3684
|
console.log(
|
|
3626
3685
|
`[rsc-router] OK ${name.padEnd(40)} (${elapsed}ms)`
|
|
@@ -3657,7 +3716,7 @@ async function renderStaticHandlers(state, rscEnv, registry) {
|
|
|
3657
3716
|
}
|
|
3658
3717
|
const totalStaticElapsed = (performance.now() - startStatic).toFixed(0);
|
|
3659
3718
|
if (staticDone > 0) {
|
|
3660
|
-
state.
|
|
3719
|
+
state.staticManifestEntries = manifestEntries;
|
|
3661
3720
|
}
|
|
3662
3721
|
const staticParts = [`${staticDone} done`];
|
|
3663
3722
|
if (staticSkip > 0) staticParts.push(`${staticSkip} skipped`);
|
|
@@ -3879,8 +3938,8 @@ async function discoverRouters(state, rscEnv) {
|
|
|
3879
3938
|
}
|
|
3880
3939
|
|
|
3881
3940
|
// src/vite/discovery/route-types-writer.ts
|
|
3882
|
-
import { dirname as dirname3, basename, join as join3, resolve as
|
|
3883
|
-
import { readFileSync as readFileSync4, writeFileSync as
|
|
3941
|
+
import { dirname as dirname3, basename, join as join3, resolve as resolve6 } from "node:path";
|
|
3942
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5, unlinkSync as unlinkSync2 } from "node:fs";
|
|
3884
3943
|
function filterUserNamedRoutes(manifest) {
|
|
3885
3944
|
const filtered = {};
|
|
3886
3945
|
for (const [name, pattern] of Object.entries(manifest)) {
|
|
@@ -3914,7 +3973,7 @@ function writeCombinedRouteTypesWithTracking(state, opts) {
|
|
|
3914
3973
|
""
|
|
3915
3974
|
);
|
|
3916
3975
|
const outPath = join3(routerDir, `${routerBasename}.named-routes.gen.ts`);
|
|
3917
|
-
if (!
|
|
3976
|
+
if (!existsSync5(outPath)) continue;
|
|
3918
3977
|
try {
|
|
3919
3978
|
const content = readFileSync4(outPath, "utf-8");
|
|
3920
3979
|
if (content !== preContent.get(outPath)) {
|
|
@@ -3928,10 +3987,10 @@ function writeRouteTypesFiles(state) {
|
|
|
3928
3987
|
if (state.perRouterManifests.length === 0) return;
|
|
3929
3988
|
try {
|
|
3930
3989
|
const entryDir = dirname3(
|
|
3931
|
-
|
|
3990
|
+
resolve6(state.projectRoot, state.resolvedEntryPath)
|
|
3932
3991
|
);
|
|
3933
3992
|
const oldCombinedPath = join3(entryDir, "named-routes.gen.ts");
|
|
3934
|
-
if (
|
|
3993
|
+
if (existsSync5(oldCombinedPath)) {
|
|
3935
3994
|
unlinkSync2(oldCombinedPath);
|
|
3936
3995
|
console.log(
|
|
3937
3996
|
`[rsc-router] Removed stale combined route types: ${oldCombinedPath}`
|
|
@@ -3975,10 +4034,10 @@ Set an explicit \`id\` on createRouter() or check the call site.`
|
|
|
3975
4034
|
userRoutes,
|
|
3976
4035
|
effectiveSearchSchemas && Object.keys(effectiveSearchSchemas).length > 0 ? effectiveSearchSchemas : void 0
|
|
3977
4036
|
);
|
|
3978
|
-
const existing =
|
|
4037
|
+
const existing = existsSync5(outPath) ? readFileSync4(outPath, "utf-8") : null;
|
|
3979
4038
|
if (existing !== source) {
|
|
3980
4039
|
markSelfGenWrite(state, outPath, source);
|
|
3981
|
-
|
|
4040
|
+
writeFileSync3(outPath, source);
|
|
3982
4041
|
console.log(`[rsc-router] Generated route types -> ${outPath}`);
|
|
3983
4042
|
}
|
|
3984
4043
|
}
|
|
@@ -4025,10 +4084,10 @@ function supplementGenFilesWithRuntimeRoutes(state) {
|
|
|
4025
4084
|
mergedRoutes,
|
|
4026
4085
|
Object.keys(mergedSearchSchemas).length > 0 ? mergedSearchSchemas : void 0
|
|
4027
4086
|
);
|
|
4028
|
-
const existing =
|
|
4087
|
+
const existing = existsSync5(outPath) ? readFileSync4(outPath, "utf-8") : null;
|
|
4029
4088
|
if (existing !== source) {
|
|
4030
4089
|
markSelfGenWrite(state, outPath, source);
|
|
4031
|
-
|
|
4090
|
+
writeFileSync3(outPath, source);
|
|
4032
4091
|
}
|
|
4033
4092
|
}
|
|
4034
4093
|
}
|
|
@@ -4173,14 +4232,13 @@ function generatePerRouterModule(state, routerId) {
|
|
|
4173
4232
|
}
|
|
4174
4233
|
|
|
4175
4234
|
// src/vite/discovery/bundle-postprocess.ts
|
|
4176
|
-
import { resolve as
|
|
4177
|
-
import {
|
|
4178
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync } from "node:fs";
|
|
4235
|
+
import { resolve as resolve7 } from "node:path";
|
|
4236
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "node:fs";
|
|
4179
4237
|
function postprocessBundle(state) {
|
|
4180
|
-
const hasPrerenderData = state.
|
|
4181
|
-
const hasStaticData = state.
|
|
4238
|
+
const hasPrerenderData = state.prerenderManifestEntries && Object.keys(state.prerenderManifestEntries).length > 0;
|
|
4239
|
+
const hasStaticData = state.staticManifestEntries && Object.keys(state.staticManifestEntries).length > 0;
|
|
4182
4240
|
if (!hasPrerenderData && !hasStaticData) return;
|
|
4183
|
-
const rscEntryPath =
|
|
4241
|
+
const rscEntryPath = resolve7(
|
|
4184
4242
|
state.projectRoot,
|
|
4185
4243
|
"dist/rsc",
|
|
4186
4244
|
state.rscEntryFileName ?? "index.js"
|
|
@@ -4201,7 +4259,7 @@ function postprocessBundle(state) {
|
|
|
4201
4259
|
];
|
|
4202
4260
|
for (const target of evictionTargets) {
|
|
4203
4261
|
if (!target.info) continue;
|
|
4204
|
-
const chunkPath =
|
|
4262
|
+
const chunkPath = resolve7(
|
|
4205
4263
|
state.projectRoot,
|
|
4206
4264
|
"dist/rsc",
|
|
4207
4265
|
target.info.fileName
|
|
@@ -4215,7 +4273,7 @@ function postprocessBundle(state) {
|
|
|
4215
4273
|
target.brand
|
|
4216
4274
|
);
|
|
4217
4275
|
if (result) {
|
|
4218
|
-
|
|
4276
|
+
writeFileSync4(chunkPath, result.code);
|
|
4219
4277
|
const savedKB = (result.savedBytes / 1024).toFixed(1);
|
|
4220
4278
|
console.log(
|
|
4221
4279
|
`[rsc-router] Evicted ${target.label} (${savedKB} KB saved): ${target.info.fileName}`
|
|
@@ -4229,44 +4287,38 @@ function postprocessBundle(state) {
|
|
|
4229
4287
|
}
|
|
4230
4288
|
state.handlerChunkInfo = null;
|
|
4231
4289
|
state.staticHandlerChunkInfo = null;
|
|
4232
|
-
if (hasPrerenderData &&
|
|
4290
|
+
if (hasPrerenderData && existsSync6(rscEntryPath)) {
|
|
4233
4291
|
const rscCode = readFileSync5(rscEntryPath, "utf-8");
|
|
4234
4292
|
if (!rscCode.includes("__prerender-manifest.js")) {
|
|
4235
4293
|
try {
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4294
|
+
let totalBytes = copyStagedBuildAssets(
|
|
4295
|
+
state.projectRoot,
|
|
4296
|
+
Object.values(state.prerenderManifestEntries)
|
|
4297
|
+
);
|
|
4298
|
+
const manifestMap = {};
|
|
4299
|
+
for (const [key, assetFileName] of Object.entries(
|
|
4300
|
+
state.prerenderManifestEntries
|
|
4242
4301
|
)) {
|
|
4243
|
-
|
|
4244
|
-
const contentHash = createHash4("sha256").update(entryJson).digest("hex").slice(0, 8);
|
|
4245
|
-
const assetFileName = `__pr-${contentHash}.js`;
|
|
4246
|
-
const assetPath = resolve6(assetsDir, assetFileName);
|
|
4247
|
-
const assetCode = `export default ${entryJson};
|
|
4248
|
-
`;
|
|
4249
|
-
writeFileSync3(assetPath, assetCode);
|
|
4250
|
-
totalBytes += Buffer.byteLength(assetCode);
|
|
4251
|
-
manifestEntries.push(
|
|
4252
|
-
`${JSON.stringify(key)}:()=>import("./assets/${assetFileName}")`
|
|
4253
|
-
);
|
|
4302
|
+
manifestMap[key] = `./assets/${assetFileName}`;
|
|
4254
4303
|
}
|
|
4255
|
-
const manifestCode =
|
|
4256
|
-
|
|
4257
|
-
|
|
4304
|
+
const manifestCode = [
|
|
4305
|
+
`const m=JSON.parse('${JSON.stringify(manifestMap).replace(/'/g, "\\'")}');`,
|
|
4306
|
+
`export function loadPrerenderAsset(s){return import(s)}`,
|
|
4307
|
+
`export default m;`,
|
|
4308
|
+
""
|
|
4309
|
+
].join("\n");
|
|
4310
|
+
const manifestPath = resolve7(
|
|
4258
4311
|
state.projectRoot,
|
|
4259
4312
|
"dist/rsc/__prerender-manifest.js"
|
|
4260
4313
|
);
|
|
4261
|
-
|
|
4314
|
+
writeFileSync4(manifestPath, manifestCode);
|
|
4262
4315
|
totalBytes += Buffer.byteLength(manifestCode);
|
|
4263
|
-
const injection = `
|
|
4264
|
-
globalThis.__PRERENDER_MANIFEST = __pm;
|
|
4316
|
+
const injection = `globalThis.__loadPrerenderManifestModule = () => import("./__prerender-manifest.js");
|
|
4265
4317
|
`;
|
|
4266
|
-
|
|
4318
|
+
writeFileSync4(rscEntryPath, injection + rscCode);
|
|
4267
4319
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
4268
4320
|
console.log(
|
|
4269
|
-
`[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.
|
|
4321
|
+
`[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries).length} entries)`
|
|
4270
4322
|
);
|
|
4271
4323
|
} catch (err) {
|
|
4272
4324
|
throw new Error(
|
|
@@ -4275,44 +4327,36 @@ globalThis.__PRERENDER_MANIFEST = __pm;
|
|
|
4275
4327
|
}
|
|
4276
4328
|
}
|
|
4277
4329
|
}
|
|
4278
|
-
if (hasStaticData &&
|
|
4330
|
+
if (hasStaticData && existsSync6(rscEntryPath)) {
|
|
4279
4331
|
const rscCode = readFileSync5(rscEntryPath, "utf-8");
|
|
4280
4332
|
if (!rscCode.includes("__STATIC_MANIFEST")) {
|
|
4281
4333
|
try {
|
|
4282
|
-
const assetsDir = resolve6(state.projectRoot, "dist/rsc/assets");
|
|
4283
|
-
mkdirSync(assetsDir, { recursive: true });
|
|
4284
4334
|
const manifestEntries = [];
|
|
4285
|
-
let totalBytes =
|
|
4286
|
-
|
|
4287
|
-
state.
|
|
4335
|
+
let totalBytes = copyStagedBuildAssets(
|
|
4336
|
+
state.projectRoot,
|
|
4337
|
+
Object.values(state.staticManifestEntries)
|
|
4338
|
+
);
|
|
4339
|
+
for (const [handlerId, assetFileName] of Object.entries(
|
|
4340
|
+
state.staticManifestEntries
|
|
4288
4341
|
)) {
|
|
4289
|
-
const hasHandles = Object.keys(handles).length > 0;
|
|
4290
|
-
const exportValue = hasHandles ? JSON.stringify({ encoded, handles }) : JSON.stringify(encoded);
|
|
4291
|
-
const contentHash = createHash4("sha256").update(exportValue).digest("hex").slice(0, 8);
|
|
4292
|
-
const assetFileName = `__st-${contentHash}.js`;
|
|
4293
|
-
const assetPath = resolve6(assetsDir, assetFileName);
|
|
4294
|
-
const assetCode = `export default ${exportValue};
|
|
4295
|
-
`;
|
|
4296
|
-
writeFileSync3(assetPath, assetCode);
|
|
4297
|
-
totalBytes += Buffer.byteLength(assetCode);
|
|
4298
4342
|
manifestEntries.push(
|
|
4299
4343
|
`${JSON.stringify(handlerId)}:()=>import("./assets/${assetFileName}")`
|
|
4300
4344
|
);
|
|
4301
4345
|
}
|
|
4302
4346
|
const manifestCode = `const m={${manifestEntries.join(",")}};globalThis.__STATIC_MANIFEST=m;export default m;
|
|
4303
4347
|
`;
|
|
4304
|
-
const manifestPath =
|
|
4348
|
+
const manifestPath = resolve7(
|
|
4305
4349
|
state.projectRoot,
|
|
4306
4350
|
"dist/rsc/__static-manifest.js"
|
|
4307
4351
|
);
|
|
4308
|
-
|
|
4352
|
+
writeFileSync4(manifestPath, manifestCode);
|
|
4309
4353
|
totalBytes += Buffer.byteLength(manifestCode);
|
|
4310
4354
|
const injection = `import "./__static-manifest.js";
|
|
4311
4355
|
`;
|
|
4312
|
-
|
|
4356
|
+
writeFileSync4(rscEntryPath, injection + rscCode);
|
|
4313
4357
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
4314
4358
|
console.log(
|
|
4315
|
-
`[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.
|
|
4359
|
+
`[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries).length} entries)`
|
|
4316
4360
|
);
|
|
4317
4361
|
} catch (err) {
|
|
4318
4362
|
throw new Error(
|
|
@@ -4427,8 +4471,8 @@ function createRouterDiscoveryPlugin(entryPath, opts) {
|
|
|
4427
4471
|
if (globalThis.__rscRouterDiscoveryActive) return;
|
|
4428
4472
|
s.devServer = server;
|
|
4429
4473
|
let resolveDiscovery;
|
|
4430
|
-
const discoveryPromise = new Promise((
|
|
4431
|
-
resolveDiscovery =
|
|
4474
|
+
const discoveryPromise = new Promise((resolve10) => {
|
|
4475
|
+
resolveDiscovery = resolve10;
|
|
4432
4476
|
});
|
|
4433
4477
|
const getDevServerOrigin = () => server.resolvedUrls?.local?.[0]?.replace(/\/$/, "") || `http://localhost:${server.config.server.port || 5173}`;
|
|
4434
4478
|
let prerenderTempServer = null;
|
|
@@ -4503,8 +4547,8 @@ ${err.stack}`
|
|
|
4503
4547
|
resolveDiscovery();
|
|
4504
4548
|
}
|
|
4505
4549
|
};
|
|
4506
|
-
s.discoveryDone = new Promise((
|
|
4507
|
-
setTimeout(() => discover().then(
|
|
4550
|
+
s.discoveryDone = new Promise((resolve10) => {
|
|
4551
|
+
setTimeout(() => discover().then(resolve10, resolve10), 0);
|
|
4508
4552
|
});
|
|
4509
4553
|
let mainRegistry = null;
|
|
4510
4554
|
const propagateDiscoveryState = async (rscEnv) => {
|
|
@@ -4673,7 +4717,7 @@ ${err.stack}`
|
|
|
4673
4717
|
if (hasCreateRouter) {
|
|
4674
4718
|
const nestedRouterConflict = findNestedRouterConflict([
|
|
4675
4719
|
...s.cachedRouterFiles ?? [],
|
|
4676
|
-
|
|
4720
|
+
resolve8(filePath)
|
|
4677
4721
|
]);
|
|
4678
4722
|
if (nestedRouterConflict) {
|
|
4679
4723
|
server.config.logger.error(
|
|
@@ -4703,6 +4747,9 @@ ${err.stack}`
|
|
|
4703
4747
|
async buildStart() {
|
|
4704
4748
|
if (!s.isBuildMode) return;
|
|
4705
4749
|
if (s.mergedRouteManifest !== null) return;
|
|
4750
|
+
resetStagedBuildAssets(s.projectRoot);
|
|
4751
|
+
s.prerenderManifestEntries = null;
|
|
4752
|
+
s.staticManifestEntries = null;
|
|
4706
4753
|
let tempServer = null;
|
|
4707
4754
|
globalThis.__rscRouterDiscoveryActive = true;
|
|
4708
4755
|
try {
|
|
@@ -4944,7 +4991,7 @@ async function rango(options) {
|
|
|
4944
4991
|
name: "@rangojs/router:auto-discover",
|
|
4945
4992
|
config(userConfig) {
|
|
4946
4993
|
if (routerRef.path) return;
|
|
4947
|
-
const root = userConfig.root ?
|
|
4994
|
+
const root = userConfig.root ? resolve9(process.cwd(), userConfig.root) : process.cwd();
|
|
4948
4995
|
const filter = createScanFilter(root, {
|
|
4949
4996
|
include: resolvedOptions.include,
|
|
4950
4997
|
exclude: resolvedOptions.exclude
|
package/package.json
CHANGED
package/src/prerender/store.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Prerender Store
|
|
3
3
|
*
|
|
4
4
|
* Reads pre-rendered segment data from the worker bundle at build time.
|
|
5
|
-
* The
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* The manifest module is lazily loaded via globalThis.__loadPrerenderManifestModule,
|
|
6
|
+
* a function injected into the RSC entry that returns the manifest module
|
|
7
|
+
* containing a key-to-specifier map and a `loadPrerenderAsset` function
|
|
8
|
+
* that anchors import() resolution relative to the manifest file.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import type {
|
|
@@ -34,11 +35,20 @@ export interface StaticStore {
|
|
|
34
35
|
get(handlerId: string): Promise<StaticEntry | null>;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
interface PrerenderManifestModule {
|
|
39
|
+
default: Record<string, string>;
|
|
40
|
+
loadPrerenderAsset: (
|
|
41
|
+
specifier: string,
|
|
42
|
+
) => Promise<{ default: PrerenderEntry }>;
|
|
43
|
+
}
|
|
44
|
+
|
|
37
45
|
declare global {
|
|
38
|
-
// Injected by closeBundle post-processing:
|
|
46
|
+
// Injected by closeBundle post-processing: lazy loader for the prerender
|
|
47
|
+
// manifest module. The module exports a key→specifier map and a
|
|
48
|
+
// loadPrerenderAsset function that anchors import() relative to the manifest.
|
|
39
49
|
// eslint-disable-next-line no-var
|
|
40
|
-
var
|
|
41
|
-
|
|
|
50
|
+
var __loadPrerenderManifestModule:
|
|
51
|
+
| (() => Promise<PrerenderManifestModule>)
|
|
42
52
|
| undefined;
|
|
43
53
|
// Injected by closeBundle post-processing: map of handlerId -> () => import("./assets/__st-*.js")
|
|
44
54
|
// Asset default export is either a string (no handles) or { encoded, handles } object.
|
|
@@ -78,17 +88,28 @@ export function createDevPrerenderStore(devUrl: string): PrerenderStore {
|
|
|
78
88
|
/**
|
|
79
89
|
* Create a prerender store.
|
|
80
90
|
* Dev mode: on-demand fetch from Vite dev server (node:fs works there).
|
|
81
|
-
* Production: backed by globalThis.
|
|
91
|
+
* Production: backed by globalThis.__loadPrerenderManifestModule which lazily
|
|
92
|
+
* loads the manifest module on first access.
|
|
82
93
|
* Returns null if no prerender data is available.
|
|
83
94
|
*/
|
|
84
95
|
export function createPrerenderStore(): PrerenderStore | null {
|
|
85
96
|
if (globalThis.__PRERENDER_DEV_URL) {
|
|
86
97
|
return createDevPrerenderStore(globalThis.__PRERENDER_DEV_URL);
|
|
87
98
|
}
|
|
88
|
-
|
|
89
|
-
if (!manifest || Object.keys(manifest).length === 0) return null;
|
|
99
|
+
if (!globalThis.__loadPrerenderManifestModule) return null;
|
|
90
100
|
|
|
91
101
|
const cache = new Map<string, Promise<PrerenderEntry | null>>();
|
|
102
|
+
let manifestModulePromise: Promise<PrerenderManifestModule | null> | null =
|
|
103
|
+
null;
|
|
104
|
+
|
|
105
|
+
function loadManifestModule(): Promise<PrerenderManifestModule | null> {
|
|
106
|
+
if (!manifestModulePromise) {
|
|
107
|
+
manifestModulePromise = globalThis.__loadPrerenderManifestModule!().catch(
|
|
108
|
+
() => null,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return manifestModulePromise;
|
|
112
|
+
}
|
|
92
113
|
|
|
93
114
|
return {
|
|
94
115
|
get(routeName: string, paramHash: string): Promise<PrerenderEntry | null> {
|
|
@@ -96,18 +117,38 @@ export function createPrerenderStore(): PrerenderStore | null {
|
|
|
96
117
|
const cached = cache.get(key);
|
|
97
118
|
if (cached) return cached;
|
|
98
119
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
120
|
+
const promise = loadManifestModule().then((mod) => {
|
|
121
|
+
if (!mod) return null;
|
|
122
|
+
const specifier = mod.default[key];
|
|
123
|
+
if (!specifier) return null;
|
|
124
|
+
return mod
|
|
125
|
+
.loadPrerenderAsset(specifier)
|
|
126
|
+
.then((asset) => asset.default)
|
|
127
|
+
.catch(() => null);
|
|
128
|
+
});
|
|
105
129
|
cache.set(key, promise);
|
|
106
130
|
return promise;
|
|
107
131
|
},
|
|
108
132
|
};
|
|
109
133
|
}
|
|
110
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Load the prerender manifest index for test introspection.
|
|
137
|
+
* Returns the key→specifier map or null if unavailable.
|
|
138
|
+
*/
|
|
139
|
+
export async function loadPrerenderManifestIndex(): Promise<Record<
|
|
140
|
+
string,
|
|
141
|
+
string
|
|
142
|
+
> | null> {
|
|
143
|
+
if (!globalThis.__loadPrerenderManifestModule) return null;
|
|
144
|
+
try {
|
|
145
|
+
const mod = await globalThis.__loadPrerenderManifestModule();
|
|
146
|
+
return mod.default;
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
111
152
|
/**
|
|
112
153
|
* Create a static segment store.
|
|
113
154
|
* Production only: backed by globalThis.__STATIC_MANIFEST injected at build time.
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
|
-
import {
|
|
10
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
11
10
|
import { evictHandlerCode } from "../utils/bundle-analysis.js";
|
|
11
|
+
import { copyStagedBuildAssets } from "../utils/prerender-utils.js";
|
|
12
12
|
import type { DiscoveryState } from "./state.js";
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -17,11 +17,11 @@ import type { DiscoveryState } from "./state.js";
|
|
|
17
17
|
*/
|
|
18
18
|
export function postprocessBundle(state: DiscoveryState): void {
|
|
19
19
|
const hasPrerenderData =
|
|
20
|
-
state.
|
|
21
|
-
Object.keys(state.
|
|
20
|
+
state.prerenderManifestEntries &&
|
|
21
|
+
Object.keys(state.prerenderManifestEntries).length > 0;
|
|
22
22
|
const hasStaticData =
|
|
23
|
-
state.
|
|
24
|
-
Object.keys(state.
|
|
23
|
+
state.staticManifestEntries &&
|
|
24
|
+
Object.keys(state.staticManifestEntries).length > 0;
|
|
25
25
|
if (!hasPrerenderData && !hasStaticData) return;
|
|
26
26
|
|
|
27
27
|
// Find RSC entry (recorded in generateBundle, fallback to dist/rsc/index.js)
|
|
@@ -88,39 +88,30 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
88
88
|
state.staticHandlerChunkInfo = null;
|
|
89
89
|
|
|
90
90
|
// 2. Write prerender data as separate importable asset modules
|
|
91
|
-
// and inject a manifest
|
|
91
|
+
// and inject a lazy manifest loader into the RSC entry.
|
|
92
92
|
if (hasPrerenderData && existsSync(rscEntryPath)) {
|
|
93
93
|
const rscCode = readFileSync(rscEntryPath, "utf-8");
|
|
94
|
-
// Check for the specific injection marker
|
|
95
|
-
// The runtime code (prerender store) also references __PRERENDER_MANIFEST,
|
|
96
|
-
// so a broad string check would false-positive and skip injection.
|
|
94
|
+
// Check for the specific injection marker to avoid double-injection.
|
|
97
95
|
if (!rscCode.includes("__prerender-manifest.js")) {
|
|
98
96
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let totalBytes = 0;
|
|
97
|
+
let totalBytes = copyStagedBuildAssets(
|
|
98
|
+
state.projectRoot,
|
|
99
|
+
Object.values(state.prerenderManifestEntries!),
|
|
100
|
+
);
|
|
104
101
|
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
const manifestMap: Record<string, string> = {};
|
|
103
|
+
for (const [key, assetFileName] of Object.entries(
|
|
104
|
+
state.prerenderManifestEntries!,
|
|
107
105
|
)) {
|
|
108
|
-
|
|
109
|
-
const contentHash = createHash("sha256")
|
|
110
|
-
.update(entryJson)
|
|
111
|
-
.digest("hex")
|
|
112
|
-
.slice(0, 8);
|
|
113
|
-
const assetFileName = `__pr-${contentHash}.js`;
|
|
114
|
-
const assetPath = resolve(assetsDir, assetFileName);
|
|
115
|
-
const assetCode = `export default ${entryJson};\n`;
|
|
116
|
-
writeFileSync(assetPath, assetCode);
|
|
117
|
-
totalBytes += Buffer.byteLength(assetCode);
|
|
118
|
-
manifestEntries.push(
|
|
119
|
-
`${JSON.stringify(key)}:()=>import("./assets/${assetFileName}")`,
|
|
120
|
-
);
|
|
106
|
+
manifestMap[key] = `./assets/${assetFileName}`;
|
|
121
107
|
}
|
|
122
108
|
|
|
123
|
-
const manifestCode =
|
|
109
|
+
const manifestCode = [
|
|
110
|
+
`const m=JSON.parse('${JSON.stringify(manifestMap).replace(/'/g, "\\'")}');`,
|
|
111
|
+
`export function loadPrerenderAsset(s){return import(s)}`,
|
|
112
|
+
`export default m;`,
|
|
113
|
+
"",
|
|
114
|
+
].join("\n");
|
|
124
115
|
const manifestPath = resolve(
|
|
125
116
|
state.projectRoot,
|
|
126
117
|
"dist/rsc/__prerender-manifest.js",
|
|
@@ -128,12 +119,12 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
128
119
|
writeFileSync(manifestPath, manifestCode);
|
|
129
120
|
totalBytes += Buffer.byteLength(manifestCode);
|
|
130
121
|
|
|
131
|
-
const injection = `
|
|
122
|
+
const injection = `globalThis.__loadPrerenderManifestModule = () => import("./__prerender-manifest.js");\n`;
|
|
132
123
|
writeFileSync(rscEntryPath, injection + rscCode);
|
|
133
124
|
|
|
134
125
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
135
126
|
console.log(
|
|
136
|
-
`[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.
|
|
127
|
+
`[rsc-router] Wrote prerender assets (${totalKB} KB total, ${Object.keys(state.prerenderManifestEntries!).length} entries)`,
|
|
137
128
|
);
|
|
138
129
|
} catch (err: any) {
|
|
139
130
|
throw new Error(
|
|
@@ -149,31 +140,15 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
149
140
|
const rscCode = readFileSync(rscEntryPath, "utf-8");
|
|
150
141
|
if (!rscCode.includes("__STATIC_MANIFEST")) {
|
|
151
142
|
try {
|
|
152
|
-
const assetsDir = resolve(state.projectRoot, "dist/rsc/assets");
|
|
153
|
-
mkdirSync(assetsDir, { recursive: true });
|
|
154
|
-
|
|
155
143
|
const manifestEntries: string[] = [];
|
|
156
|
-
let totalBytes =
|
|
144
|
+
let totalBytes = copyStagedBuildAssets(
|
|
145
|
+
state.projectRoot,
|
|
146
|
+
Object.values(state.staticManifestEntries!),
|
|
147
|
+
);
|
|
157
148
|
|
|
158
|
-
for (const [handlerId,
|
|
159
|
-
state.
|
|
149
|
+
for (const [handlerId, assetFileName] of Object.entries(
|
|
150
|
+
state.staticManifestEntries!,
|
|
160
151
|
)) {
|
|
161
|
-
// Store both the Flight payload and handle data
|
|
162
|
-
const hasHandles = Object.keys(handles).length > 0;
|
|
163
|
-
const exportValue = hasHandles
|
|
164
|
-
? JSON.stringify({ encoded, handles })
|
|
165
|
-
: JSON.stringify(encoded);
|
|
166
|
-
// Hash the full payload that is written so distinct handle
|
|
167
|
-
// snapshots produce distinct asset filenames.
|
|
168
|
-
const contentHash = createHash("sha256")
|
|
169
|
-
.update(exportValue)
|
|
170
|
-
.digest("hex")
|
|
171
|
-
.slice(0, 8);
|
|
172
|
-
const assetFileName = `__st-${contentHash}.js`;
|
|
173
|
-
const assetPath = resolve(assetsDir, assetFileName);
|
|
174
|
-
const assetCode = `export default ${exportValue};\n`;
|
|
175
|
-
writeFileSync(assetPath, assetCode);
|
|
176
|
-
totalBytes += Buffer.byteLength(assetCode);
|
|
177
152
|
manifestEntries.push(
|
|
178
153
|
`${JSON.stringify(handlerId)}:()=>import("./assets/${assetFileName}")`,
|
|
179
154
|
);
|
|
@@ -197,7 +172,7 @@ export function postprocessBundle(state: DiscoveryState): void {
|
|
|
197
172
|
|
|
198
173
|
const totalKB = (totalBytes / 1024).toFixed(1);
|
|
199
174
|
console.log(
|
|
200
|
-
`[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.
|
|
175
|
+
`[rsc-router] Wrote static assets (${totalKB} KB total, ${Object.keys(state.staticManifestEntries!).length} entries)`,
|
|
201
176
|
);
|
|
202
177
|
} catch (err: any) {
|
|
203
178
|
throw new Error(
|
|
@@ -13,12 +13,14 @@ import {
|
|
|
13
13
|
runWithConcurrency,
|
|
14
14
|
groupByConcurrency,
|
|
15
15
|
notifyOnError,
|
|
16
|
+
stageBuildAssetModule,
|
|
16
17
|
} from "../utils/prerender-utils.js";
|
|
17
18
|
import type { DiscoveryState } from "./state.js";
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Expand prerender routes into concrete URLs and render them via the
|
|
21
|
-
* RSC runner.
|
|
22
|
+
* RSC runner. Stages asset modules and stores key-to-file entries in
|
|
23
|
+
* state.prerenderManifestEntries.
|
|
22
24
|
*/
|
|
23
25
|
export async function expandPrerenderRoutes(
|
|
24
26
|
state: DiscoveryState,
|
|
@@ -150,7 +152,7 @@ export async function expandPrerenderRoutes(
|
|
|
150
152
|
|
|
151
153
|
const { hashParams } = await rscEnv.runner.import("@rangojs/router/build");
|
|
152
154
|
|
|
153
|
-
const
|
|
155
|
+
const manifestEntries: Record<string, string> = {};
|
|
154
156
|
let doneCount = 0;
|
|
155
157
|
let skipCount = 0;
|
|
156
158
|
const startTotal = performance.now();
|
|
@@ -187,18 +189,30 @@ export async function expandPrerenderRoutes(
|
|
|
187
189
|
}
|
|
188
190
|
|
|
189
191
|
const paramHash = hashParams(result.params || {});
|
|
190
|
-
|
|
192
|
+
const mainKey = `${result.routeName}/${paramHash}`;
|
|
193
|
+
const mainValue = JSON.stringify({
|
|
191
194
|
segments: result.segments,
|
|
192
195
|
handles: result.handles,
|
|
193
|
-
};
|
|
196
|
+
});
|
|
197
|
+
manifestEntries[mainKey] = stageBuildAssetModule(
|
|
198
|
+
state.projectRoot,
|
|
199
|
+
"__pr",
|
|
200
|
+
mainValue,
|
|
201
|
+
);
|
|
194
202
|
if (result.interceptSegments?.length) {
|
|
195
|
-
|
|
203
|
+
const interceptKey = `${result.routeName}/${paramHash}/i`;
|
|
204
|
+
const interceptValue = JSON.stringify({
|
|
196
205
|
segments: [...result.segments, ...result.interceptSegments],
|
|
197
206
|
handles: {
|
|
198
207
|
...result.handles,
|
|
199
208
|
...(result.interceptHandles || {}),
|
|
200
209
|
},
|
|
201
|
-
};
|
|
210
|
+
});
|
|
211
|
+
manifestEntries[interceptKey] = stageBuildAssetModule(
|
|
212
|
+
state.projectRoot,
|
|
213
|
+
"__pr",
|
|
214
|
+
interceptValue,
|
|
215
|
+
);
|
|
202
216
|
}
|
|
203
217
|
const elapsed = (performance.now() - startUrl).toFixed(0);
|
|
204
218
|
console.log(
|
|
@@ -244,7 +258,7 @@ export async function expandPrerenderRoutes(
|
|
|
244
258
|
|
|
245
259
|
const totalElapsed = (performance.now() - startTotal).toFixed(0);
|
|
246
260
|
if (doneCount > 0) {
|
|
247
|
-
state.
|
|
261
|
+
state.prerenderManifestEntries = manifestEntries;
|
|
248
262
|
}
|
|
249
263
|
const parts = [`${doneCount} done`];
|
|
250
264
|
if (skipCount > 0) parts.push(`${skipCount} skipped`);
|
|
@@ -256,7 +270,8 @@ export async function expandPrerenderRoutes(
|
|
|
256
270
|
/**
|
|
257
271
|
* Render Static handlers at build time. Each Static handler is called
|
|
258
272
|
* with a synthetic BuildContext and its output is RSC-serialized.
|
|
259
|
-
*
|
|
273
|
+
* Stages asset modules and stores handlerId-to-file entries in
|
|
274
|
+
* state.staticManifestEntries.
|
|
260
275
|
*/
|
|
261
276
|
export async function renderStaticHandlers(
|
|
262
277
|
state: DiscoveryState,
|
|
@@ -270,10 +285,7 @@ export async function renderStaticHandlers(
|
|
|
270
285
|
)
|
|
271
286
|
return;
|
|
272
287
|
|
|
273
|
-
const
|
|
274
|
-
string,
|
|
275
|
-
{ encoded: string; handles: Record<string, unknown[]> }
|
|
276
|
-
> = {};
|
|
288
|
+
const manifestEntries: Record<string, string> = {};
|
|
277
289
|
let staticDone = 0;
|
|
278
290
|
let staticSkip = 0;
|
|
279
291
|
let totalStaticCount = 0;
|
|
@@ -316,7 +328,15 @@ export async function renderStaticHandlers(
|
|
|
316
328
|
(def as any).$$routePrefix,
|
|
317
329
|
);
|
|
318
330
|
if (result) {
|
|
319
|
-
|
|
331
|
+
const hasHandles = Object.keys(result.handles).length > 0;
|
|
332
|
+
const exportValue = hasHandles
|
|
333
|
+
? JSON.stringify(result)
|
|
334
|
+
: JSON.stringify(result.encoded);
|
|
335
|
+
manifestEntries[def.$$id] = stageBuildAssetModule(
|
|
336
|
+
state.projectRoot,
|
|
337
|
+
"__st",
|
|
338
|
+
exportValue,
|
|
339
|
+
);
|
|
320
340
|
const elapsed = (performance.now() - startHandler).toFixed(0);
|
|
321
341
|
console.log(
|
|
322
342
|
`[rsc-router] OK ${name.padEnd(40)} (${elapsed}ms)`,
|
|
@@ -355,7 +375,7 @@ export async function renderStaticHandlers(
|
|
|
355
375
|
|
|
356
376
|
const totalStaticElapsed = (performance.now() - startStatic).toFixed(0);
|
|
357
377
|
if (staticDone > 0) {
|
|
358
|
-
state.
|
|
378
|
+
state.staticManifestEntries = manifestEntries;
|
|
359
379
|
}
|
|
360
380
|
const staticParts = [`${staticDone} done`];
|
|
361
381
|
if (staticSkip > 0) staticParts.push(`${staticSkip} skipped`);
|
|
@@ -56,11 +56,8 @@ export interface DiscoveryState {
|
|
|
56
56
|
perRouterPrecomputedMap: Map<string, PrecomputedEntry[]>;
|
|
57
57
|
perRouterManifestDataMap: Map<string, Record<string, string>>;
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
string,
|
|
62
|
-
{ encoded: string; handles: Record<string, unknown[]> }
|
|
63
|
-
> | null;
|
|
59
|
+
prerenderManifestEntries: Record<string, string> | null;
|
|
60
|
+
staticManifestEntries: Record<string, string> | null;
|
|
64
61
|
handlerChunkInfo: ChunkInfo | null;
|
|
65
62
|
staticHandlerChunkInfo: ChunkInfo | null;
|
|
66
63
|
rscEntryFileName: string | null;
|
|
@@ -96,8 +93,8 @@ export function createDiscoveryState(
|
|
|
96
93
|
perRouterPrecomputedMap: new Map(),
|
|
97
94
|
perRouterManifestDataMap: new Map(),
|
|
98
95
|
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
prerenderManifestEntries: null,
|
|
97
|
+
staticManifestEntries: null,
|
|
101
98
|
handlerChunkInfo: null,
|
|
102
99
|
staticHandlerChunkInfo: null,
|
|
103
100
|
rscEntryFileName: null,
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
generatePerRouterModule,
|
|
43
43
|
} from "./discovery/virtual-module-codegen.js";
|
|
44
44
|
import { postprocessBundle } from "./discovery/bundle-postprocess.js";
|
|
45
|
+
import { resetStagedBuildAssets } from "./utils/prerender-utils.js";
|
|
45
46
|
|
|
46
47
|
export { VIRTUAL_ROUTES_MANIFEST_ID };
|
|
47
48
|
|
|
@@ -604,6 +605,9 @@ export function createRouterDiscoveryPlugin(
|
|
|
604
605
|
if (!s.isBuildMode) return;
|
|
605
606
|
// Only run once across environment builds
|
|
606
607
|
if (s.mergedRouteManifest !== null) return;
|
|
608
|
+
resetStagedBuildAssets(s.projectRoot);
|
|
609
|
+
s.prerenderManifestEntries = null;
|
|
610
|
+
s.staticManifestEntries = null;
|
|
607
611
|
|
|
608
612
|
let tempServer: any = null;
|
|
609
613
|
// Signal to user-space code (e.g. reverse.ts) that build-time discovery
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
copyFileSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
statSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from "node:fs";
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
|
|
1
12
|
/**
|
|
2
13
|
* Escape special RegExp characters in a string for safe interpolation
|
|
3
14
|
* into new RegExp() patterns.
|
|
@@ -127,3 +138,52 @@ export function notifyOnError(
|
|
|
127
138
|
break; // Only notify the first router with onError
|
|
128
139
|
}
|
|
129
140
|
}
|
|
141
|
+
|
|
142
|
+
function getStagedAssetDir(projectRoot: string): string {
|
|
143
|
+
return resolve(projectRoot, "node_modules/.rangojs-router-build/rsc-assets");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function resetStagedBuildAssets(projectRoot: string): void {
|
|
147
|
+
rmSync(getStagedAssetDir(projectRoot), { recursive: true, force: true });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function stageBuildAssetModule(
|
|
151
|
+
projectRoot: string,
|
|
152
|
+
prefix: "__pr" | "__st",
|
|
153
|
+
exportValue: string,
|
|
154
|
+
): string {
|
|
155
|
+
const stagedDir = getStagedAssetDir(projectRoot);
|
|
156
|
+
mkdirSync(stagedDir, { recursive: true });
|
|
157
|
+
|
|
158
|
+
const contentHash = createHash("sha256")
|
|
159
|
+
.update(exportValue)
|
|
160
|
+
.digest("hex")
|
|
161
|
+
.slice(0, 8);
|
|
162
|
+
const fileName = `${prefix}-${contentHash}.js`;
|
|
163
|
+
const filePath = resolve(stagedDir, fileName);
|
|
164
|
+
|
|
165
|
+
if (!existsSync(filePath)) {
|
|
166
|
+
writeFileSync(filePath, `export default ${exportValue};\n`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return fileName;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function copyStagedBuildAssets(
|
|
173
|
+
projectRoot: string,
|
|
174
|
+
fileNames: Iterable<string>,
|
|
175
|
+
): number {
|
|
176
|
+
const stagedDir = getStagedAssetDir(projectRoot);
|
|
177
|
+
const distAssetsDir = resolve(projectRoot, "dist/rsc/assets");
|
|
178
|
+
mkdirSync(distAssetsDir, { recursive: true });
|
|
179
|
+
|
|
180
|
+
let totalBytes = 0;
|
|
181
|
+
for (const fileName of new Set(fileNames)) {
|
|
182
|
+
const stagedPath = resolve(stagedDir, fileName);
|
|
183
|
+
const distPath = resolve(distAssetsDir, fileName);
|
|
184
|
+
copyFileSync(stagedPath, distPath);
|
|
185
|
+
totalBytes += statSync(stagedPath).size;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return totalBytes;
|
|
189
|
+
}
|