@rangojs/router 0.0.0-experimental.fb4fdc18 → 0.0.0-experimental.fce7fbd1
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 +9 -9
- package/dist/bin/rango.js +147 -57
- package/dist/testing/vitest.js +48 -0
- package/dist/vite/index.js +914 -485
- package/package.json +55 -11
- 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/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +214 -18
- package/skills/host-router/SKILL.md +45 -20
- package/skills/intercept/SKILL.md +26 -4
- package/skills/layout/SKILL.md +6 -7
- package/skills/links/SKILL.md +173 -17
- package/skills/loader/SKILL.md +149 -6
- package/skills/middleware/SKILL.md +13 -9
- package/skills/migrate-nextjs/SKILL.md +1 -1
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +5 -6
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +242 -26
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +58 -9
- package/skills/route/SKILL.md +13 -4
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +53 -41
- package/skills/testing/SKILL.md +599 -0
- package/skills/typesafety/SKILL.md +310 -26
- 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 +117 -0
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/event-controller.ts +42 -66
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +6 -6
- package/src/browser/navigation-client.ts +12 -15
- package/src/browser/navigation-store.ts +7 -8
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +9 -19
- package/src/browser/react/NavigationProvider.tsx +29 -40
- 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-params.ts +3 -4
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +14 -1
- package/src/browser/response-adapter.ts +25 -0
- package/src/browser/rsc-router.tsx +30 -16
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +23 -30
- package/src/browser/types.ts +2 -0
- 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 +2 -0
- 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-scope.ts +28 -42
- package/src/cache/cf/cf-cache-store.ts +49 -6
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +10 -8
- package/src/context-var.ts +5 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -1
- package/src/handle.ts +26 -13
- 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 -20
- package/src/index.rsc.ts +6 -4
- package/src/index.ts +13 -6
- 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/prerender.ts +4 -4
- package/src/response-utils.ts +9 -0
- package/src/reverse.ts +65 -41
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +238 -263
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +37 -14
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +19 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +15 -2
- package/src/router/error-handling.ts +1 -1
- package/src/router/handler-context.ts +4 -42
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/lazy-includes.ts +2 -2
- package/src/router/loader-resolution.ts +16 -2
- package/src/router/match-handlers.ts +62 -20
- package/src/router/match-middleware/cache-lookup.ts +44 -91
- package/src/router/match-middleware/cache-store.ts +3 -2
- package/src/router/match-result.ts +32 -30
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +1 -1
- package/src/router/middleware.ts +46 -78
- package/src/router/prerender-match.ts +1 -1
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +4 -28
- package/src/router/revalidation.ts +43 -1
- package/src/router/router-interfaces.ts +45 -28
- package/src/router/router-options.ts +40 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +19 -6
- package/src/router/segment-resolution/revalidation.ts +19 -6
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry.ts +99 -0
- package/src/router/types.ts +8 -0
- package/src/router.ts +37 -21
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +20 -65
- package/src/rsc/helpers.ts +22 -2
- package/src/rsc/index.ts +1 -1
- package/src/rsc/origin-guard.ts +28 -10
- package/src/rsc/response-route-handler.ts +32 -52
- package/src/rsc/rsc-rendering.ts +27 -53
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +13 -37
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +2 -2
- package/src/search-params.ts +4 -4
- package/src/segment-system.tsx +121 -65
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +118 -51
- package/src/server/cookie-store.ts +28 -4
- package/src/server/request-context.ts +10 -0
- package/src/static-handler.ts +1 -1
- package/src/testing/cache-status.ts +166 -0
- package/src/testing/collect-handle.ts +63 -0
- package/src/testing/dispatch.ts +440 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +154 -0
- package/src/testing/e2e/index.ts +149 -0
- package/src/testing/e2e/matchers.ts +51 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +306 -0
- package/src/testing/e2e/server.ts +183 -0
- package/src/testing/flight-matchers.ts +104 -0
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +22 -0
- package/src/testing/flight.ts +182 -0
- package/src/testing/generated-routes.ts +223 -0
- package/src/testing/index.ts +105 -0
- package/src/testing/internal/context.ts +193 -0
- package/src/testing/render-route.tsx +536 -0
- package/src/testing/run-loader.ts +296 -0
- package/src/testing/run-middleware.ts +170 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +183 -0
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +56 -11
- package/src/types/index.ts +1 -0
- package/src/types/segments.ts +18 -1
- package/src/urls/include-helper.ts +10 -53
- package/src/urls/index.ts +0 -3
- package/src/urls/path-helper-types.ts +11 -3
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +20 -19
- package/src/urls/type-extraction.ts +26 -116
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +413 -42
- package/src/vite/debug.ts +1 -0
- package/src/vite/discovery/bundle-postprocess.ts +6 -6
- package/src/vite/discovery/discover-routers.ts +70 -48
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/prerender-collection.ts +19 -25
- package/src/vite/discovery/route-types-writer.ts +40 -84
- 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 +3 -7
- package/src/vite/plugins/client-ref-hashing.ts +12 -1
- package/src/vite/plugins/cloudflare-protocol-stub.ts +1 -1
- package/src/vite/plugins/expose-action-id.ts +2 -2
- 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-internal-ids.ts +47 -67
- package/src/vite/plugins/performance-tracks.ts +12 -16
- package/src/vite/plugins/use-cache-transform.ts +13 -11
- package/src/vite/plugins/version-injector.ts +2 -12
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +67 -15
- package/src/vite/router-discovery.ts +208 -63
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- 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 +21 -5
- 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) {
|
|
@@ -317,9 +406,10 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
317
406
|
const namespace = `${ctx.namespace}.${cacheIndex}`;
|
|
318
407
|
const cacheShortCode = store.getShortCode("cache");
|
|
319
408
|
|
|
320
|
-
const
|
|
409
|
+
const urlPrefix = getUrlPrefix();
|
|
321
410
|
|
|
322
411
|
const entry = {
|
|
412
|
+
...emptySegmentBase(),
|
|
323
413
|
id: namespace,
|
|
324
414
|
shortCode: cacheShortCode,
|
|
325
415
|
type: "cache",
|
|
@@ -327,40 +417,22 @@ const cache: RouteHelpers<any, any>["cache"] = (
|
|
|
327
417
|
cache: cacheConfig,
|
|
328
418
|
// Cache entries render like layouts (with Outlet as default handler)
|
|
329
419
|
handler: RootLayout, // RootLayout just renders <Outlet />
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
revalidate: [],
|
|
333
|
-
errorBoundary: [],
|
|
334
|
-
notFoundBoundary: [],
|
|
335
|
-
layout: [],
|
|
336
|
-
parallel: {},
|
|
337
|
-
intercept: [],
|
|
338
|
-
loader: [],
|
|
339
|
-
...(cacheUrlPrefix2 ? { mountPath: cacheUrlPrefix2 } : {}),
|
|
340
|
-
} as EntryData;
|
|
420
|
+
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
421
|
+
} satisfies EntryData;
|
|
341
422
|
|
|
342
423
|
// Run children with cache entry as parent
|
|
343
|
-
const result =
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
424
|
+
const result = runAndValidateUseItems(
|
|
425
|
+
store,
|
|
426
|
+
namespace,
|
|
427
|
+
entry,
|
|
428
|
+
children,
|
|
429
|
+
"cache",
|
|
430
|
+
"children",
|
|
348
431
|
);
|
|
349
432
|
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
Array.isArray(result) &&
|
|
354
|
-
result.some((item) => hasRoutesInItem(item));
|
|
355
|
-
|
|
356
|
-
if (!hasRoutes) {
|
|
357
|
-
const parent = ctx.parent;
|
|
358
|
-
if (parent && "layout" in parent) {
|
|
359
|
-
// Attach to parent's layout array (cache entries are structural like layouts)
|
|
360
|
-
entry.parent = null;
|
|
361
|
-
parent.layout.push(entry);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
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);
|
|
364
436
|
|
|
365
437
|
return { name: namespace, type: "cache", uses: result } as CacheItem;
|
|
366
438
|
};
|
|
@@ -406,9 +478,9 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
406
478
|
}
|
|
407
479
|
}
|
|
408
480
|
|
|
409
|
-
const store =
|
|
410
|
-
|
|
411
|
-
|
|
481
|
+
const { store, ctx } = requireDslContext(
|
|
482
|
+
"middleware() must be called inside urls()",
|
|
483
|
+
);
|
|
412
484
|
|
|
413
485
|
if (!children) {
|
|
414
486
|
// Sibling mode: attach to parent entry
|
|
@@ -427,22 +499,15 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
427
499
|
|
|
428
500
|
const urlPrefix = getUrlPrefix();
|
|
429
501
|
const entry = {
|
|
502
|
+
...emptySegmentBase(),
|
|
430
503
|
id: namespace,
|
|
431
504
|
shortCode: store.getShortCode("layout"),
|
|
432
505
|
type: "layout",
|
|
433
506
|
parent: ctx.parent,
|
|
434
507
|
handler: RootLayout,
|
|
435
|
-
loading: undefined,
|
|
436
508
|
middleware: [...fns],
|
|
437
|
-
revalidate: [],
|
|
438
|
-
errorBoundary: [],
|
|
439
|
-
notFoundBoundary: [],
|
|
440
|
-
layout: [],
|
|
441
|
-
parallel: {},
|
|
442
|
-
intercept: [],
|
|
443
|
-
loader: [],
|
|
444
509
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
445
|
-
}
|
|
510
|
+
} satisfies EntryData;
|
|
446
511
|
|
|
447
512
|
// Run children callback. If the second arg was actually a middleware fn
|
|
448
513
|
// (old variadic form: middleware(mw1, mw2)), this will return a non-array
|
|
@@ -455,25 +520,14 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
455
520
|
"To pass multiple middleware, use middleware([fn1, fn2]).",
|
|
456
521
|
);
|
|
457
522
|
|
|
458
|
-
const result =
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
523
|
+
const result = validateUseItems(
|
|
524
|
+
rawResult.flat(3),
|
|
525
|
+
namespace,
|
|
526
|
+
"middleware",
|
|
527
|
+
"children",
|
|
463
528
|
);
|
|
464
529
|
|
|
465
|
-
|
|
466
|
-
result &&
|
|
467
|
-
Array.isArray(result) &&
|
|
468
|
-
result.some((item) => item != null && hasRoutesInItem(item));
|
|
469
|
-
|
|
470
|
-
if (!hasRoutes) {
|
|
471
|
-
const parent = ctx.parent;
|
|
472
|
-
if (parent && "layout" in parent) {
|
|
473
|
-
entry.parent = null;
|
|
474
|
-
parent.layout.push(entry);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
530
|
+
if (isOrphan(result)) attachOrphanSibling(ctx.parent, entry);
|
|
477
531
|
|
|
478
532
|
return {
|
|
479
533
|
name: namespace,
|
|
@@ -483,9 +537,9 @@ const middleware: RouteHelpers<any, any>["middleware"] = (...args: any[]) => {
|
|
|
483
537
|
};
|
|
484
538
|
|
|
485
539
|
const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
486
|
-
const store =
|
|
487
|
-
|
|
488
|
-
|
|
540
|
+
const { store, ctx } = requireDslContext(
|
|
541
|
+
"parallel() must be called inside urls()",
|
|
542
|
+
);
|
|
489
543
|
|
|
490
544
|
if (!ctx.parent || !ctx.parent?.parallel) {
|
|
491
545
|
invariant(false, "No parent entry available for parallel()");
|
|
@@ -537,20 +591,12 @@ const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
|
537
591
|
// Create full EntryData for parallel with its own loaders/revalidate/loading
|
|
538
592
|
const parallelUrlPrefix = getUrlPrefix();
|
|
539
593
|
const entry = {
|
|
594
|
+
...emptySegmentBase(),
|
|
540
595
|
id: namespace,
|
|
541
596
|
shortCode: store.getShortCode("parallel"),
|
|
542
597
|
type: "parallel",
|
|
543
598
|
parent: null, // Parallels don't participate in parent chain traversal
|
|
544
599
|
handler: unwrappedSlots,
|
|
545
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
546
|
-
middleware: [],
|
|
547
|
-
revalidate: [],
|
|
548
|
-
errorBoundary: [],
|
|
549
|
-
notFoundBoundary: [],
|
|
550
|
-
layout: [],
|
|
551
|
-
parallel: {},
|
|
552
|
-
intercept: [],
|
|
553
|
-
loader: [],
|
|
554
600
|
...(parallelUrlPrefix ? { mountPath: parallelUrlPrefix } : {}),
|
|
555
601
|
...(hasStaticSlot
|
|
556
602
|
? {
|
|
@@ -605,10 +651,13 @@ const parallel: RouteHelpers<any, any>["parallel"] = (slots, use) => {
|
|
|
605
651
|
"parallel",
|
|
606
652
|
);
|
|
607
653
|
if (slotMergedUse) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
654
|
+
runAndValidateUseItems(
|
|
655
|
+
store,
|
|
656
|
+
namespace,
|
|
657
|
+
slotEntry,
|
|
658
|
+
slotMergedUse,
|
|
659
|
+
"parallel",
|
|
660
|
+
"use",
|
|
612
661
|
);
|
|
613
662
|
}
|
|
614
663
|
|
|
@@ -648,9 +697,9 @@ const intercept = (
|
|
|
648
697
|
handler: any,
|
|
649
698
|
use?: () => any[],
|
|
650
699
|
) => {
|
|
651
|
-
const store =
|
|
652
|
-
|
|
653
|
-
|
|
700
|
+
const { store, ctx } = requireDslContext(
|
|
701
|
+
"intercept() must be called inside urls()",
|
|
702
|
+
);
|
|
654
703
|
|
|
655
704
|
if (!ctx.parent || !ctx.parent?.intercept) {
|
|
656
705
|
invariant(false, "No parent entry available for intercept()");
|
|
@@ -689,15 +738,13 @@ const intercept = (
|
|
|
689
738
|
|
|
690
739
|
// Run merged use callback to collect loaders, revalidate, middleware, etc.
|
|
691
740
|
if (mergedUse) {
|
|
692
|
-
//
|
|
693
|
-
// so that middleware, loader, revalidate attach to the intercept entry
|
|
694
|
-
const originalParent = ctx.parent;
|
|
695
|
-
|
|
696
|
-
// Capture layouts in a temporary array
|
|
741
|
+
// Capture layout() calls into a temporary array
|
|
697
742
|
const capturedLayouts: EntryData[] = [];
|
|
698
743
|
|
|
744
|
+
// Temporary parent so middleware/loader/revalidate/when attach to the
|
|
745
|
+
// intercept entry; the loading get/set accessor mirrors writes onto `entry`.
|
|
699
746
|
const tempParent = {
|
|
700
|
-
...
|
|
747
|
+
...ctx.parent,
|
|
701
748
|
middleware: entry.middleware,
|
|
702
749
|
revalidate: entry.revalidate,
|
|
703
750
|
errorBoundary: entry.errorBoundary,
|
|
@@ -705,7 +752,6 @@ const intercept = (
|
|
|
705
752
|
loader: entry.loader,
|
|
706
753
|
layout: capturedLayouts, // Capture layout() calls
|
|
707
754
|
when: entry.when, // Capture when() conditions
|
|
708
|
-
// Use getter/setter to capture loading on the entry
|
|
709
755
|
get loading() {
|
|
710
756
|
return entry.loading;
|
|
711
757
|
},
|
|
@@ -713,12 +759,10 @@ const intercept = (
|
|
|
713
759
|
entry.loading = value;
|
|
714
760
|
},
|
|
715
761
|
};
|
|
716
|
-
ctx.parent = tempParent as EntryData;
|
|
717
|
-
|
|
718
|
-
const result = mergedUse()?.flat(3);
|
|
719
762
|
|
|
720
|
-
|
|
721
|
-
|
|
763
|
+
const result = withParent(ctx, tempParent as EntryData, () =>
|
|
764
|
+
mergedUse()?.flat(3),
|
|
765
|
+
);
|
|
722
766
|
|
|
723
767
|
// Extract layout from captured layouts (use first one if multiple)
|
|
724
768
|
// Layout inside intercept should always be ReactNode or Handler, not Record slots
|
|
@@ -728,10 +772,7 @@ const intercept = (
|
|
|
728
772
|
| Handler<any, any, any>;
|
|
729
773
|
}
|
|
730
774
|
|
|
731
|
-
|
|
732
|
-
Array.isArray(result) && result.every((item) => isValidUseItem(item)),
|
|
733
|
-
`intercept() use() callback must return an array of use items [${namespace}]`,
|
|
734
|
-
);
|
|
775
|
+
validateUseItems(result, namespace, "intercept", "use");
|
|
735
776
|
}
|
|
736
777
|
|
|
737
778
|
ctx.parent.intercept.push(entry);
|
|
@@ -741,10 +782,10 @@ const intercept = (
|
|
|
741
782
|
/**
|
|
742
783
|
* Loader helper - attaches a loader to the current entry
|
|
743
784
|
*/
|
|
744
|
-
const
|
|
745
|
-
const store =
|
|
746
|
-
|
|
747
|
-
|
|
785
|
+
const loader: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
786
|
+
const { store, ctx } = requireDslContext(
|
|
787
|
+
"loader() must be called inside urls()",
|
|
788
|
+
);
|
|
748
789
|
|
|
749
790
|
// Attach to last entry in stack
|
|
750
791
|
if (!ctx.parent || !ctx.parent?.loader) {
|
|
@@ -765,23 +806,22 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
765
806
|
|
|
766
807
|
// If any use callback is in effect, run it to collect revalidation rules and cache config
|
|
767
808
|
if (mergedUse) {
|
|
768
|
-
// Temporarily set context for revalidate()/cache() calls to target this loader
|
|
769
|
-
const originalParent = ctx.parent;
|
|
770
809
|
// Create a temporary "parent" with type "loader" so cache() can detect it.
|
|
771
810
|
// Save existing .cache to distinguish inherited config from newly set config.
|
|
772
|
-
const parentCache = (
|
|
811
|
+
const parentCache = (ctx.parent as any).cache;
|
|
773
812
|
const tempParent = {
|
|
774
|
-
...
|
|
813
|
+
...ctx.parent,
|
|
775
814
|
type: "loader",
|
|
776
815
|
revalidate: loaderEntry.revalidate,
|
|
777
816
|
};
|
|
778
|
-
ctx.parent = tempParent as EntryData;
|
|
779
817
|
|
|
780
|
-
const result =
|
|
818
|
+
const result = withParent(ctx, tempParent as EntryData, () =>
|
|
819
|
+
mergedUse()?.flat(3),
|
|
820
|
+
);
|
|
781
821
|
|
|
782
822
|
// Copy cache config only if cache() was called during the use() callback.
|
|
783
|
-
// The spread
|
|
784
|
-
//
|
|
823
|
+
// The spread may carry an inherited .cache from a parent cache() boundary —
|
|
824
|
+
// only copy if it was newly set.
|
|
785
825
|
if (
|
|
786
826
|
(tempParent as any).cache &&
|
|
787
827
|
(tempParent as any).cache !== parentCache
|
|
@@ -789,13 +829,7 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
789
829
|
(loaderEntry as any).cache = (tempParent as any).cache;
|
|
790
830
|
}
|
|
791
831
|
|
|
792
|
-
|
|
793
|
-
ctx.parent = originalParent;
|
|
794
|
-
|
|
795
|
-
invariant(
|
|
796
|
-
Array.isArray(result) && result.every((item) => isValidUseItem(item)),
|
|
797
|
-
`loader() use() callback must return an array of use items [${name}]`,
|
|
798
|
-
);
|
|
832
|
+
validateUseItems(result, name, "loader", "use");
|
|
799
833
|
}
|
|
800
834
|
|
|
801
835
|
ctx.parent.loader.push(loaderEntry);
|
|
@@ -806,10 +840,10 @@ const loaderFn: RouteHelpers<any, any>["loader"] = (loaderDef, use) => {
|
|
|
806
840
|
* Loading helper - attaches a loading component to the current entry
|
|
807
841
|
* Loading components are static (no context) and shown during navigation
|
|
808
842
|
*/
|
|
809
|
-
const
|
|
810
|
-
const store =
|
|
811
|
-
|
|
812
|
-
|
|
843
|
+
const loading: RouteHelpers<any, any>["loading"] = (component, options) => {
|
|
844
|
+
const { store, ctx } = requireDslContext(
|
|
845
|
+
"loading() must be called inside urls()",
|
|
846
|
+
);
|
|
813
847
|
|
|
814
848
|
const parent = ctx.parent;
|
|
815
849
|
if (!parent || !("loading" in parent)) {
|
|
@@ -832,10 +866,13 @@ const loadingFn: RouteHelpers<any, any>["loading"] = (component, options) => {
|
|
|
832
866
|
};
|
|
833
867
|
|
|
834
868
|
/**
|
|
835
|
-
* Transition helper -
|
|
836
|
-
*
|
|
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.
|
|
837
874
|
*/
|
|
838
|
-
const
|
|
875
|
+
const transition = (
|
|
839
876
|
configOrChildren?: TransitionConfig | (() => UseItems<AllUseItems>),
|
|
840
877
|
maybeChildren?: () => UseItems<AllUseItems>,
|
|
841
878
|
): TransitionItem => {
|
|
@@ -849,9 +886,9 @@ const transitionFn = (
|
|
|
849
886
|
const children: (() => UseItems<AllUseItems>) | undefined =
|
|
850
887
|
typeof configOrChildren === "function" ? configOrChildren : maybeChildren;
|
|
851
888
|
|
|
852
|
-
const store =
|
|
853
|
-
|
|
854
|
-
|
|
889
|
+
const { store, ctx } = requireDslContext(
|
|
890
|
+
"transition() must be called inside urls()",
|
|
891
|
+
);
|
|
855
892
|
|
|
856
893
|
const name = `$${store.getNextIndex("transition")}`;
|
|
857
894
|
|
|
@@ -868,68 +905,43 @@ const transitionFn = (
|
|
|
868
905
|
// Position 2: wrapper — create a transparent layout with transition config
|
|
869
906
|
const namespace = `${ctx.namespace}.${store.getNextIndex("transition")}`;
|
|
870
907
|
const entry = {
|
|
908
|
+
...emptySegmentBase(),
|
|
871
909
|
id: namespace,
|
|
872
910
|
shortCode: store.getShortCode("layout"),
|
|
873
911
|
type: "layout",
|
|
874
912
|
parent: ctx.parent,
|
|
875
913
|
handler: RootLayout,
|
|
876
|
-
loading: undefined,
|
|
877
914
|
transition: config,
|
|
878
|
-
|
|
879
|
-
revalidate: [],
|
|
880
|
-
errorBoundary: [],
|
|
881
|
-
notFoundBoundary: [],
|
|
882
|
-
layout: [],
|
|
883
|
-
parallel: {},
|
|
884
|
-
intercept: [],
|
|
885
|
-
loader: [],
|
|
886
|
-
} as EntryData;
|
|
887
|
-
|
|
888
|
-
const result = store.run(namespace, entry, children)?.flat(3);
|
|
915
|
+
} satisfies EntryData;
|
|
889
916
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
917
|
+
const result = runAndValidateUseItems(
|
|
918
|
+
store,
|
|
919
|
+
namespace,
|
|
920
|
+
entry,
|
|
921
|
+
children,
|
|
922
|
+
"transition",
|
|
923
|
+
"children",
|
|
893
924
|
);
|
|
894
925
|
|
|
895
|
-
|
|
896
|
-
result &&
|
|
897
|
-
Array.isArray(result) &&
|
|
898
|
-
result.some((item) => hasRoutesInItem(item));
|
|
899
|
-
|
|
900
|
-
if (!hasRoutes) {
|
|
901
|
-
const parent = ctx.parent;
|
|
902
|
-
if (parent && "layout" in parent) {
|
|
903
|
-
entry.parent = null;
|
|
904
|
-
parent.layout.push(entry);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
926
|
+
if (isOrphan(result)) attachOrphanSibling(ctx.parent, entry);
|
|
907
927
|
|
|
908
928
|
return { name: namespace, type: "transition" } as TransitionItem;
|
|
909
929
|
};
|
|
910
930
|
|
|
911
|
-
const
|
|
912
|
-
const store =
|
|
913
|
-
|
|
914
|
-
|
|
931
|
+
const route: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
932
|
+
const { store, ctx } = requireDslContext(
|
|
933
|
+
"route() must be called inside urls()",
|
|
934
|
+
);
|
|
915
935
|
|
|
916
936
|
const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${name}`;
|
|
917
937
|
|
|
918
938
|
const entry = {
|
|
939
|
+
...emptySegmentBase(),
|
|
919
940
|
id: namespace,
|
|
920
941
|
shortCode: store.getShortCode("route"),
|
|
921
942
|
type: "route",
|
|
922
943
|
parent: ctx.parent,
|
|
923
944
|
handler: handler as unknown as Handler<any, any, any>,
|
|
924
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
925
|
-
middleware: [],
|
|
926
|
-
revalidate: [],
|
|
927
|
-
errorBoundary: [],
|
|
928
|
-
notFoundBoundary: [],
|
|
929
|
-
layout: [],
|
|
930
|
-
parallel: {},
|
|
931
|
-
intercept: [],
|
|
932
|
-
loader: [],
|
|
933
945
|
} satisfies EntryData;
|
|
934
946
|
|
|
935
947
|
/* We will throw if user is registring same route name twice */
|
|
@@ -944,10 +956,13 @@ const routeFn: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
|
944
956
|
const mergedUse = mergeHandlerUse(handlerUseFn, use, "route");
|
|
945
957
|
/* Run use and attach handlers */
|
|
946
958
|
if (mergedUse) {
|
|
947
|
-
const result =
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
959
|
+
const result = runAndValidateUseItems(
|
|
960
|
+
store,
|
|
961
|
+
namespace,
|
|
962
|
+
entry,
|
|
963
|
+
mergedUse,
|
|
964
|
+
"route",
|
|
965
|
+
"use",
|
|
951
966
|
);
|
|
952
967
|
return { name: namespace, type: "route", uses: result } as RouteItem;
|
|
953
968
|
}
|
|
@@ -957,9 +972,9 @@ const routeFn: RouteHelpers<any, any>["route"] = (name, handler, use) => {
|
|
|
957
972
|
};
|
|
958
973
|
|
|
959
974
|
const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
960
|
-
const store =
|
|
961
|
-
|
|
962
|
-
|
|
975
|
+
const { store, ctx } = requireDslContext(
|
|
976
|
+
"layout() must be called inside urls()",
|
|
977
|
+
);
|
|
963
978
|
|
|
964
979
|
invariant(
|
|
965
980
|
!ctx.parent || ctx.parent.type !== "parallel",
|
|
@@ -977,20 +992,12 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
977
992
|
|
|
978
993
|
const urlPrefix = getUrlPrefix();
|
|
979
994
|
const entry = {
|
|
995
|
+
...emptySegmentBase(),
|
|
980
996
|
id: namespace,
|
|
981
997
|
shortCode,
|
|
982
998
|
type: "layout",
|
|
983
999
|
parent: ctx.parent,
|
|
984
1000
|
handler: unwrappedHandler,
|
|
985
|
-
loading: undefined, // Allow loading() to attach loading state
|
|
986
|
-
middleware: [],
|
|
987
|
-
revalidate: [],
|
|
988
|
-
errorBoundary: [],
|
|
989
|
-
notFoundBoundary: [],
|
|
990
|
-
parallel: {},
|
|
991
|
-
intercept: [],
|
|
992
|
-
layout: [],
|
|
993
|
-
loader: [],
|
|
994
1001
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
995
1002
|
...(isStatic
|
|
996
1003
|
? {
|
|
@@ -1012,11 +1019,13 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
1012
1019
|
// Run merged use callback if present
|
|
1013
1020
|
let result: AllUseItems[] | undefined;
|
|
1014
1021
|
if (mergedUse) {
|
|
1015
|
-
result =
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1022
|
+
result = runAndValidateUseItems(
|
|
1023
|
+
store,
|
|
1024
|
+
namespace,
|
|
1025
|
+
entry,
|
|
1026
|
+
mergedUse,
|
|
1027
|
+
"layout",
|
|
1028
|
+
"use",
|
|
1020
1029
|
);
|
|
1021
1030
|
}
|
|
1022
1031
|
|
|
@@ -1058,9 +1067,7 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
1058
1067
|
`Orphan layouts can only be defined inside route or layout > check [${namespace}]`,
|
|
1059
1068
|
);
|
|
1060
1069
|
|
|
1061
|
-
|
|
1062
|
-
entry.parent = null;
|
|
1063
|
-
parent.layout.push(entry);
|
|
1070
|
+
attachOrphanSibling(parent, entry);
|
|
1064
1071
|
}
|
|
1065
1072
|
}
|
|
1066
1073
|
|
|
@@ -1073,33 +1080,15 @@ const layout: RouteHelpers<any, any>["layout"] = (handler, use) => {
|
|
|
1073
1080
|
} as LayoutItem;
|
|
1074
1081
|
};
|
|
1075
1082
|
|
|
1076
|
-
const isValidUseItem = (item: any): item is AllUseItems | undefined | null =>
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
(item
|
|
1081
|
-
typeof item === "object" &&
|
|
1082
|
-
"type" in item &&
|
|
1083
|
-
[
|
|
1084
|
-
"layout",
|
|
1085
|
-
"route",
|
|
1086
|
-
"middleware",
|
|
1087
|
-
"revalidate",
|
|
1088
|
-
"parallel",
|
|
1089
|
-
"intercept",
|
|
1090
|
-
"loader",
|
|
1091
|
-
"loading",
|
|
1092
|
-
"errorBoundary",
|
|
1093
|
-
"notFoundBoundary",
|
|
1094
|
-
"when",
|
|
1095
|
-
"cache",
|
|
1096
|
-
"transition",
|
|
1097
|
-
"include", // For urls() include() helper
|
|
1098
|
-
].includes(item.type))
|
|
1099
|
-
);
|
|
1100
|
-
};
|
|
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));
|
|
1101
1088
|
|
|
1102
|
-
//
|
|
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.
|
|
1103
1092
|
export {
|
|
1104
1093
|
layout,
|
|
1105
1094
|
cache,
|
|
@@ -1110,25 +1099,11 @@ export {
|
|
|
1110
1099
|
when,
|
|
1111
1100
|
errorBoundary,
|
|
1112
1101
|
notFoundBoundary,
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
const isOrphanLayout = (item: AllUseItems): boolean => {
|
|
1119
|
-
return (
|
|
1120
|
-
item.type === "layout" &&
|
|
1121
|
-
!item.uses?.some((child) => hasRoutesInItem(child))
|
|
1122
|
-
);
|
|
1123
|
-
};
|
|
1124
|
-
|
|
1125
|
-
// Internal exports used by helper-factories.ts
|
|
1126
|
-
export {
|
|
1127
|
-
routeFn,
|
|
1128
|
-
loaderFn,
|
|
1129
|
-
loadingFn,
|
|
1130
|
-
transitionFn,
|
|
1131
|
-
hasRoutesInItem,
|
|
1102
|
+
route,
|
|
1103
|
+
loader,
|
|
1104
|
+
loading,
|
|
1105
|
+
transition,
|
|
1132
1106
|
isValidUseItem,
|
|
1133
|
-
|
|
1107
|
+
emptySegmentBase,
|
|
1108
|
+
runAndValidateUseItems,
|
|
1134
1109
|
};
|