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