Skip to content

Commit 876222c

Browse files
committed
Improve prefix behavior with non-segment params
1 parent dc9a133 commit 876222c

File tree

3 files changed

+131
-26
lines changed

3 files changed

+131
-26
lines changed

Readme.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,33 @@ The path string can be used to define parameters and populate the keys.
4545

4646
#### Named Parameters
4747

48-
Named parameters are defined by prefixing a colon to the parameter name (`:foo`). By default, this parameter will match the following path segment.
48+
Named parameters are defined by prefixing a colon to the parameter name (`:foo`). By default, the parameter will match until the following path segment.
4949

5050
```js
5151
var re = pathToRegexp('/:foo/:bar', keys)
52-
// keys = [{ name: 'foo', ... }, { name: 'bar', ... }]
52+
// keys = [{ name: 'foo', prefix: '/', ... }, { name: 'bar', prefix: '/', ... }]
5353

5454
re.exec('/test/route')
5555
//=> ['/test/route', 'test', 'route']
5656
```
5757

5858
**Please note:** Named parameters must be made up of "word characters" (`[A-Za-z0-9_]`).
5959

60-
#### Suffixed Parameters
60+
Path segments are defined by "prefix" characters (`.` or `/`). If a prefix is used, and the parameter is followed by the same prefix character or end of the path, it is considered a segment and the prefix is part of the match. This behavior is apparent when using optional parameters.
61+
62+
```js
63+
var re = pathToRegexp('/:prefix(apple-)?icon-:res(\\d+).png', keys)
64+
// keys = [{ name: 'prefix', prefix: '', ... }, { name: 'res', prefix: '', ... }]
65+
66+
re.exec('/icon-76.png')
67+
//=> ['/icon-76.png', undefined, '76']
68+
```
69+
70+
#### Modified Parameters
6171

6272
##### Optional
6373

64-
Parameters can be suffixed with a question mark (`?`) to make the parameter optional. This will also make any the prefixed path delimiter optional (`/` or `.`).
74+
Parameters can be suffixed with a question mark (`?`) to make the parameter optional. This will also make the prefix optional.
6575

6676
```js
6777
var re = pathToRegexp('/:foo/:bar?', keys)
@@ -76,7 +86,7 @@ re.exec('/test/route')
7686

7787
##### Zero or more
7888

79-
Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. The prefixed path delimiter is taken into account for each match.
89+
Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. The prefix is taken into account for each match.
8090

8191
```js
8292
var re = pathToRegexp('/:foo*', keys)
@@ -91,7 +101,7 @@ re.exec('/bar/baz')
91101

92102
##### One or more
93103

94-
Parameters can be suffixed with a plus sign (`+`) to denote a one or more parameter matches. The prefixed path delimiter is taken into account for each match.
104+
Parameters can be suffixed with a plus sign (`+`) to denote a one or more parameter matches. The prefix is taken into account for each match.
95105

96106
```js
97107
var re = pathToRegexp('/:foo+', keys)
@@ -106,7 +116,7 @@ re.exec('/bar/baz')
106116

107117
#### Custom Match Parameters
108118

109-
All parameters can be provided a custom regexp, which overrides the default (`[^\/]+`). Please note: Backslashes need to be escaped with another backslash in strings.
119+
All parameters can be provided a custom regexp, which overrides the default (`[^\/]+`).
110120

111121
```js
112122
var re = pathToRegexp('/:foo(\\d+)', keys)
@@ -119,6 +129,8 @@ re.exec('/abc')
119129
//=> null
120130
```
121131

132+
**Please note:** Backslashes need to be escaped with another backslash in strings.
133+
122134
#### Unnamed Parameters
123135

124136
It is possible to write an unnamed parameter that only consists of a matching group. It works the same as a named parameter, except it will be numerically indexed.

index.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,29 @@ function parse (str) {
5353
continue
5454
}
5555

56-
// Push the current path onto the tokens.
57-
if (path) {
58-
tokens.push(path)
59-
path = ''
60-
}
61-
56+
var next = str[index]
6257
var prefix = res[2]
6358
var name = res[3]
6459
var capture = res[4]
6560
var group = res[5]
66-
var suffix = res[6]
61+
var modifier = res[6]
6762
var asterisk = res[7]
6863

69-
var repeat = suffix === '+' || suffix === '*'
70-
var optional = suffix === '?' || suffix === '*'
71-
var delimiter = prefix || '/'
64+
// Only use the prefix when followed by another path segment.
65+
if (prefix != null && next != null && next !== prefix) {
66+
path += prefix
67+
prefix = null
68+
}
69+
70+
// Push the current path onto the tokens.
71+
if (path) {
72+
tokens.push(path)
73+
path = ''
74+
}
75+
76+
var repeat = modifier === '+' || modifier === '*'
77+
var optional = modifier === '?' || modifier === '*'
78+
var delimiter = res[2] || '/'
7279
var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?')
7380

7481
tokens.push({

test.js

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,50 @@ var TESTS = [
542542
[{ test: 'foobar' }, '/foobar/']
543543
]
544544
],
545+
[
546+
'/:test?/bar',
547+
null,
548+
[
549+
{
550+
name: 'test',
551+
prefix: '/',
552+
delimiter: '/',
553+
optional: true,
554+
repeat: false,
555+
pattern: '[^\\/]+?'
556+
},
557+
'/bar'
558+
],
559+
[
560+
['/foo/bar', ['/foo/bar', 'foo']]
561+
],
562+
[
563+
[{ test: 'foo' }, '/foo/bar']
564+
]
565+
],
566+
[
567+
'/:test?-bar',
568+
null,
569+
[
570+
'/',
571+
{
572+
name: 'test',
573+
prefix: '',
574+
delimiter: '/',
575+
optional: true,
576+
repeat: false,
577+
pattern: '[^\\/]+?'
578+
},
579+
'-bar'
580+
],
581+
[
582+
['/-bar', ['/-bar', undefined]],
583+
['/foo-bar', ['/foo-bar', 'foo']]
584+
],
585+
[
586+
[{ test: 'foo' }, '/foo-bar']
587+
]
588+
],
545589

546590
/**
547591
* Repeated one or more times parameters.
@@ -936,9 +980,10 @@ var TESTS = [
936980
'/:test.json',
937981
null,
938982
[
983+
'/',
939984
{
940985
name: 'test',
941-
prefix: '/',
986+
prefix: '',
942987
delimiter: '/',
943988
optional: false,
944989
repeat: false,
@@ -947,11 +992,13 @@ var TESTS = [
947992
'.json'
948993
],
949994
[
995+
['/.json', null],
950996
['/test.json', ['/test.json', 'test']],
951997
['/route.json', ['/route.json', 'route']],
952998
['/route.json.json', ['/route.json.json', 'route.json']]
953999
],
9541000
[
1001+
[{ test: '' }, null],
9551002
[{ test: 'foo' }, '/foo.json']
9561003
]
9571004
],
@@ -1094,9 +1141,10 @@ var TESTS = [
10941141
'/:test.:format',
10951142
null,
10961143
[
1144+
'/',
10971145
{
10981146
name: 'test',
1099-
prefix: '/',
1147+
prefix: '',
11001148
delimiter: '/',
11011149
optional: false,
11021150
repeat: false,
@@ -1125,9 +1173,10 @@ var TESTS = [
11251173
'/:test.:format?',
11261174
null,
11271175
[
1176+
'/',
11281177
{
11291178
name: 'test',
1130-
prefix: '/',
1179+
prefix: '',
11311180
delimiter: '/',
11321181
optional: false,
11331182
repeat: false,
@@ -1159,9 +1208,10 @@ var TESTS = [
11591208
end: false
11601209
},
11611210
[
1211+
'/',
11621212
{
11631213
name: 'test',
1164-
prefix: '/',
1214+
prefix: '',
11651215
delimiter: '/',
11661216
optional: false,
11671217
repeat: false,
@@ -1194,10 +1244,10 @@ var TESTS = [
11941244
end: false
11951245
},
11961246
[
1197-
'/test',
1247+
'/test.',
11981248
{
11991249
name: 'format',
1200-
prefix: '.',
1250+
prefix: '',
12011251
delimiter: '.',
12021252
optional: false,
12031253
repeat: false,
@@ -1594,6 +1644,39 @@ var TESTS = [
15941644
]
15951645
],
15961646

1647+
/**
1648+
* Unnamed group prefix.
1649+
*/
1650+
[
1651+
'/(apple-)?icon-:res(\\d+).png',
1652+
null,
1653+
[
1654+
'/',
1655+
{
1656+
name: 0,
1657+
prefix: '',
1658+
delimiter: '/',
1659+
optional: true,
1660+
repeat: false,
1661+
pattern: 'apple-'
1662+
},
1663+
'icon-',
1664+
{
1665+
name: 'res',
1666+
prefix: '',
1667+
delimiter: '/',
1668+
optional: false,
1669+
repeat: false,
1670+
pattern: '\\d+'
1671+
},
1672+
'.png'
1673+
],
1674+
[
1675+
['/apple-icon-240.png', ['/apple-icon-240.png', 'apple-', '240']]
1676+
],
1677+
[]
1678+
],
1679+
15971680
/**
15981681
* Random examples.
15991682
*/
@@ -1660,9 +1743,10 @@ var TESTS = [
16601743
'/:foo\\?',
16611744
null,
16621745
[
1746+
'/',
16631747
{
16641748
name: 'foo',
1665-
prefix: '/',
1749+
prefix: '',
16661750
delimiter: '/',
16671751
optional: false,
16681752
repeat: false,
@@ -1681,9 +1765,10 @@ var TESTS = [
16811765
'/:foo\\(:bar?\\)',
16821766
null,
16831767
[
1768+
'/',
16841769
{
16851770
name: 'foo',
1686-
prefix: '/',
1771+
prefix: '',
16871772
delimiter: '/',
16881773
optional: false,
16891774
repeat: false,
@@ -1713,9 +1798,10 @@ var TESTS = [
17131798
'/:postType(video|audio|text)(\\+.+)?',
17141799
null,
17151800
[
1801+
'/',
17161802
{
17171803
name: 'postType',
1718-
prefix: '/',
1804+
prefix: '',
17191805
delimiter: '/',
17201806
optional: false,
17211807
repeat: false,

0 commit comments

Comments
 (0)