Skip to content

Commit d103537

Browse files
authored
Pick up dashes in dynamic parameter names (#11160)
1 parent c951e7c commit d103537

File tree

4 files changed

+58
-6
lines changed

4 files changed

+58
-6
lines changed

.changeset/dynamic-param-dash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/router": patch
3+
---
4+
5+
Fix bug where dashes were not picked up in dynamic parameter names

packages/react-router/__tests__/generatePath-test.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ describe("generatePath", () => {
5252
// incorrect usage but worked in 6.3.0 so keep it to avoid the regression
5353
expect(generatePath("/courses/*", { "*": 0 })).toBe("/courses/0");
5454
});
55+
56+
it("handles dashes in dynamic params", () => {
57+
expect(generatePath("/courses/:foo-bar", { "foo-bar": "baz" })).toBe(
58+
"/courses/baz"
59+
);
60+
});
5561
});
5662

5763
describe("with extraneous params", () => {

packages/react-router/__tests__/path-matching-test.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,44 @@ describe("path matching", () => {
130130

131131
expect(pickPaths(routes, "/page")).toEqual(["page"]);
132132
});
133+
134+
test("dynamic segments can contain dashes", () => {
135+
let routes = [
136+
{
137+
path: ":foo-bar",
138+
},
139+
{
140+
path: "foo-bar",
141+
},
142+
];
143+
144+
expect(matchRoutes(routes, "/foo-bar")).toMatchInlineSnapshot(`
145+
[
146+
{
147+
"params": {},
148+
"pathname": "/foo-bar",
149+
"pathnameBase": "/foo-bar",
150+
"route": {
151+
"path": "foo-bar",
152+
},
153+
},
154+
]
155+
`);
156+
expect(matchRoutes(routes, "/whatever")).toMatchInlineSnapshot(`
157+
[
158+
{
159+
"params": {
160+
"foo-bar": "whatever",
161+
},
162+
"pathname": "/whatever",
163+
"pathnameBase": "/whatever",
164+
"route": {
165+
"path": ":foo-bar",
166+
},
167+
},
168+
]
169+
`);
170+
});
133171
});
134172

135173
describe("path matching with a basename", () => {

packages/router/utils.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ function rankRouteBranches(branches: RouteBranch[]): void {
685685
);
686686
}
687687

688-
const paramRe = /^:\w+$/;
688+
const paramRe = /^:[\w-]+$/;
689689
const dynamicSegmentValue = 3;
690690
const indexRouteValue = 2;
691691
const emptySegmentValue = 1;
@@ -822,7 +822,7 @@ export function generatePath<Path extends string>(
822822
return stringify(params[star]);
823823
}
824824

825-
const keyMatch = segment.match(/^:(\w+)(\??)$/);
825+
const keyMatch = segment.match(/^:([\w-]+)(\??)$/);
826826
if (keyMatch) {
827827
const [, key, optional] = keyMatch;
828828
let param = params[key as PathParam<Path>];
@@ -967,10 +967,13 @@ function compilePath(
967967
.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
968968
.replace(/^\/*/, "/") // Make sure it has a leading /
969969
.replace(/[\\.*+^${}|()[\]]/g, "\\$&") // Escape special regex chars
970-
.replace(/\/:(\w+)(\?)?/g, (_: string, paramName: string, isOptional) => {
971-
params.push({ paramName, isOptional: isOptional != null });
972-
return isOptional ? "/?([^\\/]+)?" : "/([^\\/]+)";
973-
});
970+
.replace(
971+
/\/:([\w-]+)(\?)?/g,
972+
(_: string, paramName: string, isOptional) => {
973+
params.push({ paramName, isOptional: isOptional != null });
974+
return isOptional ? "/?([^\\/]+)?" : "/([^\\/]+)";
975+
}
976+
);
974977

975978
if (path.endsWith("*")) {
976979
params.push({ paramName: "*" });

0 commit comments

Comments
 (0)