Skip to content

Commit 68d3424

Browse files
authored
fix: nextjs parallel routes with catchall isn't supported (#69)
1 parent e5872fb commit 68d3424

File tree

3 files changed

+43
-12
lines changed

3 files changed

+43
-12
lines changed

packages/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@vercel/speed-insights",
3-
"version": "1.0.10",
3+
"version": "1.0.11",
44
"description": "Speed Insights is a tool for measuring web performance and providing suggestions for improvement.",
55
"keywords": [
66
"speed-insights",

packages/web/src/utils.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,21 @@ describe('utils', () => {
4646
const input = '/en/us/next-site';
4747
const params = {
4848
langs: ['en', 'us'],
49-
teamSlug: 'vercel',
5049
};
5150
const expected = '/[...langs]/next-site';
5251
expect(computeRoute(input, params)).toBe(expected);
5352
});
5453

54+
it('handles array segments and individual segments', () => {
55+
const input = '/en/us/next-site';
56+
const params = {
57+
langs: ['en', 'us'],
58+
team: 'next-site',
59+
};
60+
const expected = '/[...langs]/[team]';
61+
expect(computeRoute(input, params)).toBe(expected);
62+
});
63+
5564
it('handles special characters in url', () => {
5665
const input = '/123/test(test';
5766
const params = {
@@ -73,6 +82,17 @@ describe('utils', () => {
7382
expect(computeRoute(input, params)).toBe(expected);
7483
});
7584

85+
it('parallel routes where params matched both individually and within arrays', () => {
86+
const params = {
87+
catchAll: ['m', 'john', 'p', 'shirt'],
88+
merchantId: 'john',
89+
productSlug: 'shirt',
90+
};
91+
expect(computeRoute('/m/john/p/shirt', params)).toBe(
92+
'/m/[merchantId]/p/[productSlug]',
93+
);
94+
});
95+
7696
describe('edge case handling (same values for multiple params)', () => {
7797
it('replaces based on the priority of the pathParams keys', () => {
7898
const input = '/test/test';

packages/web/src/utils.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,36 @@ export function computeRoute(
3131
}
3232

3333
let result = pathname;
34-
3534
try {
36-
for (const [key, valueOrArray] of Object.entries(pathParams)) {
37-
const isValueArray = Array.isArray(valueOrArray);
38-
const value = isValueArray ? valueOrArray.join('/') : valueOrArray;
39-
const expr = isValueArray ? `...${key}` : key;
40-
41-
const matcher = new RegExp(`/${escapeRegExp(value)}(?=[/?#]|$)`);
42-
if (matcher.test(result)) {
43-
result = result.replace(matcher, `/[${expr}]`);
35+
const entries = Object.entries(pathParams);
36+
// simple keys must be handled first
37+
for (const [key, value] of entries) {
38+
if (!Array.isArray(value)) {
39+
const matcher = turnValueToRegExp(value);
40+
if (matcher.test(result)) {
41+
result = result.replace(matcher, `/[${key}]`);
42+
}
43+
}
44+
}
45+
// array values next
46+
for (const [key, value] of entries) {
47+
if (Array.isArray(value)) {
48+
const matcher = turnValueToRegExp(value.join('/'));
49+
if (matcher.test(result)) {
50+
result = result.replace(matcher, `/[...${key}]`);
51+
}
4452
}
4553
}
46-
4754
return result;
4855
} catch (e) {
4956
return pathname;
5057
}
5158
}
5259

60+
function turnValueToRegExp(value: string): RegExp {
61+
return new RegExp(`/${escapeRegExp(value)}(?=[/?#]|$)`);
62+
}
63+
5364
function escapeRegExp(string: string): string {
5465
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
5566
}

0 commit comments

Comments
 (0)