@tanstack/react-router 1.13.0 → 1.14.2
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/dist/cjs/Matches.cjs +49 -16
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +2 -0
- package/dist/cjs/fileRoute.cjs.map +1 -1
- package/dist/cjs/fileRoute.d.cts +3 -1
- package/dist/cjs/index.cjs +5 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -0
- package/dist/cjs/not-found.cjs +46 -0
- package/dist/cjs/not-found.cjs.map +1 -0
- package/dist/cjs/not-found.d.cts +16 -0
- package/dist/cjs/route.cjs +6 -0
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +7 -0
- package/dist/cjs/router.cjs +102 -50
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +11 -2
- package/dist/esm/Matches.d.ts +2 -0
- package/dist/esm/Matches.js +44 -11
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/fileRoute.d.ts +3 -1
- package/dist/esm/fileRoute.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/not-found.d.ts +16 -0
- package/dist/esm/not-found.js +46 -0
- package/dist/esm/not-found.js.map +1 -0
- package/dist/esm/route.d.ts +7 -0
- package/dist/esm/route.js +6 -0
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +11 -2
- package/dist/esm/router.js +64 -12
- package/dist/esm/router.js.map +1 -1
- package/package.json +1 -1
- package/src/Matches.tsx +52 -5
- package/src/fileRoute.ts +5 -6
- package/src/index.tsx +1 -0
- package/src/not-found.tsx +54 -0
- package/src/route.ts +18 -6
- package/src/router.ts +98 -16
package/src/Matches.tsx
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
RootSearchSchema,
|
|
12
12
|
StaticDataRouteOption,
|
|
13
13
|
UpdatableStaticRouteOption,
|
|
14
|
+
rootRouteId,
|
|
14
15
|
} from './route'
|
|
15
16
|
import {
|
|
16
17
|
AllParams,
|
|
@@ -23,6 +24,12 @@ import {
|
|
|
23
24
|
} from './routeInfo'
|
|
24
25
|
import { RegisteredRouter, RouterState } from './router'
|
|
25
26
|
import { DeepOptional, Expand, NoInfer, StrictOrFrom, pick } from './utils'
|
|
27
|
+
import {
|
|
28
|
+
CatchNotFound,
|
|
29
|
+
DefaultGlobalNotFound,
|
|
30
|
+
NotFoundError,
|
|
31
|
+
isNotFound,
|
|
32
|
+
} from './not-found'
|
|
26
33
|
|
|
27
34
|
export const matchContext = React.createContext<string | undefined>(undefined)
|
|
28
35
|
|
|
@@ -66,6 +73,7 @@ export interface RouteMatch<
|
|
|
66
73
|
meta?: JSX.IntrinsicElements['meta'][]
|
|
67
74
|
links?: JSX.IntrinsicElements['link'][]
|
|
68
75
|
scripts?: JSX.IntrinsicElements['script'][]
|
|
76
|
+
notFoundError?: NotFoundError
|
|
69
77
|
staticData: StaticDataRouteOption
|
|
70
78
|
}
|
|
71
79
|
|
|
@@ -125,6 +133,12 @@ export function Match({ matchId }: { matchId: string }) {
|
|
|
125
133
|
router.options.defaultErrorComponent ??
|
|
126
134
|
ErrorComponent
|
|
127
135
|
|
|
136
|
+
const routeNotFoundComponent = route.isRoot
|
|
137
|
+
? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component
|
|
138
|
+
route.options.notFoundComponent ??
|
|
139
|
+
router.options.notFoundRoute?.options.component
|
|
140
|
+
: route.options.notFoundComponent
|
|
141
|
+
|
|
128
142
|
const ResolvedSuspenseBoundary =
|
|
129
143
|
route.options.wrapInSuspense ??
|
|
130
144
|
PendingComponent ??
|
|
@@ -138,17 +152,35 @@ export function Match({ matchId }: { matchId: string }) {
|
|
|
138
152
|
? CatchBoundary
|
|
139
153
|
: SafeFragment
|
|
140
154
|
|
|
155
|
+
const ResolvedNotFoundBoundary = routeNotFoundComponent
|
|
156
|
+
? CatchNotFound
|
|
157
|
+
: SafeFragment
|
|
158
|
+
|
|
141
159
|
return (
|
|
142
160
|
<matchContext.Provider value={matchId}>
|
|
143
161
|
<ResolvedSuspenseBoundary fallback={pendingElement}>
|
|
144
162
|
<ResolvedCatchBoundary
|
|
145
163
|
getResetKey={() => router.state.resolvedLocation.state?.key}
|
|
146
164
|
errorComponent={routeErrorComponent}
|
|
147
|
-
onCatch={() => {
|
|
165
|
+
onCatch={(error) => {
|
|
166
|
+
// Forward not found errors (we don't want to show the error component for these)
|
|
167
|
+
if (isNotFound(error)) throw error
|
|
148
168
|
warning(false, `Error in route match: ${matchId}`)
|
|
149
169
|
}}
|
|
150
170
|
>
|
|
151
|
-
<
|
|
171
|
+
<ResolvedNotFoundBoundary
|
|
172
|
+
fallback={(error) => {
|
|
173
|
+
// If the current not found handler doesn't exist or doesn't handle global not founds, forward it up the tree
|
|
174
|
+
if (!routeNotFoundComponent || (error.global && !route.isRoot))
|
|
175
|
+
throw error
|
|
176
|
+
|
|
177
|
+
return React.createElement(routeNotFoundComponent, {
|
|
178
|
+
data: error.data,
|
|
179
|
+
})
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
<MatchInner matchId={matchId!} pendingElement={pendingElement} />
|
|
183
|
+
</ResolvedNotFoundBoundary>
|
|
152
184
|
</ResolvedCatchBoundary>
|
|
153
185
|
</ResolvedSuspenseBoundary>
|
|
154
186
|
</matchContext.Provider>
|
|
@@ -170,16 +202,31 @@ function MatchInner({
|
|
|
170
202
|
|
|
171
203
|
const route = router.routesById[routeId]!
|
|
172
204
|
|
|
173
|
-
const match = useRouterState({
|
|
174
|
-
select: (s) =>
|
|
175
|
-
pick(getRenderedMatches(s).find((d) => d.id === matchId)!, [
|
|
205
|
+
const { match } = useRouterState({
|
|
206
|
+
select: (s) => ({
|
|
207
|
+
match: pick(getRenderedMatches(s).find((d) => d.id === matchId)!, [
|
|
176
208
|
'status',
|
|
177
209
|
'error',
|
|
178
210
|
'showPending',
|
|
179
211
|
'loadPromise',
|
|
212
|
+
'notFoundError',
|
|
180
213
|
]),
|
|
214
|
+
}),
|
|
181
215
|
})
|
|
182
216
|
|
|
217
|
+
// If a global not-found is found, and it's the root route, render the global not-found component.
|
|
218
|
+
if (match.notFoundError) {
|
|
219
|
+
if (routeId === rootRouteId && !route.options.notFoundComponent)
|
|
220
|
+
return <DefaultGlobalNotFound />
|
|
221
|
+
|
|
222
|
+
invariant(
|
|
223
|
+
route.options.notFoundComponent,
|
|
224
|
+
'Route matched with notFoundError should have a notFoundComponent',
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
return <route.options.notFoundComponent data={match.notFoundError} />
|
|
228
|
+
}
|
|
229
|
+
|
|
183
230
|
if (match.status === 'error') {
|
|
184
231
|
if (isServerSideError(match.error)) {
|
|
185
232
|
const deserializeError =
|
package/src/fileRoute.ts
CHANGED
|
@@ -85,12 +85,11 @@ export type ResolveFilePath<
|
|
|
85
85
|
export type FileRoutePath<
|
|
86
86
|
TParentRoute extends AnyRoute,
|
|
87
87
|
TFilePath extends string,
|
|
88
|
-
> =
|
|
89
|
-
|
|
88
|
+
> = ResolveFilePath<TParentRoute, TFilePath> extends `_${infer _}`
|
|
89
|
+
? ''
|
|
90
|
+
: ResolveFilePath<TParentRoute, TFilePath> extends `/_${infer _}`
|
|
90
91
|
? ''
|
|
91
|
-
: ResolveFilePath<TParentRoute, TFilePath>
|
|
92
|
-
? ''
|
|
93
|
-
: ResolveFilePath<TParentRoute, TFilePath>
|
|
92
|
+
: ResolveFilePath<TParentRoute, TFilePath>
|
|
94
93
|
|
|
95
94
|
export function createFileRoute<
|
|
96
95
|
TFilePath extends keyof FileRoutesByPath,
|
|
@@ -268,7 +267,7 @@ export function FileRouteLoader<
|
|
|
268
267
|
|
|
269
268
|
export type LazyRouteOptions = Pick<
|
|
270
269
|
UpdatableRouteOptions<AnySearchSchema, any>,
|
|
271
|
-
'component' | 'errorComponent' | 'pendingComponent'
|
|
270
|
+
'component' | 'errorComponent' | 'pendingComponent' | 'notFoundComponent'
|
|
272
271
|
>
|
|
273
272
|
|
|
274
273
|
export class LazyRoute<TRoute extends AnyRoute> {
|
package/src/index.tsx
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { CatchBoundary } from './CatchBoundary'
|
|
3
|
+
import { useRouterState } from './useRouterState'
|
|
4
|
+
import { RegisteredRouter, RouteIds } from '.'
|
|
5
|
+
|
|
6
|
+
export type NotFoundError = {
|
|
7
|
+
global?: boolean
|
|
8
|
+
data?: any
|
|
9
|
+
throw?: boolean
|
|
10
|
+
route?: RouteIds<RegisteredRouter['routeTree']>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function notFound(options: NotFoundError = {}) {
|
|
14
|
+
;(options as any).isNotFound = true
|
|
15
|
+
if (options.throw) throw options
|
|
16
|
+
return options
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isNotFound(obj: any): obj is NotFoundError {
|
|
20
|
+
return !!obj?.isNotFound
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function CatchNotFound(props: {
|
|
24
|
+
fallback?: (error: NotFoundError) => React.ReactElement
|
|
25
|
+
onCatch?: (error: any) => void
|
|
26
|
+
children: React.ReactNode
|
|
27
|
+
}) {
|
|
28
|
+
// TODO: Some way for the user to programmatically reset the not-found boundary?
|
|
29
|
+
const resetKey = useRouterState({
|
|
30
|
+
select: (s) => `not-found-${s.location.pathname}-${s.status}`,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<CatchBoundary
|
|
35
|
+
getResetKey={() => resetKey}
|
|
36
|
+
onCatch={(error) => {
|
|
37
|
+
if (isNotFound(error)) {
|
|
38
|
+
props.onCatch?.(error)
|
|
39
|
+
} else {
|
|
40
|
+
throw error
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
errorComponent={({ error }: { error: NotFoundError }) =>
|
|
44
|
+
props.fallback?.(error)
|
|
45
|
+
}
|
|
46
|
+
>
|
|
47
|
+
{props.children}
|
|
48
|
+
</CatchBoundary>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function DefaultGlobalNotFound() {
|
|
53
|
+
return <p>Not Found</p>
|
|
54
|
+
}
|
package/src/route.ts
CHANGED
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
} from './utils'
|
|
20
20
|
import { BuildLocationFn, NavigateFn } from './RouterProvider'
|
|
21
21
|
import { LazyRoute } from '.'
|
|
22
|
+
import warning from 'tiny-warning'
|
|
23
|
+
import { NotFoundError, notFound } from '.'
|
|
22
24
|
|
|
23
25
|
export const rootRouteId = '__root__' as const
|
|
24
26
|
export type RootRouteId = typeof rootRouteId
|
|
@@ -191,6 +193,7 @@ export type UpdatableRouteOptions<
|
|
|
191
193
|
// The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />`
|
|
192
194
|
component?: RouteComponent
|
|
193
195
|
errorComponent?: false | null | ErrorRouteComponent
|
|
196
|
+
notFoundComponent?: NotFoundRouteComponent
|
|
194
197
|
pendingComponent?: RouteComponent
|
|
195
198
|
pendingMs?: number
|
|
196
199
|
pendingMinMs?: number
|
|
@@ -368,12 +371,11 @@ export type MergeFromFromParent<T, U> = IsAny<T, U, T & U>
|
|
|
368
371
|
export type ResolveAllParams<
|
|
369
372
|
TParentRoute extends AnyRoute,
|
|
370
373
|
TParams extends AnyPathParams,
|
|
371
|
-
> =
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
>
|
|
374
|
+
> = Record<never, string> extends TParentRoute['types']['allParams']
|
|
375
|
+
? TParams
|
|
376
|
+
: Expand<
|
|
377
|
+
UnionToIntersection<TParentRoute['types']['allParams'] & TParams> & {}
|
|
378
|
+
>
|
|
377
379
|
|
|
378
380
|
export type RouteConstraints = {
|
|
379
381
|
TParentRoute: AnyRoute
|
|
@@ -573,6 +575,10 @@ export class RouteApi<
|
|
|
573
575
|
}): TSelected => {
|
|
574
576
|
return useLoaderData({ ...opts, from: this.id } as any)
|
|
575
577
|
}
|
|
578
|
+
|
|
579
|
+
notFound = (opts?: NotFoundError) => {
|
|
580
|
+
return notFound({ route: this.id as string, ...opts })
|
|
581
|
+
}
|
|
576
582
|
}
|
|
577
583
|
|
|
578
584
|
/**
|
|
@@ -1249,6 +1255,10 @@ export type ErrorComponentProps = {
|
|
|
1249
1255
|
error: unknown
|
|
1250
1256
|
info: { componentStack: string }
|
|
1251
1257
|
}
|
|
1258
|
+
export type NotFoundRouteProps = {
|
|
1259
|
+
// TODO: Make sure this is `| null | undefined` (this is for global not-founds)
|
|
1260
|
+
data: unknown
|
|
1261
|
+
}
|
|
1252
1262
|
//
|
|
1253
1263
|
|
|
1254
1264
|
export type ReactNode = any
|
|
@@ -1266,6 +1276,8 @@ export type RouteComponent<TProps = any> = SyncRouteComponent<TProps> &
|
|
|
1266
1276
|
|
|
1267
1277
|
export type ErrorRouteComponent = RouteComponent<ErrorComponentProps>
|
|
1268
1278
|
|
|
1279
|
+
export type NotFoundRouteComponent = SyncRouteComponent<NotFoundRouteProps>
|
|
1280
|
+
|
|
1269
1281
|
export class NotFoundRoute<
|
|
1270
1282
|
TParentRoute extends AnyRootRoute,
|
|
1271
1283
|
TSearchSchemaInput extends Record<string, any> = {},
|
package/src/router.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
RouteMask,
|
|
18
18
|
Route,
|
|
19
19
|
LoaderFnContext,
|
|
20
|
+
rootRouteId,
|
|
20
21
|
} from './route'
|
|
21
22
|
import {
|
|
22
23
|
FullSearchSchema,
|
|
@@ -63,6 +64,7 @@ import {
|
|
|
63
64
|
} from './path'
|
|
64
65
|
import invariant from 'tiny-invariant'
|
|
65
66
|
import { isRedirect } from './redirects'
|
|
67
|
+
import { NotFoundError, isNotFound } from './not-found'
|
|
66
68
|
import { ResolveRelativePath, ToOptions } from './link'
|
|
67
69
|
import { NoInfer } from '@tanstack/react-store'
|
|
68
70
|
// import warning from 'tiny-warning'
|
|
@@ -131,9 +133,15 @@ export interface RouterOptions<
|
|
|
131
133
|
unmaskOnReload?: boolean
|
|
132
134
|
Wrap?: (props: { children: any }) => JSX.Element
|
|
133
135
|
InnerWrap?: (props: { children: any }) => JSX.Element
|
|
136
|
+
/**
|
|
137
|
+
* @deprecated
|
|
138
|
+
* Use `notFoundComponent` instead.
|
|
139
|
+
* See https://tanstack.com/router/v1/docs/guide/not-found-errors#migrating-from-notfoundroute for more info.
|
|
140
|
+
*/
|
|
134
141
|
notFoundRoute?: AnyRoute
|
|
135
142
|
transformer?: RouterTransformer
|
|
136
143
|
errorSerializer?: RouterErrorSerializer<TSerializedError>
|
|
144
|
+
globalNotFound?: RouteComponent
|
|
137
145
|
}
|
|
138
146
|
|
|
139
147
|
export interface RouterTransformer {
|
|
@@ -182,7 +190,7 @@ export interface DehydratedRouterState {
|
|
|
182
190
|
|
|
183
191
|
export type DehydratedRouteMatch = Pick<
|
|
184
192
|
RouteMatch,
|
|
185
|
-
'id' | 'status' | 'updatedAt' | 'loaderData'
|
|
193
|
+
'id' | 'status' | 'updatedAt' | 'notFoundError' | 'loaderData'
|
|
186
194
|
>
|
|
187
195
|
|
|
188
196
|
export interface DehydratedRouter {
|
|
@@ -200,6 +208,7 @@ export const componentTypes = [
|
|
|
200
208
|
'component',
|
|
201
209
|
'errorComponent',
|
|
202
210
|
'pendingComponent',
|
|
211
|
+
'notFoundComponent',
|
|
203
212
|
] as const
|
|
204
213
|
|
|
205
214
|
export type RouterEvents = {
|
|
@@ -309,6 +318,12 @@ export class Router<
|
|
|
309
318
|
TSerializedError
|
|
310
319
|
>,
|
|
311
320
|
) => {
|
|
321
|
+
if (newOptions.notFoundRoute) {
|
|
322
|
+
console.warn(
|
|
323
|
+
'The notFoundRoute API is deprecated and will be removed in the next major version. See https://tanstack.com/router/v1/docs/guide/not-found-errors#migrating-from-notfoundroute for more info.',
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
|
|
312
327
|
const previousOptions = this.options
|
|
313
328
|
this.options = {
|
|
314
329
|
...this.options,
|
|
@@ -593,17 +608,23 @@ export class Router<
|
|
|
593
608
|
|
|
594
609
|
let matchedRoutes: AnyRoute[] = [routeCursor]
|
|
595
610
|
|
|
611
|
+
let isGlobalNotFound = false
|
|
612
|
+
|
|
596
613
|
// Check to see if the route needs a 404 entry
|
|
597
614
|
if (
|
|
598
615
|
// If we found a route, and it's not an index route and we have left over path
|
|
599
|
-
|
|
616
|
+
foundRoute
|
|
600
617
|
? foundRoute.path !== '/' && routeParams['**']
|
|
601
618
|
: // Or if we didn't find a route and we have left over path
|
|
602
|
-
trimPathRight(pathname)
|
|
603
|
-
// And we have a 404 route configured
|
|
604
|
-
this.options.notFoundRoute
|
|
619
|
+
trimPathRight(pathname)
|
|
605
620
|
) {
|
|
606
|
-
|
|
621
|
+
// If the user has defined an (old) 404 route, use it
|
|
622
|
+
if (this.options.notFoundRoute) {
|
|
623
|
+
matchedRoutes.push(this.options.notFoundRoute)
|
|
624
|
+
} else {
|
|
625
|
+
// If there is no routes found during path matching
|
|
626
|
+
isGlobalNotFound = true
|
|
627
|
+
}
|
|
607
628
|
}
|
|
608
629
|
|
|
609
630
|
while (routeCursor?.parentRoute) {
|
|
@@ -714,15 +735,15 @@ export class Router<
|
|
|
714
735
|
? 'stay'
|
|
715
736
|
: 'enter'
|
|
716
737
|
|
|
717
|
-
// Create a fresh route match
|
|
718
|
-
const hasLoaders = !!(
|
|
719
|
-
route.options.loader ||
|
|
720
|
-
route.lazyFn ||
|
|
721
|
-
componentTypes.some((d) => (route.options[d] as any)?.preload)
|
|
722
|
-
)
|
|
723
|
-
|
|
724
738
|
const match: AnyRouteMatch = existingMatch
|
|
725
|
-
? {
|
|
739
|
+
? {
|
|
740
|
+
...existingMatch,
|
|
741
|
+
cause,
|
|
742
|
+
notFoundError:
|
|
743
|
+
isGlobalNotFound && route.id === rootRouteId
|
|
744
|
+
? { global: true }
|
|
745
|
+
: undefined,
|
|
746
|
+
}
|
|
726
747
|
: {
|
|
727
748
|
id: matchId,
|
|
728
749
|
routeId: route.id,
|
|
@@ -731,7 +752,7 @@ export class Router<
|
|
|
731
752
|
updatedAt: Date.now(),
|
|
732
753
|
search: {} as any,
|
|
733
754
|
searchError: undefined,
|
|
734
|
-
status:
|
|
755
|
+
status: 'pending',
|
|
735
756
|
showPending: false,
|
|
736
757
|
isFetching: false,
|
|
737
758
|
error: undefined,
|
|
@@ -745,6 +766,10 @@ export class Router<
|
|
|
745
766
|
loaderDeps,
|
|
746
767
|
invalid: false,
|
|
747
768
|
preload: false,
|
|
769
|
+
notFoundError:
|
|
770
|
+
isGlobalNotFound && route.id === rootRouteId
|
|
771
|
+
? { global: true }
|
|
772
|
+
: undefined,
|
|
748
773
|
links: route.options.links?.(),
|
|
749
774
|
scripts: route.options.scripts?.(),
|
|
750
775
|
staticData: route.options.staticData || {},
|
|
@@ -1110,6 +1135,10 @@ export class Router<
|
|
|
1110
1135
|
throw err
|
|
1111
1136
|
}
|
|
1112
1137
|
|
|
1138
|
+
if (isNotFound(err)) {
|
|
1139
|
+
this.updateMatchesWithNotFound(matches, match, err)
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1113
1142
|
try {
|
|
1114
1143
|
route.options.onError?.(err)
|
|
1115
1144
|
} catch (errorHandlerErr) {
|
|
@@ -1212,6 +1241,11 @@ export class Router<
|
|
|
1212
1241
|
}
|
|
1213
1242
|
return true
|
|
1214
1243
|
}
|
|
1244
|
+
|
|
1245
|
+
if (isNotFound(err)) {
|
|
1246
|
+
this.updateMatchesWithNotFound(matches, match, err)
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1215
1249
|
return false
|
|
1216
1250
|
}
|
|
1217
1251
|
|
|
@@ -1708,7 +1742,15 @@ export class Router<
|
|
|
1708
1742
|
return {
|
|
1709
1743
|
state: {
|
|
1710
1744
|
dehydratedMatches: this.state.matches.map((d) => ({
|
|
1711
|
-
...pick(d, [
|
|
1745
|
+
...pick(d, [
|
|
1746
|
+
'id',
|
|
1747
|
+
'status',
|
|
1748
|
+
'updatedAt',
|
|
1749
|
+
'loaderData',
|
|
1750
|
+
// Not-founds that occur during SSR don't require the client to load data before
|
|
1751
|
+
// triggering in order to prevent the flicker of the loading component
|
|
1752
|
+
'notFoundError',
|
|
1753
|
+
]),
|
|
1712
1754
|
// If an error occurs server-side during SSRing,
|
|
1713
1755
|
// send a small subset of the error to the client
|
|
1714
1756
|
error: d.error
|
|
@@ -1777,6 +1819,46 @@ export class Router<
|
|
|
1777
1819
|
})
|
|
1778
1820
|
}
|
|
1779
1821
|
|
|
1822
|
+
// Finds a match that has a notFoundComponent
|
|
1823
|
+
updateMatchesWithNotFound = (
|
|
1824
|
+
matches: AnyRouteMatch[],
|
|
1825
|
+
currentMatch: AnyRouteMatch,
|
|
1826
|
+
err: NotFoundError,
|
|
1827
|
+
) => {
|
|
1828
|
+
const matchesByRouteId = Object.fromEntries(
|
|
1829
|
+
matches.map((match) => [match.routeId, match]),
|
|
1830
|
+
) as Record<string, AnyRouteMatch>
|
|
1831
|
+
|
|
1832
|
+
if (err.global) {
|
|
1833
|
+
matchesByRouteId[rootRouteId]!.notFoundError = err
|
|
1834
|
+
} else {
|
|
1835
|
+
// If the err contains a routeId, start searching up from that route
|
|
1836
|
+
let currentRoute = (this.routesById as any)[
|
|
1837
|
+
err.route ?? currentMatch.routeId
|
|
1838
|
+
] as AnyRoute
|
|
1839
|
+
|
|
1840
|
+
// Go up the tree until we find a route with a notFoundComponent
|
|
1841
|
+
while (!currentRoute.options.notFoundComponent) {
|
|
1842
|
+
currentRoute = currentRoute?.parentRoute
|
|
1843
|
+
|
|
1844
|
+
invariant(
|
|
1845
|
+
currentRoute,
|
|
1846
|
+
'Found invalid route tree while trying to find not-found handler.',
|
|
1847
|
+
)
|
|
1848
|
+
|
|
1849
|
+
if (currentRoute.id === rootRouteId) break
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
const match = matchesByRouteId[currentRoute.id]
|
|
1853
|
+
invariant(match, 'Could not find match for route: ' + currentRoute.id)
|
|
1854
|
+
match.notFoundError = err
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
hasNotFoundMatch = () => {
|
|
1859
|
+
return this.__store.state.matches.some((d) => d.notFoundError)
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1780
1862
|
// resolveMatchPromise = (matchId: string, key: string, value: any) => {
|
|
1781
1863
|
// state.matches
|
|
1782
1864
|
// .find((d) => d.id === matchId)
|