path-to-regexp 6.2.1 → 7.0.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/dist/index.js CHANGED
@@ -1,397 +1,392 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.pathToRegexp = exports.tokensToRegexp = exports.regexpToFunction = exports.match = exports.tokensToFunction = exports.compile = exports.parse = void 0;
3
+ exports.pathToRegexp = exports.match = exports.compile = exports.parse = exports.TokenData = void 0;
4
+ const DEFAULT_DELIMITER = "/";
5
+ const NOOP_VALUE = (value) => value;
6
+ const ID_CHAR = /^\p{XID_Continue}$/u;
7
+ const SIMPLE_TOKENS = {
8
+ "!": "!",
9
+ "@": "@",
10
+ ";": ";",
11
+ ",": ",",
12
+ "*": "*",
13
+ "+": "+",
14
+ "?": "?",
15
+ "{": "{",
16
+ "}": "}",
17
+ };
4
18
  /**
5
19
  * Tokenize input string.
6
20
  */
7
21
  function lexer(str) {
8
- var tokens = [];
9
- var i = 0;
10
- while (i < str.length) {
11
- var char = str[i];
12
- if (char === "*" || char === "+" || char === "?") {
13
- tokens.push({ type: "MODIFIER", index: i, value: str[i++] });
22
+ const chars = [...str];
23
+ const tokens = [];
24
+ let i = 0;
25
+ while (i < chars.length) {
26
+ const value = chars[i];
27
+ const type = SIMPLE_TOKENS[value];
28
+ if (type) {
29
+ tokens.push({ type, index: i++, value });
14
30
  continue;
15
31
  }
16
- if (char === "\\") {
17
- tokens.push({ type: "ESCAPED_CHAR", index: i++, value: str[i++] });
32
+ if (value === "\\") {
33
+ tokens.push({ type: "ESCAPED", index: i++, value: chars[i++] });
18
34
  continue;
19
35
  }
20
- if (char === "{") {
21
- tokens.push({ type: "OPEN", index: i, value: str[i++] });
22
- continue;
23
- }
24
- if (char === "}") {
25
- tokens.push({ type: "CLOSE", index: i, value: str[i++] });
26
- continue;
27
- }
28
- if (char === ":") {
29
- var name = "";
30
- var j = i + 1;
31
- while (j < str.length) {
32
- var code = str.charCodeAt(j);
33
- if (
34
- // `0-9`
35
- (code >= 48 && code <= 57) ||
36
- // `A-Z`
37
- (code >= 65 && code <= 90) ||
38
- // `a-z`
39
- (code >= 97 && code <= 122) ||
40
- // `_`
41
- code === 95) {
42
- name += str[j++];
43
- continue;
44
- }
45
- break;
36
+ if (value === ":") {
37
+ let name = "";
38
+ while (ID_CHAR.test(chars[++i])) {
39
+ name += chars[i];
40
+ }
41
+ if (!name) {
42
+ throw new TypeError(`Missing parameter name at ${i}`);
46
43
  }
47
- if (!name)
48
- throw new TypeError("Missing parameter name at ".concat(i));
49
44
  tokens.push({ type: "NAME", index: i, value: name });
50
- i = j;
51
45
  continue;
52
46
  }
53
- if (char === "(") {
54
- var count = 1;
55
- var pattern = "";
56
- var j = i + 1;
57
- if (str[j] === "?") {
58
- throw new TypeError("Pattern cannot start with \"?\" at ".concat(j));
47
+ if (value === "(") {
48
+ const pos = i++;
49
+ let count = 1;
50
+ let pattern = "";
51
+ if (chars[i] === "?") {
52
+ throw new TypeError(`Pattern cannot start with "?" at ${i}`);
59
53
  }
60
- while (j < str.length) {
61
- if (str[j] === "\\") {
62
- pattern += str[j++] + str[j++];
54
+ while (i < chars.length) {
55
+ if (chars[i] === "\\") {
56
+ pattern += chars[i++] + chars[i++];
63
57
  continue;
64
58
  }
65
- if (str[j] === ")") {
59
+ if (chars[i] === ")") {
66
60
  count--;
67
61
  if (count === 0) {
68
- j++;
62
+ i++;
69
63
  break;
70
64
  }
71
65
  }
72
- else if (str[j] === "(") {
66
+ else if (chars[i] === "(") {
73
67
  count++;
74
- if (str[j + 1] !== "?") {
75
- throw new TypeError("Capturing groups are not allowed at ".concat(j));
68
+ if (chars[i + 1] !== "?") {
69
+ throw new TypeError(`Capturing groups are not allowed at ${i}`);
76
70
  }
77
71
  }
78
- pattern += str[j++];
72
+ pattern += chars[i++];
79
73
  }
80
74
  if (count)
81
- throw new TypeError("Unbalanced pattern at ".concat(i));
75
+ throw new TypeError(`Unbalanced pattern at ${pos}`);
82
76
  if (!pattern)
83
- throw new TypeError("Missing pattern at ".concat(i));
77
+ throw new TypeError(`Missing pattern at ${pos}`);
84
78
  tokens.push({ type: "PATTERN", index: i, value: pattern });
85
- i = j;
86
79
  continue;
87
80
  }
88
- tokens.push({ type: "CHAR", index: i, value: str[i++] });
81
+ tokens.push({ type: "CHAR", index: i, value: chars[i++] });
89
82
  }
90
83
  tokens.push({ type: "END", index: i, value: "" });
91
- return tokens;
84
+ return new Iter(tokens);
92
85
  }
93
- /**
94
- * Parse a string for the raw tokens.
95
- */
96
- function parse(str, options) {
97
- if (options === void 0) { options = {}; }
98
- var tokens = lexer(str);
99
- var _a = options.prefixes, prefixes = _a === void 0 ? "./" : _a;
100
- var defaultPattern = "[^".concat(escapeString(options.delimiter || "/#?"), "]+?");
101
- var result = [];
102
- var key = 0;
103
- var i = 0;
104
- var path = "";
105
- var tryConsume = function (type) {
106
- if (i < tokens.length && tokens[i].type === type)
107
- return tokens[i++].value;
108
- };
109
- var mustConsume = function (type) {
110
- var value = tryConsume(type);
86
+ class Iter {
87
+ constructor(tokens) {
88
+ this.tokens = tokens;
89
+ this.index = 0;
90
+ }
91
+ peek() {
92
+ return this.tokens[this.index];
93
+ }
94
+ tryConsume(type) {
95
+ const token = this.peek();
96
+ if (token.type !== type)
97
+ return;
98
+ this.index++;
99
+ return token.value;
100
+ }
101
+ consume(type) {
102
+ const value = this.tryConsume(type);
111
103
  if (value !== undefined)
112
104
  return value;
113
- var _a = tokens[i], nextType = _a.type, index = _a.index;
114
- throw new TypeError("Unexpected ".concat(nextType, " at ").concat(index, ", expected ").concat(type));
115
- };
116
- var consumeText = function () {
117
- var result = "";
118
- var value;
119
- while ((value = tryConsume("CHAR") || tryConsume("ESCAPED_CHAR"))) {
105
+ const { type: nextType, index } = this.peek();
106
+ throw new TypeError(`Unexpected ${nextType} at ${index}, expected ${type}: https://git.new/pathToRegexpError`);
107
+ }
108
+ text() {
109
+ let result = "";
110
+ let value;
111
+ while ((value = this.tryConsume("CHAR") || this.tryConsume("ESCAPED"))) {
120
112
  result += value;
121
113
  }
122
114
  return result;
123
- };
124
- while (i < tokens.length) {
125
- var char = tryConsume("CHAR");
126
- var name = tryConsume("NAME");
127
- var pattern = tryConsume("PATTERN");
115
+ }
116
+ modifier() {
117
+ return (this.tryConsume("?") || this.tryConsume("*") || this.tryConsume("+") || "");
118
+ }
119
+ }
120
+ /**
121
+ * Tokenized path instance. Can we passed around instead of string.
122
+ */
123
+ class TokenData {
124
+ constructor(tokens, delimiter) {
125
+ this.tokens = tokens;
126
+ this.delimiter = delimiter;
127
+ }
128
+ }
129
+ exports.TokenData = TokenData;
130
+ /**
131
+ * Parse a string for the raw tokens.
132
+ */
133
+ function parse(str, options = {}) {
134
+ const { delimiter = DEFAULT_DELIMITER, encodePath = NOOP_VALUE } = options;
135
+ const tokens = [];
136
+ const it = lexer(str);
137
+ let key = 0;
138
+ do {
139
+ const path = it.text();
140
+ if (path)
141
+ tokens.push(encodePath(path));
142
+ const name = it.tryConsume("NAME");
143
+ const pattern = it.tryConsume("PATTERN");
128
144
  if (name || pattern) {
129
- var prefix = char || "";
130
- if (prefixes.indexOf(prefix) === -1) {
131
- path += prefix;
132
- prefix = "";
133
- }
134
- if (path) {
135
- result.push(path);
136
- path = "";
137
- }
138
- result.push({
139
- name: name || key++,
140
- prefix: prefix,
141
- suffix: "",
142
- pattern: pattern || defaultPattern,
143
- modifier: tryConsume("MODIFIER") || "",
145
+ tokens.push({
146
+ name: name || String(key++),
147
+ pattern,
144
148
  });
149
+ const next = it.peek();
150
+ if (next.type === "*") {
151
+ throw new TypeError(`Unexpected * at ${next.index}, you probably want \`/*\` or \`{/:foo}*\`: https://git.new/pathToRegexpError`);
152
+ }
145
153
  continue;
146
154
  }
147
- var value = char || tryConsume("ESCAPED_CHAR");
148
- if (value) {
149
- path += value;
155
+ const asterisk = it.tryConsume("*");
156
+ if (asterisk) {
157
+ tokens.push({
158
+ name: String(key++),
159
+ pattern: `[^${escape(delimiter)}]*`,
160
+ modifier: "*",
161
+ separator: delimiter,
162
+ });
150
163
  continue;
151
164
  }
152
- if (path) {
153
- result.push(path);
154
- path = "";
155
- }
156
- var open = tryConsume("OPEN");
165
+ const open = it.tryConsume("{");
157
166
  if (open) {
158
- var prefix = consumeText();
159
- var name_1 = tryConsume("NAME") || "";
160
- var pattern_1 = tryConsume("PATTERN") || "";
161
- var suffix = consumeText();
162
- mustConsume("CLOSE");
163
- result.push({
164
- name: name_1 || (pattern_1 ? key++ : ""),
165
- pattern: name_1 && !pattern_1 ? defaultPattern : pattern_1,
166
- prefix: prefix,
167
- suffix: suffix,
168
- modifier: tryConsume("MODIFIER") || "",
167
+ const prefix = it.text();
168
+ const name = it.tryConsume("NAME");
169
+ const pattern = it.tryConsume("PATTERN");
170
+ const suffix = it.text();
171
+ const separator = it.tryConsume(";") ? it.text() : prefix + suffix;
172
+ it.consume("}");
173
+ const modifier = it.modifier();
174
+ tokens.push({
175
+ name: name || (pattern ? String(key++) : ""),
176
+ prefix: encodePath(prefix),
177
+ suffix: encodePath(suffix),
178
+ pattern,
179
+ modifier,
180
+ separator,
169
181
  });
170
182
  continue;
171
183
  }
172
- mustConsume("END");
173
- }
174
- return result;
184
+ it.consume("END");
185
+ break;
186
+ } while (true);
187
+ return new TokenData(tokens, delimiter);
175
188
  }
176
189
  exports.parse = parse;
177
190
  /**
178
191
  * Compile a string to a template function for the path.
179
192
  */
180
- function compile(str, options) {
181
- return tokensToFunction(parse(str, options), options);
193
+ function compile(path, options = {}) {
194
+ const data = path instanceof TokenData ? path : parse(path, options);
195
+ return compileTokens(data, options);
182
196
  }
183
197
  exports.compile = compile;
184
198
  /**
185
- * Expose a method for transforming tokens into the path function.
199
+ * Convert a single token into a path building function.
186
200
  */
187
- function tokensToFunction(tokens, options) {
188
- if (options === void 0) { options = {}; }
189
- var reFlags = flags(options);
190
- var _a = options.encode, encode = _a === void 0 ? function (x) { return x; } : _a, _b = options.validate, validate = _b === void 0 ? true : _b;
191
- // Compile all the tokens into regexps.
192
- var matches = tokens.map(function (token) {
193
- if (typeof token === "object") {
194
- return new RegExp("^(?:".concat(token.pattern, ")$"), reFlags);
195
- }
196
- });
197
- return function (data) {
198
- var path = "";
199
- for (var i = 0; i < tokens.length; i++) {
200
- var token = tokens[i];
201
- if (typeof token === "string") {
202
- path += token;
203
- continue;
204
- }
205
- var value = data ? data[token.name] : undefined;
206
- var optional = token.modifier === "?" || token.modifier === "*";
207
- var repeat = token.modifier === "*" || token.modifier === "+";
208
- if (Array.isArray(value)) {
209
- if (!repeat) {
210
- throw new TypeError("Expected \"".concat(token.name, "\" to not repeat, but got an array"));
211
- }
212
- if (value.length === 0) {
213
- if (optional)
214
- continue;
215
- throw new TypeError("Expected \"".concat(token.name, "\" to not be empty"));
216
- }
217
- for (var j = 0; j < value.length; j++) {
218
- var segment = encode(value[j], token);
219
- if (validate && !matches[i].test(segment)) {
220
- throw new TypeError("Expected all \"".concat(token.name, "\" to match \"").concat(token.pattern, "\", but got \"").concat(segment, "\""));
221
- }
222
- path += token.prefix + segment + token.suffix;
223
- }
224
- continue;
201
+ function tokenToFunction(token, encode) {
202
+ if (typeof token === "string") {
203
+ return () => token;
204
+ }
205
+ const encodeValue = encode || NOOP_VALUE;
206
+ const repeated = token.modifier === "+" || token.modifier === "*";
207
+ const optional = token.modifier === "?" || token.modifier === "*";
208
+ const { prefix = "", suffix = "", separator = "" } = token;
209
+ if (encode && repeated) {
210
+ const stringify = (value, index) => {
211
+ if (typeof value !== "string") {
212
+ throw new TypeError(`Expected "${token.name}/${index}" to be a string`);
225
213
  }
226
- if (typeof value === "string" || typeof value === "number") {
227
- var segment = encode(String(value), token);
228
- if (validate && !matches[i].test(segment)) {
229
- throw new TypeError("Expected \"".concat(token.name, "\" to match \"").concat(token.pattern, "\", but got \"").concat(segment, "\""));
230
- }
231
- path += token.prefix + segment + token.suffix;
232
- continue;
214
+ return encodeValue(value);
215
+ };
216
+ const compile = (value) => {
217
+ if (!Array.isArray(value)) {
218
+ throw new TypeError(`Expected "${token.name}" to be an array`);
233
219
  }
234
- if (optional)
235
- continue;
236
- var typeOfMessage = repeat ? "an array" : "a string";
237
- throw new TypeError("Expected \"".concat(token.name, "\" to be ").concat(typeOfMessage));
220
+ if (value.length === 0)
221
+ return "";
222
+ return prefix + value.map(stringify).join(separator) + suffix;
223
+ };
224
+ if (optional) {
225
+ return (data) => {
226
+ const value = data[token.name];
227
+ if (value == null)
228
+ return "";
229
+ return value.length ? compile(value) : "";
230
+ };
238
231
  }
239
- return path;
232
+ return (data) => {
233
+ const value = data[token.name];
234
+ return compile(value);
235
+ };
236
+ }
237
+ const stringify = (value) => {
238
+ if (typeof value !== "string") {
239
+ throw new TypeError(`Expected "${token.name}" to be a string`);
240
+ }
241
+ return prefix + encodeValue(value) + suffix;
242
+ };
243
+ if (optional) {
244
+ return (data) => {
245
+ const value = data[token.name];
246
+ if (value == null)
247
+ return "";
248
+ return stringify(value);
249
+ };
250
+ }
251
+ return (data) => {
252
+ const value = data[token.name];
253
+ return stringify(value);
240
254
  };
241
255
  }
242
- exports.tokensToFunction = tokensToFunction;
243
256
  /**
244
- * Create path match function from `path-to-regexp` spec.
257
+ * Transform tokens into a path building function.
245
258
  */
246
- function match(str, options) {
247
- var keys = [];
248
- var re = pathToRegexp(str, keys, options);
249
- return regexpToFunction(re, keys, options);
259
+ function compileTokens(data, options) {
260
+ const { encode = encodeURIComponent, loose = true, validate = true, } = options;
261
+ const reFlags = flags(options);
262
+ const stringify = toStringify(loose, data.delimiter);
263
+ const keyToRegexp = toKeyRegexp(stringify, data.delimiter);
264
+ // Compile all the tokens into regexps.
265
+ const encoders = data.tokens.map((token) => {
266
+ const fn = tokenToFunction(token, encode);
267
+ if (!validate || typeof token === "string")
268
+ return fn;
269
+ const pattern = keyToRegexp(token);
270
+ const validRe = new RegExp(`^${pattern}$`, reFlags);
271
+ return (data) => {
272
+ const value = fn(data);
273
+ if (!validRe.test(value)) {
274
+ throw new TypeError(`Invalid value for "${token.name}": ${JSON.stringify(value)}`);
275
+ }
276
+ return value;
277
+ };
278
+ });
279
+ return function path(data = {}) {
280
+ let path = "";
281
+ for (const encoder of encoders)
282
+ path += encoder(data);
283
+ return path;
284
+ };
250
285
  }
251
- exports.match = match;
252
286
  /**
253
- * Create a path match function from `path-to-regexp` output.
287
+ * Create path match function from `path-to-regexp` spec.
254
288
  */
255
- function regexpToFunction(re, keys, options) {
256
- if (options === void 0) { options = {}; }
257
- var _a = options.decode, decode = _a === void 0 ? function (x) { return x; } : _a;
258
- return function (pathname) {
259
- var m = re.exec(pathname);
289
+ function match(path, options = {}) {
290
+ const { decode = decodeURIComponent, loose = true } = options;
291
+ const data = path instanceof TokenData ? path : parse(path, options);
292
+ const stringify = toStringify(loose, data.delimiter);
293
+ const keys = [];
294
+ const re = tokensToRegexp(data, keys, options);
295
+ const decoders = keys.map((key) => {
296
+ if (decode && (key.modifier === "+" || key.modifier === "*")) {
297
+ const re = new RegExp(stringify(key.separator || ""), "g");
298
+ return (value) => value.split(re).map(decode);
299
+ }
300
+ return decode || NOOP_VALUE;
301
+ });
302
+ return function match(pathname) {
303
+ const m = re.exec(pathname);
260
304
  if (!m)
261
305
  return false;
262
- var path = m[0], index = m.index;
263
- var params = Object.create(null);
264
- var _loop_1 = function (i) {
306
+ const { 0: path, index } = m;
307
+ const params = Object.create(null);
308
+ for (let i = 1; i < m.length; i++) {
265
309
  if (m[i] === undefined)
266
- return "continue";
267
- var key = keys[i - 1];
268
- if (key.modifier === "*" || key.modifier === "+") {
269
- params[key.name] = m[i].split(key.prefix + key.suffix).map(function (value) {
270
- return decode(value, key);
271
- });
272
- }
273
- else {
274
- params[key.name] = decode(m[i], key);
275
- }
276
- };
277
- for (var i = 1; i < m.length; i++) {
278
- _loop_1(i);
310
+ continue;
311
+ const key = keys[i - 1];
312
+ const decoder = decoders[i - 1];
313
+ params[key.name] = decoder(m[i]);
279
314
  }
280
- return { path: path, index: index, params: params };
315
+ return { path, index, params };
281
316
  };
282
317
  }
283
- exports.regexpToFunction = regexpToFunction;
318
+ exports.match = match;
284
319
  /**
285
320
  * Escape a regular expression string.
286
321
  */
287
- function escapeString(str) {
322
+ function escape(str) {
288
323
  return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
289
324
  }
290
325
  /**
291
- * Get the flags for a regexp from the options.
326
+ * Escape and repeat loose characters for regular expressions.
292
327
  */
293
- function flags(options) {
294
- return options && options.sensitive ? "" : "i";
295
- }
296
- /**
297
- * Pull out keys from a regexp.
298
- */
299
- function regexpToRegexp(path, keys) {
300
- if (!keys)
301
- return path;
302
- var groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g;
303
- var index = 0;
304
- var execResult = groupsRegex.exec(path.source);
305
- while (execResult) {
306
- keys.push({
307
- // Use parenthesized substring match if available, index otherwise
308
- name: execResult[1] || index++,
309
- prefix: "",
310
- suffix: "",
311
- modifier: "",
312
- pattern: "",
313
- });
314
- execResult = groupsRegex.exec(path.source);
315
- }
316
- return path;
328
+ function looseReplacer(value, loose) {
329
+ return loose ? `${escape(value)}+` : escape(value);
317
330
  }
318
331
  /**
319
- * Transform an array into a regexp.
332
+ * Encode all non-delimiter characters using the encode function.
320
333
  */
321
- function arrayToRegexp(paths, keys, options) {
322
- var parts = paths.map(function (path) { return pathToRegexp(path, keys, options).source; });
323
- return new RegExp("(?:".concat(parts.join("|"), ")"), flags(options));
334
+ function toStringify(loose, delimiter) {
335
+ if (!loose)
336
+ return escape;
337
+ const re = new RegExp(`[^${escape(delimiter)}]+|(.)`, "g");
338
+ return (value) => value.replace(re, looseReplacer);
324
339
  }
325
340
  /**
326
- * Create a path regexp from string input.
341
+ * Get the flags for a regexp from the options.
327
342
  */
328
- function stringToRegexp(path, keys, options) {
329
- return tokensToRegexp(parse(path, options), keys, options);
343
+ function flags(options) {
344
+ return options.sensitive ? "" : "i";
330
345
  }
331
346
  /**
332
347
  * Expose a function for taking tokens and returning a RegExp.
333
348
  */
334
- function tokensToRegexp(tokens, keys, options) {
335
- if (options === void 0) { options = {}; }
336
- var _a = options.strict, strict = _a === void 0 ? false : _a, _b = options.start, start = _b === void 0 ? true : _b, _c = options.end, end = _c === void 0 ? true : _c, _d = options.encode, encode = _d === void 0 ? function (x) { return x; } : _d, _e = options.delimiter, delimiter = _e === void 0 ? "/#?" : _e, _f = options.endsWith, endsWith = _f === void 0 ? "" : _f;
337
- var endsWithRe = "[".concat(escapeString(endsWith), "]|$");
338
- var delimiterRe = "[".concat(escapeString(delimiter), "]");
339
- var route = start ? "^" : "";
340
- // Iterate over the tokens and create our regexp string.
341
- for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) {
342
- var token = tokens_1[_i];
349
+ function tokensToRegexp(data, keys, options) {
350
+ const { trailing = true, start = true, end = true, loose = true } = options;
351
+ const stringify = toStringify(loose, data.delimiter);
352
+ const keyToRegexp = toKeyRegexp(stringify, data.delimiter);
353
+ let pattern = start ? "^" : "";
354
+ for (const token of data.tokens) {
343
355
  if (typeof token === "string") {
344
- route += escapeString(encode(token));
356
+ pattern += stringify(token);
345
357
  }
346
358
  else {
347
- var prefix = escapeString(encode(token.prefix));
348
- var suffix = escapeString(encode(token.suffix));
349
- if (token.pattern) {
350
- if (keys)
351
- keys.push(token);
352
- if (prefix || suffix) {
353
- if (token.modifier === "+" || token.modifier === "*") {
354
- var mod = token.modifier === "*" ? "?" : "";
355
- route += "(?:".concat(prefix, "((?:").concat(token.pattern, ")(?:").concat(suffix).concat(prefix, "(?:").concat(token.pattern, "))*)").concat(suffix, ")").concat(mod);
356
- }
357
- else {
358
- route += "(?:".concat(prefix, "(").concat(token.pattern, ")").concat(suffix, ")").concat(token.modifier);
359
- }
360
- }
361
- else {
362
- if (token.modifier === "+" || token.modifier === "*") {
363
- route += "((?:".concat(token.pattern, ")").concat(token.modifier, ")");
364
- }
365
- else {
366
- route += "(".concat(token.pattern, ")").concat(token.modifier);
367
- }
368
- }
369
- }
370
- else {
371
- route += "(?:".concat(prefix).concat(suffix, ")").concat(token.modifier);
372
- }
359
+ if (token.name)
360
+ keys.push(token);
361
+ pattern += keyToRegexp(token);
373
362
  }
374
363
  }
375
- if (end) {
376
- if (!strict)
377
- route += "".concat(delimiterRe, "?");
378
- route += !options.endsWith ? "$" : "(?=".concat(endsWithRe, ")");
379
- }
380
- else {
381
- var endToken = tokens[tokens.length - 1];
382
- var isEndDelimited = typeof endToken === "string"
383
- ? delimiterRe.indexOf(endToken[endToken.length - 1]) > -1
384
- : endToken === undefined;
385
- if (!strict) {
386
- route += "(?:".concat(delimiterRe, "(?=").concat(endsWithRe, "))?");
387
- }
388
- if (!isEndDelimited) {
389
- route += "(?=".concat(delimiterRe, "|").concat(endsWithRe, ")");
364
+ if (trailing)
365
+ pattern += `(?:${stringify(data.delimiter)})?`;
366
+ pattern += end ? "$" : `(?=${escape(data.delimiter)}|$)`;
367
+ return new RegExp(pattern, flags(options));
368
+ }
369
+ /**
370
+ * Convert a token into a regexp string (re-used for path validation).
371
+ */
372
+ function toKeyRegexp(stringify, delimiter) {
373
+ const segmentPattern = `[^${escape(delimiter)}]+?`;
374
+ return (key) => {
375
+ const prefix = key.prefix ? stringify(key.prefix) : "";
376
+ const suffix = key.suffix ? stringify(key.suffix) : "";
377
+ const modifier = key.modifier || "";
378
+ if (key.name) {
379
+ const pattern = key.pattern || segmentPattern;
380
+ if (key.modifier === "+" || key.modifier === "*") {
381
+ const mod = key.modifier === "*" ? "?" : "";
382
+ const split = key.separator ? stringify(key.separator) : "";
383
+ return `(?:${prefix}((?:${pattern})(?:${split}(?:${pattern}))*)${suffix})${mod}`;
384
+ }
385
+ return `(?:${prefix}(${pattern})${suffix})${modifier}`;
390
386
  }
391
- }
392
- return new RegExp(route, flags(options));
387
+ return `(?:${prefix}${suffix})${modifier}`;
388
+ };
393
389
  }
394
- exports.tokensToRegexp = tokensToRegexp;
395
390
  /**
396
391
  * Normalize the given path string, returning a regular expression.
397
392
  *
@@ -399,12 +394,11 @@ exports.tokensToRegexp = tokensToRegexp;
399
394
  * placeholder key descriptions. For example, using `/user/:id`, `keys` will
400
395
  * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
401
396
  */
402
- function pathToRegexp(path, keys, options) {
403
- if (path instanceof RegExp)
404
- return regexpToRegexp(path, keys);
405
- if (Array.isArray(path))
406
- return arrayToRegexp(path, keys, options);
407
- return stringToRegexp(path, keys, options);
397
+ function pathToRegexp(path, options = {}) {
398
+ const data = path instanceof TokenData ? path : parse(path, options);
399
+ const keys = [];
400
+ const regexp = tokensToRegexp(data, keys, options);
401
+ return Object.assign(regexp, { keys });
408
402
  }
409
403
  exports.pathToRegexp = pathToRegexp;
410
404
  //# sourceMappingURL=index.js.map