@remix-run/router 1.0.3 → 1.0.4-pre.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 +10 -0
- package/dist/history.d.ts +3 -3
- package/dist/router.cjs.js +257 -166
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +14 -2
- package/dist/router.js +257 -166
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +257 -166
- 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 +7 -3
- package/history.ts +12 -8
- package/package.json +1 -1
- package/router.ts +288 -183
- package/utils.ts +21 -5
package/dist/router.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.0.
|
|
2
|
+
* @remix-run/router v1.0.4-pre.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -115,8 +115,13 @@
|
|
|
115
115
|
return typeof to === "string" ? to : createPath(to);
|
|
116
116
|
},
|
|
117
117
|
|
|
118
|
-
encodeLocation(
|
|
119
|
-
|
|
118
|
+
encodeLocation(to) {
|
|
119
|
+
let path = typeof to === "string" ? parsePath(to) : to;
|
|
120
|
+
return {
|
|
121
|
+
pathname: path.pathname || "",
|
|
122
|
+
search: path.search || "",
|
|
123
|
+
hash: path.hash || ""
|
|
124
|
+
};
|
|
120
125
|
},
|
|
121
126
|
|
|
122
127
|
push(to, state) {
|
|
@@ -474,14 +479,14 @@
|
|
|
474
479
|
return createHref(window, to);
|
|
475
480
|
},
|
|
476
481
|
|
|
477
|
-
encodeLocation(
|
|
482
|
+
encodeLocation(to) {
|
|
478
483
|
// Encode a Location the same way window.location would
|
|
479
|
-
let url = createURL(createPath(
|
|
480
|
-
return
|
|
484
|
+
let url = createURL(typeof to === "string" ? to : createPath(to));
|
|
485
|
+
return {
|
|
481
486
|
pathname: url.pathname,
|
|
482
487
|
search: url.search,
|
|
483
488
|
hash: url.hash
|
|
484
|
-
}
|
|
489
|
+
};
|
|
485
490
|
},
|
|
486
491
|
|
|
487
492
|
push,
|
|
@@ -1256,10 +1261,21 @@
|
|
|
1256
1261
|
*/
|
|
1257
1262
|
|
|
1258
1263
|
class ErrorResponse {
|
|
1259
|
-
constructor(status, statusText, data) {
|
|
1264
|
+
constructor(status, statusText, data, internal) {
|
|
1265
|
+
if (internal === void 0) {
|
|
1266
|
+
internal = false;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1260
1269
|
this.status = status;
|
|
1261
1270
|
this.statusText = statusText || "";
|
|
1262
|
-
this.
|
|
1271
|
+
this.internal = internal;
|
|
1272
|
+
|
|
1273
|
+
if (data instanceof Error) {
|
|
1274
|
+
this.data = data.toString();
|
|
1275
|
+
this.error = data;
|
|
1276
|
+
} else {
|
|
1277
|
+
this.data = data;
|
|
1278
|
+
}
|
|
1263
1279
|
}
|
|
1264
1280
|
|
|
1265
1281
|
}
|
|
@@ -1279,6 +1295,12 @@
|
|
|
1279
1295
|
* A Router instance manages all navigation and data loading/mutations
|
|
1280
1296
|
*/
|
|
1281
1297
|
|
|
1298
|
+
const validActionMethodsArr = ["post", "put", "patch", "delete"];
|
|
1299
|
+
const validActionMethods = new Set(validActionMethodsArr);
|
|
1300
|
+
const validRequestMethodsArr = ["get", ...validActionMethodsArr];
|
|
1301
|
+
const validRequestMethods = new Set(validRequestMethodsArr);
|
|
1302
|
+
const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
|
|
1303
|
+
const redirectPreserveMethodStatusCodes = new Set([307, 308]);
|
|
1282
1304
|
const IDLE_NAVIGATION = {
|
|
1283
1305
|
state: "idle",
|
|
1284
1306
|
location: undefined,
|
|
@@ -1329,11 +1351,13 @@
|
|
|
1329
1351
|
if (initialMatches == null) {
|
|
1330
1352
|
// If we do not match a user-provided-route, fall back to the root
|
|
1331
1353
|
// to allow the error boundary to take over
|
|
1354
|
+
let error = getInternalRouterError(404, {
|
|
1355
|
+
pathname: init.history.location.pathname
|
|
1356
|
+
});
|
|
1332
1357
|
let {
|
|
1333
1358
|
matches,
|
|
1334
|
-
route
|
|
1335
|
-
|
|
1336
|
-
} = getNotFoundMatches(dataRoutes);
|
|
1359
|
+
route
|
|
1360
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
1337
1361
|
initialMatches = matches;
|
|
1338
1362
|
initialErrors = {
|
|
1339
1363
|
[route.id]: error
|
|
@@ -1509,7 +1533,7 @@
|
|
|
1509
1533
|
// the same encoding we'd get from a history.pushState/window.location read
|
|
1510
1534
|
// without having to touch history
|
|
1511
1535
|
|
|
1512
|
-
location = init.history.encodeLocation(location);
|
|
1536
|
+
location = _extends({}, location, init.history.encodeLocation(location));
|
|
1513
1537
|
let historyAction = (opts && opts.replace) === true || submission != null ? exports.Action.Replace : exports.Action.Push;
|
|
1514
1538
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
1515
1539
|
return await startNavigation(historyAction, location, {
|
|
@@ -1573,11 +1597,13 @@
|
|
|
1573
1597
|
let matches = matchRoutes(dataRoutes, location, init.basename); // Short circuit with a 404 on the root error boundary if we match nothing
|
|
1574
1598
|
|
|
1575
1599
|
if (!matches) {
|
|
1600
|
+
let error = getInternalRouterError(404, {
|
|
1601
|
+
pathname: location.pathname
|
|
1602
|
+
});
|
|
1576
1603
|
let {
|
|
1577
1604
|
matches: notFoundMatches,
|
|
1578
|
-
route
|
|
1579
|
-
|
|
1580
|
-
} = getNotFoundMatches(dataRoutes); // Cancel all pending deferred on 404s since we don't keep any routes
|
|
1605
|
+
route
|
|
1606
|
+
} = getShortCircuitMatches(dataRoutes); // Cancel all pending deferred on 404s since we don't keep any routes
|
|
1581
1607
|
|
|
1582
1608
|
cancelActiveDeferreds();
|
|
1583
1609
|
completeNavigation(location, {
|
|
@@ -1673,7 +1699,14 @@
|
|
|
1673
1699
|
let actionMatch = getTargetMatch(matches, location);
|
|
1674
1700
|
|
|
1675
1701
|
if (!actionMatch.route.action) {
|
|
1676
|
-
result =
|
|
1702
|
+
result = {
|
|
1703
|
+
type: ResultType.error,
|
|
1704
|
+
error: getInternalRouterError(405, {
|
|
1705
|
+
method: request.method,
|
|
1706
|
+
pathname: location.pathname,
|
|
1707
|
+
routeId: actionMatch.route.id
|
|
1708
|
+
})
|
|
1709
|
+
};
|
|
1677
1710
|
} else {
|
|
1678
1711
|
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
1679
1712
|
|
|
@@ -1685,12 +1718,7 @@
|
|
|
1685
1718
|
}
|
|
1686
1719
|
|
|
1687
1720
|
if (isRedirectResult(result)) {
|
|
1688
|
-
|
|
1689
|
-
state: "loading",
|
|
1690
|
-
location: createLocation(state.location, result.location)
|
|
1691
|
-
}, submission);
|
|
1692
|
-
|
|
1693
|
-
await startRedirectNavigation(result, redirectNavigation, opts && opts.replace);
|
|
1721
|
+
await startRedirectNavigation(state, result, opts && opts.replace === true);
|
|
1694
1722
|
return {
|
|
1695
1723
|
shortCircuited: true
|
|
1696
1724
|
};
|
|
@@ -1817,8 +1845,7 @@
|
|
|
1817
1845
|
let redirect = findRedirect(results);
|
|
1818
1846
|
|
|
1819
1847
|
if (redirect) {
|
|
1820
|
-
|
|
1821
|
-
await startRedirectNavigation(redirect, redirectNavigation, replace);
|
|
1848
|
+
await startRedirectNavigation(state, redirect, replace);
|
|
1822
1849
|
return {
|
|
1823
1850
|
shortCircuited: true
|
|
1824
1851
|
};
|
|
@@ -1864,7 +1891,9 @@
|
|
|
1864
1891
|
let matches = matchRoutes(dataRoutes, href, init.basename);
|
|
1865
1892
|
|
|
1866
1893
|
if (!matches) {
|
|
1867
|
-
setFetcherError(key, routeId,
|
|
1894
|
+
setFetcherError(key, routeId, getInternalRouterError(404, {
|
|
1895
|
+
pathname: href
|
|
1896
|
+
}));
|
|
1868
1897
|
return;
|
|
1869
1898
|
}
|
|
1870
1899
|
|
|
@@ -1892,9 +1921,11 @@
|
|
|
1892
1921
|
fetchLoadMatches.delete(key);
|
|
1893
1922
|
|
|
1894
1923
|
if (!match.route.action) {
|
|
1895
|
-
let {
|
|
1896
|
-
|
|
1897
|
-
|
|
1924
|
+
let error = getInternalRouterError(405, {
|
|
1925
|
+
method: submission.formMethod,
|
|
1926
|
+
pathname: path,
|
|
1927
|
+
routeId: routeId
|
|
1928
|
+
});
|
|
1898
1929
|
setFetcherError(key, routeId, error);
|
|
1899
1930
|
return;
|
|
1900
1931
|
} // Put this fetcher into it's submitting state
|
|
@@ -1942,14 +1973,7 @@
|
|
|
1942
1973
|
updateState({
|
|
1943
1974
|
fetchers: new Map(state.fetchers)
|
|
1944
1975
|
});
|
|
1945
|
-
|
|
1946
|
-
let redirectNavigation = _extends({
|
|
1947
|
-
state: "loading",
|
|
1948
|
-
location: createLocation(state.location, actionResult.location)
|
|
1949
|
-
}, submission);
|
|
1950
|
-
|
|
1951
|
-
await startRedirectNavigation(actionResult, redirectNavigation);
|
|
1952
|
-
return;
|
|
1976
|
+
return startRedirectNavigation(state, actionResult);
|
|
1953
1977
|
} // Process any non-redirect errors thrown
|
|
1954
1978
|
|
|
1955
1979
|
|
|
@@ -2023,9 +2047,7 @@
|
|
|
2023
2047
|
let redirect = findRedirect(results);
|
|
2024
2048
|
|
|
2025
2049
|
if (redirect) {
|
|
2026
|
-
|
|
2027
|
-
await startRedirectNavigation(redirect, redirectNavigation);
|
|
2028
|
-
return;
|
|
2050
|
+
return startRedirectNavigation(state, redirect);
|
|
2029
2051
|
} // Process and commit output from loaders
|
|
2030
2052
|
|
|
2031
2053
|
|
|
@@ -2110,8 +2132,7 @@
|
|
|
2110
2132
|
|
|
2111
2133
|
|
|
2112
2134
|
if (isRedirectResult(result)) {
|
|
2113
|
-
|
|
2114
|
-
await startRedirectNavigation(result, redirectNavigation);
|
|
2135
|
+
await startRedirectNavigation(state, result);
|
|
2115
2136
|
return;
|
|
2116
2137
|
} // Process any non-redirect errors thrown
|
|
2117
2138
|
|
|
@@ -2167,19 +2188,55 @@
|
|
|
2167
2188
|
*/
|
|
2168
2189
|
|
|
2169
2190
|
|
|
2170
|
-
async function startRedirectNavigation(
|
|
2191
|
+
async function startRedirectNavigation(state, redirect, replace) {
|
|
2171
2192
|
if (redirect.revalidate) {
|
|
2172
2193
|
isRevalidationRequired = true;
|
|
2173
2194
|
}
|
|
2174
2195
|
|
|
2175
|
-
|
|
2196
|
+
let redirectLocation = createLocation(state.location, redirect.location);
|
|
2197
|
+
invariant(redirectLocation, "Expected a location on the redirect navigation");
|
|
2198
|
+
|
|
2199
|
+
if (redirect.external && typeof window !== "undefined" && typeof window.location !== "undefined") {
|
|
2200
|
+
window.location.replace(redirect.location);
|
|
2201
|
+
return;
|
|
2202
|
+
} // There's no need to abort on redirects, since we don't detect the
|
|
2176
2203
|
// redirect until the action/loaders have settled
|
|
2177
2204
|
|
|
2205
|
+
|
|
2178
2206
|
pendingNavigationController = null;
|
|
2179
2207
|
let redirectHistoryAction = replace === true ? exports.Action.Replace : exports.Action.Push;
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2208
|
+
let {
|
|
2209
|
+
formMethod,
|
|
2210
|
+
formAction,
|
|
2211
|
+
formEncType,
|
|
2212
|
+
formData
|
|
2213
|
+
} = state.navigation; // If this was a 307/308 submission we want to preserve the HTTP method and
|
|
2214
|
+
// re-submit the POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2215
|
+
// redirected location
|
|
2216
|
+
|
|
2217
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod && isSubmissionMethod(formMethod) && formEncType && formData) {
|
|
2218
|
+
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2219
|
+
submission: {
|
|
2220
|
+
formMethod,
|
|
2221
|
+
formAction: redirect.location,
|
|
2222
|
+
formEncType,
|
|
2223
|
+
formData
|
|
2224
|
+
}
|
|
2225
|
+
});
|
|
2226
|
+
} else {
|
|
2227
|
+
// Otherwise, we kick off a new loading navigation, preserving the
|
|
2228
|
+
// submission info for the duration of this navigation
|
|
2229
|
+
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2230
|
+
overrideNavigation: {
|
|
2231
|
+
state: "loading",
|
|
2232
|
+
location: redirectLocation,
|
|
2233
|
+
formMethod: formMethod || undefined,
|
|
2234
|
+
formAction: formAction || undefined,
|
|
2235
|
+
formEncType: formEncType || undefined,
|
|
2236
|
+
formData: formData || undefined
|
|
2237
|
+
}
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2183
2240
|
}
|
|
2184
2241
|
|
|
2185
2242
|
async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
|
|
@@ -2383,6 +2440,7 @@
|
|
|
2383
2440
|
// Passthrough to history-aware createHref used by useHref so we get proper
|
|
2384
2441
|
// hash-aware URLs in DOM paths
|
|
2385
2442
|
createHref: to => init.history.createHref(to),
|
|
2443
|
+
encodeLocation: to => init.history.encodeLocation(to),
|
|
2386
2444
|
getFetcher,
|
|
2387
2445
|
deleteFetcher,
|
|
2388
2446
|
dispose,
|
|
@@ -2395,11 +2453,10 @@
|
|
|
2395
2453
|
//#region createStaticHandler
|
|
2396
2454
|
////////////////////////////////////////////////////////////////////////////////
|
|
2397
2455
|
|
|
2398
|
-
|
|
2399
|
-
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);
|
|
2400
|
-
function unstable_createStaticHandler(routes) {
|
|
2456
|
+
function unstable_createStaticHandler(routes, opts) {
|
|
2401
2457
|
invariant(routes.length > 0, "You must provide a non-empty routes array to unstable_createStaticHandler");
|
|
2402
2458
|
let dataRoutes = convertRoutesToDataRoutes(routes);
|
|
2459
|
+
let basename = (opts ? opts.basename : null) || "/";
|
|
2403
2460
|
/**
|
|
2404
2461
|
* The query() method is intended for document requests, in which we want to
|
|
2405
2462
|
* call an optional action and potentially multiple loaders for all nested
|
|
@@ -2422,16 +2479,20 @@
|
|
|
2422
2479
|
|
|
2423
2480
|
async function query(request) {
|
|
2424
2481
|
let url = new URL(request.url);
|
|
2482
|
+
let method = request.method.toLowerCase();
|
|
2425
2483
|
let location = createLocation("", createPath(url), null, "default");
|
|
2426
|
-
let matches = matchRoutes(dataRoutes, location);
|
|
2484
|
+
let matches = matchRoutes(dataRoutes, location, basename); // SSR supports HEAD requests while SPA doesn't
|
|
2427
2485
|
|
|
2428
|
-
if (!
|
|
2486
|
+
if (!isValidMethod(method) && method !== "head") {
|
|
2487
|
+
let error = getInternalRouterError(405, {
|
|
2488
|
+
method
|
|
2489
|
+
});
|
|
2429
2490
|
let {
|
|
2430
2491
|
matches: methodNotAllowedMatches,
|
|
2431
|
-
route
|
|
2432
|
-
|
|
2433
|
-
} = getMethodNotAllowedMatches(dataRoutes);
|
|
2492
|
+
route
|
|
2493
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
2434
2494
|
return {
|
|
2495
|
+
basename,
|
|
2435
2496
|
location,
|
|
2436
2497
|
matches: methodNotAllowedMatches,
|
|
2437
2498
|
loaderData: {},
|
|
@@ -2444,12 +2505,15 @@
|
|
|
2444
2505
|
actionHeaders: {}
|
|
2445
2506
|
};
|
|
2446
2507
|
} else if (!matches) {
|
|
2508
|
+
let error = getInternalRouterError(404, {
|
|
2509
|
+
pathname: location.pathname
|
|
2510
|
+
});
|
|
2447
2511
|
let {
|
|
2448
2512
|
matches: notFoundMatches,
|
|
2449
|
-
route
|
|
2450
|
-
|
|
2451
|
-
} = getNotFoundMatches(dataRoutes);
|
|
2513
|
+
route
|
|
2514
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
2452
2515
|
return {
|
|
2516
|
+
basename,
|
|
2453
2517
|
location,
|
|
2454
2518
|
matches: notFoundMatches,
|
|
2455
2519
|
loaderData: {},
|
|
@@ -2473,7 +2537,8 @@
|
|
|
2473
2537
|
|
|
2474
2538
|
|
|
2475
2539
|
return _extends({
|
|
2476
|
-
location
|
|
2540
|
+
location,
|
|
2541
|
+
basename
|
|
2477
2542
|
}, result);
|
|
2478
2543
|
}
|
|
2479
2544
|
/**
|
|
@@ -2489,35 +2554,42 @@
|
|
|
2489
2554
|
* can do proper boundary identification in Remix where a thrown Response
|
|
2490
2555
|
* must go to the Catch Boundary but a returned Response is happy-path.
|
|
2491
2556
|
*
|
|
2492
|
-
* One thing to note is that any Router-initiated
|
|
2493
|
-
*
|
|
2494
|
-
*
|
|
2557
|
+
* One thing to note is that any Router-initiated Errors that make sense
|
|
2558
|
+
* to associate with a status code will be thrown as an ErrorResponse
|
|
2559
|
+
* instance which include the raw Error, such that the calling context can
|
|
2560
|
+
* serialize the error as they see fit while including the proper response
|
|
2561
|
+
* code. Examples here are 404 and 405 errors that occur prior to reaching
|
|
2562
|
+
* any user-defined loaders.
|
|
2495
2563
|
*/
|
|
2496
2564
|
|
|
2497
2565
|
|
|
2498
2566
|
async function queryRoute(request, routeId) {
|
|
2499
2567
|
let url = new URL(request.url);
|
|
2568
|
+
let method = request.method.toLowerCase();
|
|
2500
2569
|
let location = createLocation("", createPath(url), null, "default");
|
|
2501
|
-
let matches = matchRoutes(dataRoutes, location);
|
|
2570
|
+
let matches = matchRoutes(dataRoutes, location, basename); // SSR supports HEAD requests while SPA doesn't
|
|
2502
2571
|
|
|
2503
|
-
if (!
|
|
2504
|
-
throw
|
|
2505
|
-
|
|
2506
|
-
statusText: "Method Not Allowed"
|
|
2572
|
+
if (!isValidMethod(method) && method !== "head") {
|
|
2573
|
+
throw getInternalRouterError(405, {
|
|
2574
|
+
method
|
|
2507
2575
|
});
|
|
2508
2576
|
} else if (!matches) {
|
|
2509
|
-
throw
|
|
2510
|
-
|
|
2511
|
-
statusText: "Not Found"
|
|
2577
|
+
throw getInternalRouterError(404, {
|
|
2578
|
+
pathname: location.pathname
|
|
2512
2579
|
});
|
|
2513
2580
|
}
|
|
2514
2581
|
|
|
2515
2582
|
let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location);
|
|
2516
2583
|
|
|
2517
|
-
if (!match) {
|
|
2518
|
-
throw
|
|
2519
|
-
|
|
2520
|
-
|
|
2584
|
+
if (routeId && !match) {
|
|
2585
|
+
throw getInternalRouterError(403, {
|
|
2586
|
+
pathname: location.pathname,
|
|
2587
|
+
routeId
|
|
2588
|
+
});
|
|
2589
|
+
} else if (!match) {
|
|
2590
|
+
// This should never hit I don't think?
|
|
2591
|
+
throw getInternalRouterError(404, {
|
|
2592
|
+
pathname: location.pathname
|
|
2521
2593
|
});
|
|
2522
2594
|
}
|
|
2523
2595
|
|
|
@@ -2546,7 +2618,7 @@
|
|
|
2546
2618
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2547
2619
|
|
|
2548
2620
|
try {
|
|
2549
|
-
if (
|
|
2621
|
+
if (isSubmissionMethod(request.method.toLowerCase())) {
|
|
2550
2622
|
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), routeMatch != null);
|
|
2551
2623
|
return result;
|
|
2552
2624
|
}
|
|
@@ -2582,17 +2654,22 @@
|
|
|
2582
2654
|
let result;
|
|
2583
2655
|
|
|
2584
2656
|
if (!actionMatch.route.action) {
|
|
2657
|
+
let error = getInternalRouterError(405, {
|
|
2658
|
+
method: request.method,
|
|
2659
|
+
pathname: createURL(request.url).pathname,
|
|
2660
|
+
routeId: actionMatch.route.id
|
|
2661
|
+
});
|
|
2662
|
+
|
|
2585
2663
|
if (isRouteRequest) {
|
|
2586
|
-
throw
|
|
2587
|
-
status: 405,
|
|
2588
|
-
statusText: "Method Not Allowed"
|
|
2589
|
-
});
|
|
2664
|
+
throw error;
|
|
2590
2665
|
}
|
|
2591
2666
|
|
|
2592
|
-
result =
|
|
2667
|
+
result = {
|
|
2668
|
+
type: ResultType.error,
|
|
2669
|
+
error
|
|
2670
|
+
};
|
|
2593
2671
|
} else {
|
|
2594
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches,
|
|
2595
|
-
true, isRouteRequest);
|
|
2672
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest);
|
|
2596
2673
|
|
|
2597
2674
|
if (request.signal.aborted) {
|
|
2598
2675
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2621,20 +2698,7 @@
|
|
|
2621
2698
|
// Note: This should only be non-Response values if we get here, since
|
|
2622
2699
|
// isRouteRequest should throw any Response received in callLoaderOrAction
|
|
2623
2700
|
if (isErrorResult(result)) {
|
|
2624
|
-
|
|
2625
|
-
return {
|
|
2626
|
-
matches: [actionMatch],
|
|
2627
|
-
loaderData: {},
|
|
2628
|
-
actionData: null,
|
|
2629
|
-
errors: {
|
|
2630
|
-
[boundaryMatch.route.id]: result.error
|
|
2631
|
-
},
|
|
2632
|
-
// Note: statusCode + headers are unused here since queryRoute will
|
|
2633
|
-
// return the raw Response or value
|
|
2634
|
-
statusCode: 500,
|
|
2635
|
-
loaderHeaders: {},
|
|
2636
|
-
actionHeaders: {}
|
|
2637
|
-
};
|
|
2701
|
+
throw result.error;
|
|
2638
2702
|
}
|
|
2639
2703
|
|
|
2640
2704
|
return {
|
|
@@ -2683,9 +2747,18 @@
|
|
|
2683
2747
|
}
|
|
2684
2748
|
|
|
2685
2749
|
async function loadRouteData(request, matches, routeMatch, pendingActionError) {
|
|
2686
|
-
let isRouteRequest = routeMatch != null;
|
|
2750
|
+
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
2751
|
+
|
|
2752
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
2753
|
+
throw getInternalRouterError(400, {
|
|
2754
|
+
method: request.method,
|
|
2755
|
+
pathname: createURL(request.url).pathname,
|
|
2756
|
+
routeId: routeMatch == null ? void 0 : routeMatch.route.id
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2687
2760
|
let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
|
|
2688
|
-
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run
|
|
2761
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run (query())
|
|
2689
2762
|
|
|
2690
2763
|
if (matchesToLoad.length === 0) {
|
|
2691
2764
|
return {
|
|
@@ -2697,8 +2770,7 @@
|
|
|
2697
2770
|
};
|
|
2698
2771
|
}
|
|
2699
2772
|
|
|
2700
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches,
|
|
2701
|
-
true, isRouteRequest))]);
|
|
2773
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest))]);
|
|
2702
2774
|
|
|
2703
2775
|
if (request.signal.aborted) {
|
|
2704
2776
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2719,14 +2791,6 @@
|
|
|
2719
2791
|
});
|
|
2720
2792
|
}
|
|
2721
2793
|
|
|
2722
|
-
function createRouterErrorResponse(body, init) {
|
|
2723
|
-
return new Response(body, _extends({}, init, {
|
|
2724
|
-
headers: _extends({}, init.headers, {
|
|
2725
|
-
"X-Remix-Router-Error": "yes"
|
|
2726
|
-
})
|
|
2727
|
-
}));
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
2794
|
return {
|
|
2731
2795
|
dataRoutes,
|
|
2732
2796
|
query,
|
|
@@ -2751,9 +2815,14 @@
|
|
|
2751
2815
|
});
|
|
2752
2816
|
|
|
2753
2817
|
return newContext;
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
function isSubmissionNavigation(opts) {
|
|
2821
|
+
return opts != null && "formData" in opts;
|
|
2754
2822
|
} // Normalize navigation options by converting formMethod=GET formData objects to
|
|
2755
2823
|
// URLSearchParams so they behave identically to links with query params
|
|
2756
2824
|
|
|
2825
|
+
|
|
2757
2826
|
function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
2758
2827
|
if (isFetcher === void 0) {
|
|
2759
2828
|
isFetcher = false;
|
|
@@ -2761,14 +2830,23 @@
|
|
|
2761
2830
|
|
|
2762
2831
|
let path = typeof to === "string" ? to : createPath(to); // Return location verbatim on non-submission navigations
|
|
2763
2832
|
|
|
2764
|
-
if (!opts || !(
|
|
2833
|
+
if (!opts || !isSubmissionNavigation(opts)) {
|
|
2765
2834
|
return {
|
|
2766
2835
|
path
|
|
2767
2836
|
};
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
if (opts.formMethod && !isValidMethod(opts.formMethod)) {
|
|
2840
|
+
return {
|
|
2841
|
+
path,
|
|
2842
|
+
error: getInternalRouterError(405, {
|
|
2843
|
+
method: opts.formMethod
|
|
2844
|
+
})
|
|
2845
|
+
};
|
|
2768
2846
|
} // Create a Submission on non-GET navigations
|
|
2769
2847
|
|
|
2770
2848
|
|
|
2771
|
-
if (opts.formMethod
|
|
2849
|
+
if (opts.formMethod && isSubmissionMethod(opts.formMethod)) {
|
|
2772
2850
|
return {
|
|
2773
2851
|
path,
|
|
2774
2852
|
submission: {
|
|
@@ -2778,13 +2856,6 @@
|
|
|
2778
2856
|
formData: opts.formData
|
|
2779
2857
|
}
|
|
2780
2858
|
};
|
|
2781
|
-
} // No formData to flatten for GET submission
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
if (!opts.formData) {
|
|
2785
|
-
return {
|
|
2786
|
-
path
|
|
2787
|
-
};
|
|
2788
2859
|
} // Flatten submission onto URLSearchParams for GET submissions
|
|
2789
2860
|
|
|
2790
2861
|
|
|
@@ -2803,31 +2874,13 @@
|
|
|
2803
2874
|
} catch (e) {
|
|
2804
2875
|
return {
|
|
2805
2876
|
path,
|
|
2806
|
-
error:
|
|
2877
|
+
error: getInternalRouterError(400)
|
|
2807
2878
|
};
|
|
2808
2879
|
}
|
|
2809
2880
|
|
|
2810
2881
|
return {
|
|
2811
2882
|
path: createPath(parsedPath)
|
|
2812
2883
|
};
|
|
2813
|
-
}
|
|
2814
|
-
|
|
2815
|
-
function getLoaderRedirect(state, redirect) {
|
|
2816
|
-
let {
|
|
2817
|
-
formMethod,
|
|
2818
|
-
formAction,
|
|
2819
|
-
formEncType,
|
|
2820
|
-
formData
|
|
2821
|
-
} = state.navigation;
|
|
2822
|
-
let navigation = {
|
|
2823
|
-
state: "loading",
|
|
2824
|
-
location: createLocation(state.location, redirect.location),
|
|
2825
|
-
formMethod: formMethod || undefined,
|
|
2826
|
-
formAction: formAction || undefined,
|
|
2827
|
-
formEncType: formEncType || undefined,
|
|
2828
|
-
formData: formData || undefined
|
|
2829
|
-
};
|
|
2830
|
-
return navigation;
|
|
2831
2884
|
} // Filter out all routes below any caught error as they aren't going to
|
|
2832
2885
|
// render so we don't need to load them
|
|
2833
2886
|
|
|
@@ -2928,6 +2981,10 @@
|
|
|
2928
2981
|
}
|
|
2929
2982
|
|
|
2930
2983
|
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest) {
|
|
2984
|
+
if (basename === void 0) {
|
|
2985
|
+
basename = "/";
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2931
2988
|
if (isStaticRequest === void 0) {
|
|
2932
2989
|
isStaticRequest = false;
|
|
2933
2990
|
}
|
|
@@ -2953,6 +3010,7 @@
|
|
|
2953
3010
|
request,
|
|
2954
3011
|
params: match.params
|
|
2955
3012
|
}), abortPromise]);
|
|
3013
|
+
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`.");
|
|
2956
3014
|
} catch (e) {
|
|
2957
3015
|
resultType = ResultType.error;
|
|
2958
3016
|
result = e;
|
|
@@ -2963,26 +3021,31 @@
|
|
|
2963
3021
|
if (result instanceof Response) {
|
|
2964
3022
|
let status = result.status; // Process redirects
|
|
2965
3023
|
|
|
2966
|
-
if (status
|
|
3024
|
+
if (redirectStatusCodes.has(status)) {
|
|
2967
3025
|
let location = result.headers.get("Location");
|
|
2968
|
-
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header"); //
|
|
3026
|
+
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header"); // Check if this an external redirect that goes to a new origin
|
|
2969
3027
|
|
|
2970
|
-
let
|
|
2971
|
-
let routePathnames = getPathContributingMatches(activeMatches).map(match => match.pathnameBase);
|
|
2972
|
-
let requestPath = createURL(request.url).pathname;
|
|
2973
|
-
let resolvedLocation = resolveTo(location, routePathnames, requestPath);
|
|
2974
|
-
invariant(createPath(resolvedLocation), "Unable to resolve redirect location: " + result.headers.get("Location")); // Prepend the basename to the redirect location if we have one
|
|
3028
|
+
let external = createURL(location).origin !== createURL("/").origin; // Support relative routing in internal redirects
|
|
2975
3029
|
|
|
2976
|
-
if (
|
|
2977
|
-
let
|
|
2978
|
-
|
|
2979
|
-
|
|
3030
|
+
if (!external) {
|
|
3031
|
+
let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
|
|
3032
|
+
let routePathnames = getPathContributingMatches(activeMatches).map(match => match.pathnameBase);
|
|
3033
|
+
let requestPath = createURL(request.url).pathname;
|
|
3034
|
+
let resolvedLocation = resolveTo(location, routePathnames, requestPath);
|
|
3035
|
+
invariant(createPath(resolvedLocation), "Unable to resolve redirect location: " + location); // Prepend the basename to the redirect location if we have one
|
|
3036
|
+
|
|
3037
|
+
if (basename) {
|
|
3038
|
+
let path = resolvedLocation.pathname;
|
|
3039
|
+
resolvedLocation.pathname = path === "/" ? basename : joinPaths([basename, path]);
|
|
3040
|
+
}
|
|
2980
3041
|
|
|
2981
|
-
|
|
3042
|
+
location = createPath(resolvedLocation);
|
|
3043
|
+
} // Don't process redirects in the router during static requests requests.
|
|
2982
3044
|
// Instead, throw the Response and let the server handle it with an HTTP
|
|
2983
3045
|
// redirect. We also update the Location header in place in this flow so
|
|
2984
3046
|
// basename and relative routing is taken into account
|
|
2985
3047
|
|
|
3048
|
+
|
|
2986
3049
|
if (isStaticRequest) {
|
|
2987
3050
|
result.headers.set("Location", location);
|
|
2988
3051
|
throw result;
|
|
@@ -2992,7 +3055,8 @@
|
|
|
2992
3055
|
type: ResultType.redirect,
|
|
2993
3056
|
status,
|
|
2994
3057
|
location,
|
|
2995
|
-
revalidate: result.headers.get("X-Remix-Revalidate") !== null
|
|
3058
|
+
revalidate: result.headers.get("X-Remix-Revalidate") !== null,
|
|
3059
|
+
external
|
|
2996
3060
|
};
|
|
2997
3061
|
} // For SSR single-route requests, we want to hand Responses back directly
|
|
2998
3062
|
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
@@ -3220,10 +3284,10 @@
|
|
|
3220
3284
|
return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0];
|
|
3221
3285
|
}
|
|
3222
3286
|
|
|
3223
|
-
function getShortCircuitMatches(routes
|
|
3287
|
+
function getShortCircuitMatches(routes) {
|
|
3224
3288
|
// Prefer a root layout route if present, otherwise shim in a route object
|
|
3225
3289
|
let route = routes.find(r => r.index || !r.path || r.path === "/") || {
|
|
3226
|
-
id: "__shim-
|
|
3290
|
+
id: "__shim-error-route__"
|
|
3227
3291
|
};
|
|
3228
3292
|
return {
|
|
3229
3293
|
matches: [{
|
|
@@ -3232,26 +3296,45 @@
|
|
|
3232
3296
|
pathnameBase: "",
|
|
3233
3297
|
route
|
|
3234
3298
|
}],
|
|
3235
|
-
route
|
|
3236
|
-
error: new ErrorResponse(status, statusText, null)
|
|
3299
|
+
route
|
|
3237
3300
|
};
|
|
3238
3301
|
}
|
|
3239
3302
|
|
|
3240
|
-
function
|
|
3241
|
-
|
|
3242
|
-
|
|
3303
|
+
function getInternalRouterError(status, _temp) {
|
|
3304
|
+
let {
|
|
3305
|
+
pathname,
|
|
3306
|
+
routeId,
|
|
3307
|
+
method,
|
|
3308
|
+
message
|
|
3309
|
+
} = _temp === void 0 ? {} : _temp;
|
|
3310
|
+
let statusText = "Unknown Server Error";
|
|
3311
|
+
let errorMessage = "Unknown @remix-run/router error";
|
|
3312
|
+
|
|
3313
|
+
if (status === 400) {
|
|
3314
|
+
statusText = "Bad Request";
|
|
3315
|
+
|
|
3316
|
+
if (method && pathname && routeId) {
|
|
3317
|
+
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.";
|
|
3318
|
+
} else {
|
|
3319
|
+
errorMessage = "Cannot submit binary form data using GET";
|
|
3320
|
+
}
|
|
3321
|
+
} else if (status === 403) {
|
|
3322
|
+
statusText = "Forbidden";
|
|
3323
|
+
errorMessage = "Route \"" + routeId + "\" does not match URL \"" + pathname + "\"";
|
|
3324
|
+
} else if (status === 404) {
|
|
3325
|
+
statusText = "Not Found";
|
|
3326
|
+
errorMessage = "No route matches URL \"" + pathname + "\"";
|
|
3327
|
+
} else if (status === 405) {
|
|
3328
|
+
statusText = "Method Not Allowed";
|
|
3243
3329
|
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3330
|
+
if (method && pathname && routeId) {
|
|
3331
|
+
errorMessage = "You made a " + method.toUpperCase() + " request to \"" + pathname + "\" but " + ("did not provide an `action` for route \"" + routeId + "\", ") + "so there is no way to handle the request.";
|
|
3332
|
+
} else if (method) {
|
|
3333
|
+
errorMessage = "Invalid request method \"" + method.toUpperCase() + "\"";
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3247
3336
|
|
|
3248
|
-
|
|
3249
|
-
let href = typeof path === "string" ? path : createPath(path);
|
|
3250
|
-
console.warn("You're trying to submit to a route that does not have an action. To " + "fix this, please add an `action` function to the route for " + ("[" + href + "]"));
|
|
3251
|
-
return {
|
|
3252
|
-
type: ResultType.error,
|
|
3253
|
-
error: new ErrorResponse(405, "Method Not Allowed", "")
|
|
3254
|
-
};
|
|
3337
|
+
return new ErrorResponse(status || 500, statusText, new Error(errorMessage), true);
|
|
3255
3338
|
} // Find any returned redirect errors, starting from the lowest match
|
|
3256
3339
|
|
|
3257
3340
|
|
|
@@ -3302,6 +3385,14 @@
|
|
|
3302
3385
|
return obj && obj.response instanceof Response && (obj.type === ResultType.data || ResultType.error);
|
|
3303
3386
|
}
|
|
3304
3387
|
|
|
3388
|
+
function isValidMethod(method) {
|
|
3389
|
+
return validRequestMethods.has(method);
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3392
|
+
function isSubmissionMethod(method) {
|
|
3393
|
+
return validActionMethods.has(method);
|
|
3394
|
+
}
|
|
3395
|
+
|
|
3305
3396
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|
|
3306
3397
|
for (let index = 0; index < results.length; index++) {
|
|
3307
3398
|
let result = results[index];
|