path-to-regexp 2.3.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/History.md CHANGED
@@ -1,3 +1,19 @@
1
+ 3.0.0 / 2019-01-13
2
+ ==================
3
+
4
+ * Always use prefix character as delimiter token, allowing any character to be a delimiter (e.g. `/:att1-:att2-:att3-:att4-:att5`)
5
+ * Remove `partial` support, prefer escaping the prefix delimiter explicitly (e.g. `\\/(apple-)?icon-:res(\\d+).png`)
6
+
7
+ 2.4.0 / 2018-08-26
8
+ ==================
9
+
10
+ * Support `start` option to disable anchoring from beginning of the string
11
+
12
+ 2.3.0 / 2018-08-20
13
+ ==================
14
+
15
+ * Use `delimiter` when processing repeated matching groups (e.g. `foo/bar` has no prefix, but has a delimiter)
16
+
1
17
  2.2.1 / 2018-04-24
2
18
  ==================
3
19
 
package/Readme.md CHANGED
@@ -18,32 +18,33 @@ npm install path-to-regexp --save
18
18
  ## Usage
19
19
 
20
20
  ```javascript
21
- var pathToRegexp = require('path-to-regexp')
21
+ const pathToRegexp = require('path-to-regexp')
22
22
 
23
23
  // pathToRegexp(path, keys?, options?)
24
+ // pathToRegexp.match(path)
24
25
  // pathToRegexp.parse(path)
25
26
  // pathToRegexp.compile(path)
26
27
  ```
27
28
 
28
29
  - **path** A string, array of strings, or a regular expression.
29
- - **keys** An array to be populated with keys found in the path.
30
+ - **keys** An array to populate with keys found in the path.
30
31
  - **options**
31
32
  - **sensitive** When `true` the regexp will be case sensitive. (default: `false`)
32
33
  - **strict** When `true` the regexp allows an optional trailing delimiter to match. (default: `false`)
33
- - **end** When `false` the regexp will match the beginning instead of the entire string. (default: `true`)
34
- - Advanced options (use for non-pathname strings, e.g. host names):
35
- - **delimiter** The default delimiter for segments. (default: `'/'`)
36
- - **endsWith** Optional character, or list of characters, to treat as "end" characters.
37
- - **delimiters** List of characters to consider delimiters when parsing. (default: `'./'`)
34
+ - **end** When `true` the regexp will match to the end of the string. (default: `true`)
35
+ - **start** When `true` the regexp will match from the beginning of the string. (default: `true`)
36
+ - **delimiter** The default delimiter for segments. (default: `'/'`)
37
+ - **endsWith** Optional character, or list of characters, to treat as "end" characters.
38
+ - **whitelist** List of characters to consider delimiters when parsing. (default: `undefined`, any character)
38
39
 
39
40
  ```javascript
40
- var keys = []
41
- var re = pathToRegexp('/foo/:bar', keys)
42
- // re = /^\/foo\/([^\/]+?)\/?$/i
41
+ const keys = []
42
+ const regexp = pathToRegexp('/foo/:bar', keys)
43
+ // regexp = /^\/foo\/([^\/]+?)\/?$/i
43
44
  // keys = [{ name: 'bar', prefix: '/', delimiter: '/', optional: false, repeat: false, pattern: '[^\\/]+?' }]
44
45
  ```
45
46
 
46
- **Please note:** The `RegExp` returned by `path-to-regexp` is intended for ordered data (e.g. pathnames, hostnames). It does not handle arbitrary data (e.g. query strings, URL fragments, JSON, etc).
47
+ **Please note:** The `RegExp` returned by `path-to-regexp` is intended for ordered data (e.g. pathnames, hostnames). It can not handle arbitrarily ordered data (e.g. query strings, URL fragments, JSON, etc).
47
48
 
48
49
  ### Parameters
49
50
 
@@ -51,17 +52,17 @@ The path argument is used to define parameters and populate the list of keys.
51
52
 
52
53
  #### Named Parameters
53
54
 
54
- Named parameters are defined by prefixing a colon to the parameter name (`:foo`). By default, the parameter will match until the following path segment.
55
+ Named parameters are defined by prefixing a colon to the parameter name (`:foo`). By default, the parameter will match until the next prefix (e.g. `[^/]+`).
55
56
 
56
57
  ```js
57
- var re = pathToRegexp('/:foo/:bar')
58
+ const regexp = pathToRegexp('/:foo/:bar')
58
59
  // keys = [{ name: 'foo', prefix: '/', ... }, { name: 'bar', prefix: '/', ... }]
59
60
 
60
- re.exec('/test/route')
61
- //=> ['/test/route', 'test', 'route']
61
+ regexp.exec('/test/route')
62
+ //=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
62
63
  ```
63
64
 
64
- **Please note:** Parameter names must be made up of "word characters" (`[A-Za-z0-9_]`).
65
+ **Please note:** Parameter names must use "word characters" (`[A-Za-z0-9_]`).
65
66
 
66
67
  #### Parameter Modifiers
67
68
 
@@ -70,83 +71,103 @@ re.exec('/test/route')
70
71
  Parameters can be suffixed with a question mark (`?`) to make the parameter optional.
71
72
 
72
73
  ```js
73
- var re = pathToRegexp('/:foo/:bar?')
74
+ const regexp = pathToRegexp('/:foo/:bar?')
74
75
  // keys = [{ name: 'foo', ... }, { name: 'bar', delimiter: '/', optional: true, repeat: false }]
75
76
 
76
- re.exec('/test')
77
- //=> ['/test', 'test', undefined]
77
+ regexp.exec('/test')
78
+ //=> [ '/test', 'test', undefined, index: 0, input: '/test', groups: undefined ]
78
79
 
79
- re.exec('/test/route')
80
- //=> ['/test', 'test', 'route']
80
+ regexp.exec('/test/route')
81
+ //=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
81
82
  ```
82
83
 
83
- **Tip:** If the parameter is the _only_ value in the segment, the prefix is also optional.
84
+ **Tip:** The prefix is also optional, escape the prefix `\/` to make it required.
84
85
 
85
86
  ##### Zero or more
86
87
 
87
- Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. The prefix is taken into account for each match.
88
+ Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. The prefix is used for each match.
88
89
 
89
90
  ```js
90
- var re = pathToRegexp('/:foo*')
91
+ const regexp = pathToRegexp('/:foo*')
91
92
  // keys = [{ name: 'foo', delimiter: '/', optional: true, repeat: true }]
92
93
 
93
- re.exec('/')
94
- //=> ['/', undefined]
94
+ regexp.exec('/')
95
+ //=> [ '/', undefined, index: 0, input: '/', groups: undefined ]
95
96
 
96
- re.exec('/bar/baz')
97
- //=> ['/bar/baz', 'bar/baz']
97
+ regexp.exec('/bar/baz')
98
+ //=> [ '/bar/baz', 'bar/baz', index: 0, input: '/bar/baz', groups: undefined ]
98
99
  ```
99
100
 
100
101
  ##### One or more
101
102
 
102
- 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.
103
+ Parameters can be suffixed with a plus sign (`+`) to denote a one or more parameter matches. The prefix is used for each match.
103
104
 
104
105
  ```js
105
- var re = pathToRegexp('/:foo+')
106
+ const regexp = pathToRegexp('/:foo+')
106
107
  // keys = [{ name: 'foo', delimiter: '/', optional: false, repeat: true }]
107
108
 
108
- re.exec('/')
109
+ regexp.exec('/')
109
110
  //=> null
110
111
 
111
- re.exec('/bar/baz')
112
- //=> ['/bar/baz', 'bar/baz']
112
+ regexp.exec('/bar/baz')
113
+ //=> [ '/bar/baz','bar/baz', index: 0, input: '/bar/baz', groups: undefined ]
114
+ ```
115
+
116
+ #### Unnamed Parameters
117
+
118
+ 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.
119
+
120
+ ```js
121
+ const regexp = pathToRegexp('/:foo/(.*)')
122
+ // keys = [{ name: 'foo', ... }, { name: 0, ... }]
123
+
124
+ regexp.exec('/test/route')
125
+ //=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
113
126
  ```
114
127
 
115
128
  #### Custom Matching Parameters
116
129
 
117
- All parameters can be provided a custom regexp, which overrides the default match (`[^\/]+`). For example, you can match digits in the path:
130
+ All parameters can have a custom regexp, which overrides the default match (`[^/]+`). For example, you can match digits or names in a path:
118
131
 
119
132
  ```js
120
- var re = pathToRegexp('/icon-:foo(\\d+).png')
133
+ const regexpNumbers = pathToRegexp('/icon-:foo(\\d+).png')
121
134
  // keys = [{ name: 'foo', ... }]
122
135
 
123
- re.exec('/icon-123.png')
136
+ regexpNumbers.exec('/icon-123.png')
124
137
  //=> ['/icon-123.png', '123']
125
138
 
126
- re.exec('/icon-abc.png')
139
+ regexpNumbers.exec('/icon-abc.png')
140
+ //=> null
141
+
142
+ const regexpWord = pathToRegexp('/(user|u)')
143
+ // keys = [{ name: 0, ... }]
144
+
145
+ regexpWord.exec('/u')
146
+ //=> ['/u', 'u']
147
+
148
+ regexpWord.exec('/users')
127
149
  //=> null
128
150
  ```
129
151
 
130
- **Please note:** Backslashes need to be escaped with another backslash in strings.
152
+ **Tip:** Backslashes need to be escaped with another backslash in JavaScript strings.
131
153
 
132
- #### Unnamed Parameters
154
+ ### Match
133
155
 
134
- 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.
156
+ The `match` function will return a function for transforming paths into parameters:
135
157
 
136
158
  ```js
137
- var re = pathToRegexp('/:foo/(.*)')
138
- // keys = [{ name: 'foo', ... }, { name: 0, ... }]
159
+ const match = pathToRegexp.match('/user/:id')
139
160
 
140
- re.exec('/test/route')
141
- //=> ['/test/route', 'test', 'route']
161
+ match('/user/123') //=> { path: '/user/123', index: 0, params: { id: '123' } }
162
+ match('/invalid') //=> false
142
163
  ```
143
164
 
144
165
  ### Parse
145
166
 
146
- 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:
147
168
 
148
169
  ```js
149
- var tokens = pathToRegexp.parse('/route/:foo/(.*)')
170
+ const tokens = pathToRegexp.parse('/route/:foo/(.*)')
150
171
 
151
172
  console.log(tokens[0])
152
173
  //=> "/route"
@@ -162,28 +183,29 @@ console.log(tokens[2])
162
183
 
163
184
  ### Compile ("Reverse" Path-To-RegExp)
164
185
 
165
- 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:
166
187
 
167
188
  ```js
168
- var toPath = pathToRegexp.compile('/user/:id')
189
+ const toPath = pathToRegexp.compile('/user/:id')
169
190
 
170
191
  toPath({ id: 123 }) //=> "/user/123"
171
192
  toPath({ id: 'café' }) //=> "/user/caf%C3%A9"
172
193
  toPath({ id: '/' }) //=> "/user/%2F"
173
194
 
174
195
  toPath({ id: ':/' }) //=> "/user/%3A%2F"
175
- toPath({ id: ':/' }, { encode: (value, token) => value }) //=> "/user/:/"
196
+ toPath({ id: ':/' }, { encode: (value, token) => value, validate: false }) //=> "/user/:/"
176
197
 
177
- var toPathRepeated = pathToRegexp.compile('/:segment+')
198
+ const toPathRepeated = pathToRegexp.compile('/:segment+')
178
199
 
179
200
  toPathRepeated({ segment: 'foo' }) //=> "/foo"
180
201
  toPathRepeated({ segment: ['a', 'b', 'c'] }) //=> "/a/b/c"
181
202
 
182
- var toPathRegexp = pathToRegexp.compile('/user/:id(\\d+)')
203
+ const toPathRegexp = pathToRegexp.compile('/user/:id(\\d+)')
183
204
 
184
205
  toPathRegexp({ id: 123 }) //=> "/user/123"
185
206
  toPathRegexp({ id: '123' }) //=> "/user/123"
186
207
  toPathRegexp({ id: 'abc' }) //=> Throws `TypeError`.
208
+ toPathRegexp({ id: 'abc' }, { validate: false }) //=> "/user/abc"
187
209
  ```
188
210
 
189
211
  **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.
@@ -198,11 +220,10 @@ Path-To-RegExp exposes the two functions used internally that accept an array of
198
220
  #### Token Information
199
221
 
200
222
  * `name` The name of the token (`string` for named or `number` for index)
201
- * `prefix` The prefix character for the segment (`/` or `.`)
202
- * `delimiter` The delimiter for the segment (same as prefix or `/`)
223
+ * `prefix` The prefix character for the segment (e.g. `/`)
224
+ * `delimiter` The delimiter for the segment (same as prefix or default delimiter)
203
225
  * `optional` Indicates the token is optional (`boolean`)
204
226
  * `repeat` Indicates the token is repeated (`boolean`)
205
- * `partial` Indicates this token is a partial path segment (`boolean`)
206
227
  * `pattern` The RegExp used to match this token (`string`)
207
228
 
208
229
  ## Compatibility with Express <= 4.x
package/index.d.ts CHANGED
@@ -11,9 +11,13 @@ declare namespace pathToRegexp {
11
11
  */
12
12
  strict?: boolean;
13
13
  /**
14
- * When `false` the regexp will match the beginning instead of the entire string. (default: `true`)
14
+ * When `true` the regexp will match to the end of the string. (default: `true`)
15
15
  */
16
16
  end?: boolean;
17
+ /**
18
+ * When `true` the regexp will match from the beginning of the string. (default: `true`)
19
+ */
20
+ start?: boolean;
17
21
  /**
18
22
  * Sets the final character for non-ending optimistic matches. (default: `/`)
19
23
  */
@@ -22,6 +26,10 @@ declare namespace pathToRegexp {
22
26
  * List of characters that can also be "end" characters.
23
27
  */
24
28
  endsWith?: string | string[];
29
+ /**
30
+ * List of characters to consider delimiters when parsing. (default: `undefined`, any character)
31
+ */
32
+ whitelist?: string | string[];
25
33
  }
26
34
 
27
35
  export interface ParseOptions {
@@ -29,10 +37,13 @@ declare namespace pathToRegexp {
29
37
  * Set the default delimiter for repeat parameters. (default: `'/'`)
30
38
  */
31
39
  delimiter?: string;
40
+ }
41
+
42
+ export interface TokensToFunctionOptions {
32
43
  /**
33
- * List of valid delimiter characters. (default: `'./'`)
44
+ * When `true` the regexp will be case sensitive. (default: `false`)
34
45
  */
35
- delimiters?: string | string[];
46
+ sensitive?: boolean;
36
47
  }
37
48
 
38
49
  /**
@@ -40,15 +51,25 @@ declare namespace pathToRegexp {
40
51
  */
41
52
  export function parse (path: string, options?: ParseOptions): Token[];
42
53
 
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
+
43
64
  /**
44
65
  * Transforming an Express-style path into a valid path.
45
66
  */
46
- export function compile (path: string, options?: ParseOptions): PathFunction;
67
+ export function compile <P extends object = object> (path: string, options?: ParseOptions & TokensToFunctionOptions): PathFunction<P>;
47
68
 
48
69
  /**
49
70
  * Transform an array of tokens into a path generator function.
50
71
  */
51
- export function tokensToFunction (tokens: Token[]): PathFunction;
72
+ export function tokensToFunction <P extends object = object> (tokens: Token[], options?: TokensToFunctionOptions): PathFunction<P>;
52
73
 
53
74
  /**
54
75
  * Transform an array of tokens into a matching regular expression.
@@ -62,7 +83,6 @@ declare namespace pathToRegexp {
62
83
  optional: boolean;
63
84
  repeat: boolean;
64
85
  pattern: string;
65
- partial: boolean;
66
86
  }
67
87
 
68
88
  interface PathFunctionOptions {
@@ -70,11 +90,31 @@ declare namespace pathToRegexp {
70
90
  * Function for encoding input strings for output.
71
91
  */
72
92
  encode?: (value: string, token: Key) => string;
93
+ /**
94
+ * When `false` the function can produce an invalid (unmatched) path. (default: `true`)
95
+ */
96
+ validate?: boolean;
73
97
  }
74
98
 
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
+
75
114
  export type Token = string | Key;
76
115
  export type Path = string | RegExp | Array<string | RegExp>;
77
- export type PathFunction = (data?: Object, options?: PathFunctionOptions) => string;
116
+ 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>;
78
118
  }
79
119
 
80
120
  export = pathToRegexp;
package/index.js CHANGED
@@ -2,6 +2,8 @@
2
2
  * Expose `pathToRegexp`.
3
3
  */
4
4
  module.exports = pathToRegexp
5
+ module.exports.match = match
6
+ module.exports.regexpToFunction = regexpToFunction
5
7
  module.exports.parse = parse
6
8
  module.exports.compile = compile
7
9
  module.exports.tokensToFunction = tokensToFunction
@@ -11,7 +13,6 @@ module.exports.tokensToRegExp = tokensToRegExp
11
13
  * Default configs.
12
14
  */
13
15
  var DEFAULT_DELIMITER = '/'
14
- var DEFAULT_DELIMITERS = './'
15
16
 
16
17
  /**
17
18
  * The main path matching regexp utility.
@@ -43,7 +44,7 @@ function parse (str, options) {
43
44
  var index = 0
44
45
  var path = ''
45
46
  var defaultDelimiter = (options && options.delimiter) || DEFAULT_DELIMITER
46
- var delimiters = (options && options.delimiters) || DEFAULT_DELIMITERS
47
+ var whitelist = (options && options.whitelist) || undefined
47
48
  var pathEscaped = false
48
49
  var res
49
50
 
@@ -62,7 +63,6 @@ function parse (str, options) {
62
63
  }
63
64
 
64
65
  var prev = ''
65
- var next = str[index]
66
66
  var name = res[2]
67
67
  var capture = res[3]
68
68
  var group = res[4]
@@ -70,9 +70,11 @@ function parse (str, options) {
70
70
 
71
71
  if (!pathEscaped && path.length) {
72
72
  var k = path.length - 1
73
+ var c = path[k]
74
+ var matches = whitelist ? whitelist.indexOf(c) > -1 : true
73
75
 
74
- if (delimiters.indexOf(path[k]) > -1) {
75
- prev = path[k]
76
+ if (matches) {
77
+ prev = c
76
78
  path = path.slice(0, k)
77
79
  }
78
80
  }
@@ -84,11 +86,10 @@ function parse (str, options) {
84
86
  pathEscaped = false
85
87
  }
86
88
 
87
- var partial = prev !== '' && next !== undefined && next !== prev
88
89
  var repeat = modifier === '+' || modifier === '*'
89
90
  var optional = modifier === '?' || modifier === '*'
90
- var delimiter = prev || defaultDelimiter
91
91
  var pattern = capture || group
92
+ var delimiter = prev || defaultDelimiter
92
93
 
93
94
  tokens.push({
94
95
  name: name || key++,
@@ -96,8 +97,9 @@ function parse (str, options) {
96
97
  delimiter: delimiter,
97
98
  optional: optional,
98
99
  repeat: repeat,
99
- partial: partial,
100
- pattern: pattern ? escapeGroup(pattern) : '[^' + escapeString(delimiter) + ']+?'
100
+ pattern: pattern
101
+ ? escapeGroup(pattern)
102
+ : '[^' + escapeString(delimiter === defaultDelimiter ? delimiter : (delimiter + defaultDelimiter)) + ']+?'
101
103
  })
102
104
  }
103
105
 
@@ -117,26 +119,67 @@ function parse (str, options) {
117
119
  * @return {!function(Object=, Object=)}
118
120
  */
119
121
  function compile (str, options) {
120
- return tokensToFunction(parse(str, options))
122
+ return tokensToFunction(parse(str, options), options)
123
+ }
124
+
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
+ }
121
163
  }
122
164
 
123
165
  /**
124
166
  * Expose a method for transforming tokens into the path function.
125
167
  */
126
- function tokensToFunction (tokens) {
168
+ function tokensToFunction (tokens, options) {
127
169
  // Compile all the tokens into regexps.
128
170
  var matches = new Array(tokens.length)
129
171
 
130
172
  // Compile all the patterns before compilation.
131
173
  for (var i = 0; i < tokens.length; i++) {
132
174
  if (typeof tokens[i] === 'object') {
133
- matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$')
175
+ matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options))
134
176
  }
135
177
  }
136
178
 
137
179
  return function (data, options) {
138
180
  var path = ''
139
181
  var encode = (options && options.encode) || encodeURIComponent
182
+ var validate = options ? options.validate !== false : true
140
183
 
141
184
  for (var i = 0; i < tokens.length; i++) {
142
185
  var token = tokens[i]
@@ -163,7 +206,7 @@ function tokensToFunction (tokens) {
163
206
  for (var j = 0; j < value.length; j++) {
164
207
  segment = encode(value[j], token)
165
208
 
166
- if (!matches[i].test(segment)) {
209
+ if (validate && !matches[i].test(segment)) {
167
210
  throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '"')
168
211
  }
169
212
 
@@ -176,7 +219,7 @@ function tokensToFunction (tokens) {
176
219
  if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
177
220
  segment = encode(String(value), token)
178
221
 
179
- if (!matches[i].test(segment)) {
222
+ if (validate && !matches[i].test(segment)) {
180
223
  throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but got "' + segment + '"')
181
224
  }
182
225
 
@@ -184,12 +227,7 @@ function tokensToFunction (tokens) {
184
227
  continue
185
228
  }
186
229
 
187
- if (token.optional) {
188
- // Prepend partial segment prefixes.
189
- if (token.partial) path += token.prefix
190
-
191
- continue
192
- }
230
+ if (token.optional) continue
193
231
 
194
232
  throw new TypeError('Expected "' + token.name + '" to be ' + (token.repeat ? 'an array' : 'a string'))
195
233
  }
@@ -249,7 +287,6 @@ function regexpToRegexp (path, keys) {
249
287
  delimiter: null,
250
288
  optional: false,
251
289
  repeat: false,
252
- partial: false,
253
290
  pattern: null
254
291
  })
255
292
  }
@@ -300,12 +337,11 @@ function tokensToRegExp (tokens, keys, options) {
300
337
  options = options || {}
301
338
 
302
339
  var strict = options.strict
340
+ var start = options.start !== false
303
341
  var end = options.end !== false
304
- var delimiter = escapeString(options.delimiter || DEFAULT_DELIMITER)
305
- var delimiters = options.delimiters || DEFAULT_DELIMITERS
342
+ var delimiter = options.delimiter || DEFAULT_DELIMITER
306
343
  var endsWith = [].concat(options.endsWith || []).map(escapeString).concat('$').join('|')
307
- var route = ''
308
- var isEndDelimited = tokens.length === 0
344
+ var route = start ? '^' : ''
309
345
 
310
346
  // Iterate over the tokens and create our regexp string.
311
347
  for (var i = 0; i < tokens.length; i++) {
@@ -313,7 +349,6 @@ function tokensToRegExp (tokens, keys, options) {
313
349
 
314
350
  if (typeof token === 'string') {
315
351
  route += escapeString(token)
316
- isEndDelimited = i === tokens.length - 1 && delimiters.indexOf(token[token.length - 1]) > -1
317
352
  } else {
318
353
  var capture = token.repeat
319
354
  ? '(?:' + token.pattern + ')(?:' + escapeString(token.delimiter) + '(?:' + token.pattern + '))*'
@@ -322,8 +357,8 @@ function tokensToRegExp (tokens, keys, options) {
322
357
  if (keys) keys.push(token)
323
358
 
324
359
  if (token.optional) {
325
- if (token.partial) {
326
- route += escapeString(token.prefix) + '(' + capture + ')?'
360
+ if (!token.prefix) {
361
+ route += '(' + capture + ')?'
327
362
  } else {
328
363
  route += '(?:' + escapeString(token.prefix) + '(' + capture + '))?'
329
364
  }
@@ -334,15 +369,20 @@ function tokensToRegExp (tokens, keys, options) {
334
369
  }
335
370
 
336
371
  if (end) {
337
- if (!strict) route += '(?:' + delimiter + ')?'
372
+ if (!strict) route += '(?:' + escapeString(delimiter) + ')?'
338
373
 
339
374
  route += endsWith === '$' ? '$' : '(?=' + endsWith + ')'
340
375
  } else {
341
- if (!strict) route += '(?:' + delimiter + '(?=' + endsWith + '))?'
342
- if (!isEndDelimited) route += '(?=' + delimiter + '|' + endsWith + ')'
376
+ var endToken = tokens[tokens.length - 1]
377
+ var isEndDelimited = typeof endToken === 'string'
378
+ ? endToken[endToken.length - 1] === delimiter
379
+ : endToken === undefined
380
+
381
+ if (!strict) route += '(?:' + escapeString(delimiter) + '(?=' + endsWith + '))?'
382
+ if (!isEndDelimited) route += '(?=' + escapeString(delimiter) + '|' + endsWith + ')'
343
383
  }
344
384
 
345
- return new RegExp('^' + route, flags(options))
385
+ return new RegExp(route, flags(options))
346
386
  }
347
387
 
348
388
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "path-to-regexp",
3
3
  "description": "Express style path to RegExp utility",
4
- "version": "2.3.0",
4
+ "version": "3.2.0",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
7
7
  "files": [
@@ -12,7 +12,8 @@
12
12
  "scripts": {
13
13
  "lint": "standard",
14
14
  "test-spec": "mocha --require ts-node/register -R spec --bail test.ts",
15
- "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require ts-node/register -R spec test.ts",
15
+ "test-cov": "nyc --reporter=lcov mocha -- --require ts-node/register -R spec test.ts",
16
+ "coverage": "nyc report --reporter=text-lcov",
16
17
  "test": "npm run lint && npm run test-cov"
17
18
  },
18
19
  "keywords": [
@@ -34,12 +35,12 @@
34
35
  "devDependencies": {
35
36
  "@types/chai": "^4.0.4",
36
37
  "@types/mocha": "^5.2.5",
37
- "@types/node": "^10.7.1",
38
+ "@types/node": "^12.7.3",
38
39
  "chai": "^4.1.1",
39
- "istanbul": "^0.4.5",
40
- "mocha": "^5.2.0",
41
- "standard": "^11.0.1",
42
- "ts-node": "^7.0.1",
43
- "typescript": "^3.0.1"
40
+ "mocha": "^6.2.0",
41
+ "nyc": "^14.1.1",
42
+ "standard": "^14.1.0",
43
+ "ts-node": "^8.3.0",
44
+ "typescript": "^3.7.2"
44
45
  }
45
46
  }