@tanstack/router-core 0.0.1-beta.4 → 0.0.1-beta.41

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 (61) hide show
  1. package/build/cjs/{packages/router-core/src/index.js → index.js} +25 -8
  2. package/build/cjs/{packages/router-core/src/index.js.map → index.js.map} +1 -1
  3. package/build/cjs/{packages/router-core/src/path.js → path.js} +19 -43
  4. package/build/cjs/path.js.map +1 -0
  5. package/build/cjs/{packages/router-core/src/qss.js → qss.js} +8 -13
  6. package/build/cjs/qss.js.map +1 -0
  7. package/build/cjs/route.js +155 -0
  8. package/build/cjs/route.js.map +1 -0
  9. package/build/cjs/{packages/router-core/src/routeConfig.js → routeConfig.js} +14 -13
  10. package/build/cjs/routeConfig.js.map +1 -0
  11. package/build/cjs/routeMatch.js +242 -0
  12. package/build/cjs/routeMatch.js.map +1 -0
  13. package/build/cjs/router.js +807 -0
  14. package/build/cjs/router.js.map +1 -0
  15. package/build/cjs/{packages/router-core/src/searchParams.js → searchParams.js} +10 -12
  16. package/build/cjs/searchParams.js.map +1 -0
  17. package/build/cjs/sharedClone.js +122 -0
  18. package/build/cjs/sharedClone.js.map +1 -0
  19. package/build/cjs/utils.js +47 -0
  20. package/build/cjs/utils.js.map +1 -0
  21. package/build/esm/index.js +890 -1739
  22. package/build/esm/index.js.map +1 -1
  23. package/build/stats-html.html +59 -49
  24. package/build/stats-react.json +196 -178
  25. package/build/types/index.d.ts +287 -283
  26. package/build/umd/index.development.js +1233 -922
  27. package/build/umd/index.development.js.map +1 -1
  28. package/build/umd/index.production.js +1 -1
  29. package/build/umd/index.production.js.map +1 -1
  30. package/package.json +4 -2
  31. package/src/frameworks.ts +2 -2
  32. package/src/index.ts +1 -1
  33. package/src/link.ts +86 -43
  34. package/src/path.ts +12 -8
  35. package/src/route.ts +170 -158
  36. package/src/routeConfig.ts +105 -77
  37. package/src/routeInfo.ts +26 -8
  38. package/src/routeMatch.ts +204 -217
  39. package/src/router.ts +680 -503
  40. package/src/sharedClone.ts +118 -0
  41. package/src/utils.ts +14 -72
  42. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
  43. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  44. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
  45. package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
  46. package/build/cjs/node_modules/history/index.js +0 -815
  47. package/build/cjs/node_modules/history/index.js.map +0 -1
  48. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
  49. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
  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 -161
  53. package/build/cjs/packages/router-core/src/route.js.map +0 -1
  54. package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
  55. package/build/cjs/packages/router-core/src/routeMatch.js +0 -266
  56. package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
  57. package/build/cjs/packages/router-core/src/router.js +0 -789
  58. package/build/cjs/packages/router-core/src/router.js.map +0 -1
  59. package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
  60. package/build/cjs/packages/router-core/src/utils.js +0 -118
  61. package/build/cjs/packages/router-core/src/utils.js.map +0 -1
package/src/routeMatch.ts CHANGED
@@ -1,6 +1,6 @@
1
+ import { boolean } from 'zod'
1
2
  import { GetFrameworkGeneric } from './frameworks'
2
3
  import { Route } from './route'
3
- import { AnyPathParams } from './routeConfig'
4
4
  import {
5
5
  AnyAllRouteInfo,
6
6
  AnyRouteInfo,
@@ -8,41 +8,55 @@ import {
8
8
  RouteInfo,
9
9
  } from './routeInfo'
10
10
  import { Router } from './router'
11
- import { replaceEqualDeep, Timeout } from './utils'
11
+ import { batch, createStore } from '@solidjs/reactivity'
12
+ import { Expand } from './utils'
13
+ import { sharedClone } from './sharedClone'
12
14
 
13
- export interface RouteMatch<
15
+ export interface RouteMatchStore<
14
16
  TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
15
17
  TRouteInfo extends AnyRouteInfo = RouteInfo,
16
- > extends Route<TAllRouteInfo, TRouteInfo> {
17
- matchId: string
18
- pathname: string
19
- params: TRouteInfo['params']
18
+ > {
20
19
  parentMatch?: RouteMatch
21
- childMatches: RouteMatch[]
22
20
  routeSearch: TRouteInfo['searchSchema']
23
- search: TRouteInfo['fullSearchSchema']
21
+ search: Expand<
22
+ TAllRouteInfo['fullSearchSchema'] & TRouteInfo['fullSearchSchema']
23
+ >
24
24
  status: 'idle' | 'loading' | 'success' | 'error'
25
25
  updatedAt?: number
26
26
  error?: unknown
27
+ invalid: boolean
27
28
  isInvalid: boolean
28
- getIsInvalid: () => boolean
29
29
  loaderData: TRouteInfo['loaderData']
30
30
  routeLoaderData: TRouteInfo['routeLoaderData']
31
31
  isFetching: boolean
32
- isPending: boolean
33
32
  invalidAt: number
33
+ }
34
+
35
+ export interface RouteMatch<
36
+ TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
37
+ TRouteInfo extends AnyRouteInfo = RouteInfo,
38
+ > extends Route<TAllRouteInfo, TRouteInfo> {
39
+ store: RouteMatchStore<TAllRouteInfo, TRouteInfo>
40
+ // setStore: WritableStore<RouteMatchStore<TAllRouteInfo, TRouteInfo>>
41
+ matchId: string
42
+ pathname: string
43
+ params: TRouteInfo['allParams']
44
+ childMatches: RouteMatch[]
45
+ cancel: () => void
46
+ load: (
47
+ loaderOpts?:
48
+ | { preload: true; maxAge: number; gcMaxAge: number }
49
+ | { preload?: false; maxAge?: never; gcMaxAge?: never },
50
+ ) => Promise<TRouteInfo['routeLoaderData']>
51
+ fetch: (opts?: { maxAge?: number }) => Promise<TRouteInfo['routeLoaderData']>
52
+ invalidate: () => void
53
+ hasLoaders: () => boolean
34
54
  __: {
35
- element?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
36
- errorElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
37
- catchElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
38
- pendingElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
55
+ setParentMatch: (parentMatch?: RouteMatch) => void
56
+ component?: GetFrameworkGeneric<'Component'>
57
+ errorComponent?: GetFrameworkGeneric<'ErrorComponent'>
58
+ pendingComponent?: GetFrameworkGeneric<'Component'>
39
59
  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>
46
60
  onExit?:
47
61
  | void
48
62
  | ((matchContext: {
@@ -50,143 +64,127 @@ export interface RouteMatch<
50
64
  search: TRouteInfo['fullSearchSchema']
51
65
  }) => void)
52
66
  abortController: AbortController
53
- latestId: string
54
- // setParentMatch: (parentMatch: RouteMatch) => void
55
- // addChildMatch: (childMatch: RouteMatch) => void
56
67
  validate: () => void
57
- startPending: () => void
58
- cancelPending: () => void
59
- notify: () => void
60
- resolve: () => void
61
68
  }
62
- cancel: () => void
63
- load: (
64
- loaderOpts?: { withPending?: boolean } & (
65
- | { preload: true; maxAge: number; gcMaxAge: number }
66
- | { preload?: false; maxAge?: never; gcMaxAge?: never }
67
- ),
68
- ) => Promise<TRouteInfo['routeLoaderData']>
69
- fetch: (opts?: { maxAge?: number }) => Promise<TRouteInfo['routeLoaderData']>
70
- invalidate: () => void
71
- hasLoaders: () => boolean
72
69
  }
73
70
 
74
- const elementTypes = [
75
- 'element',
76
- 'errorElement',
77
- 'catchElement',
78
- 'pendingElement',
71
+ const componentTypes = [
72
+ 'component',
73
+ 'errorComponent',
74
+ 'pendingComponent',
79
75
  ] as const
80
76
 
81
77
  export function createRouteMatch<
82
78
  TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
83
79
  TRouteInfo extends AnyRouteInfo = RouteInfo,
84
80
  >(
85
- router: Router<any, any>,
81
+ router: Router<any, any, any>,
86
82
  route: Route<TAllRouteInfo, TRouteInfo>,
87
83
  opts: {
84
+ parentMatch?: RouteMatch<any, any>
88
85
  matchId: string
89
86
  params: TRouteInfo['allParams']
90
87
  pathname: string
91
88
  },
92
89
  ): RouteMatch<TAllRouteInfo, TRouteInfo> {
93
- const routeMatch: RouteMatch<TAllRouteInfo, TRouteInfo> = {
94
- ...route,
95
- ...opts,
96
- router,
90
+ let componentsPromise: Promise<void>
91
+ let dataPromise: Promise<TRouteInfo['routeLoaderData']>
92
+ let latestId = ''
93
+ let resolve = () => {}
94
+
95
+ function setLoaderData(loaderData: TRouteInfo['routeLoaderData']) {
96
+ batch(() => {
97
+ setStore((s) => {
98
+ s.routeLoaderData = sharedClone(s.routeLoaderData, loaderData)
99
+ })
100
+ updateLoaderData()
101
+ })
102
+ }
103
+
104
+ function updateLoaderData() {
105
+ setStore((s) => {
106
+ s.loaderData = sharedClone(s.loaderData, {
107
+ ...store.parentMatch?.store.loaderData,
108
+ ...s.routeLoaderData,
109
+ }) as TRouteInfo['loaderData']
110
+ })
111
+ }
112
+
113
+ const [store, setStore] = createStore<
114
+ RouteMatchStore<TAllRouteInfo, TRouteInfo>
115
+ >({
97
116
  routeSearch: {},
98
- search: {},
99
- childMatches: [],
117
+ search: {} as any,
100
118
  status: 'idle',
101
119
  routeLoaderData: {} as TRouteInfo['routeLoaderData'],
102
120
  loaderData: {} as TRouteInfo['loaderData'],
103
- isPending: false,
104
121
  isFetching: false,
105
- isInvalid: false,
122
+ invalid: false,
106
123
  invalidAt: Infinity,
107
- getIsInvalid: () => {
124
+ get isInvalid(): boolean {
108
125
  const now = Date.now()
109
- return routeMatch.isInvalid || routeMatch.invalidAt < now
126
+ return this.invalid || this.invalidAt < now
110
127
  },
111
- __: {
112
- abortController: new AbortController(),
113
- latestId: '',
114
- resolve: () => {},
115
- notify: () => {
116
- routeMatch.__.resolve()
117
- routeMatch.router.notify()
118
- },
119
- startPending: () => {
120
- const pendingMs =
121
- routeMatch.options.pendingMs ?? router.options.defaultPendingMs
122
- const pendingMinMs =
123
- routeMatch.options.pendingMinMs ?? router.options.defaultPendingMinMs
128
+ })
124
129
 
125
- if (
126
- routeMatch.__.pendingTimeout ||
127
- routeMatch.status !== 'loading' ||
128
- typeof pendingMs === 'undefined'
129
- ) {
130
- return
131
- }
130
+ const routeMatch: RouteMatch<TAllRouteInfo, TRouteInfo> = {
131
+ ...route,
132
+ ...opts,
133
+ store,
134
+ // setStore,
135
+ router,
136
+ childMatches: [],
137
+ __: {
138
+ setParentMatch: (parentMatch?: RouteMatch) => {
139
+ batch(() => {
140
+ setStore((s) => {
141
+ s.parentMatch = parentMatch
142
+ })
132
143
 
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)
144
+ updateLoaderData()
145
+ })
143
146
  },
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
- // },
147
+ abortController: new AbortController(),
162
148
  validate: () => {
163
149
  // Validate the search params and stabilize them
164
150
  const parentSearch =
165
- routeMatch.parentMatch?.search ?? router.location.search
151
+ store.parentMatch?.store.search ?? router.store.currentLocation.search
166
152
 
167
153
  try {
168
- const prevSearch = routeMatch.routeSearch
154
+ const prevSearch = store.routeSearch
169
155
 
170
156
  const validator =
171
157
  typeof routeMatch.options.validateSearch === 'object'
172
158
  ? routeMatch.options.validateSearch.parse
173
159
  : routeMatch.options.validateSearch
174
160
 
175
- let nextSearch = replaceEqualDeep(
161
+ let nextSearch = sharedClone(
176
162
  prevSearch,
177
- validator?.(parentSearch),
163
+ validator?.(parentSearch) ?? {},
178
164
  )
179
165
 
180
- // Invalidate route matches when search param stability changes
181
- if (prevSearch !== nextSearch) {
182
- routeMatch.isInvalid = true
183
- }
166
+ batch(() => {
167
+ // Invalidate route matches when search param stability changes
168
+ if (prevSearch !== nextSearch) {
169
+ setStore((s) => (s.invalid = true))
170
+ }
171
+
172
+ // TODO: Alright, do we need batch() here?
173
+ setStore((s) => {
174
+ s.routeSearch = nextSearch
175
+ s.search = sharedClone(parentSearch, {
176
+ ...parentSearch,
177
+ ...nextSearch,
178
+ })
179
+ })
180
+ })
184
181
 
185
- routeMatch.routeSearch = nextSearch
182
+ componentTypes.map(async (type) => {
183
+ const component = routeMatch.options[type]
186
184
 
187
- routeMatch.search = replaceEqualDeep(parentSearch, {
188
- ...parentSearch,
189
- ...nextSearch,
185
+ if (typeof routeMatch.__[type] !== 'function') {
186
+ routeMatch.__[type] = component
187
+ }
190
188
  })
191
189
  } catch (err: any) {
192
190
  console.error(err)
@@ -194,8 +192,12 @@ export function createRouteMatch<
194
192
  cause: err,
195
193
  })
196
194
  error.code = 'INVALID_SEARCH_PARAMS'
197
- routeMatch.status = 'error'
198
- routeMatch.error = error
195
+
196
+ setStore((s) => {
197
+ s.status = 'error'
198
+ s.error = error
199
+ })
200
+
199
201
  // Do not proceed with loading the route
200
202
  return
201
203
  }
@@ -203,15 +205,14 @@ export function createRouteMatch<
203
205
  },
204
206
  cancel: () => {
205
207
  routeMatch.__.abortController?.abort()
206
- routeMatch.__.cancelPending()
207
208
  },
208
209
  invalidate: () => {
209
- routeMatch.isInvalid = true
210
+ setStore((s) => (s.invalid = true))
210
211
  },
211
212
  hasLoaders: () => {
212
213
  return !!(
213
214
  route.options.loader ||
214
- elementTypes.some((d) => typeof route.options[d] === 'function')
215
+ componentTypes.some((d) => route.options[d]?.preload)
215
216
  )
216
217
  },
217
218
  load: async (loaderOpts) => {
@@ -224,12 +225,14 @@ export function createRouteMatch<
224
225
  if (loaderOpts?.preload && minMaxAge > 0) {
225
226
  // If the match is currently active, don't preload it
226
227
  if (
227
- router.state.matches.find((d) => d.matchId === routeMatch.matchId)
228
+ router.store.currentMatches.find(
229
+ (d) => d.matchId === routeMatch.matchId,
230
+ )
228
231
  ) {
229
232
  return
230
233
  }
231
234
 
232
- router.matchCache[routeMatch.matchId] = {
235
+ router.store.matchCache[routeMatch.matchId] = {
233
236
  gc: now + loaderOpts.gcMaxAge,
234
237
  match: routeMatch as RouteMatch<any, any>,
235
238
  }
@@ -237,137 +240,121 @@ export function createRouteMatch<
237
240
 
238
241
  // If the match is invalid, errored or idle, trigger it to load
239
242
  if (
240
- (routeMatch.status === 'success' && routeMatch.getIsInvalid()) ||
241
- routeMatch.status === 'error' ||
242
- routeMatch.status === 'idle'
243
+ (store.status === 'success' && store.isInvalid) ||
244
+ store.status === 'error' ||
245
+ store.status === 'idle'
243
246
  ) {
244
247
  const maxAge = loaderOpts?.preload ? loaderOpts?.maxAge : undefined
245
248
 
246
- routeMatch.fetch({ maxAge })
249
+ await routeMatch.fetch({ maxAge })
247
250
  }
248
251
  },
249
252
  fetch: async (opts) => {
250
- const id = '' + Date.now() + Math.random()
251
- routeMatch.__.latestId = id
252
-
253
- // If the match was in an error state, set it
254
- // to a loading state again. Otherwise, keep it
255
- // as loading or resolved
256
- if (routeMatch.status === 'idle') {
257
- routeMatch.status = 'loading'
253
+ const loadId = '' + Date.now() + Math.random()
254
+ latestId = loadId
255
+ const checkLatest = async () => {
256
+ if (loadId !== latestId) {
257
+ // warning(true, 'Data loader is out of date!')
258
+ return new Promise(() => {})
259
+ }
258
260
  }
259
261
 
260
- // We started loading the route, so it's no longer invalid
261
- routeMatch.isInvalid = false
262
+ batch(() => {
263
+ // If the match was in an error state, set it
264
+ // to a loading state again. Otherwise, keep it
265
+ // as loading or resolved
266
+ if (store.status === 'idle') {
267
+ setStore((s) => (s.status = 'loading'))
268
+ }
269
+
270
+ // We started loading the route, so it's no longer invalid
271
+ setStore((s) => (s.invalid = false))
272
+ })
262
273
 
263
- routeMatch.__.loadPromise = new Promise(async (resolve) => {
274
+ routeMatch.__.loadPromise = new Promise(async (r) => {
264
275
  // We are now fetching, even if it's in the background of a
265
276
  // resolved state
266
- routeMatch.isFetching = true
267
- routeMatch.__.resolve = resolve as () => void
268
-
269
- const loaderPromise = (async () => {
270
- // Load the elements and data in parallel
277
+ setStore((s) => (s.isFetching = true))
278
+ resolve = r as () => void
271
279
 
272
- routeMatch.__.elementsPromise = (async () => {
273
- // then run all element and data loaders in parallel
274
- // For each element type, potentially load it asynchronously
280
+ componentsPromise = (async () => {
281
+ // then run all component and data loaders in parallel
282
+ // For each component type, potentially load it asynchronously
275
283
 
276
- await Promise.all(
277
- elementTypes.map(async (type) => {
278
- const routeElement = routeMatch.options[type]
284
+ await Promise.all(
285
+ componentTypes.map(async (type) => {
286
+ const component = routeMatch.options[type]
279
287
 
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,
288
+ if (routeMatch.__[type]?.preload) {
289
+ routeMatch.__[type] = await router.options.loadComponent!(
290
+ component,
306
291
  )
307
292
  }
293
+ }),
294
+ )
295
+ })()
296
+
297
+ dataPromise = Promise.resolve().then(async () => {
298
+ try {
299
+ if (routeMatch.options.loader) {
300
+ const data = await router.loadMatchData(routeMatch)
301
+ await checkLatest()
302
+
303
+ setLoaderData(data)
304
+ }
308
305
 
309
- routeMatch.error = undefined
310
- routeMatch.status = 'success'
311
- routeMatch.updatedAt = Date.now()
312
- routeMatch.invalidAt =
313
- routeMatch.updatedAt +
306
+ setStore((s) => {
307
+ s.error = undefined
308
+ s.status = 'success'
309
+ s.updatedAt = Date.now()
310
+ s.invalidAt =
311
+ s.updatedAt +
314
312
  (opts?.maxAge ??
315
313
  routeMatch.options.loaderMaxAge ??
316
314
  router.options.defaultLoaderMaxAge ??
317
315
  0)
318
- } catch (err) {
319
- if (id !== routeMatch.__.latestId) {
320
- return routeMatch.__.loaderPromise
321
- }
316
+ })
322
317
 
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
- })
318
+ return store.routeLoaderData
319
+ } catch (err) {
320
+ await checkLatest()
331
321
 
332
- try {
333
- await Promise.all([
334
- routeMatch.__.elementsPromise,
335
- routeMatch.__.dataPromise,
336
- ])
337
- if (id !== routeMatch.__.latestId) {
338
- return routeMatch.__.loaderPromise
322
+ if (process.env.NODE_ENV !== 'production') {
323
+ console.error(err)
339
324
  }
340
325
 
341
- if (routeMatch.__.pendingMinPromise) {
342
- await routeMatch.__.pendingMinPromise
343
- delete routeMatch.__.pendingMinPromise
344
- }
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()
326
+ setStore((s) => {
327
+ s.error = err
328
+ s.status = 'error'
329
+ s.updatedAt = Date.now()
330
+ })
331
+
332
+ throw err
353
333
  }
354
- })()
334
+ })
355
335
 
356
- routeMatch.__.loaderPromise = loaderPromise
357
- await loaderPromise
336
+ const after = async () => {
337
+ await checkLatest()
338
+ setStore((s) => (s.isFetching = false))
339
+ delete routeMatch.__.loadPromise
340
+ resolve()
341
+ }
358
342
 
359
- if (id !== routeMatch.__.latestId) {
360
- return routeMatch.__.loaderPromise
343
+ try {
344
+ await Promise.all([componentsPromise, dataPromise.catch(() => {})])
345
+ after()
346
+ } catch {
347
+ after()
361
348
  }
362
- delete routeMatch.__.loaderPromise
363
349
  })
364
350
 
365
- return await routeMatch.__.loadPromise
351
+ await routeMatch.__.loadPromise
352
+ await checkLatest()
366
353
  },
367
354
  }
368
355
 
369
356
  if (!routeMatch.hasLoaders()) {
370
- routeMatch.status = 'success'
357
+ setStore((s) => (s.status = 'success'))
371
358
  }
372
359
 
373
360
  return routeMatch