@tanstack/router-core 0.0.1-beta.14 → 0.0.1-beta.145

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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/history.js +226 -0
  3. package/build/cjs/history.js.map +1 -0
  4. package/build/cjs/{packages/router-core/src/index.js → index.js} +33 -15
  5. package/build/cjs/{packages/router-core/src/index.js.map → index.js.map} +1 -1
  6. package/build/cjs/{packages/router-core/src/path.js → path.js} +45 -56
  7. package/build/cjs/path.js.map +1 -0
  8. package/build/cjs/{packages/router-core/src/qss.js → qss.js} +10 -16
  9. package/build/cjs/qss.js.map +1 -0
  10. package/build/cjs/route.js +147 -0
  11. package/build/cjs/route.js.map +1 -0
  12. package/build/cjs/router.js +1102 -0
  13. package/build/cjs/router.js.map +1 -0
  14. package/build/cjs/{packages/router-core/src/searchParams.js → searchParams.js} +11 -13
  15. package/build/cjs/searchParams.js.map +1 -0
  16. package/build/cjs/{packages/router-core/src/utils.js → utils.js} +54 -64
  17. package/build/cjs/utils.js.map +1 -0
  18. package/build/esm/index.js +1439 -2099
  19. package/build/esm/index.js.map +1 -1
  20. package/build/stats-html.html +59 -49
  21. package/build/stats-react.json +186 -249
  22. package/build/types/index.d.ts +559 -422
  23. package/build/umd/index.development.js +1675 -2232
  24. package/build/umd/index.development.js.map +1 -1
  25. package/build/umd/index.production.js +12 -2
  26. package/build/umd/index.production.js.map +1 -1
  27. package/package.json +11 -7
  28. package/src/history.ts +292 -0
  29. package/src/index.ts +2 -10
  30. package/src/link.ts +116 -113
  31. package/src/path.ts +37 -17
  32. package/src/qss.ts +1 -2
  33. package/src/route.ts +927 -218
  34. package/src/routeInfo.ts +121 -197
  35. package/src/router.ts +1481 -1018
  36. package/src/searchParams.ts +1 -1
  37. package/src/utils.ts +80 -49
  38. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
  39. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  40. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
  41. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
  42. package/build/cjs/node_modules/history/index.js +0 -815
  43. package/build/cjs/node_modules/history/index.js.map +0 -1
  44. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
  45. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
  46. package/build/cjs/packages/router-core/src/path.js.map +0 -1
  47. package/build/cjs/packages/router-core/src/qss.js.map +0 -1
  48. package/build/cjs/packages/router-core/src/route.js +0 -147
  49. package/build/cjs/packages/router-core/src/route.js.map +0 -1
  50. package/build/cjs/packages/router-core/src/routeConfig.js +0 -69
  51. package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
  52. package/build/cjs/packages/router-core/src/routeMatch.js +0 -226
  53. package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
  54. package/build/cjs/packages/router-core/src/router.js +0 -832
  55. package/build/cjs/packages/router-core/src/router.js.map +0 -1
  56. package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
  57. package/build/cjs/packages/router-core/src/utils.js.map +0 -1
  58. package/src/frameworks.ts +0 -11
  59. package/src/routeConfig.ts +0 -489
  60. package/src/routeMatch.ts +0 -312
package/src/route.ts CHANGED
@@ -1,227 +1,936 @@
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
81
20
  }
82
21
 
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
- },
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
128
+ }
129
+
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>
144
245
  }
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,
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
288
+ }
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?: RegisterRouteErrorComponent<
314
+ RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
315
+ > //
316
+ // If supported by your framework, the content to be rendered as the fallback content until the route is ready to render
317
+ pendingComponent?: RegisteredRouteComponent<
318
+ RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
319
+ >
320
+ // Filter functions that can manipulate search params *before* they are passed to links and navigate
321
+ // calls that match this route.
322
+ preSearchFilters?: SearchFilter<TFullSearchSchema>[]
323
+ // Filter functions that can manipulate search params *after* they are passed to links and navigate
324
+ // calls that match this route.
325
+ postSearchFilters?: SearchFilter<TFullSearchSchema>[]
326
+ // If set, preload matches of this route will be considered fresh for this many milliseconds.
327
+ preloadMaxAge?: number
328
+ // If set, a match of this route will be considered fresh for this many milliseconds.
329
+ maxAge?: number
330
+ // If set, a match of this route that becomes inactive (or unused) will be garbage collected after this many milliseconds
331
+ gcMaxAge?: number
332
+ // This async function is called before a route is loaded.
333
+ // If an error is thrown here, the route's loader will not be called.
334
+ // If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onLoadError` function.
335
+ // If thrown during a preload event, the error will be logged to the console.
336
+ beforeLoad?: (
337
+ opts: LoaderContext<
338
+ TSearchSchema,
339
+ TFullSearchSchema,
340
+ TAllParams,
341
+ NoInfer<TRouteContext>,
342
+ TContext
343
+ >,
344
+ ) => Promise<void> | void
345
+ // This function will be called if the route's loader throws an error **during an attempted navigation**.
346
+ // If you want to redirect due to an error, call `router.navigate()` from within this function.
347
+ onBeforeLoadError?: (err: any) => void
348
+ // This function will be called if the route's validateSearch option throws an error **during an attempted validation**.
349
+ // If you want to redirect due to an error, call `router.navigate()` from within this function.
350
+ // If you want to display the errorComponent, rethrow the error
351
+ onValidateSearchError?: (err: any) => void
352
+ onParseParamsError?: (err: any) => void
353
+ onLoadError?: (err: any) => void
354
+ onError?: (err: any) => void
355
+ // This function is called
356
+ // when moving from an inactive state to an active one. Likewise, when moving from
357
+ // an active to an inactive state, the return function (if provided) is called.
358
+ onLoaded?: (matchContext: {
359
+ params: TAllParams
360
+ search: TFullSearchSchema
361
+ }) =>
362
+ | void
363
+ | undefined
364
+ | ((match: { params: TAllParams; search: TFullSearchSchema }) => void)
365
+ // This function is called when the route remains active from one transition to the next.
366
+ onTransition?: (match: {
367
+ params: TAllParams
368
+ search: TFullSearchSchema
369
+ }) => void
370
+ }
371
+
372
+ export type ParseParamsOption<TPath extends string, TParams> = ParseParamsFn<
373
+ TPath,
374
+ TParams
375
+ >
376
+ // | ParseParamsObj<TPath, TParams>
377
+
378
+ export type ParseParamsFn<TPath extends string, TParams> = (
379
+ rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>,
380
+ ) => TParams extends Record<ParsePathParams<TPath>, any>
381
+ ? TParams
382
+ : 'parseParams must return an object'
383
+
384
+ export type ParseParamsObj<TPath extends string, TParams> = {
385
+ parse?: ParseParamsFn<TPath, TParams>
386
+ }
387
+
388
+ // The parse type here allows a zod schema to be passed directly to the validator
389
+ export type SearchSchemaValidator<TReturn, TParentSchema> =
390
+ | SearchSchemaValidatorObj<TReturn, TParentSchema>
391
+ | SearchSchemaValidatorFn<TReturn, TParentSchema>
392
+
393
+ export type SearchSchemaValidatorObj<TReturn, TParentSchema> = {
394
+ parse?: SearchSchemaValidatorFn<TReturn, TParentSchema>
395
+ }
396
+
397
+ export type SearchSchemaValidatorFn<TReturn, TParentSchema> = (
398
+ searchObj: Record<string, unknown>,
399
+ ) => {} extends TParentSchema
400
+ ? TReturn
401
+ : keyof TReturn extends keyof TParentSchema
402
+ ? {
403
+ error: 'Top level search params cannot be redefined by child routes!'
404
+ keys: keyof TReturn & keyof TParentSchema
405
+ }
406
+ : TReturn
407
+
408
+ export type DefinedPathParamWarning =
409
+ 'Path params cannot be redefined by child routes!'
410
+
411
+ export type ParentParams<TParentParams> = AnyPathParams extends TParentParams
412
+ ? {}
413
+ : {
414
+ [Key in keyof TParentParams]?: DefinedPathParamWarning
415
+ }
416
+
417
+ export type LoaderFn<
418
+ TLoader = unknown,
419
+ TSearchSchema extends AnySearchSchema = {},
420
+ TFullSearchSchema extends AnySearchSchema = {},
421
+ TAllParams = {},
422
+ TContext extends AnyContext = AnyContext,
423
+ TAllContext extends AnyContext = AnyContext,
424
+ > = (
425
+ match: LoaderContext<
426
+ TSearchSchema,
427
+ TFullSearchSchema,
428
+ TAllParams,
429
+ TContext,
430
+ TAllContext
431
+ > & {
432
+ parentMatchPromise?: Promise<void>
433
+ },
434
+ ) => Promise<TLoader> | TLoader
435
+
436
+ export type GetKeyFn<
437
+ TFullSearchSchema extends AnySearchSchema = {},
438
+ TAllParams = {},
439
+ > = (loaderContext: { params: TAllParams; search: TFullSearchSchema }) => any
440
+
441
+ export interface LoaderContext<
442
+ TSearchSchema extends AnySearchSchema = {},
443
+ TFullSearchSchema extends AnySearchSchema = {},
444
+ TAllParams = {},
445
+ TContext extends AnyContext = AnyContext,
446
+ TAllContext extends AnyContext = AnyContext,
447
+ > {
448
+ params: TAllParams
449
+ routeSearch: TSearchSchema
450
+ search: TFullSearchSchema
451
+ abortController: AbortController
452
+ preload: boolean
453
+ routeContext: TContext
454
+ context: TAllContext
455
+ }
456
+
457
+ export type UnloaderFn<TPath extends string> = (
458
+ routeMatch: RouteMatch<any, Route>,
459
+ ) => void
460
+
461
+ export type SearchFilter<T, U = T> = (prev: T) => U
462
+
463
+ export type ResolveId<
464
+ TParentRoute,
465
+ TCustomId extends string,
466
+ TPath extends string,
467
+ > = TParentRoute extends { id: infer TParentId extends string }
468
+ ? RoutePrefix<TParentId, string extends TCustomId ? TPath : TCustomId>
469
+ : RootRouteId
470
+
471
+ export type InferFullSearchSchema<TRoute> = TRoute extends {
472
+ isRoot: true
473
+ __types: {
474
+ searchSchema: infer TSearchSchema
475
+ }
476
+ }
477
+ ? TSearchSchema
478
+ : TRoute extends {
479
+ __types: {
480
+ fullSearchSchema: infer TFullSearchSchema
183
481
  }
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,
482
+ }
483
+ ? TFullSearchSchema
484
+ : {}
485
+
486
+ export type ResolveFullSearchSchema<TParentRoute, TSearchSchema> =
487
+ InferFullSearchSchema<TParentRoute> & TSearchSchema
488
+
489
+ export interface AnyRoute
490
+ extends Route<
491
+ any,
492
+ any,
493
+ any,
494
+ any,
495
+ any,
496
+ any,
497
+ any,
498
+ any,
499
+ any,
500
+ any,
501
+ any,
502
+ any,
503
+ any,
504
+ any,
505
+ any,
506
+ any,
507
+ any
508
+ > {}
509
+
510
+ export type AnyRouteWithRouterContext<TRouterContext extends AnyContext> =
511
+ Route<
512
+ any,
513
+ any,
514
+ any,
515
+ any,
516
+ any,
517
+ any,
518
+ any,
519
+ any,
520
+ any,
521
+ any,
522
+ any,
523
+ any,
524
+ any,
525
+ any,
526
+ TRouterContext,
527
+ any,
528
+ any
529
+ >
530
+
531
+ export type MergeParamsFromParent<T, U> = IsAny<T, U, T & U>
532
+
533
+ export type UseLoaderResult<T> = T extends Record<PropertyKey, infer U>
534
+ ? {
535
+ [K in keyof T]: UseLoaderResultPromise<T[K]>
536
+ }
537
+ : UseLoaderResultPromise<T>
538
+
539
+ export type UseLoaderResultPromise<T> = T extends Promise<infer U>
540
+ ? StreamedPromise<U>
541
+ : T
542
+
543
+ export type StreamedPromise<T> = {
544
+ promise: Promise<T>
545
+ status: 'resolved' | 'pending'
546
+ data: T
547
+ resolve: (value: T) => void
548
+ }
549
+
550
+ export class Route<
551
+ TParentRoute extends AnyRoute = AnyRoute,
552
+ TPath extends string = '/',
553
+ TFullPath extends ResolveFullPath<TParentRoute, TPath> = ResolveFullPath<
554
+ TParentRoute,
555
+ TPath
556
+ >,
557
+ TCustomId extends string = string,
558
+ TId extends ResolveId<TParentRoute, TCustomId, TPath> = ResolveId<
559
+ TParentRoute,
560
+ TCustomId,
561
+ TPath
562
+ >,
563
+ TLoader = unknown,
564
+ TSearchSchema extends AnySearchSchema = {},
565
+ TFullSearchSchema extends AnySearchSchema = ResolveFullSearchSchema<
566
+ TParentRoute,
567
+ TSearchSchema
568
+ >,
569
+ TParams extends Record<ParsePathParams<TPath>, any> = Record<
570
+ ParsePathParams<TPath>,
571
+ string
572
+ >,
573
+ TAllParams extends MergeParamsFromParent<
574
+ TParentRoute['__types']['allParams'],
575
+ TParams
576
+ > = MergeParamsFromParent<TParentRoute['__types']['allParams'], TParams>,
577
+ TParentContext extends TParentRoute['__types']['routeContext'] = TParentRoute['__types']['routeContext'],
578
+ TAllParentContext extends TParentRoute['__types']['context'] = TParentRoute['__types']['context'],
579
+ TRouteContext extends RouteContext = RouteContext,
580
+ TContext extends MergeParamsFromParent<
581
+ TParentRoute['__types']['context'],
582
+ TRouteContext
583
+ > = MergeParamsFromParent<TParentRoute['__types']['context'], TRouteContext>,
584
+ TRouterContext extends AnyContext = AnyContext,
585
+ TChildren extends unknown = unknown,
586
+ TRoutesInfo extends DefaultRoutesInfo = DefaultRoutesInfo,
587
+ > {
588
+ __types!: {
589
+ parentRoute: TParentRoute
590
+ path: TPath
591
+ to: TrimPathRight<TFullPath>
592
+ fullPath: TFullPath
593
+ customId: TCustomId
594
+ id: TId
595
+ loader: TLoader
596
+ searchSchema: TSearchSchema
597
+ fullSearchSchema: TFullSearchSchema
598
+ params: TParams
599
+ allParams: TAllParams
600
+ parentContext: TParentContext
601
+ allParentContext: TAllParentContext
602
+ routeContext: TRouteContext
603
+ context: TContext
604
+ children: TChildren
605
+ routesInfo: TRoutesInfo
606
+ routerContext: TRouterContext
607
+ }
608
+ isRoot: TParentRoute extends Route<any> ? true : false
609
+ options: RouteOptions<
610
+ TParentRoute,
611
+ TCustomId,
612
+ TPath,
613
+ TLoader,
614
+ InferFullSearchSchema<TParentRoute>,
615
+ TSearchSchema,
616
+ TFullSearchSchema,
617
+ TParentRoute['__types']['allParams'],
618
+ TParams,
619
+ TAllParams,
620
+ TParentContext,
621
+ TAllParentContext,
622
+ TRouteContext,
623
+ TContext
624
+ > &
625
+ UpdatableRouteOptions<
626
+ TLoader,
627
+ TSearchSchema,
628
+ TFullSearchSchema,
629
+ TAllParams,
630
+ TRouteContext,
631
+ TContext
632
+ >
633
+
634
+ // Set up in this.init()
635
+ parentRoute!: TParentRoute
636
+ id!: TId
637
+ // customId!: TCustomId
638
+ path!: TPath
639
+ fullPath!: TFullPath
640
+ to!: TrimPathRight<TFullPath>
641
+
642
+ // Optional
643
+ children?: TChildren
644
+ originalIndex?: number
645
+ router?: Router<TRoutesInfo['routeTree'], TRoutesInfo>
646
+ rank!: number
647
+
648
+ constructor(
649
+ options: RouteOptions<
650
+ TParentRoute,
651
+ TCustomId,
652
+ TPath,
653
+ TLoader,
654
+ InferFullSearchSchema<TParentRoute>,
655
+ TSearchSchema,
656
+ TFullSearchSchema,
657
+ TParentRoute['__types']['allParams'],
658
+ TParams,
659
+ TAllParams,
660
+ TParentContext,
661
+ TAllParentContext,
662
+ TRouteContext,
663
+ TContext
664
+ > &
665
+ UpdatableRouteOptions<
666
+ TLoader,
667
+ TSearchSchema,
668
+ TFullSearchSchema,
669
+ TAllParams,
670
+ TRouteContext,
671
+ TContext
672
+ >,
673
+ ) {
674
+ this.options = (options as any) || {}
675
+ this.isRoot = !options?.getParentRoute as any
676
+ Route.__onInit(this as any)
677
+ }
678
+
679
+ init = (opts: { originalIndex: number; router: AnyRouter }) => {
680
+ this.originalIndex = opts.originalIndex
681
+ this.router = opts.router
682
+
683
+ const options = this.options as RouteOptions<
684
+ TParentRoute,
685
+ TCustomId,
686
+ TPath,
687
+ InferFullSearchSchema<TParentRoute>,
688
+ TSearchSchema,
689
+ TParentRoute['__types']['allParams'],
690
+ TParams
691
+ > &
692
+ RoutePathOptionsIntersection<TCustomId, TPath>
693
+
694
+ const isRoot = !options?.path && !options?.id
695
+
696
+ this.parentRoute = this.options?.getParentRoute?.()
697
+
698
+ if (isRoot) {
699
+ this.path = rootRouteId as TPath
700
+ } else {
701
+ invariant(
702
+ this.parentRoute,
703
+ `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`,
220
704
  )
221
- },
705
+ }
706
+
707
+ let path: undefined | string = isRoot ? rootRouteId : options.path
708
+
709
+ // If the path is anything other than an index path, trim it up
710
+ if (path && path !== '/') {
711
+ path = trimPath(path)
712
+ }
713
+
714
+ const customId = options?.id || path
715
+
716
+ // Strip the parentId prefix from the first level of children
717
+ let id = isRoot
718
+ ? rootRouteId
719
+ : joinPaths([
720
+ (this.parentRoute.id as any) === rootRouteId
721
+ ? ''
722
+ : this.parentRoute.id,
723
+ customId,
724
+ ])
725
+
726
+ if (path === rootRouteId) {
727
+ path = '/'
728
+ }
729
+
730
+ if (id !== rootRouteId) {
731
+ id = joinPaths(['/', id])
732
+ }
733
+
734
+ const fullPath =
735
+ id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path])
736
+
737
+ this.path = path as TPath
738
+ this.id = id as TId
739
+ // this.customId = customId as TCustomId
740
+ this.fullPath = fullPath as TFullPath
741
+ this.to = fullPath as TrimPathRight<TFullPath>
742
+ }
743
+
744
+ addChildren = <TNewChildren extends AnyRoute[]>(
745
+ children: TNewChildren,
746
+ ): Route<
747
+ TParentRoute,
748
+ TPath,
749
+ TFullPath,
750
+ TCustomId,
751
+ TId,
752
+ TLoader,
753
+ TSearchSchema,
754
+ TFullSearchSchema,
755
+ TParams,
756
+ TAllParams,
757
+ TParentContext,
758
+ TAllParentContext,
759
+ TRouteContext,
760
+ TContext,
761
+ TRouterContext,
762
+ TNewChildren,
763
+ TRoutesInfo
764
+ > => {
765
+ this.children = children as any
766
+ return this as any
222
767
  }
223
768
 
224
- router.options.createRoute?.({ router, route })
769
+ update = (
770
+ options: UpdatableRouteOptions<
771
+ TLoader,
772
+ TSearchSchema,
773
+ TFullSearchSchema,
774
+ TAllParams,
775
+ TRouteContext,
776
+ TContext
777
+ >,
778
+ ) => {
779
+ Object.assign(this.options, options)
780
+ return this
781
+ }
782
+
783
+ static __onInit = (route: typeof this) => {
784
+ // This is a dummy static method that should get
785
+ // replaced by a framework specific implementation if necessary
786
+ }
787
+ }
788
+
789
+ export type AnyRootRoute = RootRoute<any, any, any, any>
790
+
791
+ export class RouterContext<TRouterContext extends {}> {
792
+ constructor() {}
225
793
 
226
- return route
794
+ createRootRoute = <
795
+ TLoader = unknown,
796
+ TSearchSchema extends AnySearchSchema = {},
797
+ TContext extends RouteContext = RouteContext,
798
+ >(
799
+ options?: Omit<
800
+ RouteOptions<AnyRoute, RootRouteId, '', {}, TSearchSchema, {}, {}>,
801
+ | 'path'
802
+ | 'id'
803
+ | 'getParentRoute'
804
+ | 'caseSensitive'
805
+ | 'parseParams'
806
+ | 'stringifyParams'
807
+ >,
808
+ ) => {
809
+ return new RootRoute<TLoader, TSearchSchema, TContext, TRouterContext>(
810
+ options,
811
+ )
812
+ }
227
813
  }
814
+
815
+ export class RootRoute<
816
+ TLoader = unknown,
817
+ TSearchSchema extends AnySearchSchema = {},
818
+ TContext extends RouteContext = RouteContext,
819
+ TRouterContext extends {} = {},
820
+ > extends Route<
821
+ any,
822
+ '/',
823
+ '/',
824
+ string,
825
+ RootRouteId,
826
+ TLoader,
827
+ TSearchSchema,
828
+ TSearchSchema,
829
+ {},
830
+ {},
831
+ TRouterContext,
832
+ TRouterContext,
833
+ MergeParamsFromParent<TRouterContext, TContext>,
834
+ MergeParamsFromParent<TRouterContext, TContext>,
835
+ TRouterContext,
836
+ any,
837
+ any
838
+ > {
839
+ constructor(
840
+ options?: Omit<
841
+ RouteOptions<AnyRoute, RootRouteId, '', {}, TSearchSchema, {}, {}>,
842
+ | 'path'
843
+ | 'id'
844
+ | 'getParentRoute'
845
+ | 'caseSensitive'
846
+ | 'parseParams'
847
+ | 'stringifyParams'
848
+ >,
849
+ ) {
850
+ super(options as any)
851
+ }
852
+ }
853
+
854
+ export type ResolveFullPath<
855
+ TParentRoute extends AnyRoute,
856
+ TPath extends string,
857
+ TPrefixed extends RoutePrefix<TParentRoute['fullPath'], TPath> = RoutePrefix<
858
+ TParentRoute['fullPath'],
859
+ TPath
860
+ >,
861
+ > = TPrefixed extends RootRouteId ? '/' : TPrefixed
862
+
863
+ type RoutePrefix<
864
+ TPrefix extends string,
865
+ TPath extends string,
866
+ > = string extends TPath
867
+ ? RootRouteId
868
+ : TPath extends string
869
+ ? TPrefix extends RootRouteId
870
+ ? TPath extends '/'
871
+ ? '/'
872
+ : `/${TrimPath<TPath>}`
873
+ : `${TPrefix}/${TPath}` extends '/'
874
+ ? '/'
875
+ : `/${TrimPathLeft<`${TrimPathRight<TPrefix>}/${TrimPath<TPath>}`>}`
876
+ : never
877
+
878
+ export type TrimPath<T extends string> = '' extends T
879
+ ? ''
880
+ : TrimPathRight<TrimPathLeft<T>>
881
+
882
+ export type TrimPathLeft<T extends string> =
883
+ T extends `${RootRouteId}/${infer U}`
884
+ ? TrimPathLeft<U>
885
+ : T extends `/${infer U}`
886
+ ? TrimPathLeft<U>
887
+ : T
888
+ export type TrimPathRight<T extends string> = T extends '/'
889
+ ? '/'
890
+ : T extends `${infer U}/`
891
+ ? TrimPathRight<U>
892
+ : T
893
+
894
+ // const rootRoute = new RootRoute({
895
+ // validateSearch: () => null as unknown as { root?: boolean },
896
+ // })
897
+
898
+ // const aRoute = new Route({
899
+ // getParentRoute: () => rootRoute,
900
+ // path: 'a',
901
+ // validateSearch: () => null as unknown as { a?: string },
902
+ // })
903
+
904
+ // const bRoute = new Route({
905
+ // getParentRoute: () => aRoute,
906
+ // path: 'b',
907
+ // })
908
+
909
+ // const rootIsRoot = rootRoute.isRoot
910
+ // // ^?
911
+ // const aIsRoot = aRoute.isRoot
912
+ // // ^?
913
+
914
+ // const rId = rootRoute.id
915
+ // // ^?
916
+ // const aId = aRoute.id
917
+ // // ^?
918
+ // const bId = bRoute.id
919
+ // // ^?
920
+
921
+ // const rPath = rootRoute.fullPath
922
+ // // ^?
923
+ // const aPath = aRoute.fullPath
924
+ // // ^?
925
+ // const bPath = bRoute.fullPath
926
+ // // ^?
927
+
928
+ // const rSearch = rootRoute.__types.fullSearchSchema
929
+ // // ^?
930
+ // const aSearch = aRoute.__types.fullSearchSchema
931
+ // // ^?
932
+ // const bSearch = bRoute.__types.fullSearchSchema
933
+ // // ^?
934
+
935
+ // const config = rootRoute.addChildren([aRoute.addChildren([bRoute])])
936
+ // // ^?