175be4f005c74734f0a3ed24a6dafdc0e9de6e48e45aa4717812270e3da03e2bf69caa9222dcd4606f7d30f80ce5ffab0bb358a5ad7552729bdb18e7893022 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.pathToRegexp = exports.tokensToRegexp = exports.regexpToFunction = exports.match = exports.tokensToFunction = exports.compile = exports.parse = void 0;
  4. /**
  5. * Tokenize input string.
  6. */
  7. 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++] });
  14. continue;
  15. }
  16. if (char === "\\") {
  17. tokens.push({ type: "ESCAPED_CHAR", index: i++, value: str[i++] });
  18. continue;
  19. }
  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;
  46. }
  47. if (!name)
  48. throw new TypeError("Missing parameter name at ".concat(i));
  49. tokens.push({ type: "NAME", index: i, value: name });
  50. i = j;
  51. continue;
  52. }
  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));
  59. }
  60. while (j < str.length) {
  61. if (str[j] === "\\") {
  62. pattern += str[j++] + str[j++];
  63. continue;
  64. }
  65. if (str[j] === ")") {
  66. count--;
  67. if (count === 0) {
  68. j++;
  69. break;
  70. }
  71. }
  72. else if (str[j] === "(") {
  73. count++;
  74. if (str[j + 1] !== "?") {
  75. throw new TypeError("Capturing groups are not allowed at ".concat(j));
  76. }
  77. }
  78. pattern += str[j++];
  79. }
  80. if (count)
  81. throw new TypeError("Unbalanced pattern at ".concat(i));
  82. if (!pattern)
  83. throw new TypeError("Missing pattern at ".concat(i));
  84. tokens.push({ type: "PATTERN", index: i, value: pattern });
  85. i = j;
  86. continue;
  87. }
  88. tokens.push({ type: "CHAR", index: i, value: str[i++] });
  89. }
  90. tokens.push({ type: "END", index: i, value: "" });
  91. return tokens;
  92. }
  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, _b = options.delimiter, delimiter = _b === void 0 ? "/#?" : _b;
  100. var result = [];
  101. var key = 0;
  102. var i = 0;
  103. var path = "";
  104. var tryConsume = function (type) {
  105. if (i < tokens.length && tokens[i].type === type)
  106. return tokens[i++].value;
  107. };
  108. var mustConsume = function (type) {
  109. var value = tryConsume(type);
  110. if (value !== undefined)
  111. return value;
  112. var _a = tokens[i], nextType = _a.type, index = _a.index;
  113. throw new TypeError("Unexpected ".concat(nextType, " at ").concat(index, ", expected ").concat(type));
  114. };
  115. var consumeText = function () {
  116. var result = "";
  117. var value;
  118. while ((value = tryConsume("CHAR") || tryConsume("ESCAPED_CHAR"))) {
  119. result += value;
  120. }
  121. return result;
  122. };
  123. var isSafe = function (value) {
  124. for (var _i = 0, delimiter_1 = delimiter; _i < delimiter_1.length; _i++) {
  125. var char = delimiter_1[_i];
  126. if (value.indexOf(char) > -1)
  127. return true;
  128. }
  129. return false;
  130. };
  131. var safePattern = function (prefix) {
  132. var prev = result[result.length - 1];
  133. var prevText = prefix || (prev && typeof prev === "string" ? prev : "");
  134. if (prev && !prevText) {
  135. throw new TypeError("Must have text between two parameters, missing text after \"".concat(prev.name, "\""));
  136. }
  137. if (!prevText || isSafe(prevText))
  138. return "[^".concat(escapeString(delimiter), "]+?");
  139. return "(?:(?!".concat(escapeString(prevText), ")[^").concat(escapeString(delimiter), "])+?");
  140. };
  141. while (i < tokens.length) {
  142. var char = tryConsume("CHAR");
  143. var name = tryConsume("NAME");
  144. var pattern = tryConsume("PATTERN");
  145. if (name || pattern) {
  146. var prefix = char || "";
  147. if (prefixes.indexOf(prefix) === -1) {
  148. path += prefix;
  149. prefix = "";
  150. }
  151. if (path) {
  152. result.push(path);
  153. path = "";
  154. }
  155. result.push({
  156. name: name || key++,
  157. prefix: prefix,
  158. suffix: "",
  159. pattern: pattern || safePattern(prefix),
  160. modifier: tryConsume("MODIFIER") || "",
  161. });
  162. continue;
  163. }
  164. var value = char || tryConsume("ESCAPED_CHAR");
  165. if (value) {
  166. path += value;
  167. continue;
  168. }
  169. if (path) {
  170. result.push(path);
  171. path = "";
  172. }
  173. var open = tryConsume("OPEN");
  174. if (open) {
  175. var prefix = consumeText();
  176. var name_1 = tryConsume("NAME") || "";
  177. var pattern_1 = tryConsume("PATTERN") || "";
  178. var suffix = consumeText();
  179. mustConsume("CLOSE");
  180. result.push({
  181. name: name_1 || (pattern_1 ? key++ : ""),
  182. pattern: name_1 && !pattern_1 ? safePattern(prefix) : pattern_1,
  183. prefix: prefix,
  184. suffix: suffix,
  185. modifier: tryConsume("MODIFIER") || "",
  186. });
  187. continue;
  188. }
  189. mustConsume("END");
  190. }
  191. return result;
  192. }
  193. exports.parse = parse;
  194. /**
  195. * Compile a string to a template function for the path.
  196. */
  197. function compile(str, options) {
  198. return tokensToFunction(parse(str, options), options);
  199. }
  200. exports.compile = compile;
  201. /**
  202. * Expose a method for transforming tokens into the path function.
  203. */
  204. function tokensToFunction(tokens, options) {
  205. if (options === void 0) { options = {}; }
  206. var reFlags = flags(options);
  207. var _a = options.encode, encode = _a === void 0 ? function (x) { return x; } : _a, _b = options.validate, validate = _b === void 0 ? true : _b;
  208. // Compile all the tokens into regexps.
  209. var matches = tokens.map(function (token) {
  210. if (typeof token === "object") {
  211. return new RegExp("^(?:".concat(token.pattern, ")$"), reFlags);
  212. }
  213. });
  214. return function (data) {
  215. var path = "";
  216. for (var i = 0; i < tokens.length; i++) {
  217. var token = tokens[i];
  218. if (typeof token === "string") {
  219. path += token;
  220. continue;
  221. }
  222. var value = data ? data[token.name] : undefined;
  223. var optional = token.modifier === "?" || token.modifier === "*";
  224. var repeat = token.modifier === "*" || token.modifier === "+";
  225. if (Array.isArray(value)) {
  226. if (!repeat) {
  227. throw new TypeError("Expected \"".concat(token.name, "\" to not repeat, but got an array"));
  228. }
  229. if (value.length === 0) {
  230. if (optional)
  231. continue;
  232. throw new TypeError("Expected \"".concat(token.name, "\" to not be empty"));
  233. }
  234. for (var j = 0; j < value.length; j++) {
  235. var segment = encode(value[j], token);
  236. if (validate && !matches[i].test(segment)) {
  237. throw new TypeError("Expected all \"".concat(token.name, "\" to match \"").concat(token.pattern, "\", but got \"").concat(segment, "\""));
  238. }
  239. path += token.prefix + segment + token.suffix;
  240. }
  241. continue;
  242. }
  243. if (typeof value === "string" || typeof value === "number") {
  244. var segment = encode(String(value), token);
  245. if (validate && !matches[i].test(segment)) {
  246. throw new TypeError("Expected \"".concat(token.name, "\" to match \"").concat(token.pattern, "\", but got \"").concat(segment, "\""));
  247. }
  248. path += token.prefix + segment + token.suffix;
  249. continue;
  250. }
  251. if (optional)
  252. continue;
  253. var typeOfMessage = repeat ? "an array" : "a string";
  254. throw new TypeError("Expected \"".concat(token.name, "\" to be ").concat(typeOfMessage));
  255. }
  256. return path;
  257. };
  258. }
  259. exports.tokensToFunction = tokensToFunction;
  260. /**
  261. * Create path match function from `path-to-regexp` spec.
  262. */
  263. function match(str, options) {
  264. var keys = [];
  265. var re = pathToRegexp(str, keys, options);
  266. return regexpToFunction(re, keys, options);
  267. }
  268. exports.match = match;
  269. /**
  270. * Create a path match function from `path-to-regexp` output.
  271. */
  272. function regexpToFunction(re, keys, options) {
  273. if (options === void 0) { options = {}; }
  274. var _a = options.decode, decode = _a === void 0 ? function (x) { return x; } : _a;
  275. return function (pathname) {
  276. var m = re.exec(pathname);
  277. if (!m)
  278. return false;
  279. var path = m[0], index = m.index;
  280. var params = Object.create(null);
  281. var _loop_1 = function (i) {
  282. if (m[i] === undefined)
  283. return "continue";
  284. var key = keys[i - 1];
  285. if (key.modifier === "*" || key.modifier === "+") {
  286. params[key.name] = m[i].split(key.prefix + key.suffix).map(function (value) {
  287. return decode(value, key);
  288. });
  289. }
  290. else {
  291. params[key.name] = decode(m[i], key);
  292. }
  293. };
  294. for (var i = 1; i < m.length; i++) {
  295. _loop_1(i);
  296. }
  297. return { path: path, index: index, params: params };
  298. };
  299. }
  300. exports.regexpToFunction = regexpToFunction;
  301. /**
  302. * Escape a regular expression string.
  303. */
  304. function escapeString(str) {
  305. return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
  306. }
  307. /**
  308. * Get the flags for a regexp from the options.
  309. */
  310. function flags(options) {
  311. return options && options.sensitive ? "" : "i";
  312. }
  313. /**
  314. * Pull out keys from a regexp.
  315. */
  316. function regexpToRegexp(path, keys) {
  317. if (!keys)
  318. return path;
  319. var groupsRegex = /\((?:\?<(.*?)>)?(?!\?)/g;
  320. var index = 0;
  321. var execResult = groupsRegex.exec(path.source);
  322. while (execResult) {
  323. keys.push({
  324. // Use parenthesized substring match if available, index otherwise
  325. name: execResult[1] || index++,
  326. prefix: "",
  327. suffix: "",
  328. modifier: "",
  329. pattern: "",
  330. });
  331. execResult = groupsRegex.exec(path.source);
  332. }
  333. return path;
  334. }
  335. /**
  336. * Transform an array into a regexp.
  337. */
  338. function arrayToRegexp(paths, keys, options) {
  339. var parts = paths.map(function (path) { return pathToRegexp(path, keys, options).source; });
  340. return new RegExp("(?:".concat(parts.join("|"), ")"), flags(options));
  341. }
  342. /**
  343. * Create a path regexp from string input.
  344. */
  345. function stringToRegexp(path, keys, options) {
  346. return tokensToRegexp(parse(path, options), keys, options);
  347. }
  348. /**
  349. * Expose a function for taking tokens and returning a RegExp.
  350. */
  351. function tokensToRegexp(tokens, keys, options) {
  352. if (options === void 0) { options = {}; }
  353. 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;
  354. var endsWithRe = "[".concat(escapeString(endsWith), "]|$");
  355. var delimiterRe = "[".concat(escapeString(delimiter), "]");
  356. var route = start ? "^" : "";
  357. // Iterate over the tokens and create our regexp string.
  358. for (var _i = 0, tokens_1 = tokens; _i < tokens_1.length; _i++) {
  359. var token = tokens_1[_i];
  360. if (typeof token === "string") {
  361. route += escapeString(encode(token));
  362. }
  363. else {
  364. var prefix = escapeString(encode(token.prefix));
  365. var suffix = escapeString(encode(token.suffix));
  366. if (token.pattern) {
  367. if (keys)
  368. keys.push(token);
  369. if (prefix || suffix) {
  370. if (token.modifier === "+" || token.modifier === "*") {
  371. var mod = token.modifier === "*" ? "?" : "";
  372. route += "(?:".concat(prefix, "((?:").concat(token.pattern, ")(?:").concat(suffix).concat(prefix, "(?:").concat(token.pattern, "))*)").concat(suffix, ")").concat(mod);
  373. }
  374. else {
  375. route += "(?:".concat(prefix, "(").concat(token.pattern, ")").concat(suffix, ")").concat(token.modifier);
  376. }
  377. }
  378. else {
  379. if (token.modifier === "+" || token.modifier === "*") {
  380. throw new TypeError("Can not repeat \"".concat(token.name, "\" without a prefix and suffix"));
  381. }
  382. route += "(".concat(token.pattern, ")").concat(token.modifier);
  383. }
  384. }
  385. else {
  386. route += "(?:".concat(prefix).concat(suffix, ")").concat(token.modifier);
  387. }
  388. }
  389. }
  390. if (end) {
  391. if (!strict)
  392. route += "".concat(delimiterRe, "?");
  393. route += !options.endsWith ? "$" : "(?=".concat(endsWithRe, ")");
  394. }
  395. else {
  396. var endToken = tokens[tokens.length - 1];
  397. var isEndDelimited = typeof endToken === "string"
  398. ? delimiterRe.indexOf(endToken[endToken.length - 1]) > -1
  399. : endToken === undefined;
  400. if (!strict) {
  401. route += "(?:".concat(delimiterRe, "(?=").concat(endsWithRe, "))?");
  402. }
  403. if (!isEndDelimited) {
  404. route += "(?=".concat(delimiterRe, "|").concat(endsWithRe, ")");
  405. }
  406. }
  407. return new RegExp(route, flags(options));
  408. }
  409. exports.tokensToRegexp = tokensToRegexp;
  410. /**
  411. * Normalize the given path string, returning a regular expression.
  412. *
  413. * An empty array can be passed in for the keys, which will hold the
  414. * placeholder key descriptions. For example, using `/user/:id`, `keys` will
  415. * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
  416. */
  417. function pathToRegexp(path, keys, options) {
  418. if (path instanceof RegExp)
  419. return regexpToRegexp(path, keys);
  420. if (Array.isArray(path))
  421. return arrayToRegexp(path, keys, options);
  422. return stringToRegexp(path, keys, options);
  423. }
  424. exports.pathToRegexp = pathToRegexp;
  425. //# sourceMappingURL=index.js.map