@tanstack/router-core 0.0.1-alpha.5 → 0.0.1-alpha.7
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/index.js +33 -1451
- package/build/cjs/packages/router-core/src/index.js.map +1 -1
- package/build/cjs/packages/router-core/src/path.js +222 -0
- package/build/cjs/packages/router-core/src/path.js.map +1 -0
- package/build/cjs/packages/router-core/src/qss.js +1 -1
- package/build/cjs/packages/router-core/src/qss.js.map +1 -1
- package/build/cjs/packages/router-core/src/route.js +126 -0
- package/build/cjs/packages/router-core/src/route.js.map +1 -0
- package/build/cjs/packages/router-core/src/routeConfig.js +69 -0
- package/build/cjs/packages/router-core/src/routeConfig.js.map +1 -0
- package/build/cjs/packages/router-core/src/routeMatch.js +260 -0
- package/build/cjs/packages/router-core/src/routeMatch.js.map +1 -0
- package/build/cjs/packages/router-core/src/router.js +787 -0
- package/build/cjs/packages/router-core/src/router.js.map +1 -0
- package/build/cjs/packages/router-core/src/searchParams.js +70 -0
- package/build/cjs/packages/router-core/src/searchParams.js.map +1 -0
- package/build/cjs/packages/router-core/src/utils.js +118 -0
- package/build/cjs/packages/router-core/src/utils.js.map +1 -0
- package/build/esm/index.js +1304 -1238
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +374 -57
- package/build/types/index.d.ts +361 -333
- package/build/umd/index.development.js +1313 -1238
- 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 +2 -3
- package/src/frameworks.ts +13 -0
- package/src/index.ts +15 -3054
- package/src/link.ts +289 -0
- package/src/path.ts +236 -0
- package/src/qss.ts +1 -1
- package/src/route.ts +181 -0
- package/src/routeConfig.ts +523 -0
- package/src/routeInfo.ts +228 -0
- package/src/routeMatch.ts +357 -0
- package/src/router.ts +1182 -0
- package/src/searchParams.ts +54 -0
- package/src/utils.ts +157 -0
package/src/routeInfo.ts
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { ParsePathParams } from './link'
|
|
2
|
+
import { Route } from './route'
|
|
3
|
+
import {
|
|
4
|
+
AnyLoaderData,
|
|
5
|
+
AnyPathParams,
|
|
6
|
+
AnyRouteConfig,
|
|
7
|
+
AnyRouteConfigWithChildren,
|
|
8
|
+
AnySearchSchema,
|
|
9
|
+
RootRouteId,
|
|
10
|
+
RouteConfig,
|
|
11
|
+
RouteOptions,
|
|
12
|
+
} from './routeConfig'
|
|
13
|
+
import { IsAny, Values } from './utils'
|
|
14
|
+
|
|
15
|
+
export interface AnyAllRouteInfo {
|
|
16
|
+
routeConfig: AnyRouteConfig
|
|
17
|
+
routeInfo: AnyRouteInfo
|
|
18
|
+
routeInfoById: Record<string, AnyRouteInfo>
|
|
19
|
+
routeInfoByFullPath: Record<string, AnyRouteInfo>
|
|
20
|
+
routeIds: any
|
|
21
|
+
routePaths: any
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DefaultAllRouteInfo {
|
|
25
|
+
routeConfig: RouteConfig
|
|
26
|
+
routeInfo: RouteInfo
|
|
27
|
+
routeInfoById: Record<string, RouteInfo>
|
|
28
|
+
routeInfoByFullPath: Record<string, RouteInfo>
|
|
29
|
+
routeIds: string
|
|
30
|
+
routePaths: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface AllRouteInfo<TRouteConfig extends AnyRouteConfig = RouteConfig>
|
|
34
|
+
extends RoutesInfoInner<TRouteConfig, ParseRouteConfig<TRouteConfig>> {}
|
|
35
|
+
|
|
36
|
+
export type ParseRouteConfig<TRouteConfig = AnyRouteConfig> =
|
|
37
|
+
TRouteConfig extends AnyRouteConfig
|
|
38
|
+
? RouteConfigRoute<TRouteConfig> | ParseRouteChildren<TRouteConfig>
|
|
39
|
+
: never
|
|
40
|
+
|
|
41
|
+
type ParseRouteChildren<TRouteConfig> =
|
|
42
|
+
TRouteConfig extends AnyRouteConfigWithChildren<infer TChildren>
|
|
43
|
+
? unknown extends TChildren
|
|
44
|
+
? never
|
|
45
|
+
: TChildren extends AnyRouteConfig[]
|
|
46
|
+
? Values<{
|
|
47
|
+
[TId in TChildren[number]['id']]: ParseRouteChild<
|
|
48
|
+
TChildren[number],
|
|
49
|
+
TId
|
|
50
|
+
>
|
|
51
|
+
}>
|
|
52
|
+
: never // Children are not routes
|
|
53
|
+
: never // No children
|
|
54
|
+
|
|
55
|
+
type ParseRouteChild<TRouteConfig, TId> = TRouteConfig & {
|
|
56
|
+
id: TId
|
|
57
|
+
} extends AnyRouteConfig
|
|
58
|
+
? ParseRouteConfig<TRouteConfig>
|
|
59
|
+
: never
|
|
60
|
+
|
|
61
|
+
export type RouteConfigRoute<TRouteConfig> = TRouteConfig extends RouteConfig<
|
|
62
|
+
infer TId,
|
|
63
|
+
infer TRouteId,
|
|
64
|
+
infer TPath,
|
|
65
|
+
infer TFullPath,
|
|
66
|
+
infer TRouteLoaderData,
|
|
67
|
+
infer TLoaderData,
|
|
68
|
+
infer TActionPayload,
|
|
69
|
+
infer TActionResponse,
|
|
70
|
+
infer TParentSearchSchema,
|
|
71
|
+
infer TSearchSchema,
|
|
72
|
+
infer TFullSearchSchema,
|
|
73
|
+
infer TParentParams,
|
|
74
|
+
infer TParams,
|
|
75
|
+
infer TAllParams,
|
|
76
|
+
any
|
|
77
|
+
>
|
|
78
|
+
? string extends TRouteId
|
|
79
|
+
? never
|
|
80
|
+
: RouteInfo<
|
|
81
|
+
TId,
|
|
82
|
+
TRouteId,
|
|
83
|
+
TPath,
|
|
84
|
+
TFullPath,
|
|
85
|
+
TRouteLoaderData,
|
|
86
|
+
TLoaderData,
|
|
87
|
+
TActionPayload,
|
|
88
|
+
TActionResponse,
|
|
89
|
+
TParentSearchSchema,
|
|
90
|
+
TSearchSchema,
|
|
91
|
+
TFullSearchSchema,
|
|
92
|
+
TParentParams,
|
|
93
|
+
TParams,
|
|
94
|
+
TAllParams
|
|
95
|
+
>
|
|
96
|
+
: never
|
|
97
|
+
|
|
98
|
+
export interface RoutesInfoInner<
|
|
99
|
+
TRouteConfig extends AnyRouteConfig,
|
|
100
|
+
TRouteInfo extends RouteInfo<
|
|
101
|
+
string,
|
|
102
|
+
string,
|
|
103
|
+
any,
|
|
104
|
+
any,
|
|
105
|
+
any,
|
|
106
|
+
any,
|
|
107
|
+
any,
|
|
108
|
+
any,
|
|
109
|
+
any,
|
|
110
|
+
any,
|
|
111
|
+
any,
|
|
112
|
+
any,
|
|
113
|
+
any,
|
|
114
|
+
any
|
|
115
|
+
> = RouteInfo,
|
|
116
|
+
TRouteInfoById = {
|
|
117
|
+
[TInfo in TRouteInfo as TInfo['id']]: TInfo
|
|
118
|
+
},
|
|
119
|
+
TRouteInfoByFullPath = {
|
|
120
|
+
[TInfo in TRouteInfo as TInfo['fullPath'] extends RootRouteId
|
|
121
|
+
? never
|
|
122
|
+
: string extends TInfo['fullPath']
|
|
123
|
+
? never
|
|
124
|
+
: TInfo['fullPath']]: TInfo
|
|
125
|
+
},
|
|
126
|
+
> {
|
|
127
|
+
routeConfig: TRouteConfig
|
|
128
|
+
routeInfo: TRouteInfo
|
|
129
|
+
routeInfoById: TRouteInfoById
|
|
130
|
+
routeInfoByFullPath: TRouteInfoByFullPath
|
|
131
|
+
routeIds: keyof TRouteInfoById
|
|
132
|
+
routePaths: keyof TRouteInfoByFullPath
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface AnyRouteInfo
|
|
136
|
+
extends RouteInfo<
|
|
137
|
+
any,
|
|
138
|
+
any,
|
|
139
|
+
any,
|
|
140
|
+
any,
|
|
141
|
+
any,
|
|
142
|
+
any,
|
|
143
|
+
any,
|
|
144
|
+
any,
|
|
145
|
+
any,
|
|
146
|
+
any,
|
|
147
|
+
any,
|
|
148
|
+
any,
|
|
149
|
+
any,
|
|
150
|
+
any
|
|
151
|
+
> {}
|
|
152
|
+
|
|
153
|
+
export interface RouteInfo<
|
|
154
|
+
TId extends string = string,
|
|
155
|
+
TRouteId extends string = string,
|
|
156
|
+
TPath extends string = string,
|
|
157
|
+
TFullPath extends string = string,
|
|
158
|
+
TRouteLoaderData extends AnyLoaderData = {},
|
|
159
|
+
TLoaderData extends AnyLoaderData = {},
|
|
160
|
+
TActionPayload = unknown,
|
|
161
|
+
TActionResponse = unknown,
|
|
162
|
+
TParentSearchSchema extends {} = {},
|
|
163
|
+
TSearchSchema extends AnySearchSchema = {},
|
|
164
|
+
TFullSearchSchema extends AnySearchSchema = {},
|
|
165
|
+
TParentParams extends AnyPathParams = {},
|
|
166
|
+
TParams extends Record<ParsePathParams<TPath>, unknown> = Record<
|
|
167
|
+
ParsePathParams<TPath>,
|
|
168
|
+
string
|
|
169
|
+
>,
|
|
170
|
+
TAllParams extends AnyPathParams = {},
|
|
171
|
+
> {
|
|
172
|
+
id: TId
|
|
173
|
+
routeId: TRouteId
|
|
174
|
+
path: TPath
|
|
175
|
+
fullPath: TFullPath
|
|
176
|
+
routeLoaderData: TRouteLoaderData
|
|
177
|
+
loaderData: TLoaderData
|
|
178
|
+
actionPayload: TActionPayload
|
|
179
|
+
actionResponse: TActionResponse
|
|
180
|
+
searchSchema: TSearchSchema
|
|
181
|
+
fullSearchSchema: TFullSearchSchema
|
|
182
|
+
parentParams: TParentParams
|
|
183
|
+
params: TParams
|
|
184
|
+
allParams: TAllParams
|
|
185
|
+
options: RouteOptions<
|
|
186
|
+
TRouteId,
|
|
187
|
+
TPath,
|
|
188
|
+
TRouteLoaderData,
|
|
189
|
+
TLoaderData,
|
|
190
|
+
TActionPayload,
|
|
191
|
+
TActionResponse,
|
|
192
|
+
TParentSearchSchema,
|
|
193
|
+
TSearchSchema,
|
|
194
|
+
TFullSearchSchema,
|
|
195
|
+
TParentParams,
|
|
196
|
+
TParams,
|
|
197
|
+
TAllParams
|
|
198
|
+
>
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export type RoutesById<TAllRouteInfo extends AnyAllRouteInfo> = {
|
|
202
|
+
[K in keyof TAllRouteInfo['routeInfoById']]: Route<
|
|
203
|
+
TAllRouteInfo,
|
|
204
|
+
TAllRouteInfo['routeInfoById'][K]
|
|
205
|
+
>
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type RouteInfoById<
|
|
209
|
+
TAllRouteInfo extends AnyAllRouteInfo,
|
|
210
|
+
TId,
|
|
211
|
+
> = TId extends keyof TAllRouteInfo['routeInfoById']
|
|
212
|
+
? IsAny<
|
|
213
|
+
TAllRouteInfo['routeInfoById'][TId]['id'],
|
|
214
|
+
RouteInfo,
|
|
215
|
+
TAllRouteInfo['routeInfoById'][TId]
|
|
216
|
+
>
|
|
217
|
+
: never
|
|
218
|
+
|
|
219
|
+
export type RouteInfoByPath<
|
|
220
|
+
TAllRouteInfo extends AnyAllRouteInfo,
|
|
221
|
+
TPath,
|
|
222
|
+
> = TPath extends keyof TAllRouteInfo['routeInfoByFullPath']
|
|
223
|
+
? IsAny<
|
|
224
|
+
TAllRouteInfo['routeInfoByFullPath'][TPath]['id'],
|
|
225
|
+
RouteInfo,
|
|
226
|
+
TAllRouteInfo['routeInfoByFullPath'][TPath]
|
|
227
|
+
>
|
|
228
|
+
: never
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { GetFrameworkGeneric } from './frameworks'
|
|
2
|
+
import { Route } from './route'
|
|
3
|
+
import { AnyPathParams } from './routeConfig'
|
|
4
|
+
import {
|
|
5
|
+
AnyAllRouteInfo,
|
|
6
|
+
AnyRouteInfo,
|
|
7
|
+
DefaultAllRouteInfo,
|
|
8
|
+
RouteInfo,
|
|
9
|
+
} from './routeInfo'
|
|
10
|
+
import { Router } from './router'
|
|
11
|
+
import { replaceEqualDeep, Timeout } from './utils'
|
|
12
|
+
|
|
13
|
+
export interface RouteMatch<
|
|
14
|
+
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
15
|
+
TRouteInfo extends AnyRouteInfo = RouteInfo,
|
|
16
|
+
> extends Route<TAllRouteInfo, TRouteInfo> {
|
|
17
|
+
matchId: string
|
|
18
|
+
pathname: string
|
|
19
|
+
params: AnyPathParams
|
|
20
|
+
parentMatch?: RouteMatch
|
|
21
|
+
childMatches: RouteMatch[]
|
|
22
|
+
routeSearch: TRouteInfo['searchSchema']
|
|
23
|
+
search: TRouteInfo['fullSearchSchema']
|
|
24
|
+
status: 'idle' | 'loading' | 'success' | 'error'
|
|
25
|
+
updatedAt?: number
|
|
26
|
+
error?: unknown
|
|
27
|
+
isInvalid: boolean
|
|
28
|
+
getIsInvalid: () => boolean
|
|
29
|
+
loaderData: TRouteInfo['loaderData']
|
|
30
|
+
routeLoaderData: TRouteInfo['routeLoaderData']
|
|
31
|
+
isFetching: boolean
|
|
32
|
+
isPending: boolean
|
|
33
|
+
__: {
|
|
34
|
+
element?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
|
|
35
|
+
errorElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
|
|
36
|
+
catchElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
|
|
37
|
+
pendingElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
|
|
38
|
+
loadPromise?: Promise<void>
|
|
39
|
+
loaderPromise?: Promise<void>
|
|
40
|
+
importPromise?: 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: () => Promise<void>
|
|
64
|
+
invalidate: () => void
|
|
65
|
+
hasLoaders: () => boolean
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const elementTypes = [
|
|
69
|
+
'element',
|
|
70
|
+
'errorElement',
|
|
71
|
+
'catchElement',
|
|
72
|
+
'pendingElement',
|
|
73
|
+
] as const
|
|
74
|
+
|
|
75
|
+
export function createRouteMatch<
|
|
76
|
+
TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
|
|
77
|
+
TRouteInfo extends AnyRouteInfo = RouteInfo,
|
|
78
|
+
>(
|
|
79
|
+
router: Router<any, any>,
|
|
80
|
+
route: Route<TAllRouteInfo, TRouteInfo>,
|
|
81
|
+
opts: {
|
|
82
|
+
matchId: string
|
|
83
|
+
params: TRouteInfo['allParams']
|
|
84
|
+
pathname: string
|
|
85
|
+
},
|
|
86
|
+
): RouteMatch<TAllRouteInfo, TRouteInfo> {
|
|
87
|
+
const routeMatch: RouteMatch<TAllRouteInfo, TRouteInfo> = {
|
|
88
|
+
...route,
|
|
89
|
+
...opts,
|
|
90
|
+
router,
|
|
91
|
+
routeSearch: {},
|
|
92
|
+
search: {},
|
|
93
|
+
childMatches: [],
|
|
94
|
+
status: 'idle',
|
|
95
|
+
routeLoaderData: {} as TRouteInfo['routeLoaderData'],
|
|
96
|
+
loaderData: {} as TRouteInfo['loaderData'],
|
|
97
|
+
isPending: false,
|
|
98
|
+
isFetching: false,
|
|
99
|
+
isInvalid: false,
|
|
100
|
+
getIsInvalid: () => {
|
|
101
|
+
const now = Date.now()
|
|
102
|
+
const maxAge =
|
|
103
|
+
routeMatch.options.loaderMaxAge ??
|
|
104
|
+
router.options.defaultLoaderMaxAge ??
|
|
105
|
+
0
|
|
106
|
+
return routeMatch.isInvalid || routeMatch.updatedAt! + maxAge < now
|
|
107
|
+
},
|
|
108
|
+
__: {
|
|
109
|
+
abortController: new AbortController(),
|
|
110
|
+
latestId: '',
|
|
111
|
+
resolve: () => {},
|
|
112
|
+
notify: () => {
|
|
113
|
+
routeMatch.__.resolve()
|
|
114
|
+
routeMatch.router.notify()
|
|
115
|
+
},
|
|
116
|
+
startPending: () => {
|
|
117
|
+
const pendingMs =
|
|
118
|
+
routeMatch.options.pendingMs ?? router.options.defaultPendingMs
|
|
119
|
+
const pendingMinMs =
|
|
120
|
+
routeMatch.options.pendingMinMs ?? router.options.defaultPendingMinMs
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
routeMatch.__.pendingTimeout ||
|
|
124
|
+
routeMatch.status !== 'loading' ||
|
|
125
|
+
typeof pendingMs === 'undefined'
|
|
126
|
+
) {
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
routeMatch.__.pendingTimeout = setTimeout(() => {
|
|
131
|
+
routeMatch.isPending = true
|
|
132
|
+
routeMatch.__.resolve()
|
|
133
|
+
if (typeof pendingMinMs !== 'undefined') {
|
|
134
|
+
routeMatch.__.pendingMinPromise = new Promise(
|
|
135
|
+
(r) =>
|
|
136
|
+
(routeMatch.__.pendingMinTimeout = setTimeout(r, pendingMinMs)),
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
}, pendingMs)
|
|
140
|
+
},
|
|
141
|
+
cancelPending: () => {
|
|
142
|
+
routeMatch.isPending = false
|
|
143
|
+
clearTimeout(routeMatch.__.pendingTimeout)
|
|
144
|
+
clearTimeout(routeMatch.__.pendingMinTimeout)
|
|
145
|
+
delete routeMatch.__.pendingMinPromise
|
|
146
|
+
},
|
|
147
|
+
// setParentMatch: (parentMatch?: RouteMatch) => {
|
|
148
|
+
// routeMatch.parentMatch = parentMatch
|
|
149
|
+
// },
|
|
150
|
+
// addChildMatch: (childMatch: RouteMatch) => {
|
|
151
|
+
// if (
|
|
152
|
+
// routeMatch.childMatches.find((d) => d.matchId === childMatch.matchId)
|
|
153
|
+
// ) {
|
|
154
|
+
// return
|
|
155
|
+
// }
|
|
156
|
+
|
|
157
|
+
// routeMatch.childMatches.push(childMatch)
|
|
158
|
+
// },
|
|
159
|
+
validate: () => {
|
|
160
|
+
// Validate the search params and stabilize them
|
|
161
|
+
const parentSearch =
|
|
162
|
+
routeMatch.parentMatch?.search ?? router.location.search
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const prevSearch = routeMatch.routeSearch
|
|
166
|
+
|
|
167
|
+
const validator =
|
|
168
|
+
typeof routeMatch.options.validateSearch === 'object'
|
|
169
|
+
? routeMatch.options.validateSearch.parse
|
|
170
|
+
: routeMatch.options.validateSearch
|
|
171
|
+
|
|
172
|
+
let nextSearch = replaceEqualDeep(
|
|
173
|
+
prevSearch,
|
|
174
|
+
validator?.(parentSearch),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
// Invalidate route matches when search param stability changes
|
|
178
|
+
if (prevSearch !== nextSearch) {
|
|
179
|
+
routeMatch.isInvalid = true
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
routeMatch.routeSearch = nextSearch
|
|
183
|
+
|
|
184
|
+
routeMatch.search = replaceEqualDeep(parentSearch, {
|
|
185
|
+
...parentSearch,
|
|
186
|
+
...nextSearch,
|
|
187
|
+
})
|
|
188
|
+
} catch (err: any) {
|
|
189
|
+
console.error(err)
|
|
190
|
+
const error = new (Error as any)('Invalid search params found', {
|
|
191
|
+
cause: err,
|
|
192
|
+
})
|
|
193
|
+
error.code = 'INVALID_SEARCH_PARAMS'
|
|
194
|
+
routeMatch.status = 'error'
|
|
195
|
+
routeMatch.error = error
|
|
196
|
+
// Do not proceed with loading the route
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
cancel: () => {
|
|
202
|
+
routeMatch.__.abortController?.abort()
|
|
203
|
+
routeMatch.__.cancelPending()
|
|
204
|
+
},
|
|
205
|
+
invalidate: () => {
|
|
206
|
+
routeMatch.isInvalid = true
|
|
207
|
+
},
|
|
208
|
+
hasLoaders: () => {
|
|
209
|
+
return !!(
|
|
210
|
+
route.options.loader ||
|
|
211
|
+
route.options.import ||
|
|
212
|
+
elementTypes.some((d) => typeof route.options[d] === 'function')
|
|
213
|
+
)
|
|
214
|
+
},
|
|
215
|
+
load: async () => {
|
|
216
|
+
const id = '' + Date.now() + Math.random()
|
|
217
|
+
routeMatch.__.latestId = id
|
|
218
|
+
|
|
219
|
+
// If the match was in an error state, set it
|
|
220
|
+
// to a loading state again. Otherwise, keep it
|
|
221
|
+
// as loading or resolved
|
|
222
|
+
if (routeMatch.status === 'idle') {
|
|
223
|
+
routeMatch.status = 'loading'
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// We started loading the route, so it's no longer invalid
|
|
227
|
+
routeMatch.isInvalid = false
|
|
228
|
+
|
|
229
|
+
routeMatch.__.loadPromise = new Promise(async (resolve) => {
|
|
230
|
+
// We are now fetching, even if it's in the background of a
|
|
231
|
+
// resolved state
|
|
232
|
+
routeMatch.isFetching = true
|
|
233
|
+
routeMatch.__.resolve = resolve as () => void
|
|
234
|
+
|
|
235
|
+
const loaderPromise = (async () => {
|
|
236
|
+
const importer = routeMatch.options.import
|
|
237
|
+
|
|
238
|
+
// First, run any importers
|
|
239
|
+
if (importer) {
|
|
240
|
+
routeMatch.__.importPromise = importer({
|
|
241
|
+
params: routeMatch.params,
|
|
242
|
+
// search: routeMatch.search,
|
|
243
|
+
}).then((imported) => {
|
|
244
|
+
routeMatch.__ = {
|
|
245
|
+
...routeMatch.__,
|
|
246
|
+
...imported,
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Wait for the importer to finish before
|
|
252
|
+
// attempting to load elements and data
|
|
253
|
+
await routeMatch.__.importPromise
|
|
254
|
+
|
|
255
|
+
// Next, load the elements and data in parallel
|
|
256
|
+
|
|
257
|
+
routeMatch.__.elementsPromise = (async () => {
|
|
258
|
+
// then run all element and data loaders in parallel
|
|
259
|
+
// For each element type, potentially load it asynchronously
|
|
260
|
+
|
|
261
|
+
await Promise.all(
|
|
262
|
+
elementTypes.map(async (type) => {
|
|
263
|
+
const routeElement = routeMatch.options[type]
|
|
264
|
+
|
|
265
|
+
if (routeMatch.__[type]) {
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (typeof routeElement === 'function') {
|
|
270
|
+
const res = await (routeElement as any)(routeMatch)
|
|
271
|
+
|
|
272
|
+
routeMatch.__[type] = res
|
|
273
|
+
} else {
|
|
274
|
+
routeMatch.__[type] = routeMatch.options[type] as any
|
|
275
|
+
}
|
|
276
|
+
}),
|
|
277
|
+
)
|
|
278
|
+
})()
|
|
279
|
+
|
|
280
|
+
routeMatch.__.dataPromise = Promise.resolve().then(async () => {
|
|
281
|
+
try {
|
|
282
|
+
if (routeMatch.options.loader) {
|
|
283
|
+
const data = await routeMatch.options.loader({
|
|
284
|
+
params: routeMatch.params,
|
|
285
|
+
search: routeMatch.routeSearch,
|
|
286
|
+
signal: routeMatch.__.abortController.signal,
|
|
287
|
+
})
|
|
288
|
+
if (id !== routeMatch.__.latestId) {
|
|
289
|
+
return routeMatch.__.loaderPromise
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
routeMatch.routeLoaderData = replaceEqualDeep(
|
|
293
|
+
routeMatch.routeLoaderData,
|
|
294
|
+
data,
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
routeMatch.error = undefined
|
|
299
|
+
routeMatch.status = 'success'
|
|
300
|
+
routeMatch.updatedAt = Date.now()
|
|
301
|
+
} catch (err) {
|
|
302
|
+
if (id !== routeMatch.__.latestId) {
|
|
303
|
+
return routeMatch.__.loaderPromise
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
307
|
+
console.error(err)
|
|
308
|
+
}
|
|
309
|
+
routeMatch.error = err
|
|
310
|
+
routeMatch.status = 'error'
|
|
311
|
+
routeMatch.updatedAt = Date.now()
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
try {
|
|
316
|
+
await Promise.all([
|
|
317
|
+
routeMatch.__.elementsPromise,
|
|
318
|
+
routeMatch.__.dataPromise,
|
|
319
|
+
])
|
|
320
|
+
if (id !== routeMatch.__.latestId) {
|
|
321
|
+
return routeMatch.__.loaderPromise
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (routeMatch.__.pendingMinPromise) {
|
|
325
|
+
await routeMatch.__.pendingMinPromise
|
|
326
|
+
delete routeMatch.__.pendingMinPromise
|
|
327
|
+
}
|
|
328
|
+
} finally {
|
|
329
|
+
if (id !== routeMatch.__.latestId) {
|
|
330
|
+
return routeMatch.__.loaderPromise
|
|
331
|
+
}
|
|
332
|
+
routeMatch.__.cancelPending()
|
|
333
|
+
routeMatch.isPending = false
|
|
334
|
+
routeMatch.isFetching = false
|
|
335
|
+
routeMatch.__.notify()
|
|
336
|
+
}
|
|
337
|
+
})()
|
|
338
|
+
|
|
339
|
+
routeMatch.__.loaderPromise = loaderPromise
|
|
340
|
+
await loaderPromise
|
|
341
|
+
|
|
342
|
+
if (id !== routeMatch.__.latestId) {
|
|
343
|
+
return routeMatch.__.loaderPromise
|
|
344
|
+
}
|
|
345
|
+
delete routeMatch.__.loaderPromise
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
return await routeMatch.__.loadPromise
|
|
349
|
+
},
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!routeMatch.hasLoaders()) {
|
|
353
|
+
routeMatch.status = 'success'
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return routeMatch
|
|
357
|
+
}
|