@remix-run/router 1.0.5 → 1.1.0-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 +55 -0
- package/dist/router.cjs.js +126 -39
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.js +126 -39
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +126 -39
- 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 +5 -5
- package/package.json +1 -1
- package/router.ts +54 -30
- package/utils.ts +105 -11
package/dist/router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.0.
|
|
2
|
+
* @remix-run/router v1.1.0-pre.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -573,9 +573,9 @@ function flattenRoutes(routes, branches, parentsMeta, parentPath) {
|
|
|
573
573
|
parentPath = "";
|
|
574
574
|
}
|
|
575
575
|
|
|
576
|
-
|
|
576
|
+
let flattenRoute = (route, index, relativePath) => {
|
|
577
577
|
let meta = {
|
|
578
|
-
relativePath: route.path || "",
|
|
578
|
+
relativePath: relativePath === undefined ? route.path || "" : relativePath,
|
|
579
579
|
caseSensitive: route.caseSensitive === true,
|
|
580
580
|
childrenIndex: index,
|
|
581
581
|
route
|
|
@@ -609,9 +609,71 @@ function flattenRoutes(routes, branches, parentsMeta, parentPath) {
|
|
|
609
609
|
score: computeScore(path, route.index),
|
|
610
610
|
routesMeta
|
|
611
611
|
});
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
routes.forEach((route, index) => {
|
|
615
|
+
var _route$path;
|
|
616
|
+
|
|
617
|
+
// coarse-grain check for optional params
|
|
618
|
+
if (route.path === "" || !((_route$path = route.path) != null && _route$path.includes("?"))) {
|
|
619
|
+
flattenRoute(route, index);
|
|
620
|
+
} else {
|
|
621
|
+
for (let exploded of explodeOptionalSegments(route.path)) {
|
|
622
|
+
flattenRoute(route, index, exploded);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
612
625
|
});
|
|
613
626
|
return branches;
|
|
614
627
|
}
|
|
628
|
+
/**
|
|
629
|
+
* Computes all combinations of optional path segments for a given path,
|
|
630
|
+
* excluding combinations that are ambiguous and of lower priority.
|
|
631
|
+
*
|
|
632
|
+
* For example, `/one/:two?/three/:four?/:five?` explodes to:
|
|
633
|
+
* - `/one/three`
|
|
634
|
+
* - `/one/:two/three`
|
|
635
|
+
* - `/one/three/:four`
|
|
636
|
+
* - `/one/three/:five`
|
|
637
|
+
* - `/one/:two/three/:four`
|
|
638
|
+
* - `/one/:two/three/:five`
|
|
639
|
+
* - `/one/three/:four/:five`
|
|
640
|
+
* - `/one/:two/three/:four/:five`
|
|
641
|
+
*/
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
function explodeOptionalSegments(path) {
|
|
645
|
+
let segments = path.split("/");
|
|
646
|
+
if (segments.length === 0) return [];
|
|
647
|
+
let [first, ...rest] = segments; // Optional path segments are denoted by a trailing `?`
|
|
648
|
+
|
|
649
|
+
let isOptional = first.endsWith("?"); // Compute the corresponding required segment: `foo?` -> `foo`
|
|
650
|
+
|
|
651
|
+
let required = first.replace(/\?$/, "");
|
|
652
|
+
|
|
653
|
+
if (rest.length === 0) {
|
|
654
|
+
// Intepret empty string as omitting an optional segment
|
|
655
|
+
// `["one", "", "three"]` corresponds to omitting `:two` from `/one/:two?/three` -> `/one/three`
|
|
656
|
+
return isOptional ? [required, ""] : [required];
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
let restExploded = explodeOptionalSegments(rest.join("/"));
|
|
660
|
+
let result = []; // All child paths with the prefix. Do this for all children before the
|
|
661
|
+
// optional version for all children so we get consistent ordering where the
|
|
662
|
+
// parent optional aspect is preferred as required. Otherwise, we can get
|
|
663
|
+
// child sections interspersed where deeper optional segments are higher than
|
|
664
|
+
// parent optional segments, where for example, /:two would explodes _earlier_
|
|
665
|
+
// then /:one. By always including the parent as required _for all children_
|
|
666
|
+
// first, we avoid this issue
|
|
667
|
+
|
|
668
|
+
result.push(...restExploded.map(subpath => subpath === "" ? required : [required, subpath].join("/"))); // Then if this is an optional value, add all child versions without
|
|
669
|
+
|
|
670
|
+
if (isOptional) {
|
|
671
|
+
result.push(...restExploded);
|
|
672
|
+
} // for absolute paths, ensure `/` instead of empty segment
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
return result.map(exploded => path.startsWith("/") && exploded === "" ? "/" : exploded);
|
|
676
|
+
}
|
|
615
677
|
|
|
616
678
|
function rankRouteBranches(branches) {
|
|
617
679
|
branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first
|
|
@@ -695,14 +757,24 @@ function matchRouteBranch(branch, pathname) {
|
|
|
695
757
|
*/
|
|
696
758
|
|
|
697
759
|
|
|
698
|
-
function generatePath(
|
|
760
|
+
function generatePath(originalPath, params) {
|
|
699
761
|
if (params === void 0) {
|
|
700
762
|
params = {};
|
|
701
763
|
}
|
|
702
764
|
|
|
703
|
-
|
|
765
|
+
let path = originalPath;
|
|
766
|
+
|
|
767
|
+
if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
|
|
768
|
+
warning(false, "Route path \"" + path + "\" will be treated as if it were " + ("\"" + path.replace(/\*$/, "/*") + "\" because the `*` character must ") + "always follow a `/` in the pattern. To get rid of this warning, " + ("please change the route path to \"" + path.replace(/\*$/, "/*") + "\"."));
|
|
769
|
+
path = path.replace(/\*$/, "/*");
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return path.replace(/^:(\w+)/g, (_, key) => {
|
|
704
773
|
invariant(params[key] != null, "Missing \":" + key + "\" param");
|
|
705
774
|
return params[key];
|
|
775
|
+
}).replace(/\/:(\w+)/g, (_, key) => {
|
|
776
|
+
invariant(params[key] != null, "Missing \":" + key + "\" param");
|
|
777
|
+
return "/" + params[key];
|
|
706
778
|
}).replace(/(\/?)\*/, (_, prefix, __, str) => {
|
|
707
779
|
const star = "*";
|
|
708
780
|
|
|
@@ -771,9 +843,9 @@ function compilePath(path, caseSensitive, end) {
|
|
|
771
843
|
let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
|
|
772
844
|
.replace(/^\/*/, "/") // Make sure it has a leading /
|
|
773
845
|
.replace(/[\\.*+^$?{}|()[\]]/g, "\\$&") // Escape special regex chars
|
|
774
|
-
.replace(
|
|
846
|
+
.replace(/\/:(\w+)/g, (_, paramName) => {
|
|
775
847
|
paramNames.push(paramName);
|
|
776
|
-
return "([^\\/]+)";
|
|
848
|
+
return "/([^\\/]+)";
|
|
777
849
|
});
|
|
778
850
|
|
|
779
851
|
if (path.endsWith("*")) {
|
|
@@ -1247,9 +1319,9 @@ function isRouteErrorResponse(e) {
|
|
|
1247
1319
|
return e instanceof ErrorResponse;
|
|
1248
1320
|
}
|
|
1249
1321
|
|
|
1250
|
-
const
|
|
1251
|
-
const
|
|
1252
|
-
const validRequestMethodsArr = ["get", ...
|
|
1322
|
+
const validMutationMethodsArr = ["post", "put", "patch", "delete"];
|
|
1323
|
+
const validMutationMethods = new Set(validMutationMethodsArr);
|
|
1324
|
+
const validRequestMethodsArr = ["get", ...validMutationMethodsArr];
|
|
1253
1325
|
const validRequestMethods = new Set(validRequestMethodsArr);
|
|
1254
1326
|
const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
|
|
1255
1327
|
const redirectPreserveMethodStatusCodes = new Set([307, 308]);
|
|
@@ -1486,7 +1558,7 @@ function createRouter(init) {
|
|
|
1486
1558
|
// without having to touch history
|
|
1487
1559
|
|
|
1488
1560
|
location = _extends({}, location, init.history.encodeLocation(location));
|
|
1489
|
-
let historyAction = (opts && opts.replace) === true || submission != null ? Action.Replace : Action.Push;
|
|
1561
|
+
let historyAction = (opts && opts.replace) === true || submission != null && isMutationMethod(submission.formMethod) ? Action.Replace : Action.Push;
|
|
1490
1562
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
1491
1563
|
return await startNavigation(historyAction, location, {
|
|
1492
1564
|
submission,
|
|
@@ -1590,7 +1662,7 @@ function createRouter(init) {
|
|
|
1590
1662
|
pendingError = {
|
|
1591
1663
|
[findNearestBoundary(matches).route.id]: opts.pendingError
|
|
1592
1664
|
};
|
|
1593
|
-
} else if (opts && opts.submission) {
|
|
1665
|
+
} else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
|
|
1594
1666
|
// Call action if we received an action submission
|
|
1595
1667
|
let actionOutput = await handleAction(request, location, opts.submission, matches, {
|
|
1596
1668
|
replace: opts.replace
|
|
@@ -1717,14 +1789,15 @@ function createRouter(init) {
|
|
|
1717
1789
|
let loadingNavigation = overrideNavigation;
|
|
1718
1790
|
|
|
1719
1791
|
if (!loadingNavigation) {
|
|
1720
|
-
let navigation = {
|
|
1792
|
+
let navigation = _extends({
|
|
1721
1793
|
state: "loading",
|
|
1722
1794
|
location,
|
|
1723
1795
|
formMethod: undefined,
|
|
1724
1796
|
formAction: undefined,
|
|
1725
1797
|
formEncType: undefined,
|
|
1726
1798
|
formData: undefined
|
|
1727
|
-
};
|
|
1799
|
+
}, submission);
|
|
1800
|
+
|
|
1728
1801
|
loadingNavigation = navigation;
|
|
1729
1802
|
}
|
|
1730
1803
|
|
|
@@ -1859,7 +1932,7 @@ function createRouter(init) {
|
|
|
1859
1932
|
} = normalizeNavigateOptions(href, opts, true);
|
|
1860
1933
|
let match = getTargetMatch(matches, path);
|
|
1861
1934
|
|
|
1862
|
-
if (submission) {
|
|
1935
|
+
if (submission && isMutationMethod(submission.formMethod)) {
|
|
1863
1936
|
handleFetcherAction(key, routeId, path, match, matches, submission);
|
|
1864
1937
|
return;
|
|
1865
1938
|
} // Store off the match so we can call it's shouldRevalidate on subsequent
|
|
@@ -1867,7 +1940,7 @@ function createRouter(init) {
|
|
|
1867
1940
|
|
|
1868
1941
|
|
|
1869
1942
|
fetchLoadMatches.set(key, [path, match, matches]);
|
|
1870
|
-
handleFetcherLoader(key, routeId, path, match, matches);
|
|
1943
|
+
handleFetcherLoader(key, routeId, path, match, matches, submission);
|
|
1871
1944
|
} // Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
1872
1945
|
// errors, and revalidation
|
|
1873
1946
|
|
|
@@ -2048,17 +2121,19 @@ function createRouter(init) {
|
|
|
2048
2121
|
} // Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
2049
2122
|
|
|
2050
2123
|
|
|
2051
|
-
async function handleFetcherLoader(key, routeId, path, match, matches) {
|
|
2124
|
+
async function handleFetcherLoader(key, routeId, path, match, matches, submission) {
|
|
2052
2125
|
let existingFetcher = state.fetchers.get(key); // Put this fetcher into it's loading state
|
|
2053
2126
|
|
|
2054
|
-
let loadingFetcher = {
|
|
2127
|
+
let loadingFetcher = _extends({
|
|
2055
2128
|
state: "loading",
|
|
2056
2129
|
formMethod: undefined,
|
|
2057
2130
|
formAction: undefined,
|
|
2058
2131
|
formEncType: undefined,
|
|
2059
|
-
formData: undefined
|
|
2132
|
+
formData: undefined
|
|
2133
|
+
}, submission, {
|
|
2060
2134
|
data: existingFetcher && existingFetcher.data
|
|
2061
|
-
};
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2062
2137
|
state.fetchers.set(key, loadingFetcher);
|
|
2063
2138
|
updateState({
|
|
2064
2139
|
fetchers: new Map(state.fetchers)
|
|
@@ -2178,10 +2253,10 @@ function createRouter(init) {
|
|
|
2178
2253
|
formEncType,
|
|
2179
2254
|
formData
|
|
2180
2255
|
} = state.navigation; // If this was a 307/308 submission we want to preserve the HTTP method and
|
|
2181
|
-
// re-submit the POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2256
|
+
// re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2182
2257
|
// redirected location
|
|
2183
2258
|
|
|
2184
|
-
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod &&
|
|
2259
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod && isMutationMethod(formMethod) && formEncType && formData) {
|
|
2185
2260
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2186
2261
|
submission: {
|
|
2187
2262
|
formMethod,
|
|
@@ -2592,7 +2667,7 @@ function unstable_createStaticHandler(routes, opts) {
|
|
|
2592
2667
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2593
2668
|
|
|
2594
2669
|
try {
|
|
2595
|
-
if (
|
|
2670
|
+
if (isMutationMethod(request.method.toLowerCase())) {
|
|
2596
2671
|
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), requestContext, routeMatch != null);
|
|
2597
2672
|
return result;
|
|
2598
2673
|
}
|
|
@@ -2709,6 +2784,8 @@ function unstable_createStaticHandler(routes, opts) {
|
|
|
2709
2784
|
|
|
2710
2785
|
|
|
2711
2786
|
let loaderRequest = new Request(request.url, {
|
|
2787
|
+
headers: request.headers,
|
|
2788
|
+
redirect: request.redirect,
|
|
2712
2789
|
signal: request.signal
|
|
2713
2790
|
});
|
|
2714
2791
|
let context = await loadRouteData(loaderRequest, matches, requestContext);
|
|
@@ -2824,16 +2901,22 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2824
2901
|
} // Create a Submission on non-GET navigations
|
|
2825
2902
|
|
|
2826
2903
|
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
}
|
|
2904
|
+
let submission;
|
|
2905
|
+
|
|
2906
|
+
if (opts.formData) {
|
|
2907
|
+
submission = {
|
|
2908
|
+
formMethod: opts.formMethod || "get",
|
|
2909
|
+
formAction: stripHashFromPath(path),
|
|
2910
|
+
formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
|
|
2911
|
+
formData: opts.formData
|
|
2836
2912
|
};
|
|
2913
|
+
|
|
2914
|
+
if (isMutationMethod(submission.formMethod)) {
|
|
2915
|
+
return {
|
|
2916
|
+
path,
|
|
2917
|
+
submission
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2837
2920
|
} // Flatten submission onto URLSearchParams for GET submissions
|
|
2838
2921
|
|
|
2839
2922
|
|
|
@@ -2857,7 +2940,8 @@ function normalizeNavigateOptions(to, opts, isFetcher) {
|
|
|
2857
2940
|
}
|
|
2858
2941
|
|
|
2859
2942
|
return {
|
|
2860
|
-
path: createPath(parsedPath)
|
|
2943
|
+
path: createPath(parsedPath),
|
|
2944
|
+
submission
|
|
2861
2945
|
};
|
|
2862
2946
|
} // Filter out all routes below any caught error as they aren't going to
|
|
2863
2947
|
// render so we don't need to load them
|
|
@@ -3101,7 +3185,7 @@ function createClientSideRequest(location, signal, submission) {
|
|
|
3101
3185
|
signal
|
|
3102
3186
|
};
|
|
3103
3187
|
|
|
3104
|
-
if (submission) {
|
|
3188
|
+
if (submission && isMutationMethod(submission.formMethod)) {
|
|
3105
3189
|
let {
|
|
3106
3190
|
formMethod,
|
|
3107
3191
|
formEncType,
|
|
@@ -3151,11 +3235,14 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
|
|
|
3151
3235
|
pendingError = undefined;
|
|
3152
3236
|
}
|
|
3153
3237
|
|
|
3154
|
-
errors =
|
|
3155
|
-
|
|
3156
|
-
|
|
3238
|
+
errors = errors || {}; // Prefer higher error values if lower errors bubble to the same boundary
|
|
3239
|
+
|
|
3240
|
+
if (errors[boundaryMatch.route.id] == null) {
|
|
3241
|
+
errors[boundaryMatch.route.id] = error;
|
|
3242
|
+
} // Once we find our first (highest) error, we set the status code and
|
|
3157
3243
|
// prevent deeper status codes from overriding
|
|
3158
3244
|
|
|
3245
|
+
|
|
3159
3246
|
if (!foundError) {
|
|
3160
3247
|
foundError = true;
|
|
3161
3248
|
statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
|
|
@@ -3371,8 +3458,8 @@ function isValidMethod(method) {
|
|
|
3371
3458
|
return validRequestMethods.has(method);
|
|
3372
3459
|
}
|
|
3373
3460
|
|
|
3374
|
-
function
|
|
3375
|
-
return
|
|
3461
|
+
function isMutationMethod(method) {
|
|
3462
|
+
return validMutationMethods.has(method);
|
|
3376
3463
|
}
|
|
3377
3464
|
|
|
3378
3465
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|