@tanstack/react-router 0.0.1-beta.225 → 0.0.1-beta.227
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/Matches.js +8 -6
- package/build/cjs/Matches.js.map +1 -1
- package/build/cjs/RouterProvider.js +1 -26
- package/build/cjs/RouterProvider.js.map +1 -1
- package/build/cjs/fileRoute.js.map +1 -1
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js +54 -54
- package/build/cjs/router.js.map +1 -1
- package/build/esm/index.js +62 -86
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +291 -285
- package/build/types/Matches.d.ts +0 -1
- package/build/types/RouterProvider.d.ts +1 -6
- package/build/types/fileRoute.d.ts +2 -2
- package/build/types/route.d.ts +7 -3
- package/build/types/router.d.ts +7 -3
- package/build/umd/index.development.js +62 -86
- 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 -2
- package/src/Matches.tsx +21 -23
- package/src/RouterProvider.tsx +10 -79
- package/src/fileRoute.ts +7 -6
- package/src/route.ts +39 -39
- package/src/router.ts +95 -88
package/build/types/Matches.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ export interface RouteMatch<TRouteTree extends AnyRoute = AnyRoute, TRouteId ext
|
|
|
20
20
|
loaderData?: RouteById<TRouteTree, TRouteId>['types']['loaderData'];
|
|
21
21
|
__resolveLoadPromise?: () => void;
|
|
22
22
|
context: RouteById<TRouteTree, TRouteId>['types']['allContext'];
|
|
23
|
-
routeSearch: RouteById<TRouteTree, TRouteId>['types']['searchSchema'];
|
|
24
23
|
search: FullSearchSchema<TRouteTree> & RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema'];
|
|
25
24
|
fetchedAt: number;
|
|
26
25
|
shouldReloadDeps: any;
|
|
@@ -3,7 +3,7 @@ import { LinkInfo, LinkOptions, NavigateOptions, ResolveRelativePath, ToOptions
|
|
|
3
3
|
import { ParsedLocation } from './location';
|
|
4
4
|
import { AnyRoute } from './route';
|
|
5
5
|
import { RouteById, RoutePaths } from './routeInfo';
|
|
6
|
-
import { BuildNextOptions,
|
|
6
|
+
import { BuildNextOptions, RegisteredRouter, Router, RouterOptions, RouterState } from './router';
|
|
7
7
|
import { NoInfer } from './utils';
|
|
8
8
|
import { MatchRouteOptions } from './Matches';
|
|
9
9
|
import { RouteMatch } from './Matches';
|
|
@@ -21,11 +21,6 @@ export interface MatchLocation {
|
|
|
21
21
|
export type BuildLinkFn<TRouteTree extends AnyRoute> = <TFrom extends RoutePaths<TRouteTree> = '/', TTo extends string = ''>(dest: LinkOptions<TRouteTree, TFrom, TTo>) => LinkInfo;
|
|
22
22
|
export type NavigateFn<TRouteTree extends AnyRoute> = <TFrom extends RoutePaths<TRouteTree> = '/', TTo extends string = '', TMaskFrom extends RoutePaths<TRouteTree> = TFrom, TMaskTo extends string = ''>(opts: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>) => Promise<void>;
|
|
23
23
|
export type MatchRouteFn<TRouteTree extends AnyRoute> = <TFrom extends RoutePaths<TRouteTree> = '/', TTo extends string = '', TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>>(location: ToOptions<TRouteTree, TFrom, TTo>, opts?: MatchRouteOptions) => false | RouteById<TRouteTree, TResolved>['types']['allParams'];
|
|
24
|
-
export type LoadFn = (opts?: {
|
|
25
|
-
next?: ParsedLocation;
|
|
26
|
-
throwOnError?: boolean;
|
|
27
|
-
__dehydratedMatches?: DehydratedRouteMatch[];
|
|
28
|
-
}) => Promise<void>;
|
|
29
24
|
export type BuildLocationFn<TRouteTree extends AnyRoute> = (opts: BuildNextOptions) => ParsedLocation;
|
|
30
25
|
export type InjectedHtmlEntry = string | (() => Promise<string> | string);
|
|
31
26
|
export declare const routerContext: React.Context<Router<any, Record<string, any>>>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ParsePathParams } from './link';
|
|
2
|
-
import { AnyRoute, ResolveFullPath, ResolveFullSearchSchema, MergeFromFromParent, RouteContext, AnyContext, RouteOptions, UpdatableRouteOptions, Route,
|
|
2
|
+
import { AnyRoute, ResolveFullPath, ResolveFullSearchSchema, MergeFromFromParent, RouteContext, AnyContext, RouteOptions, UpdatableRouteOptions, Route, RootRouteId, TrimPathLeft, RouteConstraints } from './route';
|
|
3
3
|
import { Assign, Expand, IsAny } from './utils';
|
|
4
4
|
export interface FileRoutesByPath {
|
|
5
5
|
}
|
|
@@ -13,7 +13,7 @@ export type FileRoutePath<TParentRoute extends AnyRoute, TFilePath extends strin
|
|
|
13
13
|
export declare class FileRoute<TFilePath extends keyof FileRoutesByPath, TParentRoute extends AnyRoute = FileRoutesByPath[TFilePath]['parentRoute'], TId extends RouteConstraints['TId'] = TFilePath, TPath extends RouteConstraints['TPath'] = FileRoutePath<TParentRoute, TFilePath>, TFullPath extends RouteConstraints['TFullPath'] = ResolveFullPath<TParentRoute, RemoveUnderScores<TPath>>> {
|
|
14
14
|
path: TFilePath;
|
|
15
15
|
constructor(path: TFilePath);
|
|
16
|
-
createRoute: <TSearchSchema extends import("./route").AnySearchSchema = {}, TFullSearchSchema extends import("./route").AnySearchSchema = ResolveFullSearchSchema<TParentRoute, TSearchSchema>, TParams extends Record<string, any> =
|
|
16
|
+
createRoute: <TSearchSchema extends import("./route").AnySearchSchema = {}, TFullSearchSchema extends import("./route").AnySearchSchema = ResolveFullSearchSchema<TParentRoute, TSearchSchema>, TParams extends Record<string, any> = Expand<Record<ParsePathParams<TPath>, string>>, TAllParams extends Record<string, any> = MergeFromFromParent<TParentRoute["types"]["allParams"], TParams>, TRouteContext extends RouteContext = RouteContext, TContext extends Expand<Assign<IsAny<TParentRoute["types"]["allContext"], {}>, TRouteContext>> = Expand<Assign<IsAny<TParentRoute["types"]["allContext"], {}>, TRouteContext>>, TRouterContext extends AnyContext = AnyContext, TLoaderData extends unknown = unknown, TChildren extends unknown = unknown, TRouteTree extends AnyRoute = AnyRoute>(options?: (Omit<RouteOptions<TParentRoute, string, TPath, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TRouteContext, TContext, TLoaderData>, "path" | "id" | "getParentRoute"> & {
|
|
17
17
|
meta?: import("./route").RouteMeta | undefined;
|
|
18
18
|
} & {
|
|
19
19
|
caseSensitive?: boolean | undefined;
|
package/build/types/route.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export type MetaOptions = keyof PickRequired<RouteMeta> extends never ? {
|
|
|
29
29
|
} : {
|
|
30
30
|
meta: RouteMeta;
|
|
31
31
|
};
|
|
32
|
-
export type RouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TSearchSchema extends Record<string, any> = {}, TFullSearchSchema extends Record<string, any> = TSearchSchema, TParams extends AnyPathParams = AnyPathParams, TAllParams extends AnyPathParams = TParams, TRouteContext extends RouteContext = RouteContext, TAllContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = BaseRouteOptions<TParentRoute, TCustomId, TPath, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TRouteContext, TAllContext, TLoaderData> & NoInfer<
|
|
32
|
+
export type RouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TSearchSchema extends Record<string, any> = {}, TFullSearchSchema extends Record<string, any> = TSearchSchema, TParams extends AnyPathParams = AnyPathParams, TAllParams extends AnyPathParams = TParams, TRouteContext extends RouteContext = RouteContext, TAllContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = BaseRouteOptions<TParentRoute, TCustomId, TPath, TSearchSchema, TFullSearchSchema, TParams, TAllParams, TRouteContext, TAllContext, TLoaderData> & UpdatableRouteOptions<NoInfer<TFullSearchSchema>, NoInfer<TAllParams>, NoInfer<TAllContext>, NoInfer<TLoaderData>>;
|
|
33
33
|
export type ParamsFallback<TPath extends string, TParams> = unknown extends TParams ? Record<ParsePathParams<TPath>, string> : TParams;
|
|
34
34
|
export type BaseRouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, TSearchSchema extends Record<string, any> = {}, TFullSearchSchema extends Record<string, any> = TSearchSchema, TParams extends AnyPathParams = {}, TAllParams = ParamsFallback<TPath, TParams>, TRouteContext extends RouteContext = RouteContext, TAllContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = RoutePathOptions<TCustomId, TPath> & {
|
|
35
35
|
getParentRoute: () => TParentRoute;
|
|
@@ -40,7 +40,11 @@ export type BaseRouteOptions<TParentRoute extends AnyRoute = AnyRoute, TCustomId
|
|
|
40
40
|
} : {
|
|
41
41
|
beforeLoad: BeforeLoadFn<TFullSearchSchema, TParentRoute, TAllParams, TRouteContext>;
|
|
42
42
|
}) & {
|
|
43
|
-
|
|
43
|
+
key?: (opts: {
|
|
44
|
+
search: TFullSearchSchema;
|
|
45
|
+
location: ParsedLocation;
|
|
46
|
+
}) => any;
|
|
47
|
+
loader?: RouteLoaderFn<TAllParams, TFullSearchSchema, NoInfer<TAllContext>, NoInfer<TRouteContext>, TLoaderData>;
|
|
44
48
|
} & ({
|
|
45
49
|
parseParams?: (rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>) => TParams extends Record<ParsePathParams<TPath>, any> ? TParams : 'parseParams must return an object';
|
|
46
50
|
stringifyParams?: (params: NoInfer<ParamsFallback<TPath, TParams>>) => Record<ParsePathParams<TPath>, string>;
|
|
@@ -87,7 +91,7 @@ export type DefinedPathParamWarning = 'Path params cannot be redefined by child
|
|
|
87
91
|
export type ParentParams<TParentParams> = AnyPathParams extends TParentParams ? {} : {
|
|
88
92
|
[Key in keyof TParentParams]?: DefinedPathParamWarning;
|
|
89
93
|
};
|
|
90
|
-
export type
|
|
94
|
+
export type RouteLoaderFn<TAllParams = {}, TFullSearchSchema extends Record<string, any> = {}, TAllContext extends Record<string, any> = AnyContext, TRouteContext extends Record<string, any> = AnyContext, TLoaderData extends any = unknown> = (match: LoaderFnContext<TAllParams, TFullSearchSchema, TAllContext, TRouteContext>) => Promise<TLoaderData> | TLoaderData;
|
|
91
95
|
export interface LoaderFnContext<TAllParams = {}, TFullSearchSchema extends Record<string, any> = {}, TAllContext extends Record<string, any> = AnyContext, TRouteContext extends Record<string, any> = AnyContext> {
|
|
92
96
|
abortController: AbortController;
|
|
93
97
|
preload: boolean;
|
package/build/types/router.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { AnyRouteMatch, RouteMatch } from './Matches';
|
|
|
9
9
|
import { ParsedLocation } from './location';
|
|
10
10
|
import { LocationState } from './location';
|
|
11
11
|
import { SearchSerializer, SearchParser } from './searchParams';
|
|
12
|
-
import { BuildLinkFn, BuildLocationFn, CommitLocationOptions, InjectedHtmlEntry,
|
|
12
|
+
import { BuildLinkFn, BuildLocationFn, CommitLocationOptions, InjectedHtmlEntry, MatchRouteFn, NavigateFn } from './RouterProvider';
|
|
13
13
|
declare global {
|
|
14
14
|
interface Window {
|
|
15
15
|
__TSR_DEHYDRATED__?: HydrationCtx;
|
|
@@ -152,12 +152,16 @@ export declare class Router<TRouteTree extends AnyRoute = AnyRoute, TDehydrated
|
|
|
152
152
|
commitLocation: ({ startTransition, ...next }: ParsedLocation & CommitLocationOptions) => Promise<void>;
|
|
153
153
|
buildAndCommitLocation: ({ replace, resetScroll, startTransition, ...rest }?: BuildNextOptions & CommitLocationOptions) => Promise<void>;
|
|
154
154
|
navigate: NavigateFn<TRouteTree>;
|
|
155
|
-
loadMatches: ({ checkLatest, matches, preload, }: {
|
|
155
|
+
loadMatches: ({ checkLatest, matches, preload, invalidate, }: {
|
|
156
156
|
checkLatest: () => Promise<void> | undefined;
|
|
157
157
|
matches: AnyRouteMatch[];
|
|
158
158
|
preload?: boolean | undefined;
|
|
159
|
+
invalidate?: boolean | undefined;
|
|
159
160
|
}) => Promise<RouteMatch[]>;
|
|
160
|
-
|
|
161
|
+
invalidate: () => Promise<void>;
|
|
162
|
+
load: (opts?: {
|
|
163
|
+
invalidate?: boolean;
|
|
164
|
+
}) => Promise<void>;
|
|
161
165
|
preloadRoute: (navigateOpts?: BuildNextOptions) => Promise<readonly [RouteMatch<AnyRoute, any>, RouteMatch<AnyRoute, any>[]]>;
|
|
162
166
|
buildLink: BuildLinkFn<TRouteTree>;
|
|
163
167
|
matchRoute: MatchRouteFn<TRouteTree>;
|
|
@@ -963,7 +963,6 @@
|
|
|
963
963
|
matches: matches
|
|
964
964
|
}) : null));
|
|
965
965
|
}
|
|
966
|
-
const defaultPending = () => null;
|
|
967
966
|
function SafeFragment(props) {
|
|
968
967
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.children);
|
|
969
968
|
}
|
|
@@ -978,9 +977,9 @@
|
|
|
978
977
|
const routeId = match?.routeId;
|
|
979
978
|
const route = routesById[routeId];
|
|
980
979
|
const locationKey = useRouterState().location.state?.key;
|
|
981
|
-
const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent
|
|
980
|
+
const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent;
|
|
982
981
|
const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
|
|
983
|
-
const ResolvedSuspenseBoundary = route.options.wrapInSuspense ? React__namespace.Suspense : SafeFragment;
|
|
982
|
+
const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? PendingComponent ? React__namespace.Suspense : SafeFragment;
|
|
984
983
|
const errorComponent = routeErrorComponent ? React__namespace.useCallback(props => {
|
|
985
984
|
return /*#__PURE__*/React__namespace.createElement(routeErrorComponent, {
|
|
986
985
|
...props,
|
|
@@ -990,6 +989,7 @@
|
|
|
990
989
|
useParams: route.useParams
|
|
991
990
|
});
|
|
992
991
|
}, [route]) : undefined;
|
|
992
|
+
const ResolvedCatchBoundary = errorComponent ? CatchBoundary : SafeFragment;
|
|
993
993
|
return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
|
|
994
994
|
value: matches
|
|
995
995
|
}, /*#__PURE__*/React__namespace.createElement(ResolvedSuspenseBoundary, {
|
|
@@ -999,7 +999,7 @@
|
|
|
999
999
|
useSearch: route.useSearch,
|
|
1000
1000
|
useParams: route.useParams
|
|
1001
1001
|
})
|
|
1002
|
-
},
|
|
1002
|
+
}, /*#__PURE__*/React__namespace.createElement(ResolvedCatchBoundary, {
|
|
1003
1003
|
resetKey: locationKey,
|
|
1004
1004
|
errorComponent: errorComponent,
|
|
1005
1005
|
onCatch: () => {
|
|
@@ -1007,8 +1007,6 @@
|
|
|
1007
1007
|
}
|
|
1008
1008
|
}, /*#__PURE__*/React__namespace.createElement(MatchInner, {
|
|
1009
1009
|
match: match
|
|
1010
|
-
})) : /*#__PURE__*/React__namespace.createElement(SafeFragment, null, /*#__PURE__*/React__namespace.createElement(MatchInner, {
|
|
1011
|
-
match: match
|
|
1012
1010
|
}))));
|
|
1013
1011
|
}
|
|
1014
1012
|
function MatchInner({
|
|
@@ -1019,6 +1017,9 @@
|
|
|
1019
1017
|
routesById
|
|
1020
1018
|
} = useRouter();
|
|
1021
1019
|
const route = routesById[match.routeId];
|
|
1020
|
+
if (match.id.split('/').length === 4) {
|
|
1021
|
+
console.log(match.id, pick(match, ['status', 'cause', 'isFetching']));
|
|
1022
|
+
}
|
|
1022
1023
|
if (match.status === 'error') {
|
|
1023
1024
|
throw match.error;
|
|
1024
1025
|
}
|
|
@@ -1112,31 +1113,6 @@
|
|
|
1112
1113
|
return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
|
|
1113
1114
|
}
|
|
1114
1115
|
|
|
1115
|
-
// export type RouterContext<
|
|
1116
|
-
// TRouteTree extends AnyRoute,
|
|
1117
|
-
// // TDehydrated extends Record<string, any>,
|
|
1118
|
-
// > = {
|
|
1119
|
-
// buildLink: BuildLinkFn<TRouteTree>
|
|
1120
|
-
// state: RouterState<TRouteTree>
|
|
1121
|
-
// navigate: NavigateFn<TRouteTree>
|
|
1122
|
-
// matchRoute: MatchRouteFn<TRouteTree>
|
|
1123
|
-
// routeTree: TRouteTree
|
|
1124
|
-
// routesById: RoutesById<TRouteTree>
|
|
1125
|
-
// options: RouterOptions<TRouteTree>
|
|
1126
|
-
// history: RouterHistory
|
|
1127
|
-
// load: LoadFn
|
|
1128
|
-
// buildLocation: BuildLocationFn<TRouteTree>
|
|
1129
|
-
// subscribe: Router<TRouteTree>['subscribe']
|
|
1130
|
-
// resetNextScrollRef: React.MutableRefObject<boolean>
|
|
1131
|
-
// injectedHtmlRef: React.MutableRefObject<InjectedHtmlEntry[]>
|
|
1132
|
-
// injectHtml: (entry: InjectedHtmlEntry) => void
|
|
1133
|
-
// dehydrateData: <T>(
|
|
1134
|
-
// key: any,
|
|
1135
|
-
// getData: T | (() => Promise<T> | T),
|
|
1136
|
-
// ) => () => void
|
|
1137
|
-
// hydrateData: <T>(key: any) => T | undefined
|
|
1138
|
-
// }
|
|
1139
|
-
|
|
1140
1116
|
const routerContext = /*#__PURE__*/React__namespace.createContext(null);
|
|
1141
1117
|
if (typeof document !== 'undefined') {
|
|
1142
1118
|
window.__TSR_ROUTER_CONTEXT__ = routerContext;
|
|
@@ -1205,7 +1181,7 @@
|
|
|
1205
1181
|
return () => {
|
|
1206
1182
|
unsub();
|
|
1207
1183
|
};
|
|
1208
|
-
}, [history]);
|
|
1184
|
+
}, [router.history]);
|
|
1209
1185
|
React__namespace.useLayoutEffect(() => {
|
|
1210
1186
|
if (!isTransitioning && state.resolvedLocation !== state.location) {
|
|
1211
1187
|
router.emit({
|
|
@@ -1810,38 +1786,66 @@
|
|
|
1810
1786
|
}
|
|
1811
1787
|
return;
|
|
1812
1788
|
});
|
|
1813
|
-
const matches =
|
|
1789
|
+
const matches = [];
|
|
1790
|
+
matchedRoutes.forEach((route, index) => {
|
|
1791
|
+
// Take each matched route and resolve + validate its search params
|
|
1792
|
+
// This has to happen serially because each route's search params
|
|
1793
|
+
// can depend on the parent route's search params
|
|
1794
|
+
// It must also happen before we create the match so that we can
|
|
1795
|
+
// pass the search params to the route's potential key function
|
|
1796
|
+
// which is used to uniquely identify the route match in state
|
|
1797
|
+
|
|
1798
|
+
const parentMatch = matches[index - 1];
|
|
1799
|
+
const [preMatchSearch, searchError] = (() => {
|
|
1800
|
+
// Validate the search params and stabilize them
|
|
1801
|
+
const parentSearch = parentMatch?.search ?? locationSearch;
|
|
1802
|
+
try {
|
|
1803
|
+
const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
|
|
1804
|
+
let search = validator?.(parentSearch) ?? {};
|
|
1805
|
+
return [{
|
|
1806
|
+
...parentSearch,
|
|
1807
|
+
...search
|
|
1808
|
+
}, undefined];
|
|
1809
|
+
} catch (err) {
|
|
1810
|
+
const searchError = new SearchParamError(err.message, {
|
|
1811
|
+
cause: err
|
|
1812
|
+
});
|
|
1813
|
+
if (opts?.throwOnError) {
|
|
1814
|
+
throw searchError;
|
|
1815
|
+
}
|
|
1816
|
+
return [parentSearch, searchError];
|
|
1817
|
+
}
|
|
1818
|
+
})();
|
|
1814
1819
|
const interpolatedPath = interpolatePath(route.path, routeParams);
|
|
1815
|
-
const matchId = interpolatePath(route.id, routeParams, true)
|
|
1820
|
+
const matchId = interpolatePath(route.id, routeParams, true) + (route.options.key?.({
|
|
1821
|
+
search: preMatchSearch,
|
|
1822
|
+
location: this.state.location
|
|
1823
|
+
}) ?? '');
|
|
1816
1824
|
|
|
1817
1825
|
// Waste not, want not. If we already have a match for this route,
|
|
1818
1826
|
// reuse it. This is important for layout routes, which might stick
|
|
1819
1827
|
// around between navigation actions that only change leaf routes.
|
|
1820
1828
|
const existingMatch = getRouteMatch(this.state, matchId);
|
|
1821
1829
|
const cause = this.state.matches.find(d => d.id === matchId) ? 'stay' : 'enter';
|
|
1822
|
-
if (existingMatch) {
|
|
1823
|
-
return {
|
|
1824
|
-
...existingMatch,
|
|
1825
|
-
cause
|
|
1826
|
-
};
|
|
1827
|
-
}
|
|
1828
1830
|
|
|
1829
1831
|
// Create a fresh route match
|
|
1830
1832
|
const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
|
|
1831
|
-
const
|
|
1833
|
+
const match = existingMatch ? {
|
|
1834
|
+
...existingMatch,
|
|
1835
|
+
cause
|
|
1836
|
+
} : {
|
|
1832
1837
|
id: matchId,
|
|
1833
1838
|
routeId: route.id,
|
|
1834
1839
|
params: routeParams,
|
|
1835
1840
|
pathname: joinPaths([this.basepath, interpolatedPath]),
|
|
1836
1841
|
updatedAt: Date.now(),
|
|
1837
|
-
routeSearch: {},
|
|
1838
1842
|
search: {},
|
|
1843
|
+
searchError: undefined,
|
|
1839
1844
|
status: hasLoaders ? 'pending' : 'success',
|
|
1840
1845
|
isFetching: false,
|
|
1841
1846
|
invalid: false,
|
|
1842
1847
|
error: undefined,
|
|
1843
1848
|
paramsError: parseErrors[index],
|
|
1844
|
-
searchError: undefined,
|
|
1845
1849
|
loadPromise: Promise.resolve(),
|
|
1846
1850
|
context: undefined,
|
|
1847
1851
|
abortController: new AbortController(),
|
|
@@ -1849,46 +1853,13 @@
|
|
|
1849
1853
|
fetchedAt: 0,
|
|
1850
1854
|
cause
|
|
1851
1855
|
};
|
|
1852
|
-
return routeMatch;
|
|
1853
|
-
});
|
|
1854
1856
|
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
const searchInfo = (() => {
|
|
1862
|
-
// Validate the search params and stabilize them
|
|
1863
|
-
const parentSearchInfo = {
|
|
1864
|
-
search: parentMatch?.search ?? locationSearch,
|
|
1865
|
-
routeSearch: parentMatch?.routeSearch ?? locationSearch
|
|
1866
|
-
};
|
|
1867
|
-
try {
|
|
1868
|
-
const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
|
|
1869
|
-
let routeSearch = validator?.(parentSearchInfo.search) ?? {};
|
|
1870
|
-
let search = {
|
|
1871
|
-
...parentSearchInfo.search,
|
|
1872
|
-
...routeSearch
|
|
1873
|
-
};
|
|
1874
|
-
routeSearch = replaceEqualDeep(match.routeSearch, routeSearch);
|
|
1875
|
-
search = replaceEqualDeep(match.search, search);
|
|
1876
|
-
return {
|
|
1877
|
-
routeSearch,
|
|
1878
|
-
search,
|
|
1879
|
-
searchDidChange: match.routeSearch !== routeSearch
|
|
1880
|
-
};
|
|
1881
|
-
} catch (err) {
|
|
1882
|
-
match.searchError = new SearchParamError(err.message, {
|
|
1883
|
-
cause: err
|
|
1884
|
-
});
|
|
1885
|
-
if (opts?.throwOnError) {
|
|
1886
|
-
throw match.searchError;
|
|
1887
|
-
}
|
|
1888
|
-
return parentSearchInfo;
|
|
1889
|
-
}
|
|
1890
|
-
})();
|
|
1891
|
-
Object.assign(match, searchInfo);
|
|
1857
|
+
// Regardless of whether we're reusing an existing match or creating
|
|
1858
|
+
// a new one, we need to update the match's search params
|
|
1859
|
+
match.search = replaceEqualDeep(match.search, preMatchSearch);
|
|
1860
|
+
// And also update the searchError if there is one
|
|
1861
|
+
match.searchError = searchError;
|
|
1862
|
+
matches.push(match);
|
|
1892
1863
|
});
|
|
1893
1864
|
return matches;
|
|
1894
1865
|
};
|
|
@@ -2082,7 +2053,8 @@
|
|
|
2082
2053
|
loadMatches = async ({
|
|
2083
2054
|
checkLatest,
|
|
2084
2055
|
matches,
|
|
2085
|
-
preload
|
|
2056
|
+
preload,
|
|
2057
|
+
invalidate
|
|
2086
2058
|
}) => {
|
|
2087
2059
|
let latestPromise;
|
|
2088
2060
|
let firstBadMatchIndex;
|
|
@@ -2198,7 +2170,7 @@
|
|
|
2198
2170
|
// Default to reloading the route all the time
|
|
2199
2171
|
let shouldReload = true;
|
|
2200
2172
|
let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!(route.options.shouldReload ?? true);
|
|
2201
|
-
if (match.cause === 'enter') {
|
|
2173
|
+
if (match.cause === 'enter' || invalidate) {
|
|
2202
2174
|
match.shouldReloadDeps = shouldReloadDeps;
|
|
2203
2175
|
} else if (match.cause === 'stay') {
|
|
2204
2176
|
if (typeof shouldReloadDeps === 'object') {
|
|
@@ -2281,7 +2253,10 @@
|
|
|
2281
2253
|
await Promise.all(matchPromises);
|
|
2282
2254
|
return matches;
|
|
2283
2255
|
};
|
|
2284
|
-
|
|
2256
|
+
invalidate = () => this.load({
|
|
2257
|
+
invalidate: true
|
|
2258
|
+
});
|
|
2259
|
+
load = async opts => {
|
|
2285
2260
|
const promise = new Promise(async (resolve, reject) => {
|
|
2286
2261
|
const next = this.latestLocation;
|
|
2287
2262
|
const prevLocation = this.state.resolvedLocation;
|
|
@@ -2316,7 +2291,8 @@
|
|
|
2316
2291
|
// Load the matches
|
|
2317
2292
|
await this.loadMatches({
|
|
2318
2293
|
matches,
|
|
2319
|
-
checkLatest: () => this.checkLatest(promise)
|
|
2294
|
+
checkLatest: () => this.checkLatest(promise),
|
|
2295
|
+
invalidate: opts?.invalidate
|
|
2320
2296
|
});
|
|
2321
2297
|
} catch (err) {
|
|
2322
2298
|
// swallow this error, since we'll display the
|