@tanstack/router-core 0.0.1-beta.11 → 0.0.1-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/packages/router-core/src/routeConfig.js.map +1 -1
- package/build/cjs/packages/router-core/src/routeMatch.js +57 -94
- package/build/cjs/packages/router-core/src/routeMatch.js.map +1 -1
- package/build/cjs/packages/router-core/src/router.js +17 -15
- package/build/cjs/packages/router-core/src/router.js.map +1 -1
- package/build/esm/index.js +76 -111
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +131 -138
- package/build/types/index.d.ts +54 -72
- package/build/umd/index.development.js +76 -111
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +1 -1
- package/src/frameworks.ts +1 -2
- package/src/index.ts +0 -1
- package/src/routeConfig.ts +7 -13
- package/src/routeMatch.ts +72 -130
- package/src/router.ts +26 -26
package/package.json
CHANGED
package/src/frameworks.ts
CHANGED
|
@@ -3,8 +3,7 @@ export interface FrameworkGenerics {
|
|
|
3
3
|
// and are extended by framework adapters, but cannot be
|
|
4
4
|
// pre-defined as constraints:
|
|
5
5
|
//
|
|
6
|
-
//
|
|
7
|
-
// SyncOrAsyncElement?: any
|
|
6
|
+
// Component: any
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
export type GetFrameworkGeneric<U> = U extends keyof FrameworkGenerics
|
package/src/index.ts
CHANGED
package/src/routeConfig.ts
CHANGED
|
@@ -108,18 +108,12 @@ export type RouteOptions<
|
|
|
108
108
|
// Filter functions that can manipulate search params *after* they are passed to links and navigate
|
|
109
109
|
// calls that match this route.
|
|
110
110
|
postSearchFilters?: SearchFilter<TFullSearchSchema>[]
|
|
111
|
-
// The
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
// The content to be rendered when `loader` encounters an error
|
|
118
|
-
errorElement?: GetFrameworkGeneric<'SyncOrAsyncElement'> // , NoInfer<TLoaderData>>
|
|
119
|
-
// The content to be rendered when rendering encounters an error
|
|
120
|
-
catchElement?: GetFrameworkGeneric<'SyncOrAsyncElement'> // , NoInfer<TLoaderData>>
|
|
121
|
-
// The content to be rendered when the duration of `loader` execution surpasses the `pendingMs` duration
|
|
122
|
-
pendingElement?: GetFrameworkGeneric<'SyncOrAsyncElement'> //, NoInfer<TLoaderData>>
|
|
111
|
+
// The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />`
|
|
112
|
+
component?: GetFrameworkGeneric<'Component'> // , NoInfer<TLoaderData>>
|
|
113
|
+
// The content to be rendered when the route encounters an error
|
|
114
|
+
errorComponent?: GetFrameworkGeneric<'Component'> // , NoInfer<TLoaderData>>
|
|
115
|
+
// If supported by your framework, the content to be rendered as the fallback content until the route is ready to render
|
|
116
|
+
pendingComponent?: GetFrameworkGeneric<'Component'> //, NoInfer<TLoaderData>>
|
|
123
117
|
// An asynchronous function responsible for preparing or fetching data for the route before it is rendered
|
|
124
118
|
loader?: LoaderFn<TRouteLoaderData, TFullSearchSchema, TAllParams>
|
|
125
119
|
// The max age to consider loader data fresh (not-stale) for this route in milliseconds from the time of fetch
|
|
@@ -132,7 +126,7 @@ export type RouteOptions<
|
|
|
132
126
|
// might invalidate the route's data.
|
|
133
127
|
action?: ActionFn<TActionPayload, TActionResponse>
|
|
134
128
|
// Set this to true to rethrow errors up the component tree to either the nearest error boundary or
|
|
135
|
-
// route with error
|
|
129
|
+
// route with error component, whichever comes first.
|
|
136
130
|
useErrorBoundary?: boolean
|
|
137
131
|
// This function is called
|
|
138
132
|
// when moving from an inactive state to an active one. Likewise, when moving from
|
package/src/routeMatch.ts
CHANGED
|
@@ -28,20 +28,14 @@ export interface RouteMatch<
|
|
|
28
28
|
loaderData: TRouteInfo['loaderData']
|
|
29
29
|
routeLoaderData: TRouteInfo['routeLoaderData']
|
|
30
30
|
isFetching: boolean
|
|
31
|
-
isPending: boolean
|
|
32
31
|
invalidAt: number
|
|
33
32
|
__: {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
pendingElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
|
|
33
|
+
component?: GetFrameworkGeneric<'Component'> // , TRouteInfo['loaderData']>
|
|
34
|
+
errorComponent?: GetFrameworkGeneric<'Component'> // , TRouteInfo['loaderData']>
|
|
35
|
+
pendingComponent?: GetFrameworkGeneric<'Component'> // , TRouteInfo['loaderData']>
|
|
38
36
|
loadPromise?: Promise<void>
|
|
39
|
-
|
|
40
|
-
elementsPromise?: Promise<void>
|
|
37
|
+
componentsPromise?: Promise<void>
|
|
41
38
|
dataPromise?: Promise<void>
|
|
42
|
-
pendingTimeout?: Timeout
|
|
43
|
-
pendingMinTimeout?: Timeout
|
|
44
|
-
pendingMinPromise?: Promise<void>
|
|
45
39
|
onExit?:
|
|
46
40
|
| void
|
|
47
41
|
| ((matchContext: {
|
|
@@ -53,28 +47,24 @@ export interface RouteMatch<
|
|
|
53
47
|
// setParentMatch: (parentMatch: RouteMatch) => void
|
|
54
48
|
// addChildMatch: (childMatch: RouteMatch) => void
|
|
55
49
|
validate: () => void
|
|
56
|
-
startPending: () => void
|
|
57
|
-
cancelPending: () => void
|
|
58
50
|
notify: () => void
|
|
59
51
|
resolve: () => void
|
|
60
52
|
}
|
|
61
53
|
cancel: () => void
|
|
62
54
|
load: (
|
|
63
|
-
loaderOpts?:
|
|
55
|
+
loaderOpts?:
|
|
64
56
|
| { preload: true; maxAge: number; gcMaxAge: number }
|
|
65
|
-
| { preload?: false; maxAge?: never; gcMaxAge?: never }
|
|
66
|
-
),
|
|
57
|
+
| { preload?: false; maxAge?: never; gcMaxAge?: never },
|
|
67
58
|
) => Promise<TRouteInfo['routeLoaderData']>
|
|
68
59
|
fetch: (opts?: { maxAge?: number }) => Promise<TRouteInfo['routeLoaderData']>
|
|
69
60
|
invalidate: () => void
|
|
70
61
|
hasLoaders: () => boolean
|
|
71
62
|
}
|
|
72
63
|
|
|
73
|
-
const
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
'pendingElement',
|
|
64
|
+
const componentTypes = [
|
|
65
|
+
'component',
|
|
66
|
+
'errorComponent',
|
|
67
|
+
'pendingComponent',
|
|
78
68
|
] as const
|
|
79
69
|
|
|
80
70
|
export function createRouteMatch<
|
|
@@ -99,7 +89,6 @@ export function createRouteMatch<
|
|
|
99
89
|
status: 'idle',
|
|
100
90
|
routeLoaderData: {} as TRouteInfo['routeLoaderData'],
|
|
101
91
|
loaderData: {} as TRouteInfo['loaderData'],
|
|
102
|
-
isPending: false,
|
|
103
92
|
isFetching: false,
|
|
104
93
|
isInvalid: false,
|
|
105
94
|
invalidAt: Infinity,
|
|
@@ -116,37 +105,6 @@ export function createRouteMatch<
|
|
|
116
105
|
routeMatch.__.resolve()
|
|
117
106
|
routeMatch.router.notify()
|
|
118
107
|
},
|
|
119
|
-
startPending: () => {
|
|
120
|
-
const pendingMs =
|
|
121
|
-
routeMatch.options.pendingMs ?? router.options.defaultPendingMs
|
|
122
|
-
const pendingMinMs =
|
|
123
|
-
routeMatch.options.pendingMinMs ?? router.options.defaultPendingMinMs
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
routeMatch.__.pendingTimeout ||
|
|
127
|
-
routeMatch.status !== 'loading' ||
|
|
128
|
-
typeof pendingMs === 'undefined'
|
|
129
|
-
) {
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
routeMatch.__.pendingTimeout = setTimeout(() => {
|
|
134
|
-
routeMatch.isPending = true
|
|
135
|
-
routeMatch.__.resolve()
|
|
136
|
-
if (typeof pendingMinMs !== 'undefined') {
|
|
137
|
-
routeMatch.__.pendingMinPromise = new Promise(
|
|
138
|
-
(r) =>
|
|
139
|
-
(routeMatch.__.pendingMinTimeout = setTimeout(r, pendingMinMs)),
|
|
140
|
-
)
|
|
141
|
-
}
|
|
142
|
-
}, pendingMs)
|
|
143
|
-
},
|
|
144
|
-
cancelPending: () => {
|
|
145
|
-
routeMatch.isPending = false
|
|
146
|
-
clearTimeout(routeMatch.__.pendingTimeout)
|
|
147
|
-
clearTimeout(routeMatch.__.pendingMinTimeout)
|
|
148
|
-
delete routeMatch.__.pendingMinPromise
|
|
149
|
-
},
|
|
150
108
|
validate: () => {
|
|
151
109
|
// Validate the search params and stabilize them
|
|
152
110
|
const parentSearch =
|
|
@@ -177,11 +135,11 @@ export function createRouteMatch<
|
|
|
177
135
|
...nextSearch,
|
|
178
136
|
})
|
|
179
137
|
|
|
180
|
-
|
|
181
|
-
const
|
|
138
|
+
componentTypes.map(async (type) => {
|
|
139
|
+
const component = routeMatch.options[type]
|
|
182
140
|
|
|
183
141
|
if (typeof routeMatch.__[type] !== 'function') {
|
|
184
|
-
routeMatch.__[type] =
|
|
142
|
+
routeMatch.__[type] = component
|
|
185
143
|
}
|
|
186
144
|
})
|
|
187
145
|
} catch (err: any) {
|
|
@@ -199,7 +157,6 @@ export function createRouteMatch<
|
|
|
199
157
|
},
|
|
200
158
|
cancel: () => {
|
|
201
159
|
routeMatch.__.abortController?.abort()
|
|
202
|
-
routeMatch.__.cancelPending()
|
|
203
160
|
},
|
|
204
161
|
invalidate: () => {
|
|
205
162
|
routeMatch.isInvalid = true
|
|
@@ -207,7 +164,7 @@ export function createRouteMatch<
|
|
|
207
164
|
hasLoaders: () => {
|
|
208
165
|
return !!(
|
|
209
166
|
route.options.loader ||
|
|
210
|
-
|
|
167
|
+
componentTypes.some((d) => route.options[d]?.preload)
|
|
211
168
|
)
|
|
212
169
|
},
|
|
213
170
|
load: async (loaderOpts) => {
|
|
@@ -262,102 +219,87 @@ export function createRouteMatch<
|
|
|
262
219
|
routeMatch.isFetching = true
|
|
263
220
|
routeMatch.__.resolve = resolve as () => void
|
|
264
221
|
|
|
265
|
-
routeMatch.__.
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
routeMatch.__.elementsPromise = (async () => {
|
|
269
|
-
// then run all element and data loaders in parallel
|
|
270
|
-
// For each element type, potentially load it asynchronously
|
|
271
|
-
|
|
272
|
-
await Promise.all(
|
|
273
|
-
elementTypes.map(async (type) => {
|
|
274
|
-
const routeElement = routeMatch.options[type]
|
|
275
|
-
|
|
276
|
-
if (typeof routeMatch.__[type] === 'function') {
|
|
277
|
-
routeMatch.__[type] = await router.options.createElement!(
|
|
278
|
-
routeElement,
|
|
279
|
-
)
|
|
280
|
-
}
|
|
281
|
-
}),
|
|
282
|
-
)
|
|
283
|
-
})()
|
|
222
|
+
routeMatch.__.componentsPromise = (async () => {
|
|
223
|
+
// then run all component and data loaders in parallel
|
|
224
|
+
// For each component type, potentially load it asynchronously
|
|
284
225
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const data = await routeMatch.options.loader({
|
|
289
|
-
params: routeMatch.params,
|
|
290
|
-
search: routeMatch.routeSearch,
|
|
291
|
-
signal: routeMatch.__.abortController.signal,
|
|
292
|
-
})
|
|
293
|
-
if (id !== routeMatch.__.latestId) {
|
|
294
|
-
return routeMatch.__.loadPromise
|
|
295
|
-
}
|
|
226
|
+
await Promise.all(
|
|
227
|
+
componentTypes.map(async (type) => {
|
|
228
|
+
const component = routeMatch.options[type]
|
|
296
229
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
230
|
+
if (routeMatch.__[type]?.preload) {
|
|
231
|
+
routeMatch.__[type] = await router.options.loadComponent!(
|
|
232
|
+
component,
|
|
300
233
|
)
|
|
301
234
|
}
|
|
235
|
+
}),
|
|
236
|
+
)
|
|
237
|
+
})()
|
|
302
238
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
routeMatch.
|
|
307
|
-
routeMatch.
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
0)
|
|
312
|
-
} catch (err) {
|
|
239
|
+
routeMatch.__.dataPromise = Promise.resolve().then(async () => {
|
|
240
|
+
try {
|
|
241
|
+
if (routeMatch.options.loader) {
|
|
242
|
+
const data = await routeMatch.options.loader({
|
|
243
|
+
params: routeMatch.params,
|
|
244
|
+
search: routeMatch.routeSearch,
|
|
245
|
+
signal: routeMatch.__.abortController.signal,
|
|
246
|
+
})
|
|
313
247
|
if (id !== routeMatch.__.latestId) {
|
|
314
248
|
return routeMatch.__.loadPromise
|
|
315
249
|
}
|
|
316
250
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
routeMatch.status = 'error'
|
|
322
|
-
routeMatch.updatedAt = Date.now()
|
|
251
|
+
routeMatch.routeLoaderData = replaceEqualDeep(
|
|
252
|
+
routeMatch.routeLoaderData,
|
|
253
|
+
data,
|
|
254
|
+
)
|
|
323
255
|
}
|
|
324
|
-
})
|
|
325
256
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
257
|
+
routeMatch.error = undefined
|
|
258
|
+
routeMatch.status = 'success'
|
|
259
|
+
routeMatch.updatedAt = Date.now()
|
|
260
|
+
routeMatch.invalidAt =
|
|
261
|
+
routeMatch.updatedAt +
|
|
262
|
+
(opts?.maxAge ??
|
|
263
|
+
routeMatch.options.loaderMaxAge ??
|
|
264
|
+
router.options.defaultLoaderMaxAge ??
|
|
265
|
+
0)
|
|
266
|
+
} catch (err) {
|
|
331
267
|
if (id !== routeMatch.__.latestId) {
|
|
332
268
|
return routeMatch.__.loadPromise
|
|
333
269
|
}
|
|
334
270
|
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
delete routeMatch.__.pendingMinPromise
|
|
271
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
272
|
+
console.error(err)
|
|
338
273
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
343
|
-
routeMatch.__.cancelPending()
|
|
344
|
-
routeMatch.isPending = false
|
|
345
|
-
routeMatch.isFetching = false
|
|
346
|
-
routeMatch.__.notify()
|
|
274
|
+
routeMatch.error = err
|
|
275
|
+
routeMatch.status = 'error'
|
|
276
|
+
routeMatch.updatedAt = Date.now()
|
|
347
277
|
}
|
|
348
|
-
})
|
|
278
|
+
})
|
|
349
279
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
280
|
+
try {
|
|
281
|
+
await Promise.all([
|
|
282
|
+
routeMatch.__.componentsPromise,
|
|
283
|
+
routeMatch.__.dataPromise,
|
|
284
|
+
])
|
|
285
|
+
if (id !== routeMatch.__.latestId) {
|
|
286
|
+
return routeMatch.__.loadPromise
|
|
287
|
+
}
|
|
288
|
+
} finally {
|
|
289
|
+
if (id !== routeMatch.__.latestId) {
|
|
290
|
+
return routeMatch.__.loadPromise
|
|
291
|
+
}
|
|
292
|
+
routeMatch.isFetching = false
|
|
293
|
+
routeMatch.__.notify()
|
|
354
294
|
}
|
|
355
|
-
|
|
356
|
-
delete routeMatch.__.loaderDataPromise
|
|
357
295
|
})
|
|
358
296
|
|
|
359
297
|
await routeMatch.__.loadPromise
|
|
360
298
|
|
|
299
|
+
if (id !== routeMatch.__.latestId) {
|
|
300
|
+
return routeMatch.__.loadPromise
|
|
301
|
+
}
|
|
302
|
+
|
|
361
303
|
delete routeMatch.__.loadPromise
|
|
362
304
|
},
|
|
363
305
|
}
|
package/src/router.ts
CHANGED
|
@@ -91,12 +91,9 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
91
91
|
defaultPreloadGcMaxAge?: number
|
|
92
92
|
defaultPreloadDelay?: number
|
|
93
93
|
useErrorBoundary?: boolean
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
defaultPendingElement?: GetFrameworkGeneric<'Element'>
|
|
98
|
-
defaultPendingMs?: number
|
|
99
|
-
defaultPendingMinMs?: number
|
|
94
|
+
defaultComponent?: GetFrameworkGeneric<'Component'>
|
|
95
|
+
defaultErrorComponent?: GetFrameworkGeneric<'Component'>
|
|
96
|
+
defaultPendingComponent?: GetFrameworkGeneric<'Component'>
|
|
100
97
|
defaultLoaderMaxAge?: number
|
|
101
98
|
defaultLoaderGcMaxAge?: number
|
|
102
99
|
caseSensitive?: boolean
|
|
@@ -104,9 +101,12 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
104
101
|
basepath?: string
|
|
105
102
|
createRouter?: (router: Router<any, any>) => void
|
|
106
103
|
createRoute?: (opts: { route: AnyRoute; router: Router<any, any> }) => void
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
) => Promise<GetFrameworkGeneric<'
|
|
104
|
+
loadComponent?: (
|
|
105
|
+
component: GetFrameworkGeneric<'Component'>,
|
|
106
|
+
) => Promise<GetFrameworkGeneric<'Component'>>
|
|
107
|
+
// renderComponent?: (
|
|
108
|
+
// component: GetFrameworkGeneric<'Component'>,
|
|
109
|
+
// ) => GetFrameworkGeneric<'Element'>
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
export interface Action<
|
|
@@ -294,10 +294,9 @@ export interface Router<
|
|
|
294
294
|
) => RouteMatch[]
|
|
295
295
|
loadMatches: (
|
|
296
296
|
resolvedMatches: RouteMatch[],
|
|
297
|
-
loaderOpts?:
|
|
297
|
+
loaderOpts?:
|
|
298
298
|
| { preload: true; maxAge: number; gcMaxAge: number }
|
|
299
|
-
| { preload?: false; maxAge?: never; gcMaxAge?: never }
|
|
300
|
-
),
|
|
299
|
+
| { preload?: false; maxAge?: never; gcMaxAge?: never },
|
|
301
300
|
) => Promise<void>
|
|
302
301
|
invalidateRoute: (opts: MatchLocation) => void
|
|
303
302
|
reload: () => Promise<void>
|
|
@@ -555,21 +554,28 @@ export function createRouter<
|
|
|
555
554
|
strictParseParams: true,
|
|
556
555
|
})
|
|
557
556
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
557
|
+
if (typeof document !== 'undefined') {
|
|
558
|
+
router.state = {
|
|
559
|
+
...router.state,
|
|
560
|
+
pending: {
|
|
561
|
+
matches: matches,
|
|
562
|
+
location: router.location,
|
|
563
|
+
},
|
|
564
|
+
status: 'loading',
|
|
565
|
+
}
|
|
566
|
+
} else {
|
|
567
|
+
router.state = {
|
|
568
|
+
...router.state,
|
|
561
569
|
matches: matches,
|
|
562
570
|
location: router.location,
|
|
563
|
-
|
|
564
|
-
|
|
571
|
+
status: 'loading',
|
|
572
|
+
}
|
|
565
573
|
}
|
|
566
574
|
|
|
567
575
|
router.notify()
|
|
568
576
|
|
|
569
577
|
// Load the matches
|
|
570
|
-
await router.loadMatches(matches
|
|
571
|
-
withPending: true,
|
|
572
|
-
})
|
|
578
|
+
await router.loadMatches(matches)
|
|
573
579
|
|
|
574
580
|
if (router.startedLoadingAt !== id) {
|
|
575
581
|
// Ignore side-effects of match loading
|
|
@@ -821,14 +827,8 @@ export function createRouter<
|
|
|
821
827
|
match.__.validate()
|
|
822
828
|
match.load(loaderOpts)
|
|
823
829
|
|
|
824
|
-
if (match.status === 'loading') {
|
|
825
|
-
// If requested, start the pending timers
|
|
826
|
-
if (loaderOpts?.withPending) match.__.startPending()
|
|
827
|
-
}
|
|
828
|
-
|
|
829
830
|
if (match.__.loadPromise) {
|
|
830
831
|
// Wait for the first sign of activity from the match
|
|
831
|
-
// This might be completion, error, or a pending state
|
|
832
832
|
await match.__.loadPromise
|
|
833
833
|
}
|
|
834
834
|
})
|