@rangojs/router 0.0.0-experimental.133 → 0.0.0-experimental.135
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/bin/rango.js +7 -2
- package/dist/vite/index.js +41 -27
- package/package.json +23 -24
- package/skills/composability/SKILL.md +0 -1
- package/skills/handler-use/SKILL.md +7 -7
- package/skills/intercept/SKILL.md +38 -13
- package/skills/loader/SKILL.md +10 -0
- package/skills/migrate-nextjs/SKILL.md +3 -3
- package/skills/migrate-react-router/SKILL.md +144 -1
- package/skills/prerender/SKILL.md +20 -17
- package/skills/router-setup/SKILL.md +1 -2
- package/skills/testing/SKILL.md +1 -0
- package/skills/testing/render-handler.md +15 -14
- package/skills/use-cache/SKILL.md +11 -0
- package/skills/view-transitions/SKILL.md +43 -0
- package/src/browser/navigation-bridge.ts +65 -16
- package/src/browser/navigation-client.ts +27 -1
- package/src/browser/navigation-store.ts +82 -8
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +43 -3
- package/src/browser/prefetch/cache.ts +8 -0
- package/src/browser/prefetch/fetch.ts +32 -4
- package/src/browser/react/NavigationProvider.tsx +195 -4
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- package/src/browser/response-adapter.ts +38 -9
- package/src/browser/types.ts +32 -1
- package/src/cache/cache-runtime.ts +26 -5
- package/src/cache/document-cache.ts +17 -1
- package/src/cache/profile-registry.ts +15 -0
- package/src/cache/read-through-swr.ts +15 -1
- package/src/handles/MetaTags.tsx +6 -0
- package/src/index.rsc.ts +6 -1
- package/src/index.ts +6 -4
- package/src/internal-debug.ts +11 -8
- package/src/render-error-thrower.tsx +20 -0
- package/src/route-content-wrapper.tsx +12 -5
- package/src/route-definition/dsl-helpers.ts +21 -32
- package/src/route-definition/helper-factories.ts +0 -2
- package/src/route-definition/helpers-types.ts +38 -39
- package/src/route-definition/index.ts +1 -2
- package/src/route-definition/resolve-handler-use.ts +0 -1
- package/src/route-definition/use-item-types.ts +3 -6
- package/src/route-types.ts +0 -5
- package/src/router/match-api.ts +5 -1
- package/src/router/match-middleware/background-revalidation.ts +40 -23
- package/src/router/match-middleware/cache-store.ts +39 -24
- package/src/router/segment-resolution/fresh.ts +4 -0
- package/src/router/segment-resolution/loader-cache.ts +14 -2
- package/src/router/segment-resolution/revalidation.ts +3 -0
- package/src/router/segment-resolution/view-transition-default.ts +35 -15
- package/src/rsc/progressive-enhancement.ts +56 -2
- package/src/rsc/rsc-rendering.ts +7 -2
- package/src/rsc/server-action.ts +25 -2
- package/src/rsc/transition-gate.ts +89 -0
- package/src/segment-system.tsx +59 -8
- package/src/server/context.ts +13 -0
- package/src/server/loader-registry.ts +13 -1
- package/src/server/request-context.ts +52 -3
- package/src/testing/index.ts +6 -0
- package/src/testing/render-handler.ts +14 -0
- package/src/testing/run-transition-when.ts +164 -0
- package/src/types/handler-context.ts +1 -1
- package/src/types/index.ts +2 -0
- package/src/types/segments.ts +100 -0
- package/src/urls/path-helper-types.ts +10 -7
- package/src/urls/urls-function.ts +0 -1
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugins/version-injector.ts +22 -7
- package/src/vite/plugins/virtual-entries.ts +28 -9
- package/src/vite/router-discovery.ts +8 -13
- package/src/network-error-thrower.tsx +0 -18
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bake the resolved INTERNAL_RANGO_DEBUG value into the router's `internal-debug`
|
|
3
|
+
* module so the flag reaches the CLIENT debug logs by just setting the env var.
|
|
4
|
+
*
|
|
5
|
+
* internal-debug.ts normally reads the flag via `typeof __RANGO_DEBUG__`, a Vite
|
|
6
|
+
* define. That delivery is unreliable on the client: in dev Vite ships the define
|
|
7
|
+
* only as an injected global whose presence varies across consumer setups, so the
|
|
8
|
+
* module can fall through to `process.env` (undefined in the browser) and the FE
|
|
9
|
+
* debug flag silently stays false while the server logs work. A Vite `transform`
|
|
10
|
+
* runs on the module regardless of how (or whether) the define is delivered, in
|
|
11
|
+
* both dev and build and for every environment, so the discovery plugin uses this
|
|
12
|
+
* to replace the module with the resolved literal.
|
|
13
|
+
*
|
|
14
|
+
* Returns null for any module that is not the router's internal-debug module.
|
|
15
|
+
*/
|
|
16
|
+
export function injectClientDebugFlag(
|
|
17
|
+
id: string,
|
|
18
|
+
): { code: string; map: null } | null {
|
|
19
|
+
// Cheap early-out: this hook runs on every module in every environment.
|
|
20
|
+
if (!id.includes("internal-debug")) return null;
|
|
21
|
+
const norm = id.replace(/\\/g, "/");
|
|
22
|
+
// Scope to the router's own internal-debug module: the published package
|
|
23
|
+
// (`/@rangojs/router/`, incl. pnpm's nested layout) or the monorepo workspace
|
|
24
|
+
// (`/packages/rangojs-router/`). The package-anchored path avoids matching a
|
|
25
|
+
// consumer file that merely sits under a directory named `rangojs-router`.
|
|
26
|
+
const isInternalDebug =
|
|
27
|
+
/\/internal-debug\.[cm]?[jt]sx?(\?|$)/.test(norm) &&
|
|
28
|
+
(norm.includes("/@rangojs/router/") ||
|
|
29
|
+
norm.includes("/packages/rangojs-router/"));
|
|
30
|
+
if (!isInternalDebug) return null;
|
|
31
|
+
// Emit the whole module: internal-debug.ts has a single export, kept in sync.
|
|
32
|
+
return {
|
|
33
|
+
code: `export const INTERNAL_RANGO_DEBUG = ${!!process.env.INTERNAL_RANGO_DEBUG};\n`,
|
|
34
|
+
map: null,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -2,12 +2,25 @@ import type { Plugin } from "vite";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import * as Vite from "vite";
|
|
4
4
|
import { resolveRscEntryFromConfig } from "../utils/shared-utils.js";
|
|
5
|
+
import { RSC_ENTRY_BOOTSTRAP_IMPORTS } from "./virtual-entries.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* Plugin that auto-injects VERSION and
|
|
8
|
-
* If a custom entry.rsc file uses createRSCHandler but
|
|
9
|
-
* this transform adds the import and property
|
|
10
|
-
*
|
|
8
|
+
* Plugin that auto-injects VERSION, routes-manifest, and loader-manifest into
|
|
9
|
+
* custom entry.rsc files. If a custom entry.rsc file uses createRSCHandler but
|
|
10
|
+
* doesn't pass version, this transform adds the import and property
|
|
11
|
+
* automatically. It also ensures the routes-manifest and loader-manifest
|
|
12
|
+
* virtual modules are always imported.
|
|
13
|
+
*
|
|
14
|
+
* The loader-manifest import is what makes fetchable loaders resolvable on a
|
|
15
|
+
* custom worker entry. The virtual RSC entry (getVirtualEntryRSC) imports the
|
|
16
|
+
* loader manifest itself, but a hand-written entry (e.g. a Cloudflare
|
|
17
|
+
* worker.rsc.tsx) does not — so without this injection, setLoaderImports() is
|
|
18
|
+
* never bundled, lazyLoaderImports stays null at runtime, and a fetchable
|
|
19
|
+
* loader that is never imported by the server graph (not registered via
|
|
20
|
+
* loader(), reachable only through a client component) cannot be found by the
|
|
21
|
+
* _rsc_loader endpoint in production. Dev still works because it resolves
|
|
22
|
+
* loaders by parsing their id into a file path; only production relied on the
|
|
23
|
+
* manifest, hence the production-only failure this fixes.
|
|
11
24
|
* @internal
|
|
12
25
|
*/
|
|
13
26
|
export function createVersionInjectorPlugin(
|
|
@@ -36,9 +49,11 @@ export function createVersionInjectorPlugin(
|
|
|
36
49
|
return null;
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
]
|
|
52
|
+
// Same startup bootstrap imports the generated virtual RSC entry uses,
|
|
53
|
+
// from the single shared list so the two paths cannot drift.
|
|
54
|
+
const prepend: string[] = RSC_ENTRY_BOOTSTRAP_IMPORTS.map(
|
|
55
|
+
(id) => `import "${id}";`,
|
|
56
|
+
);
|
|
42
57
|
|
|
43
58
|
let newCode = code;
|
|
44
59
|
const needsVersion =
|
|
@@ -51,7 +51,32 @@ export const renderHTML = createSSRHandler({
|
|
|
51
51
|
});
|
|
52
52
|
`.trim();
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Virtual modules an RSC entry must import at startup to register the data the
|
|
56
|
+
* request handler needs before the first request arrives:
|
|
57
|
+
*
|
|
58
|
+
* - routes-manifest: the pre-generated route map so href()/matching work on a
|
|
59
|
+
* cold start (full map in build, in-memory no-op in dev).
|
|
60
|
+
* - loader-manifest: setLoaderImports() for fetchable loaders. Critical for
|
|
61
|
+
* serverless/multi-process deployments and for fetchable loaders reachable
|
|
62
|
+
* only through a client component — without it those loaders are never
|
|
63
|
+
* registered for the _rsc_loader endpoint and fail in production.
|
|
64
|
+
*
|
|
65
|
+
* Single source of truth: both the generated virtual RSC entry below and the
|
|
66
|
+
* custom-entry injector (version-injector) consume this list, so a new
|
|
67
|
+
* bootstrap manifest cannot be added to one path and forgotten on the other.
|
|
68
|
+
* That exact drift (loader-manifest present here but missing from the injector)
|
|
69
|
+
* is what left fetchable loaders unresolved on custom worker entries.
|
|
70
|
+
*/
|
|
71
|
+
export const RSC_ENTRY_BOOTSTRAP_IMPORTS: readonly string[] = [
|
|
72
|
+
"virtual:rsc-router/routes-manifest",
|
|
73
|
+
"virtual:rsc-router/loader-manifest",
|
|
74
|
+
];
|
|
75
|
+
|
|
54
76
|
export function getVirtualEntryRSC(routerPath: string): string {
|
|
77
|
+
const bootstrapImports = RSC_ENTRY_BOOTSTRAP_IMPORTS.map(
|
|
78
|
+
(id) => `import "${id}";`,
|
|
79
|
+
).join("\n");
|
|
55
80
|
return `
|
|
56
81
|
import {
|
|
57
82
|
renderToReadableStream,
|
|
@@ -65,15 +90,9 @@ import { router } from "${routerPath}";
|
|
|
65
90
|
import { createRSCHandler } from "@rangojs/router/internal/rsc-handler";
|
|
66
91
|
import { VERSION } from "@rangojs/router:version";
|
|
67
92
|
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
import "virtual:rsc-router/loader-manifest";
|
|
72
|
-
|
|
73
|
-
// Import pre-generated route manifest so href() works immediately on cold start.
|
|
74
|
-
// In build mode, this contains the full route map generated at build time.
|
|
75
|
-
// In dev mode, this is a no-op (manifest is populated in-memory by the discovery plugin).
|
|
76
|
-
import "virtual:rsc-router/routes-manifest";
|
|
93
|
+
// Startup bootstrap imports (routes + loader manifests). See
|
|
94
|
+
// RSC_ENTRY_BOOTSTRAP_IMPORTS — the same list the custom-entry injector uses.
|
|
95
|
+
${bootstrapImports}
|
|
77
96
|
|
|
78
97
|
// Lazily create the handler on first request so that ESM live bindings
|
|
79
98
|
// have resolved by the time we read \`router\`. During HMR the module may
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
createScanFilter,
|
|
20
20
|
} from "../build/generate-route-types.js";
|
|
21
21
|
import { firstCodeMatchIndex } from "../build/route-types/source-scan.js";
|
|
22
|
+
import { injectClientDebugFlag } from "./inject-client-debug.js";
|
|
22
23
|
import { createVersionPlugin } from "./plugins/version-plugin.js";
|
|
23
24
|
import { createVirtualStubPlugin } from "./plugins/virtual-stub-plugin.js";
|
|
24
25
|
import {
|
|
@@ -310,19 +311,13 @@ export function createRouterDiscoveryPlugin(
|
|
|
310
311
|
return {
|
|
311
312
|
name: "@rangojs/router:discovery",
|
|
312
313
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
// rest of the RSC entry. A previous design forced them into dedicated
|
|
321
|
-
// __prerender-handlers / __static-handlers chunks via manualChunks,
|
|
322
|
-
// but Rollup hoisted all shared dependencies into those chunks,
|
|
323
|
-
// inflating them to ~1 MB with active runtime code. Handler code is
|
|
324
|
-
// evicted in closeBundle regardless of which chunk it lands in.
|
|
325
|
-
return config;
|
|
314
|
+
// Make INTERNAL_RANGO_DEBUG reach the CLIENT debug logs by just setting the
|
|
315
|
+
// env var. See injectClientDebugFlag: bakes the resolved flag into the
|
|
316
|
+
// internal-debug module so FE debug no longer depends on Vite delivering the
|
|
317
|
+
// `__RANGO_DEBUG__` define to the client (which it does only as an injected
|
|
318
|
+
// global whose presence varies across consumer setups). Runs in dev and build.
|
|
319
|
+
transform(_code, id) {
|
|
320
|
+
return injectClientDebugFlag(id);
|
|
326
321
|
},
|
|
327
322
|
|
|
328
323
|
configResolved(config) {
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import type { ReactNode } from "react";
|
|
4
|
-
import type { NetworkError } from "./errors.js";
|
|
5
|
-
|
|
6
|
-
interface NetworkErrorThrowerProps {
|
|
7
|
-
error: NetworkError;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Client component that throws a NetworkError during render.
|
|
12
|
-
* Errors thrown during render are caught by error boundaries; async errors are not.
|
|
13
|
-
*/
|
|
14
|
-
export function NetworkErrorThrower({
|
|
15
|
-
error,
|
|
16
|
-
}: NetworkErrorThrowerProps): ReactNode {
|
|
17
|
-
throw error;
|
|
18
|
-
}
|