@remix-run/router 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/dist/history.d.ts +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/router.cjs.js +220 -90
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +6 -3
- package/dist/router.js +216 -90
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +220 -90
- package/dist/router.umd.js.map +1 -1
- package/dist/router.umd.min.js +2 -2
- package/dist/router.umd.min.js.map +1 -1
- package/dist/utils.d.ts +25 -6
- package/history.ts +1 -1
- package/index.ts +6 -2
- package/package.json +1 -1
- package/router.ts +239 -30
- package/utils.ts +102 -69
package/dist/router.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 v1.
|
|
2
|
+
* @remix-run/router v1.4.0
|
|
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
|
|
|
@@ -1953,7 +1973,7 @@ function createRouter(init) {
|
|
|
1953
1973
|
let result;
|
|
1954
1974
|
let actionMatch = getTargetMatch(matches, location);
|
|
1955
1975
|
|
|
1956
|
-
if (!actionMatch.route.action) {
|
|
1976
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
1957
1977
|
result = {
|
|
1958
1978
|
type: ResultType.error,
|
|
1959
1979
|
error: getInternalRouterError(405, {
|
|
@@ -1963,7 +1983,7 @@ function createRouter(init) {
|
|
|
1963
1983
|
})
|
|
1964
1984
|
};
|
|
1965
1985
|
} else {
|
|
1966
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
1986
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, router.basename);
|
|
1967
1987
|
|
|
1968
1988
|
if (request.signal.aborted) {
|
|
1969
1989
|
return {
|
|
@@ -2209,7 +2229,7 @@ function createRouter(init) {
|
|
|
2209
2229
|
interruptActiveLoads();
|
|
2210
2230
|
fetchLoadMatches.delete(key);
|
|
2211
2231
|
|
|
2212
|
-
if (!match.route.action) {
|
|
2232
|
+
if (!match.route.action && !match.route.lazy) {
|
|
2213
2233
|
let error = getInternalRouterError(405, {
|
|
2214
2234
|
method: submission.formMethod,
|
|
2215
2235
|
pathname: path,
|
|
@@ -2237,7 +2257,7 @@ function createRouter(init) {
|
|
|
2237
2257
|
let abortController = new AbortController();
|
|
2238
2258
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
2239
2259
|
fetchControllers.set(key, abortController);
|
|
2240
|
-
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
2260
|
+
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, detectErrorBoundary, router.basename);
|
|
2241
2261
|
|
|
2242
2262
|
if (fetchRequest.signal.aborted) {
|
|
2243
2263
|
// We can delete this so long as we weren't aborted by ou our own fetcher
|
|
@@ -2408,7 +2428,7 @@ function createRouter(init) {
|
|
|
2408
2428
|
let abortController = new AbortController();
|
|
2409
2429
|
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
2410
2430
|
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
|
|
2431
|
+
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
2432
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2413
2433
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
2414
2434
|
// below if that happens
|
|
@@ -2577,9 +2597,9 @@ function createRouter(init) {
|
|
|
2577
2597
|
// Call all navigation loaders and revalidating fetcher loaders in parallel,
|
|
2578
2598
|
// then slice off the results into separate arrays so we can handle them
|
|
2579
2599
|
// accordingly
|
|
2580
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(f => {
|
|
2600
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, router.basename)), ...fetchersToLoad.map(f => {
|
|
2581
2601
|
if (f.matches && f.match) {
|
|
2582
|
-
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, router.basename);
|
|
2602
|
+
return callLoaderOrAction("loader", createClientSideRequest(init.history, f.path, request.signal), f.match, f.matches, manifest, detectErrorBoundary, router.basename);
|
|
2583
2603
|
} else {
|
|
2584
2604
|
let error = {
|
|
2585
2605
|
type: ResultType.error,
|
|
@@ -2871,7 +2891,9 @@ function createRouter(init) {
|
|
|
2871
2891
|
const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2872
2892
|
function createStaticHandler(routes, opts) {
|
|
2873
2893
|
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
2874
|
-
let
|
|
2894
|
+
let manifest = {};
|
|
2895
|
+
let detectErrorBoundary = (opts == null ? void 0 : opts.detectErrorBoundary) || defaultDetectErrorBoundary;
|
|
2896
|
+
let dataRoutes = convertRoutesToDataRoutes(routes, detectErrorBoundary, undefined, manifest);
|
|
2875
2897
|
let basename = (opts ? opts.basename : null) || "/";
|
|
2876
2898
|
/**
|
|
2877
2899
|
* The query() method is intended for document requests, in which we want to
|
|
@@ -3093,7 +3115,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3093
3115
|
async function submit(request, matches, actionMatch, requestContext, isRouteRequest) {
|
|
3094
3116
|
let result;
|
|
3095
3117
|
|
|
3096
|
-
if (!actionMatch.route.action) {
|
|
3118
|
+
if (!actionMatch.route.action && !actionMatch.route.lazy) {
|
|
3097
3119
|
let error = getInternalRouterError(405, {
|
|
3098
3120
|
method: request.method,
|
|
3099
3121
|
pathname: new URL(request.url).pathname,
|
|
@@ -3109,7 +3131,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3109
3131
|
error
|
|
3110
3132
|
};
|
|
3111
3133
|
} else {
|
|
3112
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest, requestContext);
|
|
3134
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext);
|
|
3113
3135
|
|
|
3114
3136
|
if (request.signal.aborted) {
|
|
3115
3137
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3207,7 +3229,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3207
3229
|
async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
|
|
3208
3230
|
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
3209
3231
|
|
|
3210
|
-
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
3232
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader) && !(routeMatch != null && routeMatch.route.lazy)) {
|
|
3211
3233
|
throw getInternalRouterError(400, {
|
|
3212
3234
|
method: request.method,
|
|
3213
3235
|
pathname: new URL(request.url).pathname,
|
|
@@ -3216,7 +3238,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3216
3238
|
}
|
|
3217
3239
|
|
|
3218
3240
|
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())
|
|
3241
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy); // Short circuit if we have no loaders to run (query())
|
|
3220
3242
|
|
|
3221
3243
|
if (matchesToLoad.length === 0) {
|
|
3222
3244
|
return {
|
|
@@ -3232,7 +3254,7 @@ function createStaticHandler(routes, opts) {
|
|
|
3232
3254
|
};
|
|
3233
3255
|
}
|
|
3234
3256
|
|
|
3235
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest, requestContext))]);
|
|
3257
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, manifest, detectErrorBoundary, basename, true, isRouteRequest, requestContext))]);
|
|
3236
3258
|
|
|
3237
3259
|
if (request.signal.aborted) {
|
|
3238
3260
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -3373,6 +3395,11 @@ function getMatchesToLoad(history, state, matches, submission, location, isReval
|
|
|
3373
3395
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3374
3396
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3375
3397
|
let navigationMatches = boundaryMatches.filter((match, index) => {
|
|
3398
|
+
if (match.route.lazy) {
|
|
3399
|
+
// We haven't loaded this route yet so we don't know if it's got a loader!
|
|
3400
|
+
return true;
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3376
3403
|
if (match.route.loader == null) {
|
|
3377
3404
|
return false;
|
|
3378
3405
|
} // Always call the loader on new route instances and pending defer cancellations
|
|
@@ -3486,8 +3513,66 @@ function shouldRevalidateLoader(loaderMatch, arg) {
|
|
|
3486
3513
|
|
|
3487
3514
|
return arg.defaultShouldRevalidate;
|
|
3488
3515
|
}
|
|
3516
|
+
/**
|
|
3517
|
+
* Execute route.lazy() methods to lazily load route modules (loader, action,
|
|
3518
|
+
* shouldRevalidate) and update the routeManifest in place which shares objects
|
|
3519
|
+
* with dataRoutes so those get updated as well.
|
|
3520
|
+
*/
|
|
3521
|
+
|
|
3522
|
+
|
|
3523
|
+
async function loadLazyRouteModule(route, detectErrorBoundary, manifest) {
|
|
3524
|
+
if (!route.lazy) {
|
|
3525
|
+
return;
|
|
3526
|
+
}
|
|
3527
|
+
|
|
3528
|
+
let lazyRoute = await route.lazy(); // If the lazy route function was executed and removed by another parallel
|
|
3529
|
+
// call then we can return - first lazy() to finish wins because the return
|
|
3530
|
+
// value of lazy is expected to be static
|
|
3531
|
+
|
|
3532
|
+
if (!route.lazy) {
|
|
3533
|
+
return;
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3536
|
+
let routeToUpdate = manifest[route.id];
|
|
3537
|
+
invariant(routeToUpdate, "No route found in manifest"); // Update the route in place. This should be safe because there's no way
|
|
3538
|
+
// we could yet be sitting on this route as we can't get there without
|
|
3539
|
+
// resolving lazy() first.
|
|
3540
|
+
//
|
|
3541
|
+
// This is different than the HMR "update" use-case where we may actively be
|
|
3542
|
+
// on the route being updated. The main concern boils down to "does this
|
|
3543
|
+
// mutation affect any ongoing navigations or any current state.matches
|
|
3544
|
+
// values?". If not, it should be safe to update in place.
|
|
3545
|
+
|
|
3546
|
+
let routeUpdates = {};
|
|
3547
|
+
|
|
3548
|
+
for (let lazyRouteProperty in lazyRoute) {
|
|
3549
|
+
let staticRouteValue = routeToUpdate[lazyRouteProperty];
|
|
3550
|
+
let isPropertyStaticallyDefined = staticRouteValue !== undefined && // This property isn't static since it should always be updated based
|
|
3551
|
+
// on the route updates
|
|
3552
|
+
lazyRouteProperty !== "hasErrorBoundary";
|
|
3553
|
+
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."));
|
|
3554
|
+
|
|
3555
|
+
if (!isPropertyStaticallyDefined && !immutableRouteKeys.has(lazyRouteProperty)) {
|
|
3556
|
+
routeUpdates[lazyRouteProperty] = lazyRoute[lazyRouteProperty];
|
|
3557
|
+
}
|
|
3558
|
+
} // Mutate the route with the provided updates. Do this first so we pass
|
|
3559
|
+
// the updated version to detectErrorBoundary
|
|
3560
|
+
|
|
3561
|
+
|
|
3562
|
+
Object.assign(routeToUpdate, routeUpdates); // Mutate the `hasErrorBoundary` property on the route based on the route
|
|
3563
|
+
// updates and remove the `lazy` function so we don't resolve the lazy
|
|
3564
|
+
// route again.
|
|
3489
3565
|
|
|
3490
|
-
|
|
3566
|
+
Object.assign(routeToUpdate, {
|
|
3567
|
+
// To keep things framework agnostic, we use the provided
|
|
3568
|
+
// `detectErrorBoundary` function to set the `hasErrorBoundary` route
|
|
3569
|
+
// property since the logic will differ between frameworks.
|
|
3570
|
+
hasErrorBoundary: detectErrorBoundary(_extends({}, routeToUpdate)),
|
|
3571
|
+
lazy: undefined
|
|
3572
|
+
});
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
async function callLoaderOrAction(type, request, match, matches, manifest, detectErrorBoundary, basename, isStaticRequest, isRouteRequest, requestContext) {
|
|
3491
3576
|
if (basename === void 0) {
|
|
3492
3577
|
basename = "/";
|
|
3493
3578
|
}
|
|
@@ -3501,29 +3586,70 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
3501
3586
|
}
|
|
3502
3587
|
|
|
3503
3588
|
let resultType;
|
|
3504
|
-
let result;
|
|
3589
|
+
let result;
|
|
3590
|
+
let onReject;
|
|
3505
3591
|
|
|
3506
|
-
let
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3592
|
+
let runHandler = handler => {
|
|
3593
|
+
// Setup a promise we can race against so that abort signals short circuit
|
|
3594
|
+
let reject;
|
|
3595
|
+
let abortPromise = new Promise((_, r) => reject = r);
|
|
3510
3596
|
|
|
3511
|
-
|
|
3597
|
+
onReject = () => reject();
|
|
3512
3598
|
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3516
|
-
result = await Promise.race([handler({
|
|
3599
|
+
request.signal.addEventListener("abort", onReject);
|
|
3600
|
+
return Promise.race([handler({
|
|
3517
3601
|
request,
|
|
3518
3602
|
params: match.params,
|
|
3519
3603
|
context: requestContext
|
|
3520
3604
|
}), abortPromise]);
|
|
3605
|
+
};
|
|
3606
|
+
|
|
3607
|
+
try {
|
|
3608
|
+
let handler = match.route[type];
|
|
3609
|
+
|
|
3610
|
+
if (match.route.lazy) {
|
|
3611
|
+
if (handler) {
|
|
3612
|
+
// Run statically defined handler in parallel with lazy()
|
|
3613
|
+
let values = await Promise.all([runHandler(handler), loadLazyRouteModule(match.route, detectErrorBoundary, manifest)]);
|
|
3614
|
+
result = values[0];
|
|
3615
|
+
} else {
|
|
3616
|
+
// Load lazy route module, then run any returned handler
|
|
3617
|
+
await loadLazyRouteModule(match.route, detectErrorBoundary, manifest);
|
|
3618
|
+
handler = match.route[type];
|
|
3619
|
+
|
|
3620
|
+
if (handler) {
|
|
3621
|
+
// Handler still run even if we got interrupted to maintain consistency
|
|
3622
|
+
// with un-abortable behavior of handler execution on non-lazy or
|
|
3623
|
+
// previously-lazy-loaded routes
|
|
3624
|
+
result = await runHandler(handler);
|
|
3625
|
+
} else if (type === "action") {
|
|
3626
|
+
throw getInternalRouterError(405, {
|
|
3627
|
+
method: request.method,
|
|
3628
|
+
pathname: new URL(request.url).pathname,
|
|
3629
|
+
routeId: match.route.id
|
|
3630
|
+
});
|
|
3631
|
+
} else {
|
|
3632
|
+
// lazy() route has no loader to run. Short circuit here so we don't
|
|
3633
|
+
// hit the invariant below that errors on returning undefined.
|
|
3634
|
+
return {
|
|
3635
|
+
type: ResultType.data,
|
|
3636
|
+
data: undefined
|
|
3637
|
+
};
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
} else {
|
|
3641
|
+
invariant(handler, "Could not find the " + type + " to run on the \"" + match.route.id + "\" route");
|
|
3642
|
+
result = await runHandler(handler);
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3521
3645
|
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
3646
|
} catch (e) {
|
|
3523
3647
|
resultType = ResultType.error;
|
|
3524
3648
|
result = e;
|
|
3525
3649
|
} finally {
|
|
3526
|
-
|
|
3650
|
+
if (onReject) {
|
|
3651
|
+
request.signal.removeEventListener("abort", onReject);
|
|
3652
|
+
}
|
|
3527
3653
|
}
|
|
3528
3654
|
|
|
3529
3655
|
if (isResponse(result)) {
|
|
@@ -4040,5 +4166,5 @@ function getTargetMatch(matches, location) {
|
|
|
4040
4166
|
return pathMatches[pathMatches.length - 1];
|
|
4041
4167
|
} //#endregion
|
|
4042
4168
|
|
|
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
|
|
4169
|
+
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
4170
|
//# sourceMappingURL=router.js.map
|