@tanstack/router-core 0.0.1-beta.15 → 0.0.1-beta.150

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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/fileRoute.js +29 -0
  3. package/build/cjs/fileRoute.js.map +1 -0
  4. package/build/cjs/history.js +226 -0
  5. package/build/cjs/history.js.map +1 -0
  6. package/build/cjs/index.js +78 -0
  7. package/build/cjs/{packages/router-core/src/index.js.map → index.js.map} +1 -1
  8. package/build/cjs/{packages/router-core/src/path.js → path.js} +45 -56
  9. package/build/cjs/path.js.map +1 -0
  10. package/build/cjs/{packages/router-core/src/qss.js → qss.js} +10 -16
  11. package/build/cjs/qss.js.map +1 -0
  12. package/build/cjs/route.js +103 -0
  13. package/build/cjs/route.js.map +1 -0
  14. package/build/cjs/router.js +1113 -0
  15. package/build/cjs/router.js.map +1 -0
  16. package/build/cjs/{packages/router-core/src/searchParams.js → searchParams.js} +11 -13
  17. package/build/cjs/searchParams.js.map +1 -0
  18. package/build/cjs/{packages/router-core/src/utils.js → utils.js} +54 -64
  19. package/build/cjs/utils.js.map +1 -0
  20. package/build/esm/index.js +1417 -2105
  21. package/build/esm/index.js.map +1 -1
  22. package/build/stats-html.html +59 -49
  23. package/build/stats-react.json +203 -234
  24. package/build/types/index.d.ts +604 -426
  25. package/build/umd/index.development.js +1640 -2224
  26. package/build/umd/index.development.js.map +1 -1
  27. package/build/umd/index.production.js +13 -2
  28. package/build/umd/index.production.js.map +1 -1
  29. package/package.json +11 -7
  30. package/src/fileRoute.ts +122 -0
  31. package/src/history.ts +292 -0
  32. package/src/index.ts +3 -10
  33. package/src/link.ts +118 -113
  34. package/src/path.ts +37 -17
  35. package/src/qss.ts +1 -2
  36. package/src/route.ts +891 -218
  37. package/src/routeInfo.ts +121 -204
  38. package/src/router.ts +1494 -1017
  39. package/src/searchParams.ts +1 -1
  40. package/src/utils.ts +80 -49
  41. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
  42. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  43. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
  44. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
  45. package/build/cjs/node_modules/history/index.js +0 -815
  46. package/build/cjs/node_modules/history/index.js.map +0 -1
  47. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
  48. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
  49. package/build/cjs/packages/router-core/src/index.js +0 -58
  50. package/build/cjs/packages/router-core/src/path.js.map +0 -1
  51. package/build/cjs/packages/router-core/src/qss.js.map +0 -1
  52. package/build/cjs/packages/router-core/src/route.js +0 -147
  53. package/build/cjs/packages/router-core/src/route.js.map +0 -1
  54. package/build/cjs/packages/router-core/src/routeConfig.js +0 -69
  55. package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
  56. package/build/cjs/packages/router-core/src/routeMatch.js +0 -231
  57. package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
  58. package/build/cjs/packages/router-core/src/router.js +0 -833
  59. package/build/cjs/packages/router-core/src/router.js.map +0 -1
  60. package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
  61. package/build/cjs/packages/router-core/src/utils.js.map +0 -1
  62. package/src/frameworks.ts +0 -11
  63. package/src/routeConfig.ts +0 -514
  64. package/src/routeMatch.ts +0 -319
package/src/route.ts CHANGED
@@ -1,227 +1,900 @@
1
- import {
2
- CheckRelativePath,
3
- LinkInfo,
4
- LinkOptions,
5
- ResolveRelativePath,
6
- ToOptions,
7
- } from './link'
8
- import { LoaderContext, RouteConfig, RouteOptions } from './routeConfig'
9
- import {
10
- AnyAllRouteInfo,
11
- AnyRouteInfo,
12
- DefaultAllRouteInfo,
13
- RouteInfo,
14
- RouteInfoByPath,
15
- } from './routeInfo'
16
- import {
17
- Action,
18
- ActionState,
19
- Loader,
20
- LoaderState,
21
- MatchRouteOptions,
22
- Router,
23
- } from './router'
24
- import { NoInfer } from './utils'
25
-
26
- export interface AnyRoute extends Route<any, any> {}
27
-
28
- export interface Route<
29
- TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
30
- TRouteInfo extends AnyRouteInfo = RouteInfo,
31
- > {
32
- routeId: TRouteInfo['id']
33
- routeRouteId: TRouteInfo['routeId']
34
- routePath: TRouteInfo['path']
35
- fullPath: TRouteInfo['fullPath']
36
- parentRoute?: AnyRoute
37
- childRoutes?: AnyRoute[]
38
- options: RouteOptions
39
- router: Router<TAllRouteInfo['routeConfig'], TAllRouteInfo>
40
- buildLink: <TTo extends string = '.'>(
41
- options: Omit<
42
- LinkOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo>,
43
- 'from'
44
- >,
45
- ) => LinkInfo
46
- matchRoute: <
47
- TTo extends string = '.',
48
- TResolved extends string = ResolveRelativePath<TRouteInfo['id'], TTo>,
49
- >(
50
- matchLocation: CheckRelativePath<
51
- TAllRouteInfo,
52
- TRouteInfo['fullPath'],
53
- NoInfer<TTo>
54
- > &
55
- Omit<ToOptions<TAllRouteInfo, TRouteInfo['fullPath'], TTo>, 'from'>,
56
- opts?: MatchRouteOptions,
57
- ) => RouteInfoByPath<TAllRouteInfo, TResolved>['allParams']
58
- navigate: <TTo extends string = '.'>(
59
- options: Omit<LinkOptions<TAllRouteInfo, TRouteInfo['id'], TTo>, 'from'>,
60
- ) => Promise<void>
61
- action: unknown extends TRouteInfo['actionResponse']
62
- ?
63
- | Action<TRouteInfo['actionPayload'], TRouteInfo['actionResponse']>
64
- | undefined
65
- : Action<TRouteInfo['actionPayload'], TRouteInfo['actionResponse']>
66
- loader: unknown extends TRouteInfo['routeLoaderData']
67
- ?
68
- | Action<
69
- LoaderContext<
70
- TRouteInfo['fullSearchSchema'],
71
- TRouteInfo['allParams']
72
- >,
73
- TRouteInfo['routeLoaderData']
74
- >
75
- | undefined
76
- : Loader<
77
- TRouteInfo['fullSearchSchema'],
78
- TRouteInfo['allParams'],
79
- TRouteInfo['routeLoaderData']
80
- >
1
+ import { ParsePathParams } from './link'
2
+ import { AnyRouter, Router, RouteMatch } from './router'
3
+ import { IsAny, NoInfer, PickRequired, UnionToIntersection } from './utils'
4
+ import invariant from 'tiny-invariant'
5
+ import { joinPaths, trimPath } from './path'
6
+ import { AnyRoutesInfo, DefaultRoutesInfo } from './routeInfo'
7
+
8
+ export const rootRouteId = '__root__' as const
9
+ export type RootRouteId = typeof rootRouteId
10
+ export type AnyPathParams = {}
11
+ export type AnySearchSchema = {}
12
+ export type AnyContext = {}
13
+ export interface RouteMeta {}
14
+ export interface RouteContext {}
15
+ export interface RegisterRouteComponent<TProps> {
16
+ // RouteComponent: unknown // This is registered by the framework
17
+ }
18
+ export interface RegisterRouteErrorComponent<TProps> {
19
+ // RouteErrorComponent: unknown // This is registered by the framework
20
+ }
21
+
22
+ export type RegisteredRouteComponent<TProps> =
23
+ RegisterRouteComponent<TProps> extends {
24
+ RouteComponent: infer T
25
+ }
26
+ ? T
27
+ : (props: TProps) => unknown
28
+
29
+ export type RegisteredRouteErrorComponent<TProps> =
30
+ RegisterRouteErrorComponent<TProps> extends {
31
+ RouteErrorComponent: infer T
32
+ }
33
+ ? T
34
+ : (props: TProps) => unknown
35
+
36
+ export type PreloadableObj = { preload?: () => Promise<void> }
37
+
38
+ export type RoutePathOptions<TCustomId, TPath> =
39
+ | {
40
+ path: TPath
41
+ }
42
+ | {
43
+ id: TCustomId
44
+ }
45
+
46
+ export type RoutePathOptionsIntersection<TCustomId, TPath> =
47
+ UnionToIntersection<RoutePathOptions<TCustomId, TPath>>
48
+
49
+ export type MetaOptions = keyof PickRequired<RouteMeta> extends never
50
+ ? {
51
+ meta?: RouteMeta
52
+ }
53
+ : {
54
+ meta: RouteMeta
55
+ }
56
+
57
+ export type AnyRouteProps = RouteProps<any, any, any, any, any>
58
+ export type ComponentPropsFromRoute<TRoute> = TRoute extends Route<
59
+ infer TParentRoute,
60
+ infer TPath,
61
+ infer TFullPath,
62
+ infer TCustomId,
63
+ infer TId,
64
+ infer TLoader,
65
+ infer TSearchSchema,
66
+ infer TFullSearchSchema,
67
+ infer TParams,
68
+ infer TAllParams,
69
+ infer TParentContext,
70
+ infer TAllParentContext,
71
+ infer TRouteContext,
72
+ infer TContext,
73
+ infer TRouterContext,
74
+ infer TChildren,
75
+ infer TRoutesInfo
76
+ >
77
+ ? RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
78
+ : never
79
+
80
+ export type ComponentFromRoute<TRoute> = RegisteredRouteComponent<
81
+ ComponentPropsFromRoute<TRoute>
82
+ >
83
+
84
+ export type RouteLoaderFromRoute<TRoute extends AnyRoute> = LoaderFn<
85
+ TRoute['__types']['loader'],
86
+ TRoute['__types']['searchSchema'],
87
+ TRoute['__types']['fullSearchSchema'],
88
+ TRoute['__types']['allParams'],
89
+ TRoute['__types']['routeContext'],
90
+ TRoute['__types']['context']
91
+ >
92
+
93
+ export type RouteProps<
94
+ TLoader = unknown,
95
+ TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
96
+ TAllParams = AnyPathParams,
97
+ TRouteContext = AnyContext,
98
+ TContext = AnyContext,
99
+ > = {
100
+ useMatch: () => RouteMatch<AnyRoutesInfo, AnyRoute>
101
+ useLoader: () => UseLoaderResult<TLoader>
102
+ useSearch: <
103
+ TStrict extends boolean = true,
104
+ TSearch = TFullSearchSchema,
105
+ TSelected = TSearch,
106
+ >(opts?: {
107
+ strict?: TStrict
108
+ select?: (search: TSearch) => TSelected
109
+ }) => TStrict extends true ? TSelected : TSelected | undefined
110
+ useParams: <
111
+ TDefaultSelected = TAllParams,
112
+ TSelected = TDefaultSelected,
113
+ >(opts?: {
114
+ select?: (params: TDefaultSelected) => TSelected
115
+ }) => TSelected
116
+ useContext: <
117
+ TDefaultSelected = TContext,
118
+ TSelected = TDefaultSelected,
119
+ >(opts?: {
120
+ select?: (context: TDefaultSelected) => TSelected
121
+ }) => TSelected
122
+ useRouteContext: <
123
+ TDefaultSelected = TRouteContext,
124
+ TSelected = TDefaultSelected,
125
+ >(opts?: {
126
+ select?: (context: TDefaultSelected) => TSelected
127
+ }) => TSelected
81
128
  }
82
129
 
83
- export function createRoute<
84
- TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
85
- TRouteInfo extends AnyRouteInfo = RouteInfo,
86
- >(
87
- routeConfig: RouteConfig,
88
- options: TRouteInfo['options'],
89
- parent: undefined | Route<TAllRouteInfo, any>,
90
- router: Router<TAllRouteInfo['routeConfig'], TAllRouteInfo>,
91
- ): Route<TAllRouteInfo, TRouteInfo> {
92
- const { id, routeId, path: routePath, fullPath } = routeConfig
93
-
94
- const action =
95
- router.state.actions[id] ||
96
- (() => {
97
- router.state.actions[id] = {
98
- submissions: [],
99
- submit: async <T, U>(
100
- submission: T,
101
- actionOpts?: { invalidate?: boolean; multi?: boolean },
102
- ) => {
103
- if (!route) {
104
- return
105
- }
106
-
107
- const invalidate = actionOpts?.invalidate ?? true
108
-
109
- if (!actionOpts?.multi) {
110
- action.submissions = action.submissions.filter((d) => d.isMulti)
111
- }
112
-
113
- const actionState: ActionState<T, U> = {
114
- submittedAt: Date.now(),
115
- status: 'pending',
116
- submission,
117
- isMulti: !!actionOpts?.multi,
118
- }
119
-
120
- action.current = actionState
121
- action.latest = actionState
122
- action.submissions.push(actionState)
123
-
124
- router.notify()
125
-
126
- try {
127
- const res = await route.options.action?.(submission)
128
- actionState.data = res as U
129
-
130
- if (invalidate) {
131
- router.invalidateRoute({ to: '.', fromCurrent: true })
132
- await router.reload()
133
- }
134
- actionState.status = 'success'
135
- return res
136
- } catch (err) {
137
- console.error(err)
138
- actionState.error = err
139
- actionState.status = 'error'
140
- } finally {
141
- router.notify()
142
- }
143
- },
130
+ export type RouteOptions<
131
+ TParentRoute extends AnyRoute = AnyRoute,
132
+ TCustomId extends string = string,
133
+ TPath extends string = string,
134
+ TLoader = unknown,
135
+ TParentSearchSchema extends AnySearchSchema = {},
136
+ TSearchSchema extends AnySearchSchema = {},
137
+ TFullSearchSchema extends AnySearchSchema = TSearchSchema,
138
+ TParentParams extends AnyPathParams = AnyPathParams,
139
+ TParams extends AnyPathParams = Record<ParsePathParams<TPath>, string>,
140
+ TAllParams extends AnyPathParams = TParams,
141
+ TParentContext extends AnyContext = AnyContext,
142
+ TAllParentContext extends IsAny<
143
+ TParentRoute['__types']['allParams'],
144
+ TParentContext,
145
+ TParentRoute['__types']['allParams'] & TParentContext
146
+ > = IsAny<
147
+ TParentRoute['__types']['allParams'],
148
+ TParentContext,
149
+ TParentRoute['__types']['allParams'] & TParentContext
150
+ >,
151
+ TRouteContext extends RouteContext = RouteContext,
152
+ TContext extends MergeParamsFromParent<
153
+ TAllParentContext,
154
+ TRouteContext
155
+ > = MergeParamsFromParent<TAllParentContext, TRouteContext>,
156
+ > = BaseRouteOptions<
157
+ TParentRoute,
158
+ TCustomId,
159
+ TPath,
160
+ TLoader,
161
+ TParentSearchSchema,
162
+ TSearchSchema,
163
+ TFullSearchSchema,
164
+ TParentParams,
165
+ TParams,
166
+ TAllParams,
167
+ TParentContext,
168
+ TAllParentContext,
169
+ TRouteContext,
170
+ TContext
171
+ > &
172
+ UpdatableRouteOptions<
173
+ TLoader,
174
+ TSearchSchema,
175
+ TFullSearchSchema,
176
+ TAllParams,
177
+ TRouteContext,
178
+ TContext
179
+ >
180
+
181
+ export type ParamsFallback<
182
+ TPath extends string,
183
+ TParams,
184
+ > = unknown extends TParams ? Record<ParsePathParams<TPath>, string> : TParams
185
+
186
+ export type BaseRouteOptions<
187
+ TParentRoute extends AnyRoute = AnyRoute,
188
+ TCustomId extends string = string,
189
+ TPath extends string = string,
190
+ TLoader = unknown,
191
+ TParentSearchSchema extends AnySearchSchema = {},
192
+ TSearchSchema extends AnySearchSchema = {},
193
+ TFullSearchSchema extends AnySearchSchema = TSearchSchema,
194
+ TParentParams extends AnyPathParams = AnyPathParams,
195
+ TParams = unknown,
196
+ TAllParams = ParamsFallback<TPath, TParams>,
197
+ TParentContext extends AnyContext = AnyContext,
198
+ TAllParentContext extends IsAny<
199
+ TParentRoute['__types']['allParams'],
200
+ TParentContext,
201
+ TParentRoute['__types']['allParams'] & TParentContext
202
+ > = IsAny<
203
+ TParentRoute['__types']['allParams'],
204
+ TParentContext,
205
+ TParentRoute['__types']['allParams'] & TParentContext
206
+ >,
207
+ TRouteContext extends RouteContext = RouteContext,
208
+ TContext extends MergeParamsFromParent<
209
+ TAllParentContext,
210
+ TRouteContext
211
+ > = MergeParamsFromParent<TAllParentContext, TRouteContext>,
212
+ > = RoutePathOptions<TCustomId, TPath> & {
213
+ getParentRoute: () => TParentRoute
214
+ validateSearch?: SearchSchemaValidator<TSearchSchema, TParentSearchSchema>
215
+ loader?: LoaderFn<
216
+ TLoader,
217
+ TSearchSchema,
218
+ TFullSearchSchema,
219
+ TAllParams,
220
+ NoInfer<TRouteContext>,
221
+ TContext
222
+ >
223
+ } & (
224
+ | {
225
+ // Both or none
226
+ parseParams?: (
227
+ rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>,
228
+ ) => TParams extends Record<ParsePathParams<TPath>, any>
229
+ ? TParams
230
+ : 'parseParams must return an object'
231
+ // | {
232
+ // parse: (
233
+ // rawParams: IsAny<
234
+ // TPath,
235
+ // any,
236
+ // Record<ParsePathParams<TPath>, string>
237
+ // >,
238
+ // ) => TParams extends Record<ParsePathParams<TPath>, any>
239
+ // ? TParams
240
+ // : 'parseParams must return an object'
241
+ // }
242
+ stringifyParams?: (
243
+ params: NoInfer<ParamsFallback<TPath, TParams>>,
244
+ ) => Record<ParsePathParams<TPath>, string>
245
+ }
246
+ | {
247
+ stringifyParams?: never
248
+ parseParams?: never
249
+ }
250
+ ) &
251
+ (keyof PickRequired<RouteContext> extends never
252
+ ? {
253
+ getContext?: GetContextFn<
254
+ TParentRoute,
255
+ TAllParams,
256
+ TFullSearchSchema,
257
+ TParentContext,
258
+ TAllParentContext,
259
+ TRouteContext
260
+ >
261
+ }
262
+ : {
263
+ getContext: GetContextFn<
264
+ TParentRoute,
265
+ TAllParams,
266
+ TFullSearchSchema,
267
+ TParentContext,
268
+ TAllParentContext,
269
+ TRouteContext
270
+ >
271
+ })
272
+
273
+ type GetContextFn<
274
+ TParentRoute,
275
+ TAllParams,
276
+ TFullSearchSchema,
277
+ TParentContext,
278
+ TAllParentContext,
279
+ TRouteContext,
280
+ > = (
281
+ opts: {
282
+ params: TAllParams
283
+ search: TFullSearchSchema
284
+ } & (TParentRoute extends undefined
285
+ ? {
286
+ context?: TAllParentContext
287
+ parentContext?: TParentContext
144
288
  }
145
- return router.state.actions[id]!
146
- })()
147
-
148
- const loader =
149
- router.state.loaders[id] ||
150
- (() => {
151
- router.state.loaders[id] = {
152
- pending: [],
153
- fetch: (async (loaderContext: LoaderContext<any, any>) => {
154
- if (!route) {
155
- return
156
- }
157
-
158
- const loaderState: LoaderState<any, any> = {
159
- loadedAt: Date.now(),
160
- loaderContext,
161
- }
162
-
163
- loader.current = loaderState
164
- loader.latest = loaderState
165
- loader.pending.push(loaderState)
166
-
167
- // router.state = {
168
- // ...router.state,
169
- // currentAction: loaderState,
170
- // latestAction: loaderState,
171
- // }
172
-
173
- router.notify()
174
-
175
- try {
176
- return await route.options.loader?.(loaderContext)
177
- } finally {
178
- loader.pending = loader.pending.filter((d) => d !== loaderState)
179
- // router.removeActionQueue.push({ loader, loaderState })
180
- router.notify()
181
- }
182
- }) as any,
289
+ : {
290
+ context: TAllParentContext
291
+ parentContext: TParentContext
292
+ }),
293
+ ) => TRouteContext
294
+
295
+ export type UpdatableRouteOptions<
296
+ TLoader,
297
+ TSearchSchema extends AnySearchSchema,
298
+ TFullSearchSchema extends AnySearchSchema,
299
+ TAllParams extends AnyPathParams,
300
+ TRouteContext extends AnyContext,
301
+ TContext extends AnyContext,
302
+ > = MetaOptions & {
303
+ key?: null | false | GetKeyFn<TFullSearchSchema, TAllParams>
304
+ // If true, this route will be matched as case-sensitive
305
+ caseSensitive?: boolean
306
+ // If true, this route will be forcefully wrapped in a suspense boundary
307
+ wrapInSuspense?: boolean
308
+ // The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />`
309
+ component?: RegisteredRouteComponent<
310
+ RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
311
+ >
312
+ // The content to be rendered when the route encounters an error
313
+ errorComponent?: RegisteredRouteErrorComponent<
314
+ { error: unknown } & Partial<
315
+ RouteProps<
316
+ TLoader,
317
+ TFullSearchSchema,
318
+ TAllParams,
319
+ TRouteContext,
320
+ TContext
321
+ >
322
+ >
323
+ > //
324
+ // If supported by your framework, the content to be rendered as the fallback content until the route is ready to render
325
+ pendingComponent?: RegisteredRouteComponent<
326
+ RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
327
+ >
328
+ // Filter functions that can manipulate search params *before* they are passed to links and navigate
329
+ // calls that match this route.
330
+ preSearchFilters?: SearchFilter<TFullSearchSchema>[]
331
+ // Filter functions that can manipulate search params *after* they are passed to links and navigate
332
+ // calls that match this route.
333
+ postSearchFilters?: SearchFilter<TFullSearchSchema>[]
334
+ // If set, preload matches of this route will be considered fresh for this many milliseconds.
335
+ preloadMaxAge?: number
336
+ // If set, a match of this route will be considered fresh for this many milliseconds.
337
+ maxAge?: number
338
+ // If set, a match of this route that becomes inactive (or unused) will be garbage collected after this many milliseconds
339
+ gcMaxAge?: number
340
+ // This async function is called before a route is loaded.
341
+ // If an error is thrown here, the route's loader will not be called.
342
+ // If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onLoadError` function.
343
+ // If thrown during a preload event, the error will be logged to the console.
344
+ beforeLoad?: (
345
+ opts: LoaderContext<
346
+ TSearchSchema,
347
+ TFullSearchSchema,
348
+ TAllParams,
349
+ NoInfer<TRouteContext>,
350
+ TContext
351
+ >,
352
+ ) => Promise<void> | void
353
+ // This function will be called if the route's loader throws an error **during an attempted navigation**.
354
+ // If you want to redirect due to an error, call `router.navigate()` from within this function.
355
+ onBeforeLoadError?: (err: any) => void
356
+ // This function will be called if the route's validateSearch option throws an error **during an attempted validation**.
357
+ // If you want to redirect due to an error, call `router.navigate()` from within this function.
358
+ // If you want to display the errorComponent, rethrow the error
359
+ onValidateSearchError?: (err: any) => void
360
+ onParseParamsError?: (err: any) => void
361
+ onLoadError?: (err: any) => void
362
+ onError?: (err: any) => void
363
+ // This function is called
364
+ // when moving from an inactive state to an active one. Likewise, when moving from
365
+ // an active to an inactive state, the return function (if provided) is called.
366
+ onLoaded?: (matchContext: {
367
+ params: TAllParams
368
+ search: TFullSearchSchema
369
+ }) =>
370
+ | void
371
+ | undefined
372
+ | ((match: { params: TAllParams; search: TFullSearchSchema }) => void)
373
+ // This function is called when the route remains active from one transition to the next.
374
+ onTransition?: (match: {
375
+ params: TAllParams
376
+ search: TFullSearchSchema
377
+ }) => void
378
+ }
379
+
380
+ export type ParseParamsOption<TPath extends string, TParams> = ParseParamsFn<
381
+ TPath,
382
+ TParams
383
+ >
384
+ // | ParseParamsObj<TPath, TParams>
385
+
386
+ export type ParseParamsFn<TPath extends string, TParams> = (
387
+ rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>,
388
+ ) => TParams extends Record<ParsePathParams<TPath>, any>
389
+ ? TParams
390
+ : 'parseParams must return an object'
391
+
392
+ export type ParseParamsObj<TPath extends string, TParams> = {
393
+ parse?: ParseParamsFn<TPath, TParams>
394
+ }
395
+
396
+ // The parse type here allows a zod schema to be passed directly to the validator
397
+ export type SearchSchemaValidator<TReturn, TParentSchema> =
398
+ | SearchSchemaValidatorObj<TReturn, TParentSchema>
399
+ | SearchSchemaValidatorFn<TReturn, TParentSchema>
400
+
401
+ export type SearchSchemaValidatorObj<TReturn, TParentSchema> = {
402
+ parse?: SearchSchemaValidatorFn<TReturn, TParentSchema>
403
+ }
404
+
405
+ export type SearchSchemaValidatorFn<TReturn, TParentSchema> = (
406
+ searchObj: Record<string, unknown>,
407
+ ) => {} extends TParentSchema
408
+ ? TReturn
409
+ : keyof TReturn extends keyof TParentSchema
410
+ ? {
411
+ error: 'Top level search params cannot be redefined by child routes!'
412
+ keys: keyof TReturn & keyof TParentSchema
413
+ }
414
+ : TReturn
415
+
416
+ export type DefinedPathParamWarning =
417
+ 'Path params cannot be redefined by child routes!'
418
+
419
+ export type ParentParams<TParentParams> = AnyPathParams extends TParentParams
420
+ ? {}
421
+ : {
422
+ [Key in keyof TParentParams]?: DefinedPathParamWarning
423
+ }
424
+
425
+ export type LoaderFn<
426
+ TLoader = unknown,
427
+ TSearchSchema extends AnySearchSchema = {},
428
+ TFullSearchSchema extends AnySearchSchema = {},
429
+ TAllParams = {},
430
+ TContext extends AnyContext = AnyContext,
431
+ TAllContext extends AnyContext = AnyContext,
432
+ > = (
433
+ match: LoaderContext<
434
+ TSearchSchema,
435
+ TFullSearchSchema,
436
+ TAllParams,
437
+ TContext,
438
+ TAllContext
439
+ > & {
440
+ parentMatchPromise?: Promise<void>
441
+ },
442
+ ) => Promise<TLoader> | TLoader
443
+
444
+ export type GetKeyFn<
445
+ TFullSearchSchema extends AnySearchSchema = {},
446
+ TAllParams = {},
447
+ > = (loaderContext: { params: TAllParams; search: TFullSearchSchema }) => any
448
+
449
+ export interface LoaderContext<
450
+ TSearchSchema extends AnySearchSchema = {},
451
+ TFullSearchSchema extends AnySearchSchema = {},
452
+ TAllParams = {},
453
+ TContext extends AnyContext = AnyContext,
454
+ TAllContext extends AnyContext = AnyContext,
455
+ > {
456
+ params: TAllParams
457
+ routeSearch: TSearchSchema
458
+ search: TFullSearchSchema
459
+ abortController: AbortController
460
+ preload: boolean
461
+ routeContext: TContext
462
+ context: TAllContext
463
+ }
464
+
465
+ export type UnloaderFn<TPath extends string> = (
466
+ routeMatch: RouteMatch<any, Route>,
467
+ ) => void
468
+
469
+ export type SearchFilter<T, U = T> = (prev: T) => U
470
+
471
+ export type ResolveId<
472
+ TParentRoute,
473
+ TCustomId extends string,
474
+ TPath extends string,
475
+ > = TParentRoute extends { id: infer TParentId extends string }
476
+ ? RoutePrefix<TParentId, string extends TCustomId ? TPath : TCustomId>
477
+ : RootRouteId
478
+
479
+ export type InferFullSearchSchema<TRoute> = TRoute extends {
480
+ isRoot: true
481
+ __types: {
482
+ searchSchema: infer TSearchSchema
483
+ }
484
+ }
485
+ ? TSearchSchema
486
+ : TRoute extends {
487
+ __types: {
488
+ fullSearchSchema: infer TFullSearchSchema
183
489
  }
184
- return router.state.loaders[id]!
185
- })()
186
-
187
- let route: Route<TAllRouteInfo, TRouteInfo> = {
188
- routeId: id,
189
- routeRouteId: routeId,
190
- routePath,
191
- fullPath,
192
- options,
193
- router,
194
- childRoutes: undefined!,
195
- parentRoute: parent,
196
- action,
197
- loader: loader as any,
198
-
199
- buildLink: (options) => {
200
- return router.buildLink({
201
- ...options,
202
- from: fullPath,
203
- } as any) as any
204
- },
205
-
206
- navigate: (options) => {
207
- return router.navigate({
208
- ...options,
209
- from: fullPath,
210
- } as any) as any
211
- },
212
-
213
- matchRoute: (matchLocation, opts) => {
214
- return router.matchRoute(
215
- {
216
- ...matchLocation,
217
- from: fullPath,
218
- } as any,
219
- opts,
490
+ }
491
+ ? TFullSearchSchema
492
+ : {}
493
+
494
+ export type ResolveFullSearchSchema<TParentRoute, TSearchSchema> =
495
+ InferFullSearchSchema<TParentRoute> & TSearchSchema
496
+
497
+ export interface AnyRoute
498
+ extends Route<
499
+ any,
500
+ any,
501
+ any,
502
+ any,
503
+ any,
504
+ any,
505
+ any,
506
+ any,
507
+ any,
508
+ any,
509
+ any,
510
+ any,
511
+ any,
512
+ any,
513
+ any,
514
+ any,
515
+ any
516
+ > {}
517
+
518
+ export type AnyRouteWithRouterContext<TRouterContext extends AnyContext> =
519
+ Route<
520
+ any,
521
+ any,
522
+ any,
523
+ any,
524
+ any,
525
+ any,
526
+ any,
527
+ any,
528
+ any,
529
+ any,
530
+ any,
531
+ any,
532
+ any,
533
+ any,
534
+ TRouterContext,
535
+ any,
536
+ any
537
+ >
538
+
539
+ export type MergeParamsFromParent<T, U> = IsAny<T, U, T & U>
540
+
541
+ export type UseLoaderResult<T> = T extends Record<PropertyKey, infer U>
542
+ ? {
543
+ [K in keyof T]: UseLoaderResultPromise<T[K]>
544
+ }
545
+ : UseLoaderResultPromise<T>
546
+
547
+ export type UseLoaderResultPromise<T> = T extends Promise<infer U>
548
+ ? StreamedPromise<U>
549
+ : T
550
+
551
+ export type StreamedPromise<T> = {
552
+ promise: Promise<T>
553
+ status: 'resolved' | 'pending'
554
+ data: T
555
+ resolve: (value: T) => void
556
+ }
557
+
558
+ export class Route<
559
+ TParentRoute extends AnyRoute = AnyRoute,
560
+ TPath extends string = '/',
561
+ TFullPath extends ResolveFullPath<TParentRoute, TPath> = ResolveFullPath<
562
+ TParentRoute,
563
+ TPath
564
+ >,
565
+ TCustomId extends string = string,
566
+ TId extends ResolveId<TParentRoute, TCustomId, TPath> = ResolveId<
567
+ TParentRoute,
568
+ TCustomId,
569
+ TPath
570
+ >,
571
+ TLoader = unknown,
572
+ TSearchSchema extends AnySearchSchema = {},
573
+ TFullSearchSchema extends AnySearchSchema = ResolveFullSearchSchema<
574
+ TParentRoute,
575
+ TSearchSchema
576
+ >,
577
+ TParams extends Record<ParsePathParams<TPath>, any> = Record<
578
+ ParsePathParams<TPath>,
579
+ string
580
+ >,
581
+ TAllParams extends MergeParamsFromParent<
582
+ TParentRoute['__types']['allParams'],
583
+ TParams
584
+ > = MergeParamsFromParent<TParentRoute['__types']['allParams'], TParams>,
585
+ TParentContext extends TParentRoute['__types']['routeContext'] = TParentRoute['__types']['routeContext'],
586
+ TAllParentContext extends TParentRoute['__types']['context'] = TParentRoute['__types']['context'],
587
+ TRouteContext extends RouteContext = RouteContext,
588
+ TContext extends MergeParamsFromParent<
589
+ TParentRoute['__types']['context'],
590
+ TRouteContext
591
+ > = MergeParamsFromParent<TParentRoute['__types']['context'], TRouteContext>,
592
+ TRouterContext extends AnyContext = AnyContext,
593
+ TChildren extends unknown = unknown,
594
+ TRoutesInfo extends DefaultRoutesInfo = DefaultRoutesInfo,
595
+ > {
596
+ __types!: {
597
+ parentRoute: TParentRoute
598
+ path: TPath
599
+ to: TrimPathRight<TFullPath>
600
+ fullPath: TFullPath
601
+ customId: TCustomId
602
+ id: TId
603
+ loader: TLoader
604
+ searchSchema: TSearchSchema
605
+ fullSearchSchema: TFullSearchSchema
606
+ params: TParams
607
+ allParams: TAllParams
608
+ parentContext: TParentContext
609
+ allParentContext: TAllParentContext
610
+ routeContext: TRouteContext
611
+ context: TContext
612
+ children: TChildren
613
+ routesInfo: TRoutesInfo
614
+ routerContext: TRouterContext
615
+ }
616
+ isRoot: TParentRoute extends Route<any> ? true : false
617
+ options: RouteOptions<
618
+ TParentRoute,
619
+ TCustomId,
620
+ TPath,
621
+ TLoader,
622
+ InferFullSearchSchema<TParentRoute>,
623
+ TSearchSchema,
624
+ TFullSearchSchema,
625
+ TParentRoute['__types']['allParams'],
626
+ TParams,
627
+ TAllParams,
628
+ TParentContext,
629
+ TAllParentContext,
630
+ TRouteContext,
631
+ TContext
632
+ > &
633
+ UpdatableRouteOptions<
634
+ TLoader,
635
+ TSearchSchema,
636
+ TFullSearchSchema,
637
+ TAllParams,
638
+ TRouteContext,
639
+ TContext
640
+ >
641
+
642
+ // Set up in this.init()
643
+ parentRoute!: TParentRoute
644
+ id!: TId
645
+ // customId!: TCustomId
646
+ path!: TPath
647
+ fullPath!: TFullPath
648
+ to!: TrimPathRight<TFullPath>
649
+
650
+ // Optional
651
+ children?: TChildren
652
+ originalIndex?: number
653
+ router?: Router<TRoutesInfo['routeTree'], TRoutesInfo>
654
+ rank!: number
655
+
656
+ constructor(
657
+ options: RouteOptions<
658
+ TParentRoute,
659
+ TCustomId,
660
+ TPath,
661
+ TLoader,
662
+ InferFullSearchSchema<TParentRoute>,
663
+ TSearchSchema,
664
+ TFullSearchSchema,
665
+ TParentRoute['__types']['allParams'],
666
+ TParams,
667
+ TAllParams,
668
+ TParentContext,
669
+ TAllParentContext,
670
+ TRouteContext,
671
+ TContext
672
+ > &
673
+ UpdatableRouteOptions<
674
+ TLoader,
675
+ TSearchSchema,
676
+ TFullSearchSchema,
677
+ TAllParams,
678
+ TRouteContext,
679
+ TContext
680
+ >,
681
+ ) {
682
+ this.options = (options as any) || {}
683
+ this.isRoot = !options?.getParentRoute as any
684
+ Route.__onInit(this as any)
685
+ }
686
+
687
+ init = (opts: { originalIndex: number; router: AnyRouter }) => {
688
+ this.originalIndex = opts.originalIndex
689
+ this.router = opts.router
690
+
691
+ const options = this.options as RouteOptions<
692
+ TParentRoute,
693
+ TCustomId,
694
+ TPath,
695
+ InferFullSearchSchema<TParentRoute>,
696
+ TSearchSchema,
697
+ TParentRoute['__types']['allParams'],
698
+ TParams
699
+ > &
700
+ RoutePathOptionsIntersection<TCustomId, TPath>
701
+
702
+ const isRoot = !options?.path && !options?.id
703
+
704
+ this.parentRoute = this.options?.getParentRoute?.()
705
+
706
+ if (isRoot) {
707
+ this.path = rootRouteId as TPath
708
+ } else {
709
+ invariant(
710
+ this.parentRoute,
711
+ `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`,
220
712
  )
221
- },
713
+ }
714
+
715
+ let path: undefined | string = isRoot ? rootRouteId : options.path
716
+
717
+ // If the path is anything other than an index path, trim it up
718
+ if (path && path !== '/') {
719
+ path = trimPath(path)
720
+ }
721
+
722
+ const customId = options?.id || path
723
+
724
+ // Strip the parentId prefix from the first level of children
725
+ let id = isRoot
726
+ ? rootRouteId
727
+ : joinPaths([
728
+ (this.parentRoute.id as any) === rootRouteId
729
+ ? ''
730
+ : this.parentRoute.id,
731
+ customId,
732
+ ])
733
+
734
+ if (path === rootRouteId) {
735
+ path = '/'
736
+ }
737
+
738
+ if (id !== rootRouteId) {
739
+ id = joinPaths(['/', id])
740
+ }
741
+
742
+ const fullPath =
743
+ id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path])
744
+
745
+ this.path = path as TPath
746
+ this.id = id as TId
747
+ // this.customId = customId as TCustomId
748
+ this.fullPath = fullPath as TFullPath
749
+ this.to = fullPath as TrimPathRight<TFullPath>
222
750
  }
223
751
 
224
- router.options.createRoute?.({ router, route })
752
+ addChildren = <TNewChildren extends AnyRoute[]>(
753
+ children: TNewChildren,
754
+ ): Route<
755
+ TParentRoute,
756
+ TPath,
757
+ TFullPath,
758
+ TCustomId,
759
+ TId,
760
+ TLoader,
761
+ TSearchSchema,
762
+ TFullSearchSchema,
763
+ TParams,
764
+ TAllParams,
765
+ TParentContext,
766
+ TAllParentContext,
767
+ TRouteContext,
768
+ TContext,
769
+ TRouterContext,
770
+ TNewChildren,
771
+ TRoutesInfo
772
+ > => {
773
+ this.children = children as any
774
+ return this as any
775
+ }
225
776
 
226
- return route
777
+ update = (
778
+ options: UpdatableRouteOptions<
779
+ TLoader,
780
+ TSearchSchema,
781
+ TFullSearchSchema,
782
+ TAllParams,
783
+ TRouteContext,
784
+ TContext
785
+ >,
786
+ ) => {
787
+ Object.assign(this.options, options)
788
+ return this
789
+ }
790
+
791
+ static __onInit = (route: typeof this) => {
792
+ // This is a dummy static method that should get
793
+ // replaced by a framework specific implementation if necessary
794
+ }
227
795
  }
796
+
797
+ export type AnyRootRoute = RootRoute<any, any, any, any>
798
+
799
+ export class RouterContext<TRouterContext extends {}> {
800
+ constructor() {}
801
+
802
+ createRootRoute = <
803
+ TLoader = unknown,
804
+ TSearchSchema extends AnySearchSchema = {},
805
+ TContext extends RouteContext = RouteContext,
806
+ >(
807
+ options?: Omit<
808
+ RouteOptions<AnyRoute, RootRouteId, '', {}, TSearchSchema, {}, {}>,
809
+ | 'path'
810
+ | 'id'
811
+ | 'getParentRoute'
812
+ | 'caseSensitive'
813
+ | 'parseParams'
814
+ | 'stringifyParams'
815
+ >,
816
+ ) => {
817
+ return new RootRoute<TLoader, TSearchSchema, TContext, TRouterContext>(
818
+ options as any,
819
+ )
820
+ }
821
+ }
822
+
823
+ export class RootRoute<
824
+ TLoader = unknown,
825
+ TSearchSchema extends AnySearchSchema = {},
826
+ TContext extends RouteContext = RouteContext,
827
+ TRouterContext extends {} = {},
828
+ > extends Route<
829
+ any,
830
+ '/',
831
+ '/',
832
+ string,
833
+ RootRouteId,
834
+ TLoader,
835
+ TSearchSchema,
836
+ TSearchSchema,
837
+ {},
838
+ {},
839
+ TRouterContext,
840
+ TRouterContext,
841
+ MergeParamsFromParent<TRouterContext, TContext>,
842
+ MergeParamsFromParent<TRouterContext, TContext>,
843
+ TRouterContext,
844
+ any,
845
+ any
846
+ > {
847
+ constructor(
848
+ options?: Omit<
849
+ RouteOptions<AnyRoute, RootRouteId, '', TLoader, TSearchSchema, {}, {}>,
850
+ | 'path'
851
+ | 'id'
852
+ | 'getParentRoute'
853
+ | 'caseSensitive'
854
+ | 'parseParams'
855
+ | 'stringifyParams'
856
+ >,
857
+ ) {
858
+ super(options as any)
859
+ }
860
+ }
861
+
862
+ export type ResolveFullPath<
863
+ TParentRoute extends AnyRoute,
864
+ TPath extends string,
865
+ TPrefixed extends RoutePrefix<TParentRoute['fullPath'], TPath> = RoutePrefix<
866
+ TParentRoute['fullPath'],
867
+ TPath
868
+ >,
869
+ > = TPrefixed extends RootRouteId ? '/' : TPrefixed
870
+
871
+ type RoutePrefix<
872
+ TPrefix extends string,
873
+ TPath extends string,
874
+ > = string extends TPath
875
+ ? RootRouteId
876
+ : TPath extends string
877
+ ? TPrefix extends RootRouteId
878
+ ? TPath extends '/'
879
+ ? '/'
880
+ : `/${TrimPath<TPath>}`
881
+ : `${TPrefix}/${TPath}` extends '/'
882
+ ? '/'
883
+ : `/${TrimPathLeft<`${TrimPathRight<TPrefix>}/${TrimPath<TPath>}`>}`
884
+ : never
885
+
886
+ export type TrimPath<T extends string> = '' extends T
887
+ ? ''
888
+ : TrimPathRight<TrimPathLeft<T>>
889
+
890
+ export type TrimPathLeft<T extends string> =
891
+ T extends `${RootRouteId}/${infer U}`
892
+ ? TrimPathLeft<U>
893
+ : T extends `/${infer U}`
894
+ ? TrimPathLeft<U>
895
+ : T
896
+ export type TrimPathRight<T extends string> = T extends '/'
897
+ ? '/'
898
+ : T extends `${infer U}/`
899
+ ? TrimPathRight<U>
900
+ : T