@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/CHANGELOG.md
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
# `@remix-run/router`
|
|
2
2
|
|
|
3
|
-
## 1.3.3
|
|
3
|
+
## 1.3.3
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
- Correctly perform a
|
|
8
|
-
|
|
9
|
-
## 1.3.3-pre.0
|
|
10
|
-
|
|
11
|
-
### Patch Changes
|
|
12
|
-
|
|
13
|
-
- Change `invariant` to an `UNSAFE_` export since it's only intended for internal use ([#10066](https://github.com/remix-run/react-router/pull/10066))
|
|
7
|
+
- Correctly perform a hard redirect for same-origin absolute URLs outside of the router `basename` ([#10076](https://github.com/remix-run/react-router/pull/10076))
|
|
14
8
|
- Ensure status code and headers are maintained for `defer` loader responses in `createStaticHandler`'s `query()` method ([#10077](https://github.com/remix-run/react-router/pull/10077))
|
|
9
|
+
- Change `invariant` to an `UNSAFE_invariant` export since it's only intended for internal use ([#10066](https://github.com/remix-run/react-router/pull/10066))
|
|
15
10
|
- Add internal API for custom HMR implementations ([#9996](https://github.com/remix-run/react-router/pull/9996))
|
|
16
11
|
|
|
17
12
|
## 1.3.2
|
package/dist/history.d.ts
CHANGED
|
@@ -229,6 +229,7 @@ export declare function createHashHistory(options?: HashHistoryOptions): HashHis
|
|
|
229
229
|
*/
|
|
230
230
|
export declare function invariant(value: boolean, message?: string): asserts value;
|
|
231
231
|
export declare function invariant<T>(value: T | null | undefined, message?: string): asserts value is T;
|
|
232
|
+
export declare function warning(cond: any, message: string): void;
|
|
232
233
|
/**
|
|
233
234
|
* Creates a Location object with a unique key from the given Path
|
|
234
235
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export type { ActionFunction, ActionFunctionArgs, AgnosticDataIndexRouteObject, AgnosticDataNonIndexRouteObject, AgnosticDataRouteMatch, AgnosticDataRouteObject, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, TrackedPromise, FormEncType, FormMethod, JsonFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathPattern, RedirectFunction, ShouldRevalidateFunction, Submission, } from "./utils";
|
|
2
|
-
export { AbortedDeferredError, ErrorResponse, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, resolvePath, resolveTo, stripBasename,
|
|
1
|
+
export type { ActionFunction, ActionFunctionArgs, AgnosticDataIndexRouteObject, AgnosticDataNonIndexRouteObject, AgnosticDataRouteMatch, AgnosticDataRouteObject, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, LazyRouteFunction, TrackedPromise, FormEncType, FormMethod, JsonFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathPattern, RedirectFunction, ShouldRevalidateFunction, Submission, } from "./utils";
|
|
2
|
+
export { AbortedDeferredError, ErrorResponse, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, resolvePath, resolveTo, stripBasename, } from "./utils";
|
|
3
3
|
export type { BrowserHistory, BrowserHistoryOptions, HashHistory, HashHistoryOptions, History, InitialEntry, Location, MemoryHistory, MemoryHistoryOptions, Path, To, } from "./history";
|
|
4
4
|
export { Action, createBrowserHistory, createPath, createHashHistory, createMemoryHistory, parsePath, } from "./history";
|
|
5
5
|
export * from "./router";
|
|
6
6
|
/** @internal */
|
|
7
|
+
export type { RouteManifest as UNSAFE_RouteManifest } from "./utils";
|
|
7
8
|
export { DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, } from "./utils";
|
|
8
|
-
export { invariant as UNSAFE_invariant } from "./history";
|
|
9
|
+
export { invariant as UNSAFE_invariant, warning as UNSAFE_warning, } from "./history";
|
package/dist/router.cjs.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
|
*
|
|
@@ -92,7 +92,7 @@ function createMemoryHistory(options) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
let location = createLocation(entries ? getCurrentLocation().pathname : "/", to, state, key);
|
|
95
|
-
warning
|
|
95
|
+
warning(location.pathname.charAt(0) === "/", "relative pathnames are not supported in memory history: " + JSON.stringify(to));
|
|
96
96
|
return location;
|
|
97
97
|
}
|
|
98
98
|
|
|
@@ -283,7 +283,7 @@ function createHashHistory(options) {
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
function validateHashLocation(location, to) {
|
|
286
|
-
warning
|
|
286
|
+
warning(location.pathname.charAt(0) === "/", "relative pathnames are not supported in hash history.push(" + JSON.stringify(to) + ")");
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
return getUrlBasedHistory(createHashLocation, createHashHref, validateHashLocation, options);
|
|
@@ -301,8 +301,7 @@ function invariant(value, message) {
|
|
|
301
301
|
throw new Error(message);
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
|
-
|
|
305
|
-
function warning$1(cond, message) {
|
|
304
|
+
function warning(cond, message) {
|
|
306
305
|
if (!cond) {
|
|
307
306
|
// eslint-disable-next-line no-console
|
|
308
307
|
if (typeof console !== "undefined") console.warn(message);
|
|
@@ -565,40 +564,54 @@ let ResultType;
|
|
|
565
564
|
ResultType["error"] = "error";
|
|
566
565
|
})(ResultType || (ResultType = {}));
|
|
567
566
|
|
|
567
|
+
const immutableRouteKeys = new Set(["lazy", "caseSensitive", "path", "id", "index", "children"]);
|
|
568
|
+
/**
|
|
569
|
+
* lazy() function to load a route definition, which can add non-matching
|
|
570
|
+
* related properties to a route
|
|
571
|
+
*/
|
|
572
|
+
|
|
568
573
|
function isIndexRoute(route) {
|
|
569
574
|
return route.index === true;
|
|
570
575
|
} // Walk the route tree generating unique IDs where necessary so we are working
|
|
571
576
|
// solely with AgnosticDataRouteObject's within the Router
|
|
572
577
|
|
|
573
578
|
|
|
574
|
-
function convertRoutesToDataRoutes(routes, parentPath,
|
|
579
|
+
function convertRoutesToDataRoutes(routes, detectErrorBoundary, parentPath, manifest) {
|
|
575
580
|
if (parentPath === void 0) {
|
|
576
581
|
parentPath = [];
|
|
577
582
|
}
|
|
578
583
|
|
|
579
|
-
if (
|
|
580
|
-
|
|
584
|
+
if (manifest === void 0) {
|
|
585
|
+
manifest = {};
|
|
581
586
|
}
|
|
582
587
|
|
|
583
588
|
return routes.map((route, index) => {
|
|
584
589
|
let treePath = [...parentPath, index];
|
|
585
590
|
let id = typeof route.id === "string" ? route.id : treePath.join("-");
|
|
586
591
|
invariant(route.index !== true || !route.children, "Cannot specify children on an index route");
|
|
587
|
-
invariant(!
|
|
588
|
-
allIds.add(id);
|
|
592
|
+
invariant(!manifest[id], "Found a route id collision on id \"" + id + "\". Route " + "id's must be globally unique within Data Router usages");
|
|
589
593
|
|
|
590
594
|
if (isIndexRoute(route)) {
|
|
591
595
|
let indexRoute = _extends({}, route, {
|
|
596
|
+
hasErrorBoundary: detectErrorBoundary(route),
|
|
592
597
|
id
|
|
593
598
|
});
|
|
594
599
|
|
|
600
|
+
manifest[id] = indexRoute;
|
|
595
601
|
return indexRoute;
|
|
596
602
|
} else {
|
|
597
603
|
let pathOrLayoutRoute = _extends({}, route, {
|
|
598
604
|
id,
|
|
599
|
-
|
|
605
|
+
hasErrorBoundary: detectErrorBoundary(route),
|
|
606
|
+
children: undefined
|
|
600
607
|
});
|
|
601
608
|
|
|
609
|
+
manifest[id] = pathOrLayoutRoute;
|
|
610
|
+
|
|
611
|
+
if (route.children) {
|
|
612
|
+
pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, detectErrorBoundary, treePath, manifest);
|
|
613
|
+
}
|
|
614
|
+
|
|
602
615
|
return pathOrLayoutRoute;
|
|
603
616
|
}
|
|
604
617
|
});
|
|
@@ -845,45 +858,42 @@ function generatePath(originalPath, params) {
|
|
|
845
858
|
if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
|
|
846
859
|
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(/\*$/, "/*") + "\"."));
|
|
847
860
|
path = path.replace(/\*$/, "/*");
|
|
848
|
-
}
|
|
861
|
+
} // ensure `/` is added at the beginning if the path is absolute
|
|
849
862
|
|
|
850
|
-
return path.replace(/^:(\w+)(\??)/g, (_, key, optional) => {
|
|
851
|
-
let param = params[key];
|
|
852
863
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
864
|
+
const prefix = path.startsWith("/") ? "/" : "";
|
|
865
|
+
const segments = path.split(/\/+/).map((segment, index, array) => {
|
|
866
|
+
const isLastSegment = index === array.length - 1; // only apply the splat if it's the last segment
|
|
867
|
+
|
|
868
|
+
if (isLastSegment && segment === "*") {
|
|
869
|
+
const star = "*";
|
|
870
|
+
const starParam = params[star]; // Apply the splat
|
|
856
871
|
|
|
857
|
-
|
|
858
|
-
invariant(false, "Missing \":" + key + "\" param");
|
|
872
|
+
return starParam;
|
|
859
873
|
}
|
|
860
874
|
|
|
861
|
-
|
|
862
|
-
}).replace(/\/:(\w+)(\??)/g, (_, key, optional) => {
|
|
863
|
-
let param = params[key];
|
|
875
|
+
const keyMatch = segment.match(/^:(\w+)(\??)$/);
|
|
864
876
|
|
|
865
|
-
if (
|
|
866
|
-
|
|
867
|
-
|
|
877
|
+
if (keyMatch) {
|
|
878
|
+
const [, key, optional] = keyMatch;
|
|
879
|
+
let param = params[key];
|
|
868
880
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
881
|
+
if (optional === "?") {
|
|
882
|
+
return param == null ? "" : param;
|
|
883
|
+
}
|
|
872
884
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
const star = "*";
|
|
885
|
+
if (param == null) {
|
|
886
|
+
invariant(false, "Missing \":" + key + "\" param");
|
|
887
|
+
}
|
|
877
888
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
// the entire path
|
|
881
|
-
return str === "/*" ? "/" : "";
|
|
882
|
-
} // Apply the splat
|
|
889
|
+
return param;
|
|
890
|
+
} // Remove any optional markers from optional static segments
|
|
883
891
|
|
|
884
892
|
|
|
885
|
-
return ""
|
|
886
|
-
})
|
|
893
|
+
return segment.replace(/\?$/g, "");
|
|
894
|
+
}) // Remove empty segments
|
|
895
|
+
.filter(segment => !!segment);
|
|
896
|
+
return prefix + segments.join("/");
|
|
887
897
|
}
|
|
888
898
|
/**
|
|
889
899
|
* A PathPattern is used to match on some portion of a URL pathname.
|
|
@@ -1011,25 +1021,6 @@ function stripBasename(pathname, basename) {
|
|
|
1011
1021
|
|
|
1012
1022
|
return pathname.slice(startIndex) || "/";
|
|
1013
1023
|
}
|
|
1014
|
-
/**
|
|
1015
|
-
* @private
|
|
1016
|
-
*/
|
|
1017
|
-
|
|
1018
|
-
function warning(cond, message) {
|
|
1019
|
-
if (!cond) {
|
|
1020
|
-
// eslint-disable-next-line no-console
|
|
1021
|
-
if (typeof console !== "undefined") console.warn(message);
|
|
1022
|
-
|
|
1023
|
-
try {
|
|
1024
|
-
// Welcome to debugging @remix-run/router!
|
|
1025
|
-
//
|
|
1026
|
-
// This error is thrown as a convenience so you can more easily
|
|
1027
|
-
// find the source for a warning that appears in the console by
|
|
1028
|
-
// enabling "pause on exceptions" in your JavaScript debugger.
|
|
1029
|
-
throw new Error(message); // eslint-disable-next-line no-empty
|
|
1030
|
-
} catch (e) {}
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
1024
|
/**
|
|
1034
1025
|
* Returns a resolved path object relative to the given pathname.
|
|
1035
1026
|
*
|
|
@@ -1478,7 +1469,9 @@ const IDLE_BLOCKER = {
|
|
|
1478
1469
|
};
|
|
1479
1470
|
const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
1480
1471
|
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
1481
|
-
const isServer = !isBrowser;
|
|
1472
|
+
const isServer = !isBrowser;
|
|
1473
|
+
|
|
1474
|
+
const defaultDetectErrorBoundary = route => Boolean(route.hasErrorBoundary); //#endregion
|
|
1482
1475
|
////////////////////////////////////////////////////////////////////////////////
|
|
1483
1476
|
//#region createRouter
|
|
1484
1477
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -1487,9 +1480,14 @@ const isServer = !isBrowser; //#endregion
|
|
|
1487
1480
|
* Create a router and listen to history POP navigations
|
|
1488
1481
|
*/
|
|
1489
1482
|
|
|
1483
|
+
|
|
1490
1484
|
function createRouter(init) {
|
|
1491
1485
|
invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
|
|
1492
|
-
let
|
|
1486
|
+
let detectErrorBoundary = init.detectErrorBoundary || defaultDetectErrorBoundary; // Routes keyed by ID
|
|
1487
|
+
|
|
1488
|
+
let manifest = {}; // Routes in tree format for matching
|
|
1489
|
+
|
|
1490
|
+
let dataRoutes = convertRoutesToDataRoutes(init.routes, detectErrorBoundary, undefined, manifest);
|
|
1493
1491
|
let inFlightDataRoutes; // Cleanup function for history
|
|
1494
1492
|
|
|
1495
1493
|
let unlistenHistory = null; // Externally-provided functions to call on all state changes
|
|
@@ -1527,7 +1525,10 @@ function createRouter(init) {
|
|
|
1527
1525
|
};
|
|
1528
1526
|
}
|
|
1529
1527
|
|
|
1530
|
-
let initialized =
|
|
1528
|
+
let initialized = // All initialMatches need to be loaded before we're ready. If we have lazy
|
|
1529
|
+
// functions around still then we'll need to run them in initialize()
|
|
1530
|
+
!initialMatches.some(m => m.route.lazy) && ( // And we have to either have no loaders or have been provided hydrationData
|
|
1531
|
+
!initialMatches.some(m => m.route.loader) || init.hydrationData != null);
|
|
1531
1532
|
let router;
|
|
1532
1533
|
let state = {
|
|
1533
1534
|
historyAction: init.history.action,
|
|
@@ -1651,12 +1652,35 @@ function createRouter(init) {
|
|
|
1651
1652
|
}
|
|
1652
1653
|
|
|
1653
1654
|
return startNavigation(historyAction, location);
|
|
1654
|
-
});
|
|
1655
|
+
});
|
|
1655
1656
|
|
|
1656
|
-
if (
|
|
1657
|
-
|
|
1657
|
+
if (state.initialized) {
|
|
1658
|
+
return router;
|
|
1658
1659
|
}
|
|
1659
1660
|
|
|
1661
|
+
let lazyMatches = state.matches.filter(m => m.route.lazy);
|
|
1662
|
+
|
|
1663
|
+
if (lazyMatches.length === 0) {
|
|
1664
|
+
// Kick off initial data load if needed. Use Pop to avoid modifying history
|
|
1665
|
+
startNavigation(exports.Action.Pop, state.location);
|
|
1666
|
+
return router;
|
|
1667
|
+
} // Load lazy modules, then kick off initial data load if needed
|
|
1668
|
+
|
|
1669
|
+
|
|
1670
|
+
let lazyPromises = lazyMatches.map(m => loadLazyRouteModule(m.route, detectErrorBoundary, manifest));
|
|
1671
|
+
Promise.all(lazyPromises).then(() => {
|
|
1672
|
+
let initialized = !state.matches.some(m => m.route.loader) || init.hydrationData != null;
|
|
1673
|
+
|
|
1674
|
+
if (initialized) {
|
|
1675
|
+
// We already have required loaderData so we can just set initialized
|
|
1676
|
+
updateState({
|
|
1677
|
+
initialized: true
|
|
1678
|
+
});
|
|
1679
|
+
} else {
|
|
1680
|
+
// We still need to kick off initial data loads
|
|
1681
|
+
startNavigation(exports.Action.Pop, state.location);
|
|
1682
|
+
}
|
|
1683
|
+
});
|
|
1660
1684
|
return router;
|
|
1661
1685
|
} // Clean up a router and it's side effects
|
|
1662
1686
|
|
|
@@ -1682,6 +1706,16 @@ function createRouter(init) {
|
|
|
1682
1706
|
function updateState(newState) {
|
|
1683
1707
|
state = _extends({}, state, newState);
|
|
1684
1708
|
subscribers.forEach(subscriber => subscriber(state));
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
function completeNavigation(location, newState) {
|
|
1712
|
+
// @ts-expect-error
|
|
1713
|
+
if (typeof document !== "undefined" && document.startViewTransition) {
|
|
1714
|
+
// @ts-expect-error
|
|
1715
|
+
document.startViewTransition(() => completeNavigationForRealz(location, newState));
|
|
1716
|
+
} else {
|
|
1717
|
+
completeNavigationForRealz(location, newState);
|
|
1718
|
+
}
|
|
1685
1719
|
} // Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION
|
|
1686
1720
|
// and setting state.[historyAction/location/matches] to the new route.
|
|
1687
1721
|
// - Location is a required param
|
|
@@ -1689,7 +1723,7 @@ function createRouter(init) {
|
|
|
1689
1723
|
// - Can pass any other state in newState
|
|
1690
1724
|
|
|
1691
1725
|
|
|
1692
|
-
function
|
|
1726
|
+
function completeNavigationForRealz(location, newState) {
|
|
1693
1727
|
var _location$state, _location$state2;
|
|
1694
1728
|
|
|
1695
1729
|
// Deduce if we're in a loading/actionReload state:
|
|
@@ -2002,7 +2036,7 @@ function createRouter(init) {
|
|
|
2002
2036
|
let result;
|
|
2003
2037
|
let actionMatch = getTargetMatch(matches, location);
|
|
2004
2038
|
|
|
2005
|
-
if (!actionMatch.route.action) {
|
|
2039
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
2006
2040
|
result = {
|
|
2007
2041
|
type: ResultType.error,
|
|
2008
2042
|
error: getInternalRouterError(405, {
|
|
@@ -2012,7 +2046,7 @@ function createRouter(init) {
|
|
|
2012
2046
|
})
|
|
2013
2047
|
};
|
|
2014
2048
|
} else {
|
|
2015
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
2049
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, router.basename);
|
|
2016
2050
|
|
|
2017
2051
|
if (request.signal.aborted) {
|
|
2018
2052
|
return {
|
|
@@ -2258,7 +2292,7 @@ function createRouter(init) {
|
|
|
2258
2292
|
interruptActiveLoads();
|
|
2259
2293
|
fetchLoadMatches.delete(key);
|
|
2260
2294
|
|
|
2261
|
-
if (!match.route.action) {
|
|
2295
|
+
if (!match.route.action && !match.route.lazy) {
|
|
2262
2296
|
let error = getInternalRouterError(405, {
|
|
2263
2297
|
method: submission.formMethod,
|
|
2264
2298
|
pathname: path,
|
|
@@ -2286,7 +2320,7 @@ function createRouter(init) {
|
|
|
2286
2320
|
let abortController = new AbortController();
|
|
2287
2321
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
2288
2322
|
fetchControllers.set(key, abortController);
|
|
2289
|
-
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
2323
|
+
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, detectErrorBoundary, router.basename);
|
|
2290
2324
|
|
|
2291
2325
|
if (fetchRequest.signal.aborted) {
|
|
2292
2326
|
// We can delete this so long as we weren't aborted by ou our own fetcher
|
|
@@ -2457,7 +2491,7 @@ function createRouter(init) {
|
|
|
2457
2491
|
let abortController = new AbortController();
|
|
2458
2492
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
2459
2493
|
fetchControllers.set(key, abortController);
|
|
2460
|
-
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2494
|
+
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, manifest, detectErrorBoundary, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2461
2495
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2462
2496
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
2463
2497
|
// below if that happens
|
|
@@ -2626,9 +2660,9 @@ function createRouter(init) {
|
|
|
2626
2660
|
// Call all navigation loaders and revalidating fetcher loaders in parallel,
|
|
2627
2661
|
// then slice off the results into separate arrays so we can handle them
|
|
2628
2662
|
// accordingly
|
|
2629
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => {
|
|
2663
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, router.basename)), ...fetchersToLoad.map(f => {
|
|
2630
2664
|
if (f.matches && f.match) {
|
|
2631
|
-
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename);
|
|
2665
|
+
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, manifest, detectErrorBoundary, router.basename);
|
|
2632
2666
|
} else {
|
|
2633
2667
|
let error = {
|
|
2634
2668
|
type: ResultType.error,
|
|
@@ -2920,7 +2954,9 @@ function createRouter(init) {
|
|
|
2920
2954
|
const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2921
2955
|
function createStaticHandler(routes, opts) {
|
|
2922
2956
|
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
2923
|
-
let
|
|
2957
|
+
let manifest = {};
|
|
2958
|
+
let detectErrorBoundary = (opts == null ? void 0 : opts.detectErrorBoundary) || defaultDetectErrorBoundary;
|
|
2959
|
+
let dataRoutes = convertRoutesToDataRoutes(routes, detectErrorBoundary, undefined, manifest);
|
|
2924
2960
|
let basename = (opts ? opts.basename : null) || "/";
|
|
2925
2961
|
/**
|
|
2926
2962
|
* The query() method is intended for document requests, in which we want to
|
|
@@ -3142,7 +3178,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3142
3178
|
async function submit(request, matches, actionMatch, requestContext, isRouteRequest) {
|
|
3143
3179
|
let result;
|
|
3144
3180
|
|
|
3145
|
-
if (!actionMatch.route.action) {
|
|
3181
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
3146
3182
|
let error = getInternalRouterError(405, {
|
|
3147
3183
|
method: request.method,
|
|
3148
3184
|
pathname: new URL(request.url).pathname,
|
|
@@ -3158,7 +3194,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3158
3194
|
error
|
|
3159
3195
|
};
|
|
3160
3196
|
} else {
|
|
3161
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest, requestContext);
|
|
3197
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext);
|
|
3162
3198
|
|
|
3163
3199
|
if (request.signal.aborted) {
|
|
3164
3200
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3256,7 +3292,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3256
3292
|
async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
|
|
3257
3293
|
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
3258
3294
|
|
|
3259
|
-
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
3295
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader) && !(routeMatch != null && routeMatch.route.lazy)) {
|
|
3260
3296
|
throw getInternalRouterError(400, {
|
|
3261
3297
|
method: request.method,
|
|
3262
3298
|
pathname: new URL(request.url).pathname,
|
|
@@ -3265,7 +3301,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3265
3301
|
}
|
|
3266
3302
|
|
|
3267
3303
|
let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
|
|
3268
|
-
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run (query())
|
|
3304
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy); // Short circuit if we have no loaders to run (query())
|
|
3269
3305
|
|
|
3270
3306
|
if (matchesToLoad.length === 0) {
|
|
3271
3307
|
return {
|
|
@@ -3281,7 +3317,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3281
3317
|
};
|
|
3282
3318
|
}
|
|
3283
3319
|
|
|
3284
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest, requestContext))]);
|
|
3320
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext))]);
|
|
3285
3321
|
|
|
3286
3322
|
if (request.signal.aborted) {
|
|
3287
3323
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3422,6 +3458,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
|
|
|
3422
3458
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3423
3459
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3424
3460
|
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
3461
|
+
if (match.route.lazy) {
|
|
3462
|
+
// We haven't loaded this route yet so we don't know if it's got a loader!
|
|
3463
|
+
return true;
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3425
3466
|
if (match.route.loader == null) {
|
|
3426
3467
|
return false;
|
|
3427
3468
|
} // Always call the loader on new route instances and pending defer cancellations
|
|
@@ -3535,8 +3576,66 @@ function shouldRevalidateLoader(loaderMatch, arg) {
|
|
|
3535
3576
|
|
|
3536
3577
|
return arg.defaultShouldRevalidate;
|
|
3537
3578
|
}
|
|
3579
|
+
/**
|
|
3580
|
+
* Execute route.lazy() methods to lazily load route modules (loader, action,
|
|
3581
|
+
* shouldRevalidate) and update the routeManifest in place which shares objects
|
|
3582
|
+
* with dataRoutes so those get updated as well.
|
|
3583
|
+
*/
|
|
3584
|
+
|
|
3585
|
+
|
|
3586
|
+
async function loadLazyRouteModule(route, detectErrorBoundary, manifest) {
|
|
3587
|
+
if (!route.lazy) {
|
|
3588
|
+
return;
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
let lazyRoute = await route.lazy(); // If the lazy route function was executed and removed by another parallel
|
|
3592
|
+
// call then we can return - first lazy() to finish wins because the return
|
|
3593
|
+
// value of lazy is expected to be static
|
|
3594
|
+
|
|
3595
|
+
if (!route.lazy) {
|
|
3596
|
+
return;
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
let routeToUpdate = manifest[route.id];
|
|
3600
|
+
invariant(routeToUpdate, "No route found in manifest"); // Update the route in place. This should be safe because there's no way
|
|
3601
|
+
// we could yet be sitting on this route as we can't get there without
|
|
3602
|
+
// resolving lazy() first.
|
|
3603
|
+
//
|
|
3604
|
+
// This is different than the HMR "update" use-case where we may actively be
|
|
3605
|
+
// on the route being updated. The main concern boils down to "does this
|
|
3606
|
+
// mutation affect any ongoing navigations or any current state.matches
|
|
3607
|
+
// values?". If not, it should be safe to update in place.
|
|
3608
|
+
|
|
3609
|
+
let routeUpdates = {};
|
|
3610
|
+
|
|
3611
|
+
for (let lazyRouteProperty in lazyRoute) {
|
|
3612
|
+
let staticRouteValue = routeToUpdate[lazyRouteProperty];
|
|
3613
|
+
let isPropertyStaticallyDefined = staticRouteValue !== undefined && // This property isn't static since it should always be updated based
|
|
3614
|
+
// on the route updates
|
|
3615
|
+
lazyRouteProperty !== "hasErrorBoundary";
|
|
3616
|
+
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."));
|
|
3617
|
+
|
|
3618
|
+
if (!isPropertyStaticallyDefined && !immutableRouteKeys.has(lazyRouteProperty)) {
|
|
3619
|
+
routeUpdates[lazyRouteProperty] = lazyRoute[lazyRouteProperty];
|
|
3620
|
+
}
|
|
3621
|
+
} // Mutate the route with the provided updates. Do this first so we pass
|
|
3622
|
+
// the updated version to detectErrorBoundary
|
|
3623
|
+
|
|
3624
|
+
|
|
3625
|
+
Object.assign(routeToUpdate, routeUpdates); // Mutate the `hasErrorBoundary` property on the route based on the route
|
|
3626
|
+
// updates and remove the `lazy` function so we don't resolve the lazy
|
|
3627
|
+
// route again.
|
|
3628
|
+
|
|
3629
|
+
Object.assign(routeToUpdate, {
|
|
3630
|
+
// To keep things framework agnostic, we use the provided
|
|
3631
|
+
// `detectErrorBoundary` function to set the `hasErrorBoundary` route
|
|
3632
|
+
// property since the logic will differ between frameworks.
|
|
3633
|
+
hasErrorBoundary: detectErrorBoundary(_extends({}, routeToUpdate)),
|
|
3634
|
+
lazy: undefined
|
|
3635
|
+
});
|
|
3636
|
+
}
|
|
3538
3637
|
|
|
3539
|
-
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
3638
|
+
async function callLoaderOrAction(type, request, match, matches, manifest, detectErrorBoundary, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
3540
3639
|
if (basename === void 0) {
|
|
3541
3640
|
basename = "/";
|
|
3542
3641
|
}
|
|
@@ -3550,29 +3649,70 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
3550
3649
|
}
|
|
3551
3650
|
|
|
3552
3651
|
let resultType;
|
|
3553
|
-
let result;
|
|
3652
|
+
let result;
|
|
3653
|
+
let onReject;
|
|
3554
3654
|
|
|
3555
|
-
let
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3655
|
+
let runHandler = handler => {
|
|
3656
|
+
// Setup a promise we can race against so that abort signals short circuit
|
|
3657
|
+
let reject;
|
|
3658
|
+
let abortPromise = new Promise((_, r) => reject = r);
|
|
3559
3659
|
|
|
3560
|
-
|
|
3660
|
+
onReject = () => reject();
|
|
3561
3661
|
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3565
|
-
result = await Promise.race([handler({
|
|
3662
|
+
request.signal.addEventListener("abort", onReject);
|
|
3663
|
+
return Promise.race([handler({
|
|
3566
3664
|
request,
|
|
3567
3665
|
params: match.params,
|
|
3568
3666
|
context: requestContext
|
|
3569
3667
|
}), abortPromise]);
|
|
3668
|
+
};
|
|
3669
|
+
|
|
3670
|
+
try {
|
|
3671
|
+
let handler = match.route[type];
|
|
3672
|
+
|
|
3673
|
+
if (match.route.lazy) {
|
|
3674
|
+
if (handler) {
|
|
3675
|
+
// Run statically defined handler in parallel with lazy()
|
|
3676
|
+
let values = await Promise.all([runHandler(handler), loadLazyRouteModule(match.route, detectErrorBoundary, manifest)]);
|
|
3677
|
+
result = values[0];
|
|
3678
|
+
} else {
|
|
3679
|
+
// Load lazy route module, then run any returned handler
|
|
3680
|
+
await loadLazyRouteModule(match.route, detectErrorBoundary, manifest);
|
|
3681
|
+
handler = match.route[type];
|
|
3682
|
+
|
|
3683
|
+
if (handler) {
|
|
3684
|
+
// Handler still run even if we got interrupted to maintain consistency
|
|
3685
|
+
// with un-abortable behavior of handler execution on non-lazy or
|
|
3686
|
+
// previously-lazy-loaded routes
|
|
3687
|
+
result = await runHandler(handler);
|
|
3688
|
+
} else if (type === "action") {
|
|
3689
|
+
throw getInternalRouterError(405, {
|
|
3690
|
+
method: request.method,
|
|
3691
|
+
pathname: new URL(request.url).pathname,
|
|
3692
|
+
routeId: match.route.id
|
|
3693
|
+
});
|
|
3694
|
+
} else {
|
|
3695
|
+
// lazy() route has no loader to run. Short circuit here so we don't
|
|
3696
|
+
// hit the invariant below that errors on returning undefined.
|
|
3697
|
+
return {
|
|
3698
|
+
type: ResultType.data,
|
|
3699
|
+
data: undefined
|
|
3700
|
+
};
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
} else {
|
|
3704
|
+
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3705
|
+
result = await runHandler(handler);
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3570
3708
|
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`.");
|
|
3571
3709
|
} catch (e) {
|
|
3572
3710
|
resultType = ResultType.error;
|
|
3573
3711
|
result = e;
|
|
3574
3712
|
} finally {
|
|
3575
|
-
|
|
3713
|
+
if (onReject) {
|
|
3714
|
+
request.signal.removeEventListener("abort", onReject);
|
|
3715
|
+
}
|
|
3576
3716
|
}
|
|
3577
3717
|
|
|
3578
3718
|
if (isResponse(result)) {
|
|
@@ -4099,6 +4239,7 @@ exports.UNSAFE_DeferredData = DeferredData;
|
|
|
4099
4239
|
exports.UNSAFE_convertRoutesToDataRoutes = convertRoutesToDataRoutes;
|
|
4100
4240
|
exports.UNSAFE_getPathContributingMatches = getPathContributingMatches;
|
|
4101
4241
|
exports.UNSAFE_invariant = invariant;
|
|
4242
|
+
exports.UNSAFE_warning = warning;
|
|
4102
4243
|
exports.createBrowserHistory = createBrowserHistory;
|
|
4103
4244
|
exports.createHashHistory = createHashHistory;
|
|
4104
4245
|
exports.createMemoryHistory = createMemoryHistory;
|
|
@@ -4120,5 +4261,4 @@ exports.redirect = redirect;
|
|
|
4120
4261
|
exports.resolvePath = resolvePath;
|
|
4121
4262
|
exports.resolveTo = resolveTo;
|
|
4122
4263
|
exports.stripBasename = stripBasename;
|
|
4123
|
-
exports.warning = warning;
|
|
4124
4264
|
//# sourceMappingURL=router.cjs.js.map
|