Skip to content

Commit b4d4ff5

Browse files
committed
add matching for search params
1 parent 4914bee commit b4d4ff5

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

packages/route-pattern/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@mjackson/route-pattern",
3-
"version": "0.2.0",
3+
"version": "0.3.0",
44
"description": "Route patterns are strings that describe the structure of URLs you want to match",
55
"author": "Michael Jackson <[email protected]>",
66
"license": "MIT",

packages/route-pattern/src/lib/route-pattern.test.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,5 +469,113 @@ describe('RoutePattern', () => {
469469
});
470470
});
471471
});
472+
473+
describe('search params', () => {
474+
const searchTests = [
475+
{
476+
name: 'matches basic search param',
477+
pattern: 'search?q=test',
478+
input: 'https://example.com/search?q=test',
479+
expected: { params: {} },
480+
},
481+
{
482+
name: 'returns null for missing search param',
483+
pattern: 'search?q=test',
484+
input: 'https://example.com/search',
485+
expected: null,
486+
},
487+
{
488+
name: 'returns null for wrong search param value',
489+
pattern: 'search?q=test',
490+
input: 'https://example.com/search?q=other',
491+
expected: null,
492+
},
493+
{
494+
name: 'matches with extra search params',
495+
pattern: 'search?q=test',
496+
input: 'https://example.com/search?q=test&extra=value',
497+
expected: { params: {} },
498+
},
499+
{
500+
name: 'matches multiple search params',
501+
pattern: 'api?format=json&version=v1',
502+
input: 'https://example.com/api?format=json&version=v1',
503+
expected: { params: {} },
504+
},
505+
{
506+
name: 'returns null for missing one of multiple search params',
507+
pattern: 'api?format=json&version=v1',
508+
input: 'https://example.com/api?format=json',
509+
expected: null,
510+
},
511+
{
512+
name: 'matches search params in different order',
513+
pattern: 'api?format=json&version=v1',
514+
input: 'https://example.com/api?version=v1&format=json',
515+
expected: { params: {} },
516+
},
517+
{
518+
name: 'matches search param with empty value',
519+
pattern: 'search?q',
520+
input: 'https://example.com/search?q',
521+
expected: { params: {} },
522+
},
523+
{
524+
name: 'matches search param with special characters',
525+
pattern: 'search?q=hello%20world',
526+
input: 'https://example.com/search?q=hello%20world',
527+
expected: { params: {} },
528+
},
529+
{
530+
name: 'matches search param with URL-encoded values',
531+
pattern: 'search?q=test%26more',
532+
input: 'https://example.com/search?q=test%26more',
533+
expected: { params: {} },
534+
},
535+
{
536+
name: 'combines pathname params with search params',
537+
pattern: 'users/:id?format=json',
538+
input: 'https://example.com/users/123?format=json',
539+
expected: { params: { id: '123' } },
540+
},
541+
{
542+
name: 'combines protocol, hostname, pathname, and search',
543+
pattern: ':protocol://:subdomain.example.com/api/:version?format=json',
544+
input: 'https://api.example.com/api/v1?format=json',
545+
expected: { params: { protocol: 'https', subdomain: 'api', version: 'v1' } },
546+
},
547+
{
548+
name: 'matches search params with repeated values',
549+
pattern: 'search?tags=javascript',
550+
input: 'https://example.com/search?tags=javascript&tags=react',
551+
expected: { params: {} },
552+
},
553+
{
554+
name: 'returns null when required search param value not found in repeated values',
555+
pattern: 'search?tags=python',
556+
input: 'https://example.com/search?tags=javascript&tags=react',
557+
expected: null,
558+
},
559+
{
560+
name: 'handles search params with spaces in pattern',
561+
pattern: 'search?q=hello world',
562+
input: 'https://example.com/search?q=hello world',
563+
expected: { params: {} },
564+
},
565+
{
566+
name: 'handles complex search param combinations',
567+
pattern: 'results?page=1&limit=10&sort=date',
568+
input: 'https://example.com/results?page=1&limit=10&sort=date&extra=ignore',
569+
expected: { params: {} },
570+
},
571+
];
572+
573+
searchTests.forEach(({ name, pattern, input, expected }) => {
574+
it(name, () => {
575+
const routePattern = new RoutePattern(pattern);
576+
assert.deepStrictEqual(routePattern.match(input), expected);
577+
});
578+
});
579+
});
472580
});
473581
});

packages/route-pattern/src/lib/route-pattern.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ export class RoutePattern {
3434
if (!pathnameMatch) return null;
3535
Object.assign(params, pathnameMatch.groups ?? {});
3636

37-
// todo search
37+
if (this._ast.search) {
38+
for (const [key, value] of this._ast.search?.entries()) {
39+
if (!url.searchParams.getAll(key).includes(value)) return null;
40+
}
41+
}
3842

3943
return { params };
4044
}

0 commit comments

Comments
 (0)