@tanstack/router-core 0.0.1-beta.2 → 0.0.1-beta.21

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/src/routeMatch.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { GetFrameworkGeneric } from './frameworks'
2
2
  import { Route } from './route'
3
- import { AnyPathParams } from './routeConfig'
4
3
  import {
5
4
  AnyAllRouteInfo,
6
5
  AnyRouteInfo,
@@ -8,7 +7,7 @@ import {
8
7
  RouteInfo,
9
8
  } from './routeInfo'
10
9
  import { Router } from './router'
11
- import { replaceEqualDeep, Timeout } from './utils'
10
+ import { replaceEqualDeep } from './utils'
12
11
 
13
12
  export interface RouteMatch<
14
13
  TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
@@ -16,7 +15,7 @@ export interface RouteMatch<
16
15
  > extends Route<TAllRouteInfo, TRouteInfo> {
17
16
  matchId: string
18
17
  pathname: string
19
- params: TRouteInfo['params']
18
+ params: TRouteInfo['allParams']
20
19
  parentMatch?: RouteMatch
21
20
  childMatches: RouteMatch[]
22
21
  routeSearch: TRouteInfo['searchSchema']
@@ -29,20 +28,14 @@ export interface RouteMatch<
29
28
  loaderData: TRouteInfo['loaderData']
30
29
  routeLoaderData: TRouteInfo['routeLoaderData']
31
30
  isFetching: boolean
32
- isPending: boolean
33
31
  invalidAt: number
34
32
  __: {
35
- element?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
36
- errorElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
37
- catchElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
38
- pendingElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
33
+ component?: GetFrameworkGeneric<'Component'> // , TRouteInfo['loaderData']>
34
+ errorComponent?: GetFrameworkGeneric<'Component'> // , TRouteInfo['loaderData']>
35
+ pendingComponent?: GetFrameworkGeneric<'Component'> // , TRouteInfo['loaderData']>
39
36
  loadPromise?: Promise<void>
40
- loaderPromise?: Promise<void>
41
- elementsPromise?: Promise<void>
42
- dataPromise?: Promise<void>
43
- pendingTimeout?: Timeout
44
- pendingMinTimeout?: Timeout
45
- pendingMinPromise?: Promise<void>
37
+ componentsPromise?: Promise<void>
38
+ dataPromise?: Promise<TRouteInfo['routeLoaderData']>
46
39
  onExit?:
47
40
  | void
48
41
  | ((matchContext: {
@@ -54,28 +47,24 @@ export interface RouteMatch<
54
47
  // setParentMatch: (parentMatch: RouteMatch) => void
55
48
  // addChildMatch: (childMatch: RouteMatch) => void
56
49
  validate: () => void
57
- startPending: () => void
58
- cancelPending: () => void
59
50
  notify: () => void
60
51
  resolve: () => void
61
52
  }
62
53
  cancel: () => void
63
54
  load: (
64
- loaderOpts?: { withPending?: boolean } & (
55
+ loaderOpts?:
65
56
  | { preload: true; maxAge: number; gcMaxAge: number }
66
- | { preload?: false; maxAge?: never; gcMaxAge?: never }
67
- ),
57
+ | { preload?: false; maxAge?: never; gcMaxAge?: never },
68
58
  ) => Promise<TRouteInfo['routeLoaderData']>
69
59
  fetch: (opts?: { maxAge?: number }) => Promise<TRouteInfo['routeLoaderData']>
70
60
  invalidate: () => void
71
61
  hasLoaders: () => boolean
72
62
  }
73
63
 
74
- const elementTypes = [
75
- 'element',
76
- 'errorElement',
77
- 'catchElement',
78
- 'pendingElement',
64
+ const componentTypes = [
65
+ 'component',
66
+ 'errorComponent',
67
+ 'pendingComponent',
79
68
  ] as const
80
69
 
81
70
  export function createRouteMatch<
@@ -85,6 +74,7 @@ export function createRouteMatch<
85
74
  router: Router<any, any>,
86
75
  route: Route<TAllRouteInfo, TRouteInfo>,
87
76
  opts: {
77
+ parentMatch?: RouteMatch<any, any>
88
78
  matchId: string
89
79
  params: TRouteInfo['allParams']
90
80
  pathname: string
@@ -100,10 +90,10 @@ export function createRouteMatch<
100
90
  status: 'idle',
101
91
  routeLoaderData: {} as TRouteInfo['routeLoaderData'],
102
92
  loaderData: {} as TRouteInfo['loaderData'],
103
- isPending: false,
104
93
  isFetching: false,
105
94
  isInvalid: false,
106
95
  invalidAt: Infinity,
96
+ // pendingActions: [],
107
97
  getIsInvalid: () => {
108
98
  const now = Date.now()
109
99
  return routeMatch.isInvalid || routeMatch.invalidAt < now
@@ -116,49 +106,6 @@ export function createRouteMatch<
116
106
  routeMatch.__.resolve()
117
107
  routeMatch.router.notify()
118
108
  },
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
- // setParentMatch: (parentMatch?: RouteMatch) => {
151
- // routeMatch.parentMatch = parentMatch
152
- // },
153
- // addChildMatch: (childMatch: RouteMatch) => {
154
- // if (
155
- // routeMatch.childMatches.find((d) => d.matchId === childMatch.matchId)
156
- // ) {
157
- // return
158
- // }
159
-
160
- // routeMatch.childMatches.push(childMatch)
161
- // },
162
109
  validate: () => {
163
110
  // Validate the search params and stabilize them
164
111
  const parentSearch =
@@ -174,7 +121,7 @@ export function createRouteMatch<
174
121
 
175
122
  let nextSearch = replaceEqualDeep(
176
123
  prevSearch,
177
- validator?.(parentSearch),
124
+ validator?.(parentSearch) ?? {},
178
125
  )
179
126
 
180
127
  // Invalidate route matches when search param stability changes
@@ -188,6 +135,14 @@ export function createRouteMatch<
188
135
  ...parentSearch,
189
136
  ...nextSearch,
190
137
  })
138
+
139
+ componentTypes.map(async (type) => {
140
+ const component = routeMatch.options[type]
141
+
142
+ if (typeof routeMatch.__[type] !== 'function') {
143
+ routeMatch.__[type] = component
144
+ }
145
+ })
191
146
  } catch (err: any) {
192
147
  console.error(err)
193
148
  const error = new (Error as any)('Invalid search params found', {
@@ -203,7 +158,6 @@ export function createRouteMatch<
203
158
  },
204
159
  cancel: () => {
205
160
  routeMatch.__.abortController?.abort()
206
- routeMatch.__.cancelPending()
207
161
  },
208
162
  invalidate: () => {
209
163
  routeMatch.isInvalid = true
@@ -211,7 +165,7 @@ export function createRouteMatch<
211
165
  hasLoaders: () => {
212
166
  return !!(
213
167
  route.options.loader ||
214
- elementTypes.some((d) => typeof route.options[d] === 'function')
168
+ componentTypes.some((d) => route.options[d]?.preload)
215
169
  )
216
170
  },
217
171
  load: async (loaderOpts) => {
@@ -243,12 +197,18 @@ export function createRouteMatch<
243
197
  ) {
244
198
  const maxAge = loaderOpts?.preload ? loaderOpts?.maxAge : undefined
245
199
 
246
- routeMatch.fetch({ maxAge })
200
+ await routeMatch.fetch({ maxAge })
247
201
  }
248
202
  },
249
203
  fetch: async (opts) => {
250
- const id = '' + Date.now() + Math.random()
251
- routeMatch.__.latestId = id
204
+ const loadId = '' + Date.now() + Math.random()
205
+ routeMatch.__.latestId = loadId
206
+ const checkLatest = async () => {
207
+ if (loadId !== routeMatch.__.latestId) {
208
+ // warning(true, 'Data loader is out of date!')
209
+ return new Promise(() => {})
210
+ }
211
+ }
252
212
 
253
213
  // If the match was in an error state, set it
254
214
  // to a loading state again. Otherwise, keep it
@@ -266,103 +226,81 @@ export function createRouteMatch<
266
226
  routeMatch.isFetching = true
267
227
  routeMatch.__.resolve = resolve as () => void
268
228
 
269
- const loaderPromise = (async () => {
270
- // Load the elements and data in parallel
271
-
272
- routeMatch.__.elementsPromise = (async () => {
273
- // then run all element and data loaders in parallel
274
- // For each element type, potentially load it asynchronously
229
+ routeMatch.__.componentsPromise = (async () => {
230
+ // then run all component and data loaders in parallel
231
+ // For each component type, potentially load it asynchronously
275
232
 
276
- await Promise.all(
277
- elementTypes.map(async (type) => {
278
- const routeElement = routeMatch.options[type]
233
+ await Promise.all(
234
+ componentTypes.map(async (type) => {
235
+ const component = routeMatch.options[type]
279
236
 
280
- if (routeMatch.__[type]) {
281
- return
282
- }
283
-
284
- routeMatch.__[type] = await router.options.createElement!(
285
- routeElement,
286
- )
287
- }),
288
- )
289
- })()
290
-
291
- routeMatch.__.dataPromise = Promise.resolve().then(async () => {
292
- try {
293
- if (routeMatch.options.loader) {
294
- const data = await routeMatch.options.loader({
295
- params: routeMatch.params,
296
- search: routeMatch.routeSearch,
297
- signal: routeMatch.__.abortController.signal,
298
- })
299
- if (id !== routeMatch.__.latestId) {
300
- return routeMatch.__.loaderPromise
301
- }
302
-
303
- routeMatch.routeLoaderData = replaceEqualDeep(
304
- routeMatch.routeLoaderData,
305
- data,
237
+ if (routeMatch.__[type]?.preload) {
238
+ routeMatch.__[type] = await router.options.loadComponent!(
239
+ component,
306
240
  )
307
241
  }
242
+ }),
243
+ )
244
+ })()
308
245
 
309
- routeMatch.error = undefined
310
- routeMatch.status = 'success'
311
- routeMatch.updatedAt = Date.now()
312
- routeMatch.invalidAt =
313
- routeMatch.updatedAt +
314
- (opts?.maxAge ??
315
- routeMatch.options.loaderMaxAge ??
316
- router.options.defaultLoaderMaxAge ??
317
- 0)
318
- } catch (err) {
319
- if (id !== routeMatch.__.latestId) {
320
- return routeMatch.__.loaderPromise
321
- }
322
-
323
- if (process.env.NODE_ENV !== 'production') {
324
- console.error(err)
325
- }
326
- routeMatch.error = err
327
- routeMatch.status = 'error'
328
- routeMatch.updatedAt = Date.now()
329
- }
330
- })
331
-
246
+ routeMatch.__.dataPromise = Promise.resolve().then(async () => {
332
247
  try {
333
- await Promise.all([
334
- routeMatch.__.elementsPromise,
335
- routeMatch.__.dataPromise,
336
- ])
337
- if (id !== routeMatch.__.latestId) {
338
- return routeMatch.__.loaderPromise
248
+ if (routeMatch.options.loader) {
249
+ const data = await router.loadMatchData(routeMatch)
250
+ await checkLatest()
251
+
252
+ routeMatch.routeLoaderData = replaceEqualDeep(
253
+ routeMatch.routeLoaderData,
254
+ data,
255
+ )
339
256
  }
340
257
 
341
- if (routeMatch.__.pendingMinPromise) {
342
- await routeMatch.__.pendingMinPromise
343
- delete routeMatch.__.pendingMinPromise
258
+ routeMatch.error = undefined
259
+ routeMatch.status = 'success'
260
+ routeMatch.updatedAt = Date.now()
261
+ routeMatch.invalidAt =
262
+ routeMatch.updatedAt +
263
+ (opts?.maxAge ??
264
+ routeMatch.options.loaderMaxAge ??
265
+ router.options.defaultLoaderMaxAge ??
266
+ 0)
267
+
268
+ return routeMatch.routeLoaderData
269
+ } catch (err) {
270
+ await checkLatest()
271
+
272
+ if (process.env.NODE_ENV !== 'production') {
273
+ console.error(err)
344
274
  }
345
- } finally {
346
- if (id !== routeMatch.__.latestId) {
347
- return routeMatch.__.loaderPromise
348
- }
349
- routeMatch.__.cancelPending()
350
- routeMatch.isPending = false
351
- routeMatch.isFetching = false
352
- routeMatch.__.notify()
275
+
276
+ routeMatch.error = err
277
+ routeMatch.status = 'error'
278
+ routeMatch.updatedAt = Date.now()
279
+
280
+ throw err
353
281
  }
354
- })()
282
+ })
355
283
 
356
- routeMatch.__.loaderPromise = loaderPromise
357
- await loaderPromise
284
+ const after = async () => {
285
+ await checkLatest()
286
+ routeMatch.isFetching = false
287
+ delete routeMatch.__.loadPromise
288
+ routeMatch.__.notify()
289
+ }
358
290
 
359
- if (id !== routeMatch.__.latestId) {
360
- return routeMatch.__.loaderPromise
291
+ try {
292
+ await Promise.all([
293
+ routeMatch.__.componentsPromise,
294
+ routeMatch.__.dataPromise.catch(() => {}),
295
+ ])
296
+ after()
297
+ } catch {
298
+ after()
361
299
  }
362
- delete routeMatch.__.loaderPromise
363
300
  })
364
301
 
365
- return await routeMatch.__.loadPromise
302
+ await routeMatch.__.loadPromise
303
+ await checkLatest()
366
304
  },
367
305
  }
368
306