@remix-run/router 1.13.1 → 1.14.0-pre.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/CHANGELOG.md +181 -0
- package/dist/index.d.ts +1 -1
- package/dist/router.cjs.js +103 -35
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +16 -0
- package/dist/router.js +94 -35
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +103 -35
- package/dist/router.umd.js.map +1 -1
- package/dist/router.umd.min.js +2 -2
- package/dist/router.umd.min.js.map +1 -1
- package/dist/utils.d.ts +5 -2
- package/index.ts +1 -1
- package/package.json +1 -1
- package/router.ts +120 -21
- package/utils.ts +27 -5
package/dist/utils.d.ts
CHANGED
|
@@ -135,9 +135,11 @@ type DataFunctionValue = Response | NonNullable<unknown> | null;
|
|
|
135
135
|
/**
|
|
136
136
|
* Route loader function signature
|
|
137
137
|
*/
|
|
138
|
-
export
|
|
138
|
+
export type LoaderFunction<Context = any> = {
|
|
139
139
|
(args: LoaderFunctionArgs<Context>): Promise<DataFunctionValue> | DataFunctionValue;
|
|
140
|
-
}
|
|
140
|
+
} & {
|
|
141
|
+
hydrate?: boolean;
|
|
142
|
+
};
|
|
141
143
|
/**
|
|
142
144
|
* Route action function signature
|
|
143
145
|
*/
|
|
@@ -399,6 +401,7 @@ export declare function resolvePath(to: To, fromPathname?: string): Path;
|
|
|
399
401
|
* </Route>
|
|
400
402
|
*/
|
|
401
403
|
export declare function getPathContributingMatches<T extends AgnosticRouteMatch = AgnosticRouteMatch>(matches: T[]): T[];
|
|
404
|
+
export declare function getResolveToMatches<T extends AgnosticRouteMatch = AgnosticRouteMatch>(matches: T[], v7_relativeSplatPath: boolean): string[];
|
|
402
405
|
/**
|
|
403
406
|
* @private
|
|
404
407
|
*/
|
package/index.ts
CHANGED
|
@@ -87,7 +87,7 @@ export {
|
|
|
87
87
|
ErrorResponseImpl as UNSAFE_ErrorResponseImpl,
|
|
88
88
|
convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes,
|
|
89
89
|
convertRouteMatchToUiMatch as UNSAFE_convertRouteMatchToUiMatch,
|
|
90
|
-
|
|
90
|
+
getResolveToMatches as UNSAFE_getResolveToMatches,
|
|
91
91
|
} from "./utils";
|
|
92
92
|
|
|
93
93
|
export {
|
package/package.json
CHANGED
package/router.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
convertRouteMatchToUiMatch,
|
|
41
41
|
convertRoutesToDataRoutes,
|
|
42
42
|
getPathContributingMatches,
|
|
43
|
+
getResolveToMatches,
|
|
43
44
|
immutableRouteKeys,
|
|
44
45
|
isRouteErrorResponse,
|
|
45
46
|
joinPaths,
|
|
@@ -64,6 +65,14 @@ export interface Router {
|
|
|
64
65
|
*/
|
|
65
66
|
get basename(): RouterInit["basename"];
|
|
66
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @internal
|
|
70
|
+
* PRIVATE - DO NOT USE
|
|
71
|
+
*
|
|
72
|
+
* Return the future config for the router
|
|
73
|
+
*/
|
|
74
|
+
get future(): FutureConfig;
|
|
75
|
+
|
|
67
76
|
/**
|
|
68
77
|
* @internal
|
|
69
78
|
* PRIVATE - DO NOT USE
|
|
@@ -345,7 +354,9 @@ export type HydrationState = Partial<
|
|
|
345
354
|
export interface FutureConfig {
|
|
346
355
|
v7_fetcherPersist: boolean;
|
|
347
356
|
v7_normalizeFormMethod: boolean;
|
|
357
|
+
v7_partialHydration: boolean;
|
|
348
358
|
v7_prependBasename: boolean;
|
|
359
|
+
v7_relativeSplatPath: boolean;
|
|
349
360
|
}
|
|
350
361
|
|
|
351
362
|
/**
|
|
@@ -769,7 +780,9 @@ export function createRouter(init: RouterInit): Router {
|
|
|
769
780
|
let future: FutureConfig = {
|
|
770
781
|
v7_fetcherPersist: false,
|
|
771
782
|
v7_normalizeFormMethod: false,
|
|
783
|
+
v7_partialHydration: false,
|
|
772
784
|
v7_prependBasename: false,
|
|
785
|
+
v7_relativeSplatPath: false,
|
|
773
786
|
...init.future,
|
|
774
787
|
};
|
|
775
788
|
// Cleanup function for history
|
|
@@ -804,12 +817,19 @@ export function createRouter(init: RouterInit): Router {
|
|
|
804
817
|
initialErrors = { [route.id]: error };
|
|
805
818
|
}
|
|
806
819
|
|
|
820
|
+
// "Initialized" here really means "Can `RouterProvider` render my route tree?"
|
|
821
|
+
// Prior to `route.HydrateFallback`, we only had a root `fallbackElement` so we used
|
|
822
|
+
// `state.initialized` to render that instead of `<DataRoutes>`. Now that we
|
|
823
|
+
// support route level fallbacks we can always render and we'll just render
|
|
824
|
+
// as deep as we have data for and detect the nearest ancestor HydrateFallback
|
|
807
825
|
let initialized =
|
|
826
|
+
future.v7_partialHydration ||
|
|
808
827
|
// All initialMatches need to be loaded before we're ready. If we have lazy
|
|
809
828
|
// functions around still then we'll need to run them in initialize()
|
|
810
|
-
!initialMatches.some((m) => m.route.lazy) &&
|
|
811
|
-
|
|
812
|
-
|
|
829
|
+
(!initialMatches.some((m) => m.route.lazy) &&
|
|
830
|
+
// And we have to either have no loaders or have been provided hydrationData
|
|
831
|
+
(!initialMatches.some((m) => m.route.loader) ||
|
|
832
|
+
init.hydrationData != null));
|
|
813
833
|
|
|
814
834
|
let router: Router;
|
|
815
835
|
let state: RouterState = {
|
|
@@ -990,8 +1010,14 @@ export function createRouter(init: RouterInit): Router {
|
|
|
990
1010
|
// in the normal navigation flow. For SSR it's expected that lazy modules are
|
|
991
1011
|
// resolved prior to router creation since we can't go into a fallbackElement
|
|
992
1012
|
// UI for SSR'd apps
|
|
993
|
-
if (
|
|
994
|
-
|
|
1013
|
+
if (
|
|
1014
|
+
!state.initialized ||
|
|
1015
|
+
(future.v7_partialHydration &&
|
|
1016
|
+
state.matches.some((m) => isUnhydratedRoute(state, m.route)))
|
|
1017
|
+
) {
|
|
1018
|
+
startNavigation(HistoryAction.Pop, state.location, {
|
|
1019
|
+
initialHydration: true,
|
|
1020
|
+
});
|
|
995
1021
|
}
|
|
996
1022
|
|
|
997
1023
|
return router;
|
|
@@ -1231,6 +1257,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1231
1257
|
basename,
|
|
1232
1258
|
future.v7_prependBasename,
|
|
1233
1259
|
to,
|
|
1260
|
+
future.v7_relativeSplatPath,
|
|
1234
1261
|
opts?.fromRouteId,
|
|
1235
1262
|
opts?.relative
|
|
1236
1263
|
);
|
|
@@ -1363,6 +1390,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1363
1390
|
historyAction: HistoryAction,
|
|
1364
1391
|
location: Location,
|
|
1365
1392
|
opts?: {
|
|
1393
|
+
initialHydration?: boolean;
|
|
1366
1394
|
submission?: Submission;
|
|
1367
1395
|
fetcherSubmission?: Submission;
|
|
1368
1396
|
overrideNavigation?: Navigation;
|
|
@@ -1487,6 +1515,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1487
1515
|
opts && opts.submission,
|
|
1488
1516
|
opts && opts.fetcherSubmission,
|
|
1489
1517
|
opts && opts.replace,
|
|
1518
|
+
opts && opts.initialHydration === true,
|
|
1490
1519
|
flushSync,
|
|
1491
1520
|
pendingActionData,
|
|
1492
1521
|
pendingError
|
|
@@ -1545,7 +1574,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1545
1574
|
matches,
|
|
1546
1575
|
manifest,
|
|
1547
1576
|
mapRouteProperties,
|
|
1548
|
-
basename
|
|
1577
|
+
basename,
|
|
1578
|
+
future.v7_relativeSplatPath
|
|
1549
1579
|
);
|
|
1550
1580
|
|
|
1551
1581
|
if (request.signal.aborted) {
|
|
@@ -1607,6 +1637,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1607
1637
|
submission?: Submission,
|
|
1608
1638
|
fetcherSubmission?: Submission,
|
|
1609
1639
|
replace?: boolean,
|
|
1640
|
+
initialHydration?: boolean,
|
|
1610
1641
|
flushSync?: boolean,
|
|
1611
1642
|
pendingActionData?: RouteData,
|
|
1612
1643
|
pendingError?: RouteData
|
|
@@ -1629,6 +1660,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1629
1660
|
matches,
|
|
1630
1661
|
activeSubmission,
|
|
1631
1662
|
location,
|
|
1663
|
+
future.v7_partialHydration && initialHydration === true,
|
|
1632
1664
|
isRevalidationRequired,
|
|
1633
1665
|
cancelledDeferredRoutes,
|
|
1634
1666
|
cancelledFetcherLoads,
|
|
@@ -1674,7 +1706,12 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1674
1706
|
// state. If not, we need to switch to our loading state and load data,
|
|
1675
1707
|
// preserving any new action data or existing action data (in the case of
|
|
1676
1708
|
// a revalidation interrupting an actionReload)
|
|
1677
|
-
|
|
1709
|
+
// If we have partialHydration enabled, then don't update the state for the
|
|
1710
|
+
// initial data load since iot's not a "navigation"
|
|
1711
|
+
if (
|
|
1712
|
+
!isUninterruptedRevalidation &&
|
|
1713
|
+
(!future.v7_partialHydration || !initialHydration)
|
|
1714
|
+
) {
|
|
1678
1715
|
revalidatingFetchers.forEach((rf) => {
|
|
1679
1716
|
let fetcher = state.fetchers.get(rf.key);
|
|
1680
1717
|
let revalidatingFetcher = getLoadingFetcher(
|
|
@@ -1824,6 +1861,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1824
1861
|
basename,
|
|
1825
1862
|
future.v7_prependBasename,
|
|
1826
1863
|
href,
|
|
1864
|
+
future.v7_relativeSplatPath,
|
|
1827
1865
|
routeId,
|
|
1828
1866
|
opts?.relative
|
|
1829
1867
|
);
|
|
@@ -1930,7 +1968,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1930
1968
|
requestMatches,
|
|
1931
1969
|
manifest,
|
|
1932
1970
|
mapRouteProperties,
|
|
1933
|
-
basename
|
|
1971
|
+
basename,
|
|
1972
|
+
future.v7_relativeSplatPath
|
|
1934
1973
|
);
|
|
1935
1974
|
|
|
1936
1975
|
if (fetchRequest.signal.aborted) {
|
|
@@ -2003,6 +2042,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2003
2042
|
matches,
|
|
2004
2043
|
submission,
|
|
2005
2044
|
nextLocation,
|
|
2045
|
+
false,
|
|
2006
2046
|
isRevalidationRequired,
|
|
2007
2047
|
cancelledDeferredRoutes,
|
|
2008
2048
|
cancelledFetcherLoads,
|
|
@@ -2173,7 +2213,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2173
2213
|
matches,
|
|
2174
2214
|
manifest,
|
|
2175
2215
|
mapRouteProperties,
|
|
2176
|
-
basename
|
|
2216
|
+
basename,
|
|
2217
|
+
future.v7_relativeSplatPath
|
|
2177
2218
|
);
|
|
2178
2219
|
|
|
2179
2220
|
// Deferred isn't supported for fetcher loads, await everything and treat it
|
|
@@ -2369,7 +2410,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2369
2410
|
matches,
|
|
2370
2411
|
manifest,
|
|
2371
2412
|
mapRouteProperties,
|
|
2372
|
-
basename
|
|
2413
|
+
basename,
|
|
2414
|
+
future.v7_relativeSplatPath
|
|
2373
2415
|
)
|
|
2374
2416
|
),
|
|
2375
2417
|
...fetchersToLoad.map((f) => {
|
|
@@ -2381,7 +2423,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2381
2423
|
f.matches,
|
|
2382
2424
|
manifest,
|
|
2383
2425
|
mapRouteProperties,
|
|
2384
|
-
basename
|
|
2426
|
+
basename,
|
|
2427
|
+
future.v7_relativeSplatPath
|
|
2385
2428
|
);
|
|
2386
2429
|
} else {
|
|
2387
2430
|
let error: ErrorResult = {
|
|
@@ -2723,6 +2766,9 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2723
2766
|
get basename() {
|
|
2724
2767
|
return basename;
|
|
2725
2768
|
},
|
|
2769
|
+
get future() {
|
|
2770
|
+
return future;
|
|
2771
|
+
},
|
|
2726
2772
|
get state() {
|
|
2727
2773
|
return state;
|
|
2728
2774
|
},
|
|
@@ -2764,6 +2810,13 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2764
2810
|
|
|
2765
2811
|
export const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2766
2812
|
|
|
2813
|
+
/**
|
|
2814
|
+
* Future flags to toggle new feature behavior
|
|
2815
|
+
*/
|
|
2816
|
+
export interface StaticHandlerFutureConfig {
|
|
2817
|
+
v7_relativeSplatPath: boolean;
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2767
2820
|
export interface CreateStaticHandlerOptions {
|
|
2768
2821
|
basename?: string;
|
|
2769
2822
|
/**
|
|
@@ -2771,6 +2824,7 @@ export interface CreateStaticHandlerOptions {
|
|
|
2771
2824
|
*/
|
|
2772
2825
|
detectErrorBoundary?: DetectErrorBoundaryFunction;
|
|
2773
2826
|
mapRouteProperties?: MapRoutePropertiesFunction;
|
|
2827
|
+
future?: Partial<StaticHandlerFutureConfig>;
|
|
2774
2828
|
}
|
|
2775
2829
|
|
|
2776
2830
|
export function createStaticHandler(
|
|
@@ -2796,6 +2850,11 @@ export function createStaticHandler(
|
|
|
2796
2850
|
} else {
|
|
2797
2851
|
mapRouteProperties = defaultMapRouteProperties;
|
|
2798
2852
|
}
|
|
2853
|
+
// Config driven behavior flags
|
|
2854
|
+
let future: StaticHandlerFutureConfig = {
|
|
2855
|
+
v7_relativeSplatPath: false,
|
|
2856
|
+
...(opts ? opts.future : null),
|
|
2857
|
+
};
|
|
2799
2858
|
|
|
2800
2859
|
let dataRoutes = convertRoutesToDataRoutes(
|
|
2801
2860
|
routes,
|
|
@@ -3058,6 +3117,7 @@ export function createStaticHandler(
|
|
|
3058
3117
|
manifest,
|
|
3059
3118
|
mapRouteProperties,
|
|
3060
3119
|
basename,
|
|
3120
|
+
future.v7_relativeSplatPath,
|
|
3061
3121
|
{ isStaticRequest: true, isRouteRequest, requestContext }
|
|
3062
3122
|
);
|
|
3063
3123
|
|
|
@@ -3226,6 +3286,7 @@ export function createStaticHandler(
|
|
|
3226
3286
|
manifest,
|
|
3227
3287
|
mapRouteProperties,
|
|
3228
3288
|
basename,
|
|
3289
|
+
future.v7_relativeSplatPath,
|
|
3229
3290
|
{ isStaticRequest: true, isRouteRequest, requestContext }
|
|
3230
3291
|
)
|
|
3231
3292
|
),
|
|
@@ -3316,6 +3377,7 @@ function normalizeTo(
|
|
|
3316
3377
|
basename: string,
|
|
3317
3378
|
prependBasename: boolean,
|
|
3318
3379
|
to: To | null,
|
|
3380
|
+
v7_relativeSplatPath: boolean,
|
|
3319
3381
|
fromRouteId?: string,
|
|
3320
3382
|
relative?: RelativeRoutingType
|
|
3321
3383
|
) {
|
|
@@ -3340,7 +3402,7 @@ function normalizeTo(
|
|
|
3340
3402
|
// Resolve the relative path
|
|
3341
3403
|
let path = resolveTo(
|
|
3342
3404
|
to ? to : ".",
|
|
3343
|
-
|
|
3405
|
+
getResolveToMatches(contextualMatches, v7_relativeSplatPath),
|
|
3344
3406
|
stripBasename(location.pathname, basename) || location.pathname,
|
|
3345
3407
|
relative === "path"
|
|
3346
3408
|
);
|
|
@@ -3548,6 +3610,7 @@ function getMatchesToLoad(
|
|
|
3548
3610
|
matches: AgnosticDataRouteMatch[],
|
|
3549
3611
|
submission: Submission | undefined,
|
|
3550
3612
|
location: Location,
|
|
3613
|
+
isInitialLoad: boolean,
|
|
3551
3614
|
isRevalidationRequired: boolean,
|
|
3552
3615
|
cancelledDeferredRoutes: string[],
|
|
3553
3616
|
cancelledFetcherLoads: string[],
|
|
@@ -3573,10 +3636,17 @@ function getMatchesToLoad(
|
|
|
3573
3636
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3574
3637
|
|
|
3575
3638
|
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
3639
|
+
if (isInitialLoad) {
|
|
3640
|
+
// On initial hydration we don't do any shouldRevalidate stuff - we just
|
|
3641
|
+
// call the unhydrated loaders
|
|
3642
|
+
return isUnhydratedRoute(state, match.route);
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3576
3645
|
if (match.route.lazy) {
|
|
3577
3646
|
// We haven't loaded this route yet so we don't know if it's got a loader!
|
|
3578
3647
|
return true;
|
|
3579
3648
|
}
|
|
3649
|
+
|
|
3580
3650
|
if (match.route.loader == null) {
|
|
3581
3651
|
return false;
|
|
3582
3652
|
}
|
|
@@ -3618,8 +3688,13 @@ function getMatchesToLoad(
|
|
|
3618
3688
|
// Pick fetcher.loads that need to be revalidated
|
|
3619
3689
|
let revalidatingFetchers: RevalidatingFetcher[] = [];
|
|
3620
3690
|
fetchLoadMatches.forEach((f, key) => {
|
|
3621
|
-
// Don't revalidate
|
|
3691
|
+
// Don't revalidate:
|
|
3692
|
+
// - on initial load (shouldn't be any fetchers then anyway)
|
|
3693
|
+
// - if fetcher won't be present in the subsequent render
|
|
3694
|
+
// - no longer matches the URL (v7_fetcherPersist=false)
|
|
3695
|
+
// - was unmounted but persisted due to v7_fetcherPersist=true
|
|
3622
3696
|
if (
|
|
3697
|
+
isInitialLoad ||
|
|
3623
3698
|
!matches.some((m) => m.route.id === f.routeId) ||
|
|
3624
3699
|
deletedFetchers.has(key)
|
|
3625
3700
|
) {
|
|
@@ -3695,6 +3770,23 @@ function getMatchesToLoad(
|
|
|
3695
3770
|
return [navigationMatches, revalidatingFetchers];
|
|
3696
3771
|
}
|
|
3697
3772
|
|
|
3773
|
+
// Is this route unhydrated (when v7_partialHydration=true) such that we need
|
|
3774
|
+
// to call it's loader on the initial router creation
|
|
3775
|
+
function isUnhydratedRoute(state: RouterState, route: AgnosticDataRouteObject) {
|
|
3776
|
+
if (!route.loader) {
|
|
3777
|
+
return false;
|
|
3778
|
+
}
|
|
3779
|
+
if (route.loader.hydrate) {
|
|
3780
|
+
return true;
|
|
3781
|
+
}
|
|
3782
|
+
return (
|
|
3783
|
+
state.loaderData[route.id] === undefined &&
|
|
3784
|
+
(!state.errors ||
|
|
3785
|
+
// Loader ran but errored - don't re-run
|
|
3786
|
+
state.errors[route.id] === undefined)
|
|
3787
|
+
);
|
|
3788
|
+
}
|
|
3789
|
+
|
|
3698
3790
|
function isNewLoader(
|
|
3699
3791
|
currentLoaderData: RouteData,
|
|
3700
3792
|
currentMatch: AgnosticDataRouteMatch,
|
|
@@ -3830,6 +3922,7 @@ async function callLoaderOrAction(
|
|
|
3830
3922
|
manifest: RouteManifest,
|
|
3831
3923
|
mapRouteProperties: MapRoutePropertiesFunction,
|
|
3832
3924
|
basename: string,
|
|
3925
|
+
v7_relativeSplatPath: boolean,
|
|
3833
3926
|
opts: {
|
|
3834
3927
|
isStaticRequest?: boolean;
|
|
3835
3928
|
isRouteRequest?: boolean;
|
|
@@ -3943,7 +4036,8 @@ async function callLoaderOrAction(
|
|
|
3943
4036
|
matches.slice(0, matches.indexOf(match) + 1),
|
|
3944
4037
|
basename,
|
|
3945
4038
|
true,
|
|
3946
|
-
location
|
|
4039
|
+
location,
|
|
4040
|
+
v7_relativeSplatPath
|
|
3947
4041
|
);
|
|
3948
4042
|
} else if (!opts.isStaticRequest) {
|
|
3949
4043
|
// Strip off the protocol+origin for same-origin + same-basename absolute
|
|
@@ -3990,13 +4084,18 @@ async function callLoaderOrAction(
|
|
|
3990
4084
|
}
|
|
3991
4085
|
|
|
3992
4086
|
let data: any;
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4087
|
+
|
|
4088
|
+
try {
|
|
4089
|
+
let contentType = result.headers.get("Content-Type");
|
|
4090
|
+
// Check between word boundaries instead of startsWith() due to the last
|
|
4091
|
+
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
|
|
4092
|
+
if (contentType && /\bapplication\/json\b/.test(contentType)) {
|
|
4093
|
+
data = await result.json();
|
|
4094
|
+
} else {
|
|
4095
|
+
data = await result.text();
|
|
4096
|
+
}
|
|
4097
|
+
} catch (e) {
|
|
4098
|
+
return { type: ResultType.error, error: e };
|
|
4000
4099
|
}
|
|
4001
4100
|
|
|
4002
4101
|
if (resultType === ResultType.error) {
|
package/utils.ts
CHANGED
|
@@ -169,11 +169,11 @@ type DataFunctionValue = Response | NonNullable<unknown> | null;
|
|
|
169
169
|
/**
|
|
170
170
|
* Route loader function signature
|
|
171
171
|
*/
|
|
172
|
-
export
|
|
172
|
+
export type LoaderFunction<Context = any> = {
|
|
173
173
|
(args: LoaderFunctionArgs<Context>):
|
|
174
174
|
| Promise<DataFunctionValue>
|
|
175
175
|
| DataFunctionValue;
|
|
176
|
-
}
|
|
176
|
+
} & { hydrate?: boolean };
|
|
177
177
|
|
|
178
178
|
/**
|
|
179
179
|
* Route action function signature
|
|
@@ -1145,6 +1145,25 @@ export function getPathContributingMatches<
|
|
|
1145
1145
|
);
|
|
1146
1146
|
}
|
|
1147
1147
|
|
|
1148
|
+
// Return the array of pathnames for the current route matches - used to
|
|
1149
|
+
// generate the routePathnames input for resolveTo()
|
|
1150
|
+
export function getResolveToMatches<
|
|
1151
|
+
T extends AgnosticRouteMatch = AgnosticRouteMatch
|
|
1152
|
+
>(matches: T[], v7_relativeSplatPath: boolean) {
|
|
1153
|
+
let pathMatches = getPathContributingMatches(matches);
|
|
1154
|
+
|
|
1155
|
+
// When v7_relativeSplatPath is enabled, use the full pathname for the leaf
|
|
1156
|
+
// match so we include splat values for "." links. See:
|
|
1157
|
+
// https://github.com/remix-run/react-router/issues/11052#issuecomment-1836589329
|
|
1158
|
+
if (v7_relativeSplatPath) {
|
|
1159
|
+
return pathMatches.map((match, idx) =>
|
|
1160
|
+
idx === matches.length - 1 ? match.pathname : match.pathnameBase
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return pathMatches.map((match) => match.pathnameBase);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1148
1167
|
/**
|
|
1149
1168
|
* @private
|
|
1150
1169
|
*/
|
|
@@ -1191,9 +1210,12 @@ export function resolveTo(
|
|
|
1191
1210
|
if (toPathname == null) {
|
|
1192
1211
|
from = locationPathname;
|
|
1193
1212
|
} else if (isPathRelative) {
|
|
1194
|
-
let fromSegments =
|
|
1195
|
-
.
|
|
1196
|
-
|
|
1213
|
+
let fromSegments =
|
|
1214
|
+
routePathnames.length === 0
|
|
1215
|
+
? []
|
|
1216
|
+
: routePathnames[routePathnames.length - 1]
|
|
1217
|
+
.replace(/^\//, "")
|
|
1218
|
+
.split("/");
|
|
1197
1219
|
|
|
1198
1220
|
if (toPathname.startsWith("..")) {
|
|
1199
1221
|
let toSegments = toPathname.split("/");
|