diff --git a/.changeset/real-cups-pump.md b/.changeset/real-cups-pump.md new file mode 100644 index 0000000000..63dab828ae --- /dev/null +++ b/.changeset/real-cups-pump.md @@ -0,0 +1,5 @@ +--- +"@remix-run/router": patch +--- + +Fix optional parameter matching diff --git a/contributors.yml b/contributors.yml index 8f0a2cfa64..ed79ab2d5e 100644 --- a/contributors.yml +++ b/contributors.yml @@ -167,6 +167,7 @@ - morleytatro - ms10596 - ned-park +- neemzy - nilubisan - Nismit - nnhjs diff --git a/packages/router/__tests__/match-path-test.ts b/packages/router/__tests__/match-path-test.ts new file mode 100644 index 0000000000..a3a5d58d3d --- /dev/null +++ b/packages/router/__tests__/match-path-test.ts @@ -0,0 +1,30 @@ +import { matchPath } from "@remix-run/router"; + +describe("matchPath", () => { + describe("given a pattern with optional parameters", () => { + it("matches paths with or without these optional parameters", () => { + const pattern = { path: "/foo/:bar?/:baz?" }; + + expect(matchPath(pattern, "/foo/bar/baz")).toEqual({ + params: { bar: "bar", baz: "baz" }, + pathname: "/foo/bar/baz", + pathnameBase: "/foo/bar/baz", + pattern + }); + + expect(matchPath(pattern, "/foo/bar")).toEqual({ + params: { bar: "bar", baz: "" }, + pathname: "/foo/bar", + pathnameBase: "/foo/bar", + pattern + }); + + expect(matchPath(pattern, "/foo")).toEqual({ + params: { bar: "", baz: "" }, + pathname: "/foo", + pathnameBase: "/foo", + pattern + }); + }); + }); +}); diff --git a/packages/router/utils.ts b/packages/router/utils.ts index 5b50f6d761..8658e8aaa2 100644 --- a/packages/router/utils.ts +++ b/packages/router/utils.ts @@ -962,10 +962,10 @@ function compilePath( path .replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below .replace(/^\/*/, "/") // Make sure it has a leading / - .replace(/[\\.*+^$?{}|()[\]]/g, "\\$&") // Escape special regex chars - .replace(/\/:(\w+)/g, (_: string, paramName: string) => { + .replace(/[\\.*+^${}|()[\]]/g, "\\$&") // Escape special regex chars + .replace(/\/:(\w+)(\??)/g, (_: string, paramName: string, optional: string) => { paramNames.push(paramName); - return "/([^\\/]+)"; + return `/${optional}([^\\/]+)${optional}`; }); if (path.endsWith("*")) {