@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.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { History, Location, Path, To } from "./history";
|
|
2
2
|
import { Action as HistoryAction } from "./history";
|
|
3
|
-
import type { AgnosticDataRouteMatch, AgnosticDataRouteObject, FormEncType, FormMethod, RouteData, AgnosticRouteObject, AgnosticRouteMatch } from "./utils";
|
|
3
|
+
import type { AgnosticDataRouteMatch, AgnosticDataRouteObject, FormEncType, FormMethod, DetectErrorBoundaryFunction, RouteData, AgnosticRouteObject, AgnosticRouteMatch } from "./utils";
|
|
4
4
|
import { DeferredData } from "./utils";
|
|
5
5
|
/**
|
|
6
6
|
* A Router instance manages all navigation and data loading/mutations
|
|
@@ -244,6 +244,7 @@ export interface RouterInit {
|
|
|
244
244
|
routes: AgnosticRouteObject[];
|
|
245
245
|
history: History;
|
|
246
246
|
hydrationData?: HydrationState;
|
|
247
|
+
detectErrorBoundary?: DetectErrorBoundaryFunction;
|
|
247
248
|
}
|
|
248
249
|
/**
|
|
249
250
|
* State returned from a server-side query() call
|
|
@@ -423,9 +424,11 @@ export declare const IDLE_BLOCKER: BlockerUnblocked;
|
|
|
423
424
|
*/
|
|
424
425
|
export declare function createRouter(init: RouterInit): Router;
|
|
425
426
|
export declare const UNSAFE_DEFERRED_SYMBOL: unique symbol;
|
|
426
|
-
export
|
|
427
|
+
export interface CreateStaticHandlerOptions {
|
|
427
428
|
basename?: string;
|
|
428
|
-
|
|
429
|
+
detectErrorBoundary?: DetectErrorBoundaryFunction;
|
|
430
|
+
}
|
|
431
|
+
export declare function createStaticHandler(routes: AgnosticRouteObject[], opts?: CreateStaticHandlerOptions): StaticHandler;
|
|
429
432
|
/**
|
|
430
433
|
* Given an existing StaticHandlerContext and an error thrown at render time,
|
|
431
434
|
* provide an updated StaticHandlerContext suitable for a second SSR render
|
package/dist/router.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
|
*
|
|
@@ -95,7 +95,7 @@ function createMemoryHistory(options) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
let location = createLocation(entries ? getCurrentLocation().pathname : "/", to, state, key);
|
|
98
|
-
warning
|
|
98
|
+
warning(location.pathname.charAt(0) === "/", "relative pathnames are not supported in memory history: " + JSON.stringify(to));
|
|
99
99
|
return location;
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -260,7 +260,7 @@ function createHashHistory(options) {
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
function validateHashLocation(location, to) {
|
|
263
|
-
warning
|
|
263
|
+
warning(location.pathname.charAt(0) === "/", "relative pathnames are not supported in hash history.push(" + JSON.stringify(to) + ")");
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
return getUrlBasedHistory(createHashLocation, createHashHref, validateHashLocation, options);
|
|
@@ -270,8 +270,7 @@ function invariant(value, message) {
|
|
|
270
270
|
throw new Error(message);
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
|
-
|
|
274
|
-
function warning$1(cond, message) {
|
|
273
|
+
function warning(cond, message) {
|
|
275
274
|
if (!cond) {
|
|
276
275
|
// eslint-disable-next-line no-console
|
|
277
276
|
if (typeof console !== "undefined") console.warn(message);
|
|
@@ -527,40 +526,50 @@ var ResultType;
|
|
|
527
526
|
ResultType["error"] = "error";
|
|
528
527
|
})(ResultType || (ResultType = {}));
|
|
529
528
|
|
|
529
|
+
const immutableRouteKeys = new Set(["lazy", "caseSensitive", "path", "id", "index", "children"]);
|
|
530
|
+
|
|
530
531
|
function isIndexRoute(route) {
|
|
531
532
|
return route.index === true;
|
|
532
533
|
} // Walk the route tree generating unique IDs where necessary so we are working
|
|
533
534
|
// solely with AgnosticDataRouteObject's within the Router
|
|
534
535
|
|
|
535
536
|
|
|
536
|
-
function convertRoutesToDataRoutes(routes, parentPath,
|
|
537
|
+
function convertRoutesToDataRoutes(routes, detectErrorBoundary, parentPath, manifest) {
|
|
537
538
|
if (parentPath === void 0) {
|
|
538
539
|
parentPath = [];
|
|
539
540
|
}
|
|
540
541
|
|
|
541
|
-
if (
|
|
542
|
-
|
|
542
|
+
if (manifest === void 0) {
|
|
543
|
+
manifest = {};
|
|
543
544
|
}
|
|
544
545
|
|
|
545
546
|
return routes.map((route, index) => {
|
|
546
547
|
let treePath = [...parentPath, index];
|
|
547
548
|
let id = typeof route.id === "string" ? route.id : treePath.join("-");
|
|
548
549
|
invariant(route.index !== true || !route.children, "Cannot specify children on an index route");
|
|
549
|
-
invariant(!
|
|
550
|
-
allIds.add(id);
|
|
550
|
+
invariant(!manifest[id], "Found a route id collision on id \"" + id + "\". Route " + "id's must be globally unique within Data Router usages");
|
|
551
551
|
|
|
552
552
|
if (isIndexRoute(route)) {
|
|
553
553
|
let indexRoute = _extends({}, route, {
|
|
554
|
+
hasErrorBoundary: detectErrorBoundary(route),
|
|
554
555
|
id
|
|
555
556
|
});
|
|
556
557
|
|
|
558
|
+
manifest[id] = indexRoute;
|
|
557
559
|
return indexRoute;
|
|
558
560
|
} else {
|
|
559
561
|
let pathOrLayoutRoute = _extends({}, route, {
|
|
560
562
|
id,
|
|
561
|
-
|
|
563
|
+
hasErrorBoundary: detectErrorBoundary(route),
|
|
564
|
+
children: undefined
|
|
562
565
|
});
|
|
563
566
|
|
|
567
|
+
manifest[id] = pathOrLayoutRoute;
|
|
568
|
+
|
|
569
|
+
if (route.children) {
|
|
570
|
+
pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, detectErrorBoundary, treePath, manifest);
|
|
571
|
+
}
|
|
572
|
+
|
|
564
573
|
return pathOrLayoutRoute;
|
|
565
574
|
}
|
|
566
575
|
});
|
|
@@ -807,45 +816,42 @@ function generatePath(originalPath, params) {
|
|
|
807
816
|
if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
|
|
808
817
|
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(/\*$/, "/*") + "\"."));
|
|
809
818
|
path = path.replace(/\*$/, "/*");
|
|
810
|
-
}
|
|
819
|
+
} // ensure `/` is added at the beginning if the path is absolute
|
|
811
820
|
|
|
812
|
-
return path.replace(/^:(\w+)(\??)/g, (_, key, optional) => {
|
|
813
|
-
let param = params[key];
|
|
814
821
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
822
|
+
const prefix = path.startsWith("/") ? "/" : "";
|
|
823
|
+
const segments = path.split(/\/+/).map((segment, index, array) => {
|
|
824
|
+
const isLastSegment = index === array.length - 1; // only apply the splat if it's the last segment
|
|
825
|
+
|
|
826
|
+
if (isLastSegment && segment === "*") {
|
|
827
|
+
const star = "*";
|
|
828
|
+
const starParam = params[star]; // Apply the splat
|
|
818
829
|
|
|
819
|
-
|
|
820
|
-
invariant(false, "Missing \":" + key + "\" param");
|
|
830
|
+
return starParam;
|
|
821
831
|
}
|
|
822
832
|
|
|
823
|
-
|
|
824
|
-
}).replace(/\/:(\w+)(\??)/g, (_, key, optional) => {
|
|
825
|
-
let param = params[key];
|
|
833
|
+
const keyMatch = segment.match(/^:(\w+)(\??)$/);
|
|
826
834
|
|
|
827
|
-
if (
|
|
828
|
-
|
|
829
|
-
|
|
835
|
+
if (keyMatch) {
|
|
836
|
+
const [, key, optional] = keyMatch;
|
|
837
|
+
let param = params[key];
|
|
830
838
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
839
|
+
if (optional === "?") {
|
|
840
|
+
return param == null ? "" : param;
|
|
841
|
+
}
|
|
834
842
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const star = "*";
|
|
843
|
+
if (param == null) {
|
|
844
|
+
invariant(false, "Missing \":" + key + "\" param");
|
|
845
|
+
}
|
|
839
846
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
// the entire path
|
|
843
|
-
return str === "/*" ? "/" : "";
|
|
844
|
-
} // Apply the splat
|
|
847
|
+
return param;
|
|
848
|
+
} // Remove any optional markers from optional static segments
|
|
845
849
|
|
|
846
850
|
|
|
847
|
-
return ""
|
|
848
|
-
})
|
|
851
|
+
return segment.replace(/\?$/g, "");
|
|
852
|
+
}) // Remove empty segments
|
|
853
|
+
.filter(segment => !!segment);
|
|
854
|
+
return prefix + segments.join("/");
|
|
849
855
|
}
|
|
850
856
|
/**
|
|
851
857
|
* Performs pattern matching on a URL pathname and returns information about
|
|
@@ -970,25 +976,6 @@ function stripBasename(pathname, basename) {
|
|
|
970
976
|
|
|
971
977
|
return pathname.slice(startIndex) || "/";
|
|
972
978
|
}
|
|
973
|
-
/**
|
|
974
|
-
* @private
|
|
975
|
-
*/
|
|
976
|
-
|
|
977
|
-
function warning(cond, message) {
|
|
978
|
-
if (!cond) {
|
|
979
|
-
// eslint-disable-next-line no-console
|
|
980
|
-
if (typeof console !== "undefined") console.warn(message);
|
|
981
|
-
|
|
982
|
-
try {
|
|
983
|
-
// Welcome to debugging @remix-run/router!
|
|
984
|
-
//
|
|
985
|
-
// This error is thrown as a convenience so you can more easily
|
|
986
|
-
// find the source for a warning that appears in the console by
|
|
987
|
-
// enabling "pause on exceptions" in your JavaScript debugger.
|
|
988
|
-
throw new Error(message); // eslint-disable-next-line no-empty
|
|
989
|
-
} catch (e) {}
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
979
|
/**
|
|
993
980
|
* Returns a resolved path object relative to the given pathname.
|
|
994
981
|
*
|
|
@@ -1430,7 +1417,9 @@ const IDLE_BLOCKER = {
|
|
|
1430
1417
|
};
|
|
1431
1418
|
const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
|
|
1432
1419
|
const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
|
|
1433
|
-
const isServer = !isBrowser;
|
|
1420
|
+
const isServer = !isBrowser;
|
|
1421
|
+
|
|
1422
|
+
const defaultDetectErrorBoundary = route => Boolean(route.hasErrorBoundary); //#endregion
|
|
1434
1423
|
////////////////////////////////////////////////////////////////////////////////
|
|
1435
1424
|
//#region createRouter
|
|
1436
1425
|
////////////////////////////////////////////////////////////////////////////////
|
|
@@ -1439,9 +1428,14 @@ const isServer = !isBrowser; //#endregion
|
|
|
1439
1428
|
* Create a router and listen to history POP navigations
|
|
1440
1429
|
*/
|
|
1441
1430
|
|
|
1431
|
+
|
|
1442
1432
|
function createRouter(init) {
|
|
1443
1433
|
invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
|
|
1444
|
-
let
|
|
1434
|
+
let detectErrorBoundary = init.detectErrorBoundary || defaultDetectErrorBoundary; // Routes keyed by ID
|
|
1435
|
+
|
|
1436
|
+
let manifest = {}; // Routes in tree format for matching
|
|
1437
|
+
|
|
1438
|
+
let dataRoutes = convertRoutesToDataRoutes(init.routes, detectErrorBoundary, undefined, manifest);
|
|
1445
1439
|
let inFlightDataRoutes; // Cleanup function for history
|
|
1446
1440
|
|
|
1447
1441
|
let unlistenHistory = null; // Externally-provided functions to call on all state changes
|
|
@@ -1479,7 +1473,10 @@ function createRouter(init) {
|
|
|
1479
1473
|
};
|
|
1480
1474
|
}
|
|
1481
1475
|
|
|
1482
|
-
let initialized =
|
|
1476
|
+
let initialized = // All initialMatches need to be loaded before we're ready. If we have lazy
|
|
1477
|
+
// functions around still then we'll need to run them in initialize()
|
|
1478
|
+
!initialMatches.some(m => m.route.lazy) && ( // And we have to either have no loaders or have been provided hydrationData
|
|
1479
|
+
!initialMatches.some(m => m.route.loader) || init.hydrationData != null);
|
|
1483
1480
|
let router;
|
|
1484
1481
|
let state = {
|
|
1485
1482
|
historyAction: init.history.action,
|
|
@@ -1603,12 +1600,35 @@ function createRouter(init) {
|
|
|
1603
1600
|
}
|
|
1604
1601
|
|
|
1605
1602
|
return startNavigation(historyAction, location);
|
|
1606
|
-
});
|
|
1603
|
+
});
|
|
1607
1604
|
|
|
1608
|
-
if (
|
|
1609
|
-
|
|
1605
|
+
if (state.initialized) {
|
|
1606
|
+
return router;
|
|
1610
1607
|
}
|
|
1611
1608
|
|
|
1609
|
+
let lazyMatches = state.matches.filter(m => m.route.lazy);
|
|
1610
|
+
|
|
1611
|
+
if (lazyMatches.length === 0) {
|
|
1612
|
+
// Kick off initial data load if needed. Use Pop to avoid modifying history
|
|
1613
|
+
startNavigation(Action.Pop, state.location);
|
|
1614
|
+
return router;
|
|
1615
|
+
} // Load lazy modules, then kick off initial data load if needed
|
|
1616
|
+
|
|
1617
|
+
|
|
1618
|
+
let lazyPromises = lazyMatches.map(m => loadLazyRouteModule(m.route, detectErrorBoundary, manifest));
|
|
1619
|
+
Promise.all(lazyPromises).then(() => {
|
|
1620
|
+
let initialized = !state.matches.some(m => m.route.loader) || init.hydrationData != null;
|
|
1621
|
+
|
|
1622
|
+
if (initialized) {
|
|
1623
|
+
// We already have required loaderData so we can just set initialized
|
|
1624
|
+
updateState({
|
|
1625
|
+
initialized: true
|
|
1626
|
+
});
|
|
1627
|
+
} else {
|
|
1628
|
+
// We still need to kick off initial data loads
|
|
1629
|
+
startNavigation(Action.Pop, state.location);
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1612
1632
|
return router;
|
|
1613
1633
|
} // Clean up a router and it's side effects
|
|
1614
1634
|
|
|
@@ -1634,6 +1654,16 @@ function createRouter(init) {
|
|
|
1634
1654
|
function updateState(newState) {
|
|
1635
1655
|
state = _extends({}, state, newState);
|
|
1636
1656
|
subscribers.forEach(subscriber => subscriber(state));
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
function completeNavigation(location, newState) {
|
|
1660
|
+
// @ts-expect-error
|
|
1661
|
+
if (typeof document !== "undefined" && document.startViewTransition) {
|
|
1662
|
+
// @ts-expect-error
|
|
1663
|
+
document.startViewTransition(() => completeNavigationForRealz(location, newState));
|
|
1664
|
+
} else {
|
|
1665
|
+
completeNavigationForRealz(location, newState);
|
|
1666
|
+
}
|
|
1637
1667
|
} // Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION
|
|
1638
1668
|
// and setting state.[historyAction/location/matches] to the new route.
|
|
1639
1669
|
// - Location is a required param
|
|
@@ -1641,7 +1671,7 @@ function createRouter(init) {
|
|
|
1641
1671
|
// - Can pass any other state in newState
|
|
1642
1672
|
|
|
1643
1673
|
|
|
1644
|
-
function
|
|
1674
|
+
function completeNavigationForRealz(location, newState) {
|
|
1645
1675
|
var _location$state, _location$state2;
|
|
1646
1676
|
|
|
1647
1677
|
// Deduce if we're in a loading/actionReload state:
|
|
@@ -1953,7 +1983,7 @@ function createRouter(init) {
|
|
|
1953
1983
|
let result;
|
|
1954
1984
|
let actionMatch = getTargetMatch(matches, location);
|
|
1955
1985
|
|
|
1956
|
-
if (!actionMatch.route.action) {
|
|
1986
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
1957
1987
|
result = {
|
|
1958
1988
|
type: ResultType.error,
|
|
1959
1989
|
error: getInternalRouterError(405, {
|
|
@@ -1963,7 +1993,7 @@ function createRouter(init) {
|
|
|
1963
1993
|
})
|
|
1964
1994
|
};
|
|
1965
1995
|
} else {
|
|
1966
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
1996
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, router.basename);
|
|
1967
1997
|
|
|
1968
1998
|
if (request.signal.aborted) {
|
|
1969
1999
|
return {
|
|
@@ -2209,7 +2239,7 @@ function createRouter(init) {
|
|
|
2209
2239
|
interruptActiveLoads();
|
|
2210
2240
|
fetchLoadMatches.delete(key);
|
|
2211
2241
|
|
|
2212
|
-
if (!match.route.action) {
|
|
2242
|
+
if (!match.route.action && !match.route.lazy) {
|
|
2213
2243
|
let error = getInternalRouterError(405, {
|
|
2214
2244
|
method: submission.formMethod,
|
|
2215
2245
|
pathname: path,
|
|
@@ -2237,7 +2267,7 @@ function createRouter(init) {
|
|
|
2237
2267
|
let abortController = new AbortController();
|
|
2238
2268
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
2239
2269
|
fetchControllers.set(key, abortController);
|
|
2240
|
-
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
2270
|
+
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, detectErrorBoundary, router.basename);
|
|
2241
2271
|
|
|
2242
2272
|
if (fetchRequest.signal.aborted) {
|
|
2243
2273
|
// We can delete this so long as we weren't aborted by ou our own fetcher
|
|
@@ -2408,7 +2438,7 @@ function createRouter(init) {
|
|
|
2408
2438
|
let abortController = new AbortController();
|
|
2409
2439
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
2410
2440
|
fetchControllers.set(key, abortController);
|
|
2411
|
-
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2441
|
+
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, manifest, detectErrorBoundary, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2412
2442
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2413
2443
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
2414
2444
|
// below if that happens
|
|
@@ -2577,9 +2607,9 @@ function createRouter(init) {
|
|
|
2577
2607
|
// Call all navigation loaders and revalidating fetcher loaders in parallel,
|
|
2578
2608
|
// then slice off the results into separate arrays so we can handle them
|
|
2579
2609
|
// accordingly
|
|
2580
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => {
|
|
2610
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, router.basename)), ...fetchersToLoad.map(f => {
|
|
2581
2611
|
if (f.matches && f.match) {
|
|
2582
|
-
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename);
|
|
2612
|
+
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, manifest, detectErrorBoundary, router.basename);
|
|
2583
2613
|
} else {
|
|
2584
2614
|
let error = {
|
|
2585
2615
|
type: ResultType.error,
|
|
@@ -2871,7 +2901,9 @@ function createRouter(init) {
|
|
|
2871
2901
|
const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2872
2902
|
function createStaticHandler(routes, opts) {
|
|
2873
2903
|
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
2874
|
-
let
|
|
2904
|
+
let manifest = {};
|
|
2905
|
+
let detectErrorBoundary = (opts == null ? void 0 : opts.detectErrorBoundary) || defaultDetectErrorBoundary;
|
|
2906
|
+
let dataRoutes = convertRoutesToDataRoutes(routes, detectErrorBoundary, undefined, manifest);
|
|
2875
2907
|
let basename = (opts ? opts.basename : null) || "/";
|
|
2876
2908
|
/**
|
|
2877
2909
|
* The query() method is intended for document requests, in which we want to
|
|
@@ -3093,7 +3125,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3093
3125
|
async function submit(request, matches, actionMatch, requestContext, isRouteRequest) {
|
|
3094
3126
|
let result;
|
|
3095
3127
|
|
|
3096
|
-
if (!actionMatch.route.action) {
|
|
3128
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
3097
3129
|
let error = getInternalRouterError(405, {
|
|
3098
3130
|
method: request.method,
|
|
3099
3131
|
pathname: new URL(request.url).pathname,
|
|
@@ -3109,7 +3141,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3109
3141
|
error
|
|
3110
3142
|
};
|
|
3111
3143
|
} else {
|
|
3112
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest, requestContext);
|
|
3144
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext);
|
|
3113
3145
|
|
|
3114
3146
|
if (request.signal.aborted) {
|
|
3115
3147
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3207,7 +3239,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3207
3239
|
async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
|
|
3208
3240
|
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
3209
3241
|
|
|
3210
|
-
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
3242
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader) && !(routeMatch != null && routeMatch.route.lazy)) {
|
|
3211
3243
|
throw getInternalRouterError(400, {
|
|
3212
3244
|
method: request.method,
|
|
3213
3245
|
pathname: new URL(request.url).pathname,
|
|
@@ -3216,7 +3248,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3216
3248
|
}
|
|
3217
3249
|
|
|
3218
3250
|
let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
|
|
3219
|
-
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run (query())
|
|
3251
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy); // Short circuit if we have no loaders to run (query())
|
|
3220
3252
|
|
|
3221
3253
|
if (matchesToLoad.length === 0) {
|
|
3222
3254
|
return {
|
|
@@ -3232,7 +3264,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3232
3264
|
};
|
|
3233
3265
|
}
|
|
3234
3266
|
|
|
3235
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest, requestContext))]);
|
|
3267
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext))]);
|
|
3236
3268
|
|
|
3237
3269
|
if (request.signal.aborted) {
|
|
3238
3270
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3373,6 +3405,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
|
|
|
3373
3405
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3374
3406
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3375
3407
|
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
3408
|
+
if (match.route.lazy) {
|
|
3409
|
+
// We haven't loaded this route yet so we don't know if it's got a loader!
|
|
3410
|
+
return true;
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3376
3413
|
if (match.route.loader == null) {
|
|
3377
3414
|
return false;
|
|
3378
3415
|
} // Always call the loader on new route instances and pending defer cancellations
|
|
@@ -3486,8 +3523,66 @@ function shouldRevalidateLoader(loaderMatch, arg) {
|
|
|
3486
3523
|
|
|
3487
3524
|
return arg.defaultShouldRevalidate;
|
|
3488
3525
|
}
|
|
3526
|
+
/**
|
|
3527
|
+
* Execute route.lazy() methods to lazily load route modules (loader, action,
|
|
3528
|
+
* shouldRevalidate) and update the routeManifest in place which shares objects
|
|
3529
|
+
* with dataRoutes so those get updated as well.
|
|
3530
|
+
*/
|
|
3531
|
+
|
|
3532
|
+
|
|
3533
|
+
async function loadLazyRouteModule(route, detectErrorBoundary, manifest) {
|
|
3534
|
+
if (!route.lazy) {
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
let lazyRoute = await route.lazy(); // If the lazy route function was executed and removed by another parallel
|
|
3539
|
+
// call then we can return - first lazy() to finish wins because the return
|
|
3540
|
+
// value of lazy is expected to be static
|
|
3541
|
+
|
|
3542
|
+
if (!route.lazy) {
|
|
3543
|
+
return;
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
let routeToUpdate = manifest[route.id];
|
|
3547
|
+
invariant(routeToUpdate, "No route found in manifest"); // Update the route in place. This should be safe because there's no way
|
|
3548
|
+
// we could yet be sitting on this route as we can't get there without
|
|
3549
|
+
// resolving lazy() first.
|
|
3550
|
+
//
|
|
3551
|
+
// This is different than the HMR "update" use-case where we may actively be
|
|
3552
|
+
// on the route being updated. The main concern boils down to "does this
|
|
3553
|
+
// mutation affect any ongoing navigations or any current state.matches
|
|
3554
|
+
// values?". If not, it should be safe to update in place.
|
|
3555
|
+
|
|
3556
|
+
let routeUpdates = {};
|
|
3557
|
+
|
|
3558
|
+
for (let lazyRouteProperty in lazyRoute) {
|
|
3559
|
+
let staticRouteValue = routeToUpdate[lazyRouteProperty];
|
|
3560
|
+
let isPropertyStaticallyDefined = staticRouteValue !== undefined && // This property isn't static since it should always be updated based
|
|
3561
|
+
// on the route updates
|
|
3562
|
+
lazyRouteProperty !== "hasErrorBoundary";
|
|
3563
|
+
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."));
|
|
3564
|
+
|
|
3565
|
+
if (!isPropertyStaticallyDefined && !immutableRouteKeys.has(lazyRouteProperty)) {
|
|
3566
|
+
routeUpdates[lazyRouteProperty] = lazyRoute[lazyRouteProperty];
|
|
3567
|
+
}
|
|
3568
|
+
} // Mutate the route with the provided updates. Do this first so we pass
|
|
3569
|
+
// the updated version to detectErrorBoundary
|
|
3489
3570
|
|
|
3490
|
-
|
|
3571
|
+
|
|
3572
|
+
Object.assign(routeToUpdate, routeUpdates); // Mutate the `hasErrorBoundary` property on the route based on the route
|
|
3573
|
+
// updates and remove the `lazy` function so we don't resolve the lazy
|
|
3574
|
+
// route again.
|
|
3575
|
+
|
|
3576
|
+
Object.assign(routeToUpdate, {
|
|
3577
|
+
// To keep things framework agnostic, we use the provided
|
|
3578
|
+
// `detectErrorBoundary` function to set the `hasErrorBoundary` route
|
|
3579
|
+
// property since the logic will differ between frameworks.
|
|
3580
|
+
hasErrorBoundary: detectErrorBoundary(_extends({}, routeToUpdate)),
|
|
3581
|
+
lazy: undefined
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3585
|
+
async function callLoaderOrAction(type, request, match, matches, manifest, detectErrorBoundary, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
3491
3586
|
if (basename === void 0) {
|
|
3492
3587
|
basename = "/";
|
|
3493
3588
|
}
|
|
@@ -3501,29 +3596,70 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
3501
3596
|
}
|
|
3502
3597
|
|
|
3503
3598
|
let resultType;
|
|
3504
|
-
let result;
|
|
3505
|
-
|
|
3506
|
-
let reject;
|
|
3507
|
-
let abortPromise = new Promise((_, r) => reject = r);
|
|
3599
|
+
let result;
|
|
3600
|
+
let onReject;
|
|
3508
3601
|
|
|
3509
|
-
let
|
|
3602
|
+
let runHandler = handler => {
|
|
3603
|
+
// Setup a promise we can race against so that abort signals short circuit
|
|
3604
|
+
let reject;
|
|
3605
|
+
let abortPromise = new Promise((_, r) => reject = r);
|
|
3510
3606
|
|
|
3511
|
-
|
|
3607
|
+
onReject = () => reject();
|
|
3512
3608
|
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3516
|
-
result = await Promise.race([handler({
|
|
3609
|
+
request.signal.addEventListener("abort", onReject);
|
|
3610
|
+
return Promise.race([handler({
|
|
3517
3611
|
request,
|
|
3518
3612
|
params: match.params,
|
|
3519
3613
|
context: requestContext
|
|
3520
3614
|
}), abortPromise]);
|
|
3615
|
+
};
|
|
3616
|
+
|
|
3617
|
+
try {
|
|
3618
|
+
let handler = match.route[type];
|
|
3619
|
+
|
|
3620
|
+
if (match.route.lazy) {
|
|
3621
|
+
if (handler) {
|
|
3622
|
+
// Run statically defined handler in parallel with lazy()
|
|
3623
|
+
let values = await Promise.all([runHandler(handler), loadLazyRouteModule(match.route, detectErrorBoundary, manifest)]);
|
|
3624
|
+
result = values[0];
|
|
3625
|
+
} else {
|
|
3626
|
+
// Load lazy route module, then run any returned handler
|
|
3627
|
+
await loadLazyRouteModule(match.route, detectErrorBoundary, manifest);
|
|
3628
|
+
handler = match.route[type];
|
|
3629
|
+
|
|
3630
|
+
if (handler) {
|
|
3631
|
+
// Handler still run even if we got interrupted to maintain consistency
|
|
3632
|
+
// with un-abortable behavior of handler execution on non-lazy or
|
|
3633
|
+
// previously-lazy-loaded routes
|
|
3634
|
+
result = await runHandler(handler);
|
|
3635
|
+
} else if (type === "action") {
|
|
3636
|
+
throw getInternalRouterError(405, {
|
|
3637
|
+
method: request.method,
|
|
3638
|
+
pathname: new URL(request.url).pathname,
|
|
3639
|
+
routeId: match.route.id
|
|
3640
|
+
});
|
|
3641
|
+
} else {
|
|
3642
|
+
// lazy() route has no loader to run. Short circuit here so we don't
|
|
3643
|
+
// hit the invariant below that errors on returning undefined.
|
|
3644
|
+
return {
|
|
3645
|
+
type: ResultType.data,
|
|
3646
|
+
data: undefined
|
|
3647
|
+
};
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
} else {
|
|
3651
|
+
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3652
|
+
result = await runHandler(handler);
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3521
3655
|
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`.");
|
|
3522
3656
|
} catch (e) {
|
|
3523
3657
|
resultType = ResultType.error;
|
|
3524
3658
|
result = e;
|
|
3525
3659
|
} finally {
|
|
3526
|
-
|
|
3660
|
+
if (onReject) {
|
|
3661
|
+
request.signal.removeEventListener("abort", onReject);
|
|
3662
|
+
}
|
|
3527
3663
|
}
|
|
3528
3664
|
|
|
3529
3665
|
if (isResponse(result)) {
|
|
@@ -4040,5 +4176,5 @@ function getTargetMatch(matches, location) {
|
|
|
4040
4176
|
return pathMatches[pathMatches.length - 1];
|
|
4041
4177
|
} //#endregion
|
|
4042
4178
|
|
|
4043
|
-
export { AbortedDeferredError, Action, ErrorResponse, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, invariant as UNSAFE_invariant, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename
|
|
4179
|
+
export { AbortedDeferredError, Action, ErrorResponse, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, invariant as UNSAFE_invariant, warning as UNSAFE_warning, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename };
|
|
4044
4180
|
//# sourceMappingURL=router.js.map
|