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