@remix-run/router 0.0.0-experimental-e7e9ce6e → 0.0.0-experimental-91f2bf54
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 +3 -8
- package/dist/history.d.ts +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/router.cjs.js +231 -91
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +6 -3
- package/dist/router.js +227 -91
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +231 -91
- 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 +255 -31
- package/utils.ts +102 -69
package/dist/router.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v0.0.0-experimental-
|
|
2
|
+
* @remix-run/router v0.0.0-experimental-91f2bf54
|
|
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
|
|
|
@@ -1684,6 +1708,16 @@
|
|
|
1684
1708
|
function updateState(newState) {
|
|
1685
1709
|
state = _extends({}, state, newState);
|
|
1686
1710
|
subscribers.forEach(subscriber => subscriber(state));
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
function completeNavigation(location, newState) {
|
|
1714
|
+
// @ts-expect-error
|
|
1715
|
+
if (typeof document !== "undefined" && document.startViewTransition) {
|
|
1716
|
+
// @ts-expect-error
|
|
1717
|
+
document.startViewTransition(() => completeNavigationForRealz(location, newState));
|
|
1718
|
+
} else {
|
|
1719
|
+
completeNavigationForRealz(location, newState);
|
|
1720
|
+
}
|
|
1687
1721
|
} // Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION
|
|
1688
1722
|
// and setting state.[historyAction/location/matches] to the new route.
|
|
1689
1723
|
// - Location is a required param
|
|
@@ -1691,7 +1725,7 @@
|
|
|
1691
1725
|
// - Can pass any other state in newState
|
|
1692
1726
|
|
|
1693
1727
|
|
|
1694
|
-
function
|
|
1728
|
+
function completeNavigationForRealz(location, newState) {
|
|
1695
1729
|
var _location$state, _location$state2;
|
|
1696
1730
|
|
|
1697
1731
|
// Deduce if we're in a loading/actionReload state:
|
|
@@ -2004,7 +2038,7 @@
|
|
|
2004
2038
|
let result;
|
|
2005
2039
|
let actionMatch = getTargetMatch(matches, location);
|
|
2006
2040
|
|
|
2007
|
-
if (!actionMatch.route.action) {
|
|
2041
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
2008
2042
|
result = {
|
|
2009
2043
|
type: ResultType.error,
|
|
2010
2044
|
error: getInternalRouterError(405, {
|
|
@@ -2014,7 +2048,7 @@
|
|
|
2014
2048
|
})
|
|
2015
2049
|
};
|
|
2016
2050
|
} else {
|
|
2017
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
2051
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, router.basename);
|
|
2018
2052
|
|
|
2019
2053
|
if (request.signal.aborted) {
|
|
2020
2054
|
return {
|
|
@@ -2260,7 +2294,7 @@
|
|
|
2260
2294
|
interruptActiveLoads();
|
|
2261
2295
|
fetchLoadMatches.delete(key);
|
|
2262
2296
|
|
|
2263
|
-
if (!match.route.action) {
|
|
2297
|
+
if (!match.route.action && !match.route.lazy) {
|
|
2264
2298
|
let error = getInternalRouterError(405, {
|
|
2265
2299
|
method: submission.formMethod,
|
|
2266
2300
|
pathname: path,
|
|
@@ -2288,7 +2322,7 @@
|
|
|
2288
2322
|
let abortController = new AbortController();
|
|
2289
2323
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
2290
2324
|
fetchControllers.set(key, abortController);
|
|
2291
|
-
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
2325
|
+
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, detectErrorBoundary, router.basename);
|
|
2292
2326
|
|
|
2293
2327
|
if (fetchRequest.signal.aborted) {
|
|
2294
2328
|
// We can delete this so long as we weren't aborted by ou our own fetcher
|
|
@@ -2459,7 +2493,7 @@
|
|
|
2459
2493
|
let abortController = new AbortController();
|
|
2460
2494
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
2461
2495
|
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
|
|
2496
|
+
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
2497
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2464
2498
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
2465
2499
|
// below if that happens
|
|
@@ -2628,9 +2662,9 @@
|
|
|
2628
2662
|
// Call all navigation loaders and revalidating fetcher loaders in parallel,
|
|
2629
2663
|
// then slice off the results into separate arrays so we can handle them
|
|
2630
2664
|
// accordingly
|
|
2631
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => {
|
|
2665
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, router.basename)), ...fetchersToLoad.map(f => {
|
|
2632
2666
|
if (f.matches && f.match) {
|
|
2633
|
-
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename);
|
|
2667
|
+
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, manifest, detectErrorBoundary, router.basename);
|
|
2634
2668
|
} else {
|
|
2635
2669
|
let error = {
|
|
2636
2670
|
type: ResultType.error,
|
|
@@ -2922,7 +2956,9 @@
|
|
|
2922
2956
|
const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2923
2957
|
function createStaticHandler(routes, opts) {
|
|
2924
2958
|
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
2925
|
-
let
|
|
2959
|
+
let manifest = {};
|
|
2960
|
+
let detectErrorBoundary = (opts == null ? void 0 : opts.detectErrorBoundary) || defaultDetectErrorBoundary;
|
|
2961
|
+
let dataRoutes = convertRoutesToDataRoutes(routes, detectErrorBoundary, undefined, manifest);
|
|
2926
2962
|
let basename = (opts ? opts.basename : null) || "/";
|
|
2927
2963
|
/**
|
|
2928
2964
|
* The query() method is intended for document requests, in which we want to
|
|
@@ -3144,7 +3180,7 @@
|
|
|
3144
3180
|
async function submit(request, matches, actionMatch, requestContext, isRouteRequest) {
|
|
3145
3181
|
let result;
|
|
3146
3182
|
|
|
3147
|
-
if (!actionMatch.route.action) {
|
|
3183
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
3148
3184
|
let error = getInternalRouterError(405, {
|
|
3149
3185
|
method: request.method,
|
|
3150
3186
|
pathname: new URL(request.url).pathname,
|
|
@@ -3160,7 +3196,7 @@
|
|
|
3160
3196
|
error
|
|
3161
3197
|
};
|
|
3162
3198
|
} else {
|
|
3163
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest, requestContext);
|
|
3199
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext);
|
|
3164
3200
|
|
|
3165
3201
|
if (request.signal.aborted) {
|
|
3166
3202
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3258,7 +3294,7 @@
|
|
|
3258
3294
|
async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
|
|
3259
3295
|
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
3260
3296
|
|
|
3261
|
-
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
3297
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader) && !(routeMatch != null && routeMatch.route.lazy)) {
|
|
3262
3298
|
throw getInternalRouterError(400, {
|
|
3263
3299
|
method: request.method,
|
|
3264
3300
|
pathname: new URL(request.url).pathname,
|
|
@@ -3267,7 +3303,7 @@
|
|
|
3267
3303
|
}
|
|
3268
3304
|
|
|
3269
3305
|
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())
|
|
3306
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy); // Short circuit if we have no loaders to run (query())
|
|
3271
3307
|
|
|
3272
3308
|
if (matchesToLoad.length === 0) {
|
|
3273
3309
|
return {
|
|
@@ -3283,7 +3319,7 @@
|
|
|
3283
3319
|
};
|
|
3284
3320
|
}
|
|
3285
3321
|
|
|
3286
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest, requestContext))]);
|
|
3322
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext))]);
|
|
3287
3323
|
|
|
3288
3324
|
if (request.signal.aborted) {
|
|
3289
3325
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3424,6 +3460,11 @@
|
|
|
3424
3460
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3425
3461
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3426
3462
|
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
3463
|
+
if (match.route.lazy) {
|
|
3464
|
+
// We haven't loaded this route yet so we don't know if it's got a loader!
|
|
3465
|
+
return true;
|
|
3466
|
+
}
|
|
3467
|
+
|
|
3427
3468
|
if (match.route.loader == null) {
|
|
3428
3469
|
return false;
|
|
3429
3470
|
} // Always call the loader on new route instances and pending defer cancellations
|
|
@@ -3537,8 +3578,66 @@
|
|
|
3537
3578
|
|
|
3538
3579
|
return arg.defaultShouldRevalidate;
|
|
3539
3580
|
}
|
|
3581
|
+
/**
|
|
3582
|
+
* Execute route.lazy() methods to lazily load route modules (loader, action,
|
|
3583
|
+
* shouldRevalidate) and update the routeManifest in place which shares objects
|
|
3584
|
+
* with dataRoutes so those get updated as well.
|
|
3585
|
+
*/
|
|
3586
|
+
|
|
3587
|
+
|
|
3588
|
+
async function loadLazyRouteModule(route, detectErrorBoundary, manifest) {
|
|
3589
|
+
if (!route.lazy) {
|
|
3590
|
+
return;
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
let lazyRoute = await route.lazy(); // If the lazy route function was executed and removed by another parallel
|
|
3594
|
+
// call then we can return - first lazy() to finish wins because the return
|
|
3595
|
+
// value of lazy is expected to be static
|
|
3596
|
+
|
|
3597
|
+
if (!route.lazy) {
|
|
3598
|
+
return;
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
let routeToUpdate = manifest[route.id];
|
|
3602
|
+
invariant(routeToUpdate, "No route found in manifest"); // Update the route in place. This should be safe because there's no way
|
|
3603
|
+
// we could yet be sitting on this route as we can't get there without
|
|
3604
|
+
// resolving lazy() first.
|
|
3605
|
+
//
|
|
3606
|
+
// This is different than the HMR "update" use-case where we may actively be
|
|
3607
|
+
// on the route being updated. The main concern boils down to "does this
|
|
3608
|
+
// mutation affect any ongoing navigations or any current state.matches
|
|
3609
|
+
// values?". If not, it should be safe to update in place.
|
|
3610
|
+
|
|
3611
|
+
let routeUpdates = {};
|
|
3612
|
+
|
|
3613
|
+
for (let lazyRouteProperty in lazyRoute) {
|
|
3614
|
+
let staticRouteValue = routeToUpdate[lazyRouteProperty];
|
|
3615
|
+
let isPropertyStaticallyDefined = staticRouteValue !== undefined && // This property isn't static since it should always be updated based
|
|
3616
|
+
// on the route updates
|
|
3617
|
+
lazyRouteProperty !== "hasErrorBoundary";
|
|
3618
|
+
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."));
|
|
3619
|
+
|
|
3620
|
+
if (!isPropertyStaticallyDefined && !immutableRouteKeys.has(lazyRouteProperty)) {
|
|
3621
|
+
routeUpdates[lazyRouteProperty] = lazyRoute[lazyRouteProperty];
|
|
3622
|
+
}
|
|
3623
|
+
} // Mutate the route with the provided updates. Do this first so we pass
|
|
3624
|
+
// the updated version to detectErrorBoundary
|
|
3625
|
+
|
|
3626
|
+
|
|
3627
|
+
Object.assign(routeToUpdate, routeUpdates); // Mutate the `hasErrorBoundary` property on the route based on the route
|
|
3628
|
+
// updates and remove the `lazy` function so we don't resolve the lazy
|
|
3629
|
+
// route again.
|
|
3630
|
+
|
|
3631
|
+
Object.assign(routeToUpdate, {
|
|
3632
|
+
// To keep things framework agnostic, we use the provided
|
|
3633
|
+
// `detectErrorBoundary` function to set the `hasErrorBoundary` route
|
|
3634
|
+
// property since the logic will differ between frameworks.
|
|
3635
|
+
hasErrorBoundary: detectErrorBoundary(_extends({}, routeToUpdate)),
|
|
3636
|
+
lazy: undefined
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3540
3639
|
|
|
3541
|
-
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
3640
|
+
async function callLoaderOrAction(type, request, match, matches, manifest, detectErrorBoundary, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
3542
3641
|
if (basename === void 0) {
|
|
3543
3642
|
basename = "/";
|
|
3544
3643
|
}
|
|
@@ -3552,29 +3651,70 @@
|
|
|
3552
3651
|
}
|
|
3553
3652
|
|
|
3554
3653
|
let resultType;
|
|
3555
|
-
let result;
|
|
3654
|
+
let result;
|
|
3655
|
+
let onReject;
|
|
3556
3656
|
|
|
3557
|
-
let
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3657
|
+
let runHandler = handler => {
|
|
3658
|
+
// Setup a promise we can race against so that abort signals short circuit
|
|
3659
|
+
let reject;
|
|
3660
|
+
let abortPromise = new Promise((_, r) => reject = r);
|
|
3561
3661
|
|
|
3562
|
-
|
|
3662
|
+
onReject = () => reject();
|
|
3563
3663
|
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3567
|
-
result = await Promise.race([handler({
|
|
3664
|
+
request.signal.addEventListener("abort", onReject);
|
|
3665
|
+
return Promise.race([handler({
|
|
3568
3666
|
request,
|
|
3569
3667
|
params: match.params,
|
|
3570
3668
|
context: requestContext
|
|
3571
3669
|
}), abortPromise]);
|
|
3670
|
+
};
|
|
3671
|
+
|
|
3672
|
+
try {
|
|
3673
|
+
let handler = match.route[type];
|
|
3674
|
+
|
|
3675
|
+
if (match.route.lazy) {
|
|
3676
|
+
if (handler) {
|
|
3677
|
+
// Run statically defined handler in parallel with lazy()
|
|
3678
|
+
let values = await Promise.all([runHandler(handler), loadLazyRouteModule(match.route, detectErrorBoundary, manifest)]);
|
|
3679
|
+
result = values[0];
|
|
3680
|
+
} else {
|
|
3681
|
+
// Load lazy route module, then run any returned handler
|
|
3682
|
+
await loadLazyRouteModule(match.route, detectErrorBoundary, manifest);
|
|
3683
|
+
handler = match.route[type];
|
|
3684
|
+
|
|
3685
|
+
if (handler) {
|
|
3686
|
+
// Handler still run even if we got interrupted to maintain consistency
|
|
3687
|
+
// with un-abortable behavior of handler execution on non-lazy or
|
|
3688
|
+
// previously-lazy-loaded routes
|
|
3689
|
+
result = await runHandler(handler);
|
|
3690
|
+
} else if (type === "action") {
|
|
3691
|
+
throw getInternalRouterError(405, {
|
|
3692
|
+
method: request.method,
|
|
3693
|
+
pathname: new URL(request.url).pathname,
|
|
3694
|
+
routeId: match.route.id
|
|
3695
|
+
});
|
|
3696
|
+
} else {
|
|
3697
|
+
// lazy() route has no loader to run. Short circuit here so we don't
|
|
3698
|
+
// hit the invariant below that errors on returning undefined.
|
|
3699
|
+
return {
|
|
3700
|
+
type: ResultType.data,
|
|
3701
|
+
data: undefined
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
} else {
|
|
3706
|
+
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3707
|
+
result = await runHandler(handler);
|
|
3708
|
+
}
|
|
3709
|
+
|
|
3572
3710
|
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
3711
|
} catch (e) {
|
|
3574
3712
|
resultType = ResultType.error;
|
|
3575
3713
|
result = e;
|
|
3576
3714
|
} finally {
|
|
3577
|
-
|
|
3715
|
+
if (onReject) {
|
|
3716
|
+
request.signal.removeEventListener("abort", onReject);
|
|
3717
|
+
}
|
|
3578
3718
|
}
|
|
3579
3719
|
|
|
3580
3720
|
if (isResponse(result)) {
|
|
@@ -4101,6 +4241,7 @@
|
|
|
4101
4241
|
exports.UNSAFE_convertRoutesToDataRoutes = convertRoutesToDataRoutes;
|
|
4102
4242
|
exports.UNSAFE_getPathContributingMatches = getPathContributingMatches;
|
|
4103
4243
|
exports.UNSAFE_invariant = invariant;
|
|
4244
|
+
exports.UNSAFE_warning = warning;
|
|
4104
4245
|
exports.createBrowserHistory = createBrowserHistory;
|
|
4105
4246
|
exports.createHashHistory = createHashHistory;
|
|
4106
4247
|
exports.createMemoryHistory = createMemoryHistory;
|
|
@@ -4122,7 +4263,6 @@
|
|
|
4122
4263
|
exports.resolvePath = resolvePath;
|
|
4123
4264
|
exports.resolveTo = resolveTo;
|
|
4124
4265
|
exports.stripBasename = stripBasename;
|
|
4125
|
-
exports.warning = warning;
|
|
4126
4266
|
|
|
4127
4267
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4128
4268
|
|