@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.umd.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
|
*
|
|
@@ -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,71 @@
|
|
|
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
|
+
let result = []; // All child paths with the prefix. Do this for all children before the
|
|
701
|
+
// optional version for all children so we get consistent ordering where the
|
|
702
|
+
// parent optional aspect is preferred as required. Otherwise, we can get
|
|
703
|
+
// child sections interspersed where deeper optional segments are higher than
|
|
704
|
+
// parent optional segments, where for example, /:two would explodes _earlier_
|
|
705
|
+
// then /:one. By always including the parent as required _for all children_
|
|
706
|
+
// first, we avoid this issue
|
|
707
|
+
|
|
708
|
+
result.push(...restExploded.map(subpath => subpath === "" ? required : [required, subpath].join("/"))); // Then if this is an optional value, add all child versions without
|
|
709
|
+
|
|
710
|
+
if (isOptional) {
|
|
711
|
+
result.push(...restExploded);
|
|
712
|
+
} // for absolute paths, ensure `/` instead of empty segment
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
return result.map(exploded => path.startsWith("/") && exploded === "" ? "/" : exploded);
|
|
716
|
+
}
|
|
655
717
|
|
|
656
718
|
function rankRouteBranches(branches) {
|
|
657
719
|
branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first
|
|
@@ -735,14 +797,24 @@
|
|
|
735
797
|
*/
|
|
736
798
|
|
|
737
799
|
|
|
738
|
-
function generatePath(
|
|
800
|
+
function generatePath(originalPath, params) {
|
|
739
801
|
if (params === void 0) {
|
|
740
802
|
params = {};
|
|
741
803
|
}
|
|
742
804
|
|
|
743
|
-
|
|
805
|
+
let path = originalPath;
|
|
806
|
+
|
|
807
|
+
if (path.endsWith("*") && path !== "*" && !path.endsWith("/*")) {
|
|
808
|
+
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(/\*$/, "/*") + "\"."));
|
|
809
|
+
path = path.replace(/\*$/, "/*");
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
return path.replace(/^:(\w+)/g, (_, key) => {
|
|
744
813
|
invariant(params[key] != null, "Missing \":" + key + "\" param");
|
|
745
814
|
return params[key];
|
|
815
|
+
}).replace(/\/:(\w+)/g, (_, key) => {
|
|
816
|
+
invariant(params[key] != null, "Missing \":" + key + "\" param");
|
|
817
|
+
return "/" + params[key];
|
|
746
818
|
}).replace(/(\/?)\*/, (_, prefix, __, str) => {
|
|
747
819
|
const star = "*";
|
|
748
820
|
|
|
@@ -814,9 +886,9 @@
|
|
|
814
886
|
let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
|
|
815
887
|
.replace(/^\/*/, "/") // Make sure it has a leading /
|
|
816
888
|
.replace(/[\\.*+^$?{}|()[\]]/g, "\\$&") // Escape special regex chars
|
|
817
|
-
.replace(
|
|
889
|
+
.replace(/\/:(\w+)/g, (_, paramName) => {
|
|
818
890
|
paramNames.push(paramName);
|
|
819
|
-
return "([^\\/]+)";
|
|
891
|
+
return "/([^\\/]+)";
|
|
820
892
|
});
|
|
821
893
|
|
|
822
894
|
if (path.endsWith("*")) {
|
|
@@ -1297,9 +1369,9 @@
|
|
|
1297
1369
|
* A Router instance manages all navigation and data loading/mutations
|
|
1298
1370
|
*/
|
|
1299
1371
|
|
|
1300
|
-
const
|
|
1301
|
-
const
|
|
1302
|
-
const validRequestMethodsArr = ["get", ...
|
|
1372
|
+
const validMutationMethodsArr = ["post", "put", "patch", "delete"];
|
|
1373
|
+
const validMutationMethods = new Set(validMutationMethodsArr);
|
|
1374
|
+
const validRequestMethodsArr = ["get", ...validMutationMethodsArr];
|
|
1303
1375
|
const validRequestMethods = new Set(validRequestMethodsArr);
|
|
1304
1376
|
const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
|
|
1305
1377
|
const redirectPreserveMethodStatusCodes = new Set([307, 308]);
|
|
@@ -1536,7 +1608,7 @@
|
|
|
1536
1608
|
// without having to touch history
|
|
1537
1609
|
|
|
1538
1610
|
location = _extends({}, location, init.history.encodeLocation(location));
|
|
1539
|
-
let historyAction = (opts && opts.replace) === true || submission != null ? exports.Action.Replace : exports.Action.Push;
|
|
1611
|
+
let historyAction = (opts && opts.replace) === true || submission != null && isMutationMethod(submission.formMethod) ? exports.Action.Replace : exports.Action.Push;
|
|
1540
1612
|
let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
|
|
1541
1613
|
return await startNavigation(historyAction, location, {
|
|
1542
1614
|
submission,
|
|
@@ -1640,7 +1712,7 @@
|
|
|
1640
1712
|
pendingError = {
|
|
1641
1713
|
[findNearestBoundary(matches).route.id]: opts.pendingError
|
|
1642
1714
|
};
|
|
1643
|
-
} else if (opts && opts.submission) {
|
|
1715
|
+
} else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
|
|
1644
1716
|
// Call action if we received an action submission
|
|
1645
1717
|
let actionOutput = await handleAction(request, location, opts.submission, matches, {
|
|
1646
1718
|
replace: opts.replace
|
|
@@ -1767,14 +1839,15 @@
|
|
|
1767
1839
|
let loadingNavigation = overrideNavigation;
|
|
1768
1840
|
|
|
1769
1841
|
if (!loadingNavigation) {
|
|
1770
|
-
let navigation = {
|
|
1842
|
+
let navigation = _extends({
|
|
1771
1843
|
state: "loading",
|
|
1772
1844
|
location,
|
|
1773
1845
|
formMethod: undefined,
|
|
1774
1846
|
formAction: undefined,
|
|
1775
1847
|
formEncType: undefined,
|
|
1776
1848
|
formData: undefined
|
|
1777
|
-
};
|
|
1849
|
+
}, submission);
|
|
1850
|
+
|
|
1778
1851
|
loadingNavigation = navigation;
|
|
1779
1852
|
}
|
|
1780
1853
|
|
|
@@ -1909,7 +1982,7 @@
|
|
|
1909
1982
|
} = normalizeNavigateOptions(href, opts, true);
|
|
1910
1983
|
let match = getTargetMatch(matches, path);
|
|
1911
1984
|
|
|
1912
|
-
if (submission) {
|
|
1985
|
+
if (submission && isMutationMethod(submission.formMethod)) {
|
|
1913
1986
|
handleFetcherAction(key, routeId, path, match, matches, submission);
|
|
1914
1987
|
return;
|
|
1915
1988
|
} // Store off the match so we can call it's shouldRevalidate on subsequent
|
|
@@ -1917,7 +1990,7 @@
|
|
|
1917
1990
|
|
|
1918
1991
|
|
|
1919
1992
|
fetchLoadMatches.set(key, [path, match, matches]);
|
|
1920
|
-
handleFetcherLoader(key, routeId, path, match, matches);
|
|
1993
|
+
handleFetcherLoader(key, routeId, path, match, matches, submission);
|
|
1921
1994
|
} // Call the action for the matched fetcher.submit(), and then handle redirects,
|
|
1922
1995
|
// errors, and revalidation
|
|
1923
1996
|
|
|
@@ -2098,17 +2171,19 @@
|
|
|
2098
2171
|
} // Call the matched loader for fetcher.load(), handling redirects, errors, etc.
|
|
2099
2172
|
|
|
2100
2173
|
|
|
2101
|
-
async function handleFetcherLoader(key, routeId, path, match, matches) {
|
|
2174
|
+
async function handleFetcherLoader(key, routeId, path, match, matches, submission) {
|
|
2102
2175
|
let existingFetcher = state.fetchers.get(key); // Put this fetcher into it's loading state
|
|
2103
2176
|
|
|
2104
|
-
let loadingFetcher = {
|
|
2177
|
+
let loadingFetcher = _extends({
|
|
2105
2178
|
state: "loading",
|
|
2106
2179
|
formMethod: undefined,
|
|
2107
2180
|
formAction: undefined,
|
|
2108
2181
|
formEncType: undefined,
|
|
2109
|
-
formData: undefined
|
|
2182
|
+
formData: undefined
|
|
2183
|
+
}, submission, {
|
|
2110
2184
|
data: existingFetcher && existingFetcher.data
|
|
2111
|
-
};
|
|
2185
|
+
});
|
|
2186
|
+
|
|
2112
2187
|
state.fetchers.set(key, loadingFetcher);
|
|
2113
2188
|
updateState({
|
|
2114
2189
|
fetchers: new Map(state.fetchers)
|
|
@@ -2228,10 +2303,10 @@
|
|
|
2228
2303
|
formEncType,
|
|
2229
2304
|
formData
|
|
2230
2305
|
} = 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
|
|
2306
|
+
// re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the
|
|
2232
2307
|
// redirected location
|
|
2233
2308
|
|
|
2234
|
-
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod &&
|
|
2309
|
+
if (redirectPreserveMethodStatusCodes.has(redirect.status) && formMethod && isMutationMethod(formMethod) && formEncType && formData) {
|
|
2235
2310
|
await startNavigation(redirectHistoryAction, redirectLocation, {
|
|
2236
2311
|
submission: {
|
|
2237
2312
|
formMethod,
|
|
@@ -2642,7 +2717,7 @@
|
|
|
2642
2717
|
invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
|
|
2643
2718
|
|
|
2644
2719
|
try {
|
|
2645
|
-
if (
|
|
2720
|
+
if (isMutationMethod(request.method.toLowerCase())) {
|
|
2646
2721
|
let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), requestContext, routeMatch != null);
|
|
2647
2722
|
return result;
|
|
2648
2723
|
}
|
|
@@ -2759,6 +2834,8 @@
|
|
|
2759
2834
|
|
|
2760
2835
|
|
|
2761
2836
|
let loaderRequest = new Request(request.url, {
|
|
2837
|
+
headers: request.headers,
|
|
2838
|
+
redirect: request.redirect,
|
|
2762
2839
|
signal: request.signal
|
|
2763
2840
|
});
|
|
2764
2841
|
let context = await loadRouteData(loaderRequest, matches, requestContext);
|
|
@@ -2874,16 +2951,22 @@
|
|
|
2874
2951
|
} // Create a Submission on non-GET navigations
|
|
2875
2952
|
|
|
2876
2953
|
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
}
|
|
2954
|
+
let submission;
|
|
2955
|
+
|
|
2956
|
+
if (opts.formData) {
|
|
2957
|
+
submission = {
|
|
2958
|
+
formMethod: opts.formMethod || "get",
|
|
2959
|
+
formAction: stripHashFromPath(path),
|
|
2960
|
+
formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
|
|
2961
|
+
formData: opts.formData
|
|
2886
2962
|
};
|
|
2963
|
+
|
|
2964
|
+
if (isMutationMethod(submission.formMethod)) {
|
|
2965
|
+
return {
|
|
2966
|
+
path,
|
|
2967
|
+
submission
|
|
2968
|
+
};
|
|
2969
|
+
}
|
|
2887
2970
|
} // Flatten submission onto URLSearchParams for GET submissions
|
|
2888
2971
|
|
|
2889
2972
|
|
|
@@ -2907,7 +2990,8 @@
|
|
|
2907
2990
|
}
|
|
2908
2991
|
|
|
2909
2992
|
return {
|
|
2910
|
-
path: createPath(parsedPath)
|
|
2993
|
+
path: createPath(parsedPath),
|
|
2994
|
+
submission
|
|
2911
2995
|
};
|
|
2912
2996
|
} // Filter out all routes below any caught error as they aren't going to
|
|
2913
2997
|
// render so we don't need to load them
|
|
@@ -3151,7 +3235,7 @@
|
|
|
3151
3235
|
signal
|
|
3152
3236
|
};
|
|
3153
3237
|
|
|
3154
|
-
if (submission) {
|
|
3238
|
+
if (submission && isMutationMethod(submission.formMethod)) {
|
|
3155
3239
|
let {
|
|
3156
3240
|
formMethod,
|
|
3157
3241
|
formEncType,
|
|
@@ -3201,11 +3285,14 @@
|
|
|
3201
3285
|
pendingError = undefined;
|
|
3202
3286
|
}
|
|
3203
3287
|
|
|
3204
|
-
errors =
|
|
3205
|
-
|
|
3206
|
-
|
|
3288
|
+
errors = errors || {}; // Prefer higher error values if lower errors bubble to the same boundary
|
|
3289
|
+
|
|
3290
|
+
if (errors[boundaryMatch.route.id] == null) {
|
|
3291
|
+
errors[boundaryMatch.route.id] = error;
|
|
3292
|
+
} // Once we find our first (highest) error, we set the status code and
|
|
3207
3293
|
// prevent deeper status codes from overriding
|
|
3208
3294
|
|
|
3295
|
+
|
|
3209
3296
|
if (!foundError) {
|
|
3210
3297
|
foundError = true;
|
|
3211
3298
|
statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
|
|
@@ -3421,8 +3508,8 @@
|
|
|
3421
3508
|
return validRequestMethods.has(method);
|
|
3422
3509
|
}
|
|
3423
3510
|
|
|
3424
|
-
function
|
|
3425
|
-
return
|
|
3511
|
+
function isMutationMethod(method) {
|
|
3512
|
+
return validMutationMethods.has(method);
|
|
3426
3513
|
}
|
|
3427
3514
|
|
|
3428
3515
|
async function resolveDeferredResults(currentMatches, matchesToLoad, results, signal, isFetcher, currentLoaderData) {
|