Skip to content

Commit 714357b

Browse files
authored
feat: add router.matchAll for multiple matches (#602)
1 parent eb41e5c commit 714357b

4 files changed

Lines changed: 78 additions & 0 deletions

File tree

.changeset/smart-apes-smoke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@blinkk/root': patch
3+
---
4+
5+
feat: add router.matchAll for multiple matches

packages/root/src/render/route-trie.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,15 @@ test('walk all routes', async () => {
124124
expected.sort((a, b) => a[0].localeCompare(b[0]))
125125
);
126126
});
127+
128+
test('match all matching routes', () => {
129+
routeTrie.add('/[[...slug]]', 'a');
130+
routeTrie.add('/foo/[id]', 'b');
131+
routeTrie.add('/foo/bar', 'c');
132+
133+
assert.deepEqual(routeTrie.matchAll('/foo/bar'), [
134+
['c', {}],
135+
['b', {id: 'bar'}],
136+
['a', {slug: 'foo/bar'}],
137+
]);
138+
});

packages/root/src/render/route-trie.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ export class RouteTrie<T> {
6464
return [route, params];
6565
}
6666

67+
/**
68+
* Returns all routes that match the given path and their parameter values.
69+
*/
70+
matchAll(path: string): Array<[T, Record<string, string>]> {
71+
const matches: Array<[T, Record<string, string>]> = [];
72+
this.collectRoutes(path, {}, matches);
73+
return matches;
74+
}
75+
6776
/**
6877
* Walks the route trie and calls a callback function for each route.
6978
*/
@@ -165,6 +174,54 @@ export class RouteTrie<T> {
165174
return undefined;
166175
}
167176

177+
private collectRoutes(
178+
urlPath: string,
179+
params: Record<string, string>,
180+
results: Array<[T, Record<string, string>]>
181+
) {
182+
urlPath = this.normalizePath(urlPath);
183+
if (urlPath === '') {
184+
if (this.route) {
185+
results.push([this.route, {...params}]);
186+
}
187+
if (this.optCatchAllNodes) {
188+
const optParams = {...params};
189+
if (urlPath) {
190+
optParams[this.optCatchAllNodes.name] = urlPath;
191+
}
192+
results.push([this.optCatchAllNodes.route, optParams]);
193+
}
194+
return;
195+
}
196+
197+
const [head, tail] = this.splitPath(urlPath);
198+
199+
const child = this.children[head];
200+
if (child) {
201+
child.collectRoutes(tail, {...params}, results);
202+
}
203+
204+
if (this.paramNodes) {
205+
for (const paramChild of Object.values(this.paramNodes)) {
206+
const paramParams = {...params, [paramChild.name]: head};
207+
paramChild.trie.collectRoutes(tail, paramParams, results);
208+
}
209+
}
210+
211+
if (this.catchAllNodes) {
212+
const caParams = {...params, [this.catchAllNodes.name]: urlPath};
213+
results.push([this.catchAllNodes.route, caParams]);
214+
}
215+
216+
if (this.optCatchAllNodes) {
217+
const ocParams = {...params};
218+
if (urlPath) {
219+
ocParams[this.optCatchAllNodes.name] = urlPath;
220+
}
221+
results.push([this.optCatchAllNodes.route, ocParams]);
222+
}
223+
}
224+
168225
/**
169226
* Normalizes a path for inclusion into the route trie.
170227
*/

packages/root/src/render/router.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export class Router {
2121
return this.routeTrie.get(url);
2222
}
2323

24+
matchAll(url: string) {
25+
return this.routeTrie.matchAll(url);
26+
}
27+
2428
async walk(cb: (urlPath: string, route: Route) => void | Promise<void>) {
2529
await this.routeTrie.walk(cb);
2630
}

0 commit comments

Comments
 (0)