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