@rangojs/router 0.24.3 → 0.25.0
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 +1 -0
- package/dist/bin/rango.js +363 -39
- package/dist/testing/vitest.js +37 -4
- package/dist/types/browser/event-controller.d.ts +24 -5
- package/dist/types/browser/intercept-utils.d.ts +5 -2
- package/dist/types/browser/merge-segment-loaders.d.ts +0 -23
- package/dist/types/browser/navigation-transaction.d.ts +5 -5
- package/dist/types/browser/partial-update.d.ts +21 -13
- package/dist/types/browser/prefetch/cache.d.ts +8 -0
- package/dist/types/browser/rango-marker.d.ts +7 -0
- package/dist/types/browser/react/filter-segment-order.d.ts +10 -0
- package/dist/types/browser/react/use-handle.d.ts +20 -0
- package/dist/types/browser/react/use-href.d.ts +6 -0
- package/dist/types/browser/react/use-router.d.ts +6 -1
- package/dist/types/browser/response-adapter.d.ts +5 -3
- package/dist/types/browser/scroll-restoration.d.ts +9 -3
- package/dist/types/browser/segment-reconciler.d.ts +10 -13
- package/dist/types/browser/types.d.ts +38 -8
- package/dist/types/build/route-types/per-module-writer.d.ts +12 -0
- package/dist/types/cache/cf/cf-shell-frame.d.ts +2 -0
- package/dist/types/cache/types.d.ts +8 -0
- package/dist/types/client-urls/client-root.d.ts +4 -1
- package/dist/types/client-urls/client-urls.d.ts +28 -2
- package/dist/types/client-urls/navigation.d.ts +6 -8
- package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
- package/dist/types/client-urls/server-projection.d.ts +20 -5
- package/dist/types/client-urls/types.d.ts +35 -17
- package/dist/types/client.d.ts +2 -2
- package/dist/types/client.rsc.d.ts +2 -2
- package/dist/types/decode-loader-results.d.ts +28 -7
- package/dist/types/href-client.d.ts +3 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/loader-redirect.d.ts +2 -3
- package/dist/types/outlet-context.d.ts +7 -6
- package/dist/types/prefetch-markers.d.ts +14 -5
- package/dist/types/route-content-wrapper.d.ts +17 -14
- package/dist/types/route-definition/helpers-types.d.ts +7 -2
- package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
- package/dist/types/route-definition/rewrite.d.ts +48 -5
- package/dist/types/route-types.d.ts +2 -2
- package/dist/types/router/handler-context.d.ts +14 -2
- package/dist/types/router/intercept-resolution.d.ts +19 -46
- package/dist/types/router/lazy-includes.d.ts +7 -0
- package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
- package/dist/types/router/middleware.d.ts +0 -16
- package/dist/types/router/navigation-snapshot.d.ts +2 -0
- package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
- package/dist/types/router/revalidation.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +4 -16
- package/dist/types/router/router-interfaces.d.ts +0 -6
- package/dist/types/router/router-options.d.ts +4 -7
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
- package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
- package/dist/types/router/segment-resolution.d.ts +1 -1
- package/dist/types/router/segment-wrappers.d.ts +3 -5
- package/dist/types/router/substitute-pattern-params.d.ts +19 -2
- package/dist/types/router/tracing.d.ts +2 -2
- package/dist/types/rsc/full-payload.d.ts +14 -2
- package/dist/types/rsc/helpers.d.ts +8 -0
- package/dist/types/rsc/manifest-init.d.ts +20 -0
- package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
- package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
- package/dist/types/rsc/rewrite-passes.d.ts +12 -2
- package/dist/types/rsc/server-action.d.ts +6 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +10 -1
- package/dist/types/rsc/shell-capture.d.ts +7 -0
- package/dist/types/rsc/types.d.ts +5 -0
- package/dist/types/segment-system.d.ts +15 -0
- package/dist/types/server/context.d.ts +64 -4
- package/dist/types/server/request-context.d.ts +41 -4
- package/dist/types/server/request-headers.d.ts +12 -0
- package/dist/types/server/rewrite-trail.d.ts +8 -1
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +1 -1
- package/dist/types/testing/index.d.ts +1 -1
- package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
- package/dist/types/testing/render-route.d.ts +21 -11
- package/dist/types/testing/run-loader.d.ts +7 -0
- package/dist/types/testing/run-middleware.d.ts +7 -0
- package/dist/types/testing/run-revalidate.d.ts +5 -4
- package/dist/types/testing/serve-shell-request.d.ts +41 -4
- package/dist/types/testing/shell-status.d.ts +17 -0
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/testing/vitest.d.ts +10 -1
- package/dist/types/thenable-status.d.ts +0 -6
- package/dist/types/types/handler-context.d.ts +12 -1
- package/dist/types/types/loader-types.d.ts +4 -3
- package/dist/types/types/route-entry.d.ts +2 -1
- package/dist/types/urls/path-helper-types.d.ts +4 -2
- package/dist/types/urls/path-helper.d.ts +5 -0
- package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
- package/dist/types/vite/discovery/state.d.ts +20 -0
- package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
- package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
- package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
- package/dist/vite/index.js +6816 -6547
- package/package.json +1 -1
- package/skills/app-structure/SKILL.md +255 -0
- package/skills/app-structure/content-site.md +333 -0
- package/skills/app-structure/e-commerce.md +435 -0
- package/skills/app-structure/large-app.md +885 -0
- package/skills/app-structure/multi-app.md +280 -0
- package/skills/breadcrumbs/SKILL.md +6 -1
- package/skills/caching/SKILL.md +3 -1
- package/skills/catalog.json +12 -0
- package/skills/client-urls/SKILL.md +195 -32
- package/skills/composability/SKILL.md +10 -8
- package/skills/handler-use/SKILL.md +48 -9
- package/skills/hooks/handle-and-actions.md +104 -0
- package/skills/hooks/navigation.md +24 -10
- package/skills/hooks/urls.md +1 -1
- package/skills/i18n/SKILL.md +2 -1
- package/skills/intercept/SKILL.md +60 -29
- package/skills/layout/SKILL.md +6 -3
- package/skills/links/SKILL.md +151 -17
- package/skills/loader/SKILL.md +32 -10
- package/skills/middleware/SKILL.md +111 -15
- package/skills/observability/SKILL.md +11 -0
- package/skills/parallel/SKILL.md +5 -5
- package/skills/ppr/SKILL.md +43 -8
- package/skills/prerender/SKILL.md +1 -2
- package/skills/rango/SKILL.md +3 -0
- package/skills/response-routes/SKILL.md +4 -1
- package/skills/scripts/SKILL.md +12 -1
- package/skills/server-actions/SKILL.md +60 -20
- package/skills/testing/SKILL.md +1 -0
- package/skills/testing/client-components.md +13 -10
- package/skills/testing/loader.md +25 -24
- package/skills/testing/middleware.md +11 -11
- package/skills/testing/response-routes.md +4 -1
- package/skills/testing/serve-shell-request.md +85 -21
- package/skills/ux-architecture/SKILL.md +980 -0
- package/skills/view-transitions/SKILL.md +43 -10
- package/src/__augment-tests__/augmented.check.ts +28 -0
- package/src/browser/event-controller.ts +44 -18
- package/src/browser/intercept-utils.ts +3 -2
- package/src/browser/merge-segment-loaders.ts +0 -85
- package/src/browser/navigation-bridge.ts +119 -98
- package/src/browser/navigation-client.ts +20 -15
- package/src/browser/navigation-store.ts +4 -2
- package/src/browser/navigation-transaction.ts +9 -8
- package/src/browser/partial-update.ts +90 -32
- package/src/browser/prefetch/cache.ts +8 -0
- package/src/browser/prefetch/fetch.ts +13 -3
- package/src/browser/rango-marker.ts +13 -0
- package/src/browser/react/NavigationProvider.tsx +25 -7
- package/src/browser/react/filter-segment-order.ts +13 -0
- package/src/browser/react/use-action.ts +9 -15
- package/src/browser/react/use-handle.ts +63 -71
- package/src/browser/react/use-href.tsx +24 -5
- package/src/browser/react/use-link-status.ts +1 -1
- package/src/browser/react/use-navigation.ts +30 -40
- package/src/browser/react/use-reverse.ts +4 -17
- package/src/browser/react/use-router.ts +37 -6
- package/src/browser/react/use-segments.ts +8 -12
- package/src/browser/response-adapter.ts +11 -5
- package/src/browser/rsc-router.tsx +3 -1
- package/src/browser/scroll-restoration.ts +34 -20
- package/src/browser/segment-reconciler.ts +11 -29
- package/src/browser/server-action-bridge.ts +52 -16
- package/src/browser/types.ts +40 -8
- package/src/build/generate-manifest.ts +12 -1
- package/src/build/route-types/codegen.ts +6 -2
- package/src/build/route-types/per-module-writer.ts +94 -61
- package/src/cache/cache-runtime.ts +9 -0
- package/src/cache/cache-scope.ts +2 -1
- package/src/cache/cf/cf-cache-store.ts +2 -0
- package/src/cache/cf/cf-shell-frame.ts +3 -0
- package/src/cache/types.ts +8 -0
- package/src/cache/vercel/vercel-cache-store.ts +6 -1
- package/src/client-urls/client-root.tsx +10 -5
- package/src/client-urls/client-urls.ts +246 -166
- package/src/client-urls/navigation.ts +45 -38
- package/src/client-urls/revalidate-registry.ts +12 -3
- package/src/client-urls/server-projection.ts +286 -75
- package/src/client-urls/types.ts +40 -16
- package/src/client.rsc.tsx +11 -1
- package/src/client.tsx +9 -1
- package/src/decode-loader-results.ts +64 -31
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +1 -0
- package/src/index.ts +12 -4
- package/src/loader-redirect.tsx +2 -3
- package/src/outlet-context.ts +7 -6
- package/src/prefetch-markers.ts +15 -5
- package/src/reverse.ts +3 -3
- package/src/route-content-wrapper.tsx +19 -17
- package/src/route-definition/dsl-helpers.ts +27 -7
- package/src/route-definition/helpers-types.ts +7 -2
- package/src/route-definition/resolve-handler-use.ts +23 -10
- package/src/route-definition/rewrite.ts +86 -19
- package/src/route-types.ts +1 -2
- package/src/router/find-match.ts +37 -1
- package/src/router/handler-context.ts +85 -40
- package/src/router/intercept-resolution.ts +161 -307
- package/src/router/lazy-includes.ts +43 -27
- package/src/router/loader-resolution.ts +21 -1
- package/src/router/manifest.ts +6 -8
- package/src/router/match-api.ts +11 -1
- package/src/router/match-middleware/background-revalidation.ts +2 -4
- package/src/router/match-middleware/cache-lookup.ts +16 -20
- package/src/router/match-middleware/intercept-resolution.ts +41 -60
- package/src/router/match-result.ts +31 -2
- package/src/router/middleware.ts +6 -147
- package/src/router/navigation-snapshot.ts +38 -2
- package/src/router/prefetch-cache-ttl.ts +10 -18
- package/src/router/prerender-match.ts +2 -1
- package/src/router/revalidation.ts +24 -3
- package/src/router/router-context.ts +5 -18
- package/src/router/router-interfaces.ts +0 -7
- package/src/router/router-options.ts +4 -7
- package/src/router/segment-resolution/fresh.ts +6 -2
- package/src/router/segment-resolution/helpers.ts +7 -1
- package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
- package/src/router/segment-resolution/revalidation.ts +75 -35
- package/src/router/segment-resolution.ts +0 -1
- package/src/router/segment-wrappers.ts +8 -13
- package/src/router/substitute-pattern-params.ts +37 -2
- package/src/router/tracing.ts +2 -2
- package/src/router.ts +2 -9
- package/src/rsc/full-payload.ts +26 -4
- package/src/rsc/handler.ts +83 -41
- package/src/rsc/helpers.ts +13 -0
- package/src/rsc/manifest-init.ts +56 -3
- package/src/rsc/prefetch-render-error.ts +38 -0
- package/src/rsc/progressive-enhancement.ts +161 -211
- package/src/rsc/rewrite-passes.ts +56 -9
- package/src/rsc/rsc-rendering.ts +79 -23
- package/src/rsc/server-action.ts +25 -2
- package/src/rsc/shell-capture-constants.ts +22 -0
- package/src/rsc/shell-capture.ts +11 -0
- package/src/rsc/types.ts +5 -0
- package/src/segment-system.tsx +125 -119
- package/src/server/context.ts +96 -5
- package/src/server/handle-store.ts +3 -2
- package/src/server/request-context.ts +42 -3
- package/src/server/request-headers.ts +32 -0
- package/src/server/rewrite-trail.ts +55 -9
- package/src/ssr/ssr-root.tsx +6 -10
- package/src/testing/dispatch.ts +4 -5
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +1 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/internal/client-urls-projection.ts +31 -11
- package/src/testing/render-route.tsx +83 -40
- package/src/testing/run-loader.ts +26 -2
- package/src/testing/run-middleware.ts +10 -0
- package/src/testing/run-revalidate.ts +13 -6
- package/src/testing/serve-shell-request.ts +77 -4
- package/src/testing/shell-status.ts +40 -0
- package/src/testing/vitest-stubs/version.ts +1 -0
- package/src/testing/vitest.ts +74 -7
- package/src/thenable-status.ts +0 -11
- package/src/types/handler-context.ts +12 -1
- package/src/types/loader-types.ts +4 -3
- package/src/types/route-entry.ts +2 -1
- package/src/urls/include-helper.ts +38 -11
- package/src/urls/path-helper-types.ts +4 -2
- package/src/urls/path-helper.ts +16 -3
- package/src/vite/discovery/client-urls-projection.ts +205 -16
- package/src/vite/discovery/discover-routers.ts +2 -2
- package/src/vite/discovery/prerender-collection.ts +2 -6
- package/src/vite/discovery/state.ts +26 -0
- package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
- package/src/vite/plugins/expose-internal-ids.ts +46 -40
- package/src/vite/plugins/hoist/source.ts +8 -7
- package/src/vite/plugins/transition-when-hoist.ts +1 -0
- package/src/vite/plugins/version-plugin.ts +7 -0
- package/src/vite/plugins/version.d.ts +7 -0
- package/src/vite/plugins/virtual-entries.ts +20 -0
- package/src/vite/rango.ts +4 -1
- package/src/vite/router-discovery.ts +39 -7
- package/src/vite/utils/prerender-utils.ts +17 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toShouldRevalidateFn } from "../router/revalidation.js";
|
|
1
2
|
import type { ShouldRevalidateFn } from "../types.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -6,7 +7,9 @@ import type { ShouldRevalidateFn } from "../types.js";
|
|
|
6
7
|
* group's module loads in the RSC realm
|
|
7
8
|
* (vite/plugins/client-urls-revalidate-hoist.ts); materialized loader stubs
|
|
8
9
|
* read it (server-projection.ts). A key hashes the file and the predicate's
|
|
9
|
-
* syntax, so a re-registration under one key is the same predicate.
|
|
10
|
+
* syntax, so a re-registration under one key is the same predicate. A
|
|
11
|
+
* hoisted boolean (`revalidate(FLAG)`) is stored as its constant predicate;
|
|
12
|
+
* any other value is left unregistered for materialization to name.
|
|
10
13
|
*
|
|
11
14
|
* Server-only build output, not router state: no router hook reads it
|
|
12
15
|
* (AGENTS.md Hard rule 8).
|
|
@@ -14,9 +17,15 @@ import type { ShouldRevalidateFn } from "../types.js";
|
|
|
14
17
|
const revalidators = new Map<string, ShouldRevalidateFn<any, any>>();
|
|
15
18
|
|
|
16
19
|
export function registerClientUrlRevalidators(
|
|
17
|
-
fns: Readonly<Record<string, ShouldRevalidateFn<any, any
|
|
20
|
+
fns: Readonly<Record<string, ShouldRevalidateFn<any, any> | boolean>>,
|
|
18
21
|
): void {
|
|
19
|
-
for (const [key, fn] of Object.entries(fns))
|
|
22
|
+
for (const [key, fn] of Object.entries(fns)) {
|
|
23
|
+
if (typeof fn === "function" || typeof fn === "boolean") {
|
|
24
|
+
revalidators.set(key, toShouldRevalidateFn(fn));
|
|
25
|
+
} else {
|
|
26
|
+
revalidators.delete(key);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export function getClientUrlRevalidator(
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
import type { PathHelpers } from "../urls/path-helper-types.js";
|
|
22
22
|
import type { AllUseItems } from "../route-types.js";
|
|
23
23
|
import { urls } from "../urls/urls-function.js";
|
|
24
|
+
import { toShouldRevalidateFn } from "../router/revalidation.js";
|
|
24
25
|
import { isHoistedRevalidateRef } from "./hoisted-revalidate.js";
|
|
25
26
|
import { getClientUrlRevalidator } from "./revalidate-registry.js";
|
|
26
27
|
import { setLayoutLoaderCopies } from "./layout-loader-copies.js";
|
|
@@ -32,6 +33,13 @@ import {
|
|
|
32
33
|
ClientUrlsRoot,
|
|
33
34
|
} from "./client-root.js";
|
|
34
35
|
import { createClientUrlsWhenRef } from "../transition-when-ref.js";
|
|
36
|
+
import type { TrieLeaf, TrieNode } from "../router/route-trie-builder.js";
|
|
37
|
+
import { tryTrieMatch } from "../router/trie-matching.js";
|
|
38
|
+
import { sameMountInstance } from "../router/substitute-pattern-params.js";
|
|
39
|
+
import { getRouterTrie } from "../route-map-builder.js";
|
|
40
|
+
import { _getRequestContext } from "../server/request-context.js";
|
|
41
|
+
import { routeKeyOf } from "../urls/path-helper.js";
|
|
42
|
+
import { buildClientUrlsTrie } from "./client-urls.js";
|
|
35
43
|
import type {
|
|
36
44
|
ClientTransitionConfig,
|
|
37
45
|
ClientUrlInterceptRecord,
|
|
@@ -105,11 +113,11 @@ export interface ClientUrlProjectionRoute {
|
|
|
105
113
|
* loader(), so a prefetch of the route defers them. Absent (= none) in
|
|
106
114
|
* projections serialized before the option existed. */
|
|
107
115
|
readonly deferrableLoaderIndices?: readonly number[];
|
|
108
|
-
/** Per loader (aligned with loaderIds),
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* loader declares one. */
|
|
112
|
-
readonly loaderRevalidate?: readonly (readonly string[])[];
|
|
116
|
+
/** Per loader (aligned with loaderIds), its revalidate() arguments in
|
|
117
|
+
* declaration order: a hoisted predicate's key, whose registration
|
|
118
|
+
* materialization attaches (revalidate-registry.ts), or a boolean
|
|
119
|
+
* literal. Absent when no loader declares one. */
|
|
120
|
+
readonly loaderRevalidate?: readonly (readonly (string | boolean)[])[];
|
|
113
121
|
/** One per leading loader its layout()s declare
|
|
114
122
|
* (ClientUrlRouteRecord.layoutLoaders): the record's ordinal in the
|
|
115
123
|
* definition. Two routes whose loader at an index carries the same ordinal
|
|
@@ -130,6 +138,10 @@ export interface ClientUrlProjectionIntercept {
|
|
|
130
138
|
readonly targetName: string;
|
|
131
139
|
readonly loaderIds: readonly string[];
|
|
132
140
|
readonly hasLoading: boolean;
|
|
141
|
+
/** As on a route (ClientUrlProjectionRoute.deferrableLoaderIndices). */
|
|
142
|
+
readonly deferrableLoaderIndices?: readonly number[];
|
|
143
|
+
/** As on a route (ClientUrlProjectionRoute.loaderRevalidate). */
|
|
144
|
+
readonly loaderRevalidate?: readonly (readonly (string | boolean)[])[];
|
|
133
145
|
}
|
|
134
146
|
|
|
135
147
|
export interface ClientUrlProjection {
|
|
@@ -325,29 +337,10 @@ function serializeRoute(
|
|
|
325
337
|
return loader.$$id;
|
|
326
338
|
});
|
|
327
339
|
|
|
328
|
-
const loaderRevalidate = route.loaders.map(({ loader, revalidate }) =>
|
|
329
|
-
revalidate.flatMap((entry) => {
|
|
330
|
-
if (isHoistedRevalidateRef(entry)) return [entry.$$rangoRevalidate];
|
|
331
|
-
if (options.hoisted) {
|
|
332
|
-
throw new Error(
|
|
333
|
-
`clientUrls() revalidate() on loader "${loader.$$id}" of route ` +
|
|
334
|
-
`"${route.pattern}" was not hoisted to the server: call the ` +
|
|
335
|
-
"builder's revalidate() helper with an inline function or an " +
|
|
336
|
-
"imported predicate.",
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
return [];
|
|
340
|
-
}),
|
|
341
|
-
);
|
|
342
|
-
|
|
343
340
|
const awaitedLoaderIndices = indicesWhere(
|
|
344
341
|
route.loaders,
|
|
345
342
|
({ ssr }) => ssr === false,
|
|
346
343
|
);
|
|
347
|
-
const deferrableLoaderIndices = indicesWhere(
|
|
348
|
-
route.loaders,
|
|
349
|
-
({ prefetch }) => prefetch === false,
|
|
350
|
-
);
|
|
351
344
|
|
|
352
345
|
const transition = serializeTransition(route);
|
|
353
346
|
return Object.freeze({
|
|
@@ -360,6 +353,55 @@ function serializeRoute(
|
|
|
360
353
|
...(awaitedLoaderIndices.length > 0
|
|
361
354
|
? { awaitedLoaderIndices: Object.freeze(awaitedLoaderIndices) }
|
|
362
355
|
: {}),
|
|
356
|
+
...serializeLoaderOptions(
|
|
357
|
+
route.loaders,
|
|
358
|
+
`route "${route.pattern}"`,
|
|
359
|
+
options,
|
|
360
|
+
),
|
|
361
|
+
...(layoutLoaders.length > 0
|
|
362
|
+
? { layoutLoaders: Object.freeze(layoutLoaders) }
|
|
363
|
+
: {}),
|
|
364
|
+
...(transition ? { transition } : {}),
|
|
365
|
+
...(typeof route.transition?.when === "function"
|
|
366
|
+
? { transitionWhen: true as const }
|
|
367
|
+
: {}),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* The loader options a route and an intercept project alike: the `prefetch:
|
|
373
|
+
* false` indices and each loader's revalidate() keys and boolean literals.
|
|
374
|
+
* Under `options.hoisted` a predicate the build left in place fails loudly.
|
|
375
|
+
*/
|
|
376
|
+
function serializeLoaderOptions(
|
|
377
|
+
loaders: readonly ClientUrlLoaderRecord[],
|
|
378
|
+
owner: string,
|
|
379
|
+
options: SerializeClientUrlPatternsOptions,
|
|
380
|
+
): Pick<
|
|
381
|
+
ClientUrlProjectionRoute,
|
|
382
|
+
"deferrableLoaderIndices" | "loaderRevalidate"
|
|
383
|
+
> {
|
|
384
|
+
const loaderRevalidate = loaders.map(({ loader, revalidate }) =>
|
|
385
|
+
revalidate.flatMap((entry): (string | boolean)[] => {
|
|
386
|
+
if (isHoistedRevalidateRef(entry)) return [entry.$$rangoRevalidate];
|
|
387
|
+
if (typeof entry === "boolean") return [entry];
|
|
388
|
+
if (options.hoisted) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
`clientUrls() revalidate() on loader "${loader.$$id}" of ${owner} ` +
|
|
391
|
+
"was not hoisted to the server: call revalidate() (the builder's " +
|
|
392
|
+
'helper, or the one "@rangojs/router/client" exports for a ' +
|
|
393
|
+
"component's .use) with an inline function, an imported " +
|
|
394
|
+
"predicate or a boolean.",
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return [];
|
|
398
|
+
}),
|
|
399
|
+
);
|
|
400
|
+
const deferrableLoaderIndices = indicesWhere(
|
|
401
|
+
loaders,
|
|
402
|
+
({ prefetch }) => prefetch === false,
|
|
403
|
+
);
|
|
404
|
+
return {
|
|
363
405
|
...(deferrableLoaderIndices.length > 0
|
|
364
406
|
? { deferrableLoaderIndices: Object.freeze(deferrableLoaderIndices) }
|
|
365
407
|
: {}),
|
|
@@ -370,19 +412,13 @@ function serializeRoute(
|
|
|
370
412
|
),
|
|
371
413
|
}
|
|
372
414
|
: {}),
|
|
373
|
-
|
|
374
|
-
? { layoutLoaders: Object.freeze(layoutLoaders) }
|
|
375
|
-
: {}),
|
|
376
|
-
...(transition ? { transition } : {}),
|
|
377
|
-
...(typeof route.transition?.when === "function"
|
|
378
|
-
? { transitionWhen: true as const }
|
|
379
|
-
: {}),
|
|
380
|
-
});
|
|
415
|
+
};
|
|
381
416
|
}
|
|
382
417
|
|
|
383
418
|
function serializeIntercept(
|
|
384
419
|
record: ClientUrlInterceptRecord,
|
|
385
420
|
index: number,
|
|
421
|
+
options: SerializeClientUrlPatternsOptions,
|
|
386
422
|
): ClientUrlProjectionIntercept {
|
|
387
423
|
const loaderIds = record.loaders.map(({ loader }, loaderIndex) => {
|
|
388
424
|
if (typeof loader.$$id !== "string" || loader.$$id.length === 0) {
|
|
@@ -399,6 +435,11 @@ function serializeIntercept(
|
|
|
399
435
|
targetName: record.targetName,
|
|
400
436
|
loaderIds: Object.freeze(loaderIds),
|
|
401
437
|
hasLoading: record.loading !== undefined,
|
|
438
|
+
...serializeLoaderOptions(
|
|
439
|
+
record.loaders,
|
|
440
|
+
`intercept ".${record.targetName}"`,
|
|
441
|
+
options,
|
|
442
|
+
),
|
|
402
443
|
});
|
|
403
444
|
}
|
|
404
445
|
|
|
@@ -413,7 +454,9 @@ export function serializeClientUrlPatterns(
|
|
|
413
454
|
patterns.routes.map((route) => serializeRoute(route, options, ordinals)),
|
|
414
455
|
),
|
|
415
456
|
intercepts: Object.freeze(
|
|
416
|
-
(patterns.intercepts ?? []).map(
|
|
457
|
+
(patterns.intercepts ?? []).map((record, index) =>
|
|
458
|
+
serializeIntercept(record, index, options),
|
|
459
|
+
),
|
|
417
460
|
),
|
|
418
461
|
});
|
|
419
462
|
}
|
|
@@ -529,28 +572,48 @@ export function clearClientUrlProjections(): void {
|
|
|
529
572
|
clientUrlProjections.clear();
|
|
530
573
|
}
|
|
531
574
|
|
|
575
|
+
/** The projected loaders of a route or an intercept. */
|
|
576
|
+
interface ProjectedLoaders {
|
|
577
|
+
readonly projected: Pick<
|
|
578
|
+
ClientUrlProjectionRoute,
|
|
579
|
+
| "loaderIds"
|
|
580
|
+
| "loaderRevalidate"
|
|
581
|
+
| "awaitedLoaderIndices"
|
|
582
|
+
| "deferrableLoaderIndices"
|
|
583
|
+
>;
|
|
584
|
+
/** Names the owner in an error: `route "/a"`, `intercept ".b"`. */
|
|
585
|
+
readonly label: string;
|
|
586
|
+
/** The owner's loader records in a definition object. */
|
|
587
|
+
readonly inline: (
|
|
588
|
+
patterns: ClientUrlPatterns,
|
|
589
|
+
) => readonly ClientUrlLoaderRecord[] | undefined;
|
|
590
|
+
}
|
|
591
|
+
|
|
532
592
|
/**
|
|
533
|
-
* The revalidate() predicates of one materialized loader
|
|
534
|
-
* object (unit tests, the testing
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
* registration fails loudly here, at
|
|
593
|
+
* The revalidate() predicates of one materialized loader, a boolean as its
|
|
594
|
+
* constant predicate. A definition object (unit tests, the testing
|
|
595
|
+
* primitives) carries its functions. A client reference resolves the
|
|
596
|
+
* projection's content keys in the registry the group's hoisted module
|
|
597
|
+
* filled when it loaded; a key with no registration fails loudly here, at
|
|
598
|
+
* materialization, never at a request.
|
|
538
599
|
*/
|
|
539
600
|
function loaderRevalidators(
|
|
540
601
|
reference: ClientUrlDefinitionSource,
|
|
541
|
-
|
|
602
|
+
{ projected, label, inline }: ProjectedLoaders,
|
|
542
603
|
loaderIndex: number,
|
|
543
604
|
): ShouldRevalidateFn<any, any>[] {
|
|
544
605
|
const fromRegistry = (key: string): ShouldRevalidateFn<any, any> => {
|
|
545
606
|
if (!getClientUrlRevalidator(key)) {
|
|
546
607
|
const id = isClientUrlReference(reference) ? reference.$$id : "(inline)";
|
|
547
608
|
throw new Error(
|
|
548
|
-
`clientUrls() revalidate() on loader "${
|
|
549
|
-
`of
|
|
609
|
+
`clientUrls() revalidate() on loader "${projected.loaderIds[loaderIndex]}" ` +
|
|
610
|
+
`of ${label} in "${id}" has no server ` +
|
|
550
611
|
`registration (key "${key}"). The rango Vite plugin registers ` +
|
|
551
612
|
"hoisted predicates when the group's module loads in the RSC " +
|
|
552
|
-
"environment
|
|
553
|
-
"in
|
|
613
|
+
"environment, with those of the modules route discovery found " +
|
|
614
|
+
"its keys in (a component's .use). An argument that evaluated to " +
|
|
615
|
+
"neither a function nor a boolean there, or a transform that " +
|
|
616
|
+
"changed its syntax in one environment only, causes this.",
|
|
554
617
|
);
|
|
555
618
|
}
|
|
556
619
|
// Looked up per call: a dev re-evaluation of the hoisted module
|
|
@@ -558,14 +621,17 @@ function loaderRevalidators(
|
|
|
558
621
|
return (args) => getClientUrlRevalidator(key)!(args);
|
|
559
622
|
};
|
|
560
623
|
if (isClientUrlPatterns(reference)) {
|
|
561
|
-
|
|
562
|
-
return (record?.loaders[loaderIndex]?.revalidate ?? []).map((entry) =>
|
|
624
|
+
return (inline(reference)?.[loaderIndex]?.revalidate ?? []).map((entry) =>
|
|
563
625
|
isHoistedRevalidateRef(entry)
|
|
564
626
|
? fromRegistry(entry.$$rangoRevalidate)
|
|
565
|
-
: entry,
|
|
627
|
+
: toShouldRevalidateFn(entry),
|
|
566
628
|
);
|
|
567
629
|
}
|
|
568
|
-
return (
|
|
630
|
+
return (projected.loaderRevalidate?.[loaderIndex] ?? []).map((entry) =>
|
|
631
|
+
toShouldRevalidateFn(
|
|
632
|
+
typeof entry === "boolean" ? entry : fromRegistry(entry),
|
|
633
|
+
),
|
|
634
|
+
);
|
|
569
635
|
}
|
|
570
636
|
|
|
571
637
|
function createLoaderStub(id: string): LoaderDefinition<unknown> {
|
|
@@ -607,6 +673,100 @@ function materializedPathOptions(
|
|
|
607
673
|
};
|
|
608
674
|
}
|
|
609
675
|
|
|
676
|
+
/** A param segment on a walked path; the leaf's `pa` names it. */
|
|
677
|
+
const PARAM = "\0";
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* The patterns of the router's routes, outside the group, that a URL of the
|
|
681
|
+
* group matches too: the trie walked in step with the group's (its patterns
|
|
682
|
+
* joined to the mount), static against static, a param against either, a
|
|
683
|
+
* catch-all against everything below. Constraints are not compared, so the
|
|
684
|
+
* list may hold a route no URL of the group reaches: the client trie, which
|
|
685
|
+
* gets the patterns as they are, never lets one win a URL it does not match.
|
|
686
|
+
* `own` are the group's route keys in the router's trie.
|
|
687
|
+
*/
|
|
688
|
+
export function competingPatterns(
|
|
689
|
+
router: TrieNode,
|
|
690
|
+
group: TrieNode,
|
|
691
|
+
own: ReadonlySet<string>,
|
|
692
|
+
): string[] {
|
|
693
|
+
const found = new Set<string>();
|
|
694
|
+
const add = (leaf: TrieLeaf, path: readonly string[], tail = ""): void => {
|
|
695
|
+
if (own.has(leaf.n)) return;
|
|
696
|
+
let index = 0;
|
|
697
|
+
const pattern = path.map((piece) => {
|
|
698
|
+
if (piece[0] !== PARAM) return piece;
|
|
699
|
+
const name = leaf.pa?.[index++] ?? `p${index}`;
|
|
700
|
+
const allowed = leaf.cv?.[name];
|
|
701
|
+
return `/:${name}${allowed ? `(${allowed.join("|")})` : ""}${piece.slice(1)}`;
|
|
702
|
+
});
|
|
703
|
+
found.add(pattern.join("") + tail || "/");
|
|
704
|
+
};
|
|
705
|
+
const catchAll = (node: TrieNode, path: readonly string[]): void => {
|
|
706
|
+
const leaf = node.w!;
|
|
707
|
+
add(
|
|
708
|
+
leaf,
|
|
709
|
+
path,
|
|
710
|
+
leaf.pn === "*" ? "/*" : `/:${leaf.pn}${leaf.w1 ? "+" : "*"}`,
|
|
711
|
+
);
|
|
712
|
+
};
|
|
713
|
+
const everything = (node: TrieNode, path: readonly string[]): void => {
|
|
714
|
+
if (node.r) add(node.r, path);
|
|
715
|
+
if (node.w) catchAll(node, path);
|
|
716
|
+
for (const [key, child] of Object.entries(node.s ?? {})) {
|
|
717
|
+
everything(child, [...path, `/${key}`]);
|
|
718
|
+
}
|
|
719
|
+
if (node.p) everything(node.p.c, [...path, PARAM]);
|
|
720
|
+
for (const [suffix, child] of Object.entries(node.xp ?? {})) {
|
|
721
|
+
everything(child.c, [...path, PARAM + suffix]);
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
// Every child of the group's node that a segment `matches` lets through.
|
|
725
|
+
const groupSteps = (
|
|
726
|
+
node: TrieNode,
|
|
727
|
+
matches: (key: string) => boolean,
|
|
728
|
+
): TrieNode[] => [
|
|
729
|
+
...Object.entries(node.s ?? {}).flatMap(([key, child]) =>
|
|
730
|
+
matches(key) ? [child] : [],
|
|
731
|
+
),
|
|
732
|
+
...(node.p ? [node.p.c] : []),
|
|
733
|
+
...Object.values(node.xp ?? {}).map((child) => child.c),
|
|
734
|
+
];
|
|
735
|
+
const walk = (g: TrieNode, r: TrieNode, path: readonly string[]): void => {
|
|
736
|
+
if (g.w) return everything(r, path);
|
|
737
|
+
if (r.r && g.r) add(r.r, path);
|
|
738
|
+
if (r.w && (g.r || g.s || g.p || g.xp)) catchAll(r, path);
|
|
739
|
+
for (const [key, child] of Object.entries(r.s ?? {})) {
|
|
740
|
+
const next = [...path, `/${key}`];
|
|
741
|
+
if (g.s?.[key]) walk(g.s[key], child, next);
|
|
742
|
+
if (g.p) walk(g.p.c, child, next);
|
|
743
|
+
for (const [suffix, gx] of Object.entries(g.xp ?? {})) {
|
|
744
|
+
if (key.endsWith(suffix)) walk(gx.c, child, next);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (r.p) {
|
|
748
|
+
for (const step of groupSteps(g, () => true)) {
|
|
749
|
+
walk(step, r.p.c, [...path, PARAM]);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
for (const [suffix, child] of Object.entries(r.xp ?? {})) {
|
|
753
|
+
for (const step of groupSteps(g, (key) => key.endsWith(suffix))) {
|
|
754
|
+
walk(step, child.c, [...path, PARAM + suffix]);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
walk(group, router, []);
|
|
759
|
+
return [...found];
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/** competingPatterns per router trie, per group (name prefix and mount). */
|
|
763
|
+
const COMPETING = new WeakMap<TrieNode, Map<string, readonly string[]>>();
|
|
764
|
+
|
|
765
|
+
function currentRouterTrie(): TrieNode | undefined {
|
|
766
|
+
const routerId = _getRequestContext()?._routerId;
|
|
767
|
+
return routerId ? getRouterTrie(routerId) : undefined;
|
|
768
|
+
}
|
|
769
|
+
|
|
610
770
|
function materializeRouteItems(
|
|
611
771
|
reference: ClientUrlDefinitionSource,
|
|
612
772
|
projection: ClientUrlProjection,
|
|
@@ -627,6 +787,52 @@ function materializeRouteItems(
|
|
|
627
787
|
// One key per group MOUNT (the include's URL prefix): renderSegments keys
|
|
628
788
|
// every route segment of the group by it (ResolvedSegment.clientGroup).
|
|
629
789
|
const clientGroup = getUrlPrefix() || "/";
|
|
790
|
+
// The group's routes as the router's trie names them.
|
|
791
|
+
const own = new Set(
|
|
792
|
+
projection.routes.map((route) =>
|
|
793
|
+
routeKeyOf(namePrefix, route.pattern, route.name ?? undefined),
|
|
794
|
+
),
|
|
795
|
+
);
|
|
796
|
+
// ClientUrlsRoot's `foreign`: the router's routes that compete with the
|
|
797
|
+
// group's for a URL, so the browser presents only what the server routes
|
|
798
|
+
// here (a `/:slug` route under `/:locale?` would claim a root `/login`).
|
|
799
|
+
const foreignProp = (): { foreign?: readonly string[] } => {
|
|
800
|
+
const router = currentRouterTrie();
|
|
801
|
+
if (!router) return {};
|
|
802
|
+
let byGroup = COMPETING.get(router);
|
|
803
|
+
if (!byGroup) COMPETING.set(router, (byGroup = new Map()));
|
|
804
|
+
const key = `${namePrefix} ${clientGroup}`;
|
|
805
|
+
let foreign = byGroup.get(key);
|
|
806
|
+
if (!foreign) {
|
|
807
|
+
foreign = competingPatterns(
|
|
808
|
+
router,
|
|
809
|
+
buildClientUrlsTrie(projection.routes, clientGroup),
|
|
810
|
+
own,
|
|
811
|
+
);
|
|
812
|
+
byGroup.set(key, foreign);
|
|
813
|
+
}
|
|
814
|
+
return foreign.length ? { foreign } : {};
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
// The loader() items of a route's or an intercept's projected loaders: each
|
|
818
|
+
// stub with its `ssr: false` and `prefetch: false` flags and its
|
|
819
|
+
// revalidate() predicates.
|
|
820
|
+
const materializeLoaders = (owner: ProjectedLoaders) =>
|
|
821
|
+
owner.projected.loaderIds.map((id, loaderIndex) => {
|
|
822
|
+
const revalidators = loaderRevalidators(reference, owner, loaderIndex);
|
|
823
|
+
return loader(
|
|
824
|
+
createLoaderStub(id),
|
|
825
|
+
{
|
|
826
|
+
...(owner.projected.awaitedLoaderIndices?.includes(loaderIndex) && {
|
|
827
|
+
ssr: false,
|
|
828
|
+
}),
|
|
829
|
+
...(owner.projected.deferrableLoaderIndices?.includes(
|
|
830
|
+
loaderIndex,
|
|
831
|
+
) && { prefetch: false }),
|
|
832
|
+
},
|
|
833
|
+
() => revalidators.map((fn) => revalidate(fn)),
|
|
834
|
+
);
|
|
835
|
+
});
|
|
630
836
|
|
|
631
837
|
// Helper calls attach to the CURRENT ctx.parent as they execute, so these
|
|
632
838
|
// builders must run where the items belong: at the module top level for the
|
|
@@ -672,27 +878,15 @@ function materializeRouteItems(
|
|
|
672
878
|
definition: reference as ClientUrlPatterns,
|
|
673
879
|
routeId: route.id,
|
|
674
880
|
namePrefix,
|
|
881
|
+
...foreignProp(),
|
|
675
882
|
}),
|
|
676
883
|
materializedPathOptions(route, clientGroup),
|
|
677
884
|
recordingCopies(routeIndex, () => [
|
|
678
|
-
...
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
);
|
|
684
|
-
return loader(
|
|
685
|
-
createLoaderStub(id),
|
|
686
|
-
{
|
|
687
|
-
...(route.awaitedLoaderIndices?.includes(loaderIndex) && {
|
|
688
|
-
ssr: false,
|
|
689
|
-
}),
|
|
690
|
-
...(route.deferrableLoaderIndices?.includes(loaderIndex) && {
|
|
691
|
-
prefetch: false,
|
|
692
|
-
}),
|
|
693
|
-
},
|
|
694
|
-
() => revalidators.map((fn) => revalidate(fn)),
|
|
695
|
-
);
|
|
885
|
+
...materializeLoaders({
|
|
886
|
+
projected: route,
|
|
887
|
+
label: `route "${route.pattern}"`,
|
|
888
|
+
inline: (patterns) =>
|
|
889
|
+
patterns.routes.find(({ id }) => id === route.id)?.loaders,
|
|
696
890
|
}),
|
|
697
891
|
...(route.hasLoading
|
|
698
892
|
? [
|
|
@@ -744,15 +938,27 @@ function materializeRouteItems(
|
|
|
744
938
|
// out-of-group origin the slot's host layout is not in the rendered
|
|
745
939
|
// tree, so the modal would silently vanish. The declaration contract is
|
|
746
940
|
// module-local, so materialization synthesizes the origin check the DSL
|
|
747
|
-
// deliberately does not expose: activate only when the origin
|
|
748
|
-
//
|
|
749
|
-
//
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
941
|
+
// deliberately does not expose: activate only when the origin is one of
|
|
942
|
+
// this group's routes, in the target's mount instance. The router
|
|
943
|
+
// matched the origin already: its route name, or for an unnamed one its
|
|
944
|
+
// key in the router's trie, says whether it is the group's (a URL
|
|
945
|
+
// prefix cannot: a `/:locale?` mount prefixes every path), and its
|
|
946
|
+
// params whether another locale is another instance, a full page
|
|
947
|
+
// (beginClientUrlNavigation in navigation.ts leaves the group for it
|
|
948
|
+
// too). This is server-generated code — no function crosses the
|
|
949
|
+
// "use client" projection boundary.
|
|
950
|
+
const isInGroupOrigin = ({ from, to }: InterceptSelectorContext): boolean => {
|
|
951
|
+
if (clientGroup === "/") return true;
|
|
952
|
+
const router = currentRouterTrie();
|
|
953
|
+
const key =
|
|
954
|
+
from.routeName ??
|
|
955
|
+
(router && tryTrieMatch(router, from.url.pathname)?.routeKey);
|
|
956
|
+
return (
|
|
957
|
+
!!key &&
|
|
958
|
+
own.has(key) &&
|
|
959
|
+
sameMountInstance(clientGroup, from.params, to.params)
|
|
960
|
+
);
|
|
961
|
+
};
|
|
756
962
|
const slotNames = [...new Set(intercepts.map((record) => record.slotName))];
|
|
757
963
|
const buildInterceptItems = (): AllUseItems[] =>
|
|
758
964
|
intercepts.map(
|
|
@@ -767,7 +973,12 @@ function materializeRouteItems(
|
|
|
767
973
|
}),
|
|
768
974
|
{ when: isInGroupOrigin },
|
|
769
975
|
() => [
|
|
770
|
-
...
|
|
976
|
+
...materializeLoaders({
|
|
977
|
+
projected: record,
|
|
978
|
+
label: `intercept ".${record.targetName}"`,
|
|
979
|
+
inline: (patterns) =>
|
|
980
|
+
patterns.intercepts?.[interceptIndex]?.loaders,
|
|
981
|
+
}),
|
|
771
982
|
...(record.hasLoading
|
|
772
983
|
? [
|
|
773
984
|
loading(
|
package/src/client-urls/types.ts
CHANGED
|
@@ -28,6 +28,18 @@ export type ClientUrlItems = readonly ClientUrlItemInput[];
|
|
|
28
28
|
|
|
29
29
|
export type ClientUrlUse = () => ClientUrlItems;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* A component path(), layout() or intercept() mounts. Its optional `use`
|
|
33
|
+
* carries its own items, as a server handler's `.use` does, built with the
|
|
34
|
+
* helpers `@rangojs/router/client` exports: they come before the mount
|
|
35
|
+
* site's explicit items (an explicit loading() or transition() wins, loaders
|
|
36
|
+
* accumulate). A page component may carry loader(), loading() and
|
|
37
|
+
* transition(); a layout or intercept component loader() and loading().
|
|
38
|
+
*/
|
|
39
|
+
export type ClientUrlComponent = ComponentType & {
|
|
40
|
+
readonly use?: ClientUrlUse;
|
|
41
|
+
};
|
|
42
|
+
|
|
31
43
|
export type ClientPathOptions<
|
|
32
44
|
TName extends string = string,
|
|
33
45
|
TSearch extends SearchSchema = SearchSchema,
|
|
@@ -42,13 +54,13 @@ export type ClientPathFn = <
|
|
|
42
54
|
const TSearch extends SearchSchema = {},
|
|
43
55
|
>(
|
|
44
56
|
pattern: TPattern,
|
|
45
|
-
component:
|
|
57
|
+
component: ClientUrlComponent,
|
|
46
58
|
optionsOrUse?: ClientPathOptions<TName, TSearch> | ClientUrlUse,
|
|
47
59
|
use?: ClientUrlUse,
|
|
48
60
|
) => ClientUrlItem & TypedRouteItem<TName, TPattern, unknown, TSearch>;
|
|
49
61
|
|
|
50
62
|
export type ClientLayoutFn = <const TItems extends ClientUrlItems>(
|
|
51
|
-
component:
|
|
63
|
+
component: ClientUrlComponent,
|
|
52
64
|
children: () => TItems,
|
|
53
65
|
) => ClientUrlItem & TypedLayoutItem<ExtractRoutes<TItems>>;
|
|
54
66
|
|
|
@@ -57,11 +69,13 @@ export interface ClientUrlLoaderRecord {
|
|
|
57
69
|
/**
|
|
58
70
|
* Per-loader revalidate() predicates, run on the server; empty = the
|
|
59
71
|
* router's defaults. Under the rango Vite plugin the browser and SSR copies
|
|
60
|
-
* hold the marker the build left in place of each hoisted predicate
|
|
72
|
+
* hold the marker the build left in place of each hoisted predicate; a
|
|
73
|
+
* boolean literal stays in place.
|
|
61
74
|
*/
|
|
62
75
|
readonly revalidate: readonly (
|
|
63
76
|
| ShouldRevalidateFn<any, DefaultEnv>
|
|
64
77
|
| HoistedRevalidateRef
|
|
78
|
+
| boolean
|
|
65
79
|
)[];
|
|
66
80
|
/**
|
|
67
81
|
* loader(Def, { ssr: false }): document renders await this loader before
|
|
@@ -71,11 +85,10 @@ export interface ClientUrlLoaderRecord {
|
|
|
71
85
|
*/
|
|
72
86
|
readonly ssr?: false;
|
|
73
87
|
/**
|
|
74
|
-
* loader(Def, { prefetch: false }): a prefetch of the route
|
|
75
|
-
*
|
|
76
|
-
* {@link LoaderOptions}).
|
|
77
|
-
*
|
|
78
|
-
* urls().
|
|
88
|
+
* loader(Def, { prefetch: false }): a prefetch of the route, or of an
|
|
89
|
+
* intercept's target from its source, defers this loader and the click
|
|
90
|
+
* that adopts it runs it in its fill request (see {@link LoaderOptions}).
|
|
91
|
+
* Projected onto the materialized stub.
|
|
79
92
|
*/
|
|
80
93
|
readonly prefetch?: false;
|
|
81
94
|
}
|
|
@@ -126,7 +139,7 @@ export interface ClientUrlRouteRecord {
|
|
|
126
139
|
|
|
127
140
|
/**
|
|
128
141
|
* A restricted intercept declared inside clientUrls(). Compared to the server
|
|
129
|
-
* intercept() there is no `when` selector,
|
|
142
|
+
* intercept() there is no `when` selector, and the target must
|
|
130
143
|
* be a dot-local NAMED route in the same definition — every field is
|
|
131
144
|
* JSON-projectable, which is what makes the declaration legal in a
|
|
132
145
|
* "use client" module. `targetName` is stored bare (no leading dot).
|
|
@@ -160,8 +173,8 @@ export interface ClientUrlHelpers {
|
|
|
160
173
|
* the destination at once, commits the adopted prefetch with the other
|
|
161
174
|
* loaders' data, and runs only the flagged loaders in its one fill
|
|
162
175
|
* request. A `layout()` loader with the flag is flagged on every route
|
|
163
|
-
* below it. Inside intercept() the
|
|
164
|
-
* urls().
|
|
176
|
+
* below it. Inside intercept() it defers the loader in a prefetch of the
|
|
177
|
+
* intercept from its source, as in urls().
|
|
165
178
|
*/
|
|
166
179
|
readonly loader: <TData>(
|
|
167
180
|
definition: LoaderDefinition<TData>,
|
|
@@ -176,10 +189,11 @@ export interface ClientUrlHelpers {
|
|
|
176
189
|
* names, ...): the build moves it out of this "use client" module. It may
|
|
177
190
|
* reference imports and globals only; a binding of this module, `this`,
|
|
178
191
|
* `import()`, a browser global, an `async` function or an import from a
|
|
179
|
-
* "use client" module fails the build with its file and line.
|
|
192
|
+
* "use client" module fails the build with its file and line. A boolean
|
|
193
|
+
* is shorthand for `() => true` / `() => false`.
|
|
180
194
|
*/
|
|
181
195
|
readonly revalidate: (
|
|
182
|
-
fn: ShouldRevalidateFn<any, DefaultEnv
|
|
196
|
+
fn: ShouldRevalidateFn<any, DefaultEnv> | boolean,
|
|
183
197
|
) => ClientUrlItem;
|
|
184
198
|
/**
|
|
185
199
|
* Declare an intercept for a named route in THIS definition. The target is
|
|
@@ -190,7 +204,7 @@ export interface ClientUrlHelpers {
|
|
|
190
204
|
readonly intercept: (
|
|
191
205
|
slotName: `@${string}`,
|
|
192
206
|
targetName: `.${string}`,
|
|
193
|
-
component:
|
|
207
|
+
component: ClientUrlComponent,
|
|
194
208
|
use?: ClientUrlUse,
|
|
195
209
|
) => ClientUrlItem;
|
|
196
210
|
/**
|
|
@@ -198,7 +212,8 @@ export interface ClientUrlHelpers {
|
|
|
198
212
|
* holds previous content instead of re-streaming the loading() fallback,
|
|
199
213
|
* and on experimental React the config's ViewTransition classes apply.
|
|
200
214
|
* Data-only — no `when` gate (server-executed; declare it in the server
|
|
201
|
-
* tree). Valid inside a path() use callback
|
|
215
|
+
* tree). Valid inside a path() use callback, or a page component's
|
|
216
|
+
* `.use`.
|
|
202
217
|
*/
|
|
203
218
|
readonly transition: (config: ClientTransitionConfig) => ClientUrlItem;
|
|
204
219
|
}
|
|
@@ -215,5 +230,14 @@ export interface ClientUrlPatterns<
|
|
|
215
230
|
readonly intercepts: readonly ClientUrlInterceptRecord[];
|
|
216
231
|
readonly [CLIENT_URL_PATTERNS_BRAND]: void;
|
|
217
232
|
readonly _routes?: TRoutes;
|
|
218
|
-
|
|
233
|
+
/**
|
|
234
|
+
* Match an absolute pathname against the routes joined to `mount`, the
|
|
235
|
+
* include() mount pattern (default "/"); params include the mount's. A
|
|
236
|
+
* `foreign` pattern that wins is a match whose routeKey is that pattern.
|
|
237
|
+
*/
|
|
238
|
+
match(
|
|
239
|
+
pathname: string,
|
|
240
|
+
mount?: string,
|
|
241
|
+
foreign?: readonly string[],
|
|
242
|
+
): TrieMatchResult | null;
|
|
219
243
|
}
|