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