Skip to content

Commit 4408b70

Browse files
committed
make second names argument mandatory for regexes
1 parent 6b49532 commit 4408b70

File tree

5 files changed

+40
-68
lines changed

5 files changed

+40
-68
lines changed

README.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,21 +204,17 @@ unnamed wildcards are not collected.
204204
## make pattern from regex
205205

206206
```javascript
207-
> const pattern = new UrlPattern(/^\/api\/(.*)$/);
208-
```
209-
210-
if the pattern was created from a regex an array of the captured groups is returned on a match:
207+
> const pattern = new UrlPattern(/^\/api\/(.*)$/, ["path"]);
211208
212-
```javascript
213209
> pattern.match("/api/users");
214-
["users"]
210+
{path: "users"}
215211
216212
> pattern.match("/apiii/test");
217213
undefined
218214
```
219215

220216
when making a pattern from a regex
221-
you can pass an array of keys as the second argument.
217+
you have to pass an array of keys as the second argument.
222218
returns objects on match with each key mapped to a captured value:
223219

224220
```javascript

src/url-pattern.ts

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export default class UrlPattern {
2626
public readonly isRegex: boolean;
2727
public readonly regex: RegExp;
2828
public readonly ast?: Array<Ast<any>>;
29-
public readonly names?: string[];
29+
public readonly names: string[];
3030

3131
constructor(pattern: string, options?: IUserInputOptions);
3232
constructor(pattern: RegExp, groupNames?: string[]);
@@ -51,22 +51,25 @@ export default class UrlPattern {
5151
// handle regex pattern and return early
5252
if (pattern instanceof RegExp) {
5353
this.regex = pattern;
54-
if (optionsOrGroupNames != null) {
55-
if (!Array.isArray(optionsOrGroupNames)) {
56-
throw new TypeError([
57-
"if first argument is a RegExp the second argument",
58-
"may be an Array<String> of group names",
59-
"but you provided something else",
60-
].join(" "));
61-
}
62-
const groupCount = regexGroupCount(this.regex);
63-
if (optionsOrGroupNames.length !== groupCount) {
64-
throw new Error([
65-
`regex contains ${ groupCount } groups`,
66-
`but array of group names contains ${ optionsOrGroupNames.length }`,
67-
].join(" "));
68-
}
69-
this.names = optionsOrGroupNames;
54+
if (optionsOrGroupNames == null || !Array.isArray(optionsOrGroupNames)) {
55+
throw new TypeError([
56+
"if first argument is a RegExp the second argument",
57+
"must be an Array<String> of group names",
58+
].join(" "));
59+
}
60+
const groupCount = regexGroupCount(this.regex);
61+
if (optionsOrGroupNames.length !== groupCount) {
62+
throw new Error([
63+
`regex contains ${ groupCount } groups`,
64+
`but array of group names contains ${ optionsOrGroupNames.length }`,
65+
].join(" "));
66+
}
67+
this.names = optionsOrGroupNames;
68+
const regexNameIndex = indexOfDuplicateElement(this.names);
69+
if (regexNameIndex !== -1) {
70+
throw new Error(
71+
`duplicate name "${ this.names[regexNameIndex] }" in pattern. names must be unique`,
72+
);
7073
}
7174
return;
7275
}
@@ -114,24 +117,20 @@ export default class UrlPattern {
114117
}
115118
}
116119

117-
public match(url: string): { [index: string]: string } | string[] | undefined {
120+
public match(url: string): { [index: string]: string } | undefined {
118121
const match = this.regex.exec(url);
119122
if (match == null) {
120123
return;
121124
}
122125

123126
const groups = match.slice(1);
124-
if (this.names != null) {
125-
const mergedNamesAndGroups: { [index: string]: string } = {};
126-
for (let i = 0; i < this.names.length; i++) {
127-
if (groups[i] != null) {
128-
mergedNamesAndGroups[this.names[i]] = groups[i];
129-
}
127+
const mergedNamesAndGroups: { [index: string]: string } = {};
128+
for (let i = 0; i < this.names.length; i++) {
129+
if (groups[i] != null) {
130+
mergedNamesAndGroups[this.names[i]] = groups[i];
130131
}
131-
return mergedNamesAndGroups;
132-
} else {
133-
return groups;
134132
}
133+
return mergedNamesAndGroups;
135134
}
136135

137136
public stringify(params?: object): string {

test/errors.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,9 @@ tape("regex names", (t: tape.Test) => {
117117
try {
118118
new UntypedUrlPattern(/x/, 5);
119119
} catch (error) {
120-
t.equal(error.message, [
121-
"if first argument is a RegExp the second argument may be an Array<String>",
122-
"of group names but you provided something else",
123-
].join(" "));
120+
t.equal(error.message,
121+
"if first argument is a RegExp the second argument must be an Array<String> of group names",
122+
);
124123
}
125124
try {
126125
new UrlPattern(/(((foo)bar(boo))far)/, []);
@@ -137,7 +136,7 @@ tape("regex names", (t: tape.Test) => {
137136

138137
tape("stringify regex", (t: tape.Test) => {
139138
t.plan(1);
140-
const pattern = new UrlPattern(/x/);
139+
const pattern = new UrlPattern(/x/, []);
141140
try {
142141
pattern.stringify();
143142
} catch (error) {

test/match-fixtures.ts

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,12 @@ tape("match", (t: tape.Test) => {
2424
pattern = new UrlPattern(".foo");
2525
t.equals(pattern.match(".bar.foo"), undefined);
2626

27-
pattern = new UrlPattern(/foo/);
27+
pattern = new UrlPattern(/foo/, []);
2828
t.deepEqual(pattern.match("foo"), []);
2929

30-
pattern = new UrlPattern(/\/foo\/(.*)/);
31-
t.deepEqual(pattern.match("/foo/bar"), ["bar"]);
32-
33-
pattern = new UrlPattern(/\/foo\/(.*)/);
34-
t.deepEqual(pattern.match("/foo/"), [""]);
30+
pattern = new UrlPattern(/\/foo\/(.*)/, ["path"]);
31+
t.deepEqual(pattern.match("/foo/bar"), {path: "bar"});
32+
t.deepEqual(pattern.match("/foo/"), {path: ""});
3533

3634
pattern = new UrlPattern("/user/:userId/task/:taskId");
3735
t.deepEqual(pattern.match("/user/10/task/52"), {
@@ -308,27 +306,7 @@ tape("match", (t: tape.Test) => {
308306
scheme: "https",
309307
});
310308

311-
let regex = /\/ip\/(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
312-
pattern = new UrlPattern(regex);
313-
t.equal(undefined, pattern.match("10.10.10.10"));
314-
t.equal(undefined, pattern.match("ip/10.10.10.10"));
315-
t.equal(undefined, pattern.match("/ip/10.10.10."));
316-
t.equal(undefined, pattern.match("/ip/10."));
317-
t.equal(undefined, pattern.match("/ip/"));
318-
t.deepEqual(pattern.match("/ip/10.10.10.10"), ["10", "10", "10", "10"]);
319-
t.deepEqual(pattern.match("/ip/127.0.0.1"), ["127", "0", "0", "1"]);
320-
321-
regex = /\/ip\/((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$/;
322-
pattern = new UrlPattern(regex);
323-
t.equal(undefined, pattern.match("10.10.10.10"));
324-
t.equal(undefined, pattern.match("ip/10.10.10.10"));
325-
t.equal(undefined, pattern.match("/ip/10.10.10."));
326-
t.equal(undefined, pattern.match("/ip/10."));
327-
t.equal(undefined, pattern.match("/ip/"));
328-
t.deepEqual(pattern.match("/ip/10.10.10.10"), ["10.10.10.10"]);
329-
t.deepEqual(pattern.match("/ip/127.0.0.1"), ["127.0.0.1"]);
330-
331-
regex = /\/ip\/((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$/;
309+
const regex = /\/ip\/((?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))$/;
332310
pattern = new UrlPattern(regex, ["ip"]);
333311
t.equal(undefined, pattern.match("10.10.10.10"));
334312
t.equal(undefined, pattern.match("ip/10.10.10.10"));

test/readme.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ tape("domain example", (t: tape.Test) => {
7474
});
7575

7676
tape("regex example", (t: tape.Test) => {
77-
const pattern = new UrlPattern(/^\/api\/(.*)$/);
78-
t.deepEqual(pattern.match("/api/users"), ["users"]);
77+
const pattern = new UrlPattern(/^\/api\/(.*)$/, ["path"]);
78+
t.deepEqual(pattern.match("/api/users"), {path: "users"});
7979
t.equal(pattern.match("/apiii/users"), undefined);
8080
t.end();
8181
});

0 commit comments

Comments
 (0)