Skip to content

Commit 30decc1

Browse files
Guriablakeembrey
authored andcommitted
leave more characters unescaped for segments (#75)
treat `;,:@&=+$-_.!~*()` characters as safe to be part of segment
1 parent e06109e commit 30decc1

File tree

4 files changed

+46
-10
lines changed

4 files changed

+46
-10
lines changed

Readme.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ toPath({ id: 123 }) //=> "/user/123"
185185
toPath({ id: 'café' }) //=> "/user/caf%C3%A9"
186186
toPath({ id: '/' }) //=> "/user/%2F"
187187

188+
toPath({ id: ':' }) //=> "/user/%3A"
189+
toPath({ id: ':' }, { pretty: true }) //=> "/user/:"
190+
188191
var toPathRepeated = pathToRegexp.compile('/:segment+')
189192

190193
toPathRepeated({ segment: 'foo' }) //=> "/foo"
@@ -194,7 +197,7 @@ var toPathRegexp = pathToRegexp.compile('/user/:id(\\d+)')
194197

195198
toPathRegexp({ id: 123 }) //=> "/user/123"
196199
toPathRegexp({ id: '123' }) //=> "/user/123"
197-
toPathRegexp({ id: 'abc' }) //=> throws TypeError
200+
toPathRegexp({ id: 'abc' }) //=> Throws `TypeError`.
198201
```
199202

200203
**Note:** The generated function will throw on invalid input. It will do all necessary checks to ensure the generated path is valid. This method only works with strings.

index.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,13 @@ declare namespace pathToRegexp {
4545
pattern: string;
4646
}
4747

48+
interface PathFunctionOptions {
49+
pretty?: boolean;
50+
}
51+
4852
export type Token = string | Key;
4953
export type Path = string | RegExp | Array<string | RegExp>;
50-
export type PathFunction = (data?: Object) => string;
54+
export type PathFunction = (data?: Object, options?: PathFunctionOptions) => string;
5155
}
5256

5357
export = pathToRegexp;

index.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,24 @@ function parse (str) {
105105
* Compile a string to a template function for the path.
106106
*
107107
* @param {string} str
108-
* @return {!function(Object=)}
108+
* @return {!function(Object=, Object=)}
109109
*/
110110
function compile (str) {
111111
return tokensToFunction(parse(str))
112112
}
113113

114+
/**
115+
* Encode characters for segment that could cause trouble for parsing.
116+
*
117+
* @param {string}
118+
* @return {string}
119+
*/
120+
function encodeURIComponentPretty (str) {
121+
return encodeURI(str).replace(/[/?#'"]/g, function (c) {
122+
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
123+
})
124+
}
125+
114126
/**
115127
* Expose a method for transforming tokens into the path function.
116128
*/
@@ -125,9 +137,11 @@ function tokensToFunction (tokens) {
125137
}
126138
}
127139

128-
return function (obj) {
140+
return function (obj, opts) {
129141
var path = ''
130142
var data = obj || {}
143+
var options = opts || {}
144+
var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent
131145

132146
for (var i = 0; i < tokens.length; i++) {
133147
var token = tokens[i]
@@ -163,7 +177,7 @@ function tokensToFunction (tokens) {
163177
}
164178

165179
for (var j = 0; j < value.length; j++) {
166-
segment = encodeURIComponent(value[j])
180+
segment = encode(value[j])
167181

168182
if (!matches[i].test(segment)) {
169183
throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
@@ -175,7 +189,7 @@ function tokensToFunction (tokens) {
175189
continue
176190
}
177191

178-
segment = encodeURIComponent(value)
192+
segment = encode(value)
179193

180194
if (!matches[i].test(segment)) {
181195
throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')

test.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -398,11 +398,14 @@ var TESTS: Test[] = [
398398
['/another', ['/another', 'another']],
399399
['/something/else', null],
400400
['/route.json', ['/route.json', 'route.json']],
401-
['/something%2Felse', ['/something%2Felse', 'something%2Felse']]
401+
['/something%2Felse', ['/something%2Felse', 'something%2Felse']],
402+
['/something%2Felse%2Fmore', ['/something%2Felse%2Fmore', 'something%2Felse%2Fmore']],
403+
['/;,:@&=+$-_.!~*()', ['/;,:@&=+$-_.!~*()', ';,:@&=+$-_.!~*()']]
402404
],
403405
[
404406
[{ test: 'route' }, '/route'],
405-
[{ test: 'something/else' }, '/something%2Felse']
407+
[{ test: 'something/else' }, '/something%2Felse'],
408+
[{ test: 'something/else/more' }, '/something%2Felse%2Fmore']
406409
]
407410
],
408411
[
@@ -799,12 +802,14 @@ var TESTS: Test[] = [
799802
}
800803
],
801804
[
802-
['/anything/goes/here', ['/anything/goes/here', 'anything/goes/here']]
805+
['/anything/goes/here', ['/anything/goes/here', 'anything/goes/here']],
806+
['/;,:@&=/+$-_.!/~*()', ['/;,:@&=/+$-_.!/~*()', ';,:@&=/+$-_.!/~*()']]
803807
],
804808
[
805809
[{ test: '' }, '/'],
806810
[{ test: 'abc' }, '/abc'],
807-
[{ test: 'abc/123' }, '/abc%2F123']
811+
[{ test: 'abc/123' }, '/abc%2F123'],
812+
[{ test: 'abc/123/456' }, '/abc%2F123%2F456']
808813
]
809814
],
810815
[
@@ -1981,6 +1986,16 @@ describe('path-to-regexp', function () {
19811986
})
19821987
})
19831988

1989+
describe('compile', function () {
1990+
it('should allow pretty option', function () {
1991+
var value = ';,:@&=+$-_.!~*()'
1992+
var toPath = pathToRegexp.compile('/:value')
1993+
var path = toPath({ value }, { pretty: true })
1994+
1995+
expect(path).to.equal(`/${value}`)
1996+
})
1997+
})
1998+
19841999
describe('compile errors', function () {
19852000
it('should throw when a required param is undefined', function () {
19862001
var toPath = pathToRegexp.compile('/a/:b/c')

0 commit comments

Comments
 (0)