index.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /**
  2. * Expose `pathToRegexp`.
  3. */
  4. module.exports = pathToRegexp;
  5. /**
  6. * Match matching groups in a regular expression.
  7. */
  8. var MATCHING_GROUP_REGEXP = /\\.|\((?:\?<(.*?)>)?(?!\?)/g;
  9. /**
  10. * Normalize the given path string,
  11. * returning a regular expression.
  12. *
  13. * An empty array should be passed,
  14. * which will contain the placeholder
  15. * key names. For example "/user/:id" will
  16. * then contain ["id"].
  17. *
  18. * @param {String|RegExp|Array} path
  19. * @param {Array} keys
  20. * @param {Object} options
  21. * @return {RegExp}
  22. * @api private
  23. */
  24. function pathToRegexp(path, keys, options) {
  25. options = options || {};
  26. keys = keys || [];
  27. var strict = options.strict;
  28. var end = options.end !== false;
  29. var flags = options.sensitive ? '' : 'i';
  30. var lookahead = options.lookahead !== false;
  31. var extraOffset = 0;
  32. var keysOffset = keys.length;
  33. var i = 0;
  34. var name = 0;
  35. var pos = 0;
  36. var backtrack = '';
  37. var m;
  38. if (path instanceof RegExp) {
  39. while (m = MATCHING_GROUP_REGEXP.exec(path.source)) {
  40. if (m[0][0] === '\\') continue;
  41. keys.push({
  42. name: m[1] || name++,
  43. optional: false,
  44. offset: m.index
  45. });
  46. }
  47. return path;
  48. }
  49. if (Array.isArray(path)) {
  50. // Map array parts into regexps and return their source. We also pass
  51. // the same keys and options instance into every generation to get
  52. // consistent matching groups before we join the sources together.
  53. path = path.map(function (value) {
  54. return pathToRegexp(value, keys, options).source;
  55. });
  56. return new RegExp(path.join('|'), flags);
  57. }
  58. path = path.replace(
  59. /\\.|(\/)?(\.)?:(\w+)(\(.*?\))?(\*)?(\?)?|[.*]|\/\(/g,
  60. function (match, slash, format, key, capture, star, optional, offset) {
  61. pos = offset + match.length;
  62. if (match[0] === '\\') {
  63. backtrack += match;
  64. return match;
  65. }
  66. if (match === '.') {
  67. backtrack += '\\.';
  68. extraOffset += 1;
  69. return '\\.';
  70. }
  71. backtrack = slash || format ? '' : path.slice(pos, offset);
  72. if (match === '*') {
  73. extraOffset += 3;
  74. return '(.*)';
  75. }
  76. if (match === '/(') {
  77. backtrack += '/';
  78. extraOffset += 2;
  79. return '/(?:';
  80. }
  81. slash = slash || '';
  82. format = format ? '\\.' : '';
  83. optional = optional || '';
  84. capture = capture ?
  85. capture.replace(/\\.|\*/, function (m) { return m === '*' ? '(.*)' : m; }) :
  86. (backtrack ? '((?:(?!/|' + backtrack + ').)+?)' : '([^/' + format + ']+?)');
  87. keys.push({
  88. name: key,
  89. optional: !!optional,
  90. offset: offset + extraOffset
  91. });
  92. var result = '(?:'
  93. + format + slash + capture
  94. + (star ? '((?:[/' + format + '].+?)?)' : '')
  95. + ')'
  96. + optional;
  97. extraOffset += result.length - match.length;
  98. return result;
  99. });
  100. // This is a workaround for handling unnamed matching groups.
  101. while (m = MATCHING_GROUP_REGEXP.exec(path)) {
  102. if (m[0][0] === '\\') continue;
  103. if (keysOffset + i === keys.length || keys[keysOffset + i].offset > m.index) {
  104. keys.splice(keysOffset + i, 0, {
  105. name: name++, // Unnamed matching groups must be consistently linear.
  106. optional: false,
  107. offset: m.index
  108. });
  109. }
  110. i++;
  111. }
  112. path += strict ? '' : path[path.length - 1] === '/' ? '?' : '/?';
  113. // If the path is non-ending, match until the end or a slash.
  114. if (end) {
  115. path += '$';
  116. } else if (path[path.length - 1] !== '/') {
  117. path += lookahead ? '(?=/|$)' : '(?:/|$)';
  118. }
  119. return new RegExp('^' + path, flags);
  120. };