@pyreon/router 0.3.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/analysis/index.js.html +1 -1
- package/lib/index.js +283 -30
- package/lib/index.js.map +1 -1
- package/lib/types/index.d.ts +285 -29
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index2.d.ts +118 -5
- package/lib/types/index2.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/components.tsx +2 -1
- package/src/index.ts +12 -1
- package/src/match.ts +94 -28
- package/src/router.ts +336 -25
- package/src/tests/router.test.ts +629 -2
- package/src/types.ts +79 -7
package/src/types.ts
CHANGED
|
@@ -6,20 +6,28 @@ export type { ComponentFn }
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Extracts typed params from a path string at compile time.
|
|
9
|
+
* Supports optional params via `:param?` — their type is `string | undefined`.
|
|
9
10
|
*
|
|
10
11
|
* @example
|
|
11
12
|
* ExtractParams<'/user/:id/posts/:postId'>
|
|
12
13
|
* // → { id: string; postId: string }
|
|
14
|
+
*
|
|
15
|
+
* ExtractParams<'/user/:id?'>
|
|
16
|
+
* // → { id?: string | undefined }
|
|
13
17
|
*/
|
|
14
18
|
export type ExtractParams<T extends string> = T extends `${string}:${infer Param}*/${infer Rest}`
|
|
15
19
|
? { [K in Param]: string } & ExtractParams<`/${Rest}`>
|
|
16
20
|
: T extends `${string}:${infer Param}*`
|
|
17
21
|
? { [K in Param]: string }
|
|
18
|
-
: T extends `${string}:${infer Param}
|
|
19
|
-
? { [K in Param]
|
|
20
|
-
: T extends `${string}:${infer Param}
|
|
21
|
-
? { [K in Param]
|
|
22
|
-
:
|
|
22
|
+
: T extends `${string}:${infer Param}?/${infer Rest}`
|
|
23
|
+
? { [K in Param]?: string | undefined } & ExtractParams<`/${Rest}`>
|
|
24
|
+
: T extends `${string}:${infer Param}?`
|
|
25
|
+
? { [K in Param]?: string | undefined }
|
|
26
|
+
: T extends `${string}:${infer Param}/${infer Rest}`
|
|
27
|
+
? { [K in Param]: string } & ExtractParams<`/${Rest}`>
|
|
28
|
+
: T extends `${string}:${infer Param}`
|
|
29
|
+
? { [K in Param]: string }
|
|
30
|
+
: Record<never, never>
|
|
23
31
|
|
|
24
32
|
// ─── Route meta ───────────────────────────────────────────────────────────────
|
|
25
33
|
|
|
@@ -49,7 +57,7 @@ export interface RouteMeta {
|
|
|
49
57
|
// ─── Resolved route ───────────────────────────────────────────────────────────
|
|
50
58
|
|
|
51
59
|
export interface ResolvedRoute<
|
|
52
|
-
P extends Record<string, string> = Record<string, string>,
|
|
60
|
+
P extends Record<string, string | undefined> = Record<string, string>,
|
|
53
61
|
Q extends Record<string, string> = Record<string, string>,
|
|
54
62
|
> {
|
|
55
63
|
path: string
|
|
@@ -102,6 +110,19 @@ export type NavigationGuard = (
|
|
|
102
110
|
|
|
103
111
|
export type AfterEachHook = (to: ResolvedRoute, from: ResolvedRoute) => void
|
|
104
112
|
|
|
113
|
+
// ─── Navigation blockers ──────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Called before each navigation. Return `true` to block, `false` to allow.
|
|
117
|
+
* Async blockers are supported (e.g. to show a confirmation dialog).
|
|
118
|
+
*/
|
|
119
|
+
export type BlockerFn = (to: ResolvedRoute, from: ResolvedRoute) => boolean | Promise<boolean>
|
|
120
|
+
|
|
121
|
+
export interface Blocker {
|
|
122
|
+
/** Unregister this blocker so future navigations proceed freely. */
|
|
123
|
+
remove(): void
|
|
124
|
+
}
|
|
125
|
+
|
|
105
126
|
// ─── Route loaders ────────────────────────────────────────────────────────────
|
|
106
127
|
|
|
107
128
|
export interface LoaderContext {
|
|
@@ -133,6 +154,14 @@ export interface RouteRecord<TPath extends string = string> {
|
|
|
133
154
|
beforeEnter?: NavigationGuard | NavigationGuard[]
|
|
134
155
|
/** Guard(s) run before leaving this route. Return false to cancel. */
|
|
135
156
|
beforeLeave?: NavigationGuard | NavigationGuard[]
|
|
157
|
+
/**
|
|
158
|
+
* Alternative path(s) for this route. Alias paths render the same component
|
|
159
|
+
* and share guards, loaders, and metadata with the primary path.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* { path: "/user/:id", alias: ["/profile/:id"], component: UserPage }
|
|
163
|
+
*/
|
|
164
|
+
alias?: string | string[]
|
|
136
165
|
/** Child routes rendered inside this route's component via <RouterView /> */
|
|
137
166
|
children?: RouteRecord[]
|
|
138
167
|
/**
|
|
@@ -141,6 +170,12 @@ export interface RouteRecord<TPath extends string = string> {
|
|
|
141
170
|
* Receives an AbortSignal that fires if a newer navigation supersedes this one.
|
|
142
171
|
*/
|
|
143
172
|
loader?: RouteLoaderFn
|
|
173
|
+
/**
|
|
174
|
+
* When true, the router shows cached loader data immediately (stale) and
|
|
175
|
+
* revalidates in the background. The component re-renders once fresh data arrives.
|
|
176
|
+
* Only applies when navigating to a route that already has cached loader data.
|
|
177
|
+
*/
|
|
178
|
+
staleWhileRevalidate?: boolean
|
|
144
179
|
/** Component rendered when this route's loader throws an error */
|
|
145
180
|
errorComponent?: ComponentFn
|
|
146
181
|
}
|
|
@@ -157,6 +192,13 @@ export interface RouterOptions {
|
|
|
157
192
|
routes: RouteRecord[]
|
|
158
193
|
/** "hash" (default) uses location.hash; "history" uses pushState */
|
|
159
194
|
mode?: "hash" | "history"
|
|
195
|
+
/**
|
|
196
|
+
* Base path for the application. Used when deploying to a sub-path
|
|
197
|
+
* (e.g. `"/app"` for `https://example.com/app/`).
|
|
198
|
+
* Only applies in history mode. Must start with `/`.
|
|
199
|
+
* Default: `""` (no base path).
|
|
200
|
+
*/
|
|
201
|
+
base?: string
|
|
160
202
|
/**
|
|
161
203
|
* Global scroll behavior. Per-route meta.scrollBehavior takes precedence.
|
|
162
204
|
* Default: "top"
|
|
@@ -183,6 +225,13 @@ export interface RouterOptions {
|
|
|
183
225
|
* Default: 100.
|
|
184
226
|
*/
|
|
185
227
|
maxCacheSize?: number
|
|
228
|
+
/**
|
|
229
|
+
* Trailing slash handling:
|
|
230
|
+
* - `"strip"` — removes trailing slashes before matching (default)
|
|
231
|
+
* - `"add"` — ensures paths always end with `/`
|
|
232
|
+
* - `"ignore"` — no normalization
|
|
233
|
+
*/
|
|
234
|
+
trailingSlash?: "strip" | "add" | "ignore"
|
|
186
235
|
}
|
|
187
236
|
|
|
188
237
|
// ─── Router interface ─────────────────────────────────────────────────────────
|
|
@@ -198,8 +247,18 @@ export interface Router {
|
|
|
198
247
|
}): Promise<void>
|
|
199
248
|
/** Replace current history entry */
|
|
200
249
|
replace(path: string): Promise<void>
|
|
201
|
-
/**
|
|
250
|
+
/** Replace current history entry using a named route */
|
|
251
|
+
replace(location: {
|
|
252
|
+
name: string
|
|
253
|
+
params?: Record<string, string>
|
|
254
|
+
query?: Record<string, string>
|
|
255
|
+
}): Promise<void>
|
|
256
|
+
/** Go back one step in history */
|
|
202
257
|
back(): void
|
|
258
|
+
/** Go forward one step in history */
|
|
259
|
+
forward(): void
|
|
260
|
+
/** Navigate forward or backward by `delta` steps in the history stack */
|
|
261
|
+
go(delta: number): void
|
|
203
262
|
/** Register a global before-navigation guard. Returns an unregister function. */
|
|
204
263
|
beforeEach(guard: NavigationGuard): () => void
|
|
205
264
|
/** Register a global after-navigation hook. Returns an unregister function. */
|
|
@@ -208,6 +267,11 @@ export interface Router {
|
|
|
208
267
|
readonly currentRoute: () => ResolvedRoute
|
|
209
268
|
/** True while a navigation (guards + loaders) is in flight */
|
|
210
269
|
readonly loading: () => boolean
|
|
270
|
+
/**
|
|
271
|
+
* Promise that resolves once the initial navigation is complete.
|
|
272
|
+
* Useful for SSR and for delaying rendering until the first route is resolved.
|
|
273
|
+
*/
|
|
274
|
+
isReady(): Promise<void>
|
|
211
275
|
/** Remove all event listeners, clear caches, and abort in-flight navigations. */
|
|
212
276
|
destroy(): void
|
|
213
277
|
}
|
|
@@ -219,6 +283,8 @@ import type { Computed, Signal } from "@pyreon/reactivity"
|
|
|
219
283
|
export interface RouterInstance extends Router {
|
|
220
284
|
routes: RouteRecord[]
|
|
221
285
|
mode: "hash" | "history"
|
|
286
|
+
/** Normalized base path (e.g. "/app"), empty string if none */
|
|
287
|
+
_base: string
|
|
222
288
|
_currentPath: Signal<string>
|
|
223
289
|
_currentRoute: Computed<ResolvedRoute>
|
|
224
290
|
_componentCache: Map<RouteRecord, ComponentFn>
|
|
@@ -240,4 +306,10 @@ export interface RouterInstance extends Router {
|
|
|
240
306
|
_loaderData: Map<RouteRecord, unknown>
|
|
241
307
|
/** AbortController for the in-flight loader batch — aborted when a newer navigation starts */
|
|
242
308
|
_abortController: AbortController | null
|
|
309
|
+
/** Registered navigation blockers */
|
|
310
|
+
_blockers: Set<BlockerFn>
|
|
311
|
+
/** Resolves the isReady() promise after initial navigation completes */
|
|
312
|
+
_readyResolve: (() => void) | null
|
|
313
|
+
/** The isReady() promise instance */
|
|
314
|
+
_readyPromise: Promise<void>
|
|
243
315
|
}
|