Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve prefix behavior with non-segment params #71

Merged
merged 1 commit into from
Mar 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,33 @@ The path string can be used to define parameters and populate the keys.

#### Named Parameters

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

```js
var re = pathToRegexp('/:foo/:bar', keys)
// keys = [{ name: 'foo', ... }, { name: 'bar', ... }]
// keys = [{ name: 'foo', prefix: '/', ... }, { name: 'bar', prefix: '/', ... }]

re.exec('/test/route')
//=> ['/test/route', 'test', 'route']
```

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

#### Suffixed Parameters
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.

```js
var re = pathToRegexp('/:prefix(apple-)?icon-:res(\\d+).png', keys)
// keys = [{ name: 'prefix', prefix: '', ... }, { name: 'res', prefix: '', ... }]

re.exec('/icon-76.png')
//=> ['/icon-76.png', undefined, '76']
```

#### Modified Parameters

##### Optional

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

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

##### Zero or more

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.
Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. The prefix is taken into account for each match.

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

##### One or more

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.
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.

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

#### Custom Match Parameters

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

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

**Please note:** Backslashes need to be escaped with another backslash in strings.

#### Unnamed Parameters

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.
Expand Down
27 changes: 17 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,29 @@ function parse (str) {
continue
}

// Push the current path onto the tokens.
if (path) {
tokens.push(path)
path = ''
}

var next = str[index]
var prefix = res[2]
var name = res[3]
var capture = res[4]
var group = res[5]
var suffix = res[6]
var modifier = res[6]
var asterisk = res[7]

var repeat = suffix === '+' || suffix === '*'
var optional = suffix === '?' || suffix === '*'
var delimiter = prefix || '/'
// Only use the prefix when followed by another path segment.
if (prefix != null && next != null && next !== prefix) {
path += prefix
prefix = null
}

// Push the current path onto the tokens.
if (path) {
tokens.push(path)
path = ''
}

var repeat = modifier === '+' || modifier === '*'
var optional = modifier === '?' || modifier === '*'
var delimiter = res[2] || '/'
var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?')

tokens.push({
Expand Down
104 changes: 95 additions & 9 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,50 @@ var TESTS = [
[{ test: 'foobar' }, '/foobar/']
]
],
[
'/:test?/bar',
null,
[
{
name: 'test',
prefix: '/',
delimiter: '/',
optional: true,
repeat: false,
pattern: '[^\\/]+?'
},
'/bar'
],
[
['/foo/bar', ['/foo/bar', 'foo']]
],
[
[{ test: 'foo' }, '/foo/bar']
]
],
[
'/:test?-bar',
null,
[
'/',
{
name: 'test',
prefix: '',
delimiter: '/',
optional: true,
repeat: false,
pattern: '[^\\/]+?'
},
'-bar'
],
[
['/-bar', ['/-bar', undefined]],
['/foo-bar', ['/foo-bar', 'foo']]
],
[
[{ test: 'foo' }, '/foo-bar']
]
],

/**
* Repeated one or more times parameters.
Expand Down Expand Up @@ -936,9 +980,10 @@ var TESTS = [
'/:test.json',
null,
[
'/',
{
name: 'test',
prefix: '/',
prefix: '',
delimiter: '/',
optional: false,
repeat: false,
Expand All @@ -947,11 +992,13 @@ var TESTS = [
'.json'
],
[
['/.json', null],
['/test.json', ['/test.json', 'test']],
['/route.json', ['/route.json', 'route']],
['/route.json.json', ['/route.json.json', 'route.json']]
],
[
[{ test: '' }, null],
[{ test: 'foo' }, '/foo.json']
]
],
Expand Down Expand Up @@ -1094,9 +1141,10 @@ var TESTS = [
'/:test.:format',
null,
[
'/',
{
name: 'test',
prefix: '/',
prefix: '',
delimiter: '/',
optional: false,
repeat: false,
Expand Down Expand Up @@ -1125,9 +1173,10 @@ var TESTS = [
'/:test.:format?',
null,
[
'/',
{
name: 'test',
prefix: '/',
prefix: '',
delimiter: '/',
optional: false,
repeat: false,
Expand Down Expand Up @@ -1159,9 +1208,10 @@ var TESTS = [
end: false
},
[
'/',
{
name: 'test',
prefix: '/',
prefix: '',
delimiter: '/',
optional: false,
repeat: false,
Expand Down Expand Up @@ -1194,10 +1244,10 @@ var TESTS = [
end: false
},
[
'/test',
'/test.',
{
name: 'format',
prefix: '.',
prefix: '',
delimiter: '.',
optional: false,
repeat: false,
Expand Down Expand Up @@ -1594,6 +1644,39 @@ var TESTS = [
]
],

/**
* Unnamed group prefix.
*/
[
'/(apple-)?icon-:res(\\d+).png',
null,
[
'/',
{
name: 0,
prefix: '',
delimiter: '/',
optional: true,
repeat: false,
pattern: 'apple-'
},
'icon-',
{
name: 'res',
prefix: '',
delimiter: '/',
optional: false,
repeat: false,
pattern: '\\d+'
},
'.png'
],
[
['/apple-icon-240.png', ['/apple-icon-240.png', 'apple-', '240']]
],
[]
],

/**
* Random examples.
*/
Expand Down Expand Up @@ -1660,9 +1743,10 @@ var TESTS = [
'/:foo\\?',
null,
[
'/',
{
name: 'foo',
prefix: '/',
prefix: '',
delimiter: '/',
optional: false,
repeat: false,
Expand All @@ -1681,9 +1765,10 @@ var TESTS = [
'/:foo\\(:bar?\\)',
null,
[
'/',
{
name: 'foo',
prefix: '/',
prefix: '',
delimiter: '/',
optional: false,
repeat: false,
Expand Down Expand Up @@ -1713,9 +1798,10 @@ var TESTS = [
'/:postType(video|audio|text)(\\+.+)?',
null,
[
'/',
{
name: 'postType',
prefix: '/',
prefix: '',
delimiter: '/',
optional: false,
repeat: false,
Expand Down