diff options
Diffstat (limited to 'node_modules/micromatch/index.js')
-rw-r--r--[-rwxr-xr-x] | node_modules/micromatch/index.js | 1010 |
1 files changed, 728 insertions, 282 deletions
diff --git a/node_modules/micromatch/index.js b/node_modules/micromatch/index.js index f898ec176..fe02f2cb2 100755..100644 --- a/node_modules/micromatch/index.js +++ b/node_modules/micromatch/index.js @@ -1,431 +1,877 @@ -/*! - * micromatch <https://github.com/jonschlinkert/micromatch> - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. +'use strict'; + +/** + * Module dependencies */ -'use strict'; +var util = require('util'); +var braces = require('braces'); +var toRegex = require('to-regex'); +var extend = require('extend-shallow'); + +/** + * Local dependencies + */ -var expand = require('./lib/expand'); +var compilers = require('./lib/compilers'); +var parsers = require('./lib/parsers'); +var cache = require('./lib/cache'); var utils = require('./lib/utils'); +var MAX_LENGTH = 1024 * 64; /** - * The main function. Pass an array of filepaths, - * and a string or array of glob patterns + * The main function takes a list of strings and one or more + * glob patterns to use for matching. + * + * ```js + * var mm = require('micromatch'); + * mm(list, patterns[, options]); * - * @param {Array|String} `files` - * @param {Array|String} `patterns` - * @param {Object} `opts` - * @return {Array} Array of matches + * console.log(mm(['a.js', 'a.txt'], ['*.js'])); + * //=> [ 'a.js' ] + * ``` + * @param {Array} `list` A list of strings to match + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of matches + * @summary false + * @api public */ -function micromatch(files, patterns, opts) { - if (!files || !patterns) return []; - opts = opts || {}; +function micromatch(list, patterns, options) { + patterns = utils.arrayify(patterns); + list = utils.arrayify(list); - if (typeof opts.cache === 'undefined') { - opts.cache = true; + var len = patterns.length; + if (list.length === 0 || len === 0) { + return []; } - if (!Array.isArray(patterns)) { - return match(files, patterns, opts); + if (len === 1) { + return micromatch.match(list, patterns[0], options); } - var len = patterns.length, i = 0; - var omit = [], keep = []; + var omit = []; + var keep = []; + var idx = -1; + + while (++idx < len) { + var pattern = patterns[idx]; - while (len--) { - var glob = patterns[i++]; - if (typeof glob === 'string' && glob.charCodeAt(0) === 33 /* ! */) { - omit.push.apply(omit, match(files, glob.slice(1), opts)); + if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) { + omit.push.apply(omit, micromatch.match(list, pattern.slice(1), options)); } else { - keep.push.apply(keep, match(files, glob, opts)); + keep.push.apply(keep, micromatch.match(list, pattern, options)); } } - return utils.diff(keep, omit); + + var matches = utils.diff(keep, omit); + if (!options || options.nodupes !== false) { + return utils.unique(matches); + } + + return matches; } /** - * Return an array of files that match the given glob pattern. + * Similar to the main function, but `pattern` must be a string. * - * This function is called by the main `micromatch` function If you only - * need to pass a single pattern you might get very minor speed improvements - * using this function. + * ```js + * var mm = require('micromatch'); + * mm.match(list, pattern[, options]); * - * @param {Array} `files` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Array} + * console.log(mm.match(['a.a', 'a.aa', 'a.b', 'a.c'], '*.a')); + * //=> ['a.a', 'a.aa'] + * ``` + * @param {Array} `list` Array of strings to match + * @param {String} `pattern` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of matches + * @api public */ -function match(files, pattern, opts) { - if (utils.typeOf(files) !== 'string' && !Array.isArray(files)) { - throw new Error(msg('match', 'files', 'a string or array')); +micromatch.match = function(list, pattern, options) { + if (Array.isArray(pattern)) { + throw new TypeError('expected pattern to be a string'); } - files = utils.arrayify(files); - opts = opts || {}; + var unixify = utils.unixify(options); + var isMatch = memoize('match', pattern, options, micromatch.matcher); + var matches = []; - var negate = opts.negate || false; - var orig = pattern; + list = utils.arrayify(list); + var len = list.length; + var idx = -1; - if (typeof pattern === 'string') { - negate = pattern.charAt(0) === '!'; - if (negate) { - pattern = pattern.slice(1); + while (++idx < len) { + var ele = list[idx]; + if (ele === pattern || isMatch(ele)) { + matches.push(utils.value(ele, unixify, options)); } + } - // we need to remove the character regardless, - // so the above logic is still needed - if (opts.nonegate === true) { - negate = false; + // if no options were passed, uniquify results and return + if (typeof options === 'undefined') { + return utils.unique(matches); + } + + if (matches.length === 0) { + if (options.failglob === true) { + throw new Error('no matches found for "' + pattern + '"'); + } + if (options.nonull === true || options.nullglob === true) { + return [options.unescape ? utils.unescape(pattern) : pattern]; } } - var _isMatch = matcher(pattern, opts); - var len = files.length, i = 0; - var res = []; + // if `opts.ignore` was defined, diff ignored list + if (options.ignore) { + matches = micromatch.not(matches, options.ignore, options); + } - while (i < len) { - var file = files[i++]; - var fp = utils.unixify(file, opts); + return options.nodupes !== false ? utils.unique(matches) : matches; +}; - if (!_isMatch(fp)) { continue; } - res.push(fp); +/** + * Returns true if the specified `string` matches the given glob `pattern`. + * + * ```js + * var mm = require('micromatch'); + * mm.isMatch(string, pattern[, options]); + * + * console.log(mm.isMatch('a.a', '*.a')); + * //=> true + * console.log(mm.isMatch('a.b', '*.a')); + * //=> false + * ``` + * @param {String} `string` String to match + * @param {String} `pattern` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if the string matches the glob pattern. + * @api public + */ + +micromatch.isMatch = function(str, pattern, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); } - if (res.length === 0) { - if (opts.failglob === true) { - throw new Error('micromatch.match() found no matches for: "' + orig + '".'); - } + if (isEmptyString(str) || isEmptyString(pattern)) { + return false; + } - if (opts.nonull || opts.nullglob) { - res.push(utils.unescapeGlob(orig)); - } + var equals = utils.equalsPattern(options); + if (equals(str)) { + return true; } - // if `negate` was defined, diff negated files - if (negate) { res = utils.diff(files, res); } + var isMatch = memoize('isMatch', pattern, options, micromatch.matcher); + return isMatch(str); +}; - // if `ignore` was defined, diff ignored filed - if (opts.ignore && opts.ignore.length) { - pattern = opts.ignore; - opts = utils.omit(opts, ['ignore']); - res = utils.diff(res, micromatch(res, pattern, opts)); - } +/** + * Returns true if some of the strings in the given `list` match any of the + * given glob `patterns`. + * + * ```js + * var mm = require('micromatch'); + * mm.some(list, patterns[, options]); + * + * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // true + * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ - if (opts.nodupes) { - return utils.unique(res); +micromatch.some = function(list, patterns, options) { + if (typeof list === 'string') { + list = [list]; } - return res; -} + for (var i = 0; i < list.length; i++) { + if (micromatch(list[i], patterns, options).length === 1) { + return true; + } + } + return false; +}; /** - * Returns a function that takes a glob pattern or array of glob patterns - * to be used with `Array#filter()`. (Internally this function generates - * the matching function using the [matcher] method). + * Returns true if every string in the given `list` matches + * any of the given glob `patterns`. * * ```js - * var fn = mm.filter('[a-c]'); - * ['a', 'b', 'c', 'd', 'e'].filter(fn); - * //=> ['a', 'b', 'c'] + * var mm = require('micromatch'); + * mm.every(list, patterns[, options]); + * + * console.log(mm.every('foo.js', ['foo.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // false + * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); + * // false * ``` - * @param {String|Array} `patterns` Can be a glob or array of globs. - * @param {Options} `opts` Options to pass to the [matcher] method. - * @return {Function} Filter function to be passed to `Array#filter()`. + * @param {String|Array} `list` The string or array of strings to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public */ -function filter(patterns, opts) { - if (!Array.isArray(patterns) && typeof patterns !== 'string') { - throw new TypeError(msg('filter', 'patterns', 'a string or array')); +micromatch.every = function(list, patterns, options) { + if (typeof list === 'string') { + list = [list]; } - - patterns = utils.arrayify(patterns); - var len = patterns.length, i = 0; - var patternMatchers = Array(len); - while (i < len) { - patternMatchers[i] = matcher(patterns[i++], opts); - } - - return function(fp) { - if (fp == null) return []; - var len = patternMatchers.length, i = 0; - var res = true; - - fp = utils.unixify(fp, opts); - while (i < len) { - var fn = patternMatchers[i++]; - if (!fn(fp)) { - res = false; - break; - } + for (var i = 0; i < list.length; i++) { + if (micromatch(list[i], patterns, options).length !== 1) { + return false; } - return res; - }; -} + } + return true; +}; /** - * Returns true if the filepath contains the given - * pattern. Can also return a function for matching. + * Returns true if **any** of the given glob `patterns` + * match the specified `string`. * * ```js - * isMatch('foo.md', '*.md', {}); - * //=> true + * var mm = require('micromatch'); + * mm.any(string, patterns[, options]); * - * isMatch('*.md', {})('foo.md') + * console.log(mm.any('a.a', ['b.*', '*.a'])); * //=> true + * console.log(mm.any('a.a', 'b.*')); + * //=> false * ``` - * @param {String} `fp` - * @param {String} `pattern` - * @param {Object} `opts` - * @return {Boolean} + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public */ -function isMatch(fp, pattern, opts) { - if (typeof fp !== 'string') { - throw new TypeError(msg('isMatch', 'filepath', 'a string')); +micromatch.any = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); } - fp = utils.unixify(fp, opts); - if (utils.typeOf(pattern) === 'object') { - return matcher(fp, pattern); + if (isEmptyString(str) || isEmptyString(patterns)) { + return false; + } + + if (typeof patterns === 'string') { + patterns = [patterns]; } - return matcher(pattern, opts)(fp); -} + + for (var i = 0; i < patterns.length; i++) { + if (micromatch.isMatch(str, patterns[i], options)) { + return true; + } + } + return false; +}; /** - * Returns true if the filepath matches the - * given pattern. + * Returns true if **all** of the given `patterns` match + * the specified string. + * + * ```js + * var mm = require('micromatch'); + * mm.all(string, patterns[, options]); + * + * console.log(mm.all('foo.js', ['foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); + * // false + * + * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); + * // true + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public */ -function contains(fp, pattern, opts) { - if (typeof fp !== 'string') { - throw new TypeError(msg('contains', 'pattern', 'a string')); +micromatch.all = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } + if (typeof patterns === 'string') { + patterns = [patterns]; } + for (var i = 0; i < patterns.length; i++) { + if (!micromatch.isMatch(str, patterns[i], options)) { + return false; + } + } + return true; +}; - opts = opts || {}; - opts.contains = (pattern !== ''); - fp = utils.unixify(fp, opts); +/** + * Returns a list of strings that _**do not match any**_ of the given `patterns`. + * + * ```js + * var mm = require('micromatch'); + * mm.not(list, patterns[, options]); + * + * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); + * //=> ['b.b', 'c.c'] + * ``` + * @param {Array} `list` Array of strings to match. + * @param {String|Array} `patterns` One or more glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of strings that **do not match** the given patterns. + * @api public + */ + +micromatch.not = function(list, patterns, options) { + var opts = extend({}, options); + var ignore = opts.ignore; + delete opts.ignore; - if (opts.contains && !utils.isGlob(pattern)) { - return fp.indexOf(pattern) !== -1; + var unixify = utils.unixify(opts); + list = utils.arrayify(list).map(unixify); + + var matches = utils.diff(list, micromatch(list, patterns, opts)); + if (ignore) { + matches = utils.diff(matches, micromatch(list, ignore)); } - return matcher(pattern, opts)(fp); -} + + return opts.nodupes !== false ? utils.unique(matches) : matches; +}; /** - * Returns true if a file path matches any of the - * given patterns. + * Returns true if the given `string` contains the given pattern. Similar + * to [.isMatch](#isMatch) but the pattern can match any part of the string. + * + * ```js + * var mm = require('micromatch'); + * mm.contains(string, pattern[, options]); * - * @param {String} `fp` The filepath to test. - * @param {String|Array} `patterns` Glob patterns to use. - * @param {Object} `opts` Options to pass to the `matcher()` function. - * @return {String} + * console.log(mm.contains('aa/bb/cc', '*b')); + * //=> true + * console.log(mm.contains('aa/bb/cc', '*d')); + * //=> false + * ``` + * @param {String} `str` The string to match. + * @param {String|Array} `patterns` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if the patter matches any part of `str`. + * @api public */ -function any(fp, patterns, opts) { - if (!Array.isArray(patterns) && typeof patterns !== 'string') { - throw new TypeError(msg('any', 'patterns', 'a string or array')); +micromatch.contains = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); } - patterns = utils.arrayify(patterns); - var len = patterns.length; + if (typeof patterns === 'string') { + if (isEmptyString(str) || isEmptyString(patterns)) { + return false; + } - fp = utils.unixify(fp, opts); - while (len--) { - var isMatch = matcher(patterns[len], opts); - if (isMatch(fp)) { + var equals = utils.equalsPattern(patterns, options); + if (equals(str)) { + return true; + } + var contains = utils.containsPattern(patterns, options); + if (contains(str)) { return true; } } - return false; -} + + var opts = extend({}, options, {contains: true}); + return micromatch.any(str, patterns, opts); +}; /** - * Filter the keys of an object with the given `glob` pattern - * and `options` - * - * @param {Object} `object` - * @param {Pattern} `object` - * @return {Array} + * Returns true if the given pattern and options should enable + * the `matchBase` option. + * @return {Boolean} + * @api private */ -function matchKeys(obj, glob, options) { - if (utils.typeOf(obj) !== 'object') { - throw new TypeError(msg('matchKeys', 'first argument', 'an object')); - } +micromatch.matchBase = function(pattern, options) { + if (pattern && pattern.indexOf('/') !== -1 || !options) return false; + return options.basename === true || options.matchBase === true; +}; - var fn = matcher(glob, options); - var res = {}; +/** + * Filter the keys of the given object with the given `glob` pattern + * and `options`. Does not attempt to match nested keys. If you need this feature, + * use [glob-object][] instead. + * + * ```js + * var mm = require('micromatch'); + * mm.matchKeys(object, patterns[, options]); + * + * var obj = { aa: 'a', ab: 'b', ac: 'c' }; + * console.log(mm.matchKeys(obj, '*b')); + * //=> { ab: 'b' } + * ``` + * @param {Object} `object` The object with keys to filter. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Object} Returns an object with only keys that match the given patterns. + * @api public + */ - for (var key in obj) { - if (obj.hasOwnProperty(key) && fn(key)) { - res[key] = obj[key]; - } +micromatch.matchKeys = function(obj, patterns, options) { + if (!utils.isObject(obj)) { + throw new TypeError('expected the first argument to be an object'); } - return res; -} + var keys = micromatch(Object.keys(obj), patterns, options); + return utils.pick(obj, keys); +}; /** - * Return a function for matching based on the - * given `pattern` and `options`. + * Returns a memoized matcher function from the given glob `pattern` and `options`. + * The returned function takes a string to match as its only argument and returns + * true if the string is a match. * - * @param {String} `pattern` - * @param {Object} `options` - * @return {Function} + * ```js + * var mm = require('micromatch'); + * mm.matcher(pattern[, options]); + * + * var isMatch = mm.matcher('*.!(*a)'); + * console.log(isMatch('a.a')); + * //=> false + * console.log(isMatch('a.b')); + * //=> true + * ``` + * @param {String} `pattern` Glob pattern + * @param {Object} `options` See available [options](#options) for changing how matches are performed. + * @return {Function} Returns a matcher function. + * @api public */ -function matcher(pattern, opts) { - // pattern is a function - if (typeof pattern === 'function') { - return pattern; +micromatch.matcher = function matcher(pattern, options) { + if (Array.isArray(pattern)) { + return compose(pattern, options, matcher); } - // pattern is a regex + + // if pattern is a regex if (pattern instanceof RegExp) { - return function(fp) { - return pattern.test(fp); - }; + return test(pattern); } - if (typeof pattern !== 'string') { - throw new TypeError(msg('matcher', 'pattern', 'a string, regex, or function')); + // if pattern is invalid + if (!utils.isString(pattern)) { + throw new TypeError('expected pattern to be an array, string or regex'); } - // strings, all the way down... - pattern = utils.unixify(pattern, opts); + // if pattern is a non-glob string + if (!utils.hasSpecialChars(pattern)) { + if (options && options.nocase === true) { + pattern = pattern.toLowerCase(); + } + return utils.matchPath(pattern, options); + } + + // if pattern is a glob string + var re = micromatch.makeRe(pattern, options); - // pattern is a non-glob string - if (!utils.isGlob(pattern)) { - return utils.matchPath(pattern, opts); + // if `options.matchBase` or `options.basename` is defined + if (micromatch.matchBase(pattern, options)) { + return utils.matchBasename(re, options); } - // pattern is a glob string - var re = makeRe(pattern, opts); - // `matchBase` is defined - if (opts && opts.matchBase) { - return utils.hasFilename(re, opts); + function test(regex) { + var equals = utils.equalsPattern(options); + var unixify = utils.unixify(options); + + return function(str) { + if (equals(str)) { + return true; + } + + if (regex.test(unixify(str))) { + return true; + } + return false; + }; } - // `matchBase` is not defined - return function(fp) { - fp = utils.unixify(fp, opts); - return re.test(fp); - }; -} + + var fn = test(re); + Object.defineProperty(fn, 'result', { + configurable: true, + enumerable: false, + value: re.result + }); + return fn; +}; /** - * Create and cache a regular expression for matching - * file paths. + * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. * - * If the leading character in the `glob` is `!`, a negation - * regex is returned. + * ```js + * var mm = require('micromatch'); + * mm.capture(pattern, string[, options]); * - * @param {String} `glob` - * @param {Object} `options` - * @return {RegExp} + * console.log(mm.capture('test/*.js', 'test/foo.js')); + * //=> ['foo'] + * console.log(mm.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `pattern` Glob pattern to use for matching. + * @param {String} `string` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. + * @api public */ -function toRegex(glob, options) { - // clone options to prevent mutating the original object - var opts = Object.create(options || {}); - var flags = opts.flags || ''; - if (opts.nocase && flags.indexOf('i') === -1) { - flags += 'i'; +micromatch.capture = function(pattern, str, options) { + var re = micromatch.makeRe(pattern, extend({capture: true}, options)); + var unixify = utils.unixify(options); + + function match() { + return function(string) { + var match = re.exec(unixify(string)); + if (!match) { + return null; + } + + return match.slice(1); + }; } - var parsed = expand(glob, opts); + var capture = memoize('capture', pattern, options, match); + return capture(str); +}; - // pass in tokens to avoid parsing more than once - opts.negated = opts.negated || parsed.negated; - opts.negate = opts.negated; - glob = wrapGlob(parsed.pattern, opts); - var re; +/** + * Create a regular expression from the given glob `pattern`. + * + * ```js + * var mm = require('micromatch'); + * mm.makeRe(pattern[, options]); + * + * console.log(mm.makeRe('*.js')); + * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ + * ``` + * @param {String} `pattern` A glob pattern to convert to regex. + * @param {Object} `options` See available [options](#options) for changing how matches are performed. + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ - try { - re = new RegExp(glob, flags); - return re; - } catch (err) { - err.reason = 'micromatch invalid regex: (' + re + ')'; - if (opts.strict) throw new SyntaxError(err); +micromatch.makeRe = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected pattern to be a string'); } - // we're only here if a bad pattern was used and the user - // passed `options.silent`, so match nothing - return /$^/; -} + if (pattern.length > MAX_LENGTH) { + throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); + } + + function makeRe() { + var result = micromatch.create(pattern, options); + var ast_array = []; + var output = result.map(function(obj) { + obj.ast.state = obj.state; + ast_array.push(obj.ast); + return obj.output; + }); + + var regex = toRegex(output.join('|'), options); + Object.defineProperty(regex, 'result', { + configurable: true, + enumerable: false, + value: ast_array + }); + return regex; + } + + return memoize('makeRe', pattern, options, makeRe); +}; /** - * Create the regex to do the matching. If the leading - * character in the `glob` is `!` a negation regex is returned. + * Expand the given brace `pattern`. + * + * ```js + * var mm = require('micromatch'); + * console.log(mm.braces('foo/{a,b}/bar')); + * //=> ['foo/(a|b)/bar'] * - * @param {String} `glob` - * @param {Boolean} `negate` + * console.log(mm.braces('foo/{a,b}/bar', {expand: true})); + * //=> ['foo/(a|b)/bar'] + * ``` + * @param {String} `pattern` String with brace pattern to expand. + * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. + * @return {Array} + * @api public */ -function wrapGlob(glob, opts) { - var prefix = (opts && !opts.contains) ? '^' : ''; - var after = (opts && !opts.contains) ? '$' : ''; - glob = ('(?:' + glob + ')' + after); - if (opts && opts.negate) { - return prefix + ('(?!^' + glob + ').*$'); +micromatch.braces = function(pattern, options) { + if (typeof pattern !== 'string' && !Array.isArray(pattern)) { + throw new TypeError('expected pattern to be an array or string'); } - return prefix + glob; -} + + function expand() { + if (options && options.nobrace === true || !/\{.*\}/.test(pattern)) { + return utils.arrayify(pattern); + } + return braces(pattern, options); + } + + return memoize('braces', pattern, options, expand); +}; /** - * Create and cache a regular expression for matching file paths. - * If the leading character in the `glob` is `!`, a negation - * regex is returned. + * Proxy to the [micromatch.braces](#method), for parity with + * minimatch. + */ + +micromatch.braceExpand = function(pattern, options) { + var opts = extend({}, options, {expand: true}); + return micromatch.braces(pattern, opts); +}; + +/** + * Parses the given glob `pattern` and returns an array of abstract syntax + * trees (ASTs), with the compiled `output` and optional source `map` on + * each AST. + * + * ```js + * var mm = require('micromatch'); + * mm.create(pattern[, options]); * - * @param {String} `glob` - * @param {Object} `options` - * @return {RegExp} + * console.log(mm.create('abc/*.js')); + * // [{ options: { source: 'string', sourcemap: true }, + * // state: {}, + * // compilers: + * // { ... }, + * // output: '(\\.[\\\\\\/])?abc\\/(?!\\.)(?=.)[^\\/]*?\\.js', + * // ast: + * // { type: 'root', + * // errors: [], + * // nodes: + * // [ ... ], + * // dot: false, + * // input: 'abc/*.js' }, + * // parsingErrors: [], + * // map: + * // { version: 3, + * // sources: [ 'string' ], + * // names: [], + * // mappings: 'AAAA,GAAG,EAAC,kBAAC,EAAC,EAAE', + * // sourcesContent: [ 'abc/*.js' ] }, + * // position: { line: 1, column: 28 }, + * // content: {}, + * // files: {}, + * // idx: 6 }] + * ``` + * @param {String} `pattern` Glob pattern to parse and compile. + * @param {Object} `options` Any [options](#options) to change how parsing and compiling is performed. + * @return {Object} Returns an object with the parsed AST, compiled string and optional source map. + * @api public + */ + +micromatch.create = function(pattern, options) { + return memoize('create', pattern, options, function() { + function create(str, opts) { + return micromatch.compile(micromatch.parse(str, opts), opts); + } + + pattern = micromatch.braces(pattern, options); + var len = pattern.length; + var idx = -1; + var res = []; + + while (++idx < len) { + res.push(create(pattern[idx], options)); + } + return res; + }); +}; + +/** + * Parse the given `str` with the given `options`. + * + * ```js + * var mm = require('micromatch'); + * mm.parse(pattern[, options]); + * + * var ast = mm.parse('a/{b,c}/d'); + * console.log(ast); + * // { type: 'root', + * // errors: [], + * // input: 'a/{b,c}/d', + * // nodes: + * // [ { type: 'bos', val: '' }, + * // { type: 'text', val: 'a/' }, + * // { type: 'brace', + * // nodes: + * // [ { type: 'brace.open', val: '{' }, + * // { type: 'text', val: 'b,c' }, + * // { type: 'brace.close', val: '}' } ] }, + * // { type: 'text', val: '/d' }, + * // { type: 'eos', val: '' } ] } + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {Object} Returns an AST + * @api public */ -function makeRe(glob, opts) { - if (utils.typeOf(glob) !== 'string') { - throw new Error(msg('makeRe', 'glob', 'a string')); +micromatch.parse = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); } - return utils.cache(toRegex, glob, opts); -} + + function parse() { + var snapdragon = utils.instantiate(null, options); + parsers(snapdragon, options); + + var ast = snapdragon.parse(pattern, options); + utils.define(ast, 'snapdragon', snapdragon); + ast.input = pattern; + return ast; + } + + return memoize('parse', pattern, options, parse); +}; /** - * Make error messages consistent. Follows this format: + * Compile the given `ast` or string with the given `options`. * * ```js - * msg(methodName, argNumber, nativeType); - * // example: - * msg('matchKeys', 'first', 'an object'); + * var mm = require('micromatch'); + * mm.compile(ast[, options]); + * + * var ast = mm.parse('a/{b,c}/d'); + * console.log(mm.compile(ast)); + * // { options: { source: 'string' }, + * // state: {}, + * // compilers: + * // { eos: [Function], + * // noop: [Function], + * // bos: [Function], + * // brace: [Function], + * // 'brace.open': [Function], + * // text: [Function], + * // 'brace.close': [Function] }, + * // output: [ 'a/(b|c)/d' ], + * // ast: + * // { ... }, + * // parsingErrors: [] } * ``` + * @param {Object|String} `ast` + * @param {Object} `options` + * @return {Object} Returns an object that has an `output` property with the compiled string. + * @api public + */ + +micromatch.compile = function(ast, options) { + if (typeof ast === 'string') { + ast = micromatch.parse(ast, options); + } + + return memoize('compile', ast.input, options, function() { + var snapdragon = utils.instantiate(ast, options); + compilers(snapdragon, options); + return snapdragon.compile(ast, options); + }); +}; + +/** + * Clear the regex cache. * - * @param {String} `method` - * @param {String} `num` - * @param {String} `type` - * @return {String} + * ```js + * mm.clearCache(); + * ``` + * @api public + */ + +micromatch.clearCache = function() { + micromatch.cache.caches = {}; +}; + +/** + * Returns true if the given value is effectively an empty string + */ + +function isEmptyString(val) { + return String(val) === '' || String(val) === './'; +} + +/** + * Compose a matcher function with the given patterns. + * This allows matcher functions to be compiled once and + * called multiple times. + */ + +function compose(patterns, options, matcher) { + var matchers; + + return memoize('compose', String(patterns), options, function() { + return function(file) { + // delay composition until it's invoked the first time, + // after that it won't be called again + if (!matchers) { + matchers = []; + for (var i = 0; i < patterns.length; i++) { + matchers.push(matcher(patterns[i], options)); + } + } + + var len = matchers.length; + while (len--) { + if (matchers[len](file) === true) { + return true; + } + } + return false; + }; + }); +} + +/** + * Memoize a generated regex or function. A unique key is generated + * from the `type` (usually method name), the `pattern`, and + * user-defined options. */ -function msg(method, what, type) { - return 'micromatch.' + method + '(): ' + what + ' should be ' + type + '.'; +function memoize(type, pattern, options, fn) { + var key = utils.createKey(type + '=' + pattern, options); + + if (options && options.cache === false) { + return fn(pattern, options); + } + + if (cache.has(type, key)) { + return cache.get(type, key); + } + + var val = fn(pattern, options); + cache.set(type, key, val); + return val; } /** - * Public methods + * Expose compiler, parser and cache on `micromatch` */ -/* eslint no-multi-spaces: 0 */ -micromatch.any = any; -micromatch.braces = micromatch.braceExpand = utils.braces; -micromatch.contains = contains; -micromatch.expand = expand; -micromatch.filter = filter; -micromatch.isMatch = isMatch; -micromatch.makeRe = makeRe; -micromatch.match = match; -micromatch.matcher = matcher; -micromatch.matchKeys = matchKeys; +micromatch.compilers = compilers; +micromatch.parsers = parsers; +micromatch.caches = cache.caches; /** * Expose `micromatch` + * @type {Function} */ module.exports = micromatch; |