@tanstack/react-router 1.81.5 → 1.81.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/dist/cjs/fileRoute.cjs.map +1 -1
- package/dist/cjs/fileRoute.d.cts +3 -2
- package/dist/cjs/index.d.cts +3 -2
- package/dist/cjs/location.d.cts +2 -2
- package/dist/cjs/matchContext.cjs +4 -0
- package/dist/cjs/matchContext.cjs.map +1 -1
- package/dist/cjs/matchContext.d.cts +1 -0
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +14 -51
- package/dist/cjs/routeInfo.d.cts +6 -6
- package/dist/cjs/router.cjs +13 -6
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +6 -5
- package/dist/cjs/searchParams.cjs.map +1 -1
- package/dist/cjs/searchParams.d.cts +3 -3
- package/dist/cjs/useMatch.cjs +3 -1
- package/dist/cjs/useMatch.cjs.map +1 -1
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +17 -5
- package/dist/cjs/validators.d.cts +49 -0
- package/dist/esm/fileRoute.d.ts +3 -2
- package/dist/esm/fileRoute.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/location.d.ts +2 -2
- package/dist/esm/matchContext.d.ts +1 -0
- package/dist/esm/matchContext.js +4 -0
- package/dist/esm/matchContext.js.map +1 -1
- package/dist/esm/route.d.ts +14 -51
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/routeInfo.d.ts +6 -6
- package/dist/esm/router.d.ts +6 -5
- package/dist/esm/router.js +13 -6
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/searchParams.d.ts +3 -3
- package/dist/esm/searchParams.js.map +1 -1
- package/dist/esm/useMatch.js +4 -2
- package/dist/esm/useMatch.js.map +1 -1
- package/dist/esm/utils.d.ts +17 -5
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/validators.d.ts +49 -0
- package/package.json +3 -3
- package/src/fileRoute.ts +2 -2
- package/src/index.tsx +18 -15
- package/src/location.ts +2 -2
- package/src/matchContext.tsx +5 -0
- package/src/route.ts +32 -79
- package/src/routeInfo.ts +6 -6
- package/src/router.ts +30 -20
- package/src/searchParams.ts +2 -2
- package/src/useMatch.tsx +4 -2
- package/src/utils.ts +38 -8
- package/src/validators.ts +106 -0
package/src/router.ts
CHANGED
|
@@ -42,8 +42,6 @@ import type {
|
|
|
42
42
|
AnyContext,
|
|
43
43
|
AnyRoute,
|
|
44
44
|
AnyRouteWithContext,
|
|
45
|
-
AnySearchSchema,
|
|
46
|
-
AnySearchValidator,
|
|
47
45
|
BeforeLoadContextOptions,
|
|
48
46
|
ErrorRouteComponent,
|
|
49
47
|
LoaderFnContext,
|
|
@@ -84,6 +82,7 @@ import type { AnyRedirect, ResolvedRedirect } from './redirects'
|
|
|
84
82
|
import type { NotFoundError } from './not-found'
|
|
85
83
|
import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
|
|
86
84
|
import type { RouterTransformer } from './transformer'
|
|
85
|
+
import type { AnySchema, AnyValidator } from './validators'
|
|
87
86
|
|
|
88
87
|
declare global {
|
|
89
88
|
interface Window {
|
|
@@ -491,7 +490,7 @@ export interface RouterErrorSerializer<TSerializedError> {
|
|
|
491
490
|
|
|
492
491
|
export interface RouterState<
|
|
493
492
|
TRouteTree extends AnyRoute = AnyRoute,
|
|
494
|
-
TRouteMatch =
|
|
493
|
+
TRouteMatch = MakeRouteMatchUnion,
|
|
495
494
|
> {
|
|
496
495
|
status: 'pending' | 'idle'
|
|
497
496
|
loadedAt: number
|
|
@@ -579,10 +578,7 @@ function routeNeedsPreload(route: AnyRoute) {
|
|
|
579
578
|
return false
|
|
580
579
|
}
|
|
581
580
|
|
|
582
|
-
function validateSearch(
|
|
583
|
-
validateSearch: AnySearchValidator,
|
|
584
|
-
input: unknown,
|
|
585
|
-
): unknown {
|
|
581
|
+
function validateSearch(validateSearch: AnyValidator, input: unknown): unknown {
|
|
586
582
|
if (validateSearch == null) return {}
|
|
587
583
|
|
|
588
584
|
if ('~standard' in validateSearch) {
|
|
@@ -1075,7 +1071,7 @@ export class Router<
|
|
|
1075
1071
|
*/
|
|
1076
1072
|
public matchRoutes(
|
|
1077
1073
|
pathname: string,
|
|
1078
|
-
locationSearch:
|
|
1074
|
+
locationSearch: AnySchema,
|
|
1079
1075
|
opts?: MatchRoutesOpts,
|
|
1080
1076
|
): Array<AnyRouteMatch>
|
|
1081
1077
|
public matchRoutes(
|
|
@@ -1085,7 +1081,7 @@ export class Router<
|
|
|
1085
1081
|
|
|
1086
1082
|
public matchRoutes(
|
|
1087
1083
|
pathnameOrNext: string | ParsedLocation,
|
|
1088
|
-
locationSearchOrOpts?:
|
|
1084
|
+
locationSearchOrOpts?: AnySchema | MatchRoutesOpts,
|
|
1089
1085
|
opts?: MatchRoutesOpts,
|
|
1090
1086
|
) {
|
|
1091
1087
|
if (typeof pathnameOrNext === 'string') {
|
|
@@ -1246,9 +1242,11 @@ export class Router<
|
|
|
1246
1242
|
// pending matches that are still loading
|
|
1247
1243
|
const existingMatch = this.getMatch(matchId)
|
|
1248
1244
|
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1245
|
+
const previousMatch = this.state.matches.find(
|
|
1246
|
+
(d) => d.routeId === route.id,
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1249
|
+
const cause = previousMatch ? 'stay' : 'enter'
|
|
1252
1250
|
|
|
1253
1251
|
let match: AnyRouteMatch
|
|
1254
1252
|
|
|
@@ -1256,7 +1254,12 @@ export class Router<
|
|
|
1256
1254
|
match = {
|
|
1257
1255
|
...existingMatch,
|
|
1258
1256
|
cause,
|
|
1259
|
-
params:
|
|
1257
|
+
params: previousMatch
|
|
1258
|
+
? replaceEqualDeep(previousMatch.params, routeParams)
|
|
1259
|
+
: routeParams,
|
|
1260
|
+
search: previousMatch
|
|
1261
|
+
? replaceEqualDeep(previousMatch.search, preMatchSearch)
|
|
1262
|
+
: replaceEqualDeep(existingMatch.search, preMatchSearch),
|
|
1260
1263
|
}
|
|
1261
1264
|
} else {
|
|
1262
1265
|
const status =
|
|
@@ -1271,10 +1274,14 @@ export class Router<
|
|
|
1271
1274
|
id: matchId,
|
|
1272
1275
|
index,
|
|
1273
1276
|
routeId: route.id,
|
|
1274
|
-
params:
|
|
1277
|
+
params: previousMatch
|
|
1278
|
+
? replaceEqualDeep(previousMatch.params, routeParams)
|
|
1279
|
+
: routeParams,
|
|
1275
1280
|
pathname: joinPaths([this.basepath, interpolatedPath]),
|
|
1276
1281
|
updatedAt: Date.now(),
|
|
1277
|
-
search:
|
|
1282
|
+
search: previousMatch
|
|
1283
|
+
? replaceEqualDeep(previousMatch.search, preMatchSearch)
|
|
1284
|
+
: preMatchSearch,
|
|
1278
1285
|
searchError: undefined,
|
|
1279
1286
|
status,
|
|
1280
1287
|
isFetching: false,
|
|
@@ -1286,7 +1293,9 @@ export class Router<
|
|
|
1286
1293
|
abortController: new AbortController(),
|
|
1287
1294
|
fetchCount: 0,
|
|
1288
1295
|
cause,
|
|
1289
|
-
loaderDeps
|
|
1296
|
+
loaderDeps: previousMatch
|
|
1297
|
+
? replaceEqualDeep(previousMatch.loaderDeps, loaderDeps)
|
|
1298
|
+
: loaderDeps,
|
|
1290
1299
|
invalid: false,
|
|
1291
1300
|
preload: false,
|
|
1292
1301
|
links: route.options.links?.(),
|
|
@@ -1318,10 +1327,7 @@ export class Router<
|
|
|
1318
1327
|
match.globalNotFound = globalNotFoundRouteId === route.id
|
|
1319
1328
|
}
|
|
1320
1329
|
|
|
1321
|
-
//
|
|
1322
|
-
// a new one, we need to update the match's search params
|
|
1323
|
-
match.search = replaceEqualDeep(match.search, preMatchSearch)
|
|
1324
|
-
// And also update the searchError if there is one
|
|
1330
|
+
// update the searchError if there is one
|
|
1325
1331
|
match.searchError = searchError
|
|
1326
1332
|
|
|
1327
1333
|
const parentMatchId = parentMatch?.id
|
|
@@ -2560,6 +2566,10 @@ export class Router<
|
|
|
2560
2566
|
// to be preloaded before we resolve the match
|
|
2561
2567
|
await route._componentsPromise
|
|
2562
2568
|
} catch (err) {
|
|
2569
|
+
updateMatch(matchId, (prev) => ({
|
|
2570
|
+
...prev,
|
|
2571
|
+
loaderPromise: undefined,
|
|
2572
|
+
}))
|
|
2563
2573
|
handleRedirectAndNotFound(this.getMatch(matchId)!, err)
|
|
2564
2574
|
}
|
|
2565
2575
|
}
|
package/src/searchParams.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { decode, encode } from './qss'
|
|
2
|
-
import type {
|
|
2
|
+
import type { AnySchema } from './validators'
|
|
3
3
|
|
|
4
4
|
export const defaultParseSearch = parseSearchWith(JSON.parse)
|
|
5
5
|
export const defaultStringifySearch = stringifySearchWith(
|
|
@@ -8,7 +8,7 @@ export const defaultStringifySearch = stringifySearchWith(
|
|
|
8
8
|
)
|
|
9
9
|
|
|
10
10
|
export function parseSearchWith(parser: (str: string) => any) {
|
|
11
|
-
return (searchStr: string):
|
|
11
|
+
return (searchStr: string): AnySchema => {
|
|
12
12
|
if (searchStr.substring(0, 1) === '?') {
|
|
13
13
|
searchStr = searchStr.substring(1)
|
|
14
14
|
}
|
package/src/useMatch.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import invariant from 'tiny-invariant'
|
|
3
3
|
import { useRouterState } from './useRouterState'
|
|
4
|
-
import { matchContext } from './matchContext'
|
|
4
|
+
import { dummyMatchContext, matchContext } from './matchContext'
|
|
5
5
|
import type {
|
|
6
6
|
StructuralSharingOption,
|
|
7
7
|
ValidateSelected,
|
|
@@ -84,7 +84,9 @@ export function useMatch<
|
|
|
84
84
|
TStructuralSharing
|
|
85
85
|
>,
|
|
86
86
|
): ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow> {
|
|
87
|
-
const nearestMatchId = React.useContext(
|
|
87
|
+
const nearestMatchId = React.useContext(
|
|
88
|
+
opts.from ? dummyMatchContext : matchContext,
|
|
89
|
+
)
|
|
88
90
|
|
|
89
91
|
const matchSelection = useRouterState({
|
|
90
92
|
select: (state: any) => {
|
package/src/utils.ts
CHANGED
|
@@ -72,12 +72,12 @@ export type NonNullableUpdater<TPrevious, TResult = TPrevious> =
|
|
|
72
72
|
| TResult
|
|
73
73
|
| ((prev: TPrevious) => TResult)
|
|
74
74
|
|
|
75
|
-
export type
|
|
75
|
+
export type ExtractObjects<TUnion> = TUnion extends MergeAllPrimitive
|
|
76
76
|
? never
|
|
77
77
|
: TUnion
|
|
78
78
|
|
|
79
|
-
export type
|
|
80
|
-
|
|
79
|
+
export type PartialMergeAllObject<TUnion> =
|
|
80
|
+
ExtractObjects<TUnion> extends infer TObj
|
|
81
81
|
? {
|
|
82
82
|
[TKey in TObj extends any ? keyof TObj : never]?: TObj extends any
|
|
83
83
|
? TKey extends keyof TObj
|
|
@@ -87,23 +87,53 @@ export type MergeUnionObject<TUnion> =
|
|
|
87
87
|
}
|
|
88
88
|
: never
|
|
89
89
|
|
|
90
|
-
export type
|
|
90
|
+
export type MergeAllPrimitive =
|
|
91
91
|
| ReadonlyArray<any>
|
|
92
92
|
| number
|
|
93
93
|
| string
|
|
94
94
|
| bigint
|
|
95
95
|
| boolean
|
|
96
96
|
| symbol
|
|
97
|
+
| undefined
|
|
98
|
+
| null
|
|
97
99
|
|
|
98
|
-
export type
|
|
100
|
+
export type ExtractPrimitives<TUnion> = TUnion extends MergeAllPrimitive
|
|
99
101
|
? TUnion
|
|
100
102
|
: TUnion extends object
|
|
101
103
|
? never
|
|
102
104
|
: TUnion
|
|
103
105
|
|
|
104
|
-
export type
|
|
105
|
-
|
|
|
106
|
-
|
|
|
106
|
+
export type PartialMergeAll<TUnion> =
|
|
107
|
+
| ExtractPrimitives<TUnion>
|
|
108
|
+
| PartialMergeAllObject<TUnion>
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* To be added to router types
|
|
112
|
+
*/
|
|
113
|
+
export type UnionToIntersection<T> = (
|
|
114
|
+
T extends any ? (arg: T) => any : never
|
|
115
|
+
) extends (arg: infer T) => any
|
|
116
|
+
? T
|
|
117
|
+
: never
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Merges everything in a union into one object.
|
|
121
|
+
* This mapped type is homomorphic which means it preserves stuff! :)
|
|
122
|
+
*/
|
|
123
|
+
export type MergeAllObjects<
|
|
124
|
+
TUnion,
|
|
125
|
+
TIntersected = UnionToIntersection<ExtractObjects<TUnion>>,
|
|
126
|
+
> = [keyof TIntersected] extends [never]
|
|
127
|
+
? never
|
|
128
|
+
: {
|
|
129
|
+
[TKey in keyof TIntersected]: TUnion extends any
|
|
130
|
+
? TUnion[TKey & keyof TUnion]
|
|
131
|
+
: never
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type MergeAll<TUnion> =
|
|
135
|
+
| MergeAllObjects<TUnion>
|
|
136
|
+
| ExtractPrimitives<TUnion>
|
|
107
137
|
|
|
108
138
|
export type Constrain<T, TConstraint, TDefault = TConstraint> =
|
|
109
139
|
| (T extends TConstraint ? T : never)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { SearchSchemaInput } from './route'
|
|
2
|
+
|
|
3
|
+
export interface StandardSchemaValidatorProps<TInput, TOutput> {
|
|
4
|
+
readonly types?: StandardSchemaValidatorTypes<TInput, TOutput> | undefined
|
|
5
|
+
readonly validate: AnyStandardSchemaValidate
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface StandardSchemaValidator<TInput, TOutput> {
|
|
9
|
+
readonly '~standard': StandardSchemaValidatorProps<TInput, TOutput>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type AnyStandardSchemaValidator = StandardSchemaValidator<any, any>
|
|
13
|
+
|
|
14
|
+
export interface StandardSchemaValidatorTypes<TInput, TOutput> {
|
|
15
|
+
readonly input: TInput
|
|
16
|
+
readonly output: TOutput
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AnyStandardSchemaValidateSuccess {
|
|
20
|
+
readonly value: any
|
|
21
|
+
readonly issues?: undefined
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AnyStandardSchemaValidateFailure {
|
|
25
|
+
readonly issues: ReadonlyArray<AnyStandardSchemaValidateIssue>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface AnyStandardSchemaValidateIssue {
|
|
29
|
+
readonly message: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AnyStandardSchemaValidateInput {
|
|
33
|
+
readonly value: any
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type AnyStandardSchemaValidate = (
|
|
37
|
+
value: unknown,
|
|
38
|
+
) =>
|
|
39
|
+
| (AnyStandardSchemaValidateSuccess | AnyStandardSchemaValidateFailure)
|
|
40
|
+
| Promise<AnyStandardSchemaValidateSuccess | AnyStandardSchemaValidateFailure>
|
|
41
|
+
|
|
42
|
+
export interface ValidatorObj<TInput, TOutput> {
|
|
43
|
+
parse: ValidatorFn<TInput, TOutput>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type AnyValidatorObj = ValidatorObj<any, any>
|
|
47
|
+
|
|
48
|
+
export interface ValidatorAdapter<TInput, TOutput> {
|
|
49
|
+
types: {
|
|
50
|
+
input: TInput
|
|
51
|
+
output: TOutput
|
|
52
|
+
}
|
|
53
|
+
parse: (input: unknown) => TOutput
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type AnyValidatorAdapter = ValidatorAdapter<any, any>
|
|
57
|
+
|
|
58
|
+
export type AnyValidatorFn = ValidatorFn<any, any>
|
|
59
|
+
|
|
60
|
+
export type ValidatorFn<TInput, TOutput> = (input: TInput) => TOutput
|
|
61
|
+
|
|
62
|
+
export type Validator<TInput, TOutput> =
|
|
63
|
+
| ValidatorObj<TInput, TOutput>
|
|
64
|
+
| ValidatorFn<TInput, TOutput>
|
|
65
|
+
| ValidatorAdapter<TInput, TOutput>
|
|
66
|
+
| StandardSchemaValidator<TInput, TOutput>
|
|
67
|
+
| undefined
|
|
68
|
+
|
|
69
|
+
export type AnyValidator = Validator<any, any>
|
|
70
|
+
|
|
71
|
+
export type AnySchema = {}
|
|
72
|
+
|
|
73
|
+
export type DefaultValidator = Validator<Record<string, unknown>, AnySchema>
|
|
74
|
+
|
|
75
|
+
export type ResolveValidatorInputFn<TValidator> = TValidator extends (
|
|
76
|
+
input: infer TSchemaInput,
|
|
77
|
+
) => any
|
|
78
|
+
? TSchemaInput extends SearchSchemaInput
|
|
79
|
+
? Omit<TSchemaInput, keyof SearchSchemaInput>
|
|
80
|
+
: ResolveValidatorOutputFn<TValidator>
|
|
81
|
+
: AnySchema
|
|
82
|
+
|
|
83
|
+
export type ResolveValidatorInput<TValidator> =
|
|
84
|
+
TValidator extends AnyStandardSchemaValidator
|
|
85
|
+
? NonNullable<TValidator['~standard']['types']>['input']
|
|
86
|
+
: TValidator extends AnyValidatorAdapter
|
|
87
|
+
? TValidator['types']['input']
|
|
88
|
+
: TValidator extends AnyValidatorObj
|
|
89
|
+
? ResolveValidatorInputFn<TValidator['parse']>
|
|
90
|
+
: ResolveValidatorInputFn<TValidator>
|
|
91
|
+
|
|
92
|
+
export type ResolveValidatorOutputFn<TValidator> = TValidator extends (
|
|
93
|
+
...args: any
|
|
94
|
+
) => infer TSchema
|
|
95
|
+
? TSchema
|
|
96
|
+
: AnySchema
|
|
97
|
+
|
|
98
|
+
export type ResolveValidatorOutput<TValidator> = unknown extends TValidator
|
|
99
|
+
? TValidator
|
|
100
|
+
: TValidator extends AnyStandardSchemaValidator
|
|
101
|
+
? NonNullable<TValidator['~standard']['types']>['output']
|
|
102
|
+
: TValidator extends AnyValidatorAdapter
|
|
103
|
+
? TValidator['types']['output']
|
|
104
|
+
: TValidator extends AnyValidatorObj
|
|
105
|
+
? ResolveValidatorOutputFn<TValidator['parse']>
|
|
106
|
+
: ResolveValidatorOutputFn<TValidator>
|