| 'use strict'; |
| |
| var safe = require('safe-regex'); |
| var define = require('define-property'); |
| var extend = require('extend-shallow'); |
| var not = require('regex-not'); |
| var MAX_LENGTH = 1024 * 64; |
| |
| /** |
| * Session cache |
| */ |
| |
| var cache = {}; |
| |
| /** |
| * Create a regular expression from the given `pattern` string. |
| * |
| * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. |
| * @param {Object} `options` |
| * @return {RegExp} |
| * @api public |
| */ |
| |
| module.exports = function(patterns, options) { |
| if (!Array.isArray(patterns)) { |
| return makeRe(patterns, options); |
| } |
| return makeRe(patterns.join('|'), options); |
| }; |
| |
| /** |
| * Create a regular expression from the given `pattern` string. |
| * |
| * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. |
| * @param {Object} `options` |
| * @return {RegExp} |
| * @api public |
| */ |
| |
| function makeRe(pattern, options) { |
| if (pattern instanceof RegExp) { |
| return pattern; |
| } |
| |
| if (typeof pattern !== 'string') { |
| throw new TypeError('expected a string'); |
| } |
| |
| if (pattern.length > MAX_LENGTH) { |
| throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); |
| } |
| |
| var key = pattern; |
| // do this before shallow cloning options, it's a lot faster |
| if (!options || (options && options.cache !== false)) { |
| key = createKey(pattern, options); |
| |
| if (cache.hasOwnProperty(key)) { |
| return cache[key]; |
| } |
| } |
| |
| var opts = extend({}, options); |
| if (opts.contains === true) { |
| if (opts.negate === true) { |
| opts.strictNegate = false; |
| } else { |
| opts.strict = false; |
| } |
| } |
| |
| if (opts.strict === false) { |
| opts.strictOpen = false; |
| opts.strictClose = false; |
| } |
| |
| var open = opts.strictOpen !== false ? '^' : ''; |
| var close = opts.strictClose !== false ? '$' : ''; |
| var flags = opts.flags || ''; |
| var regex; |
| |
| if (opts.nocase === true && !/i/.test(flags)) { |
| flags += 'i'; |
| } |
| |
| try { |
| if (opts.negate || typeof opts.strictNegate === 'boolean') { |
| pattern = not.create(pattern, opts); |
| } |
| |
| var str = open + '(?:' + pattern + ')' + close; |
| regex = new RegExp(str, flags); |
| |
| if (opts.safe === true && safe(regex) === false) { |
| throw new Error('potentially unsafe regular expression: ' + regex.source); |
| } |
| |
| } catch (err) { |
| if (opts.strictErrors === true || opts.safe === true) { |
| err.key = key; |
| err.pattern = pattern; |
| err.originalOptions = options; |
| err.createdOptions = opts; |
| throw err; |
| } |
| |
| try { |
| regex = new RegExp('^' + pattern.replace(/(\W)/g, '\\$1') + '$'); |
| } catch (err) { |
| regex = /.^/; //<= match nothing |
| } |
| } |
| |
| if (opts.cache !== false) { |
| memoize(regex, key, pattern, opts); |
| } |
| return regex; |
| } |
| |
| /** |
| * Memoize generated regex. This can result in dramatic speed improvements |
| * and simplify debugging by adding options and pattern to the regex. It can be |
| * disabled by passing setting `options.cache` to false. |
| */ |
| |
| function memoize(regex, key, pattern, options) { |
| define(regex, 'cached', true); |
| define(regex, 'pattern', pattern); |
| define(regex, 'options', options); |
| define(regex, 'key', key); |
| cache[key] = regex; |
| } |
| |
| /** |
| * Create the key to use for memoization. The key is generated |
| * by iterating over the options and concatenating key-value pairs |
| * to the pattern string. |
| */ |
| |
| function createKey(pattern, options) { |
| if (!options) return pattern; |
| var key = pattern; |
| for (var prop in options) { |
| if (options.hasOwnProperty(prop)) { |
| key += ';' + prop + '=' + String(options[prop]); |
| } |
| } |
| return key; |
| } |
| |
| /** |
| * Expose `makeRe` |
| */ |
| |
| module.exports.makeRe = makeRe; |