Skip to content

Commit 33e7685

Browse files
committed
avoid regexp
1 parent 4ec7b37 commit 33e7685

File tree

2 files changed

+52
-6
lines changed

2 files changed

+52
-6
lines changed

packages/astro/src/server/middleware.ts

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,55 @@ export function interpolateRouteFromUrlAndParams(
212212
return undefined;
213213
}
214214

215-
return Object.entries(params).reduce((interpolateRoute, value) => {
216-
const [paramId, paramValue] = value;
217-
if (!paramValue) {
218-
return interpolateRoute;
215+
// Invert params map so that the param values are the keys
216+
// differentiate between rest params spanning multiple url segments
217+
// and normal, single-segment params.
218+
const valuesToMultiSegmentParams: Record<string, string> = {};
219+
const valuesToParams: Record<string, string> = {};
220+
Object.entries(params).forEach(([key, value]) => {
221+
if (!value) {
222+
return;
219223
}
220-
return interpolateRoute.replace(new RegExp(`(/|-)${escapeStringForRegex(paramValue)}(/|-|$)`), `$1[${paramId}]$2`);
224+
if (value.includes('/')) {
225+
valuesToMultiSegmentParams[value] = key;
226+
return;
227+
}
228+
valuesToParams[value] = key;
229+
});
230+
231+
function replaceWithParamName(segment: string): string {
232+
const param = valuesToParams[segment];
233+
if (param) {
234+
return `[${param}]`;
235+
}
236+
return segment;
237+
}
238+
239+
// before we match single-segment params, we first replace multi-segment params
240+
const urlWithReplacedMultiSegmentParams = Object.keys(valuesToMultiSegmentParams).reduce((acc, key) => {
241+
return acc.replace(key, `[${valuesToMultiSegmentParams[key]}]`);
221242
}, decodedUrlPathname);
243+
244+
return urlWithReplacedMultiSegmentParams
245+
.split('/')
246+
.map(segment => {
247+
if (!segment) {
248+
return '';
249+
}
250+
251+
if (valuesToParams[segment]) {
252+
return replaceWithParamName(segment);
253+
}
254+
255+
// astro permits multiple params in a single path segment, e.g. /[foo]-[bar]/
256+
const segmentParts = segment.split('-');
257+
if (segmentParts.length > 1) {
258+
return segmentParts.map(part => replaceWithParamName(part)).join('-');
259+
}
260+
261+
return segment;
262+
})
263+
.join('/');
222264
}
223265

224266
function tryDecodeUrl(url: string): string | undefined {

packages/astro/test/server/middleware.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,15 @@ describe('sentryMiddleware', () => {
336336

337337
describe('interpolateRouteFromUrlAndParams', () => {
338338
it.each([
339+
['/', {}, '/'],
339340
['/foo/bar', {}, '/foo/bar'],
340341
['/users/123', { id: '123' }, '/users/[id]'],
341342
['/users/123', { id: '123', foo: 'bar' }, '/users/[id]'],
342343
['/lang/en-US', { lang: 'en', region: 'US' }, '/lang/[lang]-[region]'],
343344
['/lang/en-US/posts', { lang: 'en', region: 'US' }, '/lang/[lang]-[region]/posts'],
345+
// edge cases that astro doesn't support
346+
['/lang/-US', { region: 'US' }, '/lang/-[region]'],
347+
['/lang/en-', { lang: 'en' }, '/lang/[lang]-'],
344348
])('interpolates route from URL and params %s', (rawUrl, params, expectedRoute) => {
345349
expect(interpolateRouteFromUrlAndParams(rawUrl, params)).toEqual(expectedRoute);
346350
});
@@ -376,7 +380,7 @@ describe('interpolateRouteFromUrlAndParams', () => {
376380

377381
it('handles set but undefined params', () => {
378382
const rawUrl = '/usernames/user';
379-
const params = { name: undefined };
383+
const params = { name: undefined, name2: '' };
380384
const expectedRoute = '/usernames/user';
381385
expect(interpolateRouteFromUrlAndParams(rawUrl, params)).toEqual(expectedRoute);
382386
});

0 commit comments

Comments
 (0)