@rangojs/router 0.0.0-experimental.20 → 0.0.0-experimental.22
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 +43 -10
- package/dist/vite/index.js +130 -84
- package/package.json +1 -1
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +13 -12
- package/skills/host-router/SKILL.md +218 -0
- package/skills/loader/SKILL.md +2 -2
- package/skills/prerender/SKILL.md +2 -2
- package/skills/rango/SKILL.md +0 -1
- package/skills/router-setup/SKILL.md +2 -2
- package/skills/typesafety/SKILL.md +1 -1
- package/src/host/index.ts +0 -3
- package/src/index.ts +30 -31
- package/src/prerender/store.ts +56 -15
- package/src/route-definition/index.ts +0 -3
- package/src/router/router-options.ts +1 -1
- package/src/rsc/index.ts +0 -20
- package/src/server.ts +6 -0
- package/src/theme/index.ts +4 -13
- package/src/vite/discovery/bundle-postprocess.ts +31 -56
- package/src/vite/discovery/discover-routers.ts +2 -3
- 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/skills/testing/SKILL.md +0 -226
- package/src/route-definition/route-function.ts +0 -119
|
@@ -239,7 +239,7 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
239
239
|
*
|
|
240
240
|
* @example Static config
|
|
241
241
|
* ```typescript
|
|
242
|
-
* import { MemorySegmentCacheStore } from "
|
|
242
|
+
* import { MemorySegmentCacheStore } from "@rangojs/router/cache";
|
|
243
243
|
*
|
|
244
244
|
* const router = createRouter({
|
|
245
245
|
* cache: {
|
package/src/rsc/index.ts
CHANGED
|
@@ -29,28 +29,8 @@ export type {
|
|
|
29
29
|
NonceProvider,
|
|
30
30
|
} from "./types.js";
|
|
31
31
|
|
|
32
|
-
// Re-export HandleStore types for consumers who need custom handling
|
|
33
|
-
export {
|
|
34
|
-
createHandleStore,
|
|
35
|
-
type HandleStore,
|
|
36
|
-
type HandleData,
|
|
37
|
-
} from "../server/handle-store.js";
|
|
38
|
-
|
|
39
32
|
// Re-export request context utilities for server-side access to env/request/params
|
|
40
33
|
export {
|
|
41
34
|
getRequestContext,
|
|
42
35
|
requireRequestContext,
|
|
43
|
-
setRequestContextParams,
|
|
44
36
|
} from "../server/request-context.js";
|
|
45
|
-
|
|
46
|
-
// Re-export cache store types and implementations
|
|
47
|
-
export type {
|
|
48
|
-
SegmentCacheStore,
|
|
49
|
-
CachedEntryData,
|
|
50
|
-
CachedEntryResult,
|
|
51
|
-
SegmentCacheProvider,
|
|
52
|
-
SegmentHandleData,
|
|
53
|
-
} from "../cache/types.js";
|
|
54
|
-
|
|
55
|
-
export { MemorySegmentCacheStore } from "../cache/memory-segment-store.js";
|
|
56
|
-
export { CFCacheStore, type CFCacheStoreOptions } from "../cache/cf/index.js";
|
package/src/server.ts
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
// Router registry (used by Vite plugin for build-time discovery)
|
|
12
12
|
export { RSC_ROUTER_BRAND, RouterRegistry } from "./router.js";
|
|
13
13
|
|
|
14
|
+
// Host router registry (used by Vite plugin for host-router lazy discovery)
|
|
15
|
+
export {
|
|
16
|
+
HostRouterRegistry,
|
|
17
|
+
type HostRouterRegistryEntry,
|
|
18
|
+
} from "./host/router.js";
|
|
19
|
+
|
|
14
20
|
// Route map builder (Vite plugin injects these via virtual modules)
|
|
15
21
|
export {
|
|
16
22
|
registerRouteMap,
|
package/src/theme/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme module exports for @rangojs/router/theme
|
|
3
3
|
*
|
|
4
|
-
* This module provides
|
|
4
|
+
* This module provides the public theme API:
|
|
5
5
|
* - useTheme: Hook for accessing theme state in client components
|
|
6
6
|
* - ThemeProvider: Component for manual theme provider setup (typically not needed)
|
|
7
|
+
* - ThemeScript: FOUC-prevention script component for document/head usage
|
|
7
8
|
* - Types for theme configuration
|
|
8
9
|
*
|
|
9
10
|
* @example
|
|
@@ -43,15 +44,5 @@ export type {
|
|
|
43
44
|
ThemeContextValue,
|
|
44
45
|
} from "./types.js";
|
|
45
46
|
|
|
46
|
-
// Constants
|
|
47
|
-
export {
|
|
48
|
-
THEME_DEFAULTS,
|
|
49
|
-
THEME_COOKIE,
|
|
50
|
-
resolveThemeConfig,
|
|
51
|
-
} from "./constants.js";
|
|
52
|
-
|
|
53
|
-
// Script generation (for advanced SSR use cases)
|
|
54
|
-
export { generateThemeScript, getNonceAttribute } from "./theme-script.js";
|
|
55
|
-
|
|
56
|
-
// Context (for advanced use cases)
|
|
57
|
-
export { ThemeContext, useThemeContext } from "./theme-context.js";
|
|
47
|
+
// Constants
|
|
48
|
+
export { THEME_DEFAULTS, THEME_COOKIE } from "./constants.js";
|
|
@@ -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(
|
|
@@ -48,9 +48,8 @@ export async function discoverRouters(
|
|
|
48
48
|
// No RSC routers found directly. Check for host routers with lazy handlers
|
|
49
49
|
// that need to be resolved to trigger sub-app createRouter() calls.
|
|
50
50
|
try {
|
|
51
|
-
const hostMod = await rscEnv.runner.import("@rangojs/router/host");
|
|
52
51
|
const hostRegistry: Map<string, any> | undefined =
|
|
53
|
-
|
|
52
|
+
serverMod.HostRouterRegistry;
|
|
54
53
|
|
|
55
54
|
if (hostRegistry && hostRegistry.size > 0) {
|
|
56
55
|
console.log(
|
|
@@ -89,7 +88,7 @@ export async function discoverRouters(
|
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
} catch {
|
|
92
|
-
//
|
|
91
|
+
// Host-router discovery is best-effort; skip if unavailable
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
// If still no routers after host router resolution, fail
|
|
@@ -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
|
+
}
|