From d70c8a46919dfd5f051c04607063749d7891bbee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=98=81=ED=9B=88?= Date: Mon, 1 Sep 2025 10:21:45 +0900 Subject: [PATCH] fix: handle parameters with static suffixes in generatePath --- contributors.yml | 1 + .../__tests__/generatePath-test.tsx | 21 +++++++++++++++++++ packages/react-router/lib/router/utils.ts | 6 +++--- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/contributors.yml b/contributors.yml index e6c663f7be..06bcb5c118 100644 --- a/contributors.yml +++ b/contributors.yml @@ -187,6 +187,7 @@ - jmjpro - johnpangalos - jonkoops +- joseph0926 - jrakotoharisoa - jrestall - juanpprieto diff --git a/packages/react-router/__tests__/generatePath-test.tsx b/packages/react-router/__tests__/generatePath-test.tsx index c4fbf35b7b..3bf3a18433 100644 --- a/packages/react-router/__tests__/generatePath-test.tsx +++ b/packages/react-router/__tests__/generatePath-test.tsx @@ -191,4 +191,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/react-router/lib/router/utils.ts b/packages/react-router/lib/router/utils.ts index 14e5e40c51..cd60e83e85 100644 --- a/packages/react-router/lib/router/utils.ts +++ b/packages/react-router/lib/router/utils.ts @@ -1301,12 +1301,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 encodeURIComponent(stringify(param)); + return encodeURIComponent(stringify(param)) + suffix; } // Remove any optional markers from optional static segments