From ad0b614123395e7cae6ea808821b50cffd034d3b Mon Sep 17 00:00:00 2001 From: joseph0926 Date: Mon, 1 Sep 2025 20:45:47 +0900 Subject: [PATCH] fix(react-router v6): handle parameters with static suffixes in generatePath --- contributors.yml | 1 + .../__tests__/generatePath-test.tsx | 21 +++++++++++++++++++ packages/router/utils.ts | 6 +++--- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/contributors.yml b/contributors.yml index 65f570562c..1c544a4e8f 100644 --- a/contributors.yml +++ b/contributors.yml @@ -133,6 +133,7 @@ - jmargeta - johnpangalos - jonkoops +- joseph0926 - jrakotoharisoa - juanpprieto - jungwoo3490 diff --git a/packages/react-router/__tests__/generatePath-test.tsx b/packages/react-router/__tests__/generatePath-test.tsx index 70232f7d46..10f9205d7a 100644 --- a/packages/react-router/__tests__/generatePath-test.tsx +++ b/packages/react-router/__tests__/generatePath-test.tsx @@ -185,4 +185,25 @@ describe("generatePath", () => { consoleWarn.mockRestore(); }); + + describe("with params followed by static text", () => { + it("interpolates params with file extensions", () => { + expect(generatePath("/books/:id.json", { id: "42" })).toBe( + "/books/42.json" + ); + expect(generatePath("/api/:resource.xml", { resource: "users" })).toBe( + "/api/users.xml" + ); + expect(generatePath("/:lang.html", { lang: "en" })).toBe("/en.html"); + }); + + it("handles multiple extensions", () => { + expect(generatePath("/files/:name.tar.gz", { name: "archive" })).toBe( + "/files/archive.tar.gz" + ); + expect(generatePath("/:file.min.js", { file: "app" })).toBe( + "/app.min.js" + ); + }); + }); }); diff --git a/packages/router/utils.ts b/packages/router/utils.ts index 8e2a8f33f0..4c29cd2bd5 100644 --- a/packages/router/utils.ts +++ b/packages/router/utils.ts @@ -903,12 +903,12 @@ export function generatePath( return stringify(params[star]); } - const keyMatch = segment.match(/^:([\w-]+)(\??)$/); + const keyMatch = segment.match(/^:([\w-]+)(\??)(.*)/); if (keyMatch) { - const [, key, optional] = keyMatch; + const [, key, optional, suffix] = keyMatch; let param = params[key as PathParam]; invariant(optional === "?" || param != null, `Missing ":${key}" param`); - return stringify(param); + return stringify(param) + suffix; } // Remove any optional markers from optional static segments