@tanstack/react-router 1.40.0 → 1.42.0
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/CatchBoundary.d.cts +2 -2
- package/dist/cjs/Match.cjs +238 -0
- package/dist/cjs/Match.cjs.map +1 -0
- package/dist/cjs/Match.d.cts +5 -0
- package/dist/cjs/Matches.cjs +8 -249
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +2 -11
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/RouterProvider.d.cts +2 -2
- package/dist/cjs/SafeFragment.cjs +8 -0
- package/dist/cjs/SafeFragment.cjs.map +1 -0
- package/dist/cjs/SafeFragment.d.cts +1 -0
- package/dist/cjs/ScriptOnce.cjs +28 -0
- package/dist/cjs/ScriptOnce.cjs.map +1 -0
- package/dist/cjs/ScriptOnce.d.cts +5 -0
- package/dist/cjs/Transitioner.cjs +2 -1
- package/dist/cjs/Transitioner.cjs.map +1 -1
- package/dist/cjs/awaited.cjs +14 -71
- package/dist/cjs/awaited.cjs.map +1 -1
- package/dist/cjs/awaited.d.cts +3 -6
- package/dist/cjs/defer.cjs +7 -13
- package/dist/cjs/defer.cjs.map +1 -1
- package/dist/cjs/defer.d.cts +2 -6
- package/dist/cjs/fileRoute.d.cts +9 -1
- package/dist/cjs/index.cjs +11 -7
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +9 -4
- package/dist/cjs/isServerSideError.cjs +22 -0
- package/dist/cjs/isServerSideError.cjs.map +1 -0
- package/dist/cjs/isServerSideError.d.cts +5 -0
- package/dist/cjs/link.cjs +1 -0
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/matchContext.cjs +23 -0
- package/dist/cjs/matchContext.cjs.map +1 -0
- package/dist/cjs/matchContext.d.cts +2 -0
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/not-found.d.cts +2 -2
- package/dist/cjs/qss.cjs.map +1 -1
- package/dist/cjs/qss.d.cts +1 -1
- package/dist/cjs/redirects.cjs.map +1 -1
- package/dist/cjs/renderRouteNotFound.cjs +22 -0
- package/dist/cjs/renderRouteNotFound.cjs.map +1 -0
- package/dist/cjs/renderRouteNotFound.d.cts +4 -0
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +37 -31
- package/dist/cjs/router.cjs +35 -25
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +14 -9
- package/dist/cjs/useMatch.cjs +2 -2
- package/dist/cjs/useMatch.cjs.map +1 -1
- package/dist/cjs/utils.cjs +4 -3
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +3 -2
- package/dist/esm/CatchBoundary.d.ts +2 -2
- package/dist/esm/Match.d.ts +5 -0
- package/dist/esm/Match.js +221 -0
- package/dist/esm/Match.js.map +1 -0
- package/dist/esm/Matches.d.ts +2 -11
- package/dist/esm/Matches.js +5 -246
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.d.ts +2 -2
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/SafeFragment.d.ts +1 -0
- package/dist/esm/SafeFragment.js +8 -0
- package/dist/esm/SafeFragment.js.map +1 -0
- package/dist/esm/ScriptOnce.d.ts +5 -0
- package/dist/esm/ScriptOnce.js +28 -0
- package/dist/esm/ScriptOnce.js.map +1 -0
- package/dist/esm/Transitioner.js +2 -1
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/awaited.d.ts +3 -6
- package/dist/esm/awaited.js +16 -73
- package/dist/esm/awaited.js.map +1 -1
- package/dist/esm/defer.d.ts +2 -6
- package/dist/esm/defer.js +8 -14
- package/dist/esm/defer.js.map +1 -1
- package/dist/esm/fileRoute.d.ts +9 -1
- package/dist/esm/index.d.ts +9 -4
- package/dist/esm/index.js +9 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/isServerSideError.d.ts +5 -0
- package/dist/esm/isServerSideError.js +22 -0
- package/dist/esm/isServerSideError.js.map +1 -0
- package/dist/esm/link.js +1 -0
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/matchContext.d.ts +2 -0
- package/dist/esm/matchContext.js +6 -0
- package/dist/esm/matchContext.js.map +1 -0
- package/dist/esm/not-found.d.ts +2 -2
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/qss.d.ts +1 -1
- package/dist/esm/qss.js.map +1 -1
- package/dist/esm/redirects.js.map +1 -1
- package/dist/esm/renderRouteNotFound.d.ts +4 -0
- package/dist/esm/renderRouteNotFound.js +22 -0
- package/dist/esm/renderRouteNotFound.js.map +1 -0
- package/dist/esm/route.d.ts +37 -31
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +14 -9
- package/dist/esm/router.js +35 -25
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/useMatch.js +1 -1
- package/dist/esm/useMatch.js.map +1 -1
- package/dist/esm/utils.d.ts +3 -2
- package/dist/esm/utils.js +4 -3
- package/dist/esm/utils.js.map +1 -1
- package/package.json +4 -4
- package/src/Match.tsx +296 -0
- package/src/Matches.tsx +4 -333
- package/src/RouterProvider.tsx +1 -1
- package/src/SafeFragment.tsx +5 -0
- package/src/ScriptOnce.tsx +27 -0
- package/src/Transitioner.tsx +1 -1
- package/src/awaited.tsx +17 -89
- package/src/defer.ts +9 -26
- package/src/index.tsx +7 -16
- package/src/isServerSideError.tsx +23 -0
- package/src/link.tsx +2 -0
- package/src/matchContext.tsx +3 -0
- package/src/not-found.tsx +1 -1
- package/src/qss.ts +5 -6
- package/src/redirects.ts +0 -1
- package/src/renderRouteNotFound.tsx +28 -0
- package/src/route.ts +61 -65
- package/src/router.ts +61 -42
- package/src/useMatch.tsx +1 -1
- package/src/utils.ts +11 -9
package/src/index.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//
|
|
2
1
|
export {
|
|
3
2
|
createHistory,
|
|
4
3
|
createBrowserHistory,
|
|
@@ -12,13 +11,9 @@ export {
|
|
|
12
11
|
} from '@tanstack/history'
|
|
13
12
|
export { default as invariant } from 'tiny-invariant'
|
|
14
13
|
export { default as warning } from 'tiny-warning'
|
|
15
|
-
export { useAwaited, Await, type AwaitOptions
|
|
16
|
-
export {
|
|
17
|
-
|
|
18
|
-
isDehydratedDeferred,
|
|
19
|
-
type DeferredPromiseState,
|
|
20
|
-
type DeferredPromise,
|
|
21
|
-
} from './defer'
|
|
14
|
+
export { useAwaited, Await, type AwaitOptions } from './awaited'
|
|
15
|
+
export { ScriptOnce } from './ScriptOnce'
|
|
16
|
+
export { defer, type DeferredPromiseState, type DeferredPromise } from './defer'
|
|
22
17
|
export { CatchBoundary, ErrorComponent } from './CatchBoundary'
|
|
23
18
|
export {
|
|
24
19
|
FileRoute,
|
|
@@ -68,23 +63,21 @@ export {
|
|
|
68
63
|
} from './link'
|
|
69
64
|
export { type ParsedLocation } from './location'
|
|
70
65
|
export {
|
|
71
|
-
matchContext,
|
|
72
66
|
Matches,
|
|
73
|
-
Match,
|
|
74
|
-
Outlet,
|
|
75
67
|
useMatchRoute,
|
|
76
68
|
MatchRoute,
|
|
77
69
|
useMatches,
|
|
78
70
|
useParentMatches,
|
|
79
71
|
useChildMatches,
|
|
80
|
-
isServerSideError,
|
|
81
|
-
defaultDeserializeError,
|
|
82
72
|
type RouteMatch,
|
|
83
73
|
type AnyRouteMatch,
|
|
84
74
|
type MatchRouteOptions,
|
|
85
75
|
type UseMatchRouteOptions,
|
|
86
76
|
type MakeMatchRouteOptions,
|
|
87
77
|
} from './Matches'
|
|
78
|
+
export { matchContext } from './matchContext'
|
|
79
|
+
export { Match, Outlet } from './Match'
|
|
80
|
+
export { isServerSideError, defaultDeserializeError } from './isServerSideError'
|
|
88
81
|
export { useMatch } from './useMatch'
|
|
89
82
|
export { useLoaderDeps } from './useLoaderDeps'
|
|
90
83
|
export { useLoaderData } from './useLoaderData'
|
|
@@ -132,16 +125,13 @@ export {
|
|
|
132
125
|
type StaticDataRouteOption,
|
|
133
126
|
type RoutePathOptionsIntersection,
|
|
134
127
|
type RouteOptions,
|
|
135
|
-
type ParamsFallback,
|
|
136
128
|
type FileBaseRouteOptions,
|
|
137
129
|
type BaseRouteOptions,
|
|
138
130
|
type UpdatableRouteOptions,
|
|
139
131
|
type UpdatableStaticRouteOption,
|
|
140
132
|
type MetaDescriptor,
|
|
141
133
|
type RouteLinkEntry,
|
|
142
|
-
type ParseParamsOption,
|
|
143
134
|
type ParseParamsFn,
|
|
144
|
-
type ParseParamsObj,
|
|
145
135
|
type SearchSchemaValidator,
|
|
146
136
|
type SearchSchemaValidatorObj,
|
|
147
137
|
type SearchSchemaValidatorFn,
|
|
@@ -268,3 +258,4 @@ export {
|
|
|
268
258
|
type NotFoundError,
|
|
269
259
|
} from './not-found'
|
|
270
260
|
export { type Manifest, type RouterManagedTag } from './manifest'
|
|
261
|
+
export { createControlledPromise, type ControlledPromise } from './utils'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function isServerSideError(error: unknown): error is {
|
|
2
|
+
__isServerError: true
|
|
3
|
+
data: Record<string, any>
|
|
4
|
+
} {
|
|
5
|
+
if (!(typeof error === 'object' && error && 'data' in error)) return false
|
|
6
|
+
if (!('__isServerError' in error && error.__isServerError)) return false
|
|
7
|
+
if (!(typeof error.data === 'object' && error.data)) return false
|
|
8
|
+
|
|
9
|
+
return error.__isServerError === true
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function defaultDeserializeError(serializedData: Record<string, any>) {
|
|
13
|
+
if ('name' in serializedData && 'message' in serializedData) {
|
|
14
|
+
const error = new Error(serializedData.message)
|
|
15
|
+
error.name = serializedData.name
|
|
16
|
+
if (process.env.NODE_ENV === 'development') {
|
|
17
|
+
error.stack = serializedData.stack
|
|
18
|
+
}
|
|
19
|
+
return error
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return serializedData.data
|
|
23
|
+
}
|
package/src/link.tsx
CHANGED
package/src/not-found.tsx
CHANGED
package/src/qss.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
|
|
3
1
|
// qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
|
|
4
2
|
|
|
5
3
|
/**
|
|
@@ -13,7 +11,7 @@
|
|
|
13
11
|
* // Expected output: "token=foo&key=value"
|
|
14
12
|
* ```
|
|
15
13
|
*/
|
|
16
|
-
export function encode(obj, pfx?: string) {
|
|
14
|
+
export function encode(obj: any, pfx?: string) {
|
|
17
15
|
let k,
|
|
18
16
|
i,
|
|
19
17
|
tmp,
|
|
@@ -44,7 +42,7 @@ export function encode(obj, pfx?: string) {
|
|
|
44
42
|
* // Example input: toValue("123")
|
|
45
43
|
* // Expected output: 123
|
|
46
44
|
*/
|
|
47
|
-
function toValue(mix) {
|
|
45
|
+
function toValue(mix: any) {
|
|
48
46
|
if (!mix) return ''
|
|
49
47
|
const str = decodeURIComponent(mix)
|
|
50
48
|
if (str === 'false') return false
|
|
@@ -61,9 +59,9 @@ function toValue(mix) {
|
|
|
61
59
|
* // Example input: decode("token=foo&key=value")
|
|
62
60
|
* // Expected output: { "token": "foo", "key": "value" }
|
|
63
61
|
*/
|
|
64
|
-
export function decode(str, pfx?: string) {
|
|
62
|
+
export function decode(str: any, pfx?: string) {
|
|
65
63
|
let tmp, k
|
|
66
|
-
const out = {},
|
|
64
|
+
const out: any = {},
|
|
67
65
|
arr = (pfx ? str.substr(pfx.length) : str).split('&')
|
|
68
66
|
|
|
69
67
|
while ((tmp = arr.shift())) {
|
|
@@ -72,6 +70,7 @@ export function decode(str, pfx?: string) {
|
|
|
72
70
|
k = tmp.slice(0, equalIndex)
|
|
73
71
|
const value = tmp.slice(equalIndex + 1)
|
|
74
72
|
if (out[k] !== void 0) {
|
|
73
|
+
// @ts-expect-error
|
|
75
74
|
out[k] = [].concat(out[k], toValue(value))
|
|
76
75
|
} else {
|
|
77
76
|
out[k] = toValue(value)
|
package/src/redirects.ts
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import warning from 'tiny-warning'
|
|
3
|
+
import { DefaultGlobalNotFound } from './not-found'
|
|
4
|
+
import { type AnyRouter } from './router'
|
|
5
|
+
import { type AnyRoute } from './route'
|
|
6
|
+
|
|
7
|
+
export function renderRouteNotFound(
|
|
8
|
+
router: AnyRouter,
|
|
9
|
+
route: AnyRoute,
|
|
10
|
+
data: any,
|
|
11
|
+
) {
|
|
12
|
+
if (!route.options.notFoundComponent) {
|
|
13
|
+
if (router.options.defaultNotFoundComponent) {
|
|
14
|
+
return <router.options.defaultNotFoundComponent data={data} />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (process.env.NODE_ENV === 'development') {
|
|
18
|
+
warning(
|
|
19
|
+
route.options.notFoundComponent,
|
|
20
|
+
`A notFoundError was encountered on the route with ID "${route.id}", but a notFoundComponent option was not configured, nor was a router level defaultNotFoundComponent configured. Consider configuring at least one of these to avoid TanStack Router's overly generic defaultNotFoundComponent (<div>Not Found<div>)`,
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return <DefaultGlobalNotFound />
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return <route.options.notFoundComponent data={data} />
|
|
28
|
+
}
|
package/src/route.ts
CHANGED
|
@@ -91,10 +91,36 @@ export type RouteOptions<
|
|
|
91
91
|
NoInfer<TLoaderDeps>
|
|
92
92
|
>
|
|
93
93
|
|
|
94
|
-
export type
|
|
95
|
-
TPath
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
export type ParseParamsFn<TPath extends string, TParams> = (
|
|
95
|
+
rawParams: Record<ParsePathParams<TPath>, string>,
|
|
96
|
+
) => TParams extends Record<ParsePathParams<TPath>, any>
|
|
97
|
+
? TParams
|
|
98
|
+
: Record<ParsePathParams<TPath>, any>
|
|
99
|
+
|
|
100
|
+
export type StringifyParamsFn<TPath extends string, TParams> = (
|
|
101
|
+
params: TParams,
|
|
102
|
+
) => Record<ParsePathParams<TPath>, string>
|
|
103
|
+
|
|
104
|
+
export type ParamsOptions<TPath extends string, TParams> = {
|
|
105
|
+
params?: {
|
|
106
|
+
parse: ParseParamsFn<TPath, TParams>
|
|
107
|
+
stringify: StringifyParamsFn<TPath, TParams>
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
@deprecated Use params.parse instead
|
|
112
|
+
*/
|
|
113
|
+
parseParams?: ParseParamsFn<TPath, TParams>
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
@deprecated Use params.stringify instead
|
|
117
|
+
*/
|
|
118
|
+
stringifyParams?: StringifyParamsFn<TPath, TParams>
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface FullSearchSchemaOption<TFullSearchSchema> {
|
|
122
|
+
search: TFullSearchSchema
|
|
123
|
+
}
|
|
98
124
|
|
|
99
125
|
export type FileBaseRouteOptions<
|
|
100
126
|
TPath extends string = string,
|
|
@@ -102,14 +128,16 @@ export type FileBaseRouteOptions<
|
|
|
102
128
|
TSearchSchema = {},
|
|
103
129
|
TFullSearchSchema = TSearchSchema,
|
|
104
130
|
TParams = {},
|
|
105
|
-
TAllParams =
|
|
131
|
+
TAllParams = {},
|
|
106
132
|
TRouteContextReturn = RouteContext,
|
|
107
133
|
TParentAllContext = AnyContext,
|
|
108
134
|
TAllContext = AnyContext,
|
|
109
135
|
TLoaderDeps extends Record<string, any> = {},
|
|
110
136
|
TLoaderDataReturn = {},
|
|
111
137
|
> = {
|
|
112
|
-
validateSearch?:
|
|
138
|
+
validateSearch?:
|
|
139
|
+
| ((input: TSearchSchemaInput) => TSearchSchema)
|
|
140
|
+
| { parse: (input: TSearchSchemaInput) => TSearchSchema }
|
|
113
141
|
shouldReload?:
|
|
114
142
|
| boolean
|
|
115
143
|
| ((
|
|
@@ -119,36 +147,14 @@ export type FileBaseRouteOptions<
|
|
|
119
147
|
// If an error is thrown here, the route's loader will not be called.
|
|
120
148
|
// If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onError` function.
|
|
121
149
|
// If thrown during a preload event, the error will be logged to the console.
|
|
122
|
-
beforeLoad?:
|
|
123
|
-
TFullSearchSchema,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
TAllParams,
|
|
131
|
-
NoInfer<TLoaderDeps>,
|
|
132
|
-
NoInfer<TAllContext>,
|
|
133
|
-
TLoaderDataReturn
|
|
134
|
-
>
|
|
135
|
-
} & (
|
|
136
|
-
| {
|
|
137
|
-
// Both or none
|
|
138
|
-
parseParams?: (
|
|
139
|
-
rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>,
|
|
140
|
-
) => TParams extends Record<ParsePathParams<TPath>, any>
|
|
141
|
-
? TParams
|
|
142
|
-
: 'parseParams must return an object'
|
|
143
|
-
stringifyParams?: (
|
|
144
|
-
params: NoInfer<ParamsFallback<TPath, TParams>>,
|
|
145
|
-
) => Record<ParsePathParams<TPath>, string>
|
|
146
|
-
}
|
|
147
|
-
| {
|
|
148
|
-
stringifyParams?: never
|
|
149
|
-
parseParams?: never
|
|
150
|
-
}
|
|
151
|
-
)
|
|
150
|
+
beforeLoad?: (
|
|
151
|
+
ctx: BeforeLoadContext<TFullSearchSchema, TAllParams, TParentAllContext>,
|
|
152
|
+
) => Promise<TRouteContextReturn> | TRouteContextReturn | void
|
|
153
|
+
loaderDeps?: (opts: FullSearchSchemaOption<TFullSearchSchema>) => TLoaderDeps
|
|
154
|
+
loader?: (
|
|
155
|
+
ctx: LoaderFnContext<TAllParams, TLoaderDeps, TAllContext>,
|
|
156
|
+
) => TLoaderDataReturn | Promise<TLoaderDataReturn>
|
|
157
|
+
} & ParamsOptions<TPath, TParams>
|
|
152
158
|
|
|
153
159
|
export type BaseRouteOptions<
|
|
154
160
|
TParentRoute extends AnyRoute = AnyRoute,
|
|
@@ -158,7 +164,7 @@ export type BaseRouteOptions<
|
|
|
158
164
|
TSearchSchema = {},
|
|
159
165
|
TFullSearchSchema = TSearchSchema,
|
|
160
166
|
TParams = {},
|
|
161
|
-
TAllParams =
|
|
167
|
+
TAllParams = {},
|
|
162
168
|
TRouteContextReturn = RouteContext,
|
|
163
169
|
TParentAllContext = AnyContext,
|
|
164
170
|
TAllContext = AnyContext,
|
|
@@ -181,16 +187,14 @@ export type BaseRouteOptions<
|
|
|
181
187
|
getParentRoute: () => TParentRoute
|
|
182
188
|
}
|
|
183
189
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
> = (opts: {
|
|
190
|
-
search: TFullSearchSchema
|
|
190
|
+
export interface BeforeLoadContext<
|
|
191
|
+
TFullSearchSchema,
|
|
192
|
+
TAllParams,
|
|
193
|
+
TParentAllContext,
|
|
194
|
+
> extends FullSearchSchemaOption<TFullSearchSchema> {
|
|
191
195
|
abortController: AbortController
|
|
192
196
|
preload: boolean
|
|
193
|
-
params: TAllParams
|
|
197
|
+
params: Expand<TAllParams>
|
|
194
198
|
context: TParentAllContext
|
|
195
199
|
location: ParsedLocation
|
|
196
200
|
/**
|
|
@@ -199,7 +203,7 @@ type BeforeLoadFn<
|
|
|
199
203
|
navigate: NavigateFn
|
|
200
204
|
buildLocation: BuildLocationFn
|
|
201
205
|
cause: 'preload' | 'enter' | 'stay'
|
|
202
|
-
}
|
|
206
|
+
}
|
|
203
207
|
|
|
204
208
|
export type UpdatableRouteOptions<
|
|
205
209
|
TRouteId,
|
|
@@ -286,21 +290,6 @@ type LdJsonValue = LdJsonPrimitive | LdJsonObject | LdJsonArray
|
|
|
286
290
|
|
|
287
291
|
export type RouteLinkEntry = {}
|
|
288
292
|
|
|
289
|
-
export type ParseParamsOption<TPath extends string, TParams> = ParseParamsFn<
|
|
290
|
-
TPath,
|
|
291
|
-
TParams
|
|
292
|
-
>
|
|
293
|
-
|
|
294
|
-
export type ParseParamsFn<TPath extends string, TParams> = (
|
|
295
|
-
rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>,
|
|
296
|
-
) => TParams extends Record<ParsePathParams<TPath>, any>
|
|
297
|
-
? TParams
|
|
298
|
-
: 'parseParams must return an object'
|
|
299
|
-
|
|
300
|
-
export type ParseParamsObj<TPath extends string, TParams> = {
|
|
301
|
-
parse?: ParseParamsFn<TPath, TParams>
|
|
302
|
-
}
|
|
303
|
-
|
|
304
293
|
// The parse type here allows a zod schema to be passed directly to the validator
|
|
305
294
|
export type SearchSchemaValidator<TInput, TReturn> =
|
|
306
295
|
| SearchSchemaValidatorObj<TInput, TReturn>
|
|
@@ -321,7 +310,7 @@ export type RouteLoaderFn<
|
|
|
321
310
|
TLoaderData = undefined,
|
|
322
311
|
> = (
|
|
323
312
|
match: LoaderFnContext<TAllParams, TLoaderDeps, TAllContext>,
|
|
324
|
-
) =>
|
|
313
|
+
) => TLoaderData | Promise<TLoaderData>
|
|
325
314
|
|
|
326
315
|
export interface LoaderFnContext<
|
|
327
316
|
in out TAllParams = {},
|
|
@@ -330,7 +319,7 @@ export interface LoaderFnContext<
|
|
|
330
319
|
> {
|
|
331
320
|
abortController: AbortController
|
|
332
321
|
preload: boolean
|
|
333
|
-
params: TAllParams
|
|
322
|
+
params: Expand<TAllParams>
|
|
334
323
|
deps: TLoaderDeps
|
|
335
324
|
context: TAllContext
|
|
336
325
|
location: ParsedLocation // Do not supply search schema here so as to demotivate people from trying to shortcut loaderDeps
|
|
@@ -706,7 +695,7 @@ export class Route<
|
|
|
706
695
|
|
|
707
696
|
const isRoot = !options?.path && !options?.id
|
|
708
697
|
|
|
709
|
-
// eslint-disable-next-line
|
|
698
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
710
699
|
this.parentRoute = this.options?.getParentRoute?.()
|
|
711
700
|
|
|
712
701
|
if (isRoot) {
|
|
@@ -992,6 +981,7 @@ export type RootRouteOptions<
|
|
|
992
981
|
| 'caseSensitive'
|
|
993
982
|
| 'parseParams'
|
|
994
983
|
| 'stringifyParams'
|
|
984
|
+
| 'params'
|
|
995
985
|
>
|
|
996
986
|
|
|
997
987
|
export function createRootRouteWithContext<TRouterContext extends {}>() {
|
|
@@ -1126,6 +1116,7 @@ export function createRootRoute<
|
|
|
1126
1116
|
| 'caseSensitive'
|
|
1127
1117
|
| 'parseParams'
|
|
1128
1118
|
| 'stringifyParams'
|
|
1119
|
+
| 'params'
|
|
1129
1120
|
>,
|
|
1130
1121
|
) {
|
|
1131
1122
|
return new RootRoute<
|
|
@@ -1294,7 +1285,12 @@ export class NotFoundRoute<
|
|
|
1294
1285
|
TLoaderDataReturn,
|
|
1295
1286
|
TLoaderData
|
|
1296
1287
|
>,
|
|
1297
|
-
|
|
1288
|
+
| 'caseSensitive'
|
|
1289
|
+
| 'parseParams'
|
|
1290
|
+
| 'stringifyParams'
|
|
1291
|
+
| 'path'
|
|
1292
|
+
| 'id'
|
|
1293
|
+
| 'params'
|
|
1298
1294
|
>,
|
|
1299
1295
|
) {
|
|
1300
1296
|
super({
|
package/src/router.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createBrowserHistory, createMemoryHistory } from '@tanstack/history'
|
|
2
2
|
import { Store } from '@tanstack/react-store'
|
|
3
3
|
import invariant from 'tiny-invariant'
|
|
4
|
-
import warning from 'tiny-warning'
|
|
5
4
|
import { rootRouteId } from './root'
|
|
6
5
|
import { defaultParseSearch, defaultStringifySearch } from './searchParams'
|
|
7
6
|
import {
|
|
@@ -44,6 +43,7 @@ import type {
|
|
|
44
43
|
LoaderFnContext,
|
|
45
44
|
NotFoundRouteComponent,
|
|
46
45
|
RootRoute,
|
|
46
|
+
RouteComponent,
|
|
47
47
|
RouteMask,
|
|
48
48
|
} from './route'
|
|
49
49
|
import type {
|
|
@@ -57,22 +57,18 @@ import type {
|
|
|
57
57
|
ControlledPromise,
|
|
58
58
|
NonNullableUpdater,
|
|
59
59
|
PickAsRequired,
|
|
60
|
-
Timeout,
|
|
61
60
|
Updater,
|
|
62
61
|
} from './utils'
|
|
63
|
-
import type { RouteComponent } from './route'
|
|
64
62
|
import type {
|
|
65
63
|
AnyRouteMatch,
|
|
66
64
|
MakeRouteMatch,
|
|
67
65
|
MatchRouteOptions,
|
|
68
|
-
RouteMatch,
|
|
69
66
|
} from './Matches'
|
|
70
67
|
import type { ParsedLocation } from './location'
|
|
71
68
|
import type { SearchParser, SearchSerializer } from './searchParams'
|
|
72
69
|
import type {
|
|
73
70
|
BuildLocationFn,
|
|
74
71
|
CommitLocationOptions,
|
|
75
|
-
InjectedHtmlEntry,
|
|
76
72
|
NavigateFn,
|
|
77
73
|
} from './RouterProvider'
|
|
78
74
|
|
|
@@ -82,13 +78,16 @@ import type { NotFoundError } from './not-found'
|
|
|
82
78
|
import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
|
|
83
79
|
import type { NoInfer } from '@tanstack/react-store'
|
|
84
80
|
import type { DeferredPromiseState } from './defer'
|
|
85
|
-
import type { ErrorInfo } from 'react'
|
|
86
81
|
|
|
87
82
|
//
|
|
88
83
|
|
|
89
84
|
declare global {
|
|
90
85
|
interface Window {
|
|
91
|
-
|
|
86
|
+
__TSR__?: {
|
|
87
|
+
matches: Array<any>
|
|
88
|
+
cleanScripts: () => void
|
|
89
|
+
dehydrated?: any
|
|
90
|
+
}
|
|
92
91
|
__TSR_ROUTER_CONTEXT__?: React.Context<Router<any, any>>
|
|
93
92
|
}
|
|
94
93
|
}
|
|
@@ -237,7 +236,7 @@ export interface RouterOptions<
|
|
|
237
236
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultoncatch-property)
|
|
238
237
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#handling-errors-with-routeoptionsoncatch)
|
|
239
238
|
*/
|
|
240
|
-
defaultOnCatch?: (error: Error, errorInfo: ErrorInfo) => void
|
|
239
|
+
defaultOnCatch?: (error: Error, errorInfo: React.ErrorInfo) => void
|
|
241
240
|
defaultViewTransition?: boolean
|
|
242
241
|
/**
|
|
243
242
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/not-found-errors#the-notfoundmode-option)
|
|
@@ -508,6 +507,20 @@ export class Router<
|
|
|
508
507
|
dehydratedData?: TDehydrated
|
|
509
508
|
viewTransitionPromise?: ControlledPromise<true>
|
|
510
509
|
manifest?: Manifest
|
|
510
|
+
AfterEachMatch?: (props: {
|
|
511
|
+
match: Pick<
|
|
512
|
+
AnyRouteMatch,
|
|
513
|
+
'id' | 'status' | 'error' | 'loadPromise' | 'minPendingPromise'
|
|
514
|
+
>
|
|
515
|
+
matchIndex: number
|
|
516
|
+
}) => any
|
|
517
|
+
serializeLoaderData?: (
|
|
518
|
+
data: any,
|
|
519
|
+
ctx: {
|
|
520
|
+
router: AnyRouter
|
|
521
|
+
match: AnyRouteMatch
|
|
522
|
+
},
|
|
523
|
+
) => any
|
|
511
524
|
|
|
512
525
|
// Must build in constructor
|
|
513
526
|
__store!: Store<RouterState<TRouteTree>>
|
|
@@ -603,7 +616,7 @@ export class Router<
|
|
|
603
616
|
}
|
|
604
617
|
|
|
605
618
|
if (
|
|
606
|
-
// eslint-disable-next-line
|
|
619
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
607
620
|
!this.history ||
|
|
608
621
|
(this.options.history && this.options.history !== this.history)
|
|
609
622
|
) {
|
|
@@ -622,7 +635,7 @@ export class Router<
|
|
|
622
635
|
this.buildRouteTree()
|
|
623
636
|
}
|
|
624
637
|
|
|
625
|
-
// eslint-disable-next-line
|
|
638
|
+
// eslint-disable-next-line ts/no-unnecessary-condition
|
|
626
639
|
if (!this.__store) {
|
|
627
640
|
this.__store = new Store(getInitialRouterState(this.latestLocation), {
|
|
628
641
|
onUpdate: () => {
|
|
@@ -921,9 +934,12 @@ export class Router<
|
|
|
921
934
|
const parseErrors = matchedRoutes.map((route) => {
|
|
922
935
|
let parsedParamsError
|
|
923
936
|
|
|
924
|
-
|
|
937
|
+
const parseParams =
|
|
938
|
+
route.options.params?.parse ?? route.options.parseParams
|
|
939
|
+
|
|
940
|
+
if (parseParams) {
|
|
925
941
|
try {
|
|
926
|
-
const parsedParams =
|
|
942
|
+
const parsedParams = parseParams(routeParams)
|
|
927
943
|
// Add the parsed params to the accumulated params bag
|
|
928
944
|
Object.assign(routeParams, parsedParams)
|
|
929
945
|
} catch (err: any) {
|
|
@@ -1042,6 +1058,7 @@ export class Router<
|
|
|
1042
1058
|
|
|
1043
1059
|
match = {
|
|
1044
1060
|
id: matchId,
|
|
1061
|
+
index,
|
|
1045
1062
|
routeId: route.id,
|
|
1046
1063
|
params: routeParams,
|
|
1047
1064
|
pathname: joinPaths([this.basepath, interpolatedPath]),
|
|
@@ -1179,7 +1196,12 @@ export class Router<
|
|
|
1179
1196
|
|
|
1180
1197
|
if (Object.keys(nextParams).length > 0) {
|
|
1181
1198
|
matches
|
|
1182
|
-
?.map((d) =>
|
|
1199
|
+
?.map((d) => {
|
|
1200
|
+
const route = this.looseRoutesById[d.routeId]
|
|
1201
|
+
return (
|
|
1202
|
+
route?.options.params?.stringify ?? route!.options.stringifyParams
|
|
1203
|
+
)
|
|
1204
|
+
})
|
|
1183
1205
|
.filter(Boolean)
|
|
1184
1206
|
.forEach((fn) => {
|
|
1185
1207
|
nextParams = { ...nextParams!, ...fn!(nextParams) }
|
|
@@ -1958,7 +1980,13 @@ export class Router<
|
|
|
1958
1980
|
)
|
|
1959
1981
|
}
|
|
1960
1982
|
|
|
1961
|
-
|
|
1983
|
+
let loaderData = await loaderPromise
|
|
1984
|
+
if (this.serializeLoaderData) {
|
|
1985
|
+
loaderData = this.serializeLoaderData(loaderData, {
|
|
1986
|
+
router: this,
|
|
1987
|
+
match,
|
|
1988
|
+
})
|
|
1989
|
+
}
|
|
1962
1990
|
checkLatest()
|
|
1963
1991
|
|
|
1964
1992
|
handleRedirectAndNotFound(match, loaderData)
|
|
@@ -2288,38 +2316,29 @@ export class Router<
|
|
|
2288
2316
|
return match
|
|
2289
2317
|
}
|
|
2290
2318
|
|
|
2291
|
-
// We use a token -> weak map to keep track of deferred promises
|
|
2292
|
-
// that are registered on the server and need to be resolved
|
|
2293
|
-
registeredDeferredsIds = new Map<string, {}>()
|
|
2294
|
-
registeredDeferreds = new WeakMap<{}, DeferredPromiseState<any>>()
|
|
2295
|
-
|
|
2296
|
-
getDeferred = (uid: string) => {
|
|
2297
|
-
const token = this.registeredDeferredsIds.get(uid)
|
|
2298
|
-
|
|
2299
|
-
if (!token) {
|
|
2300
|
-
return undefined
|
|
2301
|
-
}
|
|
2302
|
-
|
|
2303
|
-
return this.registeredDeferreds.get(token)
|
|
2304
|
-
}
|
|
2305
|
-
|
|
2306
2319
|
dehydrate = (): DehydratedRouter => {
|
|
2307
2320
|
const pickError =
|
|
2308
2321
|
this.options.errorSerializer?.serialize ?? defaultSerializeError
|
|
2309
2322
|
|
|
2310
2323
|
return {
|
|
2311
2324
|
state: {
|
|
2312
|
-
dehydratedMatches: this.state.matches.map((d) =>
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2325
|
+
dehydratedMatches: this.state.matches.map((d) => {
|
|
2326
|
+
return {
|
|
2327
|
+
...pick(d, ['id', 'status', 'updatedAt']),
|
|
2328
|
+
// If an error occurs server-side during SSRing,
|
|
2329
|
+
// send a small subset of the error to the client
|
|
2330
|
+
error: d.error
|
|
2331
|
+
? {
|
|
2332
|
+
data: pickError(d.error),
|
|
2333
|
+
__isServerError: true,
|
|
2334
|
+
}
|
|
2335
|
+
: undefined,
|
|
2336
|
+
// NOTE: We don't send the loader data here, because
|
|
2337
|
+
// there is a potential that it needs to be streamed.
|
|
2338
|
+
// Instead, we render it next to the route match in the HTML
|
|
2339
|
+
// which gives us the potential to stream it via suspense.
|
|
2340
|
+
}
|
|
2341
|
+
}),
|
|
2323
2342
|
},
|
|
2324
2343
|
manifest: this.manifest,
|
|
2325
2344
|
}
|
|
@@ -2329,12 +2348,12 @@ export class Router<
|
|
|
2329
2348
|
let _ctx = __do_not_use_server_ctx
|
|
2330
2349
|
// Client hydrates from window
|
|
2331
2350
|
if (typeof document !== 'undefined') {
|
|
2332
|
-
_ctx = window.
|
|
2351
|
+
_ctx = window.__TSR__?.dehydrated
|
|
2333
2352
|
}
|
|
2334
2353
|
|
|
2335
2354
|
invariant(
|
|
2336
2355
|
_ctx,
|
|
2337
|
-
'Expected to find a
|
|
2356
|
+
'Expected to find a dehydrated data on window.__TSR__.dehydrated... but we did not. Please file an issue!',
|
|
2338
2357
|
)
|
|
2339
2358
|
|
|
2340
2359
|
const ctx = this.options.transformer.parse(_ctx) as HydrationCtx
|
package/src/useMatch.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import invariant from 'tiny-invariant'
|
|
|
3
3
|
import { useRouterState } from './useRouterState'
|
|
4
4
|
import { type RegisteredRouter } from './router'
|
|
5
5
|
import { type AnyRoute } from './route'
|
|
6
|
-
import { matchContext } from './
|
|
6
|
+
import { matchContext } from './matchContext'
|
|
7
7
|
import type { MakeRouteMatch } from './Matches'
|
|
8
8
|
import type { RouteIds } from './routeInfo'
|
|
9
9
|
import type { StrictOrFrom } from './utils'
|