@rangojs/router 0.0.0-experimental.ede38110 → 0.0.0-experimental.f2d1a2f1
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 +50 -20
- package/dist/vite/index.js +353 -49
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +5 -3
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +28 -20
- package/skills/links/SKILL.md +88 -16
- package/skills/loader/SKILL.md +35 -2
- package/skills/migrate-react-router/SKILL.md +1 -0
- package/skills/response-routes/SKILL.md +8 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/typesafety/SKILL.md +3 -1
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/navigation-bridge.ts +51 -2
- package/src/browser/navigation-client.ts +33 -10
- package/src/browser/navigation-store.ts +25 -1
- package/src/browser/partial-update.ts +20 -1
- package/src/browser/prefetch/cache.ts +124 -26
- package/src/browser/prefetch/fetch.ts +114 -38
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +18 -13
- package/src/browser/react/NavigationProvider.tsx +50 -11
- package/src/browser/react/use-navigation.ts +30 -11
- package/src/browser/react/use-params.ts +11 -1
- package/src/browser/react/use-router.ts +8 -1
- package/src/browser/rsc-router.tsx +34 -6
- package/src/browser/types.ts +13 -0
- package/src/cache/cf/cf-cache-store.ts +5 -7
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/outlet-context.ts +1 -1
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +3 -2
- package/src/route-definition/dsl-helpers.ts +16 -3
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/router/handler-context.ts +20 -3
- package/src/router/lazy-includes.ts +1 -1
- package/src/router/loader-resolution.ts +3 -0
- package/src/router/match-api.ts +3 -3
- package/src/router/middleware-types.ts +2 -22
- package/src/router/middleware.ts +32 -4
- package/src/router/pattern-matching.ts +60 -9
- package/src/router/trie-matching.ts +10 -4
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +1 -2
- package/src/rsc/handler.ts +8 -4
- package/src/rsc/helpers.ts +69 -41
- package/src/rsc/progressive-enhancement.ts +2 -0
- package/src/rsc/response-route-handler.ts +14 -1
- package/src/rsc/rsc-rendering.ts +7 -0
- package/src/rsc/server-action.ts +2 -0
- package/src/server/request-context.ts +10 -42
- package/src/types/handler-context.ts +2 -34
- package/src/types/loader-types.ts +5 -6
- package/src/types/request-scope.ts +126 -0
- package/src/urls/response-types.ts +2 -10
- package/src/vite/debug.ts +86 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/performance-tracks.ts +4 -6
- package/src/vite/rango.ts +49 -14
- package/src/vite/router-discovery.ts +161 -23
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/package-resolution.ts +41 -1
|
@@ -10,7 +10,7 @@ import type { Plugin } from "vite";
|
|
|
10
10
|
import { createServer as createViteServer } from "vite";
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
import { readFileSync } from "node:fs";
|
|
13
|
-
import { createRequire } from "node:module";
|
|
13
|
+
import { createRequire, register } from "node:module";
|
|
14
14
|
import { pathToFileURL } from "node:url";
|
|
15
15
|
import {
|
|
16
16
|
formatNestedRouterConflictError,
|
|
@@ -19,6 +19,10 @@ import {
|
|
|
19
19
|
} from "../build/generate-route-types.js";
|
|
20
20
|
import { createVersionPlugin } from "./plugins/version-plugin.js";
|
|
21
21
|
import { createVirtualStubPlugin } from "./plugins/virtual-stub-plugin.js";
|
|
22
|
+
import {
|
|
23
|
+
BUILD_ENV_GLOBAL_KEY,
|
|
24
|
+
createCloudflareProtocolStubPlugin,
|
|
25
|
+
} from "./plugins/cloudflare-protocol-stub.js";
|
|
22
26
|
import {
|
|
23
27
|
exposeInternalIds,
|
|
24
28
|
exposeRouterId,
|
|
@@ -44,9 +48,56 @@ import {
|
|
|
44
48
|
} from "./discovery/virtual-module-codegen.js";
|
|
45
49
|
import { postprocessBundle } from "./discovery/bundle-postprocess.js";
|
|
46
50
|
import { resetStagedBuildAssets } from "./utils/prerender-utils.js";
|
|
51
|
+
import { createRangoDebugger, timed, timedSync } from "./debug.js";
|
|
52
|
+
|
|
53
|
+
const debugDiscovery = createRangoDebugger("rango:discovery");
|
|
54
|
+
const debugRoutes = createRangoDebugger("rango:routes");
|
|
47
55
|
|
|
48
56
|
export { VIRTUAL_ROUTES_MANIFEST_ID };
|
|
49
57
|
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Node ESM Loader Hook Registration
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Registers a Node ESM loader hook that resolves `cloudflare:*` specifiers
|
|
64
|
+
* to a data: URL stub. Defense-in-depth alongside the Vite transform in
|
|
65
|
+
* `cloudflare-protocol-stub.ts`:
|
|
66
|
+
*
|
|
67
|
+
* - The Vite transform catches `cloudflare:*` imports in modules that flow
|
|
68
|
+
* through Vite's plugin pipeline. That's the vast majority of cases.
|
|
69
|
+
* - The Node loader catches imports in modules that Vite/Rollup externalize
|
|
70
|
+
* (e.g. the `partyserver` package, which has a top-level
|
|
71
|
+
* `import { DurableObject, env } from "cloudflare:workers"` and ships
|
|
72
|
+
* shapes plugin-rsc marks as external). Externalized modules are loaded
|
|
73
|
+
* via Node's native ESM loader, which rejects URL schemes.
|
|
74
|
+
*
|
|
75
|
+
* Registration is process-global and one-shot. The hook only intercepts
|
|
76
|
+
* `cloudflare:*` specifiers; everything else passes through via
|
|
77
|
+
* `nextResolve()`. It runs in a separate worker thread (Node ESM loader
|
|
78
|
+
* architecture), so it can't read the `globalThis[BUILD_ENV_GLOBAL_KEY]`
|
|
79
|
+
* bridge that the Vite transform uses — the stubs served here always
|
|
80
|
+
* return `env = {}`. That's fine because externalized libraries don't
|
|
81
|
+
* typically access `env` at module top level; user source (where real
|
|
82
|
+
* `env` matters at build time) flows through the Vite transform.
|
|
83
|
+
*/
|
|
84
|
+
let loaderHookRegistered = false;
|
|
85
|
+
function ensureCloudflareProtocolLoaderRegistered(): void {
|
|
86
|
+
if (loaderHookRegistered) return;
|
|
87
|
+
loaderHookRegistered = true;
|
|
88
|
+
try {
|
|
89
|
+
register(
|
|
90
|
+
new URL("./plugins/cloudflare-protocol-loader-hook.mjs", import.meta.url),
|
|
91
|
+
);
|
|
92
|
+
} catch (err: any) {
|
|
93
|
+
// register() requires Node 18.19+ / 20.6+. Older Node still has the
|
|
94
|
+
// Vite transform as primary defense.
|
|
95
|
+
console.warn(
|
|
96
|
+
`[rsc-router] Could not register Node ESM loader hook for cloudflare:* imports (${err?.message ?? err}). Falling back to Vite transform only.`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
50
101
|
// ============================================================================
|
|
51
102
|
// Temp Server Factory
|
|
52
103
|
// ============================================================================
|
|
@@ -66,6 +117,11 @@ async function createTempRscServer(
|
|
|
66
117
|
state: DiscoveryState,
|
|
67
118
|
options: { forceBuild?: boolean; cacheDir?: string } = {},
|
|
68
119
|
) {
|
|
120
|
+
// Install the Node ESM loader hook before any module evaluation so
|
|
121
|
+
// `cloudflare:*` specifiers in externalized/loader-delegated modules
|
|
122
|
+
// (e.g. packages plugin-rsc marks as external) resolve to stubs
|
|
123
|
+
// instead of crashing Node's native loader.
|
|
124
|
+
ensureCloudflareProtocolLoaderRegistered();
|
|
69
125
|
const { default: rsc } = await import("@vitejs/plugin-rsc");
|
|
70
126
|
return createViteServer({
|
|
71
127
|
root: state.projectRoot,
|
|
@@ -88,6 +144,7 @@ async function createTempRscServer(
|
|
|
88
144
|
...(options.forceBuild ? [hashClientRefs(state.projectRoot)] : []),
|
|
89
145
|
createVersionPlugin(),
|
|
90
146
|
createVirtualStubPlugin(),
|
|
147
|
+
createCloudflareProtocolStubPlugin(),
|
|
91
148
|
// Dev prerender must use dev-mode IDs (path-based) to match the workerd
|
|
92
149
|
// runtime. forceBuild produces hashed IDs for production bundle consistency.
|
|
93
150
|
exposeInternalIds(options.forceBuild ? { forceBuild: true } : undefined),
|
|
@@ -177,6 +234,11 @@ async function acquireBuildEnv(
|
|
|
177
234
|
|
|
178
235
|
s.resolvedBuildEnv = result.env;
|
|
179
236
|
s.buildEnvDispose = result.dispose ?? null;
|
|
237
|
+
// Bridge the resolved env into `cloudflare:workers`'s stubbed `env`
|
|
238
|
+
// export so user code that does `import { env } from "cloudflare:workers"`
|
|
239
|
+
// sees the real bindings proxy during discovery + prerender instead of
|
|
240
|
+
// an empty object. The stub reads this global at module-evaluation time.
|
|
241
|
+
(globalThis as Record<string, unknown>)[BUILD_ENV_GLOBAL_KEY] = result.env;
|
|
180
242
|
return true;
|
|
181
243
|
}
|
|
182
244
|
|
|
@@ -193,6 +255,7 @@ async function releaseBuildEnv(s: DiscoveryState): Promise<void> {
|
|
|
193
255
|
s.buildEnvDispose = null;
|
|
194
256
|
}
|
|
195
257
|
s.resolvedBuildEnv = undefined;
|
|
258
|
+
delete (globalThis as Record<string, unknown>)[BUILD_ENV_GLOBAL_KEY];
|
|
196
259
|
}
|
|
197
260
|
|
|
198
261
|
/**
|
|
@@ -344,23 +407,35 @@ export function createRouterDiscoveryPlugin(
|
|
|
344
407
|
}
|
|
345
408
|
|
|
346
409
|
const discover = async () => {
|
|
410
|
+
const discoverStart = performance.now();
|
|
347
411
|
const rscEnv = (server.environments as any)?.rsc;
|
|
348
412
|
if (!rscEnv?.runner) {
|
|
349
413
|
// Cloudflare dev: no module runner available (workerd-based RSC env).
|
|
350
414
|
// Set devServerOrigin so the virtual module can inject __PRERENDER_DEV_URL
|
|
351
415
|
// for on-demand prerender via the /__rsc_prerender endpoint.
|
|
416
|
+
debugDiscovery?.("dev: no rsc runner (cloudflare path)");
|
|
352
417
|
s.devServerOrigin = getDevServerOrigin();
|
|
353
418
|
|
|
354
419
|
// Create a temp Node.js server to run runtime discovery and generate
|
|
355
420
|
// named route types (static parser can't resolve factory calls).
|
|
356
421
|
try {
|
|
357
422
|
// Acquire build-time env bindings for dev prerender
|
|
358
|
-
await
|
|
423
|
+
await timed(debugDiscovery, "acquireBuildEnv", () =>
|
|
424
|
+
acquireBuildEnv(s, viteCommand, viteMode),
|
|
425
|
+
);
|
|
359
426
|
|
|
360
|
-
const tempRscEnv = await
|
|
427
|
+
const tempRscEnv = await timed(
|
|
428
|
+
debugDiscovery,
|
|
429
|
+
"getOrCreateTempServer",
|
|
430
|
+
() => getOrCreateTempServer(),
|
|
431
|
+
);
|
|
361
432
|
if (tempRscEnv) {
|
|
362
|
-
await discoverRouters(
|
|
363
|
-
|
|
433
|
+
await timed(debugDiscovery, "discoverRouters (cloudflare)", () =>
|
|
434
|
+
discoverRouters(s, tempRscEnv),
|
|
435
|
+
);
|
|
436
|
+
timedSync(debugDiscovery, "writeRouteTypesFiles", () =>
|
|
437
|
+
writeRouteTypesFiles(s),
|
|
438
|
+
);
|
|
364
439
|
}
|
|
365
440
|
} catch (err: any) {
|
|
366
441
|
console.warn(
|
|
@@ -368,24 +443,35 @@ export function createRouterDiscoveryPlugin(
|
|
|
368
443
|
);
|
|
369
444
|
}
|
|
370
445
|
|
|
446
|
+
debugDiscovery?.(
|
|
447
|
+
"dev discovery done (%sms)",
|
|
448
|
+
(performance.now() - discoverStart).toFixed(1),
|
|
449
|
+
);
|
|
371
450
|
resolveDiscovery!();
|
|
372
451
|
return;
|
|
373
452
|
}
|
|
374
453
|
|
|
375
454
|
try {
|
|
376
455
|
// Acquire build-time env bindings for dev prerender (Node.js path)
|
|
377
|
-
|
|
456
|
+
debugDiscovery?.("dev: node path start");
|
|
457
|
+
await timed(debugDiscovery, "acquireBuildEnv", () =>
|
|
458
|
+
acquireBuildEnv(s, viteCommand, viteMode),
|
|
459
|
+
);
|
|
378
460
|
|
|
379
461
|
// Set the readiness gate BEFORE discovery so early requests
|
|
380
462
|
// block until manifest is populated
|
|
381
|
-
const serverMod = await
|
|
382
|
-
|
|
463
|
+
const serverMod = await timed(
|
|
464
|
+
debugDiscovery,
|
|
465
|
+
"import @rangojs/router/server",
|
|
466
|
+
() => rscEnv.runner.import("@rangojs/router/server"),
|
|
383
467
|
);
|
|
384
468
|
if (serverMod?.setManifestReadyPromise) {
|
|
385
469
|
serverMod.setManifestReadyPromise(discoveryPromise);
|
|
386
470
|
}
|
|
387
471
|
|
|
388
|
-
await
|
|
472
|
+
await timed(debugDiscovery, "discoverRouters", () =>
|
|
473
|
+
discoverRouters(s, rscEnv),
|
|
474
|
+
);
|
|
389
475
|
|
|
390
476
|
// Store server origin for dev prerender endpoint (virtual module injection)
|
|
391
477
|
s.devServerOrigin = getDevServerOrigin();
|
|
@@ -395,15 +481,23 @@ export function createRouterDiscoveryPlugin(
|
|
|
395
481
|
// routes (e.g. Array.from loops) that the static parser cannot see.
|
|
396
482
|
// writeRouteTypesFiles() only writes when content changes, so this
|
|
397
483
|
// won't cause unnecessary HMR triggers.
|
|
398
|
-
writeRouteTypesFiles(
|
|
484
|
+
timedSync(debugDiscovery, "writeRouteTypesFiles", () =>
|
|
485
|
+
writeRouteTypesFiles(s),
|
|
486
|
+
);
|
|
399
487
|
|
|
400
488
|
// Populate the route map and per-router data in the RSC env
|
|
401
|
-
await propagateDiscoveryState(
|
|
489
|
+
await timed(debugDiscovery, "propagateDiscoveryState", () =>
|
|
490
|
+
propagateDiscoveryState(rscEnv),
|
|
491
|
+
);
|
|
402
492
|
} catch (err: any) {
|
|
403
493
|
console.warn(
|
|
404
494
|
`[rsc-router] Router discovery failed: ${err.message}\n${err.stack}`,
|
|
405
495
|
);
|
|
406
496
|
} finally {
|
|
497
|
+
debugDiscovery?.(
|
|
498
|
+
"dev discovery done (%sms)",
|
|
499
|
+
(performance.now() - discoverStart).toFixed(1),
|
|
500
|
+
);
|
|
407
501
|
resolveDiscovery!();
|
|
408
502
|
}
|
|
409
503
|
};
|
|
@@ -613,15 +707,26 @@ export function createRouterDiscoveryPlugin(
|
|
|
613
707
|
const rscEnv = (server.environments as any)?.rsc;
|
|
614
708
|
if (!rscEnv?.runner || runtimeRediscoveryInProgress) return;
|
|
615
709
|
runtimeRediscoveryInProgress = true;
|
|
710
|
+
const hmrStart = performance.now();
|
|
616
711
|
try {
|
|
617
|
-
await
|
|
618
|
-
|
|
619
|
-
|
|
712
|
+
await timed(debugDiscovery, "hmr discoverRouters", () =>
|
|
713
|
+
discoverRouters(s, rscEnv),
|
|
714
|
+
);
|
|
715
|
+
timedSync(debugDiscovery, "hmr writeRouteTypesFiles", () =>
|
|
716
|
+
writeRouteTypesFiles(s),
|
|
717
|
+
);
|
|
718
|
+
await timed(debugDiscovery, "hmr propagateDiscoveryState", () =>
|
|
719
|
+
propagateDiscoveryState(rscEnv),
|
|
720
|
+
);
|
|
620
721
|
} catch (err: any) {
|
|
621
722
|
console.warn(
|
|
622
723
|
`[rsc-router] Runtime re-discovery failed: ${err.message}`,
|
|
623
724
|
);
|
|
624
725
|
} finally {
|
|
726
|
+
debugDiscovery?.(
|
|
727
|
+
"hmr re-discovery done (%sms)",
|
|
728
|
+
(performance.now() - hmrStart).toFixed(1),
|
|
729
|
+
);
|
|
625
730
|
runtimeRediscoveryInProgress = false;
|
|
626
731
|
}
|
|
627
732
|
};
|
|
@@ -719,12 +824,16 @@ export function createRouterDiscoveryPlugin(
|
|
|
719
824
|
if (!s.isBuildMode) return;
|
|
720
825
|
// Only run once across environment builds
|
|
721
826
|
if (s.mergedRouteManifest !== null) return;
|
|
827
|
+
const buildStartTime = performance.now();
|
|
828
|
+
debugDiscovery?.("build: start");
|
|
722
829
|
resetStagedBuildAssets(s.projectRoot);
|
|
723
830
|
s.prerenderManifestEntries = null;
|
|
724
831
|
s.staticManifestEntries = null;
|
|
725
832
|
|
|
726
833
|
// Acquire build-time env bindings if configured
|
|
727
|
-
await
|
|
834
|
+
await timed(debugDiscovery, "build acquireBuildEnv", () =>
|
|
835
|
+
acquireBuildEnv(s, viteCommand, viteMode),
|
|
836
|
+
);
|
|
728
837
|
|
|
729
838
|
let tempServer: any = null;
|
|
730
839
|
// Signal to user-space code (e.g. reverse.ts) that build-time discovery
|
|
@@ -733,7 +842,11 @@ export function createRouterDiscoveryPlugin(
|
|
|
733
842
|
// between the vite plugin and user code loaded via runner.import().
|
|
734
843
|
(globalThis as any).__rscRouterDiscoveryActive = true;
|
|
735
844
|
try {
|
|
736
|
-
tempServer = await
|
|
845
|
+
tempServer = await timed(
|
|
846
|
+
debugDiscovery,
|
|
847
|
+
"build createTempRscServer",
|
|
848
|
+
() => createTempRscServer(s, { forceBuild: true }),
|
|
849
|
+
);
|
|
737
850
|
|
|
738
851
|
const rscEnv = (tempServer.environments as any)?.rsc;
|
|
739
852
|
if (!rscEnv?.runner) {
|
|
@@ -753,11 +866,15 @@ export function createRouterDiscoveryPlugin(
|
|
|
753
866
|
s.resolvedStaticModules = tempIdsPlugin.api.staticHandlerModules;
|
|
754
867
|
}
|
|
755
868
|
|
|
756
|
-
await
|
|
869
|
+
await timed(debugDiscovery, "build discoverRouters", () =>
|
|
870
|
+
discoverRouters(s, rscEnv),
|
|
871
|
+
);
|
|
757
872
|
// Update named-routes.gen.ts from runtime discovery.
|
|
758
873
|
// The runtime manifest includes dynamically generated routes
|
|
759
874
|
// that the static parser cannot extract from source code.
|
|
760
|
-
writeRouteTypesFiles(
|
|
875
|
+
timedSync(debugDiscovery, "build writeRouteTypesFiles", () =>
|
|
876
|
+
writeRouteTypesFiles(s),
|
|
877
|
+
);
|
|
761
878
|
} catch (err: any) {
|
|
762
879
|
// Extract the user source file from the stack trace (skip internal frames)
|
|
763
880
|
const sourceFile = err.stack
|
|
@@ -782,9 +899,15 @@ export function createRouterDiscoveryPlugin(
|
|
|
782
899
|
} finally {
|
|
783
900
|
delete (globalThis as any).__rscRouterDiscoveryActive;
|
|
784
901
|
if (tempServer) {
|
|
785
|
-
await tempServer.close()
|
|
902
|
+
await timed(debugDiscovery, "build tempServer.close", () =>
|
|
903
|
+
tempServer.close(),
|
|
904
|
+
);
|
|
786
905
|
}
|
|
787
906
|
await releaseBuildEnv(s);
|
|
907
|
+
debugDiscovery?.(
|
|
908
|
+
"build discovery done (%sms)",
|
|
909
|
+
(performance.now() - buildStartTime).toFixed(1),
|
|
910
|
+
);
|
|
788
911
|
}
|
|
789
912
|
},
|
|
790
913
|
|
|
@@ -808,19 +931,34 @@ export function createRouterDiscoveryPlugin(
|
|
|
808
931
|
// This is critical for Cloudflare dev where the worker runs in a separate
|
|
809
932
|
// Miniflare process and can only receive manifest data via the virtual module.
|
|
810
933
|
if (s.discoveryDone) {
|
|
811
|
-
await
|
|
934
|
+
await timed(debugRoutes, "await discoveryDone (manifest)", () =>
|
|
935
|
+
Promise.resolve(s.discoveryDone),
|
|
936
|
+
);
|
|
812
937
|
}
|
|
813
|
-
|
|
938
|
+
const code = await timed(
|
|
939
|
+
debugRoutes,
|
|
940
|
+
"generateRoutesManifestModule",
|
|
941
|
+
() => generateRoutesManifestModule(s),
|
|
942
|
+
);
|
|
943
|
+
debugRoutes?.("manifest module emitted (%d bytes)", code?.length ?? 0);
|
|
944
|
+
return code;
|
|
814
945
|
}
|
|
815
946
|
// Per-router virtual modules: pure data exports (no side effects).
|
|
816
947
|
// ensureRouterManifest() imports the module and stores the data.
|
|
817
948
|
const perRouterPrefix = "\0" + VIRTUAL_ROUTES_MANIFEST_ID + "/";
|
|
818
949
|
if (id.startsWith(perRouterPrefix)) {
|
|
819
950
|
if (s.discoveryDone) {
|
|
820
|
-
await
|
|
951
|
+
await timed(debugRoutes, "await discoveryDone (per-router)", () =>
|
|
952
|
+
Promise.resolve(s.discoveryDone),
|
|
953
|
+
);
|
|
821
954
|
}
|
|
822
955
|
const routerId = id.slice(perRouterPrefix.length);
|
|
823
|
-
|
|
956
|
+
const code = await timed(
|
|
957
|
+
debugRoutes,
|
|
958
|
+
`generatePerRouterModule ${routerId}`,
|
|
959
|
+
() => generatePerRouterModule(s, routerId),
|
|
960
|
+
);
|
|
961
|
+
return code;
|
|
824
962
|
}
|
|
825
963
|
// virtual:rsc-router/prerender-paths load handler removed
|
|
826
964
|
return null;
|
package/src/vite/utils/banner.ts
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { existsSync } from "node:fs";
|
|
9
|
+
import { createRequire } from "node:module";
|
|
9
10
|
import { resolve } from "node:path";
|
|
10
|
-
import packageJson from "../../../package.json"
|
|
11
|
+
import packageJson from "../../../package.json";
|
|
12
|
+
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* The canonical name used in virtual entries (without scope)
|
|
@@ -119,3 +122,40 @@ export function getPackageAliases(): Record<string, string> {
|
|
|
119
122
|
|
|
120
123
|
return aliases;
|
|
121
124
|
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Plugin-rsc pushes bare specs like
|
|
128
|
+
* `@vitejs/plugin-rsc/vendor/react-server-dom/client.edge` into
|
|
129
|
+
* `optimizeDeps.include` for the ssr and rsc environments. In strict pnpm
|
|
130
|
+
* consumer apps, `@vitejs/plugin-rsc` is only reachable from @rangojs/router's
|
|
131
|
+
* node_modules, so Vite's optimizer — which resolves from the project root —
|
|
132
|
+
* can't find them and emits "Failed to resolve dependency" warnings.
|
|
133
|
+
*
|
|
134
|
+
* We resolve those specs from this plugin's location (where plugin-rsc is
|
|
135
|
+
* guaranteed to be installed as our dep) and expose them as `resolve.alias`
|
|
136
|
+
* entries. The optimizer's resolver honors aliases, so the bare specs map to
|
|
137
|
+
* absolute paths and resolve cleanly.
|
|
138
|
+
*/
|
|
139
|
+
export function getVendorAliases(): Record<string, string> {
|
|
140
|
+
// client.browser is intentionally NOT aliased. plugin-rsc injects it into
|
|
141
|
+
// the client env's optimizeDeps.include; Vite's manual-include path resolves
|
|
142
|
+
// and pre-bundles regardless of optimizeDeps.exclude, so aliasing would
|
|
143
|
+
// trigger esbuild pre-bundling of the CJS vendor file and bypass the
|
|
144
|
+
// cjs-to-esm transform that patches `require('react'|'react-dom')` into
|
|
145
|
+
// real ESM imports. The consumer may still see a single "Failed to resolve"
|
|
146
|
+
// warning for client.browser; runtime resolution from plugin-rsc's own
|
|
147
|
+
// importer works because Vite resolves relative to the importer (not root).
|
|
148
|
+
const specs = [
|
|
149
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
|
|
150
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
|
|
151
|
+
];
|
|
152
|
+
const aliases: Record<string, string> = {};
|
|
153
|
+
for (const spec of specs) {
|
|
154
|
+
try {
|
|
155
|
+
aliases[spec] = require.resolve(spec);
|
|
156
|
+
} catch {
|
|
157
|
+
// Spec unresolvable (unexpected but non-fatal — Vite will warn as before).
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return aliases;
|
|
161
|
+
}
|