@remix-run/router 1.2.1 → 1.3.0-pre.1
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 +18 -0
- package/dist/history.d.ts +6 -1
- package/dist/index.d.ts +1 -2
- package/dist/router.cjs.js +177 -90
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +3 -0
- package/dist/router.js +176 -91
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +177 -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 +14 -7
- package/history.ts +33 -23
- package/index.ts +3 -3
- package/package.json +1 -1
- package/router.ts +95 -39
- package/utils.ts +88 -42
package/dist/router.d.ts
CHANGED
|
@@ -229,6 +229,7 @@ export interface StaticHandlerContext {
|
|
|
229
229
|
statusCode: number;
|
|
230
230
|
loaderHeaders: Record<string, Headers>;
|
|
231
231
|
actionHeaders: Record<string, Headers>;
|
|
232
|
+
activeDeferreds: Record<string, DeferredData> | null;
|
|
232
233
|
_deepestRenderedBoundaryId?: string | null;
|
|
233
234
|
}
|
|
234
235
|
/**
|
|
@@ -284,6 +285,7 @@ declare type LinkNavigateOptions = {
|
|
|
284
285
|
declare type SubmissionNavigateOptions = {
|
|
285
286
|
replace?: boolean;
|
|
286
287
|
state?: any;
|
|
288
|
+
preventScrollReset?: boolean;
|
|
287
289
|
formMethod?: FormMethod;
|
|
288
290
|
formEncType?: FormEncType;
|
|
289
291
|
formData: FormData;
|
|
@@ -366,6 +368,7 @@ export declare const IDLE_FETCHER: FetcherStates["Idle"];
|
|
|
366
368
|
* Create a router and listen to history POP navigations
|
|
367
369
|
*/
|
|
368
370
|
export declare function createRouter(init: RouterInit): Router;
|
|
371
|
+
export declare const UNSAFE_DEFERRED_SYMBOL: unique symbol;
|
|
369
372
|
export declare function createStaticHandler(routes: AgnosticRouteObject[], opts?: {
|
|
370
373
|
basename?: string;
|
|
371
374
|
}): StaticHandler;
|
package/dist/router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.
|
|
2
|
+
* @remix-run/router v1.3.0-pre.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -99,6 +99,10 @@ function createMemoryHistory(options) {
|
|
|
99
99
|
return location;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function createHref(to) {
|
|
103
|
+
return typeof to === "string" ? to : createPath(to);
|
|
104
|
+
}
|
|
105
|
+
|
|
102
106
|
let history = {
|
|
103
107
|
get index() {
|
|
104
108
|
return index;
|
|
@@ -112,8 +116,10 @@ function createMemoryHistory(options) {
|
|
|
112
116
|
return getCurrentLocation();
|
|
113
117
|
},
|
|
114
118
|
|
|
115
|
-
createHref
|
|
116
|
-
|
|
119
|
+
createHref,
|
|
120
|
+
|
|
121
|
+
createURL(to) {
|
|
122
|
+
return new URL(createHref(to), "http://localhost");
|
|
117
123
|
},
|
|
118
124
|
|
|
119
125
|
encodeLocation(to) {
|
|
@@ -358,15 +364,6 @@ function parsePath(path) {
|
|
|
358
364
|
|
|
359
365
|
return parsedPath;
|
|
360
366
|
}
|
|
361
|
-
function createClientSideURL(location) {
|
|
362
|
-
// window.location.origin is "null" (the literal string value) in Firefox
|
|
363
|
-
// under certain conditions, notably when serving from a local HTML file
|
|
364
|
-
// See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
|
|
365
|
-
let base = typeof window !== "undefined" && typeof window.location !== "undefined" && window.location.origin !== "null" ? window.location.origin : window.location.href;
|
|
366
|
-
let href = typeof location === "string" ? location : createPath(location);
|
|
367
|
-
invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
|
|
368
|
-
return new URL(href, base);
|
|
369
|
-
}
|
|
370
367
|
|
|
371
368
|
function getUrlBasedHistory(getLocation, createHref, validateLocation, options) {
|
|
372
369
|
if (options === void 0) {
|
|
@@ -431,6 +428,16 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
431
428
|
}
|
|
432
429
|
}
|
|
433
430
|
|
|
431
|
+
function createURL(to) {
|
|
432
|
+
// window.location.origin is "null" (the literal string value) in Firefox
|
|
433
|
+
// under certain conditions, notably when serving from a local HTML file
|
|
434
|
+
// See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
|
|
435
|
+
let base = window.location.origin !== "null" ? window.location.origin : window.location.href;
|
|
436
|
+
let href = typeof to === "string" ? to : createPath(to);
|
|
437
|
+
invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
|
|
438
|
+
return new URL(href, base);
|
|
439
|
+
}
|
|
440
|
+
|
|
434
441
|
let history = {
|
|
435
442
|
get action() {
|
|
436
443
|
return action;
|
|
@@ -457,9 +464,11 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
457
464
|
return createHref(window, to);
|
|
458
465
|
},
|
|
459
466
|
|
|
467
|
+
createURL,
|
|
468
|
+
|
|
460
469
|
encodeLocation(to) {
|
|
461
470
|
// Encode a Location the same way window.location would
|
|
462
|
-
let url =
|
|
471
|
+
let url = createURL(to);
|
|
463
472
|
return {
|
|
464
473
|
pathname: url.pathname,
|
|
465
474
|
search: url.search,
|
|
@@ -769,13 +778,32 @@ function generatePath(originalPath, params) {
|
|
|
769
778
|
path = path.replace(/\*$/, "/*");
|
|
770
779
|
}
|
|
771
780
|
|
|
772
|
-
return path.replace(/^:(\w+)/g, (_, key) => {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
781
|
+
return path.replace(/^:(\w+)(\??)/g, (_, key, optional) => {
|
|
782
|
+
let param = params[key];
|
|
783
|
+
|
|
784
|
+
if (optional === "?") {
|
|
785
|
+
return param == null ? "" : param;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if (param == null) {
|
|
789
|
+
invariant(false, "Missing \":" + key + "\" param");
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
return param;
|
|
793
|
+
}).replace(/\/:(\w+)(\??)/g, (_, key, optional) => {
|
|
794
|
+
let param = params[key];
|
|
795
|
+
|
|
796
|
+
if (optional === "?") {
|
|
797
|
+
return param == null ? "" : "/" + param;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
if (param == null) {
|
|
801
|
+
invariant(false, "Missing \":" + key + "\" param");
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
return "/" + param;
|
|
805
|
+
}) // Remove any optional markers from optional static segments
|
|
806
|
+
.replace(/\?/g, "").replace(/(\/?)\*/, (_, prefix, __, str) => {
|
|
779
807
|
const star = "*";
|
|
780
808
|
|
|
781
809
|
if (params[star] == null) {
|
|
@@ -1119,9 +1147,10 @@ const json = function json(data, init) {
|
|
|
1119
1147
|
};
|
|
1120
1148
|
class AbortedDeferredError extends Error {}
|
|
1121
1149
|
class DeferredData {
|
|
1122
|
-
constructor(data) {
|
|
1123
|
-
this.
|
|
1124
|
-
this.
|
|
1150
|
+
constructor(data, responseInit) {
|
|
1151
|
+
this.pendingKeysSet = new Set();
|
|
1152
|
+
this.subscribers = new Set();
|
|
1153
|
+
this.deferredKeys = [];
|
|
1125
1154
|
invariant(data && typeof data === "object" && !Array.isArray(data), "defer() only accepts plain objects"); // Set up an AbortController + Promise we can race against to exit early
|
|
1126
1155
|
// cancellation
|
|
1127
1156
|
|
|
@@ -1140,6 +1169,7 @@ class DeferredData {
|
|
|
1140
1169
|
[key]: this.trackPromise(key, value)
|
|
1141
1170
|
});
|
|
1142
1171
|
}, {});
|
|
1172
|
+
this.init = responseInit;
|
|
1143
1173
|
}
|
|
1144
1174
|
|
|
1145
1175
|
trackPromise(key, value) {
|
|
@@ -1147,7 +1177,8 @@ class DeferredData {
|
|
|
1147
1177
|
return value;
|
|
1148
1178
|
}
|
|
1149
1179
|
|
|
1150
|
-
this.
|
|
1180
|
+
this.deferredKeys.push(key);
|
|
1181
|
+
this.pendingKeysSet.add(key); // We store a little wrapper promise that will be extended with
|
|
1151
1182
|
// _data/_error props upon resolve/reject
|
|
1152
1183
|
|
|
1153
1184
|
let promise = Promise.race([value, this.abortPromise]).then(data => this.onSettle(promise, key, null, data), error => this.onSettle(promise, key, error)); // Register rejection listeners to avoid uncaught promise rejections on
|
|
@@ -1169,39 +1200,41 @@ class DeferredData {
|
|
|
1169
1200
|
return Promise.reject(error);
|
|
1170
1201
|
}
|
|
1171
1202
|
|
|
1172
|
-
this.
|
|
1203
|
+
this.pendingKeysSet.delete(key);
|
|
1173
1204
|
|
|
1174
1205
|
if (this.done) {
|
|
1175
1206
|
// Nothing left to abort!
|
|
1176
1207
|
this.unlistenAbortSignal();
|
|
1177
1208
|
}
|
|
1178
1209
|
|
|
1179
|
-
const subscriber = this.subscriber;
|
|
1180
|
-
|
|
1181
1210
|
if (error) {
|
|
1182
1211
|
Object.defineProperty(promise, "_error", {
|
|
1183
1212
|
get: () => error
|
|
1184
1213
|
});
|
|
1185
|
-
|
|
1214
|
+
this.emit(false, key);
|
|
1186
1215
|
return Promise.reject(error);
|
|
1187
1216
|
}
|
|
1188
1217
|
|
|
1189
1218
|
Object.defineProperty(promise, "_data", {
|
|
1190
1219
|
get: () => data
|
|
1191
1220
|
});
|
|
1192
|
-
|
|
1221
|
+
this.emit(false, key);
|
|
1193
1222
|
return data;
|
|
1194
1223
|
}
|
|
1195
1224
|
|
|
1225
|
+
emit(aborted, settledKey) {
|
|
1226
|
+
this.subscribers.forEach(subscriber => subscriber(aborted, settledKey));
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1196
1229
|
subscribe(fn) {
|
|
1197
|
-
this.
|
|
1230
|
+
this.subscribers.add(fn);
|
|
1231
|
+
return () => this.subscribers.delete(fn);
|
|
1198
1232
|
}
|
|
1199
1233
|
|
|
1200
1234
|
cancel() {
|
|
1201
1235
|
this.controller.abort();
|
|
1202
|
-
this.
|
|
1203
|
-
|
|
1204
|
-
subscriber && subscriber(true);
|
|
1236
|
+
this.pendingKeysSet.forEach((v, k) => this.pendingKeysSet.delete(k));
|
|
1237
|
+
this.emit(true);
|
|
1205
1238
|
}
|
|
1206
1239
|
|
|
1207
1240
|
async resolveData(signal) {
|
|
@@ -1226,7 +1259,7 @@ class DeferredData {
|
|
|
1226
1259
|
}
|
|
1227
1260
|
|
|
1228
1261
|
get done() {
|
|
1229
|
-
return this.
|
|
1262
|
+
return this.pendingKeysSet.size === 0;
|
|
1230
1263
|
}
|
|
1231
1264
|
|
|
1232
1265
|
get unwrappedData() {
|
|
@@ -1239,6 +1272,10 @@ class DeferredData {
|
|
|
1239
1272
|
}, {});
|
|
1240
1273
|
}
|
|
1241
1274
|
|
|
1275
|
+
get pendingKeys() {
|
|
1276
|
+
return Array.from(this.pendingKeysSet);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1242
1279
|
}
|
|
1243
1280
|
|
|
1244
1281
|
function isTrackedPromise(value) {
|
|
@@ -1257,9 +1294,16 @@ function unwrapTrackedPromise(value) {
|
|
|
1257
1294
|
return value._data;
|
|
1258
1295
|
}
|
|
1259
1296
|
|
|
1260
|
-
function defer(data) {
|
|
1261
|
-
|
|
1262
|
-
}
|
|
1297
|
+
const defer = function defer(data, init) {
|
|
1298
|
+
if (init === void 0) {
|
|
1299
|
+
init = {};
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
let responseInit = typeof init === "number" ? {
|
|
1303
|
+
status: init
|
|
1304
|
+
} : init;
|
|
1305
|
+
return new DeferredData(data, responseInit);
|
|
1306
|
+
};
|
|
1263
1307
|
/**
|
|
1264
1308
|
* A redirect response. Sets the status code and the `Location` header.
|
|
1265
1309
|
* Defaults to "302 Found".
|
|
@@ -1498,7 +1542,7 @@ function createRouter(init) {
|
|
|
1498
1542
|
|
|
1499
1543
|
|
|
1500
1544
|
function completeNavigation(location, newState) {
|
|
1501
|
-
var _location$state;
|
|
1545
|
+
var _location$state, _location$state2;
|
|
1502
1546
|
|
|
1503
1547
|
// Deduce if we're in a loading/actionReload state:
|
|
1504
1548
|
// - We have committed actionData in the store
|
|
@@ -1524,7 +1568,10 @@ function createRouter(init) {
|
|
|
1524
1568
|
} // Always preserve any existing loaderData from re-used routes
|
|
1525
1569
|
|
|
1526
1570
|
|
|
1527
|
-
let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;
|
|
1571
|
+
let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData; // Always respect the user flag. Otherwise don't reset on mutation
|
|
1572
|
+
// submission navigations unless they redirect
|
|
1573
|
+
|
|
1574
|
+
let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true;
|
|
1528
1575
|
updateState(_extends({}, newState, {
|
|
1529
1576
|
actionData,
|
|
1530
1577
|
loaderData,
|
|
@@ -1533,9 +1580,8 @@ function createRouter(init) {
|
|
|
1533
1580
|
initialized: true,
|
|
1534
1581
|
navigation: IDLE_NAVIGATION,
|
|
1535
1582
|
revalidation: "idle",
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
preventScrollReset: pendingPreventScrollReset
|
|
1583
|
+
restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),
|
|
1584
|
+
preventScrollReset
|
|
1539
1585
|
}));
|
|
1540
1586
|
|
|
1541
1587
|
if (isUninterruptedRevalidation) ; else if (pendingAction === Action.Pop) ; else if (pendingAction === Action.Push) {
|
|
@@ -1677,7 +1723,7 @@ function createRouter(init) {
|
|
|
1677
1723
|
|
|
1678
1724
|
|
|
1679
1725
|
pendingNavigationController = new AbortController();
|
|
1680
|
-
let request = createClientSideRequest(location, pendingNavigationController.signal, opts && opts.submission);
|
|
1726
|
+
let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
|
|
1681
1727
|
let pendingActionData;
|
|
1682
1728
|
let pendingError;
|
|
1683
1729
|
|
|
@@ -1818,7 +1864,9 @@ function createRouter(init) {
|
|
|
1818
1864
|
}
|
|
1819
1865
|
|
|
1820
1866
|
if (isDeferredResult(result)) {
|
|
1821
|
-
throw
|
|
1867
|
+
throw getInternalRouterError(400, {
|
|
1868
|
+
type: "defer-action"
|
|
1869
|
+
});
|
|
1822
1870
|
}
|
|
1823
1871
|
|
|
1824
1872
|
return {
|
|
@@ -1855,7 +1903,7 @@ function createRouter(init) {
|
|
|
1855
1903
|
formData: loadingNavigation.formData,
|
|
1856
1904
|
formEncType: loadingNavigation.formEncType
|
|
1857
1905
|
} : undefined;
|
|
1858
|
-
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches); // Cancel pending deferreds for no-longer-matched routes or routes we're
|
|
1906
|
+
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches); // Cancel pending deferreds for no-longer-matched routes or routes we're
|
|
1859
1907
|
// about to reload. Note that if this is an action reload we would have
|
|
1860
1908
|
// already cancelled all pending deferreds so this would be a no-op
|
|
1861
1909
|
|
|
@@ -2037,7 +2085,7 @@ function createRouter(init) {
|
|
|
2037
2085
|
}); // Call the action for the fetcher
|
|
2038
2086
|
|
|
2039
2087
|
let abortController = new AbortController();
|
|
2040
|
-
let fetchRequest = createClientSideRequest(path, abortController.signal, submission);
|
|
2088
|
+
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
2041
2089
|
fetchControllers.set(key, abortController);
|
|
2042
2090
|
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
2043
2091
|
|
|
@@ -2078,13 +2126,15 @@ function createRouter(init) {
|
|
|
2078
2126
|
}
|
|
2079
2127
|
|
|
2080
2128
|
if (isDeferredResult(actionResult)) {
|
|
2081
|
-
|
|
2129
|
+
throw getInternalRouterError(400, {
|
|
2130
|
+
type: "defer-action"
|
|
2131
|
+
});
|
|
2082
2132
|
} // Start the data load for current matches, or the next location if we're
|
|
2083
2133
|
// in the middle of a navigation
|
|
2084
2134
|
|
|
2085
2135
|
|
|
2086
2136
|
let nextLocation = state.navigation.location || state.location;
|
|
2087
|
-
let revalidationRequest = createClientSideRequest(nextLocation, abortController.signal);
|
|
2137
|
+
let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
|
|
2088
2138
|
let matches = state.navigation.state !== "idle" ? matchRoutes(dataRoutes, state.navigation.location, init.basename) : state.matches;
|
|
2089
2139
|
invariant(matches, "Didn't find any matches after fetcher action");
|
|
2090
2140
|
let loadId = ++incrementingLoadId;
|
|
@@ -2098,7 +2148,7 @@ function createRouter(init) {
|
|
|
2098
2148
|
});
|
|
2099
2149
|
|
|
2100
2150
|
state.fetchers.set(key, loadFetcher);
|
|
2101
|
-
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
|
|
2151
|
+
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
|
|
2102
2152
|
[match.route.id]: actionResult.data
|
|
2103
2153
|
}, undefined, // No need to send through errors since we short circuit above
|
|
2104
2154
|
fetchLoadMatches); // Put all revalidating fetchers into the loading state, except for the
|
|
@@ -2211,9 +2261,9 @@ function createRouter(init) {
|
|
|
2211
2261
|
}); // Call the loader for this fetcher route match
|
|
2212
2262
|
|
|
2213
2263
|
let abortController = new AbortController();
|
|
2214
|
-
let fetchRequest = createClientSideRequest(path, abortController.signal);
|
|
2264
|
+
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
2215
2265
|
fetchControllers.set(key, abortController);
|
|
2216
|
-
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported
|
|
2266
|
+
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2217
2267
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2218
2268
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
2219
2269
|
// below if that happens
|
|
@@ -2313,7 +2363,7 @@ function createRouter(init) {
|
|
|
2313
2363
|
invariant(redirectLocation, "Expected a location on the redirect navigation"); // Check if this an external redirect that goes to a new origin
|
|
2314
2364
|
|
|
2315
2365
|
if (typeof ((_window = window) == null ? void 0 : _window.location) !== "undefined") {
|
|
2316
|
-
let newOrigin =
|
|
2366
|
+
let newOrigin = init.history.createURL(redirect.location).origin;
|
|
2317
2367
|
|
|
2318
2368
|
if (window.location.origin !== newOrigin) {
|
|
2319
2369
|
if (replace) {
|
|
@@ -2355,7 +2405,9 @@ function createRouter(init) {
|
|
|
2355
2405
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2356
2406
|
submission: _extends({}, submission, {
|
|
2357
2407
|
formAction: redirect.location
|
|
2358
|
-
})
|
|
2408
|
+
}),
|
|
2409
|
+
// Preserve this flag across redirects
|
|
2410
|
+
preventScrollReset: pendingPreventScrollReset
|
|
2359
2411
|
});
|
|
2360
2412
|
} else {
|
|
2361
2413
|
// Otherwise, we kick off a new loading navigation, preserving the
|
|
@@ -2368,7 +2420,9 @@ function createRouter(init) {
|
|
|
2368
2420
|
formAction: submission ? submission.formAction : undefined,
|
|
2369
2421
|
formEncType: submission ? submission.formEncType : undefined,
|
|
2370
2422
|
formData: submission ? submission.formData : undefined
|
|
2371
|
-
}
|
|
2423
|
+
},
|
|
2424
|
+
// Preserve this flag across redirects
|
|
2425
|
+
preventScrollReset: pendingPreventScrollReset
|
|
2372
2426
|
});
|
|
2373
2427
|
}
|
|
2374
2428
|
}
|
|
@@ -2379,7 +2433,7 @@ function createRouter(init) {
|
|
|
2379
2433
|
// accordingly
|
|
2380
2434
|
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(_ref8 => {
|
|
2381
2435
|
let [, href, match, fetchMatches] = _ref8;
|
|
2382
|
-
return callLoaderOrAction("loader", createClientSideRequest(href, request.signal), match, fetchMatches, router.basename);
|
|
2436
|
+
return callLoaderOrAction("loader", createClientSideRequest(init.history, href, request.signal), match, fetchMatches, router.basename);
|
|
2383
2437
|
})]);
|
|
2384
2438
|
let loaderResults = results.slice(0, matchesToLoad.length);
|
|
2385
2439
|
let fetcherResults = results.slice(matchesToLoad.length);
|
|
@@ -2588,6 +2642,7 @@ function createRouter(init) {
|
|
|
2588
2642
|
//#region createStaticHandler
|
|
2589
2643
|
////////////////////////////////////////////////////////////////////////////////
|
|
2590
2644
|
|
|
2645
|
+
const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2591
2646
|
function createStaticHandler(routes, opts) {
|
|
2592
2647
|
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
2593
2648
|
let dataRoutes = convertRoutesToDataRoutes(routes);
|
|
@@ -2640,7 +2695,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2640
2695
|
},
|
|
2641
2696
|
statusCode: error.status,
|
|
2642
2697
|
loaderHeaders: {},
|
|
2643
|
-
actionHeaders: {}
|
|
2698
|
+
actionHeaders: {},
|
|
2699
|
+
activeDeferreds: null
|
|
2644
2700
|
};
|
|
2645
2701
|
} else if (!matches) {
|
|
2646
2702
|
let error = getInternalRouterError(404, {
|
|
@@ -2661,7 +2717,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2661
2717
|
},
|
|
2662
2718
|
statusCode: error.status,
|
|
2663
2719
|
loaderHeaders: {},
|
|
2664
|
-
actionHeaders: {}
|
|
2720
|
+
actionHeaders: {},
|
|
2721
|
+
activeDeferreds: null
|
|
2665
2722
|
};
|
|
2666
2723
|
}
|
|
2667
2724
|
|
|
@@ -2752,8 +2809,23 @@ function createStaticHandler(routes, opts) {
|
|
|
2752
2809
|
} // Pick off the right state value to return
|
|
2753
2810
|
|
|
2754
2811
|
|
|
2755
|
-
|
|
2756
|
-
|
|
2812
|
+
if (result.actionData) {
|
|
2813
|
+
return Object.values(result.actionData)[0];
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
if (result.loaderData) {
|
|
2817
|
+
var _result$activeDeferre;
|
|
2818
|
+
|
|
2819
|
+
let data = Object.values(result.loaderData)[0];
|
|
2820
|
+
|
|
2821
|
+
if ((_result$activeDeferre = result.activeDeferreds) != null && _result$activeDeferre[match.route.id]) {
|
|
2822
|
+
data[UNSAFE_DEFERRED_SYMBOL] = result.activeDeferreds[match.route.id];
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
return data;
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
return undefined;
|
|
2757
2829
|
}
|
|
2758
2830
|
|
|
2759
2831
|
async function queryImpl(request, location, matches, requestContext, routeMatch) {
|
|
@@ -2833,7 +2905,18 @@ function createStaticHandler(routes, opts) {
|
|
|
2833
2905
|
}
|
|
2834
2906
|
|
|
2835
2907
|
if (isDeferredResult(result)) {
|
|
2836
|
-
|
|
2908
|
+
let error = getInternalRouterError(400, {
|
|
2909
|
+
type: "defer-action"
|
|
2910
|
+
});
|
|
2911
|
+
|
|
2912
|
+
if (isRouteRequest) {
|
|
2913
|
+
throw error;
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
result = {
|
|
2917
|
+
type: ResultType.error,
|
|
2918
|
+
error
|
|
2919
|
+
};
|
|
2837
2920
|
}
|
|
2838
2921
|
|
|
2839
2922
|
if (isRouteRequest) {
|
|
@@ -2854,7 +2937,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2854
2937
|
// return the raw Response or value
|
|
2855
2938
|
statusCode: 200,
|
|
2856
2939
|
loaderHeaders: {},
|
|
2857
|
-
actionHeaders: {}
|
|
2940
|
+
actionHeaders: {},
|
|
2941
|
+
activeDeferreds: null
|
|
2858
2942
|
};
|
|
2859
2943
|
}
|
|
2860
2944
|
|
|
@@ -2917,7 +3001,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2917
3001
|
}), {}),
|
|
2918
3002
|
errors: pendingActionError || null,
|
|
2919
3003
|
statusCode: 200,
|
|
2920
|
-
loaderHeaders: {}
|
|
3004
|
+
loaderHeaders: {},
|
|
3005
|
+
activeDeferreds: null
|
|
2921
3006
|
};
|
|
2922
3007
|
}
|
|
2923
3008
|
|
|
@@ -2926,27 +3011,21 @@ function createStaticHandler(routes, opts) {
|
|
|
2926
3011
|
if (request.signal.aborted) {
|
|
2927
3012
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
2928
3013
|
throw new Error(method + "() call aborted");
|
|
2929
|
-
}
|
|
2930
|
-
|
|
2931
|
-
let executedLoaders = new Set();
|
|
2932
|
-
results.forEach((result, i) => {
|
|
2933
|
-
executedLoaders.add(matchesToLoad[i].route.id); // Can't do anything with these without the Remix side of things, so just
|
|
2934
|
-
// cancel them for now
|
|
3014
|
+
} // Process and commit output from loaders
|
|
2935
3015
|
|
|
2936
|
-
if (isDeferredResult(result)) {
|
|
2937
|
-
result.deferredData.cancel();
|
|
2938
|
-
}
|
|
2939
|
-
}); // Process and commit output from loaders
|
|
2940
3016
|
|
|
2941
|
-
let
|
|
3017
|
+
let activeDeferreds = new Map();
|
|
3018
|
+
let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError, activeDeferreds); // Add a null for any non-loader matches for proper revalidation on the client
|
|
2942
3019
|
|
|
3020
|
+
let executedLoaders = new Set(matchesToLoad.map(match => match.route.id));
|
|
2943
3021
|
matches.forEach(match => {
|
|
2944
3022
|
if (!executedLoaders.has(match.route.id)) {
|
|
2945
3023
|
context.loaderData[match.route.id] = null;
|
|
2946
3024
|
}
|
|
2947
3025
|
});
|
|
2948
3026
|
return _extends({}, context, {
|
|
2949
|
-
matches
|
|
3027
|
+
matches,
|
|
3028
|
+
activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null
|
|
2950
3029
|
});
|
|
2951
3030
|
}
|
|
2952
3031
|
|
|
@@ -3065,13 +3144,13 @@ function getLoaderMatchesUntilBoundary(matches, boundaryId) {
|
|
|
3065
3144
|
return boundaryMatches;
|
|
3066
3145
|
}
|
|
3067
3146
|
|
|
3068
|
-
function getMatchesToLoad(state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
|
|
3147
|
+
function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
|
|
3069
3148
|
let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined; // Pick navigation matches that are net-new or qualify for revalidation
|
|
3070
3149
|
|
|
3071
3150
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3072
3151
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3073
3152
|
let navigationMatches = boundaryMatches.filter((match, index) => match.route.loader != null && (isNewLoader(state.loaderData, state.matches[index], match) || // If this route had a pending deferred cancelled it must be revalidated
|
|
3074
|
-
cancelledDeferredRoutes.some(id => id === match.route.id) || shouldRevalidateLoader(state.location, state.matches[index], submission, location, match, isRevalidationRequired, actionResult))); // Pick fetcher.loads that need to be revalidated
|
|
3153
|
+
cancelledDeferredRoutes.some(id => id === match.route.id) || shouldRevalidateLoader(history, state.location, state.matches[index], submission, location, match, isRevalidationRequired, actionResult))); // Pick fetcher.loads that need to be revalidated
|
|
3075
3154
|
|
|
3076
3155
|
let revalidatingFetchers = [];
|
|
3077
3156
|
fetchLoadMatches && fetchLoadMatches.forEach((_ref10, key) => {
|
|
@@ -3081,7 +3160,7 @@ function getMatchesToLoad(state, matches, submission, location, isRevalidationRe
|
|
|
3081
3160
|
if (cancelledFetcherLoads.includes(key)) {
|
|
3082
3161
|
revalidatingFetchers.push([key, href, match, fetchMatches]);
|
|
3083
3162
|
} else if (isRevalidationRequired) {
|
|
3084
|
-
let shouldRevalidate = shouldRevalidateLoader(href, match, submission, href, match, isRevalidationRequired, actionResult);
|
|
3163
|
+
let shouldRevalidate = shouldRevalidateLoader(history, href, match, submission, href, match, isRevalidationRequired, actionResult);
|
|
3085
3164
|
|
|
3086
3165
|
if (shouldRevalidate) {
|
|
3087
3166
|
revalidatingFetchers.push([key, href, match, fetchMatches]);
|
|
@@ -3111,10 +3190,10 @@ function isNewRouteInstance(currentMatch, match) {
|
|
|
3111
3190
|
);
|
|
3112
3191
|
}
|
|
3113
3192
|
|
|
3114
|
-
function shouldRevalidateLoader(currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
|
|
3115
|
-
let currentUrl =
|
|
3193
|
+
function shouldRevalidateLoader(history, currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
|
|
3194
|
+
let currentUrl = history.createURL(currentLocation);
|
|
3116
3195
|
let currentParams = currentMatch.params;
|
|
3117
|
-
let nextUrl =
|
|
3196
|
+
let nextUrl = history.createURL(location);
|
|
3118
3197
|
let nextParams = match.params; // This is the default implementation as to when we revalidate. If the route
|
|
3119
3198
|
// provides it's own implementation, then we give them full control but
|
|
3120
3199
|
// provide this value so they can leverage it if needed after they check
|
|
@@ -3191,7 +3270,7 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
3191
3270
|
if (redirectStatusCodes.has(status)) {
|
|
3192
3271
|
let location = result.headers.get("Location");
|
|
3193
3272
|
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
|
|
3194
|
-
let isAbsolute = /^[a-z
|
|
3273
|
+
let isAbsolute = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(location); // Support relative routing in internal redirects
|
|
3195
3274
|
|
|
3196
3275
|
if (!isAbsolute) {
|
|
3197
3276
|
let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
|
|
@@ -3284,8 +3363,8 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
3284
3363
|
// Request instance from the static handler (query/queryRoute)
|
|
3285
3364
|
|
|
3286
3365
|
|
|
3287
|
-
function createClientSideRequest(location, signal, submission) {
|
|
3288
|
-
let url =
|
|
3366
|
+
function createClientSideRequest(history, location, signal, submission) {
|
|
3367
|
+
let url = history.createURL(stripHashFromPath(location)).toString();
|
|
3289
3368
|
let init = {
|
|
3290
3369
|
signal
|
|
3291
3370
|
};
|
|
@@ -3358,13 +3437,16 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
|
|
|
3358
3437
|
if (result.headers) {
|
|
3359
3438
|
loaderHeaders[id] = result.headers;
|
|
3360
3439
|
}
|
|
3361
|
-
} else if (isDeferredResult(result)) {
|
|
3362
|
-
activeDeferreds && activeDeferreds.set(id, result.deferredData);
|
|
3363
|
-
loaderData[id] = result.deferredData.data; // TODO: Add statusCode/headers once we wire up streaming in Remix
|
|
3364
3440
|
} else {
|
|
3365
|
-
|
|
3441
|
+
if (isDeferredResult(result)) {
|
|
3442
|
+
activeDeferreds.set(id, result.deferredData);
|
|
3443
|
+
loaderData[id] = result.deferredData.data;
|
|
3444
|
+
} else {
|
|
3445
|
+
loaderData[id] = result.data;
|
|
3446
|
+
} // Error status codes always override success status codes, but if all
|
|
3366
3447
|
// loaders are successful we take the deepest status code.
|
|
3367
3448
|
|
|
3449
|
+
|
|
3368
3450
|
if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
|
|
3369
3451
|
statusCode = result.statusCode;
|
|
3370
3452
|
}
|
|
@@ -3414,11 +3496,11 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingError,
|
|
|
3414
3496
|
} else if (isRedirectResult(result)) {
|
|
3415
3497
|
// Should never get here, redirects should get processed above, but we
|
|
3416
3498
|
// keep this to type narrow to a success result in the else
|
|
3417
|
-
|
|
3499
|
+
invariant(false, "Unhandled fetcher revalidation redirect");
|
|
3418
3500
|
} else if (isDeferredResult(result)) {
|
|
3419
3501
|
// Should never get here, deferred data should be awaited for fetchers
|
|
3420
3502
|
// in resolveDeferredResults
|
|
3421
|
-
|
|
3503
|
+
invariant(false, "Unhandled fetcher deferred data");
|
|
3422
3504
|
} else {
|
|
3423
3505
|
let doneFetcher = {
|
|
3424
3506
|
state: "idle",
|
|
@@ -3490,7 +3572,8 @@ function getInternalRouterError(status, _temp4) {
|
|
|
3490
3572
|
let {
|
|
3491
3573
|
pathname,
|
|
3492
3574
|
routeId,
|
|
3493
|
-
method
|
|
3575
|
+
method,
|
|
3576
|
+
type
|
|
3494
3577
|
} = _temp4 === void 0 ? {} : _temp4;
|
|
3495
3578
|
let statusText = "Unknown Server Error";
|
|
3496
3579
|
let errorMessage = "Unknown @remix-run/router error";
|
|
@@ -3500,6 +3583,8 @@ function getInternalRouterError(status, _temp4) {
|
|
|
3500
3583
|
|
|
3501
3584
|
if (method && pathname && routeId) {
|
|
3502
3585
|
errorMessage = "You made a " + method + " request to \"" + pathname + "\" but " + ("did not provide a `loader` for route \"" + routeId + "\", ") + "so there is no way to handle the request.";
|
|
3586
|
+
} else if (type === "defer-action") {
|
|
3587
|
+
errorMessage = "defer() is not supported in actions";
|
|
3503
3588
|
} else {
|
|
3504
3589
|
errorMessage = "Cannot submit binary form data using GET";
|
|
3505
3590
|
}
|
|
@@ -3669,5 +3754,5 @@ function getTargetMatch(matches, location) {
|
|
|
3669
3754
|
return pathMatches[pathMatches.length - 1];
|
|
3670
3755
|
} //#endregion
|
|
3671
3756
|
|
|
3672
|
-
export { AbortedDeferredError, Action, ErrorResponse, IDLE_FETCHER, IDLE_NAVIGATION, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename, warning };
|
|
3757
|
+
export { AbortedDeferredError, Action, ErrorResponse, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, invariant, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename, warning };
|
|
3673
3758
|
//# sourceMappingURL=router.js.map
|