@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.cjs.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
|
*
|
|
@@ -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,60 @@ 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
|
+
if (replace) {
|
|
2199
|
+
window.location.replace(redirect.location);
|
|
2200
|
+
} else {
|
|
2201
|
+
window.location.assign(redirect.location);
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
return;
|
|
2205
|
+
} // There's no need to abort on redirects, since we don't detect the
|
|
2174
2206
|
// redirect until the action/loaders have settled
|
|
2175
2207
|
|
|
2208
|
+
|
|
2176
2209
|
pendingNavigationController = null;
|
|
2177
2210
|
let redirectHistoryAction = replace === true ? exports.Action.Replace : exports.Action.Push;
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2211
|
+
let {
|
|
2212
|
+
formMethod,
|
|
2213
|
+
formAction,
|
|
2214
|
+
formEncType,
|
|
2215
|
+
formData
|
|
2216
|
+
} = state.navigation; // If this was a 307/308 submission we want to preserve the HTTP method and
|
|
2217
|
+
// re-submit the POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2218
|
+
// redirected location
|
|
2219
|
+
|
|
2220
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod && isSubmissionMethod(formMethod) && formEncType && formData) {
|
|
2221
|
+
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2222
|
+
submission: {
|
|
2223
|
+
formMethod,
|
|
2224
|
+
formAction: redirect.location,
|
|
2225
|
+
formEncType,
|
|
2226
|
+
formData
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
} else {
|
|
2230
|
+
// Otherwise, we kick off a new loading navigation, preserving the
|
|
2231
|
+
// submission info for the duration of this navigation
|
|
2232
|
+
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2233
|
+
overrideNavigation: {
|
|
2234
|
+
state: "loading",
|
|
2235
|
+
location: redirectLocation,
|
|
2236
|
+
formMethod: formMethod || undefined,
|
|
2237
|
+
formAction: formAction || undefined,
|
|
2238
|
+
formEncType: formEncType || undefined,
|
|
2239
|
+
formData: formData || undefined
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2181
2243
|
}
|
|
2182
2244
|
|
|
2183
2245
|
async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
|
|
@@ -2381,6 +2443,7 @@ function createRouter(init) {
|
|
|
2381
2443
|
// Passthrough to history-aware createHref used by useHref so we get proper
|
|
2382
2444
|
// hash-aware URLs in DOM paths
|
|
2383
2445
|
createHref: to => init.history.createHref(to),
|
|
2446
|
+
encodeLocation: to => init.history.encodeLocation(to),
|
|
2384
2447
|
getFetcher,
|
|
2385
2448
|
deleteFetcher,
|
|
2386
2449
|
dispose,
|
|
@@ -2393,11 +2456,10 @@ function createRouter(init) {
|
|
|
2393
2456
|
//#region createStaticHandler
|
|
2394
2457
|
////////////////////////////////////////////////////////////////////////////////
|
|
2395
2458
|
|
|
2396
|
-
|
|
2397
|
-
const validRequestMethods = new Set(["GET", "HEAD", ...validActionMethods]);
|
|
2398
|
-
function unstable_createStaticHandler(routes) {
|
|
2459
|
+
function unstable_createStaticHandler(routes, opts) {
|
|
2399
2460
|
invariant(routes.length > 0, "You must provide a non-empty routes array to unstable_createStaticHandler");
|
|
2400
2461
|
let dataRoutes = convertRoutesToDataRoutes(routes);
|
|
2462
|
+
let basename = (opts ? opts.basename : null) || "/";
|
|
2401
2463
|
/**
|
|
2402
2464
|
* The query() method is intended for document requests, in which we want to
|
|
2403
2465
|
* call an optional action and potentially multiple loaders for all nested
|
|
@@ -2420,16 +2482,20 @@ function unstable_createStaticHandler(routes) {
|
|
|
2420
2482
|
|
|
2421
2483
|
async function query(request) {
|
|
2422
2484
|
let url = new URL(request.url);
|
|
2485
|
+
let method = request.method.toLowerCase();
|
|
2423
2486
|
let location = createLocation("", createPath(url), null, "default");
|
|
2424
|
-
let matches = matchRoutes(dataRoutes, location);
|
|
2487
|
+
let matches = matchRoutes(dataRoutes, location, basename); // SSR supports HEAD requests while SPA doesn't
|
|
2425
2488
|
|
|
2426
|
-
if (!
|
|
2489
|
+
if (!isValidMethod(method) && method !== "head") {
|
|
2490
|
+
let error = getInternalRouterError(405, {
|
|
2491
|
+
method
|
|
2492
|
+
});
|
|
2427
2493
|
let {
|
|
2428
2494
|
matches: methodNotAllowedMatches,
|
|
2429
|
-
route
|
|
2430
|
-
|
|
2431
|
-
} = getMethodNotAllowedMatches(dataRoutes);
|
|
2495
|
+
route
|
|
2496
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
2432
2497
|
return {
|
|
2498
|
+
basename,
|
|
2433
2499
|
location,
|
|
2434
2500
|
matches: methodNotAllowedMatches,
|
|
2435
2501
|
loaderData: {},
|
|
@@ -2442,12 +2508,15 @@ function unstable_createStaticHandler(routes) {
|
|
|
2442
2508
|
actionHeaders: {}
|
|
2443
2509
|
};
|
|
2444
2510
|
} else if (!matches) {
|
|
2511
|
+
let error = getInternalRouterError(404, {
|
|
2512
|
+
pathname: location.pathname
|
|
2513
|
+
});
|
|
2445
2514
|
let {
|
|
2446
2515
|
matches: notFoundMatches,
|
|
2447
|
-
route
|
|
2448
|
-
|
|
2449
|
-
} = getNotFoundMatches(dataRoutes);
|
|
2516
|
+
route
|
|
2517
|
+
} = getShortCircuitMatches(dataRoutes);
|
|
2450
2518
|
return {
|
|
2519
|
+
basename,
|
|
2451
2520
|
location,
|
|
2452
2521
|
matches: notFoundMatches,
|
|
2453
2522
|
loaderData: {},
|
|
@@ -2471,7 +2540,8 @@ function unstable_createStaticHandler(routes) {
|
|
|
2471
2540
|
|
|
2472
2541
|
|
|
2473
2542
|
return _extends({
|
|
2474
|
-
location
|
|
2543
|
+
location,
|
|
2544
|
+
basename
|
|
2475
2545
|
}, result);
|
|
2476
2546
|
}
|
|
2477
2547
|
/**
|
|
@@ -2487,35 +2557,42 @@ function unstable_createStaticHandler(routes) {
|
|
|
2487
2557
|
* can do proper boundary identification in Remix where a thrown Response
|
|
2488
2558
|
* must go to the Catch Boundary but a returned Response is happy-path.
|
|
2489
2559
|
*
|
|
2490
|
-
* One thing to note is that any Router-initiated
|
|
2491
|
-
*
|
|
2492
|
-
*
|
|
2560
|
+
* One thing to note is that any Router-initiated Errors that make sense
|
|
2561
|
+
* to associate with a status code will be thrown as an ErrorResponse
|
|
2562
|
+
* instance which include the raw Error, such that the calling context can
|
|
2563
|
+
* serialize the error as they see fit while including the proper response
|
|
2564
|
+
* code. Examples here are 404 and 405 errors that occur prior to reaching
|
|
2565
|
+
* any user-defined loaders.
|
|
2493
2566
|
*/
|
|
2494
2567
|
|
|
2495
2568
|
|
|
2496
2569
|
async function queryRoute(request, routeId) {
|
|
2497
2570
|
let url = new URL(request.url);
|
|
2571
|
+
let method = request.method.toLowerCase();
|
|
2498
2572
|
let location = createLocation("", createPath(url), null, "default");
|
|
2499
|
-
let matches = matchRoutes(dataRoutes, location);
|
|
2573
|
+
let matches = matchRoutes(dataRoutes, location, basename); // SSR supports HEAD requests while SPA doesn't
|
|
2500
2574
|
|
|
2501
|
-
if (!
|
|
2502
|
-
throw
|
|
2503
|
-
|
|
2504
|
-
statusText: "Method Not Allowed"
|
|
2575
|
+
if (!isValidMethod(method) && method !== "head") {
|
|
2576
|
+
throw getInternalRouterError(405, {
|
|
2577
|
+
method
|
|
2505
2578
|
});
|
|
2506
2579
|
} else if (!matches) {
|
|
2507
|
-
throw
|
|
2508
|
-
|
|
2509
|
-
statusText: "Not Found"
|
|
2580
|
+
throw getInternalRouterError(404, {
|
|
2581
|
+
pathname: location.pathname
|
|
2510
2582
|
});
|
|
2511
2583
|
}
|
|
2512
2584
|
|
|
2513
2585
|
let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location);
|
|
2514
2586
|
|
|
2515
|
-
if (!match) {
|
|
2516
|
-
throw
|
|
2517
|
-
|
|
2518
|
-
|
|
2587
|
+
if (routeId && !match) {
|
|
2588
|
+
throw getInternalRouterError(403, {
|
|
2589
|
+
pathname: location.pathname,
|
|
2590
|
+
routeId
|
|
2591
|
+
});
|
|
2592
|
+
} else if (!match) {
|
|
2593
|
+
// This should never hit I don't think?
|
|
2594
|
+
throw getInternalRouterError(404, {
|
|
2595
|
+
pathname: location.pathname
|
|
2519
2596
|
});
|
|
2520
2597
|
}
|
|
2521
2598
|
|
|
@@ -2544,7 +2621,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2544
2621
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2545
2622
|
|
|
2546
2623
|
try {
|
|
2547
|
-
if (
|
|
2624
|
+
if (isSubmissionMethod(request.method.toLowerCase())) {
|
|
2548
2625
|
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), routeMatch != null);
|
|
2549
2626
|
return result;
|
|
2550
2627
|
}
|
|
@@ -2580,17 +2657,22 @@ function unstable_createStaticHandler(routes) {
|
|
|
2580
2657
|
let result;
|
|
2581
2658
|
|
|
2582
2659
|
if (!actionMatch.route.action) {
|
|
2660
|
+
let error = getInternalRouterError(405, {
|
|
2661
|
+
method: request.method,
|
|
2662
|
+
pathname: createURL(request.url).pathname,
|
|
2663
|
+
routeId: actionMatch.route.id
|
|
2664
|
+
});
|
|
2665
|
+
|
|
2583
2666
|
if (isRouteRequest) {
|
|
2584
|
-
throw
|
|
2585
|
-
status: 405,
|
|
2586
|
-
statusText: "Method Not Allowed"
|
|
2587
|
-
});
|
|
2667
|
+
throw error;
|
|
2588
2668
|
}
|
|
2589
2669
|
|
|
2590
|
-
result =
|
|
2670
|
+
result = {
|
|
2671
|
+
type: ResultType.error,
|
|
2672
|
+
error
|
|
2673
|
+
};
|
|
2591
2674
|
} else {
|
|
2592
|
-
result = await callLoaderOrAction("action", request, actionMatch, matches,
|
|
2593
|
-
true, isRouteRequest);
|
|
2675
|
+
result = await callLoaderOrAction("action", request, actionMatch, matches, basename, true, isRouteRequest);
|
|
2594
2676
|
|
|
2595
2677
|
if (request.signal.aborted) {
|
|
2596
2678
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2619,20 +2701,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2619
2701
|
// Note: This should only be non-Response values if we get here, since
|
|
2620
2702
|
// isRouteRequest should throw any Response received in callLoaderOrAction
|
|
2621
2703
|
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
|
-
};
|
|
2704
|
+
throw result.error;
|
|
2636
2705
|
}
|
|
2637
2706
|
|
|
2638
2707
|
return {
|
|
@@ -2681,9 +2750,18 @@ function unstable_createStaticHandler(routes) {
|
|
|
2681
2750
|
}
|
|
2682
2751
|
|
|
2683
2752
|
async function loadRouteData(request, matches, routeMatch, pendingActionError) {
|
|
2684
|
-
let isRouteRequest = routeMatch != null;
|
|
2753
|
+
let isRouteRequest = routeMatch != null; // Short circuit if we have no loaders to run (queryRoute())
|
|
2754
|
+
|
|
2755
|
+
if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader)) {
|
|
2756
|
+
throw getInternalRouterError(400, {
|
|
2757
|
+
method: request.method,
|
|
2758
|
+
pathname: createURL(request.url).pathname,
|
|
2759
|
+
routeId: routeMatch == null ? void 0 : routeMatch.route.id
|
|
2760
|
+
});
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2685
2763
|
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
|
|
2764
|
+
let matchesToLoad = requestMatches.filter(m => m.route.loader); // Short circuit if we have no loaders to run (query())
|
|
2687
2765
|
|
|
2688
2766
|
if (matchesToLoad.length === 0) {
|
|
2689
2767
|
return {
|
|
@@ -2695,8 +2773,7 @@ function unstable_createStaticHandler(routes) {
|
|
|
2695
2773
|
};
|
|
2696
2774
|
}
|
|
2697
2775
|
|
|
2698
|
-
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches,
|
|
2699
|
-
true, isRouteRequest))]);
|
|
2776
|
+
let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, basename, true, isRouteRequest))]);
|
|
2700
2777
|
|
|
2701
2778
|
if (request.signal.aborted) {
|
|
2702
2779
|
let method = isRouteRequest ? "queryRoute" : "query";
|
|
@@ -2717,14 +2794,6 @@ function unstable_createStaticHandler(routes) {
|
|
|
2717
2794
|
});
|
|
2718
2795
|
}
|
|
2719
2796
|
|
|
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
2797
|
return {
|
|
2729
2798
|
dataRoutes,
|
|
2730
2799
|
query,
|
|
@@ -2749,9 +2818,14 @@ function getStaticContextFromError(routes, context, error) {
|
|
|
2749
2818
|
});
|
|
2750
2819
|
|
|
2751
2820
|
return newContext;
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
function isSubmissionNavigation(opts) {
|
|
2824
|
+
return opts != null && "formData" in opts;
|
|
2752
2825
|
} // Normalize navigation options by converting formMethod=GET formData objects to
|
|
2753
2826
|
// URLSearchParams so they behave identically to links with query params
|
|
2754
2827
|
|
|
2828
|
+
|
|
2755
2829
|
function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
2756
2830
|
if (isFetcher === void 0) {
|
|
2757
2831
|
isFetcher = false;
|
|
@@ -2759,14 +2833,23 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2759
2833
|
|
|
2760
2834
|
let path = typeof to === "string" ? to : createPath(to); // Return location verbatim on non-submission navigations
|
|
2761
2835
|
|
|
2762
|
-
if (!opts || !(
|
|
2836
|
+
if (!opts || !isSubmissionNavigation(opts)) {
|
|
2763
2837
|
return {
|
|
2764
2838
|
path
|
|
2765
2839
|
};
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
if (opts.formMethod && !isValidMethod(opts.formMethod)) {
|
|
2843
|
+
return {
|
|
2844
|
+
path,
|
|
2845
|
+
error: getInternalRouterError(405, {
|
|
2846
|
+
method: opts.formMethod
|
|
2847
|
+
})
|
|
2848
|
+
};
|
|
2766
2849
|
} // Create a Submission on non-GET navigations
|
|
2767
2850
|
|
|
2768
2851
|
|
|
2769
|
-
if (opts.formMethod
|
|
2852
|
+
if (opts.formMethod && isSubmissionMethod(opts.formMethod)) {
|
|
2770
2853
|
return {
|
|
2771
2854
|
path,
|
|
2772
2855
|
submission: {
|
|
@@ -2776,13 +2859,6 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2776
2859
|
formData: opts.formData
|
|
2777
2860
|
}
|
|
2778
2861
|
};
|
|
2779
|
-
} // No formData to flatten for GET submission
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
if (!opts.formData) {
|
|
2783
|
-
return {
|
|
2784
|
-
path
|
|
2785
|
-
};
|
|
2786
2862
|
} // Flatten submission onto URLSearchParams for GET submissions
|
|
2787
2863
|
|
|
2788
2864
|
|
|
@@ -2801,31 +2877,13 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2801
2877
|
} catch (e) {
|
|
2802
2878
|
return {
|
|
2803
2879
|
path,
|
|
2804
|
-
error:
|
|
2880
|
+
error: getInternalRouterError(400)
|
|
2805
2881
|
};
|
|
2806
2882
|
}
|
|
2807
2883
|
|
|
2808
2884
|
return {
|
|
2809
2885
|
path: createPath(parsedPath)
|
|
2810
2886
|
};
|
|
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
2887
|
} // Filter out all routes below any caught error as they aren't going to
|
|
2830
2888
|
// render so we don't need to load them
|
|
2831
2889
|
|
|
@@ -2926,6 +2984,10 @@ function shouldRevalidateLoader(currentLocation, currentMatch, submission, locat
|
|
|
2926
2984
|
}
|
|
2927
2985
|
|
|
2928
2986
|
async function callLoaderOrAction(type, request, match, matches, basename, isStaticRequest, isRouteRequest) {
|
|
2987
|
+
if (basename === void 0) {
|
|
2988
|
+
basename = "/";
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2929
2991
|
if (isStaticRequest === void 0) {
|
|
2930
2992
|
isStaticRequest = false;
|
|
2931
2993
|
}
|
|
@@ -2951,6 +3013,7 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2951
3013
|
request,
|
|
2952
3014
|
params: match.params
|
|
2953
3015
|
}), abortPromise]);
|
|
3016
|
+
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
3017
|
} catch (e) {
|
|
2955
3018
|
resultType = ResultType.error;
|
|
2956
3019
|
result = e;
|
|
@@ -2961,26 +3024,31 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2961
3024
|
if (result instanceof Response) {
|
|
2962
3025
|
let status = result.status; // Process redirects
|
|
2963
3026
|
|
|
2964
|
-
if (status
|
|
3027
|
+
if (redirectStatusCodes.has(status)) {
|
|
2965
3028
|
let location = result.headers.get("Location");
|
|
2966
|
-
invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header"); //
|
|
3029
|
+
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
3030
|
|
|
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
|
|
3031
|
+
let external = createURL(location).origin !== createURL("/").origin; // Support relative routing in internal redirects
|
|
2973
3032
|
|
|
2974
|
-
if (
|
|
2975
|
-
let
|
|
2976
|
-
|
|
2977
|
-
|
|
3033
|
+
if (!external) {
|
|
3034
|
+
let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
|
|
3035
|
+
let routePathnames = getPathContributingMatches(activeMatches).map(match => match.pathnameBase);
|
|
3036
|
+
let requestPath = createURL(request.url).pathname;
|
|
3037
|
+
let resolvedLocation = resolveTo(location, routePathnames, requestPath);
|
|
3038
|
+
invariant(createPath(resolvedLocation), "Unable to resolve redirect location: " + location); // Prepend the basename to the redirect location if we have one
|
|
3039
|
+
|
|
3040
|
+
if (basename) {
|
|
3041
|
+
let path = resolvedLocation.pathname;
|
|
3042
|
+
resolvedLocation.pathname = path === "/" ? basename : joinPaths([basename, path]);
|
|
3043
|
+
}
|
|
2978
3044
|
|
|
2979
|
-
|
|
3045
|
+
location = createPath(resolvedLocation);
|
|
3046
|
+
} // Don't process redirects in the router during static requests requests.
|
|
2980
3047
|
// Instead, throw the Response and let the server handle it with an HTTP
|
|
2981
3048
|
// redirect. We also update the Location header in place in this flow so
|
|
2982
3049
|
// basename and relative routing is taken into account
|
|
2983
3050
|
|
|
3051
|
+
|
|
2984
3052
|
if (isStaticRequest) {
|
|
2985
3053
|
result.headers.set("Location", location);
|
|
2986
3054
|
throw result;
|
|
@@ -2990,7 +3058,8 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
|
|
|
2990
3058
|
type: ResultType.redirect,
|
|
2991
3059
|
status,
|
|
2992
3060
|
location,
|
|
2993
|
-
revalidate: result.headers.get("X-Remix-Revalidate") !== null
|
|
3061
|
+
revalidate: result.headers.get("X-Remix-Revalidate") !== null,
|
|
3062
|
+
external
|
|
2994
3063
|
};
|
|
2995
3064
|
} // For SSR single-route requests, we want to hand Responses back directly
|
|
2996
3065
|
// without unwrapping. We do this with the QueryRouteResponse wrapper
|
|
@@ -3218,10 +3287,10 @@ function findNearestBoundary(matches, routeId) {
|
|
|
3218
3287
|
return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0];
|
|
3219
3288
|
}
|
|
3220
3289
|
|
|
3221
|
-
function getShortCircuitMatches(routes
|
|
3290
|
+
function getShortCircuitMatches(routes) {
|
|
3222
3291
|
// Prefer a root layout route if present, otherwise shim in a route object
|
|
3223
3292
|
let route = routes.find(r => r.index || !r.path || r.path === "/") || {
|
|
3224
|
-
id: "__shim-
|
|
3293
|
+
id: "__shim-error-route__"
|
|
3225
3294
|
};
|
|
3226
3295
|
return {
|
|
3227
3296
|
matches: [{
|
|
@@ -3230,26 +3299,45 @@ function getShortCircuitMatches(routes, status, statusText) {
|
|
|
3230
3299
|
pathnameBase: "",
|
|
3231
3300
|
route
|
|
3232
3301
|
}],
|
|
3233
|
-
route
|
|
3234
|
-
error: new ErrorResponse(status, statusText, null)
|
|
3302
|
+
route
|
|
3235
3303
|
};
|
|
3236
3304
|
}
|
|
3237
3305
|
|
|
3238
|
-
function
|
|
3239
|
-
|
|
3240
|
-
|
|
3306
|
+
function getInternalRouterError(status, _temp) {
|
|
3307
|
+
let {
|
|
3308
|
+
pathname,
|
|
3309
|
+
routeId,
|
|
3310
|
+
method,
|
|
3311
|
+
message
|
|
3312
|
+
} = _temp === void 0 ? {} : _temp;
|
|
3313
|
+
let statusText = "Unknown Server Error";
|
|
3314
|
+
let errorMessage = "Unknown @remix-run/router error";
|
|
3315
|
+
|
|
3316
|
+
if (status === 400) {
|
|
3317
|
+
statusText = "Bad Request";
|
|
3318
|
+
|
|
3319
|
+
if (method && pathname && routeId) {
|
|
3320
|
+
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.";
|
|
3321
|
+
} else {
|
|
3322
|
+
errorMessage = "Cannot submit binary form data using GET";
|
|
3323
|
+
}
|
|
3324
|
+
} else if (status === 403) {
|
|
3325
|
+
statusText = "Forbidden";
|
|
3326
|
+
errorMessage = "Route \"" + routeId + "\" does not match URL \"" + pathname + "\"";
|
|
3327
|
+
} else if (status === 404) {
|
|
3328
|
+
statusText = "Not Found";
|
|
3329
|
+
errorMessage = "No route matches URL \"" + pathname + "\"";
|
|
3330
|
+
} else if (status === 405) {
|
|
3331
|
+
statusText = "Method Not Allowed";
|
|
3241
3332
|
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
}
|
|
3333
|
+
if (method && pathname && routeId) {
|
|
3334
|
+
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.";
|
|
3335
|
+
} else if (method) {
|
|
3336
|
+
errorMessage = "Invalid request method \"" + method.toUpperCase() + "\"";
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3245
3339
|
|
|
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
|
-
};
|
|
3340
|
+
return new ErrorResponse(status || 500, statusText, new Error(errorMessage), true);
|
|
3253
3341
|
} // Find any returned redirect errors, starting from the lowest match
|
|
3254
3342
|
|
|
3255
3343
|
|
|
@@ -3300,6 +3388,14 @@ function isQueryRouteResponse(obj) {
|
|
|
3300
3388
|
return obj && obj.response instanceof Response && (obj.type === ResultType.data || ResultType.error);
|
|
3301
3389
|
}
|
|
3302
3390
|
|
|
3391
|
+
function isValidMethod(method) {
|
|
3392
|
+
return validRequestMethods.has(method);
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3395
|
+
function isSubmissionMethod(method) {
|
|
3396
|
+
return validActionMethods.has(method);
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3303
3399
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|
|
3304
3400
|
for (let index = 0; index < results.length; index++) {
|
|
3305
3401
|
let result = results[index];
|