Repository navigation
Expand file tree
/
Copy pathindex.d.ts
More file actions
347 lines (312 loc) · 9.78 KB
/
Copy pathindex.d.ts
File metadata and controls
347 lines (312 loc) · 9.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
import type { Component, Snippet } from 'svelte';
import type { Action } from 'svelte/action';
/**
* A Svelte action that will add a class to the anchor if its `href` matches the current route. It
* can have an optional `className` parameter to specify the class to add, otherwise it will default
* to `is-active`.
*
* ```svelte
* <a href={p('/about')} use:isActiveLink={{ className: 'active-link' }}>
* ```
*/
export const isActiveLink: IsActiveLink;
/** Create a search string from the search object that is provided in hooks. */
export function serializeSearch(search: Search): string | undefined;
/**
* Setup a new router instance with the given routes.
*
* ```js
* export const { p, navigate, isActive, route } = createRouter({
* '/': Home,
* '/about': About,
* ...
* });
* ```
*/
export function createRouter<T extends Routes>(r: T): RouterApi<T>;
/**
* The component that will render the current route. You can pass a `base` prop to set the base path
* that is prepended to every url.
*/
export const Router: Component<{ base?: string }>;
/**
* The reactive search params of the URL. It is just a wrapper around `SvelteURLSearchParam` that
* will update the url on change.
*/
export const searchParams: SearchParams;
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
type BaseProps = {};
export type LazyRouteComponent<Props extends BaseProps = BaseProps> = () => Promise<{
default: Component<Props> | Snippet<[Props]>;
}>;
export type RouteComponent<Props extends BaseProps = any> =
| Component<Props>
| Snippet<[Props]>
| LazyRouteComponent<Props>;
export type LayoutComponent = RouteComponent<{ children: Snippet }>;
export type Hooks = {
/**
* A function that will be called before the route is loaded. If it returns a promise, the route
* will wait for it to resolve before loading.
*
* You can throw a `navigate` call to redirect to another route.
*
* ```js
* async beforeLoad({ pathname }) {
* await ...
* throw navigate('/home');
* }
* ```
*/
beforeLoad?(context: HooksContext): void | Promise<void>;
/** A function that will be called after the route is loaded. */
afterLoad?(context: HooksContext): void | Promise<void>;
/** A function that will be called when the route is preloaded. */
onPreload?(context: HooksContext): void | Promise<void>;
/** A function that will be called when the route fails to load. */
onError?(error: unknown, context: HooksContext): void | Promise<void>;
};
export type Routes = {
[_: `/${string}`]: RouteComponent | Routes;
[_: `*${string}` | `(*${string})`]: RouteComponent | undefined;
layout?: LayoutComponent;
hooks?: Hooks;
meta?: RouteMeta;
};
export type IsActiveLink = Action<
HTMLAnchorElement,
| {
className?: string;
startsWith?: boolean;
}
| undefined
>;
/**
* Route metadata that can be extended via module augmentation.
*
* @example
* declare module 'sv-router' {
* interface RouteMeta {
* public?: boolean;
* requiresAuth?: boolean;
* }
* }
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface RouteMeta {}
export class Navigation extends Error {
constructor(target: string);
}
export type RouterApi<T extends Routes> = {
/**
* Construct a path while ensuring type safety.
*
* ```js
* p('/users');
* // With parameters
* p('/users/:id', { id: 1 });
* ```
*
* @param route The route to navigate to.
* @param params The parameters to replace in the route.
*/
p<U extends Path<T>>(...args: ConstructPathArgs<U>): string;
/**
* Navigate programmatically to a route.
*
* ```js
* navigate('/users');
* // With parameters
* navigate('/users/:id', {
* params: {
* id: 1,
* },
* });
* // Back and forward
* navigate(-1);
* navigate(2);
* ```
*
* @param route The route to navigate to.
* @param options The navigation options.
* @returns {@link Navigation} For use with `throw navigate(...)` inside hooks.
*/
navigate<U extends Path<T>>(...args: NavigateArgs<U>): Navigation;
/**
* Will return `true` if the given path is active.
*
* Can be used with params to check the exact path, or without to check for any params in the
* path.
*
* @param path The route to check.
* @param params The optional parameters to replace in the route.
*/
isActive: {
<U extends Path<T>>(...args: IsActiveArgs<U>): boolean;
startsWith<U extends Path<T>>(...args: IsActiveArgs<U, true>): boolean;
};
/**
* Preloads the given route.
*
* @param path The route to preload.
*/
preload<U extends Path<T>>(path: U): Promise<void>;
route: {
/**
* An object containing the parameters of the current route.
*
* For example, given the route `/posts/:slug/comments/:commentId` and the URL
* `http://localhost:5173/posts/hello-world/comments/123`, the `params` object would be `{ slug:
* 'hello-world', commentId: '123' }`.
*/
params: AllParams<T>;
/**
* Extract parameters from the given pathname. Will throw if the pathname does not match the
* current route.
*
* ```ts
* route.getParams('/posts/:slug').slug;
* ```
*
* @param pathname
*/
getParams<U extends Path<T>>(pathname: U): Record<ExtractParams<U>, string>;
/** The reactive pathname of the URL. */
pathname: (Path<T, true> & {}) | (string & {});
/** The reactive query string part of the URL. */
search: Record<string, string | number | boolean>;
/** The reactive history state that can be passed to the `navigate` function. */
state: unknown;
/** The reactive hash part of the URL. */
hash: string;
/** Arbitrary metadata associated with the route. */
meta: RouteMeta;
};
};
export type Path<T extends Routes, AnyParam extends boolean = false> = RemoveParenthesis<
RemoveLastSlash<RecursiveKeys<StripNonRoutes<T>, '', AnyParam>>
>;
export type ConstructPathArgs<TPath extends string> = {
[Path in TPath]: PathParams<Path> extends never
? [Path] | [Path, ConstructUrlOptions]
: [Path, ConstructUrlOptions & { params: PathParams<Path> }];
}[TPath];
export type IsActiveArgs<
TPath extends string,
StartsWith extends boolean = false,
> = StartsWith extends true
? {
[Path in TPath]: PathParams<Path> extends never
? [PathPrefixes<Path>]
: [PathPrefixes<Path>] | [PathPrefixes<Path>, PathParams<Path>];
}[TPath]
: {
[Path in TPath]: PathParams<Path> extends never ? [Path] : [Path] | [Path, PathParams<Path>];
}[TPath];
export type PathParams<TPath extends string> =
ExtractParams<RemoveParenthesis<TPath>> extends never
? never
: Record<ExtractParams<RemoveParenthesis<TPath>>, string>;
export type AllParams<TRoutes extends Routes> = Partial<
Record<ExtractParams<RemoveParenthesis<RecursiveKeys<TRoutes>>>, string>
>;
export type Search = string | Record<string, string | number | boolean>;
export type HooksContext = {
hash?: string;
meta: RouteMeta;
pathname: string;
replace?: boolean;
search: Record<string, string | number | boolean>;
state?: string;
};
export type NavigateOptions =
| {
replace?: boolean;
search?: Search;
state?: string;
hash?: string;
scrollToTop?: ScrollBehavior | false;
viewTransition?: boolean;
}
| undefined;
export type ConstructUrlOptions =
| {
search?: Search;
hash?: string;
}
| undefined;
export type SearchParams = Omit<
URLSearchParams,
'append' | 'delete' | 'entries' | 'get' | 'getAll' | 'set' | 'sort' | 'values'
> & {
append(name: string, value: string | number | boolean, options?: { replace?: boolean }): void;
delete(name: string, value?: string | number | boolean, options?: { replace?: boolean }): void;
entries(): [string, string | number | boolean][];
get(name: string): string | number | boolean | null;
getAll(name: string): (string | number | boolean)[];
set(name: string, value: string | number | boolean, options?: { replace?: boolean }): void;
sort(options?: { replace?: boolean }): void;
values(): (string | number | boolean)[];
};
type NavigateArgs<T extends string> =
| (PathParams<T> extends never
? [T] | [T, Omit<NavigateOptions, 'search'> & { search: Search }]
: [T, Omit<NavigateOptions, 'search'> & { search: Search; params: PathParams<T> }])
| [number];
type StripNonRoutes<T extends Routes> = {
[K in keyof T as K extends `*${string}`
? never
: K extends `(*${string})`
? never
: K extends 'layout'
? never
: K extends 'hooks'
? never
: K extends 'meta'
? never
: K]: T[K] extends Routes ? StripNonRoutes<T[K]> : T[K];
};
type RecursiveKeys<
T extends Routes,
Prefix extends string = '',
AnyParam extends boolean = false,
> = {
[K in keyof T]: K extends string
? T[K] extends Routes
? RecursiveKeys<
T[K],
`${Prefix}${AnyParam extends true ? ReplaceParamWithString<K> : K}`,
AnyParam
>
: `${Prefix}${AnyParam extends true ? ReplaceParamWithString<K> : K}`
: never;
}[keyof T];
type ReplaceParamWithString<T extends string> = T extends `/:${string}`
? `/${string}`
: T extends `/(:${string})`
? `/${string}`
: T;
type RemoveLastSlash<T extends string> = T extends '/' ? T : T extends `${infer R}/` ? R : T;
type RemoveParenthesis<T extends string> = T extends `${infer A}(${infer B})${infer C}`
? RemoveParenthesis<`${A}${B}${C}`>
: T;
type ExtractParams<T extends string> = T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? Param
: T extends `${string}*${infer Param}`
? Param extends ''
? never
: Param
: never;
type PathPrefixes<T extends string, Acc extends string = ''> = T extends '/'
? '/'
: T extends `/${infer Segment}/${infer Rest}`
? Acc extends ''
? PathPrefixes<`/${Rest}`, `/${Segment}`> | `/${Segment}`
: PathPrefixes<`/${Rest}`, `${Acc}/${Segment}`> | `${Acc}/${Segment}` | Acc
: T extends `/${infer Segment}`
? Acc extends ''
? `/${Segment}`
: `${Acc}/${Segment}` | Acc
: Acc;