Skip to content

Commit 0e0dce9

Browse files
committed
Add native match function to library
1 parent dd966ea commit 0e0dce9

File tree

6 files changed

+126
-36
lines changed

6 files changed

+126
-36
lines changed

Readme.md

+14-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ npm install path-to-regexp --save
2121
const pathToRegexp = require('path-to-regexp')
2222

2323
// pathToRegexp(path, keys?, options?)
24+
// pathToRegexp.match(path)
2425
// pathToRegexp.parse(path)
2526
// pathToRegexp.compile(path)
2627
```
@@ -150,9 +151,20 @@ regexpWord.exec('/users')
150151

151152
**Tip:** Backslashes need to be escaped with another backslash in JavaScript strings.
152153

154+
### Match
155+
156+
The `match` function will return a function for transforming paths into parameters:
157+
158+
```js
159+
const match = pathToRegexp.match('/user/:id')
160+
161+
match('/user/123') //=> { path: '/user/123', index: 0, params: { id: '123' } }
162+
match('/invalid') //=> false
163+
```
164+
153165
### Parse
154166

155-
The parse function is exposed via `pathToRegexp.parse`. This will return an array of strings and keys.
167+
The `parse` function will return a list of strings and keys from a path string:
156168

157169
```js
158170
const tokens = pathToRegexp.parse('/route/:foo/(.*)')
@@ -171,7 +183,7 @@ console.log(tokens[2])
171183

172184
### Compile ("Reverse" Path-To-RegExp)
173185

174-
Path-To-RegExp exposes a compile function for transforming a string into a valid path.
186+
The `compile` function will return a function for transforming parameters into a valid path:
175187

176188
```js
177189
const toPath = pathToRegexp.compile('/user/:id')

index.d.ts

+26
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ declare namespace pathToRegexp {
5151
*/
5252
export function parse (path: string, options?: ParseOptions): Token[];
5353

54+
/**
55+
* Create path match function from `path-to-regexp` spec.
56+
*/
57+
export function match <P extends object = object> (path: string, options?: ParseOptions): MatchFunction<P>;
58+
59+
/**
60+
* Create a path match function from `path-to-regexp` output.
61+
*/
62+
export function regexpToFunction <P extends object = object> (re: RegExp, keys: Key[]): MatchFunction<P>;
63+
5464
/**
5565
* Transforming an Express-style path into a valid path.
5666
*/
@@ -86,9 +96,25 @@ declare namespace pathToRegexp {
8696
validate?: boolean;
8797
}
8898

99+
interface MatchFunctionOptions {
100+
/**
101+
* Function for decoding strings for params.
102+
*/
103+
decode?: (value: string, token: Key) => string;
104+
}
105+
106+
interface MatchResult <P extends object = object> {
107+
path: string;
108+
index: number;
109+
params: P;
110+
}
111+
112+
type Match <P extends object = object> = false | MatchResult<P>;
113+
89114
export type Token = string | Key;
90115
export type Path = string | RegExp | Array<string | RegExp>;
91116
export type PathFunction <P extends object = object> = (data?: P, options?: PathFunctionOptions) => string;
117+
export type MatchFunction <P extends object = object> = (path: string, options?: MatchFunctionOptions) => Match<P>;
92118
}
93119

94120
export = pathToRegexp;

index.js

+42
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Expose `pathToRegexp`.
33
*/
44
module.exports = pathToRegexp
5+
module.exports.match = match
6+
module.exports.regexpToFunction = regexpToFunction
57
module.exports.parse = parse
68
module.exports.compile = compile
79
module.exports.tokensToFunction = tokensToFunction
@@ -120,6 +122,46 @@ function compile (str, options) {
120122
return tokensToFunction(parse(str, options), options)
121123
}
122124

125+
/**
126+
* Create path match function from `path-to-regexp` spec.
127+
*/
128+
function match (str, options) {
129+
var keys = []
130+
var re = pathToRegexp(str, keys, options)
131+
return regexpToFunction(re, keys)
132+
}
133+
134+
/**
135+
* Create a path match function from `path-to-regexp` output.
136+
*/
137+
function regexpToFunction (re, keys) {
138+
return function (pathname, options) {
139+
var m = re.exec(pathname)
140+
if (!m) return false
141+
142+
var path = m[0]
143+
var index = m.index
144+
var params = {}
145+
var decode = (options && options.decode) || decodeURIComponent
146+
147+
for (var i = 1; i < m.length; i++) {
148+
if (m[i] === undefined) continue
149+
150+
var key = keys[i - 1]
151+
152+
if (key.repeat) {
153+
params[key.name] = m[i].split(key.delimiter).map(function (value) {
154+
return decode(value, key)
155+
})
156+
} else {
157+
params[key.name] = decode(m[i], key)
158+
}
159+
}
160+
161+
return { path: path, index: index, params: params }
162+
}
163+
}
164+
123165
/**
124166
* Expose a method for transforming tokens into the path function.
125167
*/

package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@
4141
"nyc": "^14.1.1",
4242
"standard": "^14.1.0",
4343
"ts-node": "^8.3.0",
44-
"typescript": "^3.0.1"
44+
"typescript": "^3.7.2"
4545
}
4646
}

test.ts

+40-30
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type Test = [
1010
pathToRegexp.Path,
1111
pathToRegexp.RegExpOptions & pathToRegexp.ParseOptions,
1212
pathToRegexp.Token[],
13-
Array<[string, string[]]>,
13+
Array<[string, string[], pathToRegexp.Match?]>,
1414
Array<[any, string] | [any, string, pathToRegexp.PathFunctionOptions]>
1515
]
1616

@@ -30,8 +30,8 @@ var TESTS: Test[] = [
3030
'/'
3131
],
3232
[
33-
['/', ['/']],
34-
['/route', null]
33+
['/', ['/'], { path: '/', index: 0, params: {} }],
34+
['/route', null, false]
3535
],
3636
[
3737
[null, '/'],
@@ -46,10 +46,10 @@ var TESTS: Test[] = [
4646
'/test'
4747
],
4848
[
49-
['/test', ['/test']],
50-
['/route', null],
51-
['/test/route', null],
52-
['/test/', ['/test/']]
49+
['/test', ['/test'], { path: '/test', index: 0, params: {} }],
50+
['/route', null, false],
51+
['/test/route', null, false],
52+
['/test/', ['/test/'], { path: '/test/', index: 0, params: {} }]
5353
],
5454
[
5555
[null, '/test'],
@@ -201,7 +201,7 @@ var TESTS: Test[] = [
201201
}
202202
],
203203
[
204-
['/route', ['/route', 'route']]
204+
['/route', ['/route', 'route'], { path: '/route', index: 0, params: { test: 'route' } }]
205205
],
206206
[
207207
[{}, null],
@@ -734,9 +734,9 @@ var TESTS: Test[] = [
734734
}
735735
],
736736
[
737-
['/route', ['/route', 'route']],
738-
['/route/nested', null],
739-
['/', ['/', undefined]],
737+
['/route', ['/route', 'route'], { path: '/route', index: 0, params: { test: 'route' } }],
738+
['/route/nested', null, false],
739+
['/', ['/', undefined], { path: '/', index: 0, params: {} }],
740740
['//', null]
741741
],
742742
[
@@ -885,10 +885,10 @@ var TESTS: Test[] = [
885885
}
886886
],
887887
[
888-
['/', null],
889-
['/route', ['/route', 'route']],
890-
['/some/basic/route', ['/some/basic/route', 'some/basic/route']],
891-
['//', null]
888+
['/', null, false],
889+
['/route', ['/route', 'route'], { path: '/route', index: 0, params: { test: ['route'] } }],
890+
['/some/basic/route', ['/some/basic/route', 'some/basic/route'], { path: '/some/basic/route', index: 0, params: { test: ['some', 'basic', 'route'] } }],
891+
['//', null, false]
892892
],
893893
[
894894
[{}, null],
@@ -988,13 +988,14 @@ var TESTS: Test[] = [
988988
}
989989
],
990990
[
991-
['/', ['/', undefined]],
992-
['//', null],
993-
['/route', ['/route', 'route']],
994-
['/some/basic/route', ['/some/basic/route', 'some/basic/route']]
991+
['/', ['/', undefined], { path: '/', index: 0, params: {} }],
992+
['//', null, false],
993+
['/route', ['/route', 'route'], { path: '/route', index: 0, params: { test: ['route'] } }],
994+
['/some/basic/route', ['/some/basic/route', 'some/basic/route'], { path: '/some/basic/route', index: 0, params: { test: ['some', 'basic', 'route'] } }]
995995
],
996996
[
997997
[{}, ''],
998+
[{ test: [] }, ''],
998999
[{ test: 'foobar' }, '/foobar'],
9991000
[{ test: ['foo', 'bar'] }, '/foo/bar']
10001001
]
@@ -2782,18 +2783,18 @@ describe('path-to-regexp', function () {
27822783
var toPath = pathToRegexp.compile(path as string, opts)
27832784

27842785
compileCases.forEach(function (io) {
2785-
var input = io[0]
2786-
var output = io[1]
2786+
var params = io[0]
2787+
var path = io[1]
27872788
var options = io[2]
27882789

2789-
if (output != null) {
2790-
it('should compile using ' + util.inspect(input), function () {
2791-
expect(toPath(input, options)).to.equal(output)
2790+
if (path != null) {
2791+
it('should compile using ' + util.inspect(params), function () {
2792+
expect(toPath(params, options)).to.equal(path)
27922793
})
27932794
} else {
2794-
it('should not compile using ' + util.inspect(input), function () {
2795+
it('should not compile using ' + util.inspect(params), function () {
27952796
expect(function () {
2796-
toPath(input, options)
2797+
toPath(params, options)
27972798
}).to.throw(TypeError)
27982799
})
27992800
}
@@ -2809,13 +2810,22 @@ describe('path-to-regexp', function () {
28092810

28102811
describe('match' + (opts ? ' using ' + util.inspect(opts) : ''), function () {
28112812
matchCases.forEach(function (io) {
2812-
var input = io[0]
2813-
var output = io[1]
2814-
var message = 'should' + (output ? ' ' : ' not ') + 'match ' + util.inspect(input)
2813+
var pathname = io[0]
2814+
var matches = io[1]
2815+
var params = io[2]
2816+
var message = 'should' + (matches ? ' ' : ' not ') + 'match ' + util.inspect(pathname)
28152817

28162818
it(message, function () {
2817-
expect(exec(re, input)).to.deep.equal(output)
2819+
expect(exec(re, pathname)).to.deep.equal(matches)
28182820
})
2821+
2822+
if (typeof path === "string" && params !== undefined) {
2823+
var match = pathToRegexp.match(path)
2824+
2825+
it(message + ' params', function () {
2826+
expect(match(pathname)).to.deep.equal(params)
2827+
})
2828+
}
28192829
})
28202830
})
28212831
})

0 commit comments

Comments
 (0)