@tanstack/router-core 0.0.1-beta.16 → 0.0.1-beta.161

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