@remix-run/router 1.3.3 → 1.4.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 +66 -0
- package/dist/history.d.ts +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/router.cjs.js +220 -90
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +6 -3
- package/dist/router.js +216 -90
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +220 -90
- 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 +25 -6
- package/history.ts +1 -1
- package/index.ts +6 -2
- package/package.json +1 -1
- package/router.ts +239 -30
- package/utils.ts +102 -69
package/dist/router.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.
|
|
2
|
+
* @remix-run/router v1.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
let location = createLocation(entries ? getCurrentLocation().pathname : "/", to, state, key);
|
|
97
|
-
warning
|
|
97
|
+
warning(location.pathname.charAt(0) === "/", "relative pathnames are not supported in memory history: " + JSON.stringify(to));
|
|
98
98
|
return location;
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -285,7 +285,7 @@
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
function validateHashLocation(location, to) {
|
|
288
|
-
warning
|
|
288
|
+
warning(location.pathname.charAt(0) === "/", "relative pathnames are not supported in hash history.push(" + JSON.stringify(to) + ")");
|
|
289
289
|
}
|
|
290
290
|
|
|
291
291
|
return getUrlBasedHistory(createHashLocation, createHashHref, validateHashLocation, options);
|
|
@@ -303,8 +303,7 @@
|
|
|
303
303
|
throw new Error(message);
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
|
-
|
|
307
|
-
function warning$1(cond, message) {
|
|
306
|
+
function warning(cond, message) {
|
|
308
307
|
if (!cond) {
|
|
309
308
|
// eslint-disable-next-line no-console
|
|
310
309
|
if (typeof console !== "undefined") console.warn(message);
|
|
@@ -567,40 +566,54 @@
|
|
|
567
566
|
ResultType["error"] = "error";
|
|
568
567
|
})(ResultType || (ResultType = {}));
|
|
569
568
|
|
|
569
|
+
const immutableRouteKeys = new Set(["lazy", "caseSensitive", "path", "id", "index", "children"]);
|
|
570
|
+
/**
|
|
571
|
+
* lazy() function to load a route definition, which can add non-matching
|
|
572
|
+
* related properties to a route
|
|
573
|
+
*/
|
|
574
|
+
|
|
570
575
|
function isIndexRoute(route) {
|
|
571
576
|
return route.index === true;
|
|
572
577
|
} // Walk the route tree generating unique IDs where necessary so we are working
|
|
573
578
|
// solely with AgnosticDataRouteObject's within the Router
|
|
574
579
|
|
|
575
580
|
|
|
576
|
-
function convertRoutesToDataRoutes(routes, parentPath,
|
|
581
|
+
function convertRoutesToDataRoutes(routes, detectErrorBoundary, parentPath, manifest) {
|
|
577
582
|
if (parentPath === void 0) {
|
|
578
583
|
parentPath = [];
|
|
579
584
|
}
|
|
580
585
|
|
|
581
|
-
if (
|
|
582
|
-
|
|
586
|
+
if (manifest === void 0) {
|
|
587
|
+
manifest = {};
|
|
583
588
|
}
|
|
584
589
|
|
|
585
590
|
return routes.map((route, index) => {
|
|
586
591
|
let treePath = [...parentPath, index];
|
|
587
592
|
let id = typeof route.id === "string" ? route.id : treePath.join("-");
|
|
588
593
|
invariant(route.index !== true || !route.children, "Cannot specify children on an index route");
|
|
589
|
-
invariant(!
|
|
590
|
-
allIds.add(id);
|
|
594
|
+
invariant(!manifest[id], "Found a route id collision on id \"" + id + "\". Route " + "id's must be globally unique within Data Router usages");
|
|
591
595
|
|
|
592
596
|
if (isIndexRoute(route)) {
|
|
593
597
|
let indexRoute = _extends({}, route, {
|
|
598
|
+
hasErrorBoundary: detectErrorBoundary(route),
|
|
594
599
|
id
|
|
595
600
|
});
|
|
596
601
|
|
|
602
|
+
manifest[id] = indexRoute;
|
|
597
603
|
return indexRoute;
|
|
598
604
|
} else {
|
|
599
605
|
let pathOrLayoutRoute = _extends({}, route, {
|
|
600
606
|
id,
|
|
601
|
-
|
|
607
|
+
hasErrorBoundary: detectErrorBoundary(route),
|
|
608
|
+
children: undefined
|
|
602
609
|
});
|
|
603
610
|
|
|
611
|
+
manifest[id] = pathOrLayoutRoute;
|
|
612
|
+
|
|
613
|
+
if (route.children) {
|
|
614
|
+
pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, detectErrorBoundary, treePath, manifest);
|
|
615
|
+
}
|
|
616
|
+
|
|
604
617
|
return pathOrLayoutRoute;
|
|
605
618
|
}
|
|
606
619
|
});
|
|
@@ -847,45 +860,42 @@
|
|
|
847
860
|
if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
|
|
848
861
|
warning(false, "Route path \"" + path + "\" will be treated as if it were " + ("\"" + path.replace(/\*$/, "/*") + "\" because the `*` character must ") + "always follow a `/` in the pattern. To get rid of this warning, " + ("please change the route path to \"" + path.replace(/\*$/, "/*") + "\"."));
|
|
849
862
|
path = path.replace(/\*$/, "/*");
|
|
850
|
-
}
|
|
863
|
+
} // ensure `/` is added at the beginning if the path is absolute
|
|
851
864
|
|
|
852
|
-
return path.replace(/^:(\w+)(\??)/g, (_, key, optional) => {
|
|
853
|
-
let param = params[key];
|
|
854
865
|
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
866
|
+
const prefix = path.startsWith("/") ? "/" : "";
|
|
867
|
+
const segments = path.split(/\/+/).map((segment, index, array) => {
|
|
868
|
+
const isLastSegment = index === array.length - 1; // only apply the splat if it's the last segment
|
|
869
|
+
|
|
870
|
+
if (isLastSegment && segment === "*") {
|
|
871
|
+
const star = "*";
|
|
872
|
+
const starParam = params[star]; // Apply the splat
|
|
858
873
|
|
|
859
|
-
|
|
860
|
-
invariant(false, "Missing \":" + key + "\" param");
|
|
874
|
+
return starParam;
|
|
861
875
|
}
|
|
862
876
|
|
|
863
|
-
|
|
864
|
-
}).replace(/\/:(\w+)(\??)/g, (_, key, optional) => {
|
|
865
|
-
let param = params[key];
|
|
877
|
+
const keyMatch = segment.match(/^:(\w+)(\??)$/);
|
|
866
878
|
|
|
867
|
-
if (
|
|
868
|
-
|
|
869
|
-
|
|
879
|
+
if (keyMatch) {
|
|
880
|
+
const [, key, optional] = keyMatch;
|
|
881
|
+
let param = params[key];
|
|
870
882
|
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
883
|
+
if (optional === "?") {
|
|
884
|
+
return param == null ? "" : param;
|
|
885
|
+
}
|
|
874
886
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
const star = "*";
|
|
887
|
+
if (param == null) {
|
|
888
|
+
invariant(false, "Missing \":" + key + "\" param");
|
|
889
|
+
}
|
|
879
890
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
// the entire path
|
|
883
|
-
return str === "/*" ? "/" : "";
|
|
884
|
-
} // Apply the splat
|
|
891
|
+
return param;
|
|
892
|
+
} // Remove any optional markers from optional static segments
|
|
885
893
|
|
|
886
894
|
|
|
887
|
-
return ""
|
|
888
|
-
})
|
|
895
|
+
return segment.replace(/\?$/g, "");
|
|
896
|
+
}) // Remove empty segments
|
|
897
|
+
.filter(segment => !!segment);
|
|
898
|
+
return prefix + segments.join("/");
|
|
889
899
|
}
|
|
890
900
|
/**
|
|
891
901
|
* A PathPattern is used to match on some portion of a URL pathname.
|
|
@@ -1013,25 +1023,6 @@
|
|
|
1013
1023
|
|
|
1014
1024
|
return pathname.slice(startIndex) || "/";
|
|
1015
1025
|
}
|
|
1016
|
-
/**
|
|
1017
|
-
* @private
|
|
1018
|
-
*/
|
|
1019
|
-
|
|
1020
|
-
function warning(cond, message) {
|
|
1021
|
-
if (!cond) {
|
|
1022
|
-
// eslint-disable-next-line no-console
|
|
1023
|
-
if (typeof console !== "undefined") console.warn(message);
|
|
1024
|
-
|
|
1025
|
-
try {
|
|
1026
|
-
// Welcome to debugging @remix-run/router!
|
|
1027
|
-
//
|
|
1028
|
-
// This error is thrown as a convenience so you can more easily
|
|
1029
|
-
// find the source for a warning that appears in the console by
|
|
1030
|
-
// enabling "pause on exceptions" in your JavaScript debugger.
|
|
1031
|
-
throw new Error(message); // eslint-disable-next-line no-empty
|
|
1032
|
-
} catch (e) {}
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
1026
|
/**
|
|
1036
1027
|
* Returns a resolved path object relative to the given pathname.
|
|
1037
1028
|
*
|
|
@@ -1480,7 +1471,9 @@
|
|
|
1480
1471
|
};
|
|
1481
1472
|
const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
1482
1473
|
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
1483
|
-
const isServer = !isBrowser;
|
|
1474
|
+
const isServer = !isBrowser;
|
|
1475
|
+
|
|
1476
|
+
const defaultDetectErrorBoundary = route => Boolean(route.hasErrorBoundary); //#endregion
|
|
1484
1477
|
////////////////////////////////////////////////////////////////////////////////
|
|
1485
1478
|
//#region createRouter
|
|
1486
1479
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -1489,9 +1482,14 @@
|
|
|
1489
1482
|
* Create a router and listen to history POP navigations
|
|
1490
1483
|
*/
|
|
1491
1484
|
|
|
1485
|
+
|
|
1492
1486
|
function createRouter(init) {
|
|
1493
1487
|
invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
|
|
1494
|
-
let
|
|
1488
|
+
let detectErrorBoundary = init.detectErrorBoundary || defaultDetectErrorBoundary; // Routes keyed by ID
|
|
1489
|
+
|
|
1490
|
+
let manifest = {}; // Routes in tree format for matching
|
|
1491
|
+
|
|
1492
|
+
let dataRoutes = convertRoutesToDataRoutes(init.routes, detectErrorBoundary, undefined, manifest);
|
|
1495
1493
|
let inFlightDataRoutes; // Cleanup function for history
|
|
1496
1494
|
|
|
1497
1495
|
let unlistenHistory = null; // Externally-provided functions to call on all state changes
|
|
@@ -1529,7 +1527,10 @@
|
|
|
1529
1527
|
};
|
|
1530
1528
|
}
|
|
1531
1529
|
|
|
1532
|
-
let initialized =
|
|
1530
|
+
let initialized = // All initialMatches need to be loaded before we're ready. If we have lazy
|
|
1531
|
+
// functions around still then we'll need to run them in initialize()
|
|
1532
|
+
!initialMatches.some(m => m.route.lazy) && ( // And we have to either have no loaders or have been provided hydrationData
|
|
1533
|
+
!initialMatches.some(m => m.route.loader) || init.hydrationData != null);
|
|
1533
1534
|
let router;
|
|
1534
1535
|
let state = {
|
|
1535
1536
|
historyAction: init.history.action,
|
|
@@ -1653,12 +1654,35 @@
|
|
|
1653
1654
|
}
|
|
1654
1655
|
|
|
1655
1656
|
return startNavigation(historyAction, location);
|
|
1656
|
-
});
|
|
1657
|
+
});
|
|
1657
1658
|
|
|
1658
|
-
if (
|
|
1659
|
-
|
|
1659
|
+
if (state.initialized) {
|
|
1660
|
+
return router;
|
|
1660
1661
|
}
|
|
1661
1662
|
|
|
1663
|
+
let lazyMatches = state.matches.filter(m => m.route.lazy);
|
|
1664
|
+
|
|
1665
|
+
if (lazyMatches.length === 0) {
|
|
1666
|
+
// Kick off initial data load if needed. Use Pop to avoid modifying history
|
|
1667
|
+
startNavigation(exports.Action.Pop, state.location);
|
|
1668
|
+
return router;
|
|
1669
|
+
} // Load lazy modules, then kick off initial data load if needed
|
|
1670
|
+
|
|
1671
|
+
|
|
1672
|
+
let lazyPromises = lazyMatches.map(m => loadLazyRouteModule(m.route, detectErrorBoundary, manifest));
|
|
1673
|
+
Promise.all(lazyPromises).then(() => {
|
|
1674
|
+
let initialized = !state.matches.some(m => m.route.loader) || init.hydrationData != null;
|
|
1675
|
+
|
|
1676
|
+
if (initialized) {
|
|
1677
|
+
// We already have required loaderData so we can just set initialized
|
|
1678
|
+
updateState({
|
|
1679
|
+
initialized: true
|
|
1680
|
+
});
|
|
1681
|
+
} else {
|
|
1682
|
+
// We still need to kick off initial data loads
|
|
1683
|
+
startNavigation(exports.Action.Pop, state.location);
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1662
1686
|
return router;
|
|
1663
1687
|
} // Clean up a router and it's side effects
|
|
1664
1688
|
|
|
@@ -2004,7 +2028,7 @@
|
|
|
2004
2028
|
let result;
|
|
2005
2029
|
let actionMatch = getTargetMatch(matches, location);
|
|
2006
2030
|
|
|
2007
|
-
if (!actionMatch.route.action) {
|
|
2031
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
2008
2032
|
result = {
|
|
2009
2033
|
type: ResultType.error,
|
|
2010
2034
|
error: getInternalRouterError(405, {
|
|
@@ -2014,7 +2038,7 @@
|
|
|
2014
2038
|
})
|
|
2015
2039
|
};
|
|
2016
2040
|
} else {
|
|
2017
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
2041
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, router.basename);
|
|
2018
2042
|
|
|
2019
2043
|
if (request.signal.aborted) {
|
|
2020
2044
|
return {
|
|
@@ -2260,7 +2284,7 @@
|
|
|
2260
2284
|
interruptActiveLoads();
|
|
2261
2285
|
fetchLoadMatches.delete(key);
|
|
2262
2286
|
|
|
2263
|
-
if (!match.route.action) {
|
|
2287
|
+
if (!match.route.action && !match.route.lazy) {
|
|
2264
2288
|
let error = getInternalRouterError(405, {
|
|
2265
2289
|
method: submission.formMethod,
|
|
2266
2290
|
pathname: path,
|
|
@@ -2288,7 +2312,7 @@
|
|
|
2288
2312
|
let abortController = new AbortController();
|
|
2289
2313
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
2290
2314
|
fetchControllers.set(key, abortController);
|
|
2291
|
-
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
2315
|
+
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, detectErrorBoundary, router.basename);
|
|
2292
2316
|
|
|
2293
2317
|
if (fetchRequest.signal.aborted) {
|
|
2294
2318
|
// We can delete this so long as we weren't aborted by ou our own fetcher
|
|
@@ -2459,7 +2483,7 @@
|
|
|
2459
2483
|
let abortController = new AbortController();
|
|
2460
2484
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
2461
2485
|
fetchControllers.set(key, abortController);
|
|
2462
|
-
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2486
|
+
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, manifest, detectErrorBoundary, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2463
2487
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2464
2488
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
2465
2489
|
// below if that happens
|
|
@@ -2628,9 +2652,9 @@
|
|
|
2628
2652
|
// Call all navigation loaders and revalidating fetcher loaders in parallel,
|
|
2629
2653
|
// then slice off the results into separate arrays so we can handle them
|
|
2630
2654
|
// accordingly
|
|
2631
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => {
|
|
2655
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, router.basename)), ...fetchersToLoad.map(f => {
|
|
2632
2656
|
if (f.matches && f.match) {
|
|
2633
|
-
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename);
|
|
2657
|
+
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, manifest, detectErrorBoundary, router.basename);
|
|
2634
2658
|
} else {
|
|
2635
2659
|
let error = {
|
|
2636
2660
|
type: ResultType.error,
|
|
@@ -2922,7 +2946,9 @@
|
|
|
2922
2946
|
const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2923
2947
|
function createStaticHandler(routes, opts) {
|
|
2924
2948
|
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
2925
|
-
let
|
|
2949
|
+
let manifest = {};
|
|
2950
|
+
let detectErrorBoundary = (opts == null ? void 0 : opts.detectErrorBoundary) || defaultDetectErrorBoundary;
|
|
2951
|
+
let dataRoutes = convertRoutesToDataRoutes(routes, detectErrorBoundary, undefined, manifest);
|
|
2926
2952
|
let basename = (opts ? opts.basename : null) || "/";
|
|
2927
2953
|
/**
|
|
2928
2954
|
* The query() method is intended for document requests, in which we want to
|
|
@@ -3144,7 +3170,7 @@
|
|
|
3144
3170
|
async function submit(request, matches, actionMatch, requestContext, isRouteRequest) {
|
|
3145
3171
|
let result;
|
|
3146
3172
|
|
|
3147
|
-
if (!actionMatch.route.action) {
|
|
3173
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
3148
3174
|
let error = getInternalRouterError(405, {
|
|
3149
3175
|
method: request.method,
|
|
3150
3176
|
pathname: new URL(request.url).pathname,
|
|
@@ -3160,7 +3186,7 @@
|
|
|
3160
3186
|
error
|
|
3161
3187
|
};
|
|
3162
3188
|
} else {
|
|
3163
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest, requestContext);
|
|
3189
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext);
|
|
3164
3190
|
|
|
3165
3191
|
if (request.signal.aborted) {
|
|
3166
3192
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3258,7 +3284,7 @@
|
|
|
3258
3284
|
async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
|
|
3259
3285
|
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
3260
3286
|
|
|
3261
|
-
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
3287
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader) && !(routeMatch != null && routeMatch.route.lazy)) {
|
|
3262
3288
|
throw getInternalRouterError(400, {
|
|
3263
3289
|
method: request.method,
|
|
3264
3290
|
pathname: new URL(request.url).pathname,
|
|
@@ -3267,7 +3293,7 @@
|
|
|
3267
3293
|
}
|
|
3268
3294
|
|
|
3269
3295
|
let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
|
|
3270
|
-
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run (query())
|
|
3296
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy); // Short circuit if we have no loaders to run (query())
|
|
3271
3297
|
|
|
3272
3298
|
if (matchesToLoad.length === 0) {
|
|
3273
3299
|
return {
|
|
@@ -3283,7 +3309,7 @@
|
|
|
3283
3309
|
};
|
|
3284
3310
|
}
|
|
3285
3311
|
|
|
3286
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest, requestContext))]);
|
|
3312
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext))]);
|
|
3287
3313
|
|
|
3288
3314
|
if (request.signal.aborted) {
|
|
3289
3315
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3424,6 +3450,11 @@
|
|
|
3424
3450
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3425
3451
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3426
3452
|
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
3453
|
+
if (match.route.lazy) {
|
|
3454
|
+
// We haven't loaded this route yet so we don't know if it's got a loader!
|
|
3455
|
+
return true;
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3427
3458
|
if (match.route.loader == null) {
|
|
3428
3459
|
return false;
|
|
3429
3460
|
} // Always call the loader on new route instances and pending defer cancellations
|
|
@@ -3537,8 +3568,66 @@
|
|
|
3537
3568
|
|
|
3538
3569
|
return arg.defaultShouldRevalidate;
|
|
3539
3570
|
}
|
|
3571
|
+
/**
|
|
3572
|
+
* Execute route.lazy() methods to lazily load route modules (loader, action,
|
|
3573
|
+
* shouldRevalidate) and update the routeManifest in place which shares objects
|
|
3574
|
+
* with dataRoutes so those get updated as well.
|
|
3575
|
+
*/
|
|
3576
|
+
|
|
3577
|
+
|
|
3578
|
+
async function loadLazyRouteModule(route, detectErrorBoundary, manifest) {
|
|
3579
|
+
if (!route.lazy) {
|
|
3580
|
+
return;
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
let lazyRoute = await route.lazy(); // If the lazy route function was executed and removed by another parallel
|
|
3584
|
+
// call then we can return - first lazy() to finish wins because the return
|
|
3585
|
+
// value of lazy is expected to be static
|
|
3586
|
+
|
|
3587
|
+
if (!route.lazy) {
|
|
3588
|
+
return;
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
let routeToUpdate = manifest[route.id];
|
|
3592
|
+
invariant(routeToUpdate, "No route found in manifest"); // Update the route in place. This should be safe because there's no way
|
|
3593
|
+
// we could yet be sitting on this route as we can't get there without
|
|
3594
|
+
// resolving lazy() first.
|
|
3595
|
+
//
|
|
3596
|
+
// This is different than the HMR "update" use-case where we may actively be
|
|
3597
|
+
// on the route being updated. The main concern boils down to "does this
|
|
3598
|
+
// mutation affect any ongoing navigations or any current state.matches
|
|
3599
|
+
// values?". If not, it should be safe to update in place.
|
|
3600
|
+
|
|
3601
|
+
let routeUpdates = {};
|
|
3602
|
+
|
|
3603
|
+
for (let lazyRouteProperty in lazyRoute) {
|
|
3604
|
+
let staticRouteValue = routeToUpdate[lazyRouteProperty];
|
|
3605
|
+
let isPropertyStaticallyDefined = staticRouteValue !== undefined && // This property isn't static since it should always be updated based
|
|
3606
|
+
// on the route updates
|
|
3607
|
+
lazyRouteProperty !== "hasErrorBoundary";
|
|
3608
|
+
warning(!isPropertyStaticallyDefined, "Route \"" + routeToUpdate.id + "\" has a static property \"" + lazyRouteProperty + "\" " + "defined but its lazy function is also returning a value for this property. " + ("The lazy route property \"" + lazyRouteProperty + "\" will be ignored."));
|
|
3609
|
+
|
|
3610
|
+
if (!isPropertyStaticallyDefined && !immutableRouteKeys.has(lazyRouteProperty)) {
|
|
3611
|
+
routeUpdates[lazyRouteProperty] = lazyRoute[lazyRouteProperty];
|
|
3612
|
+
}
|
|
3613
|
+
} // Mutate the route with the provided updates. Do this first so we pass
|
|
3614
|
+
// the updated version to detectErrorBoundary
|
|
3615
|
+
|
|
3616
|
+
|
|
3617
|
+
Object.assign(routeToUpdate, routeUpdates); // Mutate the `hasErrorBoundary` property on the route based on the route
|
|
3618
|
+
// updates and remove the `lazy` function so we don't resolve the lazy
|
|
3619
|
+
// route again.
|
|
3620
|
+
|
|
3621
|
+
Object.assign(routeToUpdate, {
|
|
3622
|
+
// To keep things framework agnostic, we use the provided
|
|
3623
|
+
// `detectErrorBoundary` function to set the `hasErrorBoundary` route
|
|
3624
|
+
// property since the logic will differ between frameworks.
|
|
3625
|
+
hasErrorBoundary: detectErrorBoundary(_extends({}, routeToUpdate)),
|
|
3626
|
+
lazy: undefined
|
|
3627
|
+
});
|
|
3628
|
+
}
|
|
3540
3629
|
|
|
3541
|
-
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
3630
|
+
async function callLoaderOrAction(type, request, match, matches, manifest, detectErrorBoundary, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
3542
3631
|
if (basename === void 0) {
|
|
3543
3632
|
basename = "/";
|
|
3544
3633
|
}
|
|
@@ -3552,29 +3641,70 @@
|
|
|
3552
3641
|
}
|
|
3553
3642
|
|
|
3554
3643
|
let resultType;
|
|
3555
|
-
let result;
|
|
3556
|
-
|
|
3557
|
-
let reject;
|
|
3558
|
-
let abortPromise = new Promise((_, r) => reject = r);
|
|
3644
|
+
let result;
|
|
3645
|
+
let onReject;
|
|
3559
3646
|
|
|
3560
|
-
let
|
|
3647
|
+
let runHandler = handler => {
|
|
3648
|
+
// Setup a promise we can race against so that abort signals short circuit
|
|
3649
|
+
let reject;
|
|
3650
|
+
let abortPromise = new Promise((_, r) => reject = r);
|
|
3561
3651
|
|
|
3562
|
-
|
|
3652
|
+
onReject = () => reject();
|
|
3563
3653
|
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3567
|
-
result = await Promise.race([handler({
|
|
3654
|
+
request.signal.addEventListener("abort", onReject);
|
|
3655
|
+
return Promise.race([handler({
|
|
3568
3656
|
request,
|
|
3569
3657
|
params: match.params,
|
|
3570
3658
|
context: requestContext
|
|
3571
3659
|
}), abortPromise]);
|
|
3660
|
+
};
|
|
3661
|
+
|
|
3662
|
+
try {
|
|
3663
|
+
let handler = match.route[type];
|
|
3664
|
+
|
|
3665
|
+
if (match.route.lazy) {
|
|
3666
|
+
if (handler) {
|
|
3667
|
+
// Run statically defined handler in parallel with lazy()
|
|
3668
|
+
let values = await Promise.all([runHandler(handler), loadLazyRouteModule(match.route, detectErrorBoundary, manifest)]);
|
|
3669
|
+
result = values[0];
|
|
3670
|
+
} else {
|
|
3671
|
+
// Load lazy route module, then run any returned handler
|
|
3672
|
+
await loadLazyRouteModule(match.route, detectErrorBoundary, manifest);
|
|
3673
|
+
handler = match.route[type];
|
|
3674
|
+
|
|
3675
|
+
if (handler) {
|
|
3676
|
+
// Handler still run even if we got interrupted to maintain consistency
|
|
3677
|
+
// with un-abortable behavior of handler execution on non-lazy or
|
|
3678
|
+
// previously-lazy-loaded routes
|
|
3679
|
+
result = await runHandler(handler);
|
|
3680
|
+
} else if (type === "action") {
|
|
3681
|
+
throw getInternalRouterError(405, {
|
|
3682
|
+
method: request.method,
|
|
3683
|
+
pathname: new URL(request.url).pathname,
|
|
3684
|
+
routeId: match.route.id
|
|
3685
|
+
});
|
|
3686
|
+
} else {
|
|
3687
|
+
// lazy() route has no loader to run. Short circuit here so we don't
|
|
3688
|
+
// hit the invariant below that errors on returning undefined.
|
|
3689
|
+
return {
|
|
3690
|
+
type: ResultType.data,
|
|
3691
|
+
data: undefined
|
|
3692
|
+
};
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
} else {
|
|
3696
|
+
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3697
|
+
result = await runHandler(handler);
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3572
3700
|
invariant(result !== undefined, "You defined " + (type === "action" ? "an action" : "a loader") + " for route " + ("\"" + match.route.id + "\" but didn't return anything from your `" + type + "` ") + "function. Please return a value or `null`.");
|
|
3573
3701
|
} catch (e) {
|
|
3574
3702
|
resultType = ResultType.error;
|
|
3575
3703
|
result = e;
|
|
3576
3704
|
} finally {
|
|
3577
|
-
|
|
3705
|
+
if (onReject) {
|
|
3706
|
+
request.signal.removeEventListener("abort", onReject);
|
|
3707
|
+
}
|
|
3578
3708
|
}
|
|
3579
3709
|
|
|
3580
3710
|
if (isResponse(result)) {
|
|
@@ -4101,6 +4231,7 @@
|
|
|
4101
4231
|
exports.UNSAFE_convertRoutesToDataRoutes = convertRoutesToDataRoutes;
|
|
4102
4232
|
exports.UNSAFE_getPathContributingMatches = getPathContributingMatches;
|
|
4103
4233
|
exports.UNSAFE_invariant = invariant;
|
|
4234
|
+
exports.UNSAFE_warning = warning;
|
|
4104
4235
|
exports.createBrowserHistory = createBrowserHistory;
|
|
4105
4236
|
exports.createHashHistory = createHashHistory;
|
|
4106
4237
|
exports.createMemoryHistory = createMemoryHistory;
|
|
@@ -4122,7 +4253,6 @@
|
|
|
4122
4253
|
exports.resolvePath = resolvePath;
|
|
4123
4254
|
exports.resolveTo = resolveTo;
|
|
4124
4255
|
exports.stripBasename = stripBasename;
|
|
4125
|
-
exports.warning = warning;
|
|
4126
4256
|
|
|
4127
4257
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4128
4258
|
|