Skip to content

Commit befc532

Browse files
committed
match constraints
1 parent 6d30637 commit befc532

File tree

1 file changed

+39
-19
lines changed

1 file changed

+39
-19
lines changed

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

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,60 @@
11
import type { Node } from './ast.ts';
22
import { parse } from './parse/parse.ts';
33

4+
type Params = Record<string, string | undefined>;
5+
type Constraint = (url: URL) => Params | null;
6+
47
export function createMatcher(sources: Array<string>) {
58
const patterns = sources.map((source) => {
69
const { protocol, hostname, pathname } = parse(source);
710

8-
const regexs = {
9-
protocol: protocol === undefined ? /.*/ : toRegExp(protocol),
10-
hostname: hostname === undefined ? /.*/ : toRegExp(hostname, /[^.]*/),
11-
pathname: pathname === undefined ? /^\/$/ : toRegExp(pathname, /[^/]*/),
12-
};
13-
return { pattern: source, regexs };
11+
const protocolRE = protocol === undefined ? /^.*$/ : toRegExp(protocol);
12+
const hostnameRE = hostname === undefined ? /^.*$/ : toRegExp(hostname, { param: /[^.]*/ });
13+
const pathnameRE = pathname === undefined ? /^\/$/ : toRegExp(pathname, { param: /[^/]*/ });
14+
15+
const constraints: Array<Constraint> = [
16+
(url) => (protocolRE.test(url.protocol) ? {} : null),
17+
(url) => {
18+
const match = hostnameRE.exec(url.hostname);
19+
if (!match) return null;
20+
return match.groups ?? {};
21+
},
22+
(url) => {
23+
const match = pathnameRE.exec(url.pathname.slice(1));
24+
if (!match) return null;
25+
return match.groups ?? {};
26+
},
27+
];
28+
29+
return { pattern: source, constraints };
1430
});
1531

1632
const match = function (url: string | URL) {
33+
if (typeof url === 'string') url = new URL(url);
34+
1735
const matches: Array<{ pattern: string; params: Record<string, string | undefined> }> = [];
18-
if (typeof url === 'string') {
19-
url = new URL(url);
20-
}
21-
for (const { pattern, regexs } of patterns) {
22-
const protocol = regexs.protocol.exec(url.protocol);
23-
const hostname = regexs.hostname.exec(url.hostname);
24-
const pathname = regexs.pathname.exec(url.pathname);
25-
if (protocol === null || hostname === null || pathname === null) {
26-
continue;
36+
for (const { pattern, constraints } of patterns) {
37+
let isMatch = true;
38+
const params: Params = {};
39+
for (const constraint of constraints) {
40+
const result = constraint(url);
41+
if (result === null) {
42+
isMatch = false;
43+
break;
44+
}
45+
Object.assign(params, result);
2746
}
28-
matches.push({ pattern, params: { ...hostname.groups, ...pathname.groups } });
47+
if (!isMatch) continue;
48+
matches.push({ pattern, params });
2949
}
3050
return matches;
3151
};
3252
return { match };
3353
}
3454

35-
export function toRegExp(part: Array<Node>, paramRegExp?: RegExp) {
36-
const source = toRegExpSource(part, paramRegExp);
37-
return new RegExp(source);
55+
export function toRegExp(part: Array<Node>, options?: { param: RegExp }) {
56+
const source = toRegExpSource(part, options?.param);
57+
return new RegExp('^' + source + '$');
3858
}
3959

4060
function escape(text: string): string {

0 commit comments

Comments
 (0)