@tanstack/router-core 0.0.1-beta.1 → 0.0.1-beta.11
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 +1 -1
- package/build/cjs/packages/router-core/src/qss.js +1 -0
- package/build/cjs/packages/router-core/src/qss.js.map +1 -1
- package/build/cjs/packages/router-core/src/route.js +9 -23
- package/build/cjs/packages/router-core/src/route.js.map +1 -1
- package/build/cjs/packages/router-core/src/routeMatch.js +24 -27
- package/build/cjs/packages/router-core/src/routeMatch.js.map +1 -1
- package/build/cjs/packages/router-core/src/router.js +105 -73
- package/build/cjs/packages/router-core/src/router.js.map +1 -1
- package/build/cjs/packages/router-core/src/utils.js +7 -0
- package/build/cjs/packages/router-core/src/utils.js.map +1 -1
- package/build/esm/index.js +143 -119
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +138 -144
- package/build/types/index.d.ts +85 -80
- package/build/umd/index.development.js +143 -119
- 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 -1
- package/src/qss.ts +1 -0
- package/src/route.ts +10 -26
- package/src/routeMatch.ts +29 -33
- package/src/router.ts +140 -70
- package/src/utils.ts +7 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/router-core",
|
|
3
3
|
"author": "Tanner Linsley",
|
|
4
|
-
"version": "0.0.1-beta.
|
|
4
|
+
"version": "0.0.1-beta.11",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "tanstack/router",
|
|
7
7
|
"homepage": "https://tanstack.com/router",
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"build/**",
|
|
34
34
|
"src"
|
|
35
35
|
],
|
|
36
|
+
"sideEffects": false,
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"react": ">=16",
|
|
38
39
|
"react-dom": ">=16"
|
package/src/qss.ts
CHANGED
package/src/route.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
RouteInfo,
|
|
14
14
|
RouteInfoByPath,
|
|
15
15
|
} from './routeInfo'
|
|
16
|
-
import { RouteMatch } from './routeMatch'
|
|
17
16
|
import {
|
|
18
17
|
Action,
|
|
19
18
|
ActionState,
|
|
@@ -22,7 +21,7 @@ import {
|
|
|
22
21
|
MatchRouteOptions,
|
|
23
22
|
Router,
|
|
24
23
|
} from './router'
|
|
25
|
-
import { NoInfer
|
|
24
|
+
import { NoInfer } from './utils'
|
|
26
25
|
|
|
27
26
|
export interface AnyRoute extends Route<any, any> {}
|
|
28
27
|
|
|
@@ -96,10 +95,10 @@ export function createRoute<
|
|
|
96
95
|
router.state.actions[id] ||
|
|
97
96
|
(() => {
|
|
98
97
|
router.state.actions[id] = {
|
|
99
|
-
|
|
98
|
+
submissions: [],
|
|
100
99
|
submit: async <T, U>(
|
|
101
100
|
submission: T,
|
|
102
|
-
actionOpts?: { invalidate?: boolean },
|
|
101
|
+
actionOpts?: { invalidate?: boolean; multi?: boolean },
|
|
103
102
|
) => {
|
|
104
103
|
if (!route) {
|
|
105
104
|
return
|
|
@@ -107,27 +106,27 @@ export function createRoute<
|
|
|
107
106
|
|
|
108
107
|
const invalidate = actionOpts?.invalidate ?? true
|
|
109
108
|
|
|
109
|
+
if (!actionOpts?.multi) {
|
|
110
|
+
action.submissions = action.submissions.filter((d) => d.isMulti)
|
|
111
|
+
}
|
|
112
|
+
|
|
110
113
|
const actionState: ActionState<T, U> = {
|
|
111
114
|
submittedAt: Date.now(),
|
|
112
115
|
status: 'pending',
|
|
113
116
|
submission,
|
|
117
|
+
isMulti: !!actionOpts?.multi,
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
action.current = actionState
|
|
117
121
|
action.latest = actionState
|
|
118
|
-
action.
|
|
119
|
-
|
|
120
|
-
router.state = {
|
|
121
|
-
...router.state,
|
|
122
|
-
currentAction: actionState,
|
|
123
|
-
latestAction: actionState,
|
|
124
|
-
}
|
|
122
|
+
action.submissions.push(actionState)
|
|
125
123
|
|
|
126
124
|
router.notify()
|
|
127
125
|
|
|
128
126
|
try {
|
|
129
127
|
const res = await route.options.action?.(submission)
|
|
130
128
|
actionState.data = res as U
|
|
129
|
+
|
|
131
130
|
if (invalidate) {
|
|
132
131
|
router.invalidateRoute({ to: '.', fromCurrent: true })
|
|
133
132
|
await router.reload()
|
|
@@ -139,8 +138,6 @@ export function createRoute<
|
|
|
139
138
|
actionState.error = err
|
|
140
139
|
actionState.status = 'error'
|
|
141
140
|
} finally {
|
|
142
|
-
action.pending = action.pending.filter((d) => d !== actionState)
|
|
143
|
-
router.removeActionQueue.push({ action, actionState })
|
|
144
141
|
router.notify()
|
|
145
142
|
}
|
|
146
143
|
},
|
|
@@ -228,16 +225,3 @@ export function createRoute<
|
|
|
228
225
|
|
|
229
226
|
return route
|
|
230
227
|
}
|
|
231
|
-
|
|
232
|
-
export function cascadeLoaderData(matches: RouteMatch<any, any>[]) {
|
|
233
|
-
matches.forEach((match, index) => {
|
|
234
|
-
const parent = matches[index - 1]
|
|
235
|
-
|
|
236
|
-
if (parent) {
|
|
237
|
-
match.loaderData = replaceEqualDeep(match.loaderData, {
|
|
238
|
-
...parent.loaderData,
|
|
239
|
-
...match.routeLoaderData,
|
|
240
|
-
})
|
|
241
|
-
}
|
|
242
|
-
})
|
|
243
|
-
}
|
package/src/routeMatch.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
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'
|
|
9
|
+
import { ActionState, Router } from './router'
|
|
11
10
|
import { replaceEqualDeep, Timeout } from './utils'
|
|
12
11
|
|
|
13
12
|
export interface RouteMatch<
|
|
@@ -37,7 +36,7 @@ export interface RouteMatch<
|
|
|
37
36
|
catchElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
|
|
38
37
|
pendingElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
|
|
39
38
|
loadPromise?: Promise<void>
|
|
40
|
-
|
|
39
|
+
loaderDataPromise?: Promise<void>
|
|
41
40
|
elementsPromise?: Promise<void>
|
|
42
41
|
dataPromise?: Promise<void>
|
|
43
42
|
pendingTimeout?: Timeout
|
|
@@ -104,6 +103,7 @@ export function createRouteMatch<
|
|
|
104
103
|
isFetching: false,
|
|
105
104
|
isInvalid: false,
|
|
106
105
|
invalidAt: Infinity,
|
|
106
|
+
// pendingActions: [],
|
|
107
107
|
getIsInvalid: () => {
|
|
108
108
|
const now = Date.now()
|
|
109
109
|
return routeMatch.isInvalid || routeMatch.invalidAt < now
|
|
@@ -147,18 +147,6 @@ export function createRouteMatch<
|
|
|
147
147
|
clearTimeout(routeMatch.__.pendingMinTimeout)
|
|
148
148
|
delete routeMatch.__.pendingMinPromise
|
|
149
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
150
|
validate: () => {
|
|
163
151
|
// Validate the search params and stabilize them
|
|
164
152
|
const parentSearch =
|
|
@@ -174,7 +162,7 @@ export function createRouteMatch<
|
|
|
174
162
|
|
|
175
163
|
let nextSearch = replaceEqualDeep(
|
|
176
164
|
prevSearch,
|
|
177
|
-
validator?.(parentSearch),
|
|
165
|
+
validator?.(parentSearch) ?? {},
|
|
178
166
|
)
|
|
179
167
|
|
|
180
168
|
// Invalidate route matches when search param stability changes
|
|
@@ -188,6 +176,14 @@ export function createRouteMatch<
|
|
|
188
176
|
...parentSearch,
|
|
189
177
|
...nextSearch,
|
|
190
178
|
})
|
|
179
|
+
|
|
180
|
+
elementTypes.map(async (type) => {
|
|
181
|
+
const routeElement = routeMatch.options[type]
|
|
182
|
+
|
|
183
|
+
if (typeof routeMatch.__[type] !== 'function') {
|
|
184
|
+
routeMatch.__[type] = routeElement
|
|
185
|
+
}
|
|
186
|
+
})
|
|
191
187
|
} catch (err: any) {
|
|
192
188
|
console.error(err)
|
|
193
189
|
const error = new (Error as any)('Invalid search params found', {
|
|
@@ -243,7 +239,7 @@ export function createRouteMatch<
|
|
|
243
239
|
) {
|
|
244
240
|
const maxAge = loaderOpts?.preload ? loaderOpts?.maxAge : undefined
|
|
245
241
|
|
|
246
|
-
routeMatch.fetch({ maxAge })
|
|
242
|
+
await routeMatch.fetch({ maxAge })
|
|
247
243
|
}
|
|
248
244
|
},
|
|
249
245
|
fetch: async (opts) => {
|
|
@@ -266,7 +262,7 @@ export function createRouteMatch<
|
|
|
266
262
|
routeMatch.isFetching = true
|
|
267
263
|
routeMatch.__.resolve = resolve as () => void
|
|
268
264
|
|
|
269
|
-
|
|
265
|
+
routeMatch.__.loaderDataPromise = (async () => {
|
|
270
266
|
// Load the elements and data in parallel
|
|
271
267
|
|
|
272
268
|
routeMatch.__.elementsPromise = (async () => {
|
|
@@ -277,13 +273,11 @@ export function createRouteMatch<
|
|
|
277
273
|
elementTypes.map(async (type) => {
|
|
278
274
|
const routeElement = routeMatch.options[type]
|
|
279
275
|
|
|
280
|
-
if (routeMatch.__[type]) {
|
|
281
|
-
|
|
276
|
+
if (typeof routeMatch.__[type] === 'function') {
|
|
277
|
+
routeMatch.__[type] = await router.options.createElement!(
|
|
278
|
+
routeElement,
|
|
279
|
+
)
|
|
282
280
|
}
|
|
283
|
-
|
|
284
|
-
routeMatch.__[type] = await router.options.createElement!(
|
|
285
|
-
routeElement,
|
|
286
|
-
)
|
|
287
281
|
}),
|
|
288
282
|
)
|
|
289
283
|
})()
|
|
@@ -297,7 +291,7 @@ export function createRouteMatch<
|
|
|
297
291
|
signal: routeMatch.__.abortController.signal,
|
|
298
292
|
})
|
|
299
293
|
if (id !== routeMatch.__.latestId) {
|
|
300
|
-
return routeMatch.__.
|
|
294
|
+
return routeMatch.__.loadPromise
|
|
301
295
|
}
|
|
302
296
|
|
|
303
297
|
routeMatch.routeLoaderData = replaceEqualDeep(
|
|
@@ -317,7 +311,7 @@ export function createRouteMatch<
|
|
|
317
311
|
0)
|
|
318
312
|
} catch (err) {
|
|
319
313
|
if (id !== routeMatch.__.latestId) {
|
|
320
|
-
return routeMatch.__.
|
|
314
|
+
return routeMatch.__.loadPromise
|
|
321
315
|
}
|
|
322
316
|
|
|
323
317
|
if (process.env.NODE_ENV !== 'production') {
|
|
@@ -335,7 +329,7 @@ export function createRouteMatch<
|
|
|
335
329
|
routeMatch.__.dataPromise,
|
|
336
330
|
])
|
|
337
331
|
if (id !== routeMatch.__.latestId) {
|
|
338
|
-
return routeMatch.__.
|
|
332
|
+
return routeMatch.__.loadPromise
|
|
339
333
|
}
|
|
340
334
|
|
|
341
335
|
if (routeMatch.__.pendingMinPromise) {
|
|
@@ -344,7 +338,7 @@ export function createRouteMatch<
|
|
|
344
338
|
}
|
|
345
339
|
} finally {
|
|
346
340
|
if (id !== routeMatch.__.latestId) {
|
|
347
|
-
return routeMatch.__.
|
|
341
|
+
return routeMatch.__.loadPromise
|
|
348
342
|
}
|
|
349
343
|
routeMatch.__.cancelPending()
|
|
350
344
|
routeMatch.isPending = false
|
|
@@ -353,16 +347,18 @@ export function createRouteMatch<
|
|
|
353
347
|
}
|
|
354
348
|
})()
|
|
355
349
|
|
|
356
|
-
routeMatch.__.
|
|
357
|
-
await loaderPromise
|
|
350
|
+
await routeMatch.__.loaderDataPromise
|
|
358
351
|
|
|
359
352
|
if (id !== routeMatch.__.latestId) {
|
|
360
|
-
return routeMatch.__.
|
|
353
|
+
return routeMatch.__.loadPromise
|
|
361
354
|
}
|
|
362
|
-
|
|
355
|
+
|
|
356
|
+
delete routeMatch.__.loaderDataPromise
|
|
363
357
|
})
|
|
364
358
|
|
|
365
|
-
|
|
359
|
+
await routeMatch.__.loadPromise
|
|
360
|
+
|
|
361
|
+
delete routeMatch.__.loadPromise
|
|
366
362
|
},
|
|
367
363
|
}
|
|
368
364
|
|
package/src/router.ts
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
matchPathname,
|
|
24
24
|
resolvePath,
|
|
25
25
|
} from './path'
|
|
26
|
-
import { AnyRoute,
|
|
26
|
+
import { AnyRoute, createRoute, Route } from './route'
|
|
27
27
|
import {
|
|
28
28
|
AnyLoaderData,
|
|
29
29
|
AnyPathParams,
|
|
@@ -45,6 +45,7 @@ import { defaultParseSearch, defaultStringifySearch } from './searchParams'
|
|
|
45
45
|
import {
|
|
46
46
|
functionalUpdate,
|
|
47
47
|
last,
|
|
48
|
+
pick,
|
|
48
49
|
PickAsRequired,
|
|
49
50
|
PickRequired,
|
|
50
51
|
replaceEqualDeep,
|
|
@@ -104,9 +105,7 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
104
105
|
createRouter?: (router: Router<any, any>) => void
|
|
105
106
|
createRoute?: (opts: { route: AnyRoute; router: Router<any, any> }) => void
|
|
106
107
|
createElement?: (
|
|
107
|
-
element:
|
|
108
|
-
| GetFrameworkGeneric<'Element'>
|
|
109
|
-
| (() => Promise<GetFrameworkGeneric<'Element'>>),
|
|
108
|
+
element: GetFrameworkGeneric<'SyncOrAsyncElement'>,
|
|
110
109
|
) => Promise<GetFrameworkGeneric<'Element'>>
|
|
111
110
|
}
|
|
112
111
|
|
|
@@ -115,10 +114,13 @@ export interface Action<
|
|
|
115
114
|
TResponse = unknown,
|
|
116
115
|
// TError = unknown,
|
|
117
116
|
> {
|
|
118
|
-
submit: (
|
|
117
|
+
submit: (
|
|
118
|
+
submission?: TPayload,
|
|
119
|
+
actionOpts?: { invalidate?: boolean; multi?: boolean },
|
|
120
|
+
) => Promise<TResponse>
|
|
119
121
|
current?: ActionState<TPayload, TResponse>
|
|
120
122
|
latest?: ActionState<TPayload, TResponse>
|
|
121
|
-
|
|
123
|
+
submissions: ActionState<TPayload, TResponse>[]
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
export interface ActionState<
|
|
@@ -129,6 +131,7 @@ export interface ActionState<
|
|
|
129
131
|
submittedAt: number
|
|
130
132
|
status: 'idle' | 'pending' | 'success' | 'error'
|
|
131
133
|
submission: TPayload
|
|
134
|
+
isMulti: boolean
|
|
132
135
|
data?: TResponse
|
|
133
136
|
error?: unknown
|
|
134
137
|
}
|
|
@@ -174,9 +177,6 @@ export interface RouterState {
|
|
|
174
177
|
location: Location
|
|
175
178
|
matches: RouteMatch[]
|
|
176
179
|
lastUpdated: number
|
|
177
|
-
loaderData: unknown
|
|
178
|
-
currentAction?: ActionState
|
|
179
|
-
latestAction?: ActionState
|
|
180
180
|
actions: Record<string, Action>
|
|
181
181
|
loaders: Record<string, Loader>
|
|
182
182
|
pending?: PendingState
|
|
@@ -189,7 +189,7 @@ export interface PendingState {
|
|
|
189
189
|
matches: RouteMatch[]
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
type Listener = () => void
|
|
192
|
+
type Listener = (router: Router<any, any>) => void
|
|
193
193
|
|
|
194
194
|
export type ListenerFn = () => void
|
|
195
195
|
|
|
@@ -227,6 +227,22 @@ type LinkCurrentTargetElement = {
|
|
|
227
227
|
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
+
interface DehydratedRouterState
|
|
231
|
+
extends Pick<RouterState, 'status' | 'location' | 'lastUpdated'> {
|
|
232
|
+
matches: DehydratedRouteMatch[]
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
interface DehydratedRouteMatch
|
|
236
|
+
extends Pick<
|
|
237
|
+
RouteMatch<any, any>,
|
|
238
|
+
| 'matchId'
|
|
239
|
+
| 'status'
|
|
240
|
+
| 'routeLoaderData'
|
|
241
|
+
| 'loaderData'
|
|
242
|
+
| 'isInvalid'
|
|
243
|
+
| 'invalidAt'
|
|
244
|
+
> {}
|
|
245
|
+
|
|
230
246
|
export interface Router<
|
|
231
247
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
232
248
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
@@ -248,10 +264,10 @@ export interface Router<
|
|
|
248
264
|
routeTree: Route<TAllRouteInfo, RouteInfo>
|
|
249
265
|
routesById: RoutesById<TAllRouteInfo>
|
|
250
266
|
navigationPromise: Promise<void>
|
|
251
|
-
removeActionQueue: { action: Action; actionState: ActionState }[]
|
|
252
267
|
startedLoadingAt: number
|
|
253
268
|
resolveNavigation: () => void
|
|
254
269
|
subscribe: (listener: Listener) => () => void
|
|
270
|
+
reset: () => void
|
|
255
271
|
notify: () => void
|
|
256
272
|
mount: () => () => void
|
|
257
273
|
onFocus: () => void
|
|
@@ -261,7 +277,7 @@ export interface Router<
|
|
|
261
277
|
|
|
262
278
|
buildNext: (opts: BuildNextOptions) => Location
|
|
263
279
|
cancelMatches: () => void
|
|
264
|
-
|
|
280
|
+
load: (next?: Location) => Promise<void>
|
|
265
281
|
matchCache: Record<string, MatchCacheEntry>
|
|
266
282
|
cleanMatchCache: () => void
|
|
267
283
|
getRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
@@ -305,6 +321,8 @@ export interface Router<
|
|
|
305
321
|
>(
|
|
306
322
|
opts: LinkOptions<TAllRouteInfo, TFrom, TTo>,
|
|
307
323
|
) => LinkInfo
|
|
324
|
+
dehydrateState: () => DehydratedRouterState
|
|
325
|
+
hydrateState: (state: DehydratedRouterState) => void
|
|
308
326
|
__: {
|
|
309
327
|
buildRouteTree: (
|
|
310
328
|
routeConfig: RouteConfig,
|
|
@@ -322,13 +340,25 @@ export interface Router<
|
|
|
322
340
|
}
|
|
323
341
|
|
|
324
342
|
// Detect if we're in the DOM
|
|
325
|
-
const isServer =
|
|
326
|
-
typeof window === 'undefined' || !window.document?.createElement
|
|
327
|
-
)
|
|
343
|
+
const isServer =
|
|
344
|
+
typeof window === 'undefined' || !window.document?.createElement
|
|
328
345
|
|
|
329
346
|
// This is the default history object if none is defined
|
|
330
347
|
const createDefaultHistory = () =>
|
|
331
|
-
|
|
348
|
+
isServer ? createMemoryHistory() : createBrowserHistory()
|
|
349
|
+
|
|
350
|
+
function getInitialRouterState(): RouterState {
|
|
351
|
+
return {
|
|
352
|
+
status: 'idle',
|
|
353
|
+
location: null!,
|
|
354
|
+
matches: [],
|
|
355
|
+
actions: {},
|
|
356
|
+
loaders: {},
|
|
357
|
+
lastUpdated: Date.now(),
|
|
358
|
+
isFetching: false,
|
|
359
|
+
isPreloading: false,
|
|
360
|
+
}
|
|
361
|
+
}
|
|
332
362
|
|
|
333
363
|
export function createRouter<
|
|
334
364
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
@@ -352,7 +382,6 @@ export function createRouter<
|
|
|
352
382
|
history,
|
|
353
383
|
options: originalOptions,
|
|
354
384
|
listeners: [],
|
|
355
|
-
removeActionQueue: [],
|
|
356
385
|
// Resolved after construction
|
|
357
386
|
basepath: '',
|
|
358
387
|
routeTree: undefined!,
|
|
@@ -363,16 +392,10 @@ export function createRouter<
|
|
|
363
392
|
navigationPromise: Promise.resolve(),
|
|
364
393
|
resolveNavigation: () => {},
|
|
365
394
|
matchCache: {},
|
|
366
|
-
state:
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
actions: {},
|
|
371
|
-
loaders: {},
|
|
372
|
-
loaderData: {} as any,
|
|
373
|
-
lastUpdated: Date.now(),
|
|
374
|
-
isFetching: false,
|
|
375
|
-
isPreloading: false,
|
|
395
|
+
state: getInitialRouterState(),
|
|
396
|
+
reset: () => {
|
|
397
|
+
router.state = getInitialRouterState()
|
|
398
|
+
router.notify()
|
|
376
399
|
},
|
|
377
400
|
startedLoadingAt: Date.now(),
|
|
378
401
|
subscribe: (listener: Listener): (() => void) => {
|
|
@@ -398,7 +421,47 @@ export function createRouter<
|
|
|
398
421
|
}
|
|
399
422
|
|
|
400
423
|
cascadeLoaderData(router.state.matches)
|
|
401
|
-
router.listeners.forEach((listener) => listener())
|
|
424
|
+
router.listeners.forEach((listener) => listener(router))
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
dehydrateState: () => {
|
|
428
|
+
return {
|
|
429
|
+
...pick(router.state, ['status', 'location', 'lastUpdated']),
|
|
430
|
+
matches: router.state.matches.map((match) =>
|
|
431
|
+
pick(match, [
|
|
432
|
+
'matchId',
|
|
433
|
+
'status',
|
|
434
|
+
'routeLoaderData',
|
|
435
|
+
'loaderData',
|
|
436
|
+
'isInvalid',
|
|
437
|
+
'invalidAt',
|
|
438
|
+
]),
|
|
439
|
+
),
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
hydrateState: (dehydratedState) => {
|
|
444
|
+
// Match the routes
|
|
445
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
446
|
+
strictParseParams: true,
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
matches.forEach((match, index) => {
|
|
450
|
+
const dehydratedMatch = dehydratedState.matches[index]
|
|
451
|
+
invariant(
|
|
452
|
+
dehydratedMatch,
|
|
453
|
+
'Oh no! Dehydrated route matches did not match the active state of the router 😬',
|
|
454
|
+
)
|
|
455
|
+
Object.assign(match, dehydratedMatch)
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
router.loadMatches(matches)
|
|
459
|
+
|
|
460
|
+
router.state = {
|
|
461
|
+
...router.state,
|
|
462
|
+
...dehydratedState,
|
|
463
|
+
matches,
|
|
464
|
+
}
|
|
402
465
|
},
|
|
403
466
|
|
|
404
467
|
mount: () => {
|
|
@@ -412,14 +475,12 @@ export function createRouter<
|
|
|
412
475
|
// to the current location. Otherwise, load the current location.
|
|
413
476
|
if (next.href !== router.location.href) {
|
|
414
477
|
router.__.commitLocation(next, true)
|
|
415
|
-
} else {
|
|
416
|
-
router.loadLocation()
|
|
417
478
|
}
|
|
418
479
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
)
|
|
480
|
+
// router.load()
|
|
481
|
+
|
|
482
|
+
const unsub = router.history.listen((event) => {
|
|
483
|
+
router.load(router.__.parseLocation(event.location, router.location))
|
|
423
484
|
})
|
|
424
485
|
|
|
425
486
|
// addEventListener does not exist in React Native, but window does
|
|
@@ -432,17 +493,28 @@ export function createRouter<
|
|
|
432
493
|
|
|
433
494
|
return () => {
|
|
434
495
|
unsub()
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
496
|
+
if (!isServer && window.removeEventListener) {
|
|
497
|
+
// Be sure to unsubscribe if a new handler is set
|
|
498
|
+
window.removeEventListener('visibilitychange', router.onFocus)
|
|
499
|
+
window.removeEventListener('focus', router.onFocus)
|
|
500
|
+
}
|
|
438
501
|
}
|
|
439
502
|
},
|
|
440
503
|
|
|
441
504
|
onFocus: () => {
|
|
442
|
-
router.
|
|
505
|
+
router.load()
|
|
443
506
|
},
|
|
444
507
|
|
|
445
508
|
update: (opts) => {
|
|
509
|
+
const newHistory = opts?.history !== router.history
|
|
510
|
+
if (!router.location || newHistory) {
|
|
511
|
+
if (opts?.history) {
|
|
512
|
+
router.history = opts.history
|
|
513
|
+
}
|
|
514
|
+
router.location = router.__.parseLocation(router.history.location)
|
|
515
|
+
router.state.location = router.location
|
|
516
|
+
}
|
|
517
|
+
|
|
446
518
|
Object.assign(router.options, opts)
|
|
447
519
|
|
|
448
520
|
const { basepath, routeConfig } = router.options
|
|
@@ -466,7 +538,7 @@ export function createRouter<
|
|
|
466
538
|
})
|
|
467
539
|
},
|
|
468
540
|
|
|
469
|
-
|
|
541
|
+
load: async (next?: Location) => {
|
|
470
542
|
const id = Math.random()
|
|
471
543
|
router.startedLoadingAt = id
|
|
472
544
|
|
|
@@ -475,22 +547,11 @@ export function createRouter<
|
|
|
475
547
|
router.location = next
|
|
476
548
|
}
|
|
477
549
|
|
|
478
|
-
// Clear out old actions
|
|
479
|
-
router.removeActionQueue.forEach(({ action, actionState }) => {
|
|
480
|
-
if (router.state.currentAction === actionState) {
|
|
481
|
-
router.state.currentAction = undefined
|
|
482
|
-
}
|
|
483
|
-
if (action.current === actionState) {
|
|
484
|
-
action.current = undefined
|
|
485
|
-
}
|
|
486
|
-
})
|
|
487
|
-
router.removeActionQueue = []
|
|
488
|
-
|
|
489
550
|
// Cancel any pending matches
|
|
490
551
|
router.cancelMatches()
|
|
491
552
|
|
|
492
553
|
// Match the routes
|
|
493
|
-
const matches = router.matchRoutes(location.pathname, {
|
|
554
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
494
555
|
strictParseParams: true,
|
|
495
556
|
})
|
|
496
557
|
|
|
@@ -528,6 +589,10 @@ export function createRouter<
|
|
|
528
589
|
}
|
|
529
590
|
})
|
|
530
591
|
|
|
592
|
+
const entering = matches.filter((d) => {
|
|
593
|
+
return !previousMatches.find((dd) => dd.matchId === d.matchId)
|
|
594
|
+
})
|
|
595
|
+
|
|
531
596
|
const now = Date.now()
|
|
532
597
|
|
|
533
598
|
exiting.forEach((d) => {
|
|
@@ -535,6 +600,7 @@ export function createRouter<
|
|
|
535
600
|
params: d.params,
|
|
536
601
|
search: d.routeSearch,
|
|
537
602
|
})
|
|
603
|
+
|
|
538
604
|
// Clear idle error states when match leaves
|
|
539
605
|
if (d.status === 'error' && !d.isFetching) {
|
|
540
606
|
d.status = 'idle'
|
|
@@ -559,10 +625,6 @@ export function createRouter<
|
|
|
559
625
|
})
|
|
560
626
|
})
|
|
561
627
|
|
|
562
|
-
const entering = matches.filter((d) => {
|
|
563
|
-
return !previousMatches.find((dd) => dd.matchId === d.matchId)
|
|
564
|
-
})
|
|
565
|
-
|
|
566
628
|
entering.forEach((d) => {
|
|
567
629
|
d.__.onExit = d.options.onMatch?.({
|
|
568
630
|
params: d.params,
|
|
@@ -571,17 +633,19 @@ export function createRouter<
|
|
|
571
633
|
delete router.matchCache[d.matchId]
|
|
572
634
|
})
|
|
573
635
|
|
|
574
|
-
if (matches.some((d) => d.status === 'loading')) {
|
|
575
|
-
router.notify()
|
|
576
|
-
await Promise.all(
|
|
577
|
-
matches.map((d) => d.__.loaderPromise || Promise.resolve()),
|
|
578
|
-
)
|
|
579
|
-
}
|
|
580
636
|
if (router.startedLoadingAt !== id) {
|
|
581
637
|
// Ignore side-effects of match loading
|
|
582
638
|
return
|
|
583
639
|
}
|
|
584
640
|
|
|
641
|
+
matches.forEach((match) => {
|
|
642
|
+
// Clear actions
|
|
643
|
+
if (match.action) {
|
|
644
|
+
match.action.current = undefined
|
|
645
|
+
match.action.submissions = []
|
|
646
|
+
}
|
|
647
|
+
})
|
|
648
|
+
|
|
585
649
|
router.state = {
|
|
586
650
|
...router.state,
|
|
587
651
|
location: router.location,
|
|
@@ -760,7 +824,9 @@ export function createRouter<
|
|
|
760
824
|
if (match.status === 'loading') {
|
|
761
825
|
// If requested, start the pending timers
|
|
762
826
|
if (loaderOpts?.withPending) match.__.startPending()
|
|
827
|
+
}
|
|
763
828
|
|
|
829
|
+
if (match.__.loadPromise) {
|
|
764
830
|
// Wait for the first sign of activity from the match
|
|
765
831
|
// This might be completion, error, or a pending state
|
|
766
832
|
await match.__.loadPromise
|
|
@@ -1014,12 +1080,6 @@ export function createRouter<
|
|
|
1014
1080
|
return routeConfigs.map((routeConfig) => {
|
|
1015
1081
|
const routeOptions = routeConfig.options
|
|
1016
1082
|
const route = createRoute(routeConfig, routeOptions, parent, router)
|
|
1017
|
-
|
|
1018
|
-
// {
|
|
1019
|
-
// pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
|
|
1020
|
-
// pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
|
|
1021
|
-
// }
|
|
1022
|
-
|
|
1023
1083
|
const existingRoute = (router.routesById as any)[route.routeId]
|
|
1024
1084
|
|
|
1025
1085
|
if (existingRoute) {
|
|
@@ -1214,9 +1274,6 @@ export function createRouter<
|
|
|
1214
1274
|
},
|
|
1215
1275
|
}
|
|
1216
1276
|
|
|
1217
|
-
router.location = router.__.parseLocation(history.location)
|
|
1218
|
-
router.state.location = router.location
|
|
1219
|
-
|
|
1220
1277
|
router.update(userOptions)
|
|
1221
1278
|
|
|
1222
1279
|
// Allow frameworks to hook into the router creation
|
|
@@ -1228,3 +1285,16 @@ export function createRouter<
|
|
|
1228
1285
|
function isCtrlEvent(e: MouseEvent) {
|
|
1229
1286
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
1230
1287
|
}
|
|
1288
|
+
|
|
1289
|
+
function cascadeLoaderData(matches: RouteMatch<any, any>[]) {
|
|
1290
|
+
matches.forEach((match, index) => {
|
|
1291
|
+
const parent = matches[index - 1]
|
|
1292
|
+
|
|
1293
|
+
if (parent) {
|
|
1294
|
+
match.loaderData = replaceEqualDeep(match.loaderData, {
|
|
1295
|
+
...parent.loaderData,
|
|
1296
|
+
...match.routeLoaderData,
|
|
1297
|
+
})
|
|
1298
|
+
}
|
|
1299
|
+
})
|
|
1300
|
+
}
|