Skip to content

Commit 71b1121

Browse files
committed
fix handling optional catchall routes
1 parent 7d6e198 commit 71b1121

File tree

2 files changed

+15
-3
lines changed

2 files changed

+15
-3
lines changed

packages/nextjs/src/config/manifest/buildManifest.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ function buildRegexForDynamicRoute(routePath: string): { pattern: string; paramN
4848
const segments = routePath.split('/').filter(Boolean);
4949
const regexSegments: string[] = [];
5050
const paramNames: string[] = [];
51+
let hasOptionalCatchall = false;
5152

5253
for (const segment of segments) {
5354
if (segment.startsWith(':')) {
@@ -57,7 +58,8 @@ function buildRegexForDynamicRoute(routePath: string): { pattern: string; paramN
5758
// Optional catchall: matches zero or more segments
5859
const cleanParamName = paramName.slice(0, -2);
5960
paramNames.push(cleanParamName);
60-
regexSegments.push('(.*)');
61+
// Handling this special case in pattern construction below
62+
hasOptionalCatchall = true;
6163
} else if (paramName.endsWith('*')) {
6264
// Required catchall: matches one or more segments
6365
const cleanParamName = paramName.slice(0, -1);
@@ -74,7 +76,16 @@ function buildRegexForDynamicRoute(routePath: string): { pattern: string; paramN
7476
}
7577
}
7678

77-
const pattern = `^/${regexSegments.join('/')}$`;
79+
let pattern: string;
80+
if (hasOptionalCatchall) {
81+
// For optional catchall, make the trailing slash and segments optional
82+
// This allows matching both /catchall and /catchall/anything
83+
const staticParts = regexSegments.join('/');
84+
pattern = `^/${staticParts}(?:/(.*))?$`;
85+
} else {
86+
pattern = `^/${regexSegments.join('/')}$`;
87+
}
88+
7889
return { pattern, paramNames };
7990
}
8091

packages/nextjs/test/config/manifest/suites/catchall/catchall.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('catchall', () => {
1111
{
1212
path: '/catchall/:path*?',
1313
dynamic: true,
14-
pattern: '^/catchall/(.*)$',
14+
pattern: '^/catchall(?:/(.*))?$',
1515
paramNames: ['path'],
1616
},
1717
],
@@ -26,6 +26,7 @@ describe('catchall', () => {
2626
expect(regex.test('/catchall/123/456')).toBe(true);
2727
expect(regex.test('/catchall/123/abc/789')).toBe(true);
2828
expect(regex.test('/catchall/')).toBe(true);
29+
expect(regex.test('/catchall')).toBe(true);
2930
expect(regex.test('/123/catchall/123')).toBe(false);
3031
expect(regex.test('/')).toBe(false);
3132
});

0 commit comments

Comments
 (0)