@rangojs/router 0.0.0-experimental.19 → 0.0.0-experimental.1b930379
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 +46 -12
- package/dist/bin/rango.js +109 -15
- package/dist/vite/index.js +323 -121
- package/package.json +15 -16
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +33 -31
- package/skills/host-router/SKILL.md +218 -0
- package/skills/loader/SKILL.md +55 -15
- package/skills/prerender/SKILL.md +2 -2
- package/skills/rango/SKILL.md +0 -1
- package/skills/route/SKILL.md +3 -4
- package/skills/router-setup/SKILL.md +8 -3
- package/skills/typesafety/SKILL.md +25 -23
- package/src/__internal.ts +92 -0
- package/src/bin/rango.ts +18 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +95 -5
- package/src/browser/navigation-client.ts +97 -72
- package/src/browser/prefetch/cache.ts +112 -25
- package/src/browser/prefetch/fetch.ts +28 -30
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/react/Link.tsx +19 -7
- package/src/browser/rsc-router.tsx +11 -2
- package/src/browser/server-action-bridge.ts +448 -432
- package/src/browser/types.ts +24 -0
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/router-processing.ts +125 -15
- package/src/client.rsc.tsx +2 -1
- package/src/client.tsx +1 -46
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/host/index.ts +0 -3
- package/src/index.rsc.ts +5 -36
- package/src/index.ts +32 -66
- package/src/prerender/store.ts +56 -15
- package/src/route-definition/index.ts +0 -3
- package/src/router/handler-context.ts +30 -3
- package/src/router/loader-resolution.ts +1 -1
- package/src/router/match-api.ts +1 -1
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +53 -10
- package/src/router/middleware.ts +170 -81
- package/src/router/pattern-matching.ts +20 -5
- package/src/router/prerender-match.ts +4 -0
- package/src/router/revalidation.ts +27 -7
- package/src/router/router-interfaces.ts +14 -1
- package/src/router/router-options.ts +13 -8
- package/src/router/segment-resolution/fresh.ts +18 -0
- package/src/router/segment-resolution/helpers.ts +1 -1
- package/src/router/segment-resolution/revalidation.ts +22 -9
- package/src/router/trie-matching.ts +20 -2
- package/src/router.ts +29 -9
- package/src/rsc/handler.ts +106 -11
- package/src/rsc/index.ts +0 -20
- package/src/rsc/progressive-enhancement.ts +21 -8
- package/src/rsc/rsc-rendering.ts +30 -43
- package/src/rsc/server-action.ts +14 -10
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +2 -0
- package/src/search-params.ts +16 -13
- package/src/server/context.ts +8 -2
- package/src/server/request-context.ts +38 -16
- package/src/server.ts +6 -0
- package/src/theme/index.ts +4 -13
- package/src/types/handler-context.ts +12 -16
- package/src/types/route-config.ts +17 -8
- package/src/types/segments.ts +0 -5
- package/src/vite/discovery/bundle-postprocess.ts +31 -56
- package/src/vite/discovery/discover-routers.ts +18 -4
- package/src/vite/discovery/prerender-collection.ts +34 -14
- package/src/vite/discovery/state.ts +4 -7
- package/src/vite/index.ts +4 -3
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/rango.ts +11 -0
- package/src/vite/router-discovery.ts +16 -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
- /package/{CLAUDE.md → AGENTS.md} +0 -0
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* Honors browser reduced-data preferences when available.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { isPrefetchCacheDisabled } from "./cache.js";
|
|
9
|
+
|
|
8
10
|
type NavigatorWithConnection = Navigator & {
|
|
9
11
|
connection?: {
|
|
10
12
|
saveData?: boolean;
|
|
@@ -18,6 +20,10 @@ type NavigatorWithConnection = Navigator & {
|
|
|
18
20
|
export function shouldPrefetch(): boolean {
|
|
19
21
|
if (typeof window === "undefined") return false;
|
|
20
22
|
|
|
23
|
+
// When prefetchCacheTTL is false/0, prefetching is fully disabled —
|
|
24
|
+
// no point issuing requests whose responses will be discarded.
|
|
25
|
+
if (isPrefetchCacheDisabled()) return false;
|
|
26
|
+
|
|
21
27
|
const nav =
|
|
22
28
|
typeof navigator !== "undefined"
|
|
23
29
|
? (navigator as NavigatorWithConnection)
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
unobserveForPrefetch,
|
|
38
38
|
} from "../prefetch/observer.js";
|
|
39
39
|
|
|
40
|
-
// Touch device detection for
|
|
40
|
+
// Touch device detection for adaptive strategy.
|
|
41
41
|
// Checked once at module load (Link.tsx is "use client", runs only in browser).
|
|
42
42
|
const isTouchDevice =
|
|
43
43
|
typeof window !== "undefined" && window.matchMedia("(hover: none)").matches;
|
|
@@ -47,14 +47,14 @@ const isTouchDevice =
|
|
|
47
47
|
* - "hover": Prefetch on mouse enter (direct, no queue)
|
|
48
48
|
* - "viewport": Prefetch when link enters viewport (queued, waits for idle)
|
|
49
49
|
* - "render": Prefetch on component mount regardless of visibility (queued, waits for idle)
|
|
50
|
-
* - "
|
|
50
|
+
* - "adaptive": Hover on pointer devices, viewport on touch devices
|
|
51
51
|
* - "none": No prefetching (default)
|
|
52
52
|
*/
|
|
53
53
|
export type PrefetchStrategy =
|
|
54
54
|
| "hover"
|
|
55
55
|
| "viewport"
|
|
56
56
|
| "render"
|
|
57
|
-
| "
|
|
57
|
+
| "adaptive"
|
|
58
58
|
| "none";
|
|
59
59
|
|
|
60
60
|
/**
|
|
@@ -80,6 +80,16 @@ export interface LinkProps extends Omit<
|
|
|
80
80
|
* Force full document navigation instead of SPA
|
|
81
81
|
*/
|
|
82
82
|
reloadDocument?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Whether to revalidate server data on navigation.
|
|
85
|
+
* Set to `false` to skip the RSC server fetch and only update the URL.
|
|
86
|
+
*
|
|
87
|
+
* Only takes effect when the pathname stays the same (search param / hash changes).
|
|
88
|
+
* If the pathname changes, this option is ignored and a full navigation occurs.
|
|
89
|
+
*
|
|
90
|
+
* @default true
|
|
91
|
+
*/
|
|
92
|
+
revalidate?: boolean;
|
|
83
93
|
/**
|
|
84
94
|
* Prefetch strategy for the link destination
|
|
85
95
|
* @default "none"
|
|
@@ -170,6 +180,7 @@ export const Link: ForwardRefExoticComponent<
|
|
|
170
180
|
replace = false,
|
|
171
181
|
scroll = true,
|
|
172
182
|
reloadDocument = false,
|
|
183
|
+
revalidate,
|
|
173
184
|
prefetch = "none",
|
|
174
185
|
state,
|
|
175
186
|
children,
|
|
@@ -181,9 +192,9 @@ export const Link: ForwardRefExoticComponent<
|
|
|
181
192
|
const ctx = useContext(NavigationStoreContext);
|
|
182
193
|
const isExternal = isExternalUrl(to);
|
|
183
194
|
|
|
184
|
-
// Resolve
|
|
195
|
+
// Resolve adaptive: viewport on touch devices, hover on pointer devices
|
|
185
196
|
const resolvedStrategy =
|
|
186
|
-
prefetch === "
|
|
197
|
+
prefetch === "adaptive" ? (isTouchDevice ? "viewport" : "hover") : prefetch;
|
|
187
198
|
|
|
188
199
|
// Internal ref for viewport observation; merge with forwarded ref
|
|
189
200
|
const internalRef = useRef<HTMLAnchorElement | null>(null);
|
|
@@ -262,9 +273,9 @@ export const Link: ForwardRefExoticComponent<
|
|
|
262
273
|
resolvedState = currentState;
|
|
263
274
|
}
|
|
264
275
|
|
|
265
|
-
ctx.navigate(to, { replace, scroll, state: resolvedState });
|
|
276
|
+
ctx.navigate(to, { replace, scroll, state: resolvedState, revalidate });
|
|
266
277
|
},
|
|
267
|
-
[to, isExternal, reloadDocument, replace, scroll, ctx, onClick],
|
|
278
|
+
[to, isExternal, reloadDocument, replace, scroll, revalidate, ctx, onClick],
|
|
268
279
|
);
|
|
269
280
|
|
|
270
281
|
const handleMouseEnter = useCallback(() => {
|
|
@@ -340,6 +351,7 @@ export const Link: ForwardRefExoticComponent<
|
|
|
340
351
|
data-external={isExternal ? "" : undefined}
|
|
341
352
|
data-scroll={scroll === false ? "false" : undefined}
|
|
342
353
|
data-replace={replace ? "true" : undefined}
|
|
354
|
+
data-revalidate={revalidate === false ? "false" : undefined}
|
|
343
355
|
{...props}
|
|
344
356
|
>
|
|
345
357
|
<LinkContext.Provider value={to}>{children}</LinkContext.Provider>
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
import type { EventController } from "./event-controller.js";
|
|
23
23
|
import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
|
|
24
24
|
import { initRangoState } from "./rango-state.js";
|
|
25
|
+
import { initPrefetchCache } from "./prefetch/cache.js";
|
|
25
26
|
import {
|
|
26
27
|
isInterceptSegment,
|
|
27
28
|
splitInterceptSegments,
|
|
@@ -201,10 +202,17 @@ export async function initBrowserApp(
|
|
|
201
202
|
const rootLayout = initialPayload.metadata?.rootLayout;
|
|
202
203
|
const version = initialPayload.metadata?.version;
|
|
203
204
|
|
|
204
|
-
// Initialize the localStorage state key for
|
|
205
|
+
// Initialize the localStorage state key for cache invalidation.
|
|
205
206
|
// Uses the build version so a new deploy automatically busts all cached prefetches.
|
|
206
207
|
initRangoState(version ?? "0");
|
|
207
208
|
|
|
209
|
+
// Initialize the in-memory prefetch cache TTL from server config.
|
|
210
|
+
// A value of 0 disables the cache; undefined falls back to the module default.
|
|
211
|
+
const prefetchCacheTTL = initialPayload.metadata?.prefetchCacheTTL;
|
|
212
|
+
if (prefetchCacheTTL !== undefined) {
|
|
213
|
+
initPrefetchCache(prefetchCacheTTL);
|
|
214
|
+
}
|
|
215
|
+
|
|
208
216
|
// Create a bound renderSegments that includes rootLayout
|
|
209
217
|
const renderSegments = (
|
|
210
218
|
segments: ResolvedSegment[],
|
|
@@ -260,7 +268,7 @@ export async function initBrowserApp(
|
|
|
260
268
|
import.meta.hot.on("rsc:update", async () => {
|
|
261
269
|
console.log("[RSCRouter] HMR: Server update, refetching RSC");
|
|
262
270
|
|
|
263
|
-
|
|
271
|
+
const handle = eventController.startNavigation(window.location.href, {
|
|
264
272
|
replace: true,
|
|
265
273
|
});
|
|
266
274
|
const streamingToken = handle.startStreaming();
|
|
@@ -318,6 +326,7 @@ export async function initBrowserApp(
|
|
|
318
326
|
console.log("[RSCRouter] HMR: RSC stream complete");
|
|
319
327
|
} finally {
|
|
320
328
|
streamingToken.end();
|
|
329
|
+
handle[Symbol.dispose]();
|
|
321
330
|
}
|
|
322
331
|
});
|
|
323
332
|
}
|