@remix-run/router 1.0.5 → 1.1.0-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 +49 -0
- package/dist/router.cjs.js +122 -39
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.js +122 -39
- package/dist/router.js.map +1 -1
- package/dist/router.umd.js +122 -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 +95 -11
package/dist/router.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @remix-run/router v1.0.
|
|
2
|
+
* @remix-run/router v1.1.0-pre.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -613,9 +613,9 @@
|
|
|
613
613
|
parentPath = "";
|
|
614
614
|
}
|
|
615
615
|
|
|
616
|
-
|
|
616
|
+
let flattenRoute = (route, index, relativePath) => {
|
|
617
617
|
let meta = {
|
|
618
|
-
relativePath: route.path || "",
|
|
618
|
+
relativePath: relativePath === undefined ? route.path || "" : relativePath,
|
|
619
619
|
caseSensitive: route.caseSensitive === true,
|
|
620
620
|
childrenIndex: index,
|
|
621
621
|
route
|
|
@@ -649,9 +649,67 @@
|
|
|
649
649
|
score: computeScore(path, route.index),
|
|
650
650
|
routesMeta
|
|
651
651
|
});
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
routes.forEach((route, index) => {
|
|
655
|
+
var _route$path;
|
|
656
|
+
|
|
657
|
+
// coarse-grain check for optional params
|
|
658
|
+
if (route.path === "" || !((_route$path = route.path) != null && _route$path.includes("?"))) {
|
|
659
|
+
flattenRoute(route, index);
|
|
660
|
+
} else {
|
|
661
|
+
for (let exploded of explodeOptionalSegments(route.path)) {
|
|
662
|
+
flattenRoute(route, index, exploded);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
652
665
|
});
|
|
653
666
|
return branches;
|
|
654
667
|
}
|
|
668
|
+
/**
|
|
669
|
+
* Computes all combinations of optional path segments for a given path,
|
|
670
|
+
* excluding combinations that are ambiguous and of lower priority.
|
|
671
|
+
*
|
|
672
|
+
* For example, `/one/:two?/three/:four?/:five?` explodes to:
|
|
673
|
+
* - `/one/three`
|
|
674
|
+
* - `/one/:two/three`
|
|
675
|
+
* - `/one/three/:four`
|
|
676
|
+
* - `/one/three/:five`
|
|
677
|
+
* - `/one/:two/three/:four`
|
|
678
|
+
* - `/one/:two/three/:five`
|
|
679
|
+
* - `/one/three/:four/:five`
|
|
680
|
+
* - `/one/:two/three/:four/:five`
|
|
681
|
+
*/
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
function explodeOptionalSegments(path) {
|
|
685
|
+
let segments = path.split("/");
|
|
686
|
+
if (segments.length === 0) return [];
|
|
687
|
+
let [first, ...rest] = segments; // Optional path segments are denoted by a trailing `?`
|
|
688
|
+
|
|
689
|
+
let isOptional = first.endsWith("?"); // Compute the corresponding required segment: `foo?` -> `foo`
|
|
690
|
+
|
|
691
|
+
let required = first.replace(/\?$/, "");
|
|
692
|
+
|
|
693
|
+
if (rest.length === 0) {
|
|
694
|
+
// Intepret empty string as omitting an optional segment
|
|
695
|
+
// `["one", "", "three"]` corresponds to omitting `:two` from `/one/:two?/three` -> `/one/three`
|
|
696
|
+
return isOptional ? ["", required] : [required];
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
let restExploded = explodeOptionalSegments(rest.join("/"));
|
|
700
|
+
return restExploded.flatMap(subpath => {
|
|
701
|
+
// /one + / + :two/three -> /one/:two/three
|
|
702
|
+
let requiredExploded = subpath === "" ? required : required + "/" + subpath; // For optional segments, return the exploded path _without_ current segment first (`subpath`)
|
|
703
|
+
// and exploded path _with_ current segment later (`subpath`)
|
|
704
|
+
// This ensures that exploded paths are emitted in priority order
|
|
705
|
+
// `/one/three/:four` will come before `/one/three/:five`
|
|
706
|
+
|
|
707
|
+
return isOptional ? [subpath, requiredExploded] : [requiredExploded];
|
|
708
|
+
}).map(exploded => {
|
|
709
|
+
// for absolute paths, ensure `/` instead of empty segment
|
|
710
|
+
return path.startsWith("/") && exploded === "" ? "/" : exploded;
|
|
711
|
+
});
|
|
712
|
+
}
|
|
655
713
|
|
|
656
714
|
function rankRouteBranches(branches) {
|
|
657
715
|
branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first
|
|
@@ -735,14 +793,24 @@
|
|
|
735
793
|
*/
|
|
736
794
|
|
|
737
795
|
|
|
738
|
-
function generatePath(
|
|
796
|
+
function generatePath(originalPath, params) {
|
|
739
797
|
if (params === void 0) {
|
|
740
798
|
params = {};
|
|
741
799
|
}
|
|
742
800
|
|
|
743
|
-
|
|
801
|
+
let path = originalPath;
|
|
802
|
+
|
|
803
|
+
if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
|
|
804
|
+
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(/\*$/, "/*") + "\"."));
|
|
805
|
+
path = path.replace(/\*$/, "/*");
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
return path.replace(/^:(\w+)/g, (_, key) => {
|
|
744
809
|
invariant(params[key] != null, "Missing \":" + key + "\" param");
|
|
745
810
|
return params[key];
|
|
811
|
+
}).replace(/\/:(\w+)/g, (_, key) => {
|
|
812
|
+
invariant(params[key] != null, "Missing \":" + key + "\" param");
|
|
813
|
+
return "/" + params[key];
|
|
746
814
|
}).replace(/(\/?)\*/, (_, prefix, __, str) => {
|
|
747
815
|
const star = "*";
|
|
748
816
|
|
|
@@ -814,9 +882,9 @@
|
|
|
814
882
|
let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
|
|
815
883
|
.replace(/^\/*/, "/") // Make sure it has a leading /
|
|
816
884
|
.replace(/[\\.*+^$?{}|()[\]]/g, "\\$&") // Escape special regex chars
|
|
817
|
-
.replace(
|
|
885
|
+
.replace(/\/:(\w+)/g, (_, paramName) => {
|
|
818
886
|
paramNames.push(paramName);
|
|
819
|
-
return "([^\\/]+)";
|
|
887
|
+
return "/([^\\/]+)";
|
|
820
888
|
});
|
|
821
889
|
|
|
822
890
|
if (path.endsWith("*")) {
|
|
@@ -1297,9 +1365,9 @@
|
|
|
1297
1365
|
* A Router instance manages all navigation and data loading/mutations
|
|
1298
1366
|
*/
|
|
1299
1367
|
|
|
1300
|
-
const
|
|
1301
|
-
const
|
|
1302
|
-
const validRequestMethodsArr = ["get", ...
|
|
1368
|
+
const validMutationMethodsArr = ["post", "put", "patch", "delete"];
|
|
1369
|
+
const validMutationMethods = new Set(validMutationMethodsArr);
|
|
1370
|
+
const validRequestMethodsArr = ["get", ...validMutationMethodsArr];
|
|
1303
1371
|
const validRequestMethods = new Set(validRequestMethodsArr);
|
|
1304
1372
|
const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
|
|
1305
1373
|
const redirectPreserveMethodStatusCodes = new Set([307, 308]);
|
|
@@ -1536,7 +1604,7 @@
|
|
|
1536
1604
|
// without having to touch history
|
|
1537
1605
|
|
|
1538
1606
|
location = _extends({}, location, init.history.encodeLocation(location));
|
|
1539
|
-
let historyAction = (opts && opts.replace) === true || submission != null ? exports.Action.Replace : exports.Action.Push;
|
|
1607
|
+
let historyAction = (opts && opts.replace) === true || submission != null && isMutationMethod(submission.formMethod) ? exports.Action.Replace : exports.Action.Push;
|
|
1540
1608
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
1541
1609
|
return await startNavigation(historyAction, location, {
|
|
1542
1610
|
submission,
|
|
@@ -1640,7 +1708,7 @@
|
|
|
1640
1708
|
pendingError = {
|
|
1641
1709
|
[findNearestBoundary(matches).route.id]: opts.pendingError
|
|
1642
1710
|
};
|
|
1643
|
-
} else if (opts && opts.submission) {
|
|
1711
|
+
} else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
|
|
1644
1712
|
// Call action if we received an action submission
|
|
1645
1713
|
let actionOutput = await handleAction(request, location, opts.submission, matches, {
|
|
1646
1714
|
replace: opts.replace
|
|
@@ -1767,14 +1835,15 @@
|
|
|
1767
1835
|
let loadingNavigation = overrideNavigation;
|
|
1768
1836
|
|
|
1769
1837
|
if (!loadingNavigation) {
|
|
1770
|
-
let navigation = {
|
|
1838
|
+
let navigation = _extends({
|
|
1771
1839
|
state: "loading",
|
|
1772
1840
|
location,
|
|
1773
1841
|
formMethod: undefined,
|
|
1774
1842
|
formAction: undefined,
|
|
1775
1843
|
formEncType: undefined,
|
|
1776
1844
|
formData: undefined
|
|
1777
|
-
};
|
|
1845
|
+
}, submission);
|
|
1846
|
+
|
|
1778
1847
|
loadingNavigation = navigation;
|
|
1779
1848
|
}
|
|
1780
1849
|
|
|
@@ -1909,7 +1978,7 @@
|
|
|
1909
1978
|
} = normalizeNavigateOptions(href, opts, true);
|
|
1910
1979
|
let match = getTargetMatch(matches, path);
|
|
1911
1980
|
|
|
1912
|
-
if (submission) {
|
|
1981
|
+
if (submission && isMutationMethod(submission.formMethod)) {
|
|
1913
1982
|
handleFetcherAction(key, routeId, path, match, matches, submission);
|
|
1914
1983
|
return;
|
|
1915
1984
|
} // Store off the match so we can call it's shouldRevalidate on subsequent
|
|
@@ -1917,7 +1986,7 @@
|
|
|
1917
1986
|
|
|
1918
1987
|
|
|
1919
1988
|
fetchLoadMatches.set(key, [path, match, matches]);
|
|
1920
|
-
handleFetcherLoader(key, routeId, path, match, matches);
|
|
1989
|
+
handleFetcherLoader(key, routeId, path, match, matches, submission);
|
|
1921
1990
|
} // Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
1922
1991
|
// errors, and revalidation
|
|
1923
1992
|
|
|
@@ -2098,17 +2167,19 @@
|
|
|
2098
2167
|
} // Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
2099
2168
|
|
|
2100
2169
|
|
|
2101
|
-
async function handleFetcherLoader(key, routeId, path, match, matches) {
|
|
2170
|
+
async function handleFetcherLoader(key, routeId, path, match, matches, submission) {
|
|
2102
2171
|
let existingFetcher = state.fetchers.get(key); // Put this fetcher into it's loading state
|
|
2103
2172
|
|
|
2104
|
-
let loadingFetcher = {
|
|
2173
|
+
let loadingFetcher = _extends({
|
|
2105
2174
|
state: "loading",
|
|
2106
2175
|
formMethod: undefined,
|
|
2107
2176
|
formAction: undefined,
|
|
2108
2177
|
formEncType: undefined,
|
|
2109
|
-
formData: undefined
|
|
2178
|
+
formData: undefined
|
|
2179
|
+
}, submission, {
|
|
2110
2180
|
data: existingFetcher && existingFetcher.data
|
|
2111
|
-
};
|
|
2181
|
+
});
|
|
2182
|
+
|
|
2112
2183
|
state.fetchers.set(key, loadingFetcher);
|
|
2113
2184
|
updateState({
|
|
2114
2185
|
fetchers: new Map(state.fetchers)
|
|
@@ -2228,10 +2299,10 @@
|
|
|
2228
2299
|
formEncType,
|
|
2229
2300
|
formData
|
|
2230
2301
|
} = state.navigation; // If this was a 307/308 submission we want to preserve the HTTP method and
|
|
2231
|
-
// re-submit the POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2302
|
+
// re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2232
2303
|
// redirected location
|
|
2233
2304
|
|
|
2234
|
-
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod &&
|
|
2305
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod && isMutationMethod(formMethod) && formEncType && formData) {
|
|
2235
2306
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2236
2307
|
submission: {
|
|
2237
2308
|
formMethod,
|
|
@@ -2642,7 +2713,7 @@
|
|
|
2642
2713
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2643
2714
|
|
|
2644
2715
|
try {
|
|
2645
|
-
if (
|
|
2716
|
+
if (isMutationMethod(request.method.toLowerCase())) {
|
|
2646
2717
|
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), requestContext, routeMatch != null);
|
|
2647
2718
|
return result;
|
|
2648
2719
|
}
|
|
@@ -2759,6 +2830,8 @@
|
|
|
2759
2830
|
|
|
2760
2831
|
|
|
2761
2832
|
let loaderRequest = new Request(request.url, {
|
|
2833
|
+
headers: request.headers,
|
|
2834
|
+
redirect: request.redirect,
|
|
2762
2835
|
signal: request.signal
|
|
2763
2836
|
});
|
|
2764
2837
|
let context = await loadRouteData(loaderRequest, matches, requestContext);
|
|
@@ -2874,16 +2947,22 @@
|
|
|
2874
2947
|
} // Create a Submission on non-GET navigations
|
|
2875
2948
|
|
|
2876
2949
|
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
}
|
|
2950
|
+
let submission;
|
|
2951
|
+
|
|
2952
|
+
if (opts.formData) {
|
|
2953
|
+
submission = {
|
|
2954
|
+
formMethod: opts.formMethod || "get",
|
|
2955
|
+
formAction: stripHashFromPath(path),
|
|
2956
|
+
formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
|
|
2957
|
+
formData: opts.formData
|
|
2886
2958
|
};
|
|
2959
|
+
|
|
2960
|
+
if (isMutationMethod(submission.formMethod)) {
|
|
2961
|
+
return {
|
|
2962
|
+
path,
|
|
2963
|
+
submission
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2887
2966
|
} // Flatten submission onto URLSearchParams for GET submissions
|
|
2888
2967
|
|
|
2889
2968
|
|
|
@@ -2907,7 +2986,8 @@
|
|
|
2907
2986
|
}
|
|
2908
2987
|
|
|
2909
2988
|
return {
|
|
2910
|
-
path: createPath(parsedPath)
|
|
2989
|
+
path: createPath(parsedPath),
|
|
2990
|
+
submission
|
|
2911
2991
|
};
|
|
2912
2992
|
} // Filter out all routes below any caught error as they aren't going to
|
|
2913
2993
|
// render so we don't need to load them
|
|
@@ -3151,7 +3231,7 @@
|
|
|
3151
3231
|
signal
|
|
3152
3232
|
};
|
|
3153
3233
|
|
|
3154
|
-
if (submission) {
|
|
3234
|
+
if (submission && isMutationMethod(submission.formMethod)) {
|
|
3155
3235
|
let {
|
|
3156
3236
|
formMethod,
|
|
3157
3237
|
formEncType,
|
|
@@ -3201,11 +3281,14 @@
|
|
|
3201
3281
|
pendingError = undefined;
|
|
3202
3282
|
}
|
|
3203
3283
|
|
|
3204
|
-
errors =
|
|
3205
|
-
|
|
3206
|
-
|
|
3284
|
+
errors = errors || {}; // Prefer higher error values if lower errors bubble to the same boundary
|
|
3285
|
+
|
|
3286
|
+
if (errors[boundaryMatch.route.id] == null) {
|
|
3287
|
+
errors[boundaryMatch.route.id] = error;
|
|
3288
|
+
} // Once we find our first (highest) error, we set the status code and
|
|
3207
3289
|
// prevent deeper status codes from overriding
|
|
3208
3290
|
|
|
3291
|
+
|
|
3209
3292
|
if (!foundError) {
|
|
3210
3293
|
foundError = true;
|
|
3211
3294
|
statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
|
|
@@ -3421,8 +3504,8 @@
|
|
|
3421
3504
|
return validRequestMethods.has(method);
|
|
3422
3505
|
}
|
|
3423
3506
|
|
|
3424
|
-
function
|
|
3425
|
-
return
|
|
3507
|
+
function isMutationMethod(method) {
|
|
3508
|
+
return validMutationMethods.has(method);
|
|
3426
3509
|
}
|
|
3427
3510
|
|
|
3428
3511
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|