| /*! |
| * micromatch <https://github.com/jonschlinkert/micromatch> |
| * |
| * Copyright (c) 2014-2015, Jon Schlinkert. |
| * Licensed under the MIT License. |
| */ |
| |
| 'use strict'; |
| |
| var utils = require('./utils'); |
| var Glob = require('./glob'); |
| |
| /** |
| * Expose `expand` |
| */ |
| |
| module.exports = expand; |
| |
| /** |
| * Expand a glob pattern to resolve braces and |
| * similar patterns before converting to regex. |
| * |
| * @param {String|Array} `pattern` |
| * @param {Array} `files` |
| * @param {Options} `opts` |
| * @return {Array} |
| */ |
| |
| function expand(pattern, options) { |
| if (typeof pattern !== 'string') { |
| throw new TypeError('micromatch.expand(): argument should be a string.'); |
| } |
| |
| var glob = new Glob(pattern, options || {}); |
| var opts = glob.options; |
| |
| if (!utils.isGlob(pattern)) { |
| glob.pattern = glob.pattern.replace(/([\/.])/g, '\\$1'); |
| return glob; |
| } |
| |
| glob.pattern = glob.pattern.replace(/(\+)(?!\()/g, '\\$1'); |
| glob.pattern = glob.pattern.split('$').join('\\$'); |
| |
| if (typeof opts.braces !== 'boolean' && typeof opts.nobraces !== 'boolean') { |
| opts.braces = true; |
| } |
| |
| if (glob.pattern === '.*') { |
| return { |
| pattern: '\\.' + star, |
| tokens: tok, |
| options: opts |
| }; |
| } |
| |
| if (glob.pattern === '*') { |
| return { |
| pattern: oneStar(opts.dot), |
| tokens: tok, |
| options: opts |
| }; |
| } |
| |
| // parse the glob pattern into tokens |
| glob.parse(); |
| var tok = glob.tokens; |
| tok.is.negated = opts.negated; |
| |
| // dotfile handling |
| if ((opts.dotfiles === true || tok.is.dotfile) && opts.dot !== false) { |
| opts.dotfiles = true; |
| opts.dot = true; |
| } |
| |
| if ((opts.dotdirs === true || tok.is.dotdir) && opts.dot !== false) { |
| opts.dotdirs = true; |
| opts.dot = true; |
| } |
| |
| // check for braces with a dotfile pattern |
| if (/[{,]\./.test(glob.pattern)) { |
| opts.makeRe = false; |
| opts.dot = true; |
| } |
| |
| if (opts.nonegate !== true) { |
| opts.negated = glob.negated; |
| } |
| |
| // if the leading character is a dot or a slash, escape it |
| if (glob.pattern.charAt(0) === '.' && glob.pattern.charAt(1) !== '/') { |
| glob.pattern = '\\' + glob.pattern; |
| } |
| |
| /** |
| * Extended globs |
| */ |
| |
| // expand braces, e.g `{1..5}` |
| glob.track('before braces'); |
| if (tok.is.braces) { |
| glob.braces(); |
| } |
| glob.track('after braces'); |
| |
| // expand extglobs, e.g `foo/!(a|b)` |
| glob.track('before extglob'); |
| if (tok.is.extglob) { |
| glob.extglob(); |
| } |
| glob.track('after extglob'); |
| |
| // expand brackets, e.g `[[:alpha:]]` |
| glob.track('before brackets'); |
| if (tok.is.brackets) { |
| glob.brackets(); |
| } |
| glob.track('after brackets'); |
| |
| // special patterns |
| glob._replace('[!', '[^'); |
| glob._replace('(?', '(%~'); |
| glob._replace(/\[\]/, '\\[\\]'); |
| glob._replace('/[', '/' + (opts.dot ? dotfiles : nodot) + '[', true); |
| glob._replace('/?', '/' + (opts.dot ? dotfiles : nodot) + '[^/]', true); |
| glob._replace('/.', '/(?=.)\\.', true); |
| |
| // windows drives |
| glob._replace(/^(\w):([\\\/]+?)/gi, '(?=.)$1:$2', true); |
| |
| // negate slashes in exclusion ranges |
| if (glob.pattern.indexOf('[^') !== -1) { |
| glob.pattern = negateSlash(glob.pattern); |
| } |
| |
| if (opts.globstar !== false && glob.pattern === '**') { |
| glob.pattern = globstar(opts.dot); |
| |
| } else { |
| glob.pattern = balance(glob.pattern, '[', ']'); |
| glob.escape(glob.pattern); |
| |
| // if the pattern has `**` |
| if (tok.is.globstar) { |
| glob.pattern = collapse(glob.pattern, '/**'); |
| glob.pattern = collapse(glob.pattern, '**/'); |
| glob._replace('/**/', '(?:/' + globstar(opts.dot) + '/|/)', true); |
| glob._replace(/\*{2,}/g, '**'); |
| |
| // 'foo/*' |
| glob._replace(/(\w+)\*(?!\/)/g, '$1[^/]*?', true); |
| glob._replace(/\*\*\/\*(\w)/g, globstar(opts.dot) + '\\/' + (opts.dot ? dotfiles : nodot) + '[^/]*?$1', true); |
| |
| if (opts.dot !== true) { |
| glob._replace(/\*\*\/(.)/g, '(?:**\\/|)$1'); |
| } |
| |
| // 'foo/**' or '{**,*}', but not 'foo**' |
| if (tok.path.dirname !== '' || /,\*\*|\*\*,/.test(glob.orig)) { |
| glob._replace('**', globstar(opts.dot), true); |
| } |
| } |
| |
| // ends with /* |
| glob._replace(/\/\*$/, '\\/' + oneStar(opts.dot), true); |
| // ends with *, no slashes |
| glob._replace(/(?!\/)\*$/, star, true); |
| // has 'n*.' (partial wildcard w/ file extension) |
| glob._replace(/([^\/]+)\*/, '$1' + oneStar(true), true); |
| // has '*' |
| glob._replace('*', oneStar(opts.dot), true); |
| glob._replace('?.', '?\\.', true); |
| glob._replace('?:', '?:', true); |
| |
| glob._replace(/\?+/g, function(match) { |
| var len = match.length; |
| if (len === 1) { |
| return qmark; |
| } |
| return qmark + '{' + len + '}'; |
| }); |
| |
| // escape '.abc' => '\\.abc' |
| glob._replace(/\.([*\w]+)/g, '\\.$1'); |
| // fix '[^\\\\/]' |
| glob._replace(/\[\^[\\\/]+\]/g, qmark); |
| // '///' => '\/' |
| glob._replace(/\/+/g, '\\/'); |
| // '\\\\\\' => '\\' |
| glob._replace(/\\{2,}/g, '\\'); |
| } |
| |
| // unescape previously escaped patterns |
| glob.unescape(glob.pattern); |
| glob._replace('__UNESC_STAR__', '*'); |
| |
| // escape dots that follow qmarks |
| glob._replace('?.', '?\\.'); |
| |
| // remove unnecessary slashes in character classes |
| glob._replace('[^\\/]', qmark); |
| |
| if (glob.pattern.length > 1) { |
| if (/^[\[?*]/.test(glob.pattern)) { |
| // only prepend the string if we don't want to match dotfiles |
| glob.pattern = (opts.dot ? dotfiles : nodot) + glob.pattern; |
| } |
| } |
| |
| return glob; |
| } |
| |
| /** |
| * Collapse repeated character sequences. |
| * |
| * ```js |
| * collapse('a/../../../b', '../'); |
| * //=> 'a/../b' |
| * ``` |
| * |
| * @param {String} `str` |
| * @param {String} `ch` Character sequence to collapse |
| * @return {String} |
| */ |
| |
| function collapse(str, ch) { |
| var res = str.split(ch); |
| var isFirst = res[0] === ''; |
| var isLast = res[res.length - 1] === ''; |
| res = res.filter(Boolean); |
| if (isFirst) res.unshift(''); |
| if (isLast) res.push(''); |
| return res.join(ch); |
| } |
| |
| /** |
| * Negate slashes in exclusion ranges, per glob spec: |
| * |
| * ```js |
| * negateSlash('[^foo]'); |
| * //=> '[^\\/foo]' |
| * ``` |
| * |
| * @param {String} `str` glob pattern |
| * @return {String} |
| */ |
| |
| function negateSlash(str) { |
| return str.replace(/\[\^([^\]]*?)\]/g, function(match, inner) { |
| if (inner.indexOf('/') === -1) { |
| inner = '\\/' + inner; |
| } |
| return '[^' + inner + ']'; |
| }); |
| } |
| |
| /** |
| * Escape imbalanced braces/bracket. This is a very |
| * basic, naive implementation that only does enough |
| * to serve the purpose. |
| */ |
| |
| function balance(str, a, b) { |
| var aarr = str.split(a); |
| var alen = aarr.join('').length; |
| var blen = str.split(b).join('').length; |
| |
| if (alen !== blen) { |
| str = aarr.join('\\' + a); |
| return str.split(b).join('\\' + b); |
| } |
| return str; |
| } |
| |
| /** |
| * Special patterns to be converted to regex. |
| * Heuristics are used to simplify patterns |
| * and speed up processing. |
| */ |
| |
| /* eslint no-multi-spaces: 0 */ |
| var qmark = '[^/]'; |
| var star = qmark + '*?'; |
| var nodot = '(?!\\.)(?=.)'; |
| var dotfileGlob = '(?:\\/|^)\\.{1,2}($|\\/)'; |
| var dotfiles = '(?!' + dotfileGlob + ')(?=.)'; |
| var twoStarDot = '(?:(?!' + dotfileGlob + ').)*?'; |
| |
| /** |
| * Create a regex for `*`. |
| * |
| * If `dot` is true, or the pattern does not begin with |
| * a leading star, then return the simpler regex. |
| */ |
| |
| function oneStar(dotfile) { |
| return dotfile ? '(?!' + dotfileGlob + ')(?=.)' + star : (nodot + star); |
| } |
| |
| function globstar(dotfile) { |
| if (dotfile) { return twoStarDot; } |
| return '(?:(?!(?:\\/|^)\\.).)*?'; |
| } |