@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.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
|
*
|
|
@@ -116,8 +116,13 @@ function createMemoryHistory(options) {
|
|
|
116
116
|
return typeof to === "string" ? to : createPath(to);
|
|
117
117
|
},
|
|
118
118
|
|
|
119
|
-
encodeLocation(
|
|
120
|
-
|
|
119
|
+
encodeLocation(to) {
|
|
120
|
+
let path = typeof to === "string" ? parsePath(to) : to;
|
|
121
|
+
return {
|
|
122
|
+
pathname: path.pathname || "",
|
|
123
|
+
search: path.search || "",
|
|
124
|
+
hash: path.hash || ""
|
|
125
|
+
};
|
|
121
126
|
},
|
|
122
127
|
|
|
123
128
|
push(to, state) {
|
|
@@ -449,14 +454,14 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
|
|
|
449
454
|
return createHref(window, to);
|
|
450
455
|
},
|
|
451
456
|
|
|
452
|
-
encodeLocation(
|
|
457
|
+
encodeLocation(to) {
|
|
453
458
|
// Encode a Location the same way window.location would
|
|
454
|
-
let url = createURL(createPath(
|
|
455
|
-
return
|
|
459
|
+
let url = createURL(typeof to === "string" ? to : createPath(to));
|
|
460
|
+
return {
|
|
456
461
|
pathname: url.pathname,
|
|
457
462
|
search: url.search,
|
|
458
463
|
hash: url.hash
|
|
459
|
-
}
|
|
464
|
+
};
|
|
460
465
|
},
|
|
461
466
|
|
|
462
467
|
push,
|
|
@@ -1217,10 +1222,21 @@ const redirect = function redirect(url, init) {
|
|
|
1217
1222
|
*/
|
|
1218
1223
|
|
|
1219
1224
|
class ErrorResponse {
|
|
1220
|
-
constructor(status, statusText, data) {
|
|
1225
|
+
constructor(status, statusText, data, internal) {
|
|
1226
|
+
if (internal === void 0) {
|
|
1227
|
+
internal = false;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1221
1230
|
this.status = status;
|
|
1222
1231
|
this.statusText = statusText || "";
|
|
1223
|
-
this.
|
|
1232
|
+
this.internal = internal;
|
|
1233
|
+
|
|
1234
|
+
if (data instanceof Error) {
|
|
1235
|
+
this.data = data.toString();
|
|
1236
|
+
this.error = data;
|
|
1237
|
+
} else {
|
|
1238
|
+
this.data = data;
|
|
1239
|
+
}
|
|
1224
1240
|
}
|
|
1225
1241
|
|
|
1226
1242
|
}
|
|
@@ -1233,6 +1249,12 @@ function isRouteErrorResponse(e) {
|
|
|
1233
1249
|
return e instanceof ErrorResponse;
|
|
1234
1250
|
}
|
|
1235
1251
|
|
|
1252
|
+
const validActionMethodsArr = ["post", "put", "patch", "delete"];
|
|
1253
|
+
const validActionMethods = new Set(validActionMethodsArr);
|
|
1254
|
+
const validRequestMethodsArr = ["get", ...validActionMethodsArr];
|
|
1255
|
+
const validRequestMethods = new Set(validRequestMethodsArr);
|
|
1256
|
+
const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
|
|
1257
|
+
const redirectPreserveMethodStatusCodes = new Set([307, 308]);
|
|
1236
1258
|
const IDLE_NAVIGATION = {
|
|
1237
1259
|
state: "idle",
|
|
1238
1260
|
location: undefined,
|
|
@@ -1283,11 +1305,13 @@ function createRouter(init) {
|
|
|
1283
1305
|
if (initialMatches == null) {
|
|
1284
1306
|
// If we do not match a user-provided-route, fall back to the root
|
|
1285
1307
|
// to allow the error boundary to take over
|
|
1308
|
+
let error = getInternalRouterError(404, {
|
|
1309
|
+
pathname: init.history.location.pathname
|
|
1310
|
+
});
|
|
1286
1311
|
let {
|
|
1287
1312
|
matches,
|
|
1288
|
-
route
|
|
1289
|
-
|
|
1290
|
-
} = getNotFoundMatches(dataRoutes);
|
|
1313
|
+
route
|
|
1314
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
1291
1315
|
initialMatches = matches;
|
|
1292
1316
|
initialErrors = {
|
|
1293
1317
|
[route.id]: error
|
|
@@ -1463,7 +1487,7 @@ function createRouter(init) {
|
|
|
1463
1487
|
// the same encoding we'd get from a history.pushState/window.location read
|
|
1464
1488
|
// without having to touch history
|
|
1465
1489
|
|
|
1466
|
-
location = init.history.encodeLocation(location);
|
|
1490
|
+
location = _extends({}, location, init.history.encodeLocation(location));
|
|
1467
1491
|
let historyAction = (opts && opts.replace) === true || submission != null ? Action.Replace : Action.Push;
|
|
1468
1492
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
1469
1493
|
return await startNavigation(historyAction, location, {
|
|
@@ -1527,11 +1551,13 @@ function createRouter(init) {
|
|
|
1527
1551
|
let matches = matchRoutes(dataRoutes, location, init.basename); // Short circuit with a 404 on the root error boundary if we match nothing
|
|
1528
1552
|
|
|
1529
1553
|
if (!matches) {
|
|
1554
|
+
let error = getInternalRouterError(404, {
|
|
1555
|
+
pathname: location.pathname
|
|
1556
|
+
});
|
|
1530
1557
|
let {
|
|
1531
1558
|
matches: notFoundMatches,
|
|
1532
|
-
route
|
|
1533
|
-
|
|
1534
|
-
} = getNotFoundMatches(dataRoutes); // Cancel all pending deferred on 404s since we don't keep any routes
|
|
1559
|
+
route
|
|
1560
|
+
} = getShortCircuitMatches(dataRoutes); // Cancel all pending deferred on 404s since we don't keep any routes
|
|
1535
1561
|
|
|
1536
1562
|
cancelActiveDeferreds();
|
|
1537
1563
|
completeNavigation(location, {
|
|
@@ -1627,7 +1653,14 @@ function createRouter(init) {
|
|
|
1627
1653
|
let actionMatch = getTargetMatch(matches, location);
|
|
1628
1654
|
|
|
1629
1655
|
if (!actionMatch.route.action) {
|
|
1630
|
-
result =
|
|
1656
|
+
result = {
|
|
1657
|
+
type: ResultType.error,
|
|
1658
|
+
error: getInternalRouterError(405, {
|
|
1659
|
+
method: request.method,
|
|
1660
|
+
pathname: location.pathname,
|
|
1661
|
+
routeId: actionMatch.route.id
|
|
1662
|
+
})
|
|
1663
|
+
};
|
|
1631
1664
|
} else {
|
|
1632
1665
|
result = await callLoaderOrAction("action", request, actionMatch, matches, router.basename);
|
|
1633
1666
|
|
|
@@ -1639,12 +1672,7 @@ function createRouter(init) {
|
|
|
1639
1672
|
}
|
|
1640
1673
|
|
|
1641
1674
|
if (isRedirectResult(result)) {
|
|
1642
|
-
|
|
1643
|
-
state: "loading",
|
|
1644
|
-
location: createLocation(state.location, result.location)
|
|
1645
|
-
}, submission);
|
|
1646
|
-
|
|
1647
|
-
await startRedirectNavigation(result, redirectNavigation, opts && opts.replace);
|
|
1675
|
+
await startRedirectNavigation(state, result, opts && opts.replace === true);
|
|
1648
1676
|
return {
|
|
1649
1677
|
shortCircuited: true
|
|
1650
1678
|
};
|
|
@@ -1771,8 +1799,7 @@ function createRouter(init) {
|
|
|
1771
1799
|
let redirect = findRedirect(results);
|
|
1772
1800
|
|
|
1773
1801
|
if (redirect) {
|
|
1774
|
-
|
|
1775
|
-
await startRedirectNavigation(redirect, redirectNavigation, replace);
|
|
1802
|
+
await startRedirectNavigation(state, redirect, replace);
|
|
1776
1803
|
return {
|
|
1777
1804
|
shortCircuited: true
|
|
1778
1805
|
};
|
|
@@ -1818,7 +1845,9 @@ function createRouter(init) {
|
|
|
1818
1845
|
let matches = matchRoutes(dataRoutes, href, init.basename);
|
|
1819
1846
|
|
|
1820
1847
|
if (!matches) {
|
|
1821
|
-
setFetcherError(key, routeId,
|
|
1848
|
+
setFetcherError(key, routeId, getInternalRouterError(404, {
|
|
1849
|
+
pathname: href
|
|
1850
|
+
}));
|
|
1822
1851
|
return;
|
|
1823
1852
|
}
|
|
1824
1853
|
|
|
@@ -1846,9 +1875,11 @@ function createRouter(init) {
|
|
|
1846
1875
|
fetchLoadMatches.delete(key);
|
|
1847
1876
|
|
|
1848
1877
|
if (!match.route.action) {
|
|
1849
|
-
let {
|
|
1850
|
-
|
|
1851
|
-
|
|
1878
|
+
let error = getInternalRouterError(405, {
|
|
1879
|
+
method: submission.formMethod,
|
|
1880
|
+
pathname: path,
|
|
1881
|
+
routeId: routeId
|
|
1882
|
+
});
|
|
1852
1883
|
setFetcherError(key, routeId, error);
|
|
1853
1884
|
return;
|
|
1854
1885
|
} // Put this fetcher into it's submitting state
|
|
@@ -1896,14 +1927,7 @@ function createRouter(init) {
|
|
|
1896
1927
|
updateState({
|
|
1897
1928
|
fetchers: new Map(state.fetchers)
|
|
1898
1929
|
});
|
|
1899
|
-
|
|
1900
|
-
let redirectNavigation = _extends({
|
|
1901
|
-
state: "loading",
|
|
1902
|
-
location: createLocation(state.location, actionResult.location)
|
|
1903
|
-
}, submission);
|
|
1904
|
-
|
|
1905
|
-
await startRedirectNavigation(actionResult, redirectNavigation);
|
|
1906
|
-
return;
|
|
1930
|
+
return startRedirectNavigation(state, actionResult);
|
|
1907
1931
|
} // Process any non-redirect errors thrown
|
|
1908
1932
|
|
|
1909
1933
|
|
|
@@ -1977,9 +2001,7 @@ function createRouter(init) {
|
|
|
1977
2001
|
let redirect = findRedirect(results);
|
|
1978
2002
|
|
|
1979
2003
|
if (redirect) {
|
|
1980
|
-
|
|
1981
|
-
await startRedirectNavigation(redirect, redirectNavigation);
|
|
1982
|
-
return;
|
|
2004
|
+
return startRedirectNavigation(state, redirect);
|
|
1983
2005
|
} // Process and commit output from loaders
|
|
1984
2006
|
|
|
1985
2007
|
|
|
@@ -2064,8 +2086,7 @@ function createRouter(init) {
|
|
|
2064
2086
|
|
|
2065
2087
|
|
|
2066
2088
|
if (isRedirectResult(result)) {
|
|
2067
|
-
|
|
2068
|
-
await startRedirectNavigation(result, redirectNavigation);
|
|
2089
|
+
await startRedirectNavigation(state, result);
|
|
2069
2090
|
return;
|
|
2070
2091
|
} // Process any non-redirect errors thrown
|
|
2071
2092
|
|
|
@@ -2121,19 +2142,55 @@ function createRouter(init) {
|
|
|
2121
2142
|
*/
|
|
2122
2143
|
|
|
2123
2144
|
|
|
2124
|
-
async function startRedirectNavigation(
|
|
2145
|
+
async function startRedirectNavigation(state, redirect, replace) {
|
|
2125
2146
|
if (redirect.revalidate) {
|
|
2126
2147
|
isRevalidationRequired = true;
|
|
2127
2148
|
}
|
|
2128
2149
|
|
|
2129
|
-
|
|
2150
|
+
let redirectLocation = createLocation(state.location, redirect.location);
|
|
2151
|
+
invariant(redirectLocation, "Expected a location on the redirect navigation");
|
|
2152
|
+
|
|
2153
|
+
if (redirect.external && typeof window !== "undefined" && typeof window.location !== "undefined") {
|
|
2154
|
+
window.location.replace(redirect.location);
|
|
2155
|
+
return;
|
|
2156
|
+
} // There's no need to abort on redirects, since we don't detect the
|
|
2130
2157
|
// redirect until the action/loaders have settled
|
|
2131
2158
|
|
|
2159
|
+
|
|
2132
2160
|
pendingNavigationController = null;
|
|
2133
2161
|
let redirectHistoryAction = replace === true ? Action.Replace : Action.Push;
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2162
|
+
let {
|
|
2163
|
+
formMethod,
|
|
2164
|
+
formAction,
|
|
2165
|
+
formEncType,
|
|
2166
|
+
formData
|
|
2167
|
+
} = state.navigation; // If this was a 307/308 submission we want to preserve the HTTP method and
|
|
2168
|
+
// re-submit the POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2169
|
+
// redirected location
|
|
2170
|
+
|
|
2171
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod && isSubmissionMethod(formMethod) && formEncType && formData) {
|
|
2172
|
+
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2173
|
+
submission: {
|
|
2174
|
+
formMethod,
|
|
2175
|
+
formAction: redirect.location,
|
|
2176
|
+
formEncType,
|
|
2177
|
+
formData
|
|
2178
|
+
}
|
|
2179
|
+
});
|
|
2180
|
+
} else {
|
|
2181
|
+
// Otherwise, we kick off a new loading navigation, preserving the
|
|
2182
|
+
// submission info for the duration of this navigation
|
|
2183
|
+
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2184
|
+
overrideNavigation: {
|
|
2185
|
+
state: "loading",
|
|
2186
|
+
location: redirectLocation,
|
|
2187
|
+
formMethod: formMethod || undefined,
|
|
2188
|
+
formAction: formAction || undefined,
|
|
2189
|
+
formEncType: formEncType || undefined,
|
|
2190
|
+
formData: formData || undefined
|
|
2191
|
+
}
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2137
2194
|
}
|
|
2138
2195
|
|
|
2139
2196
|
async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
|
|
@@ -2337,6 +2394,7 @@ function createRouter(init) {
|
|
|
2337
2394
|
// Passthrough to history-aware createHref used by useHref so we get proper
|
|
2338
2395
|
// hash-aware URLs in DOM paths
|
|
2339
2396
|
createHref: to => init.history.createHref(to),
|
|
2397
|
+
encodeLocation: to => init.history.encodeLocation(to),
|
|
2340
2398
|
getFetcher,
|
|
2341
2399
|
deleteFetcher,
|
|
2342
2400
|
dispose,
|
|
@@ -2349,11 +2407,10 @@ function createRouter(init) {
|
|
|
2349
2407
|
//#region createStaticHandler
|
|
2350
2408
|
////////////////////////////////////////////////////////////////////////////////
|
|
2351
2409
|
|
|
2352
|
-
|
|
2353
|
-
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);
|
|
2354
|
-
function unstable_createStaticHandler(routes) {
|
|
2410
|
+
function unstable_createStaticHandler(routes, opts) {
|
|
2355
2411
|
invariant(routes.length > 0, "You must provide a non-empty routes array to unstable_createStaticHandler");
|
|
2356
2412
|
let dataRoutes = convertRoutesToDataRoutes(routes);
|
|
2413
|
+
let basename = (opts ? opts.basename : null) || "/";
|
|
2357
2414
|
/**
|
|
2358
2415
|
* The query() method is intended for document requests, in which we want to
|
|
2359
2416
|
* call an optional action and potentially multiple loaders for all nested
|
|
@@ -2376,16 +2433,20 @@ function unstable_createStaticHandler(routes) {
|
|
|
2376
2433
|
|
|
2377
2434
|
async function query(request) {
|
|
2378
2435
|
let url = new URL(request.url);
|
|
2436
|
+
let method = request.method.toLowerCase();
|
|
2379
2437
|
let location = createLocation("", createPath(url), null, "default");
|
|
2380
|
-
let matches = matchRoutes(dataRoutes, location);
|
|
2438
|
+
let matches = matchRoutes(dataRoutes, location, basename); // SSR supports HEAD requests while SPA doesn't
|
|
2381
2439
|
|
|
2382
|
-
if (!
|
|
2440
|
+
if (!isValidMethod(method) && method !== "head") {
|
|
2441
|
+
let error = getInternalRouterError(405, {
|
|
2442
|
+
method
|
|
2443
|
+
});
|
|
2383
2444
|
let {
|
|
2384
2445
|
matches: methodNotAllowedMatches,
|
|
2385
|
-
route
|
|
2386
|
-
|
|
2387
|
-
} = getMethodNotAllowedMatches(dataRoutes);
|
|
2446
|
+
route
|
|
2447
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
2388
2448
|
return {
|
|
2449
|
+
basename,
|
|
2389
2450
|
location,
|
|
2390
2451
|
matches: methodNotAllowedMatches,
|
|
2391
2452
|
loaderData: {},
|
|
@@ -2398,12 +2459,15 @@ function unstable_createStaticHandler(routes) {
|
|
|
2398
2459
|
actionHeaders: {}
|
|
2399
2460
|
};
|
|
2400
2461
|
} else if (!matches) {
|
|
2462
|
+
let error = getInternalRouterError(404, {
|
|
2463
|
+
pathname: location.pathname
|
|
2464
|
+
});
|
|
2401
2465
|
let {
|
|
2402
2466
|
matches: notFoundMatches,
|
|
2403
|
-
route
|
|
2404
|
-
|
|
2405
|
-
} = getNotFoundMatches(dataRoutes);
|
|
2467
|
+
route
|
|
2468
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
2406
2469
|
return {
|
|
2470
|
+
basename,
|
|
2407
2471
|
location,
|
|
2408
2472
|
matches: notFoundMatches,
|
|
2409
2473
|
loaderData: {},
|
|
@@ -2427,7 +2491,8 @@ function unstable_createStaticHandler(routes) {
|
|
|
2427
2491
|
|
|
2428
2492
|
|
|
2429
2493
|
return _extends({
|
|
2430
|
-
location
|
|
2494
|
+
location,
|
|
2495
|
+
basename
|
|
2431
2496
|
}, result);
|
|
2432
2497
|
}
|
|
2433
2498
|
/**
|
|
@@ -2443,35 +2508,42 @@ function unstable_createStaticHandler(routes) {
|
|
|
2443
2508
|
* can do proper boundary identification in Remix where a thrown Response
|
|
2444
2509
|
* must go to the Catch Boundary but a returned Response is happy-path.
|
|
2445
2510
|
*
|
|
2446
|
-
* One thing to note is that any Router-initiated
|
|
2447
|
-
*
|
|
2448
|
-
*
|
|
2511
|
+
* One thing to note is that any Router-initiated Errors that make sense
|
|
2512
|
+
* to associate with a status code will be thrown as an ErrorResponse
|
|
2513
|
+
* instance which include the raw Error, such that the calling context can
|
|
2514
|
+
* serialize the error as they see fit while including the proper response
|
|
2515
|
+
* code. Examples here are 404 and 405 errors that occur prior to reaching
|
|
2516
|
+
* any user-defined loaders.
|
|
2449
2517
|
*/
|
|
2450
2518
|
|
|
2451
2519
|
|
|
2452
2520
|
async function queryRoute(request, routeId) {
|
|
2453
2521
|
let url = new URL(request.url);
|
|
2522
|
+
let method = request.method.toLowerCase();
|
|
2454
2523
|
let location = createLocation("", createPath(url), null, "default");
|
|
2455
|
-
let matches = matchRoutes(dataRoutes, location);
|
|
2524
|
+
let matches = matchRoutes(dataRoutes, location, basename); // SSR supports HEAD requests while SPA doesn't
|
|
2456
2525
|
|
|
2457
|
-
if (!
|
|
2458
|
-
throw
|
|
2459
|
-
|
|
2460
|
-
statusText: "Method Not Allowed"
|
|
2526
|
+
if (!isValidMethod(method) && method !== "head") {
|
|
2527
|
+
throw getInternalRouterError(405, {
|
|
2528
|
+
method
|
|
2461
2529
|
});
|
|
2462
2530
|
} else if (!matches) {
|
|
2463
|
-
throw
|
|
2464
|
-
|
|
2465
|
-
statusText: "Not Found"
|
|
2531
|
+
throw getInternalRouterError(404, {
|
|
2532
|
+
pathname: location.pathname
|
|
2466
2533
|
});
|
|
2467
2534
|
}
|
|
2468
2535
|
|
|
2469
2536
|
let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location);
|
|
2470
2537
|
|
|
2471
|
-
if (!match) {
|
|
2472
|
-
throw
|
|
2473
|
-
|
|
2474
|
-
|
|
2538
|
+
if (routeId && !match) {
|
|
2539
|
+
throw getInternalRouterError(403, {
|
|
2540
|
+
pathname: location.pathname,
|
|
2541
|
+
routeId
|
|
2542
|
+
});
|
|
2543
|
+
} else if (!match) {
|
|
2544
|
+
// This should never hit I don't think?
|
|
2545
|
+
throw getInternalRouterError(404, {
|
|
2546
|
+
pathname: location.pathname
|
|
2475
2547
|
});
|
|
2476
2548
|
}
|
|
2477
2549
|
|
|
@@ -2500,7 +2572,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2500
2572
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2501
2573
|
|
|
2502
2574
|
try {
|
|
2503
|
-
if (
|
|
2575
|
+
if (isSubmissionMethod(request.method.toLowerCase())) {
|
|
2504
2576
|
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), routeMatch != null);
|
|
2505
2577
|
return result;
|
|
2506
2578
|
}
|
|
@@ -2536,17 +2608,22 @@ function unstable_createStaticHandler(routes) {
|
|
|
2536
2608
|
let result;
|
|
2537
2609
|
|
|
2538
2610
|
if (!actionMatch.route.action) {
|
|
2611
|
+
let error = getInternalRouterError(405, {
|
|
2612
|
+
method: request.method,
|
|
2613
|
+
pathname: createURL(request.url).pathname,
|
|
2614
|
+
routeId: actionMatch.route.id
|
|
2615
|
+
});
|
|
2616
|
+
|
|
2539
2617
|
if (isRouteRequest) {
|
|
2540
|
-
throw
|
|
2541
|
-
status: 405,
|
|
2542
|
-
statusText: "Method Not Allowed"
|
|
2543
|
-
});
|
|
2618
|
+
throw error;
|
|
2544
2619
|
}
|
|
2545
2620
|
|
|
2546
|
-
result =
|
|
2621
|
+
result = {
|
|
2622
|
+
type: ResultType.error,
|
|
2623
|
+
error
|
|
2624
|
+
};
|
|
2547
2625
|
} else {
|
|
2548
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches,
|
|
2549
|
-
true, isRouteRequest);
|
|
2626
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest);
|
|
2550
2627
|
|
|
2551
2628
|
if (request.signal.aborted) {
|
|
2552
2629
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2575,20 +2652,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2575
2652
|
// Note: This should only be non-Response values if we get here, since
|
|
2576
2653
|
// isRouteRequest should throw any Response received in callLoaderOrAction
|
|
2577
2654
|
if (isErrorResult(result)) {
|
|
2578
|
-
|
|
2579
|
-
return {
|
|
2580
|
-
matches: [actionMatch],
|
|
2581
|
-
loaderData: {},
|
|
2582
|
-
actionData: null,
|
|
2583
|
-
errors: {
|
|
2584
|
-
[boundaryMatch.route.id]: result.error
|
|
2585
|
-
},
|
|
2586
|
-
// Note: statusCode + headers are unused here since queryRoute will
|
|
2587
|
-
// return the raw Response or value
|
|
2588
|
-
statusCode: 500,
|
|
2589
|
-
loaderHeaders: {},
|
|
2590
|
-
actionHeaders: {}
|
|
2591
|
-
};
|
|
2655
|
+
throw result.error;
|
|
2592
2656
|
}
|
|
2593
2657
|
|
|
2594
2658
|
return {
|
|
@@ -2637,9 +2701,18 @@ function unstable_createStaticHandler(routes) {
|
|
|
2637
2701
|
}
|
|
2638
2702
|
|
|
2639
2703
|
async function loadRouteData(request, matches, routeMatch, pendingActionError) {
|
|
2640
|
-
let isRouteRequest = routeMatch != null;
|
|
2704
|
+
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
2705
|
+
|
|
2706
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
2707
|
+
throw getInternalRouterError(400, {
|
|
2708
|
+
method: request.method,
|
|
2709
|
+
pathname: createURL(request.url).pathname,
|
|
2710
|
+
routeId: routeMatch == null ? void 0 : routeMatch.route.id
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2641
2714
|
let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
|
|
2642
|
-
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run
|
|
2715
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run (query())
|
|
2643
2716
|
|
|
2644
2717
|
if (matchesToLoad.length === 0) {
|
|
2645
2718
|
return {
|
|
@@ -2651,8 +2724,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2651
2724
|
};
|
|
2652
2725
|
}
|
|
2653
2726
|
|
|
2654
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches,
|
|
2655
|
-
true, isRouteRequest))]);
|
|
2727
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest))]);
|
|
2656
2728
|
|
|
2657
2729
|
if (request.signal.aborted) {
|
|
2658
2730
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2673,14 +2745,6 @@ function unstable_createStaticHandler(routes) {
|
|
|
2673
2745
|
});
|
|
2674
2746
|
}
|
|
2675
2747
|
|
|
2676
|
-
function createRouterErrorResponse(body, init) {
|
|
2677
|
-
return new Response(body, _extends({}, init, {
|
|
2678
|
-
headers: _extends({}, init.headers, {
|
|
2679
|
-
"X-Remix-Router-Error": "yes"
|
|
2680
|
-
})
|
|
2681
|
-
}));
|
|
2682
|
-
}
|
|
2683
|
-
|
|
2684
2748
|
return {
|
|
2685
2749
|
dataRoutes,
|
|
2686
2750
|
query,
|
|
@@ -2705,9 +2769,14 @@ function getStaticContextFromError(routes, context, error) {
|
|
|
2705
2769
|
});
|
|
2706
2770
|
|
|
2707
2771
|
return newContext;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
function isSubmissionNavigation(opts) {
|
|
2775
|
+
return opts != null && "formData" in opts;
|
|
2708
2776
|
} // Normalize navigation options by converting formMethod=GET formData objects to
|
|
2709
2777
|
// URLSearchParams so they behave identically to links with query params
|
|
2710
2778
|
|
|
2779
|
+
|
|
2711
2780
|
function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
2712
2781
|
if (isFetcher === void 0) {
|
|
2713
2782
|
isFetcher = false;
|
|
@@ -2715,14 +2784,23 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2715
2784
|
|
|
2716
2785
|
let path = typeof to === "string" ? to : createPath(to); // Return location verbatim on non-submission navigations
|
|
2717
2786
|
|
|
2718
|
-
if (!opts || !(
|
|
2787
|
+
if (!opts || !isSubmissionNavigation(opts)) {
|
|
2719
2788
|
return {
|
|
2720
2789
|
path
|
|
2721
2790
|
};
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
if (opts.formMethod && !isValidMethod(opts.formMethod)) {
|
|
2794
|
+
return {
|
|
2795
|
+
path,
|
|
2796
|
+
error: getInternalRouterError(405, {
|
|
2797
|
+
method: opts.formMethod
|
|
2798
|
+
})
|
|
2799
|
+
};
|
|
2722
2800
|
} // Create a Submission on non-GET navigations
|
|
2723
2801
|
|
|
2724
2802
|
|
|
2725
|
-
if (opts.formMethod
|
|
2803
|
+
if (opts.formMethod && isSubmissionMethod(opts.formMethod)) {
|
|
2726
2804
|
return {
|
|
2727
2805
|
path,
|
|
2728
2806
|
submission: {
|
|
@@ -2732,13 +2810,6 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2732
2810
|
formData: opts.formData
|
|
2733
2811
|
}
|
|
2734
2812
|
};
|
|
2735
|
-
} // No formData to flatten for GET submission
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
if (!opts.formData) {
|
|
2739
|
-
return {
|
|
2740
|
-
path
|
|
2741
|
-
};
|
|
2742
2813
|
} // Flatten submission onto URLSearchParams for GET submissions
|
|
2743
2814
|
|
|
2744
2815
|
|
|
@@ -2757,31 +2828,13 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2757
2828
|
} catch (e) {
|
|
2758
2829
|
return {
|
|
2759
2830
|
path,
|
|
2760
|
-
error:
|
|
2831
|
+
error: getInternalRouterError(400)
|
|
2761
2832
|
};
|
|
2762
2833
|
}
|
|
2763
2834
|
|
|
2764
2835
|
return {
|
|
2765
2836
|
path: createPath(parsedPath)
|
|
2766
2837
|
};
|
|
2767
|
-
}
|
|
2768
|
-
|
|
2769
|
-
function getLoaderRedirect(state, redirect) {
|
|
2770
|
-
let {
|
|
2771
|
-
formMethod,
|
|
2772
|
-
formAction,
|
|
2773
|
-
formEncType,
|
|
2774
|
-
formData
|
|
2775
|
-
} = state.navigation;
|
|
2776
|
-
let navigation = {
|
|
2777
|
-
state: "loading",
|
|
2778
|
-
location: createLocation(state.location, redirect.location),
|
|
2779
|
-
formMethod: formMethod || undefined,
|
|
2780
|
-
formAction: formAction || undefined,
|
|
2781
|
-
formEncType: formEncType || undefined,
|
|
2782
|
-
formData: formData || undefined
|
|
2783
|
-
};
|
|
2784
|
-
return navigation;
|
|
2785
2838
|
} // Filter out all routes below any caught error as they aren't going to
|
|
2786
2839
|
// render so we don't need to load them
|
|
2787
2840
|
|
|
@@ -2882,6 +2935,10 @@ function shouldRevalidateLoader(currentLocation, currentMatch, submission, locat
|
|
|
2882
2935
|
}
|
|
2883
2936
|
|
|
2884
2937
|
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest) {
|
|
2938
|
+
if (basename === void 0) {
|
|
2939
|
+
basename = "/";
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2885
2942
|
if (isStaticRequest === void 0) {
|
|
2886
2943
|
isStaticRequest = false;
|
|
2887
2944
|
}
|
|
@@ -2907,6 +2964,7 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2907
2964
|
request,
|
|
2908
2965
|
params: match.params
|
|
2909
2966
|
}), abortPromise]);
|
|
2967
|
+
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`.");
|
|
2910
2968
|
} catch (e) {
|
|
2911
2969
|
resultType = ResultType.error;
|
|
2912
2970
|
result = e;
|
|
@@ -2917,26 +2975,31 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2917
2975
|
if (result instanceof Response) {
|
|
2918
2976
|
let status = result.status; // Process redirects
|
|
2919
2977
|
|
|
2920
|
-
if (status
|
|
2978
|
+
if (redirectStatusCodes.has(status)) {
|
|
2921
2979
|
let location = result.headers.get("Location");
|
|
2922
|
-
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header"); //
|
|
2980
|
+
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
|
|
2923
2981
|
|
|
2924
|
-
let
|
|
2925
|
-
let routePathnames = getPathContributingMatches(activeMatches).map(match => match.pathnameBase);
|
|
2926
|
-
let requestPath = createURL(request.url).pathname;
|
|
2927
|
-
let resolvedLocation = resolveTo(location, routePathnames, requestPath);
|
|
2928
|
-
invariant(createPath(resolvedLocation), "Unable to resolve redirect location: " + result.headers.get("Location")); // Prepend the basename to the redirect location if we have one
|
|
2982
|
+
let external = createURL(location).origin !== createURL("/").origin; // Support relative routing in internal redirects
|
|
2929
2983
|
|
|
2930
|
-
if (
|
|
2931
|
-
let
|
|
2932
|
-
|
|
2933
|
-
|
|
2984
|
+
if (!external) {
|
|
2985
|
+
let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
|
|
2986
|
+
let routePathnames = getPathContributingMatches(activeMatches).map(match => match.pathnameBase);
|
|
2987
|
+
let requestPath = createURL(request.url).pathname;
|
|
2988
|
+
let resolvedLocation = resolveTo(location, routePathnames, requestPath);
|
|
2989
|
+
invariant(createPath(resolvedLocation), "Unable to resolve redirect location: " + location); // Prepend the basename to the redirect location if we have one
|
|
2990
|
+
|
|
2991
|
+
if (basename) {
|
|
2992
|
+
let path = resolvedLocation.pathname;
|
|
2993
|
+
resolvedLocation.pathname = path === "/" ? basename : joinPaths([basename, path]);
|
|
2994
|
+
}
|
|
2934
2995
|
|
|
2935
|
-
|
|
2996
|
+
location = createPath(resolvedLocation);
|
|
2997
|
+
} // Don't process redirects in the router during static requests requests.
|
|
2936
2998
|
// Instead, throw the Response and let the server handle it with an HTTP
|
|
2937
2999
|
// redirect. We also update the Location header in place in this flow so
|
|
2938
3000
|
// basename and relative routing is taken into account
|
|
2939
3001
|
|
|
3002
|
+
|
|
2940
3003
|
if (isStaticRequest) {
|
|
2941
3004
|
result.headers.set("Location", location);
|
|
2942
3005
|
throw result;
|
|
@@ -2946,7 +3009,8 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2946
3009
|
type: ResultType.redirect,
|
|
2947
3010
|
status,
|
|
2948
3011
|
location,
|
|
2949
|
-
revalidate: result.headers.get("X-Remix-Revalidate") !== null
|
|
3012
|
+
revalidate: result.headers.get("X-Remix-Revalidate") !== null,
|
|
3013
|
+
external
|
|
2950
3014
|
};
|
|
2951
3015
|
} // For SSR single-route requests, we want to hand Responses back directly
|
|
2952
3016
|
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
@@ -3174,10 +3238,10 @@ function findNearestBoundary(matches, routeId) {
|
|
|
3174
3238
|
return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0];
|
|
3175
3239
|
}
|
|
3176
3240
|
|
|
3177
|
-
function getShortCircuitMatches(routes
|
|
3241
|
+
function getShortCircuitMatches(routes) {
|
|
3178
3242
|
// Prefer a root layout route if present, otherwise shim in a route object
|
|
3179
3243
|
let route = routes.find(r => r.index || !r.path || r.path === "/") || {
|
|
3180
|
-
id: "__shim-
|
|
3244
|
+
id: "__shim-error-route__"
|
|
3181
3245
|
};
|
|
3182
3246
|
return {
|
|
3183
3247
|
matches: [{
|
|
@@ -3186,26 +3250,45 @@ function getShortCircuitMatches(routes, status, statusText) {
|
|
|
3186
3250
|
pathnameBase: "",
|
|
3187
3251
|
route
|
|
3188
3252
|
}],
|
|
3189
|
-
route
|
|
3190
|
-
error: new ErrorResponse(status, statusText, null)
|
|
3253
|
+
route
|
|
3191
3254
|
};
|
|
3192
3255
|
}
|
|
3193
3256
|
|
|
3194
|
-
function
|
|
3195
|
-
|
|
3196
|
-
|
|
3257
|
+
function getInternalRouterError(status, _temp) {
|
|
3258
|
+
let {
|
|
3259
|
+
pathname,
|
|
3260
|
+
routeId,
|
|
3261
|
+
method,
|
|
3262
|
+
message
|
|
3263
|
+
} = _temp === void 0 ? {} : _temp;
|
|
3264
|
+
let statusText = "Unknown Server Error";
|
|
3265
|
+
let errorMessage = "Unknown @remix-run/router error";
|
|
3266
|
+
|
|
3267
|
+
if (status === 400) {
|
|
3268
|
+
statusText = "Bad Request";
|
|
3269
|
+
|
|
3270
|
+
if (method && pathname && routeId) {
|
|
3271
|
+
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.";
|
|
3272
|
+
} else {
|
|
3273
|
+
errorMessage = "Cannot submit binary form data using GET";
|
|
3274
|
+
}
|
|
3275
|
+
} else if (status === 403) {
|
|
3276
|
+
statusText = "Forbidden";
|
|
3277
|
+
errorMessage = "Route \"" + routeId + "\" does not match URL \"" + pathname + "\"";
|
|
3278
|
+
} else if (status === 404) {
|
|
3279
|
+
statusText = "Not Found";
|
|
3280
|
+
errorMessage = "No route matches URL \"" + pathname + "\"";
|
|
3281
|
+
} else if (status === 405) {
|
|
3282
|
+
statusText = "Method Not Allowed";
|
|
3197
3283
|
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
}
|
|
3284
|
+
if (method && pathname && routeId) {
|
|
3285
|
+
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.";
|
|
3286
|
+
} else if (method) {
|
|
3287
|
+
errorMessage = "Invalid request method \"" + method.toUpperCase() + "\"";
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3201
3290
|
|
|
3202
|
-
|
|
3203
|
-
let href = typeof path === "string" ? path : createPath(path);
|
|
3204
|
-
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 + "]"));
|
|
3205
|
-
return {
|
|
3206
|
-
type: ResultType.error,
|
|
3207
|
-
error: new ErrorResponse(405, "Method Not Allowed", "")
|
|
3208
|
-
};
|
|
3291
|
+
return new ErrorResponse(status || 500, statusText, new Error(errorMessage), true);
|
|
3209
3292
|
} // Find any returned redirect errors, starting from the lowest match
|
|
3210
3293
|
|
|
3211
3294
|
|
|
@@ -3256,6 +3339,14 @@ function isQueryRouteResponse(obj) {
|
|
|
3256
3339
|
return obj && obj.response instanceof Response && (obj.type === ResultType.data || ResultType.error);
|
|
3257
3340
|
}
|
|
3258
3341
|
|
|
3342
|
+
function isValidMethod(method) {
|
|
3343
|
+
return validRequestMethods.has(method);
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
function isSubmissionMethod(method) {
|
|
3347
|
+
return validActionMethods.has(method);
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3259
3350
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|
|
3260
3351
|
for (let index = 0; index < results.length; index++) {
|
|
3261
3352
|
let result = results[index];
|