index.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. 'use strict';
  2. var define = require('define-property');
  3. var extend = require('extend-shallow');
  4. var not = require('regex-not');
  5. var MAX_LENGTH = 1024 * 64;
  6. /**
  7. * Session cache
  8. */
  9. var cache = {};
  10. /**
  11. * Create a regular expression from the given `pattern` string.
  12. *
  13. * @param {String|RegExp} `pattern` Pattern can be a string or regular expression.
  14. * @param {Object} `options`
  15. * @return {RegExp}
  16. * @api public
  17. */
  18. module.exports = function(patterns, options) {
  19. if (!Array.isArray(patterns)) {
  20. return makeRe(patterns, options);
  21. }
  22. return makeRe(patterns.join('|'), options);
  23. };
  24. /**
  25. * Create a regular expression from the given `pattern` string.
  26. *
  27. * @param {String|RegExp} `pattern` Pattern can be a string or regular expression.
  28. * @param {Object} `options`
  29. * @return {RegExp}
  30. * @api public
  31. */
  32. function makeRe(pattern, options) {
  33. if (pattern instanceof RegExp) {
  34. return pattern;
  35. }
  36. if (typeof pattern !== 'string') {
  37. throw new TypeError('expected a string');
  38. }
  39. if (pattern.length > MAX_LENGTH) {
  40. throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters');
  41. }
  42. var key = pattern;
  43. // do this before shallow cloning options, it's a lot faster
  44. if (!options || (options && options.cache !== false)) {
  45. key = createKey(pattern, options);
  46. if (cache.hasOwnProperty(key)) {
  47. return cache[key];
  48. }
  49. }
  50. var opts = extend({}, options);
  51. if (opts.contains === true) {
  52. if (opts.negate === true) {
  53. opts.strictNegate = false;
  54. } else {
  55. opts.strict = false;
  56. }
  57. }
  58. if (opts.strict === false) {
  59. opts.strictOpen = false;
  60. opts.strictClose = false;
  61. }
  62. var open = opts.strictOpen !== false ? '^' : '';
  63. var close = opts.strictClose !== false ? '$' : '';
  64. var flags = opts.flags || '';
  65. var regex;
  66. if (opts.nocase === true && !/i/.test(flags)) {
  67. flags += 'i';
  68. }
  69. try {
  70. if (opts.negate || typeof opts.strictNegate === 'boolean') {
  71. pattern = not.create(pattern, opts);
  72. }
  73. var str = open + '(?:' + pattern + ')' + close;
  74. regex = new RegExp(str, flags);
  75. } catch (err) {
  76. if (opts.strictErrors === true) {
  77. err.key = key;
  78. err.pattern = pattern;
  79. err.originalOptions = options;
  80. err.createdOptions = opts;
  81. throw err;
  82. }
  83. try {
  84. regex = new RegExp('^' + pattern.replace(/(\W)/g, '\\$1') + '$');
  85. } catch (err) {
  86. regex = /.^/; //<= match nothing
  87. }
  88. }
  89. if (opts.cache !== false) {
  90. cacheRegex(regex, key, pattern, opts);
  91. }
  92. return regex;
  93. }
  94. /**
  95. * Cache generated regex. This can result in dramatic speed improvements
  96. * and simplify debugging by adding options and pattern to the regex. It can be
  97. * disabled by passing setting `options.cache` to false.
  98. */
  99. function cacheRegex(regex, key, pattern, options) {
  100. define(regex, 'cached', true);
  101. define(regex, 'pattern', pattern);
  102. define(regex, 'options', options);
  103. define(regex, 'key', key);
  104. cache[key] = regex;
  105. }
  106. /**
  107. * Create the key to use for memoization. The key is generated
  108. * by iterating over the options and concatenating key-value pairs
  109. * to the pattern string.
  110. */
  111. function createKey(pattern, options) {
  112. if (!options) return pattern;
  113. var key = pattern;
  114. for (var prop in options) {
  115. if (options.hasOwnProperty(prop)) {
  116. key += ';' + prop + '=' + String(options[prop]);
  117. }
  118. }
  119. return key;
  120. }
  121. /**
  122. * Expose `makeRe`
  123. */
  124. module.exports.makeRe = makeRe;