path-to-regexp 2.2.1 → 3.1.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,24 @@
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
+
17
+ 2.2.1 / 2018-04-24
18
+ ==================
19
+
20
+ * Allow empty string with `end: false` to match both relative and absolute paths
21
+
1
22
  2.2.0 / 2018-03-06
2
23
  ==================
3
24
 
package/Readme.md CHANGED
@@ -18,7 +18,7 @@ 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
24
  // pathToRegexp.parse(path)
@@ -26,24 +26,24 @@ var pathToRegexp = require('path-to-regexp')
26
26
  ```
27
27
 
28
28
  - **path** A string, array of strings, or a regular expression.
29
- - **keys** An array to be populated with the keys found in the path.
29
+ - **keys** An array to populate with keys found in the path.
30
30
  - **options**
31
- - **sensitive** When `true` the route will be case sensitive. (default: `false`)
32
- - **strict** When `false` the trailing slash is optional. (default: `false`)
33
- - **end** When `false` the path will match at the beginning. (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: `'./'`)
31
+ - **sensitive** When `true` the regexp will be case sensitive. (default: `false`)
32
+ - **strict** When `true` the regexp allows an optional trailing delimiter to match. (default: `false`)
33
+ - **end** When `true` the regexp will match to the end of the string. (default: `true`)
34
+ - **start** When `true` the regexp will match from the beginning of the string. (default: `true`)
35
+ - **delimiter** The default delimiter for segments. (default: `'/'`)
36
+ - **endsWith** Optional character, or list of characters, to treat as "end" characters.
37
+ - **whitelist** List of characters to consider delimiters when parsing. (default: `undefined`, any character)
38
38
 
39
39
  ```javascript
40
- var keys = []
41
- var re = pathToRegexp('/foo/:bar', keys)
42
- // re = /^\/foo\/([^\/]+?)\/?$/i
40
+ const keys = []
41
+ const regexp = pathToRegexp('/foo/:bar', keys)
42
+ // regexp = /^\/foo\/([^\/]+?)\/?$/i
43
43
  // keys = [{ name: 'bar', prefix: '/', delimiter: '/', optional: false, repeat: false, pattern: '[^\\/]+?' }]
44
44
  ```
45
45
 
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).
46
+ **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
47
 
48
48
  ### Parameters
49
49
 
@@ -51,17 +51,17 @@ The path argument is used to define parameters and populate the list of keys.
51
51
 
52
52
  #### Named Parameters
53
53
 
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.
54
+ 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
55
 
56
56
  ```js
57
- var re = pathToRegexp('/:foo/:bar')
57
+ const regexp = pathToRegexp('/:foo/:bar')
58
58
  // keys = [{ name: 'foo', prefix: '/', ... }, { name: 'bar', prefix: '/', ... }]
59
59
 
60
- re.exec('/test/route')
61
- //=> ['/test/route', 'test', 'route']
60
+ regexp.exec('/test/route')
61
+ //=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
62
62
  ```
63
63
 
64
- **Please note:** Parameter names must be made up of "word characters" (`[A-Za-z0-9_]`).
64
+ **Please note:** Parameter names must use "word characters" (`[A-Za-z0-9_]`).
65
65
 
66
66
  #### Parameter Modifiers
67
67
 
@@ -70,83 +70,92 @@ re.exec('/test/route')
70
70
  Parameters can be suffixed with a question mark (`?`) to make the parameter optional.
71
71
 
72
72
  ```js
73
- var re = pathToRegexp('/:foo/:bar?')
73
+ const regexp = pathToRegexp('/:foo/:bar?')
74
74
  // keys = [{ name: 'foo', ... }, { name: 'bar', delimiter: '/', optional: true, repeat: false }]
75
75
 
76
- re.exec('/test')
77
- //=> ['/test', 'test', undefined]
76
+ regexp.exec('/test')
77
+ //=> [ '/test', 'test', undefined, index: 0, input: '/test', groups: undefined ]
78
78
 
79
- re.exec('/test/route')
80
- //=> ['/test', 'test', 'route']
79
+ regexp.exec('/test/route')
80
+ //=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
81
81
  ```
82
82
 
83
- **Tip:** If the parameter is the _only_ value in the segment, the prefix is also optional.
83
+ **Tip:** The prefix is also optional, escape the prefix `\/` to make it required.
84
84
 
85
85
  ##### Zero or more
86
86
 
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.
87
+ Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. The prefix is used for each match.
88
88
 
89
89
  ```js
90
- var re = pathToRegexp('/:foo*')
90
+ const regexp = pathToRegexp('/:foo*')
91
91
  // keys = [{ name: 'foo', delimiter: '/', optional: true, repeat: true }]
92
92
 
93
- re.exec('/')
94
- //=> ['/', undefined]
93
+ regexp.exec('/')
94
+ //=> [ '/', undefined, index: 0, input: '/', groups: undefined ]
95
95
 
96
- re.exec('/bar/baz')
97
- //=> ['/bar/baz', 'bar/baz']
96
+ regexp.exec('/bar/baz')
97
+ //=> [ '/bar/baz', 'bar/baz', index: 0, input: '/bar/baz', groups: undefined ]
98
98
  ```
99
99
 
100
100
  ##### One or more
101
101
 
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.
102
+ Parameters can be suffixed with a plus sign (`+`) to denote a one or more parameter matches. The prefix is used for each match.
103
103
 
104
104
  ```js
105
- var re = pathToRegexp('/:foo+')
105
+ const regexp = pathToRegexp('/:foo+')
106
106
  // keys = [{ name: 'foo', delimiter: '/', optional: false, repeat: true }]
107
107
 
108
- re.exec('/')
108
+ regexp.exec('/')
109
109
  //=> null
110
110
 
111
- re.exec('/bar/baz')
112
- //=> ['/bar/baz', 'bar/baz']
111
+ regexp.exec('/bar/baz')
112
+ //=> [ '/bar/baz','bar/baz', index: 0, input: '/bar/baz', groups: undefined ]
113
+ ```
114
+
115
+ #### Unnamed Parameters
116
+
117
+ 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.
118
+
119
+ ```js
120
+ const regexp = pathToRegexp('/:foo/(.*)')
121
+ // keys = [{ name: 'foo', ... }, { name: 0, ... }]
122
+
123
+ regexp.exec('/test/route')
124
+ //=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
113
125
  ```
114
126
 
115
127
  #### Custom Matching Parameters
116
128
 
117
- All parameters can be provided a custom regexp, which overrides the default match (`[^\/]+`). For example, you can match digits in the path:
129
+ All parameters can have a custom regexp, which overrides the default match (`[^/]+`). For example, you can match digits or names in a path:
118
130
 
119
131
  ```js
120
- var re = pathToRegexp('/icon-:foo(\\d+).png')
132
+ const regexpNumbers = pathToRegexp('/icon-:foo(\\d+).png')
121
133
  // keys = [{ name: 'foo', ... }]
122
134
 
123
- re.exec('/icon-123.png')
135
+ regexpNumbers.exec('/icon-123.png')
124
136
  //=> ['/icon-123.png', '123']
125
137
 
126
- re.exec('/icon-abc.png')
138
+ regexpNumbers.exec('/icon-abc.png')
127
139
  //=> null
128
- ```
129
140
 
130
- **Please note:** Backslashes need to be escaped with another backslash in strings.
141
+ const regexpWord = pathToRegexp('/(user|u)')
142
+ // keys = [{ name: 0, ... }]
131
143
 
132
- #### Unnamed Parameters
144
+ regexpWord.exec('/u')
145
+ //=> ['/u', 'u']
133
146
 
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.
135
-
136
- ```js
137
- var re = pathToRegexp('/:foo/(.*)')
138
- // keys = [{ name: 'foo', ... }, { name: 0, ... }]
139
-
140
- re.exec('/test/route')
141
- //=> ['/test/route', 'test', 'route']
147
+ regexpWord.exec('/users')
148
+ //=> null
142
149
  ```
143
150
 
151
+ **Tip:** Backslashes need to be escaped with another backslash in JavaScript strings.
152
+
144
153
  ### Parse
145
154
 
146
155
  The parse function is exposed via `pathToRegexp.parse`. This will return an array of strings and keys.
147
156
 
148
157
  ```js
149
- var tokens = pathToRegexp.parse('/route/:foo/(.*)')
158
+ const tokens = pathToRegexp.parse('/route/:foo/(.*)')
150
159
 
151
160
  console.log(tokens[0])
152
161
  //=> "/route"
@@ -165,7 +174,7 @@ console.log(tokens[2])
165
174
  Path-To-RegExp exposes a compile function for transforming a string into a valid path.
166
175
 
167
176
  ```js
168
- var toPath = pathToRegexp.compile('/user/:id')
177
+ const toPath = pathToRegexp.compile('/user/:id')
169
178
 
170
179
  toPath({ id: 123 }) //=> "/user/123"
171
180
  toPath({ id: 'café' }) //=> "/user/caf%C3%A9"
@@ -174,16 +183,17 @@ toPath({ id: '/' }) //=> "/user/%2F"
174
183
  toPath({ id: ':/' }) //=> "/user/%3A%2F"
175
184
  toPath({ id: ':/' }, { encode: (value, token) => value }) //=> "/user/:/"
176
185
 
177
- var toPathRepeated = pathToRegexp.compile('/:segment+')
186
+ const toPathRepeated = pathToRegexp.compile('/:segment+')
178
187
 
179
188
  toPathRepeated({ segment: 'foo' }) //=> "/foo"
180
189
  toPathRepeated({ segment: ['a', 'b', 'c'] }) //=> "/a/b/c"
181
190
 
182
- var toPathRegexp = pathToRegexp.compile('/user/:id(\\d+)')
191
+ const toPathRegexp = pathToRegexp.compile('/user/:id(\\d+)')
183
192
 
184
193
  toPathRegexp({ id: 123 }) //=> "/user/123"
185
194
  toPathRegexp({ id: '123' }) //=> "/user/123"
186
195
  toPathRegexp({ id: 'abc' }) //=> Throws `TypeError`.
196
+ toPathRegexp({ id: 'abc' }, { validate: true }) //=> "/user/abc"
187
197
  ```
188
198
 
189
199
  **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 +208,10 @@ Path-To-RegExp exposes the two functions used internally that accept an array of
198
208
  #### Token Information
199
209
 
200
210
  * `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 `/`)
211
+ * `prefix` The prefix character for the segment (e.g. `/`)
212
+ * `delimiter` The delimiter for the segment (same as prefix or default delimiter)
203
213
  * `optional` Indicates the token is optional (`boolean`)
204
214
  * `repeat` Indicates the token is repeated (`boolean`)
205
- * `partial` Indicates this token is a partial path segment (`boolean`)
206
215
  * `pattern` The RegExp used to match this token (`string`)
207
216
 
208
217
  ## Compatibility with Express <= 4.x
package/index.d.ts CHANGED
@@ -3,17 +3,21 @@ declare function pathToRegexp (path: pathToRegexp.Path, keys?: pathToRegexp.Key[
3
3
  declare namespace pathToRegexp {
4
4
  export interface RegExpOptions {
5
5
  /**
6
- * When `true` the route will be case sensitive. (default: `false`)
6
+ * When `true` the regexp will be case sensitive. (default: `false`)
7
7
  */
8
8
  sensitive?: boolean;
9
9
  /**
10
- * When `false` the trailing slash is optional. (default: `false`)
10
+ * When `true` the regexp allows an optional trailing delimiter to match. (default: `false`)
11
11
  */
12
12
  strict?: boolean;
13
13
  /**
14
- * When `false` the path will match at the beginning. (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
  /**
@@ -43,12 +54,12 @@ declare namespace pathToRegexp {
43
54
  /**
44
55
  * Transforming an Express-style path into a valid path.
45
56
  */
46
- export function compile (path: string, options?: ParseOptions): PathFunction;
57
+ export function compile <P extends object = object> (path: string, options?: ParseOptions & TokensToFunctionOptions): PathFunction<P>;
47
58
 
48
59
  /**
49
60
  * Transform an array of tokens into a path generator function.
50
61
  */
51
- export function tokensToFunction (tokens: Token[]): PathFunction;
62
+ export function tokensToFunction <P extends object = object> (tokens: Token[], options?: TokensToFunctionOptions): PathFunction<P>;
52
63
 
53
64
  /**
54
65
  * Transform an array of tokens into a matching regular expression.
@@ -62,7 +73,6 @@ declare namespace pathToRegexp {
62
73
  optional: boolean;
63
74
  repeat: boolean;
64
75
  pattern: string;
65
- partial: boolean;
66
76
  }
67
77
 
68
78
  interface PathFunctionOptions {
@@ -70,11 +80,15 @@ declare namespace pathToRegexp {
70
80
  * Function for encoding input strings for output.
71
81
  */
72
82
  encode?: (value: string, token: Key) => string;
83
+ /**
84
+ * When `false` the function can produce an invalid (unmatched) path. (default: `true`)
85
+ */
86
+ validate?: boolean;
73
87
  }
74
88
 
75
89
  export type Token = string | Key;
76
90
  export type Path = string | RegExp | Array<string | RegExp>;
77
- export type PathFunction = (data?: Object, options?: PathFunctionOptions) => string;
91
+ export type PathFunction <P extends object = object> = (data?: P, options?: PathFunctionOptions) => string;
78
92
  }
79
93
 
80
94
  export = pathToRegexp;
package/index.js CHANGED
@@ -11,7 +11,6 @@ module.exports.tokensToRegExp = tokensToRegExp
11
11
  * Default configs.
12
12
  */
13
13
  var DEFAULT_DELIMITER = '/'
14
- var DEFAULT_DELIMITERS = './'
15
14
 
16
15
  /**
17
16
  * The main path matching regexp utility.
@@ -25,8 +24,8 @@ var PATH_REGEXP = new RegExp([
25
24
  // Match Express-style parameters and un-named parameters with a prefix
26
25
  // and optional suffixes. Matches appear as:
27
26
  //
28
- // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"]
29
- // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined]
27
+ // ":test(\\d+)?" => ["test", "\d+", undefined, "?"]
28
+ // "(\\d+)" => [undefined, undefined, "\d+", undefined]
30
29
  '(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?'
31
30
  ].join('|'), 'g')
32
31
 
@@ -43,7 +42,7 @@ function parse (str, options) {
43
42
  var index = 0
44
43
  var path = ''
45
44
  var defaultDelimiter = (options && options.delimiter) || DEFAULT_DELIMITER
46
- var delimiters = (options && options.delimiters) || DEFAULT_DELIMITERS
45
+ var whitelist = (options && options.whitelist) || undefined
47
46
  var pathEscaped = false
48
47
  var res
49
48
 
@@ -62,7 +61,6 @@ function parse (str, options) {
62
61
  }
63
62
 
64
63
  var prev = ''
65
- var next = str[index]
66
64
  var name = res[2]
67
65
  var capture = res[3]
68
66
  var group = res[4]
@@ -70,9 +68,11 @@ function parse (str, options) {
70
68
 
71
69
  if (!pathEscaped && path.length) {
72
70
  var k = path.length - 1
71
+ var c = path[k]
72
+ var matches = whitelist ? whitelist.indexOf(c) > -1 : true
73
73
 
74
- if (delimiters.indexOf(path[k]) > -1) {
75
- prev = path[k]
74
+ if (matches) {
75
+ prev = c
76
76
  path = path.slice(0, k)
77
77
  }
78
78
  }
@@ -84,11 +84,10 @@ function parse (str, options) {
84
84
  pathEscaped = false
85
85
  }
86
86
 
87
- var partial = prev !== '' && next !== undefined && next !== prev
88
87
  var repeat = modifier === '+' || modifier === '*'
89
88
  var optional = modifier === '?' || modifier === '*'
90
- var delimiter = prev || defaultDelimiter
91
89
  var pattern = capture || group
90
+ var delimiter = prev || defaultDelimiter
92
91
 
93
92
  tokens.push({
94
93
  name: name || key++,
@@ -96,8 +95,9 @@ function parse (str, options) {
96
95
  delimiter: delimiter,
97
96
  optional: optional,
98
97
  repeat: repeat,
99
- partial: partial,
100
- pattern: pattern ? escapeGroup(pattern) : '[^' + escapeString(delimiter) + ']+?'
98
+ pattern: pattern
99
+ ? escapeGroup(pattern)
100
+ : '[^' + escapeString(delimiter === defaultDelimiter ? delimiter : (delimiter + defaultDelimiter)) + ']+?'
101
101
  })
102
102
  }
103
103
 
@@ -117,26 +117,27 @@ function parse (str, options) {
117
117
  * @return {!function(Object=, Object=)}
118
118
  */
119
119
  function compile (str, options) {
120
- return tokensToFunction(parse(str, options))
120
+ return tokensToFunction(parse(str, options), options)
121
121
  }
122
122
 
123
123
  /**
124
124
  * Expose a method for transforming tokens into the path function.
125
125
  */
126
- function tokensToFunction (tokens) {
126
+ function tokensToFunction (tokens, options) {
127
127
  // Compile all the tokens into regexps.
128
128
  var matches = new Array(tokens.length)
129
129
 
130
130
  // Compile all the patterns before compilation.
131
131
  for (var i = 0; i < tokens.length; i++) {
132
132
  if (typeof tokens[i] === 'object') {
133
- matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$')
133
+ matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options))
134
134
  }
135
135
  }
136
136
 
137
137
  return function (data, options) {
138
138
  var path = ''
139
139
  var encode = (options && options.encode) || encodeURIComponent
140
+ var validate = options ? options.validate !== false : true
140
141
 
141
142
  for (var i = 0; i < tokens.length; i++) {
142
143
  var token = tokens[i]
@@ -163,7 +164,7 @@ function tokensToFunction (tokens) {
163
164
  for (var j = 0; j < value.length; j++) {
164
165
  segment = encode(value[j], token)
165
166
 
166
- if (!matches[i].test(segment)) {
167
+ if (validate && !matches[i].test(segment)) {
167
168
  throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '"')
168
169
  }
169
170
 
@@ -176,7 +177,7 @@ function tokensToFunction (tokens) {
176
177
  if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
177
178
  segment = encode(String(value), token)
178
179
 
179
- if (!matches[i].test(segment)) {
180
+ if (validate && !matches[i].test(segment)) {
180
181
  throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but got "' + segment + '"')
181
182
  }
182
183
 
@@ -184,12 +185,7 @@ function tokensToFunction (tokens) {
184
185
  continue
185
186
  }
186
187
 
187
- if (token.optional) {
188
- // Prepend partial segment prefixes.
189
- if (token.partial) path += token.prefix
190
-
191
- continue
192
- }
188
+ if (token.optional) continue
193
189
 
194
190
  throw new TypeError('Expected "' + token.name + '" to be ' + (token.repeat ? 'an array' : 'a string'))
195
191
  }
@@ -249,7 +245,6 @@ function regexpToRegexp (path, keys) {
249
245
  delimiter: null,
250
246
  optional: false,
251
247
  repeat: false,
252
- partial: false,
253
248
  pattern: null
254
249
  })
255
250
  }
@@ -300,12 +295,11 @@ function tokensToRegExp (tokens, keys, options) {
300
295
  options = options || {}
301
296
 
302
297
  var strict = options.strict
298
+ var start = options.start !== false
303
299
  var end = options.end !== false
304
- var delimiter = escapeString(options.delimiter || DEFAULT_DELIMITER)
305
- var delimiters = options.delimiters || DEFAULT_DELIMITERS
300
+ var delimiter = options.delimiter || DEFAULT_DELIMITER
306
301
  var endsWith = [].concat(options.endsWith || []).map(escapeString).concat('$').join('|')
307
- var route = ''
308
- var isEndDelimited = tokens.length === 0
302
+ var route = start ? '^' : ''
309
303
 
310
304
  // Iterate over the tokens and create our regexp string.
311
305
  for (var i = 0; i < tokens.length; i++) {
@@ -313,37 +307,40 @@ function tokensToRegExp (tokens, keys, options) {
313
307
 
314
308
  if (typeof token === 'string') {
315
309
  route += escapeString(token)
316
- isEndDelimited = i === tokens.length - 1 && delimiters.indexOf(token[token.length - 1]) > -1
317
310
  } else {
318
- var prefix = escapeString(token.prefix)
319
311
  var capture = token.repeat
320
- ? '(?:' + token.pattern + ')(?:' + prefix + '(?:' + token.pattern + '))*'
312
+ ? '(?:' + token.pattern + ')(?:' + escapeString(token.delimiter) + '(?:' + token.pattern + '))*'
321
313
  : token.pattern
322
314
 
323
315
  if (keys) keys.push(token)
324
316
 
325
317
  if (token.optional) {
326
- if (token.partial) {
327
- route += prefix + '(' + capture + ')?'
318
+ if (!token.prefix) {
319
+ route += '(' + capture + ')?'
328
320
  } else {
329
- route += '(?:' + prefix + '(' + capture + '))?'
321
+ route += '(?:' + escapeString(token.prefix) + '(' + capture + '))?'
330
322
  }
331
323
  } else {
332
- route += prefix + '(' + capture + ')'
324
+ route += escapeString(token.prefix) + '(' + capture + ')'
333
325
  }
334
326
  }
335
327
  }
336
328
 
337
329
  if (end) {
338
- if (!strict) route += '(?:' + delimiter + ')?'
330
+ if (!strict) route += '(?:' + escapeString(delimiter) + ')?'
339
331
 
340
332
  route += endsWith === '$' ? '$' : '(?=' + endsWith + ')'
341
333
  } else {
342
- if (!strict) route += '(?:' + delimiter + '(?=' + endsWith + '))?'
343
- if (!isEndDelimited) route += '(?=' + delimiter + '|' + endsWith + ')'
334
+ var endToken = tokens[tokens.length - 1]
335
+ var isEndDelimited = typeof endToken === 'string'
336
+ ? endToken[endToken.length - 1] === delimiter
337
+ : endToken === undefined
338
+
339
+ if (!strict) route += '(?:' + escapeString(delimiter) + '(?=' + endsWith + '))?'
340
+ if (!isEndDelimited) route += '(?=' + escapeString(delimiter) + '|' + endsWith + ')'
344
341
  }
345
342
 
346
- return new RegExp('^' + route, flags(options))
343
+ return new RegExp(route, flags(options))
347
344
  }
348
345
 
349
346
  /**
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.2.1",
4
+ "version": "3.1.0",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
7
7
  "files": [
@@ -33,13 +33,13 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/chai": "^4.0.4",
36
- "@types/mocha": "^2.2.42",
37
- "@types/node": "^8.0.24",
36
+ "@types/mocha": "^5.2.5",
37
+ "@types/node": "^12.7.3",
38
38
  "chai": "^4.1.1",
39
39
  "istanbul": "^0.4.5",
40
- "mocha": "^3.5.0",
41
- "standard": "^10.0.3",
42
- "ts-node": "^3.3.0",
43
- "typescript": "^2.4.2"
40
+ "mocha": "^6.2.0",
41
+ "standard": "^14.1.0",
42
+ "ts-node": "^8.3.0",
43
+ "typescript": "^3.0.1"
44
44
  }
45
45
  }