@rangojs/router 0.0.0-experimental.77 → 0.0.0-experimental.77ed8945
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 +120 -25
- package/dist/bin/rango.js +147 -57
- package/dist/vite/index.js +2103 -861
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +13 -8
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +220 -30
- package/skills/caching/SKILL.md +116 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +66 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +26 -4
- package/skills/layout/SKILL.md +6 -7
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +219 -9
- package/skills/middleware/SKILL.md +47 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +12 -6
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +238 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +33 -4
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/typesafety/SKILL.md +319 -27
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/event-controller.ts +86 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +29 -9
- package/src/browser/navigation-client.ts +99 -77
- package/src/browser/navigation-store.ts +7 -8
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +60 -40
- package/src/browser/prefetch/cache.ts +196 -49
- package/src/browser/prefetch/fetch.ts +203 -59
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/rango-state.ts +37 -13
- package/src/browser/react/Link.tsx +18 -13
- package/src/browser/react/NavigationProvider.tsx +75 -31
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +17 -9
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +23 -2
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +71 -22
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-reconciler.ts +10 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +44 -30
- package/src/browser/types.ts +12 -2
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/index.ts +8 -1
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +45 -1
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +1 -1
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +55 -14
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-runtime.ts +17 -5
- package/src/cache/cache-scope.ts +51 -49
- package/src/cache/cf/cf-cache-store.ts +502 -32
- package/src/cache/cf/index.ts +3 -0
- package/src/cache/handle-snapshot.ts +103 -0
- package/src/cache/index.ts +3 -0
- package/src/cache/memory-segment-store.ts +3 -2
- package/src/cache/types.ts +10 -6
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +96 -205
- package/src/context-var.ts +5 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -4
- package/src/handle.ts +4 -6
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -21
- package/src/index.rsc.ts +10 -6
- package/src/index.ts +17 -8
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +2 -5
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +9 -7
- package/src/prerender.ts +4 -4
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -39
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +253 -265
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +43 -15
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +26 -41
- package/src/router/content-negotiation.ts +15 -2
- package/src/router/error-handling.ts +1 -1
- package/src/router/find-match.ts +54 -6
- package/src/router/handler-context.ts +21 -41
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/lazy-includes.ts +41 -22
- package/src/router/loader-resolution.ts +82 -36
- package/src/router/manifest.ts +41 -19
- package/src/router/match-api.ts +4 -3
- package/src/router/match-handlers.ts +1 -0
- package/src/router/match-middleware/cache-lookup.ts +57 -95
- package/src/router/match-middleware/cache-store.ts +3 -2
- package/src/router/match-result.ts +53 -32
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +15 -26
- package/src/router/middleware.ts +99 -84
- package/src/router/pattern-matching.ts +116 -19
- package/src/router/prerender-match.ts +40 -15
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +40 -37
- package/src/router/revalidation.ts +58 -2
- package/src/router/router-interfaces.ts +51 -35
- package/src/router/router-options.ts +25 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +27 -6
- package/src/router/segment-resolution/revalidation.ts +147 -106
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/trie-matching.ts +40 -16
- package/src/router/types.ts +8 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +37 -25
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +58 -77
- package/src/rsc/helpers.ts +72 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +30 -10
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +76 -61
- package/src/rsc/rsc-rendering.ts +45 -51
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +33 -39
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +8 -2
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +132 -116
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +175 -53
- package/src/server/cookie-store.ts +28 -4
- package/src/server/request-context.ts +57 -51
- package/src/ssr/index.tsx +5 -1
- package/src/static-handler.ts +1 -1
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +68 -50
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +11 -0
- package/src/types/segments.ts +35 -2
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +1 -5
- package/src/urls/path-helper-types.ts +17 -3
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +413 -42
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +6 -6
- package/src/vite/discovery/discover-routers.ts +106 -75
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +72 -31
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +33 -0
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +2 -0
- package/src/vite/plugin-types.ts +67 -0
- package/src/vite/plugins/cjs-to-esm.ts +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- 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/expose-action-id.ts +54 -30
- package/src/vite/plugins/expose-id-utils.ts +12 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +496 -486
- package/src/vite/plugins/performance-tracks.ts +29 -25
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +116 -29
- package/src/vite/router-discovery.ts +753 -104
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +8 -59
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +5 -4
- package/src/vite/utils/shared-utils.ts +107 -26
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -11,12 +11,16 @@ import {
|
|
|
11
11
|
getContext,
|
|
12
12
|
getNamePrefix,
|
|
13
13
|
getUrlPrefix,
|
|
14
|
+
requireDslContext,
|
|
14
15
|
type EntryData,
|
|
16
|
+
type EntryPropDatas,
|
|
17
|
+
type EntryPropSegments,
|
|
18
|
+
type HelperContext,
|
|
15
19
|
type InterceptEntry,
|
|
16
20
|
} from "../server/context";
|
|
17
21
|
import { invariant } from "../errors";
|
|
18
22
|
import { isCachedFunction } from "../cache/taint.js";
|
|
19
|
-
import {
|
|
23
|
+
import { RangoContext } from "../server/context";
|
|
20
24
|
import { isStaticHandler } from "../static-handler.js";
|
|
21
25
|
import RootLayout from "../server/root-layout";
|
|
22
26
|
import type {
|
|
@@ -38,6 +42,7 @@ import type {
|
|
|
38
42
|
} from "../route-types.js";
|
|
39
43
|
import type { RouteHelpers } from "./helpers-types.js";
|
|
40
44
|
import { resolveHandlerUse, mergeHandlerUse } from "./resolve-handler-use.js";
|
|
45
|
+
import { ALL_USE_ITEM_TYPES } from "./use-item-types.js";
|
|
41
46
|
|
|
42
47
|
/**
|
|
43
48
|
* Check if an item contains routes (directly or inside nested structures like cache).
|
|
@@ -61,16 +66,105 @@ const hasRoutesInItem = (item: AllUseItems): boolean => {
|
|
|
61
66
|
return false;
|
|
62
67
|
};
|
|
63
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Fresh empty collections shared by every from-scratch segment entry. Returns
|
|
71
|
+
* new arrays/objects per call so no two entries share mutable references.
|
|
72
|
+
* mountPath is intentionally NOT included here — each call site adds it from
|
|
73
|
+
* getUrlPrefix() where applicable: the route() and transition() helpers add
|
|
74
|
+
* none, while path() (which also builds a `type: "route"` entry) and the
|
|
75
|
+
* structural helpers (layout/cache/middleware/parallel) do.
|
|
76
|
+
*/
|
|
77
|
+
const emptySegmentBase = (): EntryPropDatas &
|
|
78
|
+
EntryPropSegments & { loading: undefined } => ({
|
|
79
|
+
loading: undefined,
|
|
80
|
+
middleware: [],
|
|
81
|
+
revalidate: [],
|
|
82
|
+
errorBoundary: [],
|
|
83
|
+
notFoundBoundary: [],
|
|
84
|
+
layout: [],
|
|
85
|
+
parallel: {},
|
|
86
|
+
intercept: [],
|
|
87
|
+
loader: [],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Run a children/use callback as a nested scope, flatten the result, and assert
|
|
92
|
+
* every item is a valid use item. `kind` preserves the existing error wording
|
|
93
|
+
* ("use()" vs "children" callback).
|
|
94
|
+
*/
|
|
95
|
+
function runAndValidateUseItems(
|
|
96
|
+
store: ReturnType<typeof getContext>,
|
|
97
|
+
namespace: string,
|
|
98
|
+
entry: EntryData,
|
|
99
|
+
cb: () => any,
|
|
100
|
+
label: string,
|
|
101
|
+
kind: "use" | "children",
|
|
102
|
+
): AllUseItems[] {
|
|
103
|
+
const result = store.run(namespace, entry, cb)?.flat(3);
|
|
104
|
+
return validateUseItems(result, namespace, label, kind);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Assert an already-invoked, flattened callback result is a use-item array. */
|
|
108
|
+
function validateUseItems(
|
|
109
|
+
result: any,
|
|
110
|
+
namespace: string,
|
|
111
|
+
label: string,
|
|
112
|
+
kind: "use" | "children",
|
|
113
|
+
): AllUseItems[] {
|
|
114
|
+
invariant(
|
|
115
|
+
Array.isArray(result) && result.every((item) => isValidUseItem(item)),
|
|
116
|
+
`${label}() ${kind === "use" ? "use()" : "children"} callback must return an array of use items [${namespace}]`,
|
|
117
|
+
);
|
|
118
|
+
return result as AllUseItems[];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** True when a children/use result contains no routes (directly or nested). */
|
|
122
|
+
const isOrphan = (result: AllUseItems[]): boolean =>
|
|
123
|
+
!result.some((item) => item != null && hasRoutesInItem(item));
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Register a routeless structural entry as an orphan sibling: clear its parent
|
|
127
|
+
* pointer so it leaves the middleware/parent-pointer chain (LOAD-BEARING — see
|
|
128
|
+
* docs/tree-structure.md) and push it onto the parent's layout[] so it renders
|
|
129
|
+
* as a wrapper. Used by cache()/middleware()/transition(); layout() runs extra
|
|
130
|
+
* validation and registers inline.
|
|
131
|
+
*/
|
|
132
|
+
const attachOrphanSibling = (
|
|
133
|
+
parent: EntryData | null,
|
|
134
|
+
entry: EntryData,
|
|
135
|
+
): void => {
|
|
136
|
+
entry.parent = null;
|
|
137
|
+
if (parent && "layout" in parent) parent.layout.push(entry);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Run `fn` with `ctx.parent` temporarily redirected to `temp` — a satellite
|
|
142
|
+
* entry that captures the attachments declared by a use() callback — restoring
|
|
143
|
+
* the original parent afterward, including on throw. loader()/intercept() each
|
|
144
|
+
* build their own tempParent shape (intercept keeps a loading get/set accessor
|
|
145
|
+
* and a captured-layouts array); this only centralizes the save/restore.
|
|
146
|
+
*/
|
|
147
|
+
function withParent<T>(ctx: HelperContext, temp: EntryData, fn: () => T): T {
|
|
148
|
+
const original = ctx.parent;
|
|
149
|
+
ctx.parent = temp;
|
|
150
|
+
try {
|
|
151
|
+
return fn();
|
|
152
|
+
} finally {
|
|
153
|
+
ctx.parent = original;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
64
157
|
const revalidate: RouteHelpers<any, any>["revalidate"] = (fn) => {
|
|
65
|
-
const ctx =
|
|
66
|
-
|
|
158
|
+
const { store, ctx } = requireDslContext(
|
|
159
|
+
"revalidate() must be called inside urls()",
|
|
160
|
+
);
|
|
67
161
|
|
|
68
162
|
// Attach to last entry in stack
|
|
69
163
|
const parent = ctx.parent;
|
|
70
164
|
if (!parent || !("revalidate" in parent)) {
|
|
71
165
|
invariant(false, "No parent entry available for revalidate()");
|
|
72
166
|
}
|
|
73
|
-
const name = `$${
|
|
167
|
+
const name = `$${store.getNextIndex("revalidate")}`;
|
|
74
168
|
parent.revalidate.push(fn);
|
|
75
169
|
return { name, type: "revalidate" } as RevalidateItem;
|
|
76
170
|
};
|
|
@@ -108,15 +202,16 @@ const revalidate: RouteHelpers<any, any>["revalidate"] = (fn) => {
|
|
|
108
202
|
* ```
|
|
109
203
|
*/
|
|
110
204
|
const errorBoundary: RouteHelpers<any, any>["errorBoundary"] = (fallback) => {
|
|
111
|
-
const ctx =
|
|
112
|
-
|
|
205
|
+
const { store, ctx } = requireDslContext(
|
|
206
|
+
"errorBoundary() must be called inside urls()",
|
|
207
|
+
);
|
|
113
208
|
|
|
114
209
|
// Attach to parent entry in stack
|
|
115
210
|
const parent = ctx.parent;
|
|
116
211
|
if (!parent || !("errorBoundary" in parent)) {
|
|
117
212
|
invariant(false, "No parent entry available for errorBoundary()");
|
|
118
213
|
}
|
|
119
|
-
const name = `$${
|
|
214
|
+
const name = `$${store.getNextIndex("errorBoundary")}`;
|
|
120
215
|
parent.errorBoundary.push(fallback);
|
|
121
216
|
return { name, type: "errorBoundary" } as ErrorBoundaryItem;
|
|
122
217
|
};
|
|
@@ -155,15 +250,16 @@ const errorBoundary: RouteHelpers<any, any>["errorBoundary"] = (fallback) => {
|
|
|
155
250
|
const notFoundBoundary: RouteHelpers<any, any>["notFoundBoundary"] = (
|
|
156
251
|
fallback,
|
|
157
252
|
) => {
|
|
158
|
-
const ctx =
|
|
159
|
-
|
|
253
|
+
const { store, ctx } = requireDslContext(
|
|
254
|
+
"notFoundBoundary() must be called inside urls()",
|
|
255
|
+
);
|
|
160
256
|
|
|
161
257
|
// Attach to parent entry in stack
|
|
162
258
|
const parent = ctx.parent;
|
|
163
259
|
if (!parent || !("notFoundBoundary" in parent)) {
|
|
164
260
|
invariant(false, "No parent entry available for notFoundBoundary()");
|
|
165
261
|
}
|
|
166
|
-
const name = `$${
|
|
262
|
+
const name = `$${store.getNextIndex("notFoundBoundary")}`;
|
|
167
263
|
parent.notFoundBoundary.push(fallback);
|
|
168
264
|
return { name, type: "notFoundBoundary" } as NotFoundBoundaryItem;
|
|
169
265
|
};
|
|
@@ -177,8 +273,9 @@ const notFoundBoundary: RouteHelpers<any, any>["notFoundBoundary"] = (
|
|
|
177
273
|
* for the intercept to activate.
|
|
178
274
|
*/
|
|
179
275
|
const when: RouteHelpers<any, any>["when"] = (fn) => {
|
|
180
|
-
const ctx =
|
|
181
|
-
|
|
276
|
+
const { store, ctx } = requireDslContext(
|
|
277
|
+
"when() must be called inside intercept()",
|
|
278
|
+
);
|
|
182
279
|
|
|
183
280
|
// The when() function needs to be captured by the intercept's tempParent
|
|
184
281
|
// which should have a `when` array. If not present, we're not inside intercept()
|
|
@@ -190,7 +287,7 @@ const when: RouteHelpers<any, any>["when"] = (fn) => {
|
|
|
190
287
|
);
|
|
191
288
|
}
|
|
192
289
|
|
|
193
|
-
const name = `$${
|
|
290
|
+
const name = `$${store.getNextIndex("when")}`;
|
|
194
291
|
parent.when.push(fn);
|
|
195
292
|
return { name, type: "when" } as WhenItem;
|
|
196
293
|
};
|
|
@@ -217,9 +314,9 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
217
314
|
| (() => UseItems<AllUseItems>),
|
|
218
315
|
maybeChildren?: () => UseItems<AllUseItems>,
|
|
219
316
|
) => {
|
|
220
|
-
const store =
|
|
221
|
-
|
|
222
|
-
|
|
317
|
+
const { store, ctx } = requireDslContext(
|
|
318
|
+
"cache() must be called inside urls()",
|
|
319
|
+
);
|
|
223
320
|
|
|
224
321
|
// Handle overloaded signature
|
|
225
322
|
let options: PartialCacheOptions | false;
|
|
@@ -232,7 +329,7 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
232
329
|
} else if (typeof optionsOrChildren === "string") {
|
|
233
330
|
// cache('profileName') or cache('profileName', () => [...])
|
|
234
331
|
// Resolve from context-scoped profiles (set per-router via HelperContext).
|
|
235
|
-
const ctxStore =
|
|
332
|
+
const ctxStore = RangoContext.getStore();
|
|
236
333
|
const profile = ctxStore?.cacheProfiles?.[optionsOrChildren];
|
|
237
334
|
invariant(
|
|
238
335
|
profile,
|
|
@@ -271,26 +368,18 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
271
368
|
// Create orphan cache entry (like orphan layout)
|
|
272
369
|
// Subsequent siblings in the same array will attach to this entry
|
|
273
370
|
const namespace = `${ctx.namespace}.${cacheIndex}`;
|
|
274
|
-
const
|
|
371
|
+
const urlPrefix = getUrlPrefix();
|
|
275
372
|
|
|
276
373
|
const entry = {
|
|
374
|
+
...emptySegmentBase(),
|
|
277
375
|
id: namespace,
|
|
278
376
|
shortCode: store.getShortCode("cache"),
|
|
279
377
|
type: "cache",
|
|
280
378
|
parent: parent, // link to current parent for hierarchy
|
|
281
379
|
cache: cacheConfig,
|
|
282
380
|
handler: RootLayout,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
revalidate: [],
|
|
286
|
-
errorBoundary: [],
|
|
287
|
-
notFoundBoundary: [],
|
|
288
|
-
layout: [],
|
|
289
|
-
parallel: {},
|
|
290
|
-
intercept: [],
|
|
291
|
-
loader: [],
|
|
292
|
-
...(cacheUrlPrefix ? { mountPath: cacheUrlPrefix } : {}),
|
|
293
|
-
} as EntryData;
|
|
381
|
+
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
382
|
+
} satisfies EntryData;
|
|
294
383
|
|
|
295
384
|
// Attach to parent's layout array (cache entries are structural like layouts)
|
|
296
385
|
if (parent && "layout" in parent) {
|
|
@@ -304,13 +393,23 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
304
393
|
return { name: namespace, type: "cache" } as CacheItem;
|
|
305
394
|
}
|
|
306
395
|
|
|
396
|
+
// Inside a loader() use() callback, only the direct form — cache()/cache(opts)/
|
|
397
|
+
// cache("profile") — writes cache config to the loader entry. The wrapper
|
|
398
|
+
// form creates a structural cache boundary with its own children scope, which
|
|
399
|
+
// has no effect on the loader and would silently no-op.
|
|
400
|
+
invariant(
|
|
401
|
+
!(ctx.parent && (ctx.parent as any).type === "loader"),
|
|
402
|
+
"cache() wrapper form is not valid inside loader() use(). Use cache({...}) without children to configure the loader's cache.",
|
|
403
|
+
);
|
|
404
|
+
|
|
307
405
|
// With children: create a cache entry (like layout with caching semantics)
|
|
308
406
|
const namespace = `${ctx.namespace}.${cacheIndex}`;
|
|
309
407
|
const cacheShortCode = store.getShortCode("cache");
|
|
310
408
|
|
|
311
|
-
const
|
|
409
|
+
const urlPrefix = getUrlPrefix();
|
|
312
410
|
|
|
313
411
|
const entry = {
|
|
412
|
+
...emptySegmentBase(),
|
|
314
413
|
id: namespace,
|
|
315
414
|
shortCode: cacheShortCode,
|
|
316
415
|
type: "cache",
|
|
@@ -318,40 +417,22 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
318
417
|
cache: cacheConfig,
|
|
319
418
|
// Cache entries render like layouts (with Outlet as default handler)
|
|
320
419
|
handler: RootLayout, // RootLayout just renders <Outlet />
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
revalidate: [],
|
|
324
|
-
errorBoundary: [],
|
|
325
|
-
notFoundBoundary: [],
|
|
326
|
-
layout: [],
|
|
327
|
-
parallel: {},
|
|
328
|
-
intercept: [],
|
|
329
|
-
loader: [],
|
|
330
|
-
...(cacheUrlPrefix2 ? { mountPath: cacheUrlPrefix2 } : {}),
|
|
331
|
-
} as EntryData;
|
|
420
|
+
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
421
|
+
} satisfies EntryData;
|
|
332
422
|
|
|
333
423
|
// Run children with cache entry as parent
|
|
334
|
-
const result =
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
424
|
+
const result = runAndValidateUseItems(
|
|
425
|
+
store,
|
|
426
|
+
namespace,
|
|
427
|
+
entry,
|
|
428
|
+
children,
|
|
429
|
+
"cache",
|
|
430
|
+
"children",
|
|
339
431
|
);
|
|
340
432
|
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
Array.isArray(result) &&
|
|
345
|
-
result.some((item) => hasRoutesInItem(item));
|
|
346
|
-
|
|
347
|
-
if (!hasRoutes) {
|
|
348
|
-
const parent = ctx.parent;
|
|
349
|
-
if (parent && "layout" in parent) {
|
|
350
|
-
// Attach to parent's layout array (cache entries are structural like layouts)
|
|
351
|
-
entry.parent = null;
|
|
352
|
-
parent.layout.push(entry);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
433
|
+
// Cache entries are structural like layouts: with no routes inside, register
|
|
434
|
+
// as an orphan sibling.
|
|
435
|
+
if (isOrphan(result)) attachOrphanSibling(ctx.parent, entry);
|
|
355
436
|
|
|
356
437
|
return { name: namespace, type: "cache", uses: result } as CacheItem;
|
|
357
438
|
};
|
|
@@ -397,9 +478,9 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
397
478
|
}
|
|
398
479
|
}
|
|
399
480
|
|
|
400
|
-
const store =
|
|
401
|
-
|
|
402
|
-
|
|
481
|
+
const { store, ctx } = requireDslContext(
|
|
482
|
+
"middleware() must be called inside urls()",
|
|
483
|
+
);
|
|
403
484
|
|
|
404
485
|
if (!children) {
|
|
405
486
|
// Sibling mode: attach to parent entry
|
|
@@ -418,22 +499,15 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
418
499
|
|
|
419
500
|
const urlPrefix = getUrlPrefix();
|
|
420
501
|
const entry = {
|
|
502
|
+
...emptySegmentBase(),
|
|
421
503
|
id: namespace,
|
|
422
504
|
shortCode: store.getShortCode("layout"),
|
|
423
505
|
type: "layout",
|
|
424
506
|
parent: ctx.parent,
|
|
425
507
|
handler: RootLayout,
|
|
426
|
-
loading: undefined,
|
|
427
508
|
middleware: [...fns],
|
|
428
|
-
revalidate: [],
|
|
429
|
-
errorBoundary: [],
|
|
430
|
-
notFoundBoundary: [],
|
|
431
|
-
layout: [],
|
|
432
|
-
parallel: {},
|
|
433
|
-
intercept: [],
|
|
434
|
-
loader: [],
|
|
435
509
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
436
|
-
}
|
|
510
|
+
} satisfies EntryData;
|
|
437
511
|
|
|
438
512
|
// Run children callback. If the second arg was actually a middleware fn
|
|
439
513
|
// (old variadic form: middleware(mw1, mw2)), this will return a non-array
|
|
@@ -446,25 +520,14 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
446
520
|
"To pass multiple middleware, use middleware([fn1, fn2]).",
|
|
447
521
|
);
|
|
448
522
|
|
|
449
|
-
const result =
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
523
|
+
const result = validateUseItems(
|
|
524
|
+
rawResult.flat(3),
|
|
525
|
+
namespace,
|
|
526
|
+
"middleware",
|
|
527
|
+
"children",
|
|
454
528
|
);
|
|
455
529
|
|
|
456
|
-
|
|
457
|
-
result &&
|
|
458
|
-
Array.isArray(result) &&
|
|
459
|
-
result.some((item) => item != null && hasRoutesInItem(item));
|
|
460
|
-
|
|
461
|
-
if (!hasRoutes) {
|
|
462
|
-
const parent = ctx.parent;
|
|
463
|
-
if (parent && "layout" in parent) {
|
|
464
|
-
entry.parent = null;
|
|
465
|
-
parent.layout.push(entry);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
530
|
+
if (isOrphan(result)) attachOrphanSibling(ctx.parent, entry);
|
|
468
531
|
|
|
469
532
|
return {
|
|
470
533
|
name: namespace,
|
|
@@ -474,9 +537,9 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
474
537
|
};
|
|
475
538
|
|
|
476
539
|
const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
477
|
-
const store =
|
|
478
|
-
|
|
479
|
-
|
|
540
|
+
const { store, ctx } = requireDslContext(
|
|
541
|
+
"parallel() must be called inside urls()",
|
|
542
|
+
);
|
|
480
543
|
|
|
481
544
|
if (!ctx.parent || !ctx.parent?.parallel) {
|
|
482
545
|
invariant(false, "No parent entry available for parallel()");
|
|
@@ -528,20 +591,12 @@ const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
|
528
591
|
// Create full EntryData for parallel with its own loaders/revalidate/loading
|
|
529
592
|
const parallelUrlPrefix = getUrlPrefix();
|
|
530
593
|
const entry = {
|
|
594
|
+
...emptySegmentBase(),
|
|
531
595
|
id: namespace,
|
|
532
596
|
shortCode: store.getShortCode("parallel"),
|
|
533
597
|
type: "parallel",
|
|
534
598
|
parent: null, // Parallels don't participate in parent chain traversal
|
|
535
599
|
handler: unwrappedSlots,
|
|
536
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
537
|
-
middleware: [],
|
|
538
|
-
revalidate: [],
|
|
539
|
-
errorBoundary: [],
|
|
540
|
-
notFoundBoundary: [],
|
|
541
|
-
layout: [],
|
|
542
|
-
parallel: {},
|
|
543
|
-
intercept: [],
|
|
544
|
-
loader: [],
|
|
545
600
|
...(parallelUrlPrefix ? { mountPath: parallelUrlPrefix } : {}),
|
|
546
601
|
...(hasStaticSlot
|
|
547
602
|
? {
|
|
@@ -596,10 +651,13 @@ const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
|
596
651
|
"parallel",
|
|
597
652
|
);
|
|
598
653
|
if (slotMergedUse) {
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
654
|
+
runAndValidateUseItems(
|
|
655
|
+
store,
|
|
656
|
+
namespace,
|
|
657
|
+
slotEntry,
|
|
658
|
+
slotMergedUse,
|
|
659
|
+
"parallel",
|
|
660
|
+
"use",
|
|
603
661
|
);
|
|
604
662
|
}
|
|
605
663
|
|
|
@@ -639,9 +697,9 @@ const intercept = (
|
|
|
639
697
|
handler: any,
|
|
640
698
|
use?: () => any[],
|
|
641
699
|
) => {
|
|
642
|
-
const store =
|
|
643
|
-
|
|
644
|
-
|
|
700
|
+
const { store, ctx } = requireDslContext(
|
|
701
|
+
"intercept() must be called inside urls()",
|
|
702
|
+
);
|
|
645
703
|
|
|
646
704
|
if (!ctx.parent || !ctx.parent?.intercept) {
|
|
647
705
|
invariant(false, "No parent entry available for intercept()");
|
|
@@ -680,15 +738,13 @@ const intercept = (
|
|
|
680
738
|
|
|
681
739
|
// Run merged use callback to collect loaders, revalidate, middleware, etc.
|
|
682
740
|
if (mergedUse) {
|
|
683
|
-
//
|
|
684
|
-
// so that middleware, loader, revalidate attach to the intercept entry
|
|
685
|
-
const originalParent = ctx.parent;
|
|
686
|
-
|
|
687
|
-
// Capture layouts in a temporary array
|
|
741
|
+
// Capture layout() calls into a temporary array
|
|
688
742
|
const capturedLayouts: EntryData[] = [];
|
|
689
743
|
|
|
744
|
+
// Temporary parent so middleware/loader/revalidate/when attach to the
|
|
745
|
+
// intercept entry; the loading get/set accessor mirrors writes onto `entry`.
|
|
690
746
|
const tempParent = {
|
|
691
|
-
...
|
|
747
|
+
...ctx.parent,
|
|
692
748
|
middleware: entry.middleware,
|
|
693
749
|
revalidate: entry.revalidate,
|
|
694
750
|
errorBoundary: entry.errorBoundary,
|
|
@@ -696,7 +752,6 @@ const intercept = (
|
|
|
696
752
|
loader: entry.loader,
|
|
697
753
|
layout: capturedLayouts, // Capture layout() calls
|
|
698
754
|
when: entry.when, // Capture when() conditions
|
|
699
|
-
// Use getter/setter to capture loading on the entry
|
|
700
755
|
get loading() {
|
|
701
756
|
return entry.loading;
|
|
702
757
|
},
|
|
@@ -704,12 +759,10 @@ const intercept = (
|
|
|
704
759
|
entry.loading = value;
|
|
705
760
|
},
|
|
706
761
|
};
|
|
707
|
-
ctx.parent = tempParent as EntryData;
|
|
708
|
-
|
|
709
|
-
const result = mergedUse()?.flat(3);
|
|
710
762
|
|
|
711
|
-
|
|
712
|
-
|
|
763
|
+
const result = withParent(ctx, tempParent as EntryData, () =>
|
|
764
|
+
mergedUse()?.flat(3),
|
|
765
|
+
);
|
|
713
766
|
|
|
714
767
|
// Extract layout from captured layouts (use first one if multiple)
|
|
715
768
|
// Layout inside intercept should always be ReactNode or Handler, not Record slots
|
|
@@ -719,10 +772,7 @@ const intercept = (
|
|
|
719
772
|
| Handler<any, any, any>;
|
|
720
773
|
}
|
|
721
774
|
|
|
722
|
-
|
|
723
|
-
Array.isArray(result) && result.every((item) => isValidUseItem(item)),
|
|
724
|
-
`intercept() use() callback must return an array of use items [${namespace}]`,
|
|
725
|
-
);
|
|
775
|
+
validateUseItems(result, namespace, "intercept", "use");
|
|
726
776
|
}
|
|
727
777
|
|
|
728
778
|
ctx.parent.intercept.push(entry);
|
|
@@ -732,10 +782,10 @@ const intercept = (
|
|
|
732
782
|
/**
|
|
733
783
|
* Loader helper - attaches a loader to the current entry
|
|
734
784
|
*/
|
|
735
|
-
const
|
|
736
|
-
const store =
|
|
737
|
-
|
|
738
|
-
|
|
785
|
+
const loader: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
786
|
+
const { store, ctx } = requireDslContext(
|
|
787
|
+
"loader() must be called inside urls()",
|
|
788
|
+
);
|
|
739
789
|
|
|
740
790
|
// Attach to last entry in stack
|
|
741
791
|
if (!ctx.parent || !ctx.parent?.loader) {
|
|
@@ -750,25 +800,28 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
750
800
|
revalidate: [] as ShouldRevalidateFn<any, any>[],
|
|
751
801
|
};
|
|
752
802
|
|
|
753
|
-
//
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
803
|
+
// Merge handler.use defaults (attached to the loader definition) with explicit use
|
|
804
|
+
const handlerUseFn = resolveHandlerUse(loaderDef);
|
|
805
|
+
const mergedUse = mergeHandlerUse(handlerUseFn, use, "loader");
|
|
806
|
+
|
|
807
|
+
// If any use callback is in effect, run it to collect revalidation rules and cache config
|
|
808
|
+
if (mergedUse) {
|
|
757
809
|
// Create a temporary "parent" with type "loader" so cache() can detect it.
|
|
758
810
|
// Save existing .cache to distinguish inherited config from newly set config.
|
|
759
|
-
const parentCache = (
|
|
811
|
+
const parentCache = (ctx.parent as any).cache;
|
|
760
812
|
const tempParent = {
|
|
761
|
-
...
|
|
813
|
+
...ctx.parent,
|
|
762
814
|
type: "loader",
|
|
763
815
|
revalidate: loaderEntry.revalidate,
|
|
764
816
|
};
|
|
765
|
-
ctx.parent = tempParent as EntryData;
|
|
766
817
|
|
|
767
|
-
const result =
|
|
818
|
+
const result = withParent(ctx, tempParent as EntryData, () =>
|
|
819
|
+
mergedUse()?.flat(3),
|
|
820
|
+
);
|
|
768
821
|
|
|
769
822
|
// Copy cache config only if cache() was called during the use() callback.
|
|
770
|
-
// The spread
|
|
771
|
-
//
|
|
823
|
+
// The spread may carry an inherited .cache from a parent cache() boundary —
|
|
824
|
+
// only copy if it was newly set.
|
|
772
825
|
if (
|
|
773
826
|
(tempParent as any).cache &&
|
|
774
827
|
(tempParent as any).cache !== parentCache
|
|
@@ -776,13 +829,7 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
776
829
|
(loaderEntry as any).cache = (tempParent as any).cache;
|
|
777
830
|
}
|
|
778
831
|
|
|
779
|
-
|
|
780
|
-
ctx.parent = originalParent;
|
|
781
|
-
|
|
782
|
-
invariant(
|
|
783
|
-
Array.isArray(result) && result.every((item) => isValidUseItem(item)),
|
|
784
|
-
`loader() use() callback must return an array of use items [${name}]`,
|
|
785
|
-
);
|
|
832
|
+
validateUseItems(result, name, "loader", "use");
|
|
786
833
|
}
|
|
787
834
|
|
|
788
835
|
ctx.parent.loader.push(loaderEntry);
|
|
@@ -793,10 +840,10 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
793
840
|
* Loading helper - attaches a loading component to the current entry
|
|
794
841
|
* Loading components are static (no context) and shown during navigation
|
|
795
842
|
*/
|
|
796
|
-
const
|
|
797
|
-
const store =
|
|
798
|
-
|
|
799
|
-
|
|
843
|
+
const loading: RouteHelpers<any, any>["loading"] = (component, options) => {
|
|
844
|
+
const { store, ctx } = requireDslContext(
|
|
845
|
+
"loading() must be called inside urls()",
|
|
846
|
+
);
|
|
800
847
|
|
|
801
848
|
const parent = ctx.parent;
|
|
802
849
|
if (!parent || !("loading" in parent)) {
|
|
@@ -819,10 +866,13 @@ const loadingFn: RouteHelpers<any, any>["loading"] = (component, options) => {
|
|
|
819
866
|
};
|
|
820
867
|
|
|
821
868
|
/**
|
|
822
|
-
* Transition helper -
|
|
823
|
-
*
|
|
869
|
+
* Transition helper - opts the entry (or a wrapped group of routes) into
|
|
870
|
+
* transition-driven navigation by attaching a TransitionConfig. This drives the
|
|
871
|
+
* commit through startTransition (content hold on all React versions) and, on
|
|
872
|
+
* experimental React, places a `<ViewTransition>` boundary unless
|
|
873
|
+
* `viewTransition: false`. See skills/view-transitions for the matrix.
|
|
824
874
|
*/
|
|
825
|
-
const
|
|
875
|
+
const transition = (
|
|
826
876
|
configOrChildren?: TransitionConfig | (() => UseItems<AllUseItems>),
|
|
827
877
|
maybeChildren?: () => UseItems<AllUseItems>,
|
|
828
878
|
): TransitionItem => {
|
|
@@ -836,9 +886,9 @@ const transitionFn = (
|
|
|
836
886
|
const children: (() => UseItems<AllUseItems>) | undefined =
|
|
837
887
|
typeof configOrChildren === "function" ? configOrChildren : maybeChildren;
|
|
838
888
|
|
|
839
|
-
const store =
|
|
840
|
-
|
|
841
|
-
|
|
889
|
+
const { store, ctx } = requireDslContext(
|
|
890
|
+
"transition() must be called inside urls()",
|
|
891
|
+
);
|
|
842
892
|
|
|
843
893
|
const name = `$${store.getNextIndex("transition")}`;
|
|
844
894
|
|
|
@@ -855,68 +905,43 @@ const transitionFn = (
|
|
|
855
905
|
// Position 2: wrapper — create a transparent layout with transition config
|
|
856
906
|
const namespace = `${ctx.namespace}.${store.getNextIndex("transition")}`;
|
|
857
907
|
const entry = {
|
|
908
|
+
...emptySegmentBase(),
|
|
858
909
|
id: namespace,
|
|
859
910
|
shortCode: store.getShortCode("layout"),
|
|
860
911
|
type: "layout",
|
|
861
912
|
parent: ctx.parent,
|
|
862
913
|
handler: RootLayout,
|
|
863
|
-
loading: undefined,
|
|
864
914
|
transition: config,
|
|
865
|
-
|
|
866
|
-
revalidate: [],
|
|
867
|
-
errorBoundary: [],
|
|
868
|
-
notFoundBoundary: [],
|
|
869
|
-
layout: [],
|
|
870
|
-
parallel: {},
|
|
871
|
-
intercept: [],
|
|
872
|
-
loader: [],
|
|
873
|
-
} as EntryData;
|
|
874
|
-
|
|
875
|
-
const result = store.run(namespace, entry, children)?.flat(3);
|
|
915
|
+
} satisfies EntryData;
|
|
876
916
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
917
|
+
const result = runAndValidateUseItems(
|
|
918
|
+
store,
|
|
919
|
+
namespace,
|
|
920
|
+
entry,
|
|
921
|
+
children,
|
|
922
|
+
"transition",
|
|
923
|
+
"children",
|
|
880
924
|
);
|
|
881
925
|
|
|
882
|
-
|
|
883
|
-
result &&
|
|
884
|
-
Array.isArray(result) &&
|
|
885
|
-
result.some((item) => hasRoutesInItem(item));
|
|
886
|
-
|
|
887
|
-
if (!hasRoutes) {
|
|
888
|
-
const parent = ctx.parent;
|
|
889
|
-
if (parent && "layout" in parent) {
|
|
890
|
-
entry.parent = null;
|
|
891
|
-
parent.layout.push(entry);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
926
|
+
if (isOrphan(result)) attachOrphanSibling(ctx.parent, entry);
|
|
894
927
|
|
|
895
928
|
return { name: namespace, type: "transition" } as TransitionItem;
|
|
896
929
|
};
|
|
897
930
|
|
|
898
|
-
const
|
|
899
|
-
const store =
|
|
900
|
-
|
|
901
|
-
|
|
931
|
+
const route: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
932
|
+
const { store, ctx } = requireDslContext(
|
|
933
|
+
"route() must be called inside urls()",
|
|
934
|
+
);
|
|
902
935
|
|
|
903
936
|
const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${name}`;
|
|
904
937
|
|
|
905
938
|
const entry = {
|
|
939
|
+
...emptySegmentBase(),
|
|
906
940
|
id: namespace,
|
|
907
941
|
shortCode: store.getShortCode("route"),
|
|
908
942
|
type: "route",
|
|
909
943
|
parent: ctx.parent,
|
|
910
944
|
handler: handler as unknown as Handler<any, any, any>,
|
|
911
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
912
|
-
middleware: [],
|
|
913
|
-
revalidate: [],
|
|
914
|
-
errorBoundary: [],
|
|
915
|
-
notFoundBoundary: [],
|
|
916
|
-
layout: [],
|
|
917
|
-
parallel: {},
|
|
918
|
-
intercept: [],
|
|
919
|
-
loader: [],
|
|
920
945
|
} satisfies EntryData;
|
|
921
946
|
|
|
922
947
|
/* We will throw if user is registring same route name twice */
|
|
@@ -931,10 +956,13 @@ const routeFn: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
|
931
956
|
const mergedUse = mergeHandlerUse(handlerUseFn, use, "route");
|
|
932
957
|
/* Run use and attach handlers */
|
|
933
958
|
if (mergedUse) {
|
|
934
|
-
const result =
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
959
|
+
const result = runAndValidateUseItems(
|
|
960
|
+
store,
|
|
961
|
+
namespace,
|
|
962
|
+
entry,
|
|
963
|
+
mergedUse,
|
|
964
|
+
"route",
|
|
965
|
+
"use",
|
|
938
966
|
);
|
|
939
967
|
return { name: namespace, type: "route", uses: result } as RouteItem;
|
|
940
968
|
}
|
|
@@ -944,9 +972,9 @@ const routeFn: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
|
944
972
|
};
|
|
945
973
|
|
|
946
974
|
const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
947
|
-
const store =
|
|
948
|
-
|
|
949
|
-
|
|
975
|
+
const { store, ctx } = requireDslContext(
|
|
976
|
+
"layout() must be called inside urls()",
|
|
977
|
+
);
|
|
950
978
|
|
|
951
979
|
invariant(
|
|
952
980
|
!ctx.parent || ctx.parent.type !== "parallel",
|
|
@@ -964,20 +992,12 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
964
992
|
|
|
965
993
|
const urlPrefix = getUrlPrefix();
|
|
966
994
|
const entry = {
|
|
995
|
+
...emptySegmentBase(),
|
|
967
996
|
id: namespace,
|
|
968
997
|
shortCode,
|
|
969
998
|
type: "layout",
|
|
970
999
|
parent: ctx.parent,
|
|
971
1000
|
handler: unwrappedHandler,
|
|
972
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
973
|
-
middleware: [],
|
|
974
|
-
revalidate: [],
|
|
975
|
-
errorBoundary: [],
|
|
976
|
-
notFoundBoundary: [],
|
|
977
|
-
parallel: {},
|
|
978
|
-
intercept: [],
|
|
979
|
-
layout: [],
|
|
980
|
-
loader: [],
|
|
981
1001
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
982
1002
|
...(isStatic
|
|
983
1003
|
? {
|
|
@@ -999,11 +1019,13 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
999
1019
|
// Run merged use callback if present
|
|
1000
1020
|
let result: AllUseItems[] | undefined;
|
|
1001
1021
|
if (mergedUse) {
|
|
1002
|
-
result =
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1022
|
+
result = runAndValidateUseItems(
|
|
1023
|
+
store,
|
|
1024
|
+
namespace,
|
|
1025
|
+
entry,
|
|
1026
|
+
mergedUse,
|
|
1027
|
+
"layout",
|
|
1028
|
+
"use",
|
|
1007
1029
|
);
|
|
1008
1030
|
}
|
|
1009
1031
|
|
|
@@ -1045,9 +1067,7 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
1045
1067
|
`Orphan layouts can only be defined inside route or layout > check [${namespace}]`,
|
|
1046
1068
|
);
|
|
1047
1069
|
|
|
1048
|
-
|
|
1049
|
-
entry.parent = null;
|
|
1050
|
-
parent.layout.push(entry);
|
|
1070
|
+
attachOrphanSibling(parent, entry);
|
|
1051
1071
|
}
|
|
1052
1072
|
}
|
|
1053
1073
|
|
|
@@ -1060,33 +1080,15 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
1060
1080
|
} as LayoutItem;
|
|
1061
1081
|
};
|
|
1062
1082
|
|
|
1063
|
-
const isValidUseItem = (item: any): item is AllUseItems | undefined | null =>
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
(item
|
|
1068
|
-
typeof item === "object" &&
|
|
1069
|
-
"type" in item &&
|
|
1070
|
-
[
|
|
1071
|
-
"layout",
|
|
1072
|
-
"route",
|
|
1073
|
-
"middleware",
|
|
1074
|
-
"revalidate",
|
|
1075
|
-
"parallel",
|
|
1076
|
-
"intercept",
|
|
1077
|
-
"loader",
|
|
1078
|
-
"loading",
|
|
1079
|
-
"errorBoundary",
|
|
1080
|
-
"notFoundBoundary",
|
|
1081
|
-
"when",
|
|
1082
|
-
"cache",
|
|
1083
|
-
"transition",
|
|
1084
|
-
"include", // For urls() include() helper
|
|
1085
|
-
].includes(item.type))
|
|
1086
|
-
);
|
|
1087
|
-
};
|
|
1083
|
+
const isValidUseItem = (item: any): item is AllUseItems | undefined | null =>
|
|
1084
|
+
item == null ||
|
|
1085
|
+
(typeof item === "object" &&
|
|
1086
|
+
"type" in item &&
|
|
1087
|
+
ALL_USE_ITEM_TYPES.has(item.type));
|
|
1088
1088
|
|
|
1089
|
-
//
|
|
1089
|
+
// DSL helpers exported for direct import from @rangojs/router and for
|
|
1090
|
+
// assembly into the RouteHelpers object in helper-factories.ts. The route-item
|
|
1091
|
+
// types are discriminated by their `type` literal, so the helpers carry no brand.
|
|
1090
1092
|
export {
|
|
1091
1093
|
layout,
|
|
1092
1094
|
cache,
|
|
@@ -1097,25 +1099,11 @@ export {
|
|
|
1097
1099
|
when,
|
|
1098
1100
|
errorBoundary,
|
|
1099
1101
|
notFoundBoundary,
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
const isOrphanLayout = (item: AllUseItems): boolean => {
|
|
1106
|
-
return (
|
|
1107
|
-
item.type === "layout" &&
|
|
1108
|
-
!item.uses?.some((child) => hasRoutesInItem(child))
|
|
1109
|
-
);
|
|
1110
|
-
};
|
|
1111
|
-
|
|
1112
|
-
// Internal exports used by helper-factories.ts
|
|
1113
|
-
export {
|
|
1114
|
-
routeFn,
|
|
1115
|
-
loaderFn,
|
|
1116
|
-
loadingFn,
|
|
1117
|
-
transitionFn,
|
|
1118
|
-
hasRoutesInItem,
|
|
1102
|
+
route,
|
|
1103
|
+
loader,
|
|
1104
|
+
loading,
|
|
1105
|
+
transition,
|
|
1119
1106
|
isValidUseItem,
|
|
1120
|
-
|
|
1107
|
+
emptySegmentBase,
|
|
1108
|
+
runAndValidateUseItems,
|
|
1121
1109
|
};
|