@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.cjs.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
|
*
|
|
@@ -96,6 +96,10 @@ function createMemoryHistory(options) {
|
|
|
96
96
|
return location;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
function createHref(to) {
|
|
100
|
+
return typeof to === "string" ? to : createPath(to);
|
|
101
|
+
}
|
|
102
|
+
|
|
99
103
|
let history = {
|
|
100
104
|
get index() {
|
|
101
105
|
return index;
|
|
@@ -109,8 +113,10 @@ function createMemoryHistory(options) {
|
|
|
109
113
|
return getCurrentLocation();
|
|
110
114
|
},
|
|
111
115
|
|
|
112
|
-
createHref
|
|
113
|
-
|
|
116
|
+
createHref,
|
|
117
|
+
|
|
118
|
+
createURL(to) {
|
|
119
|
+
return new URL(createHref(to), "http://localhost");
|
|
114
120
|
},
|
|
115
121
|
|
|
116
122
|
encodeLocation(to) {
|
|
@@ -389,15 +395,6 @@ function parsePath(path) {
|
|
|
389
395
|
|
|
390
396
|
return parsedPath;
|
|
391
397
|
}
|
|
392
|
-
function createClientSideURL(location) {
|
|
393
|
-
// window.location.origin is "null" (the literal string value) in Firefox
|
|
394
|
-
// under certain conditions, notably when serving from a local HTML file
|
|
395
|
-
// See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
|
|
396
|
-
let base = typeof window !== "undefined" && typeof window.location !== "undefined" && window.location.origin !== "null" ? window.location.origin : window.location.href;
|
|
397
|
-
let href = typeof location === "string" ? location : createPath(location);
|
|
398
|
-
invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
|
|
399
|
-
return new URL(href, base);
|
|
400
|
-
}
|
|
401
398
|
|
|
402
399
|
function getUrlBasedHistory(getLocation, createHref, validateLocation, options) {
|
|
403
400
|
if (options === void 0) {
|
|
@@ -462,6 +459,16 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
462
459
|
}
|
|
463
460
|
}
|
|
464
461
|
|
|
462
|
+
function createURL(to) {
|
|
463
|
+
// window.location.origin is "null" (the literal string value) in Firefox
|
|
464
|
+
// under certain conditions, notably when serving from a local HTML file
|
|
465
|
+
// See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
|
|
466
|
+
let base = window.location.origin !== "null" ? window.location.origin : window.location.href;
|
|
467
|
+
let href = typeof to === "string" ? to : createPath(to);
|
|
468
|
+
invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
|
|
469
|
+
return new URL(href, base);
|
|
470
|
+
}
|
|
471
|
+
|
|
465
472
|
let history = {
|
|
466
473
|
get action() {
|
|
467
474
|
return action;
|
|
@@ -488,9 +495,11 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
488
495
|
return createHref(window, to);
|
|
489
496
|
},
|
|
490
497
|
|
|
498
|
+
createURL,
|
|
499
|
+
|
|
491
500
|
encodeLocation(to) {
|
|
492
501
|
// Encode a Location the same way window.location would
|
|
493
|
-
let url =
|
|
502
|
+
let url = createURL(to);
|
|
494
503
|
return {
|
|
495
504
|
pathname: url.pathname,
|
|
496
505
|
search: url.search,
|
|
@@ -807,13 +816,32 @@ function generatePath(originalPath, params) {
|
|
|
807
816
|
path = path.replace(/\*$/, "/*");
|
|
808
817
|
}
|
|
809
818
|
|
|
810
|
-
return path.replace(/^:(\w+)/g, (_, key) => {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
819
|
+
return path.replace(/^:(\w+)(\??)/g, (_, key, optional) => {
|
|
820
|
+
let param = params[key];
|
|
821
|
+
|
|
822
|
+
if (optional === "?") {
|
|
823
|
+
return param == null ? "" : param;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
if (param == null) {
|
|
827
|
+
invariant(false, "Missing \":" + key + "\" param");
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return param;
|
|
831
|
+
}).replace(/\/:(\w+)(\??)/g, (_, key, optional) => {
|
|
832
|
+
let param = params[key];
|
|
833
|
+
|
|
834
|
+
if (optional === "?") {
|
|
835
|
+
return param == null ? "" : "/" + param;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
if (param == null) {
|
|
839
|
+
invariant(false, "Missing \":" + key + "\" param");
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
return "/" + param;
|
|
843
|
+
}) // Remove any optional markers from optional static segments
|
|
844
|
+
.replace(/\?/g, "").replace(/(\/?)\*/, (_, prefix, __, str) => {
|
|
817
845
|
const star = "*";
|
|
818
846
|
|
|
819
847
|
if (params[star] == null) {
|
|
@@ -1160,9 +1188,10 @@ const json = function json(data, init) {
|
|
|
1160
1188
|
};
|
|
1161
1189
|
class AbortedDeferredError extends Error {}
|
|
1162
1190
|
class DeferredData {
|
|
1163
|
-
constructor(data) {
|
|
1164
|
-
this.
|
|
1165
|
-
this.
|
|
1191
|
+
constructor(data, responseInit) {
|
|
1192
|
+
this.pendingKeysSet = new Set();
|
|
1193
|
+
this.subscribers = new Set();
|
|
1194
|
+
this.deferredKeys = [];
|
|
1166
1195
|
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
|
|
1167
1196
|
// cancellation
|
|
1168
1197
|
|
|
@@ -1181,6 +1210,7 @@ class DeferredData {
|
|
|
1181
1210
|
[key]: this.trackPromise(key, value)
|
|
1182
1211
|
});
|
|
1183
1212
|
}, {});
|
|
1213
|
+
this.init = responseInit;
|
|
1184
1214
|
}
|
|
1185
1215
|
|
|
1186
1216
|
trackPromise(key, value) {
|
|
@@ -1188,7 +1218,8 @@ class DeferredData {
|
|
|
1188
1218
|
return value;
|
|
1189
1219
|
}
|
|
1190
1220
|
|
|
1191
|
-
this.
|
|
1221
|
+
this.deferredKeys.push(key);
|
|
1222
|
+
this.pendingKeysSet.add(key); // We store a little wrapper promise that will be extended with
|
|
1192
1223
|
// _data/_error props upon resolve/reject
|
|
1193
1224
|
|
|
1194
1225
|
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
|
|
@@ -1210,39 +1241,41 @@ class DeferredData {
|
|
|
1210
1241
|
return Promise.reject(error);
|
|
1211
1242
|
}
|
|
1212
1243
|
|
|
1213
|
-
this.
|
|
1244
|
+
this.pendingKeysSet.delete(key);
|
|
1214
1245
|
|
|
1215
1246
|
if (this.done) {
|
|
1216
1247
|
// Nothing left to abort!
|
|
1217
1248
|
this.unlistenAbortSignal();
|
|
1218
1249
|
}
|
|
1219
1250
|
|
|
1220
|
-
const subscriber = this.subscriber;
|
|
1221
|
-
|
|
1222
1251
|
if (error) {
|
|
1223
1252
|
Object.defineProperty(promise, "_error", {
|
|
1224
1253
|
get: () => error
|
|
1225
1254
|
});
|
|
1226
|
-
|
|
1255
|
+
this.emit(false, key);
|
|
1227
1256
|
return Promise.reject(error);
|
|
1228
1257
|
}
|
|
1229
1258
|
|
|
1230
1259
|
Object.defineProperty(promise, "_data", {
|
|
1231
1260
|
get: () => data
|
|
1232
1261
|
});
|
|
1233
|
-
|
|
1262
|
+
this.emit(false, key);
|
|
1234
1263
|
return data;
|
|
1235
1264
|
}
|
|
1236
1265
|
|
|
1266
|
+
emit(aborted, settledKey) {
|
|
1267
|
+
this.subscribers.forEach(subscriber => subscriber(aborted, settledKey));
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1237
1270
|
subscribe(fn) {
|
|
1238
|
-
this.
|
|
1271
|
+
this.subscribers.add(fn);
|
|
1272
|
+
return () => this.subscribers.delete(fn);
|
|
1239
1273
|
}
|
|
1240
1274
|
|
|
1241
1275
|
cancel() {
|
|
1242
1276
|
this.controller.abort();
|
|
1243
|
-
this.
|
|
1244
|
-
|
|
1245
|
-
subscriber && subscriber(true);
|
|
1277
|
+
this.pendingKeysSet.forEach((v, k) => this.pendingKeysSet.delete(k));
|
|
1278
|
+
this.emit(true);
|
|
1246
1279
|
}
|
|
1247
1280
|
|
|
1248
1281
|
async resolveData(signal) {
|
|
@@ -1267,7 +1300,7 @@ class DeferredData {
|
|
|
1267
1300
|
}
|
|
1268
1301
|
|
|
1269
1302
|
get done() {
|
|
1270
|
-
return this.
|
|
1303
|
+
return this.pendingKeysSet.size === 0;
|
|
1271
1304
|
}
|
|
1272
1305
|
|
|
1273
1306
|
get unwrappedData() {
|
|
@@ -1280,6 +1313,10 @@ class DeferredData {
|
|
|
1280
1313
|
}, {});
|
|
1281
1314
|
}
|
|
1282
1315
|
|
|
1316
|
+
get pendingKeys() {
|
|
1317
|
+
return Array.from(this.pendingKeysSet);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1283
1320
|
}
|
|
1284
1321
|
|
|
1285
1322
|
function isTrackedPromise(value) {
|
|
@@ -1298,9 +1335,16 @@ function unwrapTrackedPromise(value) {
|
|
|
1298
1335
|
return value._data;
|
|
1299
1336
|
}
|
|
1300
1337
|
|
|
1301
|
-
function defer(data) {
|
|
1302
|
-
|
|
1303
|
-
}
|
|
1338
|
+
const defer = function defer(data, init) {
|
|
1339
|
+
if (init === void 0) {
|
|
1340
|
+
init = {};
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
let responseInit = typeof init === "number" ? {
|
|
1344
|
+
status: init
|
|
1345
|
+
} : init;
|
|
1346
|
+
return new DeferredData(data, responseInit);
|
|
1347
|
+
};
|
|
1304
1348
|
|
|
1305
1349
|
/**
|
|
1306
1350
|
* A redirect response. Sets the status code and the `Location` header.
|
|
@@ -1546,7 +1590,7 @@ function createRouter(init) {
|
|
|
1546
1590
|
|
|
1547
1591
|
|
|
1548
1592
|
function completeNavigation(location, newState) {
|
|
1549
|
-
var _location$state;
|
|
1593
|
+
var _location$state, _location$state2;
|
|
1550
1594
|
|
|
1551
1595
|
// Deduce if we're in a loading/actionReload state:
|
|
1552
1596
|
// - We have committed actionData in the store
|
|
@@ -1572,7 +1616,10 @@ function createRouter(init) {
|
|
|
1572
1616
|
} // Always preserve any existing loaderData from re-used routes
|
|
1573
1617
|
|
|
1574
1618
|
|
|
1575
|
-
let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;
|
|
1619
|
+
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
|
|
1620
|
+
// submission navigations unless they redirect
|
|
1621
|
+
|
|
1622
|
+
let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true;
|
|
1576
1623
|
updateState(_extends({}, newState, {
|
|
1577
1624
|
// matches, errors, fetchers go through as-is
|
|
1578
1625
|
actionData,
|
|
@@ -1582,9 +1629,8 @@ function createRouter(init) {
|
|
|
1582
1629
|
initialized: true,
|
|
1583
1630
|
navigation: IDLE_NAVIGATION,
|
|
1584
1631
|
revalidation: "idle",
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
preventScrollReset: pendingPreventScrollReset
|
|
1632
|
+
restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),
|
|
1633
|
+
preventScrollReset
|
|
1588
1634
|
}));
|
|
1589
1635
|
|
|
1590
1636
|
if (isUninterruptedRevalidation) ; else if (pendingAction === exports.Action.Pop) ; else if (pendingAction === exports.Action.Push) {
|
|
@@ -1726,7 +1772,7 @@ function createRouter(init) {
|
|
|
1726
1772
|
|
|
1727
1773
|
|
|
1728
1774
|
pendingNavigationController = new AbortController();
|
|
1729
|
-
let request = createClientSideRequest(location, pendingNavigationController.signal, opts && opts.submission);
|
|
1775
|
+
let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
|
|
1730
1776
|
let pendingActionData;
|
|
1731
1777
|
let pendingError;
|
|
1732
1778
|
|
|
@@ -1867,7 +1913,9 @@ function createRouter(init) {
|
|
|
1867
1913
|
}
|
|
1868
1914
|
|
|
1869
1915
|
if (isDeferredResult(result)) {
|
|
1870
|
-
throw
|
|
1916
|
+
throw getInternalRouterError(400, {
|
|
1917
|
+
type: "defer-action"
|
|
1918
|
+
});
|
|
1871
1919
|
}
|
|
1872
1920
|
|
|
1873
1921
|
return {
|
|
@@ -1904,7 +1952,7 @@ function createRouter(init) {
|
|
|
1904
1952
|
formData: loadingNavigation.formData,
|
|
1905
1953
|
formEncType: loadingNavigation.formEncType
|
|
1906
1954
|
} : undefined;
|
|
1907
|
-
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
|
|
1955
|
+
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
|
|
1908
1956
|
// about to reload. Note that if this is an action reload we would have
|
|
1909
1957
|
// already cancelled all pending deferreds so this would be a no-op
|
|
1910
1958
|
|
|
@@ -2086,7 +2134,7 @@ function createRouter(init) {
|
|
|
2086
2134
|
}); // Call the action for the fetcher
|
|
2087
2135
|
|
|
2088
2136
|
let abortController = new AbortController();
|
|
2089
|
-
let fetchRequest = createClientSideRequest(path, abortController.signal, submission);
|
|
2137
|
+
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
|
|
2090
2138
|
fetchControllers.set(key, abortController);
|
|
2091
2139
|
let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
|
|
2092
2140
|
|
|
@@ -2127,13 +2175,15 @@ function createRouter(init) {
|
|
|
2127
2175
|
}
|
|
2128
2176
|
|
|
2129
2177
|
if (isDeferredResult(actionResult)) {
|
|
2130
|
-
|
|
2178
|
+
throw getInternalRouterError(400, {
|
|
2179
|
+
type: "defer-action"
|
|
2180
|
+
});
|
|
2131
2181
|
} // Start the data load for current matches, or the next location if we're
|
|
2132
2182
|
// in the middle of a navigation
|
|
2133
2183
|
|
|
2134
2184
|
|
|
2135
2185
|
let nextLocation = state.navigation.location || state.location;
|
|
2136
|
-
let revalidationRequest = createClientSideRequest(nextLocation, abortController.signal);
|
|
2186
|
+
let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
|
|
2137
2187
|
let matches = state.navigation.state !== "idle" ? matchRoutes(dataRoutes, state.navigation.location, init.basename) : state.matches;
|
|
2138
2188
|
invariant(matches, "Didn't find any matches after fetcher action");
|
|
2139
2189
|
let loadId = ++incrementingLoadId;
|
|
@@ -2147,7 +2197,7 @@ function createRouter(init) {
|
|
|
2147
2197
|
});
|
|
2148
2198
|
|
|
2149
2199
|
state.fetchers.set(key, loadFetcher);
|
|
2150
|
-
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
|
|
2200
|
+
let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
|
|
2151
2201
|
[match.route.id]: actionResult.data
|
|
2152
2202
|
}, undefined, // No need to send through errors since we short circuit above
|
|
2153
2203
|
fetchLoadMatches); // Put all revalidating fetchers into the loading state, except for the
|
|
@@ -2260,9 +2310,9 @@ function createRouter(init) {
|
|
|
2260
2310
|
}); // Call the loader for this fetcher route match
|
|
2261
2311
|
|
|
2262
2312
|
let abortController = new AbortController();
|
|
2263
|
-
let fetchRequest = createClientSideRequest(path, abortController.signal);
|
|
2313
|
+
let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
|
|
2264
2314
|
fetchControllers.set(key, abortController);
|
|
2265
|
-
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported
|
|
2315
|
+
let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
|
|
2266
2316
|
// as a normal load. resolveDeferredData will return undefined if this
|
|
2267
2317
|
// fetcher gets aborted, so we just leave result untouched and short circuit
|
|
2268
2318
|
// below if that happens
|
|
@@ -2362,7 +2412,7 @@ function createRouter(init) {
|
|
|
2362
2412
|
invariant(redirectLocation, "Expected a location on the redirect navigation"); // Check if this an external redirect that goes to a new origin
|
|
2363
2413
|
|
|
2364
2414
|
if (typeof ((_window = window) == null ? void 0 : _window.location) !== "undefined") {
|
|
2365
|
-
let newOrigin =
|
|
2415
|
+
let newOrigin = init.history.createURL(redirect.location).origin;
|
|
2366
2416
|
|
|
2367
2417
|
if (window.location.origin !== newOrigin) {
|
|
2368
2418
|
if (replace) {
|
|
@@ -2404,7 +2454,9 @@ function createRouter(init) {
|
|
|
2404
2454
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2405
2455
|
submission: _extends({}, submission, {
|
|
2406
2456
|
formAction: redirect.location
|
|
2407
|
-
})
|
|
2457
|
+
}),
|
|
2458
|
+
// Preserve this flag across redirects
|
|
2459
|
+
preventScrollReset: pendingPreventScrollReset
|
|
2408
2460
|
});
|
|
2409
2461
|
} else {
|
|
2410
2462
|
// Otherwise, we kick off a new loading navigation, preserving the
|
|
@@ -2417,7 +2469,9 @@ function createRouter(init) {
|
|
|
2417
2469
|
formAction: submission ? submission.formAction : undefined,
|
|
2418
2470
|
formEncType: submission ? submission.formEncType : undefined,
|
|
2419
2471
|
formData: submission ? submission.formData : undefined
|
|
2420
|
-
}
|
|
2472
|
+
},
|
|
2473
|
+
// Preserve this flag across redirects
|
|
2474
|
+
preventScrollReset: pendingPreventScrollReset
|
|
2421
2475
|
});
|
|
2422
2476
|
}
|
|
2423
2477
|
}
|
|
@@ -2428,7 +2482,7 @@ function createRouter(init) {
|
|
|
2428
2482
|
// accordingly
|
|
2429
2483
|
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(_ref8 => {
|
|
2430
2484
|
let [, href, match, fetchMatches] = _ref8;
|
|
2431
|
-
return callLoaderOrAction("loader", createClientSideRequest(href, request.signal), match, fetchMatches, router.basename);
|
|
2485
|
+
return callLoaderOrAction("loader", createClientSideRequest(init.history, href, request.signal), match, fetchMatches, router.basename);
|
|
2432
2486
|
})]);
|
|
2433
2487
|
let loaderResults = results.slice(0, matchesToLoad.length);
|
|
2434
2488
|
let fetcherResults = results.slice(matchesToLoad.length);
|
|
@@ -2637,6 +2691,7 @@ function createRouter(init) {
|
|
|
2637
2691
|
//#region createStaticHandler
|
|
2638
2692
|
////////////////////////////////////////////////////////////////////////////////
|
|
2639
2693
|
|
|
2694
|
+
const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
|
|
2640
2695
|
function createStaticHandler(routes, opts) {
|
|
2641
2696
|
invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
|
|
2642
2697
|
let dataRoutes = convertRoutesToDataRoutes(routes);
|
|
@@ -2689,7 +2744,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2689
2744
|
},
|
|
2690
2745
|
statusCode: error.status,
|
|
2691
2746
|
loaderHeaders: {},
|
|
2692
|
-
actionHeaders: {}
|
|
2747
|
+
actionHeaders: {},
|
|
2748
|
+
activeDeferreds: null
|
|
2693
2749
|
};
|
|
2694
2750
|
} else if (!matches) {
|
|
2695
2751
|
let error = getInternalRouterError(404, {
|
|
@@ -2710,7 +2766,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2710
2766
|
},
|
|
2711
2767
|
statusCode: error.status,
|
|
2712
2768
|
loaderHeaders: {},
|
|
2713
|
-
actionHeaders: {}
|
|
2769
|
+
actionHeaders: {},
|
|
2770
|
+
activeDeferreds: null
|
|
2714
2771
|
};
|
|
2715
2772
|
}
|
|
2716
2773
|
|
|
@@ -2801,8 +2858,23 @@ function createStaticHandler(routes, opts) {
|
|
|
2801
2858
|
} // Pick off the right state value to return
|
|
2802
2859
|
|
|
2803
2860
|
|
|
2804
|
-
|
|
2805
|
-
|
|
2861
|
+
if (result.actionData) {
|
|
2862
|
+
return Object.values(result.actionData)[0];
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
if (result.loaderData) {
|
|
2866
|
+
var _result$activeDeferre;
|
|
2867
|
+
|
|
2868
|
+
let data = Object.values(result.loaderData)[0];
|
|
2869
|
+
|
|
2870
|
+
if ((_result$activeDeferre = result.activeDeferreds) != null && _result$activeDeferre[match.route.id]) {
|
|
2871
|
+
data[UNSAFE_DEFERRED_SYMBOL] = result.activeDeferreds[match.route.id];
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
return data;
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
return undefined;
|
|
2806
2878
|
}
|
|
2807
2879
|
|
|
2808
2880
|
async function queryImpl(request, location, matches, requestContext, routeMatch) {
|
|
@@ -2882,7 +2954,18 @@ function createStaticHandler(routes, opts) {
|
|
|
2882
2954
|
}
|
|
2883
2955
|
|
|
2884
2956
|
if (isDeferredResult(result)) {
|
|
2885
|
-
|
|
2957
|
+
let error = getInternalRouterError(400, {
|
|
2958
|
+
type: "defer-action"
|
|
2959
|
+
});
|
|
2960
|
+
|
|
2961
|
+
if (isRouteRequest) {
|
|
2962
|
+
throw error;
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
result = {
|
|
2966
|
+
type: ResultType.error,
|
|
2967
|
+
error
|
|
2968
|
+
};
|
|
2886
2969
|
}
|
|
2887
2970
|
|
|
2888
2971
|
if (isRouteRequest) {
|
|
@@ -2903,7 +2986,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2903
2986
|
// return the raw Response or value
|
|
2904
2987
|
statusCode: 200,
|
|
2905
2988
|
loaderHeaders: {},
|
|
2906
|
-
actionHeaders: {}
|
|
2989
|
+
actionHeaders: {},
|
|
2990
|
+
activeDeferreds: null
|
|
2907
2991
|
};
|
|
2908
2992
|
}
|
|
2909
2993
|
|
|
@@ -2966,7 +3050,8 @@ function createStaticHandler(routes, opts) {
|
|
|
2966
3050
|
}), {}),
|
|
2967
3051
|
errors: pendingActionError || null,
|
|
2968
3052
|
statusCode: 200,
|
|
2969
|
-
loaderHeaders: {}
|
|
3053
|
+
loaderHeaders: {},
|
|
3054
|
+
activeDeferreds: null
|
|
2970
3055
|
};
|
|
2971
3056
|
}
|
|
2972
3057
|
|
|
@@ -2975,27 +3060,21 @@ function createStaticHandler(routes, opts) {
|
|
|
2975
3060
|
if (request.signal.aborted) {
|
|
2976
3061
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
2977
3062
|
throw new Error(method + "() call aborted");
|
|
2978
|
-
}
|
|
2979
|
-
|
|
2980
|
-
let executedLoaders = new Set();
|
|
2981
|
-
results.forEach((result, i) => {
|
|
2982
|
-
executedLoaders.add(matchesToLoad[i].route.id); // Can't do anything with these without the Remix side of things, so just
|
|
2983
|
-
// cancel them for now
|
|
3063
|
+
} // Process and commit output from loaders
|
|
2984
3064
|
|
|
2985
|
-
if (isDeferredResult(result)) {
|
|
2986
|
-
result.deferredData.cancel();
|
|
2987
|
-
}
|
|
2988
|
-
}); // Process and commit output from loaders
|
|
2989
3065
|
|
|
2990
|
-
let
|
|
3066
|
+
let activeDeferreds = new Map();
|
|
3067
|
+
let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError, activeDeferreds); // Add a null for any non-loader matches for proper revalidation on the client
|
|
2991
3068
|
|
|
3069
|
+
let executedLoaders = new Set(matchesToLoad.map(match => match.route.id));
|
|
2992
3070
|
matches.forEach(match => {
|
|
2993
3071
|
if (!executedLoaders.has(match.route.id)) {
|
|
2994
3072
|
context.loaderData[match.route.id] = null;
|
|
2995
3073
|
}
|
|
2996
3074
|
});
|
|
2997
3075
|
return _extends({}, context, {
|
|
2998
|
-
matches
|
|
3076
|
+
matches,
|
|
3077
|
+
activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null
|
|
2999
3078
|
});
|
|
3000
3079
|
}
|
|
3001
3080
|
|
|
@@ -3114,13 +3193,13 @@ function getLoaderMatchesUntilBoundary(matches, boundaryId) {
|
|
|
3114
3193
|
return boundaryMatches;
|
|
3115
3194
|
}
|
|
3116
3195
|
|
|
3117
|
-
function getMatchesToLoad(state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
|
|
3196
|
+
function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
|
|
3118
3197
|
let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined; // Pick navigation matches that are net-new or qualify for revalidation
|
|
3119
3198
|
|
|
3120
3199
|
let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
|
|
3121
3200
|
let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
|
|
3122
3201
|
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
|
|
3123
|
-
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
|
|
3202
|
+
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
|
|
3124
3203
|
|
|
3125
3204
|
let revalidatingFetchers = [];
|
|
3126
3205
|
fetchLoadMatches && fetchLoadMatches.forEach((_ref10, key) => {
|
|
@@ -3130,7 +3209,7 @@ function getMatchesToLoad(state, matches, submission, location, isRevalidationRe
|
|
|
3130
3209
|
if (cancelledFetcherLoads.includes(key)) {
|
|
3131
3210
|
revalidatingFetchers.push([key, href, match, fetchMatches]);
|
|
3132
3211
|
} else if (isRevalidationRequired) {
|
|
3133
|
-
let shouldRevalidate = shouldRevalidateLoader(href, match, submission, href, match, isRevalidationRequired, actionResult);
|
|
3212
|
+
let shouldRevalidate = shouldRevalidateLoader(history, href, match, submission, href, match, isRevalidationRequired, actionResult);
|
|
3134
3213
|
|
|
3135
3214
|
if (shouldRevalidate) {
|
|
3136
3215
|
revalidatingFetchers.push([key, href, match, fetchMatches]);
|
|
@@ -3160,10 +3239,10 @@ function isNewRouteInstance(currentMatch, match) {
|
|
|
3160
3239
|
);
|
|
3161
3240
|
}
|
|
3162
3241
|
|
|
3163
|
-
function shouldRevalidateLoader(currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
|
|
3164
|
-
let currentUrl =
|
|
3242
|
+
function shouldRevalidateLoader(history, currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
|
|
3243
|
+
let currentUrl = history.createURL(currentLocation);
|
|
3165
3244
|
let currentParams = currentMatch.params;
|
|
3166
|
-
let nextUrl =
|
|
3245
|
+
let nextUrl = history.createURL(location);
|
|
3167
3246
|
let nextParams = match.params; // This is the default implementation as to when we revalidate. If the route
|
|
3168
3247
|
// provides it's own implementation, then we give them full control but
|
|
3169
3248
|
// provide this value so they can leverage it if needed after they check
|
|
@@ -3240,7 +3319,7 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
3240
3319
|
if (redirectStatusCodes.has(status)) {
|
|
3241
3320
|
let location = result.headers.get("Location");
|
|
3242
3321
|
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
|
|
3243
|
-
let isAbsolute = /^[a-z
|
|
3322
|
+
let isAbsolute = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(location); // Support relative routing in internal redirects
|
|
3244
3323
|
|
|
3245
3324
|
if (!isAbsolute) {
|
|
3246
3325
|
let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
|
|
@@ -3333,8 +3412,8 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
3333
3412
|
// Request instance from the static handler (query/queryRoute)
|
|
3334
3413
|
|
|
3335
3414
|
|
|
3336
|
-
function createClientSideRequest(location, signal, submission) {
|
|
3337
|
-
let url =
|
|
3415
|
+
function createClientSideRequest(history, location, signal, submission) {
|
|
3416
|
+
let url = history.createURL(stripHashFromPath(location)).toString();
|
|
3338
3417
|
let init = {
|
|
3339
3418
|
signal
|
|
3340
3419
|
};
|
|
@@ -3407,13 +3486,16 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
|
|
|
3407
3486
|
if (result.headers) {
|
|
3408
3487
|
loaderHeaders[id] = result.headers;
|
|
3409
3488
|
}
|
|
3410
|
-
} else if (isDeferredResult(result)) {
|
|
3411
|
-
activeDeferreds && activeDeferreds.set(id, result.deferredData);
|
|
3412
|
-
loaderData[id] = result.deferredData.data; // TODO: Add statusCode/headers once we wire up streaming in Remix
|
|
3413
3489
|
} else {
|
|
3414
|
-
|
|
3490
|
+
if (isDeferredResult(result)) {
|
|
3491
|
+
activeDeferreds.set(id, result.deferredData);
|
|
3492
|
+
loaderData[id] = result.deferredData.data;
|
|
3493
|
+
} else {
|
|
3494
|
+
loaderData[id] = result.data;
|
|
3495
|
+
} // Error status codes always override success status codes, but if all
|
|
3415
3496
|
// loaders are successful we take the deepest status code.
|
|
3416
3497
|
|
|
3498
|
+
|
|
3417
3499
|
if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
|
|
3418
3500
|
statusCode = result.statusCode;
|
|
3419
3501
|
}
|
|
@@ -3463,11 +3545,11 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingError,
|
|
|
3463
3545
|
} else if (isRedirectResult(result)) {
|
|
3464
3546
|
// Should never get here, redirects should get processed above, but we
|
|
3465
3547
|
// keep this to type narrow to a success result in the else
|
|
3466
|
-
|
|
3548
|
+
invariant(false, "Unhandled fetcher revalidation redirect");
|
|
3467
3549
|
} else if (isDeferredResult(result)) {
|
|
3468
3550
|
// Should never get here, deferred data should be awaited for fetchers
|
|
3469
3551
|
// in resolveDeferredResults
|
|
3470
|
-
|
|
3552
|
+
invariant(false, "Unhandled fetcher deferred data");
|
|
3471
3553
|
} else {
|
|
3472
3554
|
let doneFetcher = {
|
|
3473
3555
|
state: "idle",
|
|
@@ -3539,7 +3621,8 @@ function getInternalRouterError(status, _temp4) {
|
|
|
3539
3621
|
let {
|
|
3540
3622
|
pathname,
|
|
3541
3623
|
routeId,
|
|
3542
|
-
method
|
|
3624
|
+
method,
|
|
3625
|
+
type
|
|
3543
3626
|
} = _temp4 === void 0 ? {} : _temp4;
|
|
3544
3627
|
let statusText = "Unknown Server Error";
|
|
3545
3628
|
let errorMessage = "Unknown @remix-run/router error";
|
|
@@ -3549,6 +3632,8 @@ function getInternalRouterError(status, _temp4) {
|
|
|
3549
3632
|
|
|
3550
3633
|
if (method && pathname && routeId) {
|
|
3551
3634
|
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.";
|
|
3635
|
+
} else if (type === "defer-action") {
|
|
3636
|
+
errorMessage = "defer() is not supported in actions";
|
|
3552
3637
|
} else {
|
|
3553
3638
|
errorMessage = "Cannot submit binary form data using GET";
|
|
3554
3639
|
}
|
|
@@ -3722,6 +3807,8 @@ exports.AbortedDeferredError = AbortedDeferredError;
|
|
|
3722
3807
|
exports.ErrorResponse = ErrorResponse;
|
|
3723
3808
|
exports.IDLE_FETCHER = IDLE_FETCHER;
|
|
3724
3809
|
exports.IDLE_NAVIGATION = IDLE_NAVIGATION;
|
|
3810
|
+
exports.UNSAFE_DEFERRED_SYMBOL = UNSAFE_DEFERRED_SYMBOL;
|
|
3811
|
+
exports.UNSAFE_DeferredData = DeferredData;
|
|
3725
3812
|
exports.UNSAFE_convertRoutesToDataRoutes = convertRoutesToDataRoutes;
|
|
3726
3813
|
exports.UNSAFE_getPathContributingMatches = getPathContributingMatches;
|
|
3727
3814
|
exports.createBrowserHistory = createBrowserHistory;
|