@rangojs/router 0.0.0-experimental.19 → 0.0.0-experimental.1fa245e2
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/{CLAUDE.md → AGENTS.md} +4 -0
- package/README.md +122 -30
- package/dist/bin/rango.js +245 -63
- package/dist/vite/index.js +859 -418
- package/package.json +3 -3
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +49 -8
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +33 -31
- package/skills/host-router/SKILL.md +218 -0
- package/skills/links/SKILL.md +3 -1
- package/skills/loader/SKILL.md +72 -22
- package/skills/middleware/SKILL.md +2 -0
- package/skills/parallel/SKILL.md +126 -0
- package/skills/prerender/SKILL.md +112 -70
- package/skills/rango/SKILL.md +0 -1
- package/skills/route/SKILL.md +34 -4
- package/skills/router-setup/SKILL.md +95 -5
- package/skills/typesafety/SKILL.md +35 -23
- package/src/__internal.ts +92 -0
- package/src/bin/rango.ts +18 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +5 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +114 -18
- package/src/browser/navigation-client.ts +126 -44
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +80 -15
- package/src/browser/prefetch/cache.ts +166 -27
- package/src/browser/prefetch/fetch.ts +52 -39
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +92 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/react/Link.tsx +70 -14
- package/src/browser/react/NavigationProvider.tsx +40 -4
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-router.ts +21 -8
- package/src/browser/rsc-router.tsx +143 -59
- package/src/browser/scroll-restoration.ts +41 -42
- package/src/browser/segment-reconciler.ts +6 -1
- package/src/browser/server-action-bridge.ts +454 -436
- package/src/browser/types.ts +60 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +346 -87
- package/src/build/route-types/scan-filter.ts +8 -1
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +48 -7
- package/src/cache/cf/cf-cache-store.ts +453 -11
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- package/src/cache/taint.ts +55 -0
- package/src/client.rsc.tsx +2 -1
- package/src/client.tsx +3 -102
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/handle.ts +40 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/host/index.ts +0 -3
- package/src/index.rsc.ts +8 -37
- package/src/index.ts +40 -66
- package/src/prerender/store.ts +57 -15
- package/src/prerender.ts +138 -77
- package/src/reverse.ts +22 -1
- package/src/route-definition/dsl-helpers.ts +73 -25
- package/src/route-definition/helpers-types.ts +10 -6
- package/src/route-definition/index.ts +3 -3
- package/src/route-definition/redirect.ts +11 -3
- package/src/route-definition/resolve-handler-use.ts +149 -0
- package/src/route-map-builder.ts +7 -1
- package/src/route-types.ts +11 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +108 -25
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +4 -1
- package/src/router/loader-resolution.ts +123 -11
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +9 -3
- package/src/router/match-api.ts +125 -190
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +88 -16
- package/src/router/match-middleware/cache-store.ts +53 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +61 -5
- package/src/router/match-result.ts +22 -15
- package/src/router/metrics.ts +238 -13
- package/src/router/middleware-types.ts +53 -12
- package/src/router/middleware.ts +172 -85
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +20 -5
- package/src/router/prerender-match.ts +114 -10
- package/src/router/preview-match.ts +30 -102
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +27 -7
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +50 -5
- package/src/router/router-options.ts +50 -19
- package/src/router/segment-resolution/fresh.ts +200 -19
- package/src/router/segment-resolution/helpers.ts +30 -25
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +429 -301
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/trie-matching.ts +20 -2
- package/src/router/types.ts +1 -0
- package/src/router.ts +88 -15
- package/src/rsc/handler.ts +546 -359
- package/src/rsc/index.ts +0 -20
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +25 -8
- package/src/rsc/rsc-rendering.ts +35 -43
- package/src/rsc/server-action.ts +16 -10
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +10 -1
- package/src/search-params.ts +16 -13
- package/src/segment-system.tsx +140 -4
- package/src/server/context.ts +148 -16
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +182 -34
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +4 -0
- package/src/static-handler.ts +18 -6
- package/src/theme/index.ts +4 -13
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +149 -49
- package/src/types/loader-types.ts +36 -9
- package/src/types/route-config.ts +17 -8
- package/src/types/route-entry.ts +8 -1
- package/src/types/segments.ts +2 -5
- package/src/urls/path-helper-types.ts +9 -2
- package/src/urls/path-helper.ts +48 -13
- package/src/urls/pattern-types.ts +12 -0
- package/src/urls/response-types.ts +16 -6
- package/src/use-loader.tsx +73 -4
- package/src/vite/discovery/bundle-postprocess.ts +61 -89
- package/src/vite/discovery/discover-routers.ts +23 -5
- package/src/vite/discovery/prerender-collection.ts +48 -15
- package/src/vite/discovery/state.ts +17 -13
- package/src/vite/index.ts +8 -3
- package/src/vite/plugin-types.ts +51 -79
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/expose-action-id.ts +1 -3
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +174 -211
- package/src/vite/router-discovery.ts +169 -42
- package/src/vite/utils/banner.ts +3 -3
- package/src/vite/utils/prerender-utils.ts +78 -0
- package/src/vite/utils/shared-utils.ts +3 -2
- package/skills/testing/SKILL.md +0 -226
- package/src/route-definition/route-function.ts +0 -119
package/skills/testing/SKILL.md
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: testing
|
|
3
|
-
description: Unit test route trees with buildRouteTree()
|
|
4
|
-
argument-hint:
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Route Tree Unit Testing
|
|
8
|
-
|
|
9
|
-
Unit test route definitions by inspecting the route tree, segment IDs, middleware, intercepts, loaders, and pattern matching without running a dev server.
|
|
10
|
-
|
|
11
|
-
## Setup
|
|
12
|
-
|
|
13
|
-
The `buildRouteTree` helper lives in `src/__tests__/helpers/route-tree.ts` (not shipped with npm). Import it in your test files:
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { buildRouteTree } from "./helpers/route-tree.js";
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## buildRouteTree(urlPatterns)
|
|
20
|
-
|
|
21
|
-
Takes a `urls()` result and returns a `RouteTree` with inspection methods:
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
import { urls } from "@rangojs/router";
|
|
25
|
-
import { buildRouteTree } from "./helpers/route-tree.js";
|
|
26
|
-
|
|
27
|
-
const tree = buildRouteTree(
|
|
28
|
-
urls(({ path, layout, middleware, loader, intercept, when }) => [
|
|
29
|
-
layout(RootLayout, () => [
|
|
30
|
-
middleware(authMiddleware),
|
|
31
|
-
path("/", HomePage, { name: "home" }),
|
|
32
|
-
path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
|
|
33
|
-
loader(PostLoader),
|
|
34
|
-
]),
|
|
35
|
-
]),
|
|
36
|
-
]),
|
|
37
|
-
);
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## RouteTree API
|
|
41
|
-
|
|
42
|
-
### Route Patterns
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
tree.routes(); // { home: "/", "blog.post": "/blog/:slug" }
|
|
46
|
-
tree.routeNames(); // ["home", "blog.post"]
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### URL Matching
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
const m = tree.match("/blog/hello");
|
|
53
|
-
m.routeKey; // "blog.post"
|
|
54
|
-
m.params; // { slug: "hello" }
|
|
55
|
-
|
|
56
|
-
tree.match("/nonexistent"); // null
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### Segment IDs
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
tree.segmentId("home"); // "M0L0L0R0"
|
|
63
|
-
tree.segmentIds(); // { home: "M0L0L0R0", "blog.post": "M0L0L0R1" }
|
|
64
|
-
tree.segmentPath("blog.post");
|
|
65
|
-
// [
|
|
66
|
-
// { id: "M0L0", type: "layout" }, // synthetic root
|
|
67
|
-
// { id: "M0L0L0", type: "layout" }, // RootLayout
|
|
68
|
-
// { id: "M0L0L0R1", type: "route", pattern: "/blog/:slug" },
|
|
69
|
-
// ]
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Entry Access
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
tree.entry("blog.post"); // EntryData
|
|
76
|
-
tree.entry("blog.post")!.parent!.type; // "layout"
|
|
77
|
-
tree.entryByPattern("/blog/:slug"); // EntryData (lookup by URL pattern)
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Middleware
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
tree.hasMiddleware("home"); // true (inherited from layout)
|
|
84
|
-
tree.middleware("home"); // [authMiddleware] (direct only)
|
|
85
|
-
tree.middlewareChain("home");
|
|
86
|
-
// [{ segmentId: "M0L0L0", count: 1 }] // all middleware root-to-route
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Loaders
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
tree.hasLoaders("blog.post"); // true
|
|
93
|
-
tree.loaders("blog.post"); // [LoaderEntry { loader, revalidate, cache? }]
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Intercepts
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
tree.intercepts("home");
|
|
100
|
-
// [{ slotName: "@modal", routeName: "card", hasWhen: true, whenCount: 1, hasLoader: false, hasMiddleware: false }]
|
|
101
|
-
tree.interceptEntries("home"); // raw InterceptEntry[]
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Parallel Slots
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
tree.parallelSlots("home"); // EntryData[] of type="parallel"
|
|
108
|
-
tree.parallelSlotNames("home"); // ["@sidebar", "@main"]
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Boundaries
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
tree.hasErrorBoundary("home"); // boolean
|
|
115
|
-
tree.hasNotFoundBoundary("home"); // boolean
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Cache & Loading
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
tree.hasCache("home"); // boolean
|
|
122
|
-
tree.hasLoading("home"); // boolean
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### Debug
|
|
126
|
-
|
|
127
|
-
```typescript
|
|
128
|
-
console.log(tree.debug());
|
|
129
|
-
// Route Tree:
|
|
130
|
-
// home: / [M0L0L0R0] (M0L0 > M0L0L0 > M0L0L0R0) {mw:1}
|
|
131
|
-
// blog.post: /blog/:slug [M0L0L0R1] (M0L0 > M0L0L0 > M0L0L0R1) {mw:1, ld:1}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
## Segment ID Format
|
|
135
|
-
|
|
136
|
-
| Prefix | Meaning |
|
|
137
|
-
| ------ | ----------------------------- |
|
|
138
|
-
| `M0` | Mount index (router instance) |
|
|
139
|
-
| `L` | Layout |
|
|
140
|
-
| `R` | Route |
|
|
141
|
-
| `P` | Parallel slot |
|
|
142
|
-
| `D` | Loader (data) |
|
|
143
|
-
| `C` | Cache boundary |
|
|
144
|
-
|
|
145
|
-
Example: `M0L0L0R1` = mount 0, synthetic root layout, user layout, second route.
|
|
146
|
-
|
|
147
|
-
## Examples
|
|
148
|
-
|
|
149
|
-
### include() with prefix
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
const blogPatterns = urls(({ path }) => [
|
|
153
|
-
path("/", BlogIndex, { name: "index" }),
|
|
154
|
-
path("/:slug", BlogPost, { name: "post" }),
|
|
155
|
-
]);
|
|
156
|
-
|
|
157
|
-
const tree = buildRouteTree(
|
|
158
|
-
urls(({ path, include }) => [
|
|
159
|
-
path("/", HomePage, { name: "home" }),
|
|
160
|
-
include("/blog", blogPatterns, { name: "blog" }),
|
|
161
|
-
]),
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
expect(tree.routes()).toEqual({
|
|
165
|
-
home: "/",
|
|
166
|
-
"blog.index": "/blog",
|
|
167
|
-
"blog.post": "/blog/:slug",
|
|
168
|
-
});
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Middleware chain
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
const authMw = async (ctx, next) => next();
|
|
175
|
-
const logMw = async (ctx, next) => next();
|
|
176
|
-
|
|
177
|
-
const tree = buildRouteTree(
|
|
178
|
-
urls(({ path, layout, middleware }) => [
|
|
179
|
-
layout(RootLayout, () => [
|
|
180
|
-
middleware(logMw),
|
|
181
|
-
layout(AuthLayout, () => [
|
|
182
|
-
middleware(authMw),
|
|
183
|
-
path("/dashboard", Dashboard, { name: "dashboard" }),
|
|
184
|
-
]),
|
|
185
|
-
]),
|
|
186
|
-
]),
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
expect(tree.middlewareChain("dashboard")).toEqual([
|
|
190
|
-
{ segmentId: "M0L0L0", count: 1 }, // logMw on RootLayout
|
|
191
|
-
{ segmentId: "M0L0L0L0", count: 1 }, // authMw on AuthLayout
|
|
192
|
-
]);
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
### Intercepts with when()
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
const tree = buildRouteTree(
|
|
199
|
-
urls(({ path, layout, intercept, when }) => [
|
|
200
|
-
layout(ShopLayout, () => [
|
|
201
|
-
path("/products", ProductList, { name: "products" }),
|
|
202
|
-
path("/products/:id", ProductDetail, { name: "product.detail" }),
|
|
203
|
-
intercept("@modal", "product.detail", ProductModal, () => [
|
|
204
|
-
when((ctx) => ctx.from.pathname.startsWith("/products")),
|
|
205
|
-
]),
|
|
206
|
-
]),
|
|
207
|
-
]),
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
const intercepts = tree.intercepts("products");
|
|
211
|
-
// Note: intercepts are on the parent where intercept() is called
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Constrained parameters
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
const tree = buildRouteTree(
|
|
218
|
-
urls(({ path }) => [
|
|
219
|
-
path("/:locale(en|fr)?/about", AboutPage, { name: "about" }),
|
|
220
|
-
]),
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
expect(tree.match("/about")).not.toBeNull();
|
|
224
|
-
expect(tree.match("/fr/about")!.params).toEqual({ locale: "fr" });
|
|
225
|
-
expect(tree.match("/de/about")).toBeNull();
|
|
226
|
-
```
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ResolvedRouteMap,
|
|
3
|
-
RouteConfig,
|
|
4
|
-
RouteDefinition,
|
|
5
|
-
RouteDefinitionOptions,
|
|
6
|
-
TrailingSlashMode,
|
|
7
|
-
} from "../types.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Result of route() function with paths and trailing slash config
|
|
11
|
-
*/
|
|
12
|
-
export interface RouteDefinitionResult<T extends RouteDefinition> {
|
|
13
|
-
routes: ResolvedRouteMap<T>;
|
|
14
|
-
trailingSlash: Record<string, TrailingSlashMode>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Check if a value is a RouteConfig object
|
|
19
|
-
*/
|
|
20
|
-
function isRouteConfig(value: unknown): value is RouteConfig {
|
|
21
|
-
return (
|
|
22
|
-
typeof value === "object" &&
|
|
23
|
-
value !== null &&
|
|
24
|
-
"path" in value &&
|
|
25
|
-
typeof (value as RouteConfig).path === "string"
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Define routes with optional trailing slash configuration
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```typescript
|
|
34
|
-
* // Simple string paths
|
|
35
|
-
* const routes = route({
|
|
36
|
-
* blog: "/blog",
|
|
37
|
-
* post: "/blog/:id",
|
|
38
|
-
* });
|
|
39
|
-
*
|
|
40
|
-
* // With trailing slash config
|
|
41
|
-
* const routes = route({
|
|
42
|
-
* blog: "/blog",
|
|
43
|
-
* api: { path: "/api", trailingSlash: "ignore" },
|
|
44
|
-
* }, { trailingSlash: "never" }); // global default
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
export function route<const T extends RouteDefinition>(
|
|
48
|
-
input: T,
|
|
49
|
-
options?: RouteDefinitionOptions,
|
|
50
|
-
): ResolvedRouteMap<T> & {
|
|
51
|
-
__trailingSlash?: Record<string, TrailingSlashMode>;
|
|
52
|
-
} {
|
|
53
|
-
const trailingSlash: Record<string, TrailingSlashMode> = {};
|
|
54
|
-
const routes = flattenRoutes(
|
|
55
|
-
input as RouteDefinition,
|
|
56
|
-
"",
|
|
57
|
-
trailingSlash,
|
|
58
|
-
options?.trailingSlash,
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
// Attach trailing slash config as a non-enumerable property
|
|
62
|
-
// This keeps backwards compatibility while passing the config through
|
|
63
|
-
const result = routes as ResolvedRouteMap<T> & {
|
|
64
|
-
__trailingSlash?: Record<string, TrailingSlashMode>;
|
|
65
|
-
};
|
|
66
|
-
if (Object.keys(trailingSlash).length > 0) {
|
|
67
|
-
Object.defineProperty(result, "__trailingSlash", {
|
|
68
|
-
value: trailingSlash,
|
|
69
|
-
enumerable: false,
|
|
70
|
-
writable: false,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Flatten nested route definitions
|
|
79
|
-
*/
|
|
80
|
-
function flattenRoutes(
|
|
81
|
-
routes: RouteDefinition,
|
|
82
|
-
prefix: string,
|
|
83
|
-
trailingSlashConfig: Record<string, TrailingSlashMode>,
|
|
84
|
-
defaultTrailingSlash?: TrailingSlashMode,
|
|
85
|
-
): Record<string, string> {
|
|
86
|
-
const flattened: Record<string, string> = {};
|
|
87
|
-
|
|
88
|
-
for (const [key, value] of Object.entries(routes)) {
|
|
89
|
-
const fullKey = prefix + key;
|
|
90
|
-
|
|
91
|
-
if (typeof value === "string") {
|
|
92
|
-
// Direct route pattern - include prefix
|
|
93
|
-
flattened[fullKey] = value;
|
|
94
|
-
// Apply default trailing slash if set
|
|
95
|
-
if (defaultTrailingSlash) {
|
|
96
|
-
trailingSlashConfig[fullKey] = defaultTrailingSlash;
|
|
97
|
-
}
|
|
98
|
-
} else if (isRouteConfig(value)) {
|
|
99
|
-
// Route config object with path and optional trailingSlash
|
|
100
|
-
flattened[fullKey] = value.path;
|
|
101
|
-
// Use route-specific config or fall back to default
|
|
102
|
-
const mode = value.trailingSlash ?? defaultTrailingSlash;
|
|
103
|
-
if (mode) {
|
|
104
|
-
trailingSlashConfig[fullKey] = mode;
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
// Nested routes - flatten recursively
|
|
108
|
-
const nested = flattenRoutes(
|
|
109
|
-
value,
|
|
110
|
-
`${fullKey}.`,
|
|
111
|
-
trailingSlashConfig,
|
|
112
|
-
defaultTrailingSlash,
|
|
113
|
-
);
|
|
114
|
-
Object.assign(flattened, nested);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return flattened;
|
|
119
|
-
}
|