@remix-run/router 1.0.3 → 1.0.4-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 +16 -0
- package/dist/history.d.ts +3 -3
- package/dist/router.cjs.js +262 -166
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +14 -2
- package/dist/router.js +262 -166
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +262 -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 +292 -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.1
|
|
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,60 @@ 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
|
+
if (replace) {
|
|
2155
|
+
window.location.replace(redirect.location);
|
|
2156
|
+
} else {
|
|
2157
|
+
window.location.assign(redirect.location);
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
return;
|
|
2161
|
+
} // There's no need to abort on redirects, since we don't detect the
|
|
2130
2162
|
// redirect until the action/loaders have settled
|
|
2131
2163
|
|
|
2164
|
+
|
|
2132
2165
|
pendingNavigationController = null;
|
|
2133
2166
|
let redirectHistoryAction = replace === true ? Action.Replace : Action.Push;
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2167
|
+
let {
|
|
2168
|
+
formMethod,
|
|
2169
|
+
formAction,
|
|
2170
|
+
formEncType,
|
|
2171
|
+
formData
|
|
2172
|
+
} = state.navigation; // If this was a 307/308 submission we want to preserve the HTTP method and
|
|
2173
|
+
// re-submit the POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2174
|
+
// redirected location
|
|
2175
|
+
|
|
2176
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod && isSubmissionMethod(formMethod) && formEncType && formData) {
|
|
2177
|
+
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2178
|
+
submission: {
|
|
2179
|
+
formMethod,
|
|
2180
|
+
formAction: redirect.location,
|
|
2181
|
+
formEncType,
|
|
2182
|
+
formData
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
} else {
|
|
2186
|
+
// Otherwise, we kick off a new loading navigation, preserving the
|
|
2187
|
+
// submission info for the duration of this navigation
|
|
2188
|
+
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2189
|
+
overrideNavigation: {
|
|
2190
|
+
state: "loading",
|
|
2191
|
+
location: redirectLocation,
|
|
2192
|
+
formMethod: formMethod || undefined,
|
|
2193
|
+
formAction: formAction || undefined,
|
|
2194
|
+
formEncType: formEncType || undefined,
|
|
2195
|
+
formData: formData || undefined
|
|
2196
|
+
}
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2137
2199
|
}
|
|
2138
2200
|
|
|
2139
2201
|
async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
|
|
@@ -2337,6 +2399,7 @@ function createRouter(init) {
|
|
|
2337
2399
|
// Passthrough to history-aware createHref used by useHref so we get proper
|
|
2338
2400
|
// hash-aware URLs in DOM paths
|
|
2339
2401
|
createHref: to => init.history.createHref(to),
|
|
2402
|
+
encodeLocation: to => init.history.encodeLocation(to),
|
|
2340
2403
|
getFetcher,
|
|
2341
2404
|
deleteFetcher,
|
|
2342
2405
|
dispose,
|
|
@@ -2349,11 +2412,10 @@ function createRouter(init) {
|
|
|
2349
2412
|
//#region createStaticHandler
|
|
2350
2413
|
////////////////////////////////////////////////////////////////////////////////
|
|
2351
2414
|
|
|
2352
|
-
|
|
2353
|
-
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);
|
|
2354
|
-
function unstable_createStaticHandler(routes) {
|
|
2415
|
+
function unstable_createStaticHandler(routes, opts) {
|
|
2355
2416
|
invariant(routes.length > 0, "You must provide a non-empty routes array to unstable_createStaticHandler");
|
|
2356
2417
|
let dataRoutes = convertRoutesToDataRoutes(routes);
|
|
2418
|
+
let basename = (opts ? opts.basename : null) || "/";
|
|
2357
2419
|
/**
|
|
2358
2420
|
* The query() method is intended for document requests, in which we want to
|
|
2359
2421
|
* call an optional action and potentially multiple loaders for all nested
|
|
@@ -2376,16 +2438,20 @@ function unstable_createStaticHandler(routes) {
|
|
|
2376
2438
|
|
|
2377
2439
|
async function query(request) {
|
|
2378
2440
|
let url = new URL(request.url);
|
|
2441
|
+
let method = request.method.toLowerCase();
|
|
2379
2442
|
let location = createLocation("", createPath(url), null, "default");
|
|
2380
|
-
let matches = matchRoutes(dataRoutes, location);
|
|
2443
|
+
let matches = matchRoutes(dataRoutes, location, basename); // SSR supports HEAD requests while SPA doesn't
|
|
2381
2444
|
|
|
2382
|
-
if (!
|
|
2445
|
+
if (!isValidMethod(method) && method !== "head") {
|
|
2446
|
+
let error = getInternalRouterError(405, {
|
|
2447
|
+
method
|
|
2448
|
+
});
|
|
2383
2449
|
let {
|
|
2384
2450
|
matches: methodNotAllowedMatches,
|
|
2385
|
-
route
|
|
2386
|
-
|
|
2387
|
-
} = getMethodNotAllowedMatches(dataRoutes);
|
|
2451
|
+
route
|
|
2452
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
2388
2453
|
return {
|
|
2454
|
+
basename,
|
|
2389
2455
|
location,
|
|
2390
2456
|
matches: methodNotAllowedMatches,
|
|
2391
2457
|
loaderData: {},
|
|
@@ -2398,12 +2464,15 @@ function unstable_createStaticHandler(routes) {
|
|
|
2398
2464
|
actionHeaders: {}
|
|
2399
2465
|
};
|
|
2400
2466
|
} else if (!matches) {
|
|
2467
|
+
let error = getInternalRouterError(404, {
|
|
2468
|
+
pathname: location.pathname
|
|
2469
|
+
});
|
|
2401
2470
|
let {
|
|
2402
2471
|
matches: notFoundMatches,
|
|
2403
|
-
route
|
|
2404
|
-
|
|
2405
|
-
} = getNotFoundMatches(dataRoutes);
|
|
2472
|
+
route
|
|
2473
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
2406
2474
|
return {
|
|
2475
|
+
basename,
|
|
2407
2476
|
location,
|
|
2408
2477
|
matches: notFoundMatches,
|
|
2409
2478
|
loaderData: {},
|
|
@@ -2427,7 +2496,8 @@ function unstable_createStaticHandler(routes) {
|
|
|
2427
2496
|
|
|
2428
2497
|
|
|
2429
2498
|
return _extends({
|
|
2430
|
-
location
|
|
2499
|
+
location,
|
|
2500
|
+
basename
|
|
2431
2501
|
}, result);
|
|
2432
2502
|
}
|
|
2433
2503
|
/**
|
|
@@ -2443,35 +2513,42 @@ function unstable_createStaticHandler(routes) {
|
|
|
2443
2513
|
* can do proper boundary identification in Remix where a thrown Response
|
|
2444
2514
|
* must go to the Catch Boundary but a returned Response is happy-path.
|
|
2445
2515
|
*
|
|
2446
|
-
* One thing to note is that any Router-initiated
|
|
2447
|
-
*
|
|
2448
|
-
*
|
|
2516
|
+
* One thing to note is that any Router-initiated Errors that make sense
|
|
2517
|
+
* to associate with a status code will be thrown as an ErrorResponse
|
|
2518
|
+
* instance which include the raw Error, such that the calling context can
|
|
2519
|
+
* serialize the error as they see fit while including the proper response
|
|
2520
|
+
* code. Examples here are 404 and 405 errors that occur prior to reaching
|
|
2521
|
+
* any user-defined loaders.
|
|
2449
2522
|
*/
|
|
2450
2523
|
|
|
2451
2524
|
|
|
2452
2525
|
async function queryRoute(request, routeId) {
|
|
2453
2526
|
let url = new URL(request.url);
|
|
2527
|
+
let method = request.method.toLowerCase();
|
|
2454
2528
|
let location = createLocation("", createPath(url), null, "default");
|
|
2455
|
-
let matches = matchRoutes(dataRoutes, location);
|
|
2529
|
+
let matches = matchRoutes(dataRoutes, location, basename); // SSR supports HEAD requests while SPA doesn't
|
|
2456
2530
|
|
|
2457
|
-
if (!
|
|
2458
|
-
throw
|
|
2459
|
-
|
|
2460
|
-
statusText: "Method Not Allowed"
|
|
2531
|
+
if (!isValidMethod(method) && method !== "head") {
|
|
2532
|
+
throw getInternalRouterError(405, {
|
|
2533
|
+
method
|
|
2461
2534
|
});
|
|
2462
2535
|
} else if (!matches) {
|
|
2463
|
-
throw
|
|
2464
|
-
|
|
2465
|
-
statusText: "Not Found"
|
|
2536
|
+
throw getInternalRouterError(404, {
|
|
2537
|
+
pathname: location.pathname
|
|
2466
2538
|
});
|
|
2467
2539
|
}
|
|
2468
2540
|
|
|
2469
2541
|
let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location);
|
|
2470
2542
|
|
|
2471
|
-
if (!match) {
|
|
2472
|
-
throw
|
|
2473
|
-
|
|
2474
|
-
|
|
2543
|
+
if (routeId && !match) {
|
|
2544
|
+
throw getInternalRouterError(403, {
|
|
2545
|
+
pathname: location.pathname,
|
|
2546
|
+
routeId
|
|
2547
|
+
});
|
|
2548
|
+
} else if (!match) {
|
|
2549
|
+
// This should never hit I don't think?
|
|
2550
|
+
throw getInternalRouterError(404, {
|
|
2551
|
+
pathname: location.pathname
|
|
2475
2552
|
});
|
|
2476
2553
|
}
|
|
2477
2554
|
|
|
@@ -2500,7 +2577,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2500
2577
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2501
2578
|
|
|
2502
2579
|
try {
|
|
2503
|
-
if (
|
|
2580
|
+
if (isSubmissionMethod(request.method.toLowerCase())) {
|
|
2504
2581
|
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), routeMatch != null);
|
|
2505
2582
|
return result;
|
|
2506
2583
|
}
|
|
@@ -2536,17 +2613,22 @@ function unstable_createStaticHandler(routes) {
|
|
|
2536
2613
|
let result;
|
|
2537
2614
|
|
|
2538
2615
|
if (!actionMatch.route.action) {
|
|
2616
|
+
let error = getInternalRouterError(405, {
|
|
2617
|
+
method: request.method,
|
|
2618
|
+
pathname: createURL(request.url).pathname,
|
|
2619
|
+
routeId: actionMatch.route.id
|
|
2620
|
+
});
|
|
2621
|
+
|
|
2539
2622
|
if (isRouteRequest) {
|
|
2540
|
-
throw
|
|
2541
|
-
status: 405,
|
|
2542
|
-
statusText: "Method Not Allowed"
|
|
2543
|
-
});
|
|
2623
|
+
throw error;
|
|
2544
2624
|
}
|
|
2545
2625
|
|
|
2546
|
-
result =
|
|
2626
|
+
result = {
|
|
2627
|
+
type: ResultType.error,
|
|
2628
|
+
error
|
|
2629
|
+
};
|
|
2547
2630
|
} else {
|
|
2548
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches,
|
|
2549
|
-
true, isRouteRequest);
|
|
2631
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest);
|
|
2550
2632
|
|
|
2551
2633
|
if (request.signal.aborted) {
|
|
2552
2634
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2575,20 +2657,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2575
2657
|
// Note: This should only be non-Response values if we get here, since
|
|
2576
2658
|
// isRouteRequest should throw any Response received in callLoaderOrAction
|
|
2577
2659
|
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
|
-
};
|
|
2660
|
+
throw result.error;
|
|
2592
2661
|
}
|
|
2593
2662
|
|
|
2594
2663
|
return {
|
|
@@ -2637,9 +2706,18 @@ function unstable_createStaticHandler(routes) {
|
|
|
2637
2706
|
}
|
|
2638
2707
|
|
|
2639
2708
|
async function loadRouteData(request, matches, routeMatch, pendingActionError) {
|
|
2640
|
-
let isRouteRequest = routeMatch != null;
|
|
2709
|
+
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
2710
|
+
|
|
2711
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
2712
|
+
throw getInternalRouterError(400, {
|
|
2713
|
+
method: request.method,
|
|
2714
|
+
pathname: createURL(request.url).pathname,
|
|
2715
|
+
routeId: routeMatch == null ? void 0 : routeMatch.route.id
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2641
2719
|
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
|
|
2720
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run (query())
|
|
2643
2721
|
|
|
2644
2722
|
if (matchesToLoad.length === 0) {
|
|
2645
2723
|
return {
|
|
@@ -2651,8 +2729,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2651
2729
|
};
|
|
2652
2730
|
}
|
|
2653
2731
|
|
|
2654
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches,
|
|
2655
|
-
true, isRouteRequest))]);
|
|
2732
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest))]);
|
|
2656
2733
|
|
|
2657
2734
|
if (request.signal.aborted) {
|
|
2658
2735
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2673,14 +2750,6 @@ function unstable_createStaticHandler(routes) {
|
|
|
2673
2750
|
});
|
|
2674
2751
|
}
|
|
2675
2752
|
|
|
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
2753
|
return {
|
|
2685
2754
|
dataRoutes,
|
|
2686
2755
|
query,
|
|
@@ -2705,9 +2774,14 @@ function getStaticContextFromError(routes, context, error) {
|
|
|
2705
2774
|
});
|
|
2706
2775
|
|
|
2707
2776
|
return newContext;
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2779
|
+
function isSubmissionNavigation(opts) {
|
|
2780
|
+
return opts != null && "formData" in opts;
|
|
2708
2781
|
} // Normalize navigation options by converting formMethod=GET formData objects to
|
|
2709
2782
|
// URLSearchParams so they behave identically to links with query params
|
|
2710
2783
|
|
|
2784
|
+
|
|
2711
2785
|
function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
2712
2786
|
if (isFetcher === void 0) {
|
|
2713
2787
|
isFetcher = false;
|
|
@@ -2715,14 +2789,23 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2715
2789
|
|
|
2716
2790
|
let path = typeof to === "string" ? to : createPath(to); // Return location verbatim on non-submission navigations
|
|
2717
2791
|
|
|
2718
|
-
if (!opts || !(
|
|
2792
|
+
if (!opts || !isSubmissionNavigation(opts)) {
|
|
2719
2793
|
return {
|
|
2720
2794
|
path
|
|
2721
2795
|
};
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
if (opts.formMethod && !isValidMethod(opts.formMethod)) {
|
|
2799
|
+
return {
|
|
2800
|
+
path,
|
|
2801
|
+
error: getInternalRouterError(405, {
|
|
2802
|
+
method: opts.formMethod
|
|
2803
|
+
})
|
|
2804
|
+
};
|
|
2722
2805
|
} // Create a Submission on non-GET navigations
|
|
2723
2806
|
|
|
2724
2807
|
|
|
2725
|
-
if (opts.formMethod
|
|
2808
|
+
if (opts.formMethod && isSubmissionMethod(opts.formMethod)) {
|
|
2726
2809
|
return {
|
|
2727
2810
|
path,
|
|
2728
2811
|
submission: {
|
|
@@ -2732,13 +2815,6 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2732
2815
|
formData: opts.formData
|
|
2733
2816
|
}
|
|
2734
2817
|
};
|
|
2735
|
-
} // No formData to flatten for GET submission
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
if (!opts.formData) {
|
|
2739
|
-
return {
|
|
2740
|
-
path
|
|
2741
|
-
};
|
|
2742
2818
|
} // Flatten submission onto URLSearchParams for GET submissions
|
|
2743
2819
|
|
|
2744
2820
|
|
|
@@ -2757,31 +2833,13 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2757
2833
|
} catch (e) {
|
|
2758
2834
|
return {
|
|
2759
2835
|
path,
|
|
2760
|
-
error:
|
|
2836
|
+
error: getInternalRouterError(400)
|
|
2761
2837
|
};
|
|
2762
2838
|
}
|
|
2763
2839
|
|
|
2764
2840
|
return {
|
|
2765
2841
|
path: createPath(parsedPath)
|
|
2766
2842
|
};
|
|
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
2843
|
} // Filter out all routes below any caught error as they aren't going to
|
|
2786
2844
|
// render so we don't need to load them
|
|
2787
2845
|
|
|
@@ -2882,6 +2940,10 @@ function shouldRevalidateLoader(currentLocation, currentMatch, submission, locat
|
|
|
2882
2940
|
}
|
|
2883
2941
|
|
|
2884
2942
|
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest) {
|
|
2943
|
+
if (basename === void 0) {
|
|
2944
|
+
basename = "/";
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2885
2947
|
if (isStaticRequest === void 0) {
|
|
2886
2948
|
isStaticRequest = false;
|
|
2887
2949
|
}
|
|
@@ -2907,6 +2969,7 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2907
2969
|
request,
|
|
2908
2970
|
params: match.params
|
|
2909
2971
|
}), abortPromise]);
|
|
2972
|
+
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
2973
|
} catch (e) {
|
|
2911
2974
|
resultType = ResultType.error;
|
|
2912
2975
|
result = e;
|
|
@@ -2917,26 +2980,31 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2917
2980
|
if (result instanceof Response) {
|
|
2918
2981
|
let status = result.status; // Process redirects
|
|
2919
2982
|
|
|
2920
|
-
if (status
|
|
2983
|
+
if (redirectStatusCodes.has(status)) {
|
|
2921
2984
|
let location = result.headers.get("Location");
|
|
2922
|
-
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header"); //
|
|
2985
|
+
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
2986
|
|
|
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
|
|
2987
|
+
let external = createURL(location).origin !== createURL("/").origin; // Support relative routing in internal redirects
|
|
2929
2988
|
|
|
2930
|
-
if (
|
|
2931
|
-
let
|
|
2932
|
-
|
|
2933
|
-
|
|
2989
|
+
if (!external) {
|
|
2990
|
+
let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
|
|
2991
|
+
let routePathnames = getPathContributingMatches(activeMatches).map(match => match.pathnameBase);
|
|
2992
|
+
let requestPath = createURL(request.url).pathname;
|
|
2993
|
+
let resolvedLocation = resolveTo(location, routePathnames, requestPath);
|
|
2994
|
+
invariant(createPath(resolvedLocation), "Unable to resolve redirect location: " + location); // Prepend the basename to the redirect location if we have one
|
|
2995
|
+
|
|
2996
|
+
if (basename) {
|
|
2997
|
+
let path = resolvedLocation.pathname;
|
|
2998
|
+
resolvedLocation.pathname = path === "/" ? basename : joinPaths([basename, path]);
|
|
2999
|
+
}
|
|
2934
3000
|
|
|
2935
|
-
|
|
3001
|
+
location = createPath(resolvedLocation);
|
|
3002
|
+
} // Don't process redirects in the router during static requests requests.
|
|
2936
3003
|
// Instead, throw the Response and let the server handle it with an HTTP
|
|
2937
3004
|
// redirect. We also update the Location header in place in this flow so
|
|
2938
3005
|
// basename and relative routing is taken into account
|
|
2939
3006
|
|
|
3007
|
+
|
|
2940
3008
|
if (isStaticRequest) {
|
|
2941
3009
|
result.headers.set("Location", location);
|
|
2942
3010
|
throw result;
|
|
@@ -2946,7 +3014,8 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2946
3014
|
type: ResultType.redirect,
|
|
2947
3015
|
status,
|
|
2948
3016
|
location,
|
|
2949
|
-
revalidate: result.headers.get("X-Remix-Revalidate") !== null
|
|
3017
|
+
revalidate: result.headers.get("X-Remix-Revalidate") !== null,
|
|
3018
|
+
external
|
|
2950
3019
|
};
|
|
2951
3020
|
} // For SSR single-route requests, we want to hand Responses back directly
|
|
2952
3021
|
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
@@ -3174,10 +3243,10 @@ function findNearestBoundary(matches, routeId) {
|
|
|
3174
3243
|
return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0];
|
|
3175
3244
|
}
|
|
3176
3245
|
|
|
3177
|
-
function getShortCircuitMatches(routes
|
|
3246
|
+
function getShortCircuitMatches(routes) {
|
|
3178
3247
|
// Prefer a root layout route if present, otherwise shim in a route object
|
|
3179
3248
|
let route = routes.find(r => r.index || !r.path || r.path === "/") || {
|
|
3180
|
-
id: "__shim-
|
|
3249
|
+
id: "__shim-error-route__"
|
|
3181
3250
|
};
|
|
3182
3251
|
return {
|
|
3183
3252
|
matches: [{
|
|
@@ -3186,26 +3255,45 @@ function getShortCircuitMatches(routes, status, statusText) {
|
|
|
3186
3255
|
pathnameBase: "",
|
|
3187
3256
|
route
|
|
3188
3257
|
}],
|
|
3189
|
-
route
|
|
3190
|
-
error: new ErrorResponse(status, statusText, null)
|
|
3258
|
+
route
|
|
3191
3259
|
};
|
|
3192
3260
|
}
|
|
3193
3261
|
|
|
3194
|
-
function
|
|
3195
|
-
|
|
3196
|
-
|
|
3262
|
+
function getInternalRouterError(status, _temp) {
|
|
3263
|
+
let {
|
|
3264
|
+
pathname,
|
|
3265
|
+
routeId,
|
|
3266
|
+
method,
|
|
3267
|
+
message
|
|
3268
|
+
} = _temp === void 0 ? {} : _temp;
|
|
3269
|
+
let statusText = "Unknown Server Error";
|
|
3270
|
+
let errorMessage = "Unknown @remix-run/router error";
|
|
3271
|
+
|
|
3272
|
+
if (status === 400) {
|
|
3273
|
+
statusText = "Bad Request";
|
|
3274
|
+
|
|
3275
|
+
if (method && pathname && routeId) {
|
|
3276
|
+
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.";
|
|
3277
|
+
} else {
|
|
3278
|
+
errorMessage = "Cannot submit binary form data using GET";
|
|
3279
|
+
}
|
|
3280
|
+
} else if (status === 403) {
|
|
3281
|
+
statusText = "Forbidden";
|
|
3282
|
+
errorMessage = "Route \"" + routeId + "\" does not match URL \"" + pathname + "\"";
|
|
3283
|
+
} else if (status === 404) {
|
|
3284
|
+
statusText = "Not Found";
|
|
3285
|
+
errorMessage = "No route matches URL \"" + pathname + "\"";
|
|
3286
|
+
} else if (status === 405) {
|
|
3287
|
+
statusText = "Method Not Allowed";
|
|
3197
3288
|
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
}
|
|
3289
|
+
if (method && pathname && routeId) {
|
|
3290
|
+
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.";
|
|
3291
|
+
} else if (method) {
|
|
3292
|
+
errorMessage = "Invalid request method \"" + method.toUpperCase() + "\"";
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3201
3295
|
|
|
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
|
-
};
|
|
3296
|
+
return new ErrorResponse(status || 500, statusText, new Error(errorMessage), true);
|
|
3209
3297
|
} // Find any returned redirect errors, starting from the lowest match
|
|
3210
3298
|
|
|
3211
3299
|
|
|
@@ -3256,6 +3344,14 @@ function isQueryRouteResponse(obj) {
|
|
|
3256
3344
|
return obj && obj.response instanceof Response && (obj.type === ResultType.data || ResultType.error);
|
|
3257
3345
|
}
|
|
3258
3346
|
|
|
3347
|
+
function isValidMethod(method) {
|
|
3348
|
+
return validRequestMethods.has(method);
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
function isSubmissionMethod(method) {
|
|
3352
|
+
return validActionMethods.has(method);
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3259
3355
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|
|
3260
3356
|
for (let index = 0; index < results.length; index++) {
|
|
3261
3357
|
let result = results[index];
|