react-router 8.3.0 → 8.4.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 +46 -2
- package/dist/development/dom-export.js +1 -1
- package/dist/development/index-react-server-client.js +1 -1
- package/dist/development/index-react-server.d.ts +50 -48
- package/dist/development/index-react-server.js +96 -43
- package/dist/development/index.d.ts +2 -2
- package/dist/development/index.js +3 -3
- package/dist/development/lib/actions.js +10 -5
- package/dist/development/lib/components.js +50 -10
- package/dist/development/lib/context.d.ts +14 -5
- package/dist/development/lib/context.js +11 -3
- package/dist/development/lib/dom/dom.js +2 -2
- package/dist/development/lib/dom/lib.js +33 -36
- package/dist/development/lib/dom/server.d.ts +6 -1
- package/dist/development/lib/dom/server.js +40 -14
- package/dist/development/lib/dom/ssr/components.js +16 -24
- package/dist/development/lib/dom/ssr/data.js +1 -1
- package/dist/development/lib/dom/ssr/entry.d.ts +4 -0
- package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/development/lib/dom/ssr/fallback.js +1 -1
- package/dist/development/lib/dom/ssr/fog-of-war.js +34 -12
- package/dist/development/lib/dom/ssr/hydration.d.ts +3 -1
- package/dist/development/lib/dom/ssr/hydration.js +6 -4
- package/dist/development/lib/dom/ssr/invariant.js +1 -1
- package/dist/development/lib/dom/ssr/links.js +1 -1
- package/dist/development/lib/dom/ssr/markup.js +5 -2
- package/dist/development/lib/dom/ssr/routeModules.js +1 -1
- package/dist/development/lib/dom/ssr/routes-test-stub.js +1 -1
- package/dist/development/lib/dom/ssr/routes.js +1 -1
- package/dist/development/lib/dom/ssr/server.js +2 -2
- package/dist/development/lib/dom/ssr/single-fetch.js +13 -22
- package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/development/lib/dom-export/hydrated-router.js +2 -1
- package/dist/development/lib/errors.js +1 -1
- package/dist/development/lib/hooks.js +54 -46
- package/dist/development/lib/href.js +1 -1
- package/dist/development/lib/router/history.js +2 -2
- package/dist/development/lib/router/instrumentation.js +1 -1
- package/dist/development/lib/router/matcher-route-pattern.d.ts +2 -0
- package/dist/development/lib/router/matcher-route-pattern.js +216 -0
- package/dist/development/lib/router/matcher-route-pattern.preload.d.ts +20 -0
- package/dist/development/lib/router/matcher-route-pattern.preload.js +36 -0
- package/dist/development/lib/router/matcher.d.ts +2 -0
- package/dist/development/lib/router/matcher.js +29 -0
- package/dist/development/lib/router/navigation.js +45 -0
- package/dist/development/lib/router/router.d.ts +21 -7
- package/dist/development/lib/router/router.js +111 -82
- package/dist/development/lib/router/url.js +1 -1
- package/dist/development/lib/router/utils.d.ts +6 -1
- package/dist/development/lib/router/utils.js +15 -9
- package/dist/development/lib/rsc/browser.js +5 -1
- package/dist/development/lib/rsc/errorBoundaries.js +1 -1
- package/dist/development/lib/rsc/html-stream/browser.js +1 -1
- package/dist/development/lib/rsc/html-stream/server.js +3 -5
- package/dist/development/lib/rsc/route-modules.js +1 -1
- package/dist/development/lib/rsc/server.rsc.d.ts +7 -0
- package/dist/development/lib/rsc/server.ssr.js +26 -22
- package/dist/development/lib/server-runtime/cookies.js +2 -2
- package/dist/development/lib/server-runtime/crypto.js +2 -2
- package/dist/development/lib/server-runtime/data.js +1 -1
- package/dist/development/lib/server-runtime/dev.js +2 -2
- package/dist/development/lib/server-runtime/entry.js +1 -1
- package/dist/development/lib/server-runtime/errors.js +1 -1
- package/dist/development/lib/server-runtime/headers.js +1 -1
- package/dist/development/lib/server-runtime/invariant.js +1 -1
- package/dist/development/lib/server-runtime/mode.js +1 -1
- package/dist/development/lib/server-runtime/routeMatching.js +3 -4
- package/dist/development/lib/server-runtime/routes.js +1 -1
- package/dist/development/lib/server-runtime/server.js +8 -7
- package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/development/lib/server-runtime/sessions.js +1 -1
- package/dist/development/lib/server-runtime/single-fetch.js +2 -2
- package/dist/development/lib/server-runtime/urls.js +1 -1
- package/dist/development/lib/server-runtime/warnings.js +1 -1
- package/dist/development/lib/types/internal.js +1 -1
- package/dist/development/route-pattern.d.ts +3 -0
- package/dist/development/route-pattern.js +12 -0
- package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +4 -4
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
- package/dist/production/dom-export.js +1 -1
- package/dist/production/index-react-server-client.js +1 -1
- package/dist/production/index-react-server.d.ts +50 -48
- package/dist/production/index-react-server.js +96 -43
- package/dist/production/index.d.ts +2 -2
- package/dist/production/index.js +3 -3
- package/dist/production/lib/actions.js +10 -5
- package/dist/production/lib/components.js +50 -10
- package/dist/production/lib/context.d.ts +14 -5
- package/dist/production/lib/context.js +11 -3
- package/dist/production/lib/dom/dom.js +2 -2
- package/dist/production/lib/dom/lib.js +33 -36
- package/dist/production/lib/dom/server.d.ts +6 -1
- package/dist/production/lib/dom/server.js +40 -14
- package/dist/production/lib/dom/ssr/components.js +16 -24
- package/dist/production/lib/dom/ssr/data.js +1 -1
- package/dist/production/lib/dom/ssr/entry.d.ts +4 -0
- package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/production/lib/dom/ssr/fallback.js +1 -1
- package/dist/production/lib/dom/ssr/fog-of-war.js +34 -12
- package/dist/production/lib/dom/ssr/hydration.d.ts +3 -1
- package/dist/production/lib/dom/ssr/hydration.js +6 -4
- package/dist/production/lib/dom/ssr/invariant.js +1 -1
- package/dist/production/lib/dom/ssr/links.js +1 -1
- package/dist/production/lib/dom/ssr/markup.js +5 -2
- package/dist/production/lib/dom/ssr/routeModules.js +1 -1
- package/dist/production/lib/dom/ssr/routes-test-stub.js +1 -1
- package/dist/production/lib/dom/ssr/routes.js +1 -1
- package/dist/production/lib/dom/ssr/server.js +2 -2
- package/dist/production/lib/dom/ssr/single-fetch.js +13 -22
- package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/production/lib/dom-export/hydrated-router.js +2 -1
- package/dist/production/lib/errors.js +1 -1
- package/dist/production/lib/hooks.js +54 -46
- package/dist/production/lib/href.js +1 -1
- package/dist/production/lib/router/history.js +2 -2
- package/dist/production/lib/router/instrumentation.js +1 -1
- package/dist/production/lib/router/matcher-route-pattern.d.ts +2 -0
- package/dist/production/lib/router/matcher-route-pattern.js +216 -0
- package/dist/production/lib/router/matcher-route-pattern.preload.d.ts +20 -0
- package/dist/production/lib/router/matcher-route-pattern.preload.js +36 -0
- package/dist/production/lib/router/matcher.d.ts +2 -0
- package/dist/production/lib/router/matcher.js +29 -0
- package/dist/production/lib/router/navigation.js +45 -0
- package/dist/production/lib/router/router.d.ts +21 -7
- package/dist/production/lib/router/router.js +111 -82
- package/dist/production/lib/router/url.js +1 -1
- package/dist/production/lib/router/utils.d.ts +6 -1
- package/dist/production/lib/router/utils.js +15 -9
- package/dist/production/lib/rsc/browser.js +5 -1
- package/dist/production/lib/rsc/errorBoundaries.js +1 -1
- package/dist/production/lib/rsc/html-stream/browser.js +1 -1
- package/dist/production/lib/rsc/html-stream/server.js +3 -5
- package/dist/production/lib/rsc/route-modules.js +1 -1
- package/dist/production/lib/rsc/server.rsc.d.ts +7 -0
- package/dist/production/lib/rsc/server.ssr.js +26 -22
- package/dist/production/lib/server-runtime/cookies.js +2 -2
- package/dist/production/lib/server-runtime/crypto.js +2 -2
- package/dist/production/lib/server-runtime/data.js +1 -1
- package/dist/production/lib/server-runtime/dev.js +2 -2
- package/dist/production/lib/server-runtime/entry.js +1 -1
- package/dist/production/lib/server-runtime/errors.js +1 -1
- package/dist/production/lib/server-runtime/headers.js +1 -1
- package/dist/production/lib/server-runtime/invariant.js +1 -1
- package/dist/production/lib/server-runtime/mode.js +1 -1
- package/dist/production/lib/server-runtime/routeMatching.js +3 -4
- package/dist/production/lib/server-runtime/routes.js +1 -1
- package/dist/production/lib/server-runtime/server.js +8 -7
- package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
- package/dist/production/lib/server-runtime/sessions.js +1 -1
- package/dist/production/lib/server-runtime/single-fetch.js +2 -2
- package/dist/production/lib/server-runtime/urls.js +1 -1
- package/dist/production/lib/server-runtime/warnings.js +1 -1
- package/dist/production/lib/types/internal.js +1 -1
- package/dist/production/route-pattern.d.ts +3 -0
- package/dist/production/route-pattern.js +12 -0
- package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +4 -4
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
- package/docs/how-to/fetchers.md +6 -1
- package/docs/how-to/middleware.md +3 -1
- package/docs/how-to/optimize-revalidation.md +178 -5
- package/docs/how-to/react-server-components.md +26 -3
- package/docs/how-to/resource-routes.md +3 -0
- package/docs/start/data/actions.md +2 -1
- package/docs/start/data/route-object.md +21 -6
- package/docs/start/framework/actions.md +4 -2
- package/docs/start/framework/route-module.md +32 -5
- package/docs/upgrading/future.md +68 -0
- package/package.json +11 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -135,7 +135,8 @@ const unsupportedLazyRouteObjectKeys = new Set([
|
|
|
135
135
|
"path",
|
|
136
136
|
"id",
|
|
137
137
|
"index",
|
|
138
|
-
"children"
|
|
138
|
+
"children",
|
|
139
|
+
"unstable_validateParams"
|
|
139
140
|
]);
|
|
140
141
|
function isUnsupportedLazyRouteObjectKey(key) {
|
|
141
142
|
return unsupportedLazyRouteObjectKeys.has(key);
|
|
@@ -147,7 +148,8 @@ const unsupportedLazyRouteFunctionKeys = new Set([
|
|
|
147
148
|
"id",
|
|
148
149
|
"index",
|
|
149
150
|
"middleware",
|
|
150
|
-
"children"
|
|
151
|
+
"children",
|
|
152
|
+
"unstable_validateParams"
|
|
151
153
|
]);
|
|
152
154
|
function isUnsupportedLazyRouteFunctionKey(key) {
|
|
153
155
|
return unsupportedLazyRouteFunctionKeys.has(key);
|
|
@@ -480,13 +482,13 @@ function matchPathImpl(pattern, pathname, matcher, compiledParams) {
|
|
|
480
482
|
let match = pathname.match(matcher);
|
|
481
483
|
if (!match) return null;
|
|
482
484
|
let matchedPathname = match[0];
|
|
483
|
-
let pathnameBase = matchedPathname
|
|
485
|
+
let pathnameBase = removeTrailingSlash(matchedPathname, 1);
|
|
484
486
|
let captureGroups = match.slice(1);
|
|
485
487
|
return {
|
|
486
488
|
params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
|
|
487
489
|
if (paramName === "*") {
|
|
488
490
|
let splatValue = captureGroups[index] || "";
|
|
489
|
-
pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length)
|
|
491
|
+
pathnameBase = removeTrailingSlash(matchedPathname.slice(0, matchedPathname.length - splatValue.length), 1);
|
|
490
492
|
}
|
|
491
493
|
const value = captureGroups[index];
|
|
492
494
|
if (isOptional && !value) memo[paramName] = void 0;
|
|
@@ -555,7 +557,7 @@ function resolvePath(to, fromPathname = "/") {
|
|
|
555
557
|
let pathname;
|
|
556
558
|
if (toPathname) {
|
|
557
559
|
toPathname = removeDoubleSlashes(toPathname);
|
|
558
|
-
if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
|
|
560
|
+
if (toPathname.startsWith("/") || toPathname.startsWith("\\")) pathname = resolvePathname(toPathname.substring(1), "/");
|
|
559
561
|
else pathname = resolvePathname(toPathname, fromPathname);
|
|
560
562
|
} else pathname = fromPathname;
|
|
561
563
|
return {
|
|
@@ -616,7 +618,11 @@ function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = fal
|
|
|
616
618
|
}
|
|
617
619
|
const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
|
|
618
620
|
const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
|
|
619
|
-
|
|
621
|
+
function removeTrailingSlash(path, minLength = 0) {
|
|
622
|
+
let end = path.length;
|
|
623
|
+
while (end > minLength && path.charCodeAt(end - 1) === 47) end--;
|
|
624
|
+
return end === path.length ? path : path.slice(0, end);
|
|
625
|
+
}
|
|
620
626
|
const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
|
|
621
627
|
const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
|
|
622
628
|
const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
|
|
@@ -859,7 +865,7 @@ function parseToInfo(_to, basename) {
|
|
|
859
865
|
let path = stripBasename(targetUrl.pathname, basename);
|
|
860
866
|
if (targetUrl.origin === currentUrl.origin && path != null) to = path + targetUrl.search + targetUrl.hash;
|
|
861
867
|
else isExternal = true;
|
|
862
|
-
} catch
|
|
868
|
+
} catch {
|
|
863
869
|
warning(false, `<Link to="${to}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`);
|
|
864
870
|
}
|
|
865
871
|
return {
|
|
@@ -869,4 +875,4 @@ function parseToInfo(_to, basename) {
|
|
|
869
875
|
};
|
|
870
876
|
}
|
|
871
877
|
//#endregion
|
|
872
|
-
export { ErrorResponseImpl, RouterContextProvider, SUPPORTED_ERROR_TYPES, compilePath, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createContext, createDataFunctionUrl, data, decodePath, defaultMapRouteProperties, encodePathParam, flattenAndRankRoutes, generatePath, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isBrowser, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, joinPaths, matchPath, matchRoutes, matchRoutesImpl, parseToInfo, prependBasename, redirect, redirectDocument, removeDoubleSlashes, removeTrailingSlash, replace, resolvePath, resolveTo, stripBasename };
|
|
878
|
+
export { ErrorResponseImpl, RouterContextProvider, SUPPORTED_ERROR_TYPES, compilePath, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createContext, createDataFunctionUrl, data, decodePath, defaultMapRouteProperties, encodePathParam, explodeOptionalSegments, flattenAndRankRoutes, generatePath, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isBrowser, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, joinPaths, matchPath, matchRoutes, matchRoutesImpl, normalizePathname, parseToInfo, prependBasename, redirect, redirectDocument, removeDoubleSlashes, removeTrailingSlash, replace, resolvePath, resolveTo, stripBasename };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url.js";
|
|
12
12
|
import { createBrowserHistory, createPath, invariant } from "../router/history.js";
|
|
13
13
|
import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils.js";
|
|
14
|
+
import { validateNavigationTarget } from "../router/navigation.js";
|
|
14
15
|
import { createRouter, hasInvalidProtocol, isMutationMethod } from "../router/router.js";
|
|
15
16
|
import { RSCRouterContext } from "../context.js";
|
|
16
17
|
import { RouterProvider } from "../components.js";
|
|
@@ -82,6 +83,7 @@ function createCallServer({ createFromReadableStream, createTemporaryReferenceSe
|
|
|
82
83
|
React$1.startTransition(() => Promise.resolve(payloadPromise).then(async (payload) => {
|
|
83
84
|
if (payload.type === "redirect") {
|
|
84
85
|
let location = normalizeRedirectLocation(payload.location);
|
|
86
|
+
validateNavigationTarget(payload.location, location, new URL(window.location.href), "allow-explicit");
|
|
85
87
|
if (payload.reload || isExternalLocation(location)) {
|
|
86
88
|
if (hasInvalidProtocol(location)) throw new Error("Invalid redirect location");
|
|
87
89
|
window.location.href = location;
|
|
@@ -97,6 +99,7 @@ function createCallServer({ createFromReadableStream, createTemporaryReferenceSe
|
|
|
97
99
|
if (rerender && landedActionId < actionId && globalVar.__routerActionID <= actionId) {
|
|
98
100
|
if (rerender.type === "redirect") {
|
|
99
101
|
let location = normalizeRedirectLocation(rerender.location);
|
|
102
|
+
validateNavigationTarget(rerender.location, location, new URL(window.location.href), "allow-explicit");
|
|
100
103
|
if (rerender.reload || isExternalLocation(location)) {
|
|
101
104
|
if (hasInvalidProtocol(location)) throw new Error("Invalid redirect location");
|
|
102
105
|
window.location.href = location;
|
|
@@ -166,6 +169,7 @@ function createRouterFromPayload({ fetchImplementation, createFromReadableStream
|
|
|
166
169
|
},
|
|
167
170
|
location: payload.location,
|
|
168
171
|
basename: payload.basename,
|
|
172
|
+
future: {},
|
|
169
173
|
isSpaMode: false
|
|
170
174
|
}),
|
|
171
175
|
async patchRoutesOnNavigation({ path, signal, fetcherKey }) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
+
import { escapeAttribute } from "../../dom/ssr/markup.js";
|
|
11
12
|
//#region lib/rsc/html-stream/server.ts
|
|
12
13
|
const encoder = new TextEncoder();
|
|
13
14
|
const trailer = "</body></html>";
|
|
@@ -73,7 +74,7 @@ async function writeRSCStream(reader, controller, isCancelled, nonce) {
|
|
|
73
74
|
const chunk = read.value;
|
|
74
75
|
try {
|
|
75
76
|
writeChunk(JSON.stringify(decoder.decode(chunk, { stream: true })), controller, nonce);
|
|
76
|
-
} catch
|
|
77
|
+
} catch {
|
|
77
78
|
writeChunk(`Uint8Array.from(atob(${JSON.stringify(btoa(String.fromCodePoint(...chunk)))}), m => m.codePointAt(0))`, controller, nonce);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
@@ -87,9 +88,6 @@ function writeChunk(chunk, controller, nonce) {
|
|
|
87
88
|
let nonceAttr = nonce == null ? "" : ` nonce="${escapeAttribute(nonce)}"`;
|
|
88
89
|
controller.enqueue(encoder.encode(`<script${nonceAttr}>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}<\/script>`));
|
|
89
90
|
}
|
|
90
|
-
function escapeAttribute(value) {
|
|
91
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
92
|
-
}
|
|
93
91
|
function escapeScript(script) {
|
|
94
92
|
return script.replace(/<!--/g, "<\\!--").replace(/<\/(script)/gi, "</\\$1");
|
|
95
93
|
}
|
|
@@ -116,6 +116,13 @@ type RouteDiscovery = {
|
|
|
116
116
|
* encoding an {@link unstable_RSCPayload} for consumption by an [RSC](https://react.dev/reference/rsc/server-components)
|
|
117
117
|
* enabled client router.
|
|
118
118
|
*
|
|
119
|
+
* Treat every React Server Function as a public endpoint. Server Functions are
|
|
120
|
+
* not inherently associated with a route. Any route middleware that runs is
|
|
121
|
+
* selected from the URL used to call a Server Function, but the client controls
|
|
122
|
+
* both the Server Function identifier and the URL. Perform all access control
|
|
123
|
+
* checks within each Server Function, or use a route action for
|
|
124
|
+
* middleware-driven access control.
|
|
125
|
+
*
|
|
119
126
|
* @example
|
|
120
127
|
* import {
|
|
121
128
|
* createTemporaryReferenceSet,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
11
|
import { isRouteErrorResponse } from "../router/utils.js";
|
|
12
|
-
import { hasInvalidProtocol } from "../router/router.js";
|
|
12
|
+
import { createDataRouteMatcher, hasInvalidProtocol } from "../router/router.js";
|
|
13
13
|
import { RSCRouterContext } from "../context.js";
|
|
14
14
|
import { decodeRedirectErrorDigest, decodeRouteErrorResponseDigest } from "../errors.js";
|
|
15
|
-
import {
|
|
15
|
+
import { escapeAttribute } from "../dom/ssr/markup.js";
|
|
16
16
|
import { shouldHydrateRouteLoader } from "../dom/ssr/routes.js";
|
|
17
17
|
import { FrameworkContext } from "../dom/ssr/components.js";
|
|
18
18
|
import { StaticRouterProvider, createStaticRouter } from "../dom/server.js";
|
|
@@ -179,7 +179,7 @@ async function routeRSCServerRequest({ request, serverResponse, createFromReadab
|
|
|
179
179
|
const redirectTransform = new TransformStream({ flush(controller) {
|
|
180
180
|
if (renderRedirect) {
|
|
181
181
|
if (hasInvalidProtocol(renderRedirect.location)) return;
|
|
182
|
-
controller.enqueue(new TextEncoder().encode(`<meta http-equiv="refresh" content="0;url=${
|
|
182
|
+
controller.enqueue(new TextEncoder().encode(`<meta http-equiv="refresh" content="0;url=${escapeAttribute(renderRedirect.location)}"/>`));
|
|
183
183
|
}
|
|
184
184
|
} });
|
|
185
185
|
if (!hydrate) return new Response(html.pipeThrough(redirectTransform), {
|
|
@@ -258,7 +258,7 @@ async function routeRSCServerRequest({ request, serverResponse, createFromReadab
|
|
|
258
258
|
const retryRedirectTransform = new TransformStream({ flush(controller) {
|
|
259
259
|
if (retryRedirect) {
|
|
260
260
|
if (hasInvalidProtocol(retryRedirect.location)) return;
|
|
261
|
-
controller.enqueue(new TextEncoder().encode(`<meta http-equiv="refresh" content="0;url=${
|
|
261
|
+
controller.enqueue(new TextEncoder().encode(`<meta http-equiv="refresh" content="0;url=${escapeAttribute(retryRedirect.location)}"/>`));
|
|
262
262
|
}
|
|
263
263
|
} });
|
|
264
264
|
if (!hydrate) return new Response(html.pipeThrough(retryRedirectTransform), {
|
|
@@ -273,7 +273,7 @@ async function routeRSCServerRequest({ request, serverResponse, createFromReadab
|
|
|
273
273
|
statusText,
|
|
274
274
|
headers
|
|
275
275
|
});
|
|
276
|
-
} catch
|
|
276
|
+
} catch {}
|
|
277
277
|
throw error;
|
|
278
278
|
}
|
|
279
279
|
}
|
|
@@ -333,6 +333,24 @@ function RSCStaticRouter({ getPayload, nonce }) {
|
|
|
333
333
|
if (payload.type !== "render") return null;
|
|
334
334
|
let patchedLoaderData = { ...payload.loaderData };
|
|
335
335
|
for (const match of payload.matches) if (shouldHydrateRouteLoader(match.id, match.clientLoader, match.hasLoader, false) && (match.hydrateFallbackElement || !match.hasLoader)) delete patchedLoaderData[match.id];
|
|
336
|
+
const routes = payload.matches.reduceRight((previous, match) => {
|
|
337
|
+
const route = {
|
|
338
|
+
id: match.id,
|
|
339
|
+
action: match.hasAction || !!match.clientAction,
|
|
340
|
+
element: match.element,
|
|
341
|
+
errorElement: match.errorElement,
|
|
342
|
+
handle: match.handle,
|
|
343
|
+
hydrateFallbackElement: match.hydrateFallbackElement,
|
|
344
|
+
index: match.index,
|
|
345
|
+
loader: match.hasLoader || !!match.clientLoader,
|
|
346
|
+
path: match.path,
|
|
347
|
+
shouldRevalidate: match.shouldRevalidate
|
|
348
|
+
};
|
|
349
|
+
if (previous.length > 0) route.children = previous;
|
|
350
|
+
return [route];
|
|
351
|
+
}, []);
|
|
352
|
+
const dataRouteMatcher = createDataRouteMatcher({}, payload.basename || "/");
|
|
353
|
+
dataRouteMatcher.update(routes);
|
|
336
354
|
const context = {
|
|
337
355
|
get _deepestRenderedBoundaryId() {
|
|
338
356
|
return decoded._deepestRenderedBoundaryId ?? null;
|
|
@@ -348,6 +366,7 @@ function RSCStaticRouter({ getPayload, nonce }) {
|
|
|
348
366
|
loaderHeaders: {},
|
|
349
367
|
location: payload.location,
|
|
350
368
|
statusCode: 200,
|
|
369
|
+
_match: (locationArg) => dataRouteMatcher.match(locationArg),
|
|
351
370
|
matches: payload.matches.map((match) => ({
|
|
352
371
|
params: match.params,
|
|
353
372
|
pathname: match.pathname,
|
|
@@ -363,22 +382,7 @@ function RSCStaticRouter({ getPayload, nonce }) {
|
|
|
363
382
|
}
|
|
364
383
|
}))
|
|
365
384
|
};
|
|
366
|
-
const router = createStaticRouter(
|
|
367
|
-
const route = {
|
|
368
|
-
id: match.id,
|
|
369
|
-
action: match.hasAction || !!match.clientAction,
|
|
370
|
-
element: match.element,
|
|
371
|
-
errorElement: match.errorElement,
|
|
372
|
-
handle: match.handle,
|
|
373
|
-
hydrateFallbackElement: match.hydrateFallbackElement,
|
|
374
|
-
index: match.index,
|
|
375
|
-
loader: match.hasLoader || !!match.clientLoader,
|
|
376
|
-
path: match.path,
|
|
377
|
-
shouldRevalidate: match.shouldRevalidate
|
|
378
|
-
};
|
|
379
|
-
if (previous.length > 0) route.children = previous;
|
|
380
|
-
return [route];
|
|
381
|
-
}, []), context);
|
|
385
|
+
const router = createStaticRouter(routes, context);
|
|
382
386
|
const frameworkContext = {
|
|
383
387
|
future: {},
|
|
384
388
|
isSpaMode: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -95,7 +95,7 @@ function encodeData(value) {
|
|
|
95
95
|
function decodeData(value) {
|
|
96
96
|
try {
|
|
97
97
|
return JSON.parse(decodeURIComponent(myEscape(atob(value))));
|
|
98
|
-
} catch
|
|
98
|
+
} catch {
|
|
99
99
|
return {};
|
|
100
100
|
}
|
|
101
101
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -26,7 +26,7 @@ const unsign = async (cookie, secret) => {
|
|
|
26
26
|
try {
|
|
27
27
|
let signature = byteStringToUint8Array(atob(hash));
|
|
28
28
|
return await crypto.subtle.verify("HMAC", key, signature, data) ? value : false;
|
|
29
|
-
} catch
|
|
29
|
+
} catch {
|
|
30
30
|
return false;
|
|
31
31
|
}
|
|
32
32
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -19,7 +19,7 @@ function getDevServerHooks() {
|
|
|
19
19
|
function getBuildTimeHeader(request, headerName) {
|
|
20
20
|
if (typeof process !== "undefined") try {
|
|
21
21
|
if (process.env.hasOwnProperty("IS_RR_BUILD_REQUEST") && process.env.IS_RR_BUILD_REQUEST === "yes") return request.headers.get(headerName);
|
|
22
|
-
} catch
|
|
22
|
+
} catch {}
|
|
23
23
|
return null;
|
|
24
24
|
}
|
|
25
25
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import { matchRoutesImpl } from "../router/utils.js";
|
|
12
11
|
import invariant from "./invariant.js";
|
|
13
12
|
//#region lib/server-runtime/routeMatching.ts
|
|
14
|
-
function matchServerRoutes(manifest,
|
|
15
|
-
let matches =
|
|
13
|
+
function matchServerRoutes(manifest, staticHandler, pathname) {
|
|
14
|
+
let matches = staticHandler.match(pathname);
|
|
16
15
|
if (!matches) return null;
|
|
17
16
|
return matches.map((match) => {
|
|
18
17
|
let route = manifest[match.route.id];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -90,12 +90,12 @@ function derive(build, mode) {
|
|
|
90
90
|
}
|
|
91
91
|
let manifestUrl = getManifestPath(build.routeDiscovery.manifestPath, build.basename);
|
|
92
92
|
if (build.routeDiscovery.mode === "lazy" && requestUrl.pathname === manifestUrl) try {
|
|
93
|
-
return await handleManifestRequest(build, staticHandler
|
|
93
|
+
return await handleManifestRequest(build, staticHandler, requestUrl);
|
|
94
94
|
} catch (e) {
|
|
95
95
|
handleError(e);
|
|
96
96
|
return new Response("Unknown Server Error", { status: 500 });
|
|
97
97
|
}
|
|
98
|
-
let matches = matchServerRoutes(build.routes, staticHandler
|
|
98
|
+
let matches = matchServerRoutes(build.routes, staticHandler, normalizedPathname);
|
|
99
99
|
if (matches && matches.length > 0) Object.assign(params, matches[0].params);
|
|
100
100
|
if (requestHandlerInstrumentations?.length) loadContext.set(instrumentationResultMetaContext, {
|
|
101
101
|
url: createDataFunctionUrl(request, normalizedPath),
|
|
@@ -173,7 +173,7 @@ const createRequestHandler = (build, mode) => {
|
|
|
173
173
|
return _requestHandler(request, initialContext);
|
|
174
174
|
};
|
|
175
175
|
};
|
|
176
|
-
async function handleManifestRequest(build,
|
|
176
|
+
async function handleManifestRequest(build, staticHandler, url) {
|
|
177
177
|
if (url.toString().length > 7680) return new Response(null, {
|
|
178
178
|
statusText: "Bad Request",
|
|
179
179
|
status: 400
|
|
@@ -188,7 +188,7 @@ async function handleManifestRequest(build, dataRoutes, branches, url) {
|
|
|
188
188
|
let paths = new Set(pathParam.split(",").filter(Boolean));
|
|
189
189
|
for (let path of paths) {
|
|
190
190
|
if (!path.startsWith("/")) path = `/${path}`;
|
|
191
|
-
let matches = matchServerRoutes(build.routes,
|
|
191
|
+
let matches = matchServerRoutes(build.routes, staticHandler, path);
|
|
192
192
|
if (matches) for (let match of matches) {
|
|
193
193
|
let routeId = match.route.id;
|
|
194
194
|
let route = build.assets.routes[routeId];
|
|
@@ -256,7 +256,7 @@ async function handleDocumentRequest(serverMode, build, staticHandler, request,
|
|
|
256
256
|
};
|
|
257
257
|
let entryContext = {
|
|
258
258
|
manifest: build.assets,
|
|
259
|
-
branches:
|
|
259
|
+
branches: [],
|
|
260
260
|
routeModules: createEntryRouteModules(build.routes),
|
|
261
261
|
staticHandlerContext: context,
|
|
262
262
|
criticalCss,
|
|
@@ -281,7 +281,7 @@ async function handleDocumentRequest(serverMode, build, staticHandler, request,
|
|
|
281
281
|
if (isResponse(error)) try {
|
|
282
282
|
let data = await unwrapResponse(error);
|
|
283
283
|
errorForSecondRender = new ErrorResponseImpl(error.status, error.statusText, data);
|
|
284
|
-
} catch
|
|
284
|
+
} catch {}
|
|
285
285
|
context = getStaticContextFromError(staticHandler.dataRoutes, context, errorForSecondRender);
|
|
286
286
|
if (context.errors) context.errors = sanitizeErrors(context.errors, serverMode);
|
|
287
287
|
let state = {
|
|
@@ -291,6 +291,7 @@ async function handleDocumentRequest(serverMode, build, staticHandler, request,
|
|
|
291
291
|
};
|
|
292
292
|
entryContext = {
|
|
293
293
|
...entryContext,
|
|
294
|
+
branches: [],
|
|
294
295
|
staticHandlerContext: context,
|
|
295
296
|
serverHandoffString: createServerHandoffString(baseServerHandoff),
|
|
296
297
|
serverHandoffStream: encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -23,7 +23,7 @@ async function singleFetchAction(build, serverMode, staticHandler, request, load
|
|
|
23
23
|
try {
|
|
24
24
|
try {
|
|
25
25
|
throwIfPotentialCSRFAttack(request, Array.isArray(build.allowedActionOrigins) ? build.allowedActionOrigins : []);
|
|
26
|
-
} catch
|
|
26
|
+
} catch {
|
|
27
27
|
return handleQueryError(/* @__PURE__ */ new Error("Bad Request"), 400);
|
|
28
28
|
}
|
|
29
29
|
return handleQueryResult(await staticHandler.query(request, {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-router v8.4.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) Remix Software Inc.
|
|
5
|
+
*
|
|
6
|
+
* This source code is licensed under the MIT license found in the
|
|
7
|
+
* LICENSE.md file in the root directory of this source tree.
|
|
8
|
+
*
|
|
9
|
+
* @license MIT
|
|
10
|
+
*/
|
|
11
|
+
import { unstable_preloadRoutePattern } from "./lib/router/matcher-route-pattern.preload.js";
|
|
12
|
+
export { unstable_preloadRoutePattern };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.4.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -41,7 +41,7 @@ async function decodeInitial(reader) {
|
|
|
41
41
|
let line;
|
|
42
42
|
try {
|
|
43
43
|
line = JSON.parse(read.value);
|
|
44
|
-
} catch
|
|
44
|
+
} catch {
|
|
45
45
|
throw new SyntaxError();
|
|
46
46
|
}
|
|
47
47
|
return {
|
|
@@ -64,7 +64,7 @@ async function decodeDeferred(reader) {
|
|
|
64
64
|
let jsonLine;
|
|
65
65
|
try {
|
|
66
66
|
jsonLine = JSON.parse(lineData);
|
|
67
|
-
} catch
|
|
67
|
+
} catch {
|
|
68
68
|
throw new SyntaxError();
|
|
69
69
|
}
|
|
70
70
|
const value = unflatten.call(this, jsonLine);
|
|
@@ -80,7 +80,7 @@ async function decodeDeferred(reader) {
|
|
|
80
80
|
let jsonLine;
|
|
81
81
|
try {
|
|
82
82
|
jsonLine = JSON.parse(lineData);
|
|
83
|
-
} catch
|
|
83
|
+
} catch {
|
|
84
84
|
throw new SyntaxError();
|
|
85
85
|
}
|
|
86
86
|
const value = unflatten.call(this, jsonLine);
|
package/docs/how-to/fetchers.md
CHANGED
|
@@ -85,7 +85,7 @@ export default function Component() {
|
|
|
85
85
|
|
|
86
86
|
### 3. Submit the form
|
|
87
87
|
|
|
88
|
-
If you submit the form now, the fetcher will call the action and revalidate the route data automatically.
|
|
88
|
+
If you submit the form now, the fetcher will call the action and revalidate the route data automatically. See [Revalidation Optimization](./optimize-revalidation) to skip that reload for a route or a single submission.
|
|
89
89
|
|
|
90
90
|
### 4. Render pending state
|
|
91
91
|
|
|
@@ -305,3 +305,8 @@ Fetchers can be submitted programmatically with `fetcher.submit`:
|
|
|
305
305
|
```
|
|
306
306
|
|
|
307
307
|
Note the input event's form is passed as the first argument to `fetcher.submit`. The fetcher will use that form to submit the request, reading its attributes and serializing the data from its elements.
|
|
308
|
+
|
|
309
|
+
`fetcher.submit` accepts the same [`defaultShouldRevalidate`][fetcher-submit-options] option as [`<Form>`][form]. Pass `false` to skip loader revalidation for that submission. See [Revalidation Optimization](./optimize-revalidation) for how that interacts with route-level `shouldRevalidate`.
|
|
310
|
+
|
|
311
|
+
[fetcher-submit-options]: https://api.reactrouter.com/v8/interfaces/react-router.FetcherSubmitOptions.html#defaultshouldrevalidate
|
|
312
|
+
[form]: ../api/components/Form#defaultshouldrevalidate
|