@remix-run/router 1.3.2 → 1.3.3-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 +8 -0
- package/dist/index.d.ts +2 -1
- package/dist/router.cjs.js +106 -45
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +8 -0
- package/dist/router.js +106 -45
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +106 -45
- 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/index.ts +2 -1
- package/package.json +1 -1
- package/router.ts +129 -54
package/index.ts
CHANGED
|
@@ -63,7 +63,6 @@ export {
|
|
|
63
63
|
createPath,
|
|
64
64
|
createHashHistory,
|
|
65
65
|
createMemoryHistory,
|
|
66
|
-
invariant,
|
|
67
66
|
parsePath,
|
|
68
67
|
} from "./history";
|
|
69
68
|
|
|
@@ -82,3 +81,5 @@ export {
|
|
|
82
81
|
convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes,
|
|
83
82
|
getPathContributingMatches as UNSAFE_getPathContributingMatches,
|
|
84
83
|
} from "./utils";
|
|
84
|
+
|
|
85
|
+
export { invariant as UNSAFE_invariant } from "./history";
|
package/package.json
CHANGED
package/router.ts
CHANGED
|
@@ -211,6 +211,15 @@ export interface Router {
|
|
|
211
211
|
*/
|
|
212
212
|
deleteBlocker(key: string): void;
|
|
213
213
|
|
|
214
|
+
/**
|
|
215
|
+
* @internal
|
|
216
|
+
* PRIVATE - DO NOT USE
|
|
217
|
+
*
|
|
218
|
+
* HMR needs to pass in-flight route updates to React Router
|
|
219
|
+
* TODO: Replace this with granular route update APIs (addRoute, updateRoute, deleteRoute)
|
|
220
|
+
*/
|
|
221
|
+
_internalSetRoutes(routes: AgnosticRouteObject[]): void;
|
|
222
|
+
|
|
214
223
|
/**
|
|
215
224
|
* @internal
|
|
216
225
|
* PRIVATE - DO NOT USE
|
|
@@ -556,8 +565,6 @@ interface HandleLoadersResult extends ShortCircuitable {
|
|
|
556
565
|
interface FetchLoadMatch {
|
|
557
566
|
routeId: string;
|
|
558
567
|
path: string;
|
|
559
|
-
match: AgnosticDataRouteMatch;
|
|
560
|
-
matches: AgnosticDataRouteMatch[];
|
|
561
568
|
}
|
|
562
569
|
|
|
563
570
|
/**
|
|
@@ -565,6 +572,8 @@ interface FetchLoadMatch {
|
|
|
565
572
|
*/
|
|
566
573
|
interface RevalidatingFetcher extends FetchLoadMatch {
|
|
567
574
|
key: string;
|
|
575
|
+
match: AgnosticDataRouteMatch | null;
|
|
576
|
+
matches: AgnosticDataRouteMatch[] | null;
|
|
568
577
|
}
|
|
569
578
|
|
|
570
579
|
/**
|
|
@@ -644,6 +653,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
644
653
|
);
|
|
645
654
|
|
|
646
655
|
let dataRoutes = convertRoutesToDataRoutes(init.routes);
|
|
656
|
+
let inFlightDataRoutes: AgnosticDataRouteObject[] | undefined;
|
|
647
657
|
// Cleanup function for history
|
|
648
658
|
let unlistenHistory: (() => void) | null = null;
|
|
649
659
|
// Externally-provided functions to call on all state changes
|
|
@@ -921,6 +931,11 @@ export function createRouter(init: RouterInit): Router {
|
|
|
921
931
|
isMutationMethod(state.navigation.formMethod) &&
|
|
922
932
|
location.state?._isRedirect !== true);
|
|
923
933
|
|
|
934
|
+
if (inFlightDataRoutes) {
|
|
935
|
+
dataRoutes = inFlightDataRoutes;
|
|
936
|
+
inFlightDataRoutes = undefined;
|
|
937
|
+
}
|
|
938
|
+
|
|
924
939
|
updateState({
|
|
925
940
|
...newState, // matches, errors, fetchers go through as-is
|
|
926
941
|
actionData,
|
|
@@ -1108,14 +1123,15 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1108
1123
|
saveScrollPosition(state.location, state.matches);
|
|
1109
1124
|
pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
|
|
1110
1125
|
|
|
1126
|
+
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
1111
1127
|
let loadingNavigation = opts && opts.overrideNavigation;
|
|
1112
|
-
let matches = matchRoutes(
|
|
1128
|
+
let matches = matchRoutes(routesToUse, location, init.basename);
|
|
1113
1129
|
|
|
1114
1130
|
// Short circuit with a 404 on the root error boundary if we match nothing
|
|
1115
1131
|
if (!matches) {
|
|
1116
1132
|
let error = getInternalRouterError(404, { pathname: location.pathname });
|
|
1117
1133
|
let { matches: notFoundMatches, route } =
|
|
1118
|
-
getShortCircuitMatches(
|
|
1134
|
+
getShortCircuitMatches(routesToUse);
|
|
1119
1135
|
// Cancel all pending deferred on 404s since we don't keep any routes
|
|
1120
1136
|
cancelActiveDeferreds();
|
|
1121
1137
|
completeNavigation(location, {
|
|
@@ -1352,6 +1368,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1352
1368
|
}
|
|
1353
1369
|
: undefined;
|
|
1354
1370
|
|
|
1371
|
+
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
1355
1372
|
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(
|
|
1356
1373
|
init.history,
|
|
1357
1374
|
state,
|
|
@@ -1361,9 +1378,11 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1361
1378
|
isRevalidationRequired,
|
|
1362
1379
|
cancelledDeferredRoutes,
|
|
1363
1380
|
cancelledFetcherLoads,
|
|
1381
|
+
fetchLoadMatches,
|
|
1382
|
+
routesToUse,
|
|
1383
|
+
init.basename,
|
|
1364
1384
|
pendingActionData,
|
|
1365
|
-
pendingError
|
|
1366
|
-
fetchLoadMatches
|
|
1385
|
+
pendingError
|
|
1367
1386
|
);
|
|
1368
1387
|
|
|
1369
1388
|
// Cancel pending deferreds for no-longer-matched routes or routes we're
|
|
@@ -1506,7 +1525,8 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1506
1525
|
|
|
1507
1526
|
if (fetchControllers.has(key)) abortFetcher(key);
|
|
1508
1527
|
|
|
1509
|
-
let
|
|
1528
|
+
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
1529
|
+
let matches = matchRoutes(routesToUse, href, init.basename);
|
|
1510
1530
|
if (!matches) {
|
|
1511
1531
|
setFetcherError(
|
|
1512
1532
|
key,
|
|
@@ -1528,7 +1548,7 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1528
1548
|
|
|
1529
1549
|
// Store off the match so we can call it's shouldRevalidate on subsequent
|
|
1530
1550
|
// revalidations
|
|
1531
|
-
fetchLoadMatches.set(key, { routeId, path
|
|
1551
|
+
fetchLoadMatches.set(key, { routeId, path });
|
|
1532
1552
|
handleFetcherLoader(key, routeId, path, match, matches, submission);
|
|
1533
1553
|
}
|
|
1534
1554
|
|
|
@@ -1629,9 +1649,10 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1629
1649
|
nextLocation,
|
|
1630
1650
|
abortController.signal
|
|
1631
1651
|
);
|
|
1652
|
+
let routesToUse = inFlightDataRoutes || dataRoutes;
|
|
1632
1653
|
let matches =
|
|
1633
1654
|
state.navigation.state !== "idle"
|
|
1634
|
-
? matchRoutes(
|
|
1655
|
+
? matchRoutes(routesToUse, state.navigation.location, init.basename)
|
|
1635
1656
|
: state.matches;
|
|
1636
1657
|
|
|
1637
1658
|
invariant(matches, "Didn't find any matches after fetcher action");
|
|
@@ -1656,9 +1677,11 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1656
1677
|
isRevalidationRequired,
|
|
1657
1678
|
cancelledDeferredRoutes,
|
|
1658
1679
|
cancelledFetcherLoads,
|
|
1680
|
+
fetchLoadMatches,
|
|
1681
|
+
routesToUse,
|
|
1682
|
+
init.basename,
|
|
1659
1683
|
{ [match.route.id]: actionResult.data },
|
|
1660
|
-
undefined
|
|
1661
|
-
fetchLoadMatches
|
|
1684
|
+
undefined // No need to send through errors since we short circuit above
|
|
1662
1685
|
);
|
|
1663
1686
|
|
|
1664
1687
|
// Put all revalidating fetchers into the loading state, except for the
|
|
@@ -1997,15 +2020,23 @@ export function createRouter(init: RouterInit): Router {
|
|
|
1997
2020
|
...matchesToLoad.map((match) =>
|
|
1998
2021
|
callLoaderOrAction("loader", request, match, matches, router.basename)
|
|
1999
2022
|
),
|
|
2000
|
-
...fetchersToLoad.map((f) =>
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2023
|
+
...fetchersToLoad.map((f) => {
|
|
2024
|
+
if (f.matches && f.match) {
|
|
2025
|
+
return callLoaderOrAction(
|
|
2026
|
+
"loader",
|
|
2027
|
+
createClientSideRequest(init.history, f.path, request.signal),
|
|
2028
|
+
f.match,
|
|
2029
|
+
f.matches,
|
|
2030
|
+
router.basename
|
|
2031
|
+
);
|
|
2032
|
+
} else {
|
|
2033
|
+
let error: ErrorResult = {
|
|
2034
|
+
type: ResultType.error,
|
|
2035
|
+
error: getInternalRouterError(404, { pathname: f.path }),
|
|
2036
|
+
};
|
|
2037
|
+
return error;
|
|
2038
|
+
}
|
|
2039
|
+
}),
|
|
2009
2040
|
]);
|
|
2010
2041
|
let loaderResults = results.slice(0, matchesToLoad.length);
|
|
2011
2042
|
let fetcherResults = results.slice(matchesToLoad.length);
|
|
@@ -2266,6 +2297,10 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2266
2297
|
return null;
|
|
2267
2298
|
}
|
|
2268
2299
|
|
|
2300
|
+
function _internalSetRoutes(newRoutes: AgnosticDataRouteObject[]) {
|
|
2301
|
+
inFlightDataRoutes = newRoutes;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2269
2304
|
router = {
|
|
2270
2305
|
get basename() {
|
|
2271
2306
|
return init.basename;
|
|
@@ -2293,6 +2328,9 @@ export function createRouter(init: RouterInit): Router {
|
|
|
2293
2328
|
deleteBlocker,
|
|
2294
2329
|
_internalFetchControllers: fetchControllers,
|
|
2295
2330
|
_internalActiveDeferreds: activeDeferreds,
|
|
2331
|
+
// TODO: Remove setRoutes, it's temporary to avoid dealing with
|
|
2332
|
+
// updating the tree while validating the update algorithm.
|
|
2333
|
+
_internalSetRoutes,
|
|
2296
2334
|
};
|
|
2297
2335
|
|
|
2298
2336
|
return router;
|
|
@@ -2891,9 +2929,11 @@ function getMatchesToLoad(
|
|
|
2891
2929
|
isRevalidationRequired: boolean,
|
|
2892
2930
|
cancelledDeferredRoutes: string[],
|
|
2893
2931
|
cancelledFetcherLoads: string[],
|
|
2932
|
+
fetchLoadMatches: Map<string, FetchLoadMatch>,
|
|
2933
|
+
routesToUse: AgnosticDataRouteObject[],
|
|
2934
|
+
basename: string | undefined,
|
|
2894
2935
|
pendingActionData?: RouteData,
|
|
2895
|
-
pendingError?: RouteData
|
|
2896
|
-
fetchLoadMatches?: Map<string, FetchLoadMatch>
|
|
2936
|
+
pendingError?: RouteData
|
|
2897
2937
|
): [AgnosticDataRouteMatch[], RevalidatingFetcher[]] {
|
|
2898
2938
|
let actionResult = pendingError
|
|
2899
2939
|
? Object.values(pendingError)[0]
|
|
@@ -2951,34 +2991,55 @@ function getMatchesToLoad(
|
|
|
2951
2991
|
|
|
2952
2992
|
// Pick fetcher.loads that need to be revalidated
|
|
2953
2993
|
let revalidatingFetchers: RevalidatingFetcher[] = [];
|
|
2954
|
-
fetchLoadMatches
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2994
|
+
fetchLoadMatches.forEach((f, key) => {
|
|
2995
|
+
// Don't revalidate if fetcher won't be present in the subsequent render
|
|
2996
|
+
if (!matches.some((m) => m.route.id === f.routeId)) {
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
let fetcherMatches = matchRoutes(routesToUse, f.path, basename);
|
|
3001
|
+
|
|
3002
|
+
// If the fetcher path no longer matches, push it in with null matches so
|
|
3003
|
+
// we can trigger a 404 in callLoadersAndMaybeResolveData
|
|
3004
|
+
if (!fetcherMatches) {
|
|
3005
|
+
revalidatingFetchers.push({ key, ...f, matches: null, match: null });
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
let fetcherMatch = getTargetMatch(fetcherMatches, f.path);
|
|
3010
|
+
|
|
3011
|
+
if (cancelledFetcherLoads.includes(key)) {
|
|
3012
|
+
revalidatingFetchers.push({
|
|
3013
|
+
key,
|
|
3014
|
+
matches: fetcherMatches,
|
|
3015
|
+
match: fetcherMatch,
|
|
3016
|
+
...f,
|
|
3017
|
+
});
|
|
3018
|
+
return;
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
// Revalidating fetchers are decoupled from the route matches since they
|
|
3022
|
+
// hit a static href, so they _always_ check shouldRevalidate and the
|
|
3023
|
+
// default is strictly if a revalidation is explicitly required (action
|
|
3024
|
+
// submissions, useRevalidator, X-Remix-Revalidate).
|
|
3025
|
+
let shouldRevalidate = shouldRevalidateLoader(fetcherMatch, {
|
|
3026
|
+
currentUrl,
|
|
3027
|
+
currentParams: state.matches[state.matches.length - 1].params,
|
|
3028
|
+
nextUrl,
|
|
3029
|
+
nextParams: matches[matches.length - 1].params,
|
|
3030
|
+
...submission,
|
|
3031
|
+
actionResult,
|
|
3032
|
+
defaultShouldRevalidate,
|
|
2981
3033
|
});
|
|
3034
|
+
if (shouldRevalidate) {
|
|
3035
|
+
revalidatingFetchers.push({
|
|
3036
|
+
key,
|
|
3037
|
+
matches: fetcherMatches,
|
|
3038
|
+
match: fetcherMatch,
|
|
3039
|
+
...f,
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
});
|
|
2982
3043
|
|
|
2983
3044
|
return [navigationMatches, revalidatingFetchers];
|
|
2984
3045
|
}
|
|
@@ -3183,7 +3244,12 @@ async function callLoaderOrAction(
|
|
|
3183
3244
|
}
|
|
3184
3245
|
|
|
3185
3246
|
if (result instanceof DeferredData) {
|
|
3186
|
-
return {
|
|
3247
|
+
return {
|
|
3248
|
+
type: ResultType.deferred,
|
|
3249
|
+
deferredData: result,
|
|
3250
|
+
statusCode: result.init?.status,
|
|
3251
|
+
headers: result.init?.headers && new Headers(result.init.headers),
|
|
3252
|
+
};
|
|
3187
3253
|
}
|
|
3188
3254
|
|
|
3189
3255
|
return { type: ResultType.data, data: result };
|
|
@@ -3356,7 +3422,7 @@ function processLoaderData(
|
|
|
3356
3422
|
|
|
3357
3423
|
// Process fetcher non-redirect errors
|
|
3358
3424
|
if (isErrorResult(result)) {
|
|
3359
|
-
let boundaryMatch = findNearestBoundary(state.matches, match
|
|
3425
|
+
let boundaryMatch = findNearestBoundary(state.matches, match?.route.id);
|
|
3360
3426
|
if (!(errors && errors[boundaryMatch.route.id])) {
|
|
3361
3427
|
errors = {
|
|
3362
3428
|
...errors,
|
|
@@ -3406,7 +3472,9 @@ function mergeLoaderData(
|
|
|
3406
3472
|
// incoming object with an undefined value, which is how we unset a prior
|
|
3407
3473
|
// loaderData if we encounter a loader error
|
|
3408
3474
|
}
|
|
3409
|
-
} else if (loaderData[id] !== undefined) {
|
|
3475
|
+
} else if (loaderData[id] !== undefined && match.route.loader) {
|
|
3476
|
+
// Preserve existing keys not included in newLoaderData and where a loader
|
|
3477
|
+
// wasn't removed by HMR
|
|
3410
3478
|
mergedLoaderData[id] = loaderData[id];
|
|
3411
3479
|
}
|
|
3412
3480
|
|
|
@@ -3580,7 +3648,7 @@ function isMutationMethod(method?: string): method is MutationFormMethod {
|
|
|
3580
3648
|
|
|
3581
3649
|
async function resolveDeferredResults(
|
|
3582
3650
|
currentMatches: AgnosticDataRouteMatch[],
|
|
3583
|
-
matchesToLoad: AgnosticDataRouteMatch[],
|
|
3651
|
+
matchesToLoad: (AgnosticDataRouteMatch | null)[],
|
|
3584
3652
|
results: DataResult[],
|
|
3585
3653
|
signal: AbortSignal,
|
|
3586
3654
|
isFetcher: boolean,
|
|
@@ -3589,8 +3657,15 @@ async function resolveDeferredResults(
|
|
|
3589
3657
|
for (let index = 0; index < results.length; index++) {
|
|
3590
3658
|
let result = results[index];
|
|
3591
3659
|
let match = matchesToLoad[index];
|
|
3660
|
+
// If we don't have a match, then we can have a deferred result to do
|
|
3661
|
+
// anything with. This is for revalidating fetchers where the route was
|
|
3662
|
+
// removed during HMR
|
|
3663
|
+
if (!match) {
|
|
3664
|
+
continue;
|
|
3665
|
+
}
|
|
3666
|
+
|
|
3592
3667
|
let currentMatch = currentMatches.find(
|
|
3593
|
-
(m) => m.route.id === match
|
|
3668
|
+
(m) => m.route.id === match!.route.id
|
|
3594
3669
|
);
|
|
3595
3670
|
let isRevalidatingLoader =
|
|
3596
3671
|
currentMatch != null &&
|