|
|
'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;
|