@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.26
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 +294 -28
- package/dist/bin/rango.js +355 -47
- package/dist/vite/index.js +1658 -1239
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +9 -5
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +229 -15
- package/skills/middleware/SKILL.md +109 -30
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +189 -19
- package/skills/rango/SKILL.md +1 -2
- package/skills/response-routes/SKILL.md +3 -3
- package/skills/route/SKILL.md +44 -3
- package/skills/router-setup/SKILL.md +80 -3
- package/skills/theme/SKILL.md +5 -4
- package/skills/typesafety/SKILL.md +59 -16
- package/skills/use-cache/SKILL.md +16 -2
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +56 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +29 -48
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +19 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +66 -443
- package/src/browser/navigation-client.ts +34 -62
- package/src/browser/navigation-store.ts +4 -33
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/partial-update.ts +103 -151
- package/src/browser/prefetch/cache.ts +67 -0
- package/src/browser/prefetch/fetch.ts +137 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +154 -44
- package/src/browser/react/NavigationProvider.tsx +32 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +29 -11
- package/src/browser/react/location-state.ts +6 -4
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +23 -45
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +21 -64
- package/src/browser/react/use-navigation.ts +7 -32
- package/src/browser/react/use-params.ts +5 -34
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +3 -6
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +75 -114
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +46 -22
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +458 -405
- package/src/browser/types.ts +21 -35
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +38 -13
- package/src/build/generate-route-types.ts +4 -0
- package/src/build/index.ts +1 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +170 -18
- package/src/build/runtime-discovery.ts +13 -1
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +136 -123
- package/src/cache/cache-scope.ts +76 -83
- package/src/cache/cf/cf-cache-store.ts +12 -7
- package/src/cache/document-cache.ts +93 -69
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +43 -69
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +140 -117
- package/src/cache/taint.ts +30 -3
- package/src/cache/types.ts +1 -115
- package/src/client.rsc.tsx +0 -1
- package/src/client.tsx +53 -76
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/index.ts +0 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +53 -10
- package/src/index.ts +73 -43
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +60 -18
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/index.ts +0 -3
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +96 -17
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +6 -11
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +62 -54
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +78 -10
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +34 -39
- package/src/router/middleware.ts +290 -130
- package/src/router/pattern-matching.ts +61 -10
- package/src/router/prerender-match.ts +36 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +158 -40
- package/src/router/router-options.ts +223 -1
- package/src/router/router-registry.ts +5 -2
- package/src/router/segment-resolution/fresh.ts +165 -242
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +102 -98
- package/src/router/segment-resolution/revalidation.ts +394 -272
- package/src/router/segment-resolution/static-store.ts +2 -2
- package/src/router/segment-resolution.ts +1 -3
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +20 -2
- package/src/router/types.ts +7 -1
- package/src/router.ts +203 -18
- package/src/rsc/handler-context.ts +13 -2
- package/src/rsc/handler.ts +489 -438
- package/src/rsc/helpers.ts +125 -5
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/manifest-init.ts +3 -2
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +245 -19
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +47 -43
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +166 -66
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +20 -2
- package/src/search-params.ts +38 -23
- package/src/server/context.ts +61 -7
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +84 -12
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +275 -49
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +67 -28
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +4 -18
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +6 -1
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +22 -0
- package/src/types/handler-context.ts +103 -16
- package/src/types/index.ts +1 -1
- package/src/types/loader-types.ts +9 -6
- package/src/types/route-config.ts +17 -26
- package/src/types/route-entry.ts +28 -0
- package/src/types/segments.ts +0 -5
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +29 -7
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +27 -9
- package/src/vite/discovery/bundle-postprocess.ts +32 -52
- package/src/vite/discovery/discover-routers.ts +52 -26
- package/src/vite/discovery/prerender-collection.ts +58 -41
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/state.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/index.ts +10 -51
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/expose-internal-ids.ts +4 -3
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/plugins/version-plugin.ts +188 -18
- package/src/vite/rango.ts +61 -36
- package/src/vite/router-discovery.ts +173 -100
- package/src/vite/utils/prerender-utils.ts +81 -0
- package/src/vite/utils/shared-utils.ts +19 -9
- package/skills/testing/SKILL.md +0 -226
- package/src/browser/lru-cache.ts +0 -61
- package/src/browser/react/prefetch.ts +0 -27
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/route-definition/route-function.ts +0 -119
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/{CLAUDE.md → AGENTS.md} +0 -0
package/src/vite/rango.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { PluginOption } from "vite";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
3
4
|
import { exposeActionId } from "./plugins/expose-action-id.js";
|
|
4
5
|
import {
|
|
5
6
|
exposeInternalIds,
|
|
6
7
|
exposeRouterId,
|
|
7
8
|
} from "./plugins/expose-internal-ids.js";
|
|
8
9
|
import { useCacheTransform } from "./plugins/use-cache-transform.js";
|
|
10
|
+
import { clientRefDedup } from "./plugins/client-ref-dedup.js";
|
|
9
11
|
import { VIRTUAL_IDS } from "./plugins/virtual-entries.js";
|
|
10
12
|
import {
|
|
11
13
|
getExcludeDeps,
|
|
@@ -70,8 +72,10 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
70
72
|
// Track RSC entry path for version injection
|
|
71
73
|
let rscEntryPath: string | null = null;
|
|
72
74
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
+
// Mutable ref for router path (node preset only).
|
|
76
|
+
// Set immediately when user-specified, or populated by the auto-discover
|
|
77
|
+
// config() hook using Vite's resolved root.
|
|
78
|
+
const routerRef: { path: string | undefined } = { path: undefined };
|
|
75
79
|
|
|
76
80
|
// Build-time prerendering is enabled for both presets.
|
|
77
81
|
// Collection runs in-process via the RSC dev environment runner during discoverRouters().
|
|
@@ -193,43 +197,54 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
193
197
|
// since SSR runs in workerd alongside RSC
|
|
194
198
|
plugins.push(
|
|
195
199
|
rsc({
|
|
196
|
-
|
|
197
|
-
return finalEntries;
|
|
198
|
-
},
|
|
200
|
+
entries: finalEntries,
|
|
199
201
|
serverHandler: false,
|
|
200
202
|
}) as PluginOption,
|
|
201
203
|
);
|
|
204
|
+
|
|
205
|
+
// Deduplicate client references from third-party packages in dev mode.
|
|
206
|
+
// Prevents module duplication when server components import "use client"
|
|
207
|
+
// packages that are also imported directly by client components.
|
|
208
|
+
plugins.push(clientRefDedup());
|
|
202
209
|
} else {
|
|
203
210
|
// Node preset: full RSC plugin integration
|
|
204
211
|
const nodeOptions = resolvedOptions as RangoNodeOptions;
|
|
205
|
-
routerPath = nodeOptions.router;
|
|
206
212
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
213
|
+
routerRef.path = nodeOptions.router;
|
|
214
|
+
|
|
215
|
+
// Auto-discover router using Vite's resolved root (not process.cwd())
|
|
216
|
+
if (!routerRef.path) {
|
|
217
|
+
plugins.push({
|
|
218
|
+
name: "@rangojs/router:auto-discover",
|
|
219
|
+
config(userConfig) {
|
|
220
|
+
if (routerRef.path) return;
|
|
221
|
+
const root = userConfig.root
|
|
222
|
+
? resolve(process.cwd(), userConfig.root)
|
|
223
|
+
: process.cwd();
|
|
224
|
+
const filter = createScanFilter(root, {
|
|
225
|
+
include: resolvedOptions.include,
|
|
226
|
+
exclude: resolvedOptions.exclude,
|
|
227
|
+
});
|
|
228
|
+
const candidates = findRouterFiles(root, filter);
|
|
229
|
+
if (candidates.length === 1) {
|
|
230
|
+
const abs = candidates[0];
|
|
231
|
+
routerRef.path = (
|
|
232
|
+
abs.startsWith(root) ? "./" + abs.slice(root.length + 1) : abs
|
|
233
|
+
).replaceAll("\\", "/");
|
|
234
|
+
} else if (candidates.length > 1) {
|
|
235
|
+
const list = candidates
|
|
236
|
+
.map(
|
|
237
|
+
(f) =>
|
|
238
|
+
" - " + (f.startsWith(root) ? f.slice(root.length + 1) : f),
|
|
239
|
+
)
|
|
240
|
+
.join("\n");
|
|
241
|
+
throw new Error(
|
|
242
|
+
`[rsc-router] Multiple routers found. Specify \`router\` to choose one:\n${list}`,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
// 0 found: routerRef.path stays undefined, warn at startup via discovery plugin
|
|
246
|
+
},
|
|
212
247
|
});
|
|
213
|
-
const candidates = findRouterFiles(process.cwd(), earlyFilter);
|
|
214
|
-
if (candidates.length === 1) {
|
|
215
|
-
// Convert absolute path to relative ./path
|
|
216
|
-
const abs = candidates[0];
|
|
217
|
-
const rel = abs.startsWith(process.cwd())
|
|
218
|
-
? "./" + abs.slice(process.cwd().length + 1)
|
|
219
|
-
: abs;
|
|
220
|
-
routerPath = rel;
|
|
221
|
-
} else if (candidates.length > 1) {
|
|
222
|
-
const cwd = process.cwd();
|
|
223
|
-
const list = candidates
|
|
224
|
-
.map(
|
|
225
|
-
(f) => " - " + (f.startsWith(cwd) ? f.slice(cwd.length + 1) : f),
|
|
226
|
-
)
|
|
227
|
-
.join("\n");
|
|
228
|
-
throw new Error(
|
|
229
|
-
`[rsc-router] Multiple routers found. Specify \`router\` to choose one:\n${list}`,
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
// 0 found: routerPath stays undefined, warn at startup via discovery plugin
|
|
233
248
|
}
|
|
234
249
|
|
|
235
250
|
const rscOption = nodeOptions.rsc ?? true;
|
|
@@ -373,8 +388,8 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
373
388
|
},
|
|
374
389
|
});
|
|
375
390
|
|
|
376
|
-
// Add virtual entries plugin
|
|
377
|
-
plugins.push(createVirtualEntriesPlugin(finalEntries,
|
|
391
|
+
// Add virtual entries plugin (RSC entry generated lazily from routerRef)
|
|
392
|
+
plugins.push(createVirtualEntriesPlugin(finalEntries, routerRef));
|
|
378
393
|
|
|
379
394
|
// Add the RSC plugin directly
|
|
380
395
|
// Cast to PluginOption to handle type differences between bundled vite types
|
|
@@ -384,6 +399,11 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
384
399
|
}) as PluginOption,
|
|
385
400
|
);
|
|
386
401
|
}
|
|
402
|
+
|
|
403
|
+
// Deduplicate client references from third-party packages in dev mode.
|
|
404
|
+
// Prevents module duplication when server components import "use client"
|
|
405
|
+
// packages that are also imported directly by client components.
|
|
406
|
+
plugins.push(clientRefDedup());
|
|
387
407
|
}
|
|
388
408
|
|
|
389
409
|
// Fix HMR for "use client" components.
|
|
@@ -451,10 +471,13 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
451
471
|
// Add version virtual module plugin for cache invalidation
|
|
452
472
|
plugins.push(createVersionPlugin());
|
|
453
473
|
|
|
454
|
-
// Entry path for discovery
|
|
455
|
-
//
|
|
474
|
+
// Entry path for discovery: user-specified value (if any) or undefined.
|
|
475
|
+
// Auto-discovered path is passed separately via routerRef.
|
|
456
476
|
// Cloudflare preset: deferred to configResolved (read from resolved Vite env config).
|
|
457
|
-
const discoveryEntryPath =
|
|
477
|
+
const discoveryEntryPath =
|
|
478
|
+
preset !== "cloudflare" ? routerRef.path : undefined;
|
|
479
|
+
// Ref for deferred auto-discovery (node preset only, undefined for cloudflare)
|
|
480
|
+
const discoveryRouterRef = preset !== "cloudflare" ? routerRef : undefined;
|
|
458
481
|
|
|
459
482
|
// Version injector: auto-injects VERSION and routes-manifest into custom entry.rsc files.
|
|
460
483
|
// Only applies when there's an explicit rscEntryPath or for cloudflare preset (resolved
|
|
@@ -472,8 +495,10 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
472
495
|
|
|
473
496
|
// Router discovery plugin for build-time manifest generation.
|
|
474
497
|
// For cloudflare, the entry is resolved lazily in configResolved from the RSC environment.
|
|
498
|
+
// For node, discoveryRouterRef provides the auto-discovered path when not user-specified.
|
|
475
499
|
plugins.push(
|
|
476
500
|
createRouterDiscoveryPlugin(discoveryEntryPath, {
|
|
501
|
+
routerPathRef: discoveryRouterRef,
|
|
477
502
|
enableBuildPrerender: prerenderEnabled,
|
|
478
503
|
staticRouteTypesGeneration: resolvedOptions.staticRouteTypesGeneration,
|
|
479
504
|
include: resolvedOptions.include,
|
|
@@ -11,6 +11,8 @@ import { createServer as createViteServer } from "vite";
|
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
import { readFileSync } from "node:fs";
|
|
13
13
|
import {
|
|
14
|
+
formatNestedRouterConflictError,
|
|
15
|
+
findNestedRouterConflict,
|
|
14
16
|
findRouterFiles,
|
|
15
17
|
createScanFilter,
|
|
16
18
|
} from "../build/generate-route-types.js";
|
|
@@ -25,6 +27,7 @@ import { extractHandlerExportsFromChunk } from "./utils/bundle-analysis.js";
|
|
|
25
27
|
import {
|
|
26
28
|
createDiscoveryState,
|
|
27
29
|
VIRTUAL_ROUTES_MANIFEST_ID,
|
|
30
|
+
type DiscoveryState,
|
|
28
31
|
type PluginOptions,
|
|
29
32
|
} from "./discovery/state.js";
|
|
30
33
|
import { consumeSelfGenWrite } from "./discovery/self-gen-tracking.js";
|
|
@@ -39,9 +42,59 @@ import {
|
|
|
39
42
|
generatePerRouterModule,
|
|
40
43
|
} from "./discovery/virtual-module-codegen.js";
|
|
41
44
|
import { postprocessBundle } from "./discovery/bundle-postprocess.js";
|
|
45
|
+
import { resetStagedBuildAssets } from "./utils/prerender-utils.js";
|
|
42
46
|
|
|
43
47
|
export { VIRTUAL_ROUTES_MANIFEST_ID };
|
|
44
48
|
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Temp Server Factory
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a minimal Vite server for router discovery.
|
|
55
|
+
*
|
|
56
|
+
* Both dev-mode prerender and build-mode discovery need a temp RSC server
|
|
57
|
+
* to import user router files via module runner. This factory centralizes
|
|
58
|
+
* the shared config and the mode-specific differences:
|
|
59
|
+
* - Dev: path-based IDs (no forceBuild), separate cacheDir
|
|
60
|
+
* - Build: hashed IDs (forceBuild), hashClientRefs for production bundles
|
|
61
|
+
*
|
|
62
|
+
* Returns the ViteDevServer instance. Callers access .environments.rsc as needed.
|
|
63
|
+
*/
|
|
64
|
+
async function createTempRscServer(
|
|
65
|
+
state: DiscoveryState,
|
|
66
|
+
options: { forceBuild?: boolean; cacheDir?: string } = {},
|
|
67
|
+
) {
|
|
68
|
+
const { default: rsc } = await import("@vitejs/plugin-rsc");
|
|
69
|
+
return createViteServer({
|
|
70
|
+
root: state.projectRoot,
|
|
71
|
+
configFile: false,
|
|
72
|
+
server: { middlewareMode: true },
|
|
73
|
+
appType: "custom",
|
|
74
|
+
logLevel: "silent",
|
|
75
|
+
resolve: { alias: state.userResolveAlias },
|
|
76
|
+
esbuild: { jsx: "automatic", jsxImportSource: "react" },
|
|
77
|
+
...(options.cacheDir && { cacheDir: options.cacheDir }),
|
|
78
|
+
plugins: [
|
|
79
|
+
rsc({
|
|
80
|
+
entries: {
|
|
81
|
+
client: "virtual:entry-client",
|
|
82
|
+
ssr: "virtual:entry-ssr",
|
|
83
|
+
rsc: state.resolvedEntryPath!,
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
// hashClientRefs only in build mode — production bundles need hashed refs
|
|
87
|
+
...(options.forceBuild ? [hashClientRefs(state.projectRoot)] : []),
|
|
88
|
+
createVersionPlugin(),
|
|
89
|
+
createVirtualStubPlugin(),
|
|
90
|
+
// Dev prerender must use dev-mode IDs (path-based) to match the workerd
|
|
91
|
+
// runtime. forceBuild produces hashed IDs for production bundle consistency.
|
|
92
|
+
exposeInternalIds(options.forceBuild ? { forceBuild: true } : undefined),
|
|
93
|
+
exposeRouterId(),
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
45
98
|
/**
|
|
46
99
|
* Plugin that discovers router instances at dev/build time via the RSC environment.
|
|
47
100
|
*
|
|
@@ -97,6 +150,12 @@ export function createRouterDiscoveryPlugin(
|
|
|
97
150
|
s.isBuildMode = config.command === "build";
|
|
98
151
|
// Capture user's resolve aliases for the temp server
|
|
99
152
|
s.userResolveAlias = config.resolve.alias;
|
|
153
|
+
// Node preset: pick up auto-discovered router path from the config() hook.
|
|
154
|
+
// The auto-discover plugin runs in config() using Vite's resolved root,
|
|
155
|
+
// populating the mutable ref before configResolved fires.
|
|
156
|
+
if (!s.resolvedEntryPath && opts?.routerPathRef?.path) {
|
|
157
|
+
s.resolvedEntryPath = opts.routerPathRef.path;
|
|
158
|
+
}
|
|
100
159
|
// Cloudflare preset: read entry from resolved environment config.
|
|
101
160
|
// The @cloudflare/vite-plugin reads wrangler config (toml/json/jsonc)
|
|
102
161
|
// and sets optimizeDeps.entries on the RSC environment.
|
|
@@ -179,32 +238,8 @@ export function createRouterDiscoveryPlugin(
|
|
|
179
238
|
return (prerenderTempServer.environments as any)?.rsc ?? null;
|
|
180
239
|
}
|
|
181
240
|
try {
|
|
182
|
-
|
|
183
|
-
prerenderTempServer = await createViteServer({
|
|
184
|
-
root: s.projectRoot,
|
|
185
|
-
configFile: false,
|
|
186
|
-
server: { middlewareMode: true },
|
|
187
|
-
appType: "custom",
|
|
188
|
-
logLevel: "silent",
|
|
241
|
+
prerenderTempServer = await createTempRscServer(s, {
|
|
189
242
|
cacheDir: "node_modules/.vite_prerender",
|
|
190
|
-
resolve: { alias: s.userResolveAlias },
|
|
191
|
-
esbuild: { jsx: "automatic", jsxImportSource: "react" },
|
|
192
|
-
plugins: [
|
|
193
|
-
rsc({
|
|
194
|
-
entries: {
|
|
195
|
-
client: "virtual:entry-client",
|
|
196
|
-
ssr: "virtual:entry-ssr",
|
|
197
|
-
rsc: s.resolvedEntryPath!,
|
|
198
|
-
},
|
|
199
|
-
}),
|
|
200
|
-
createVersionPlugin(),
|
|
201
|
-
createVirtualStubPlugin(),
|
|
202
|
-
// Dev prerender must use dev-mode IDs (path-based) to match the
|
|
203
|
-
// workerd runtime. forceBuild would produce hashed IDs causing
|
|
204
|
-
// handle data key mismatches when replayed into the runtime store.
|
|
205
|
-
exposeInternalIds(),
|
|
206
|
-
exposeRouterId(),
|
|
207
|
-
],
|
|
208
243
|
});
|
|
209
244
|
|
|
210
245
|
const tempRscEnv = (prerenderTempServer.environments as any)?.rsc;
|
|
@@ -260,10 +295,7 @@ export function createRouterDiscoveryPlugin(
|
|
|
260
295
|
serverMod.setManifestReadyPromise(discoveryPromise);
|
|
261
296
|
}
|
|
262
297
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// Save registry for the /__rsc_prerender endpoint (avoids creating a temp server)
|
|
266
|
-
mainRegistry = serverModAfterDiscovery?.RouterRegistry ?? null;
|
|
298
|
+
await discoverRouters(s, rscEnv);
|
|
267
299
|
|
|
268
300
|
// Store server origin for dev prerender endpoint (virtual module injection)
|
|
269
301
|
s.devServerOrigin = getDevServerOrigin();
|
|
@@ -275,37 +307,8 @@ export function createRouterDiscoveryPlugin(
|
|
|
275
307
|
// won't cause unnecessary HMR triggers.
|
|
276
308
|
writeRouteTypesFiles(s);
|
|
277
309
|
|
|
278
|
-
// Populate the route map in the RSC env
|
|
279
|
-
|
|
280
|
-
serverMod.setCachedManifest(s.mergedRouteManifest);
|
|
281
|
-
}
|
|
282
|
-
if (
|
|
283
|
-
s.mergedPrecomputedEntries &&
|
|
284
|
-
s.mergedPrecomputedEntries.length > 0 &&
|
|
285
|
-
serverMod?.setPrecomputedEntries
|
|
286
|
-
) {
|
|
287
|
-
serverMod.setPrecomputedEntries(s.mergedPrecomputedEntries);
|
|
288
|
-
}
|
|
289
|
-
if (s.mergedRouteTrie && serverMod?.setRouteTrie) {
|
|
290
|
-
serverMod.setRouteTrie(s.mergedRouteTrie);
|
|
291
|
-
}
|
|
292
|
-
// Populate per-router isolated data eagerly in dev (HMR).
|
|
293
|
-
// In production builds, per-router data is loaded lazily via import().
|
|
294
|
-
if (serverMod?.setRouterManifest) {
|
|
295
|
-
for (const [routerId, manifest] of s.perRouterManifestDataMap) {
|
|
296
|
-
serverMod.setRouterManifest(routerId, manifest);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
if (serverMod?.setRouterTrie) {
|
|
300
|
-
for (const [routerId, trie] of s.perRouterTrieMap) {
|
|
301
|
-
serverMod.setRouterTrie(routerId, trie);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
if (serverMod?.setRouterPrecomputedEntries) {
|
|
305
|
-
for (const [routerId, entries] of s.perRouterPrecomputedMap) {
|
|
306
|
-
serverMod.setRouterPrecomputedEntries(routerId, entries);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
310
|
+
// Populate the route map and per-router data in the RSC env
|
|
311
|
+
await propagateDiscoveryState(rscEnv);
|
|
309
312
|
} catch (err: any) {
|
|
310
313
|
console.warn(
|
|
311
314
|
`[rsc-router] Router discovery failed: ${err.message}\n${err.stack}`,
|
|
@@ -333,6 +336,49 @@ export function createRouterDiscoveryPlugin(
|
|
|
333
336
|
// Registry from the main server's RSC environment (populated by discoverRouters)
|
|
334
337
|
let mainRegistry: Map<string, any> | null = null;
|
|
335
338
|
|
|
339
|
+
// Push discovery state (manifest, trie, precomputed entries) to the
|
|
340
|
+
// server module so runtime request handling uses the current routes.
|
|
341
|
+
// Shared by initial discovery and HMR-triggered re-discovery.
|
|
342
|
+
const propagateDiscoveryState = async (rscEnv: any) => {
|
|
343
|
+
const serverMod = await rscEnv.runner.import("@rangojs/router/server");
|
|
344
|
+
if (!serverMod) return;
|
|
345
|
+
// Clear stale per-router and global route data before repopulating.
|
|
346
|
+
// Without this, removed routers/routes survive in the per-router maps
|
|
347
|
+
// and shrunk precomputed entries or tries are never purged.
|
|
348
|
+
if (serverMod.clearAllRouterData) {
|
|
349
|
+
serverMod.clearAllRouterData();
|
|
350
|
+
}
|
|
351
|
+
mainRegistry = serverMod.RouterRegistry ?? null;
|
|
352
|
+
if (s.mergedRouteManifest && serverMod.setCachedManifest) {
|
|
353
|
+
serverMod.setCachedManifest(s.mergedRouteManifest);
|
|
354
|
+
}
|
|
355
|
+
if (
|
|
356
|
+
s.mergedPrecomputedEntries &&
|
|
357
|
+
s.mergedPrecomputedEntries.length > 0 &&
|
|
358
|
+
serverMod.setPrecomputedEntries
|
|
359
|
+
) {
|
|
360
|
+
serverMod.setPrecomputedEntries(s.mergedPrecomputedEntries);
|
|
361
|
+
}
|
|
362
|
+
if (s.mergedRouteTrie && serverMod.setRouteTrie) {
|
|
363
|
+
serverMod.setRouteTrie(s.mergedRouteTrie);
|
|
364
|
+
}
|
|
365
|
+
if (serverMod.setRouterManifest) {
|
|
366
|
+
for (const [routerId, manifest] of s.perRouterManifestDataMap) {
|
|
367
|
+
serverMod.setRouterManifest(routerId, manifest);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (serverMod.setRouterTrie) {
|
|
371
|
+
for (const [routerId, trie] of s.perRouterTrieMap) {
|
|
372
|
+
serverMod.setRouterTrie(routerId, trie);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (serverMod.setRouterPrecomputedEntries) {
|
|
376
|
+
for (const [routerId, entries] of s.perRouterPrecomputedMap) {
|
|
377
|
+
serverMod.setRouterPrecomputedEntries(routerId, entries);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
336
382
|
server.middlewares.use("/__rsc_prerender", async (req: any, res: any) => {
|
|
337
383
|
if (s.discoveryDone) await s.discoveryDone;
|
|
338
384
|
|
|
@@ -364,12 +410,24 @@ export function createRouterDiscoveryPlugin(
|
|
|
364
410
|
}
|
|
365
411
|
|
|
366
412
|
const wantIntercept = url.searchParams.get("intercept") === "1";
|
|
413
|
+
const wantRouteName = url.searchParams.get("routeName");
|
|
414
|
+
const wantPassthrough = url.searchParams.get("passthrough") === "1";
|
|
367
415
|
|
|
368
416
|
for (const [, routerInstance] of registry) {
|
|
369
417
|
if (!routerInstance.matchForPrerender) continue;
|
|
370
418
|
try {
|
|
371
|
-
const result = await routerInstance.matchForPrerender(
|
|
419
|
+
const result = await routerInstance.matchForPrerender(
|
|
420
|
+
pathname,
|
|
421
|
+
{},
|
|
422
|
+
undefined,
|
|
423
|
+
wantPassthrough,
|
|
424
|
+
);
|
|
372
425
|
if (!result) continue;
|
|
426
|
+
if (result.passthrough) continue;
|
|
427
|
+
// When routeName is specified, only accept a match for that route.
|
|
428
|
+
// This prevents returning the wrong entry when multiple routers
|
|
429
|
+
// have prerenderable routes sharing the same pathname.
|
|
430
|
+
if (wantRouteName && result.routeName !== wantRouteName) continue;
|
|
373
431
|
res.setHeader("content-type", "application/json");
|
|
374
432
|
let payload: Record<string, unknown>;
|
|
375
433
|
if (wantIntercept && result.interceptSegments?.length) {
|
|
@@ -417,6 +475,13 @@ export function createRouterDiscoveryPlugin(
|
|
|
417
475
|
): boolean => {
|
|
418
476
|
if (!isGeneratedRouteFile(filePath)) return false;
|
|
419
477
|
if (consumeSelfGenWrite(s, filePath)) return true;
|
|
478
|
+
// In Cloudflare dev (no module runner), perRouterManifests is never
|
|
479
|
+
// refreshed after HMR so regenerateGeneratedRouteFiles() would use
|
|
480
|
+
// stale data and revert user edits. Source files own route state;
|
|
481
|
+
// gen files are derived output. Skip regeneration and let the next
|
|
482
|
+
// source-file change rebuild them from the static parser.
|
|
483
|
+
const hasRunner = !!(server.environments as any)?.rsc?.runner;
|
|
484
|
+
if (!hasRunner) return true;
|
|
420
485
|
regenerateGeneratedRouteFiles();
|
|
421
486
|
return true;
|
|
422
487
|
};
|
|
@@ -427,6 +492,26 @@ export function createRouterDiscoveryPlugin(
|
|
|
427
492
|
// only the expensive regeneration is debounced.
|
|
428
493
|
let routeChangeTimer: ReturnType<typeof setTimeout> | undefined;
|
|
429
494
|
|
|
495
|
+
// Re-run runtime discovery so factory-generated routes that the
|
|
496
|
+
// static parser cannot see are refreshed after source changes.
|
|
497
|
+
let runtimeRediscoveryInProgress = false;
|
|
498
|
+
const refreshRuntimeDiscovery = async () => {
|
|
499
|
+
const rscEnv = (server.environments as any)?.rsc;
|
|
500
|
+
if (!rscEnv?.runner || runtimeRediscoveryInProgress) return;
|
|
501
|
+
runtimeRediscoveryInProgress = true;
|
|
502
|
+
try {
|
|
503
|
+
await discoverRouters(s, rscEnv);
|
|
504
|
+
writeRouteTypesFiles(s);
|
|
505
|
+
await propagateDiscoveryState(rscEnv);
|
|
506
|
+
} catch (err: any) {
|
|
507
|
+
console.warn(
|
|
508
|
+
`[rsc-router] Runtime re-discovery failed: ${err.message}`,
|
|
509
|
+
);
|
|
510
|
+
} finally {
|
|
511
|
+
runtimeRediscoveryInProgress = false;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
430
515
|
const scheduleRouteRegeneration = () => {
|
|
431
516
|
clearTimeout(routeChangeTimer);
|
|
432
517
|
routeChangeTimer = setTimeout(() => {
|
|
@@ -441,6 +526,15 @@ export function createRouterDiscoveryPlugin(
|
|
|
441
526
|
`[rsc-router] Route regeneration error: ${err.message}`,
|
|
442
527
|
);
|
|
443
528
|
}
|
|
529
|
+
// Async: re-run runtime discovery to refresh factory-generated
|
|
530
|
+
// routes that the static parser cannot resolve.
|
|
531
|
+
if (s.perRouterManifests.length > 0) {
|
|
532
|
+
refreshRuntimeDiscovery().catch((err: any) => {
|
|
533
|
+
console.warn(
|
|
534
|
+
`[rsc-router] Runtime re-discovery error: ${err.message}`,
|
|
535
|
+
);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
444
538
|
}, 100);
|
|
445
539
|
};
|
|
446
540
|
|
|
@@ -468,6 +562,16 @@ export function createRouterDiscoveryPlugin(
|
|
|
468
562
|
if (!hasUrls && !hasCreateRouter) return;
|
|
469
563
|
// Invalidate cache when a router file changes (new router added/removed)
|
|
470
564
|
if (hasCreateRouter) {
|
|
565
|
+
const nestedRouterConflict = findNestedRouterConflict([
|
|
566
|
+
...(s.cachedRouterFiles ?? []),
|
|
567
|
+
resolve(filePath),
|
|
568
|
+
]);
|
|
569
|
+
if (nestedRouterConflict) {
|
|
570
|
+
server.config.logger.error(
|
|
571
|
+
formatNestedRouterConflictError(nestedRouterConflict),
|
|
572
|
+
);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
471
575
|
s.cachedRouterFiles = undefined;
|
|
472
576
|
}
|
|
473
577
|
scheduleRouteRegeneration();
|
|
@@ -483,8 +587,12 @@ export function createRouterDiscoveryPlugin(
|
|
|
483
587
|
server.watcher.on("change", handleRouteFileChange);
|
|
484
588
|
|
|
485
589
|
// Regenerate gen files when they are deleted (e.g. manual cleanup).
|
|
590
|
+
// Same no-runner guard as change/add: stale perRouterManifests would
|
|
591
|
+
// reintroduce reverted content.
|
|
486
592
|
server.watcher.on("unlink", (filePath) => {
|
|
487
593
|
if (!isGeneratedRouteFile(filePath)) return;
|
|
594
|
+
const hasRunner = !!(server.environments as any)?.rsc?.runner;
|
|
595
|
+
if (!hasRunner) return;
|
|
488
596
|
regenerateGeneratedRouteFiles();
|
|
489
597
|
});
|
|
490
598
|
}
|
|
@@ -497,6 +605,9 @@ export function createRouterDiscoveryPlugin(
|
|
|
497
605
|
if (!s.isBuildMode) return;
|
|
498
606
|
// Only run once across environment builds
|
|
499
607
|
if (s.mergedRouteManifest !== null) return;
|
|
608
|
+
resetStagedBuildAssets(s.projectRoot);
|
|
609
|
+
s.prerenderManifestEntries = null;
|
|
610
|
+
s.staticManifestEntries = null;
|
|
500
611
|
|
|
501
612
|
let tempServer: any = null;
|
|
502
613
|
// Signal to user-space code (e.g. reverse.ts) that build-time discovery
|
|
@@ -505,45 +616,7 @@ export function createRouterDiscoveryPlugin(
|
|
|
505
616
|
// between the vite plugin and user code loaded via runner.import().
|
|
506
617
|
(globalThis as any).__rscRouterDiscoveryActive = true;
|
|
507
618
|
try {
|
|
508
|
-
|
|
509
|
-
// We bypass the user's config file because:
|
|
510
|
-
// - Custom environments (e.g., CloudflareDevEnvironment) may not expose
|
|
511
|
-
// a module runner compatible with runner.import()
|
|
512
|
-
// - The temp server only needs RSC conditions to import the router
|
|
513
|
-
const { default: rsc } = await import("@vitejs/plugin-rsc");
|
|
514
|
-
tempServer = await createViteServer({
|
|
515
|
-
root: s.projectRoot,
|
|
516
|
-
configFile: false,
|
|
517
|
-
server: { middlewareMode: true },
|
|
518
|
-
appType: "custom",
|
|
519
|
-
logLevel: "silent",
|
|
520
|
-
// Use the resolved aliases from the real config (includes user's path aliases
|
|
521
|
-
// like @/ -> src/ AND package aliases from rsc-router)
|
|
522
|
-
resolve: { alias: s.userResolveAlias },
|
|
523
|
-
// Enable automatic JSX runtime so .tsx files don't need `import React`.
|
|
524
|
-
// Without this, esbuild defaults to classic mode (React.createElement)
|
|
525
|
-
// which fails when lazy host-router handlers load sub-app modules with JSX.
|
|
526
|
-
esbuild: { jsx: "automatic", jsxImportSource: "react" },
|
|
527
|
-
plugins: [
|
|
528
|
-
rsc({
|
|
529
|
-
entries: {
|
|
530
|
-
client: "virtual:entry-client",
|
|
531
|
-
ssr: "virtual:entry-ssr",
|
|
532
|
-
rsc: s.resolvedEntryPath!,
|
|
533
|
-
},
|
|
534
|
-
}),
|
|
535
|
-
hashClientRefs(s.projectRoot),
|
|
536
|
-
createVersionPlugin(),
|
|
537
|
-
// Stub virtual modules that the RSC entry may import
|
|
538
|
-
// (e.g., virtual:rsc-router/routes-manifest, virtual:rsc-router/loader-manifest)
|
|
539
|
-
createVirtualStubPlugin(),
|
|
540
|
-
// Inject handle + router IDs so prerender-collected handle data uses
|
|
541
|
-
// the same hashed keys as the production client/SSR bundles, and
|
|
542
|
-
// build-time router IDs match runtime IDs across environments.
|
|
543
|
-
exposeInternalIds({ forceBuild: true }),
|
|
544
|
-
exposeRouterId(),
|
|
545
|
-
],
|
|
546
|
-
});
|
|
619
|
+
tempServer = await createTempRscServer(s, { forceBuild: true });
|
|
547
620
|
|
|
548
621
|
const rscEnv = (tempServer.environments as any)?.rsc;
|
|
549
622
|
if (!rscEnv?.runner) {
|
|
@@ -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.
|
|
@@ -17,6 +28,27 @@ export function encodePathParam(value: unknown): string {
|
|
|
17
28
|
.join("/");
|
|
18
29
|
}
|
|
19
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Substitute route params into a pattern, stripping constraint and optional
|
|
33
|
+
* syntax (:param(a|b)? -> value). Also handles wildcard params (*key).
|
|
34
|
+
*/
|
|
35
|
+
export function substituteRouteParams(
|
|
36
|
+
pattern: string,
|
|
37
|
+
params: Record<string, string>,
|
|
38
|
+
encode: (value: string) => string = encodeURIComponent,
|
|
39
|
+
): string {
|
|
40
|
+
let result = pattern;
|
|
41
|
+
for (const [key, value] of Object.entries(params)) {
|
|
42
|
+
const escaped = escapeRegExp(key);
|
|
43
|
+
result = result.replace(
|
|
44
|
+
new RegExp(`:${escaped}(\\([^)]*\\))?\\??`),
|
|
45
|
+
encode(value),
|
|
46
|
+
);
|
|
47
|
+
result = result.replace(`*${key}`, encode(value));
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
20
52
|
/**
|
|
21
53
|
* Run an async function over items with bounded concurrency.
|
|
22
54
|
* Errors propagate immediately and abort remaining work.
|
|
@@ -106,3 +138,52 @@ export function notifyOnError(
|
|
|
106
138
|
break; // Only notify the first router with onError
|
|
107
139
|
}
|
|
108
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
|
+
}
|