diff --git a/README.md b/README.md index a6a9d86..1b71569 100644 --- a/README.md +++ b/README.md @@ -204,21 +204,17 @@ unnamed wildcards are not collected. ## make pattern from regex ```javascript -> const pattern = new UrlPattern(/^\/api\/(.*)$/); -``` - -if the pattern was created from a regex an array of the captured groups is returned on a match: +> const pattern = new UrlPattern(/^\/api\/(.*)$/, ["path"]); -```javascript > pattern.match("/api/users"); -["users"] +{path: "users"} > pattern.match("/apiii/test"); undefined ``` when making a pattern from a regex -you can pass an array of keys as the second argument. +you have to pass an array of keys as the second argument. returns objects on match with each key mapped to a captured value: ```javascript diff --git a/src/url-pattern.ts b/src/url-pattern.ts index 04dca2b..a457b31 100644 --- a/src/url-pattern.ts +++ b/src/url-pattern.ts @@ -26,7 +26,7 @@ export default class UrlPattern { public readonly isRegex: boolean; public readonly regex: RegExp; public readonly ast?: Array>; - public readonly names?: string[]; + public readonly names: string[]; constructor(pattern: string, options?: IUserInputOptions); constructor(pattern: RegExp, groupNames?: string[]); @@ -51,22 +51,25 @@ export default class UrlPattern { // handle regex pattern and return early if (pattern instanceof RegExp) { this.regex = pattern; - if (optionsOrGroupNames != null) { - if (!Array.isArray(optionsOrGroupNames)) { - throw new TypeError([ - "if first argument is a RegExp the second argument", - "may be an Array of group names", - "but you provided something else", - ].join(" ")); - } - const groupCount = regexGroupCount(this.regex); - if (optionsOrGroupNames.length !== groupCount) { - throw new Error([ - `regex contains ${ groupCount } groups`, - `but array of group names contains ${ optionsOrGroupNames.length }`, - ].join(" ")); - } - this.names = optionsOrGroupNames; + if (optionsOrGroupNames == null || !Array.isArray(optionsOrGroupNames)) { + throw new TypeError([ + "if first argument is a RegExp the second argument", + "must be an Array of group names", + ].join(" ")); + } + const groupCount = regexGroupCount(this.regex); + if (optionsOrGroupNames.length !== groupCount) { + throw new Error([ + `regex contains ${ groupCount } groups`, + `but array of group names contains ${ optionsOrGroupNames.length }`, + ].join(" ")); + } + this.names = optionsOrGroupNames; + const regexNameIndex = indexOfDuplicateElement(this.names); + if (regexNameIndex !== -1) { + throw new Error( + `duplicate name "${ this.names[regexNameIndex] }" in pattern. names must be unique`, + ); } return; } @@ -114,24 +117,20 @@ export default class UrlPattern { } } - public match(url: string): { [index: string]: string } | string[] | undefined { + public match(url: string): { [index: string]: string } | undefined { const match = this.regex.exec(url); if (match == null) { return; } const groups = match.slice(1); - if (this.names != null) { - const mergedNamesAndGroups: { [index: string]: string } = {}; - for (let i = 0; i < this.names.length; i++) { - if (groups[i] != null) { - mergedNamesAndGroups[this.names[i]] = groups[i]; - } + const mergedNamesAndGroups: { [index: string]: string } = {}; + for (let i = 0; i < this.names.length; i++) { + if (groups[i] != null) { + mergedNamesAndGroups[this.names[i]] = groups[i]; } - return mergedNamesAndGroups; - } else { - return groups; } + return mergedNamesAndGroups; } public stringify(params?: object): string { diff --git a/test/errors.ts b/test/errors.ts index eb1b496..c28e990 100644 --- a/test/errors.ts +++ b/test/errors.ts @@ -117,10 +117,9 @@ tape("regex names", (t: tape.Test) => { try { new UntypedUrlPattern(/x/, 5); } catch (error) { - t.equal(error.message, [ - "if first argument is a RegExp the second argument may be an Array", - "of group names but you provided something else", - ].join(" ")); + t.equal(error.message, + "if first argument is a RegExp the second argument must be an Array of group names", + ); } try { new UrlPattern(/(((foo)bar(boo))far)/, []); @@ -137,7 +136,7 @@ tape("regex names", (t: tape.Test) => { tape("stringify regex", (t: tape.Test) => { t.plan(1); - const pattern = new UrlPattern(/x/); + const pattern = new UrlPattern(/x/, []); try { pattern.stringify(); } catch (error) { diff --git a/test/match-fixtures.ts b/test/match-fixtures.ts index c69ac38..58f6ad8 100644 --- a/test/match-fixtures.ts +++ b/test/match-fixtures.ts @@ -24,14 +24,12 @@ tape("match", (t: tape.Test) => { pattern = new UrlPattern(".foo"); t.equals(pattern.match(".bar.foo"), undefined); - pattern = new UrlPattern(/foo/); + pattern = new UrlPattern(/foo/, []); t.deepEqual(pattern.match("foo"), []); - pattern = new UrlPattern(/\/foo\/(.*)/); - t.deepEqual(pattern.match("/foo/bar"), ["bar"]); - - pattern = new UrlPattern(/\/foo\/(.*)/); - t.deepEqual(pattern.match("/foo/"), [""]); + pattern = new UrlPattern(/\/foo\/(.*)/, ["path"]); + t.deepEqual(pattern.match("/foo/bar"), {path: "bar"}); + t.deepEqual(pattern.match("/foo/"), {path: ""}); pattern = new UrlPattern("/user/:userId/task/:taskId"); t.deepEqual(pattern.match("/user/10/task/52"), { @@ -308,27 +306,7 @@ tape("match", (t: tape.Test) => { scheme: "https", }); - 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]?)$/; - pattern = new UrlPattern(regex); - t.equal(undefined, pattern.match("10.10.10.10")); - t.equal(undefined, pattern.match("ip/10.10.10.10")); - t.equal(undefined, pattern.match("/ip/10.10.10.")); - t.equal(undefined, pattern.match("/ip/10.")); - t.equal(undefined, pattern.match("/ip/")); - t.deepEqual(pattern.match("/ip/10.10.10.10"), ["10", "10", "10", "10"]); - t.deepEqual(pattern.match("/ip/127.0.0.1"), ["127", "0", "0", "1"]); - - 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]?))$/; - pattern = new UrlPattern(regex); - t.equal(undefined, pattern.match("10.10.10.10")); - t.equal(undefined, pattern.match("ip/10.10.10.10")); - t.equal(undefined, pattern.match("/ip/10.10.10.")); - t.equal(undefined, pattern.match("/ip/10.")); - t.equal(undefined, pattern.match("/ip/")); - t.deepEqual(pattern.match("/ip/10.10.10.10"), ["10.10.10.10"]); - t.deepEqual(pattern.match("/ip/127.0.0.1"), ["127.0.0.1"]); - - 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]?))$/; + 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]?))$/; pattern = new UrlPattern(regex, ["ip"]); t.equal(undefined, pattern.match("10.10.10.10")); t.equal(undefined, pattern.match("ip/10.10.10.10")); diff --git a/test/readme.ts b/test/readme.ts index 39b9655..f28891e 100644 --- a/test/readme.ts +++ b/test/readme.ts @@ -74,8 +74,8 @@ tape("domain example", (t: tape.Test) => { }); tape("regex example", (t: tape.Test) => { - const pattern = new UrlPattern(/^\/api\/(.*)$/); - t.deepEqual(pattern.match("/api/users"), ["users"]); + const pattern = new UrlPattern(/^\/api\/(.*)$/, ["path"]); + t.deepEqual(pattern.match("/api/users"), {path: "users"}); t.equal(pattern.match("/apiii/users"), undefined); t.end(); });