From 7dc7f354a6c0d50a2941ba08fd4f99279ac624ca Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Fri, 21 Oct 2022 17:33:02 -0400 Subject: [PATCH] preserve search/hash in processed redirects (#9489) * preserve search/hash in redirects * bundle bump --- package.json | 2 +- packages/router/__tests__/router-test.ts | 55 ++++++++++++++++++++++++ packages/router/router.ts | 11 +++-- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 710c09c033..b8f4abe777 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ }, "filesize": { "packages/router/dist/router.js": { - "none": "106 kB" + "none": "108 kB" }, "packages/react-router/dist/react-router.production.min.js": { "none": "12.5 kB" diff --git a/packages/router/__tests__/router-test.ts b/packages/router/__tests__/router-test.ts index 9f5e8ee2c5..9950886704 100644 --- a/packages/router/__tests__/router-test.ts +++ b/packages/router/__tests__/router-test.ts @@ -5350,6 +5350,61 @@ describe("a router", () => { errors: null, }); }); + + it("preserves query and hash in redirects", async () => { + let t = setup({ routes: REDIRECT_ROUTES }); + + let nav1 = await t.fetch("/parent/child", { + formMethod: "post", + formData: createFormData({}), + }); + + let nav2 = await nav1.actions.child.redirectReturn( + "/parent?key=value#hash" + ); + await nav2.loaders.parent.resolve("PARENT"); + expect(t.router.state).toMatchObject({ + location: { + pathname: "/parent", + search: "?key=value", + hash: "#hash", + }, + navigation: IDLE_NAVIGATION, + loaderData: { + parent: "PARENT", + }, + errors: null, + }); + }); + + it("preserves query and hash in relative redirects", async () => { + let t = setup({ routes: REDIRECT_ROUTES }); + + let nav1 = await t.fetch("/parent/child", { + formMethod: "post", + formData: createFormData({}), + }); + + let nav2 = await nav1.actions.child.redirectReturn( + "..?key=value#hash", + undefined, + undefined, + ["parent"] + ); + await nav2.loaders.parent.resolve("PARENT"); + expect(t.router.state).toMatchObject({ + location: { + pathname: "/parent", + search: "?key=value", + hash: "#hash", + }, + navigation: IDLE_NAVIGATION, + loaderData: { + parent: "PARENT", + }, + errors: null, + }); + }); }); describe("scroll restoration", () => { diff --git a/packages/router/router.ts b/packages/router/router.ts index e83f6aba52..b409ffc38c 100644 --- a/packages/router/router.ts +++ b/packages/router/router.ts @@ -2561,18 +2561,21 @@ async function callLoaderOrAction( (match) => match.pathnameBase ); let requestPath = createURL(request.url).pathname; - location = resolveTo(location, routePathnames, requestPath).pathname; + let resolvedLocation = resolveTo(location, routePathnames, requestPath); invariant( - location, + createPath(resolvedLocation), `Unable to resolve redirect location: ${result.headers.get("Location")}` ); // Prepend the basename to the redirect location if we have one if (basename) { - let path = createURL(location).pathname; - location = path === "/" ? basename : joinPaths([basename, path]); + let path = resolvedLocation.pathname; + resolvedLocation.pathname = + path === "/" ? basename : joinPaths([basename, path]); } + location = createPath(resolvedLocation); + // Don't process redirects in the router during static requests requests. // Instead, throw the Response and let the server handle it with an HTTP // redirect. We also update the Location header in place in this flow so