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