2 * micromatch <https://github.com/jonschlinkert/micromatch>
4 * Copyright (c) 2014-2015, Jon Schlinkert.
5 * Licensed under the MIT License.
10 var utils = require('./utils');
11 var Glob = require('./glob');
17 module.exports = expand;
20 * Expand a glob pattern to resolve braces and
21 * similar patterns before converting to regex.
23 * @param {String|Array} `pattern`
24 * @param {Array} `files`
25 * @param {Options} `opts`
29 function expand(pattern, options) {
30 if (typeof pattern !== 'string') {
31 throw new TypeError('micromatch.expand(): argument should be a string.');
34 var glob = new Glob(pattern, options || {});
35 var opts = glob.options;
37 if (!utils.isGlob(pattern)) {
38 glob.pattern = glob.pattern.replace(/([\/.])/g, '\\$1');
42 glob.pattern = glob.pattern.replace(/(\+)(?!\()/g, '\\$1');
43 glob.pattern = glob.pattern.split('$').join('\\$');
45 if (typeof opts.braces !== 'boolean' && typeof opts.nobraces !== 'boolean') {
49 if (glob.pattern === '.*') {
51 pattern: '\\.' + star,
57 if (glob.pattern === '*') {
59 pattern: oneStar(opts.dot),
65 // parse the glob pattern into tokens
67 var tok = glob.tokens;
68 tok.is.negated = opts.negated;
71 if ((opts.dotfiles === true || tok.is.dotfile) && opts.dot !== false) {
76 if ((opts.dotdirs === true || tok.is.dotdir) && opts.dot !== false) {
81 // check for braces with a dotfile pattern
82 if (/[{,]\./.test(glob.pattern)) {
87 if (opts.nonegate !== true) {
88 opts.negated = glob.negated;
91 // if the leading character is a dot or a slash, escape it
92 if (glob.pattern.charAt(0) === '.' && glob.pattern.charAt(1) !== '/') {
93 glob.pattern = '\\' + glob.pattern;
100 // expand braces, e.g `{1..5}`
101 glob.track('before braces');
105 glob.track('after braces');
107 // expand extglobs, e.g `foo/!(a|b)`
108 glob.track('before extglob');
109 if (tok.is.extglob) {
112 glob.track('after extglob');
114 // expand brackets, e.g `[[:alpha:]]`
115 glob.track('before brackets');
116 if (tok.is.brackets) {
119 glob.track('after brackets');
122 glob._replace('[!', '[^');
123 glob._replace('(?', '(%~');
124 glob._replace(/\[\]/, '\\[\\]');
125 glob._replace('/[', '/' + (opts.dot ? dotfiles : nodot) + '[', true);
126 glob._replace('/?', '/' + (opts.dot ? dotfiles : nodot) + '[^/]', true);
127 glob._replace('/.', '/(?=.)\\.', true);
130 glob._replace(/^(\w):([\\\/]+?)/gi, '(?=.)$1:$2', true);
132 // negate slashes in exclusion ranges
133 if (glob.pattern.indexOf('[^') !== -1) {
134 glob.pattern = negateSlash(glob.pattern);
137 if (opts.globstar !== false && glob.pattern === '**') {
138 glob.pattern = globstar(opts.dot);
141 glob.pattern = balance(glob.pattern, '[', ']');
142 glob.escape(glob.pattern);
144 // if the pattern has `**`
145 if (tok.is.globstar) {
146 glob.pattern = collapse(glob.pattern, '/**');
147 glob.pattern = collapse(glob.pattern, '**/');
148 glob._replace('/**/', '(?:/' + globstar(opts.dot) + '/|/)', true);
149 glob._replace(/\*{2,}/g, '**');
152 glob._replace(/(\w+)\*(?!\/)/g, '$1[^/]*?', true);
153 glob._replace(/\*\*\/\*(\w)/g, globstar(opts.dot) + '\\/' + (opts.dot ? dotfiles : nodot) + '[^/]*?$1', true);
155 if (opts.dot !== true) {
156 glob._replace(/\*\*\/(.)/g, '(?:**\\/|)$1');
159 // 'foo/**' or '{**,*}', but not 'foo**'
160 if (tok.path.dirname !== '' || /,\*\*|\*\*,/.test(glob.orig)) {
161 glob._replace('**', globstar(opts.dot), true);
166 glob._replace(/\/\*$/, '\\/' + oneStar(opts.dot), true);
167 // ends with *, no slashes
168 glob._replace(/(?!\/)\*$/, star, true);
169 // has 'n*.' (partial wildcard w/ file extension)
170 glob._replace(/([^\/]+)\*/, '$1' + oneStar(true), true);
172 glob._replace('*', oneStar(opts.dot), true);
173 glob._replace('?.', '?\\.', true);
174 glob._replace('?:', '?:', true);
176 glob._replace(/\?+/g, function(match) {
177 var len = match.length;
181 return qmark + '{' + len + '}';
184 // escape '.abc' => '\\.abc'
185 glob._replace(/\.([*\w]+)/g, '\\.$1');
187 glob._replace(/\[\^[\\\/]+\]/g, qmark);
189 glob._replace(/\/+/g, '\\/');
191 glob._replace(/\\{2,}/g, '\\');
194 // unescape previously escaped patterns
195 glob.unescape(glob.pattern);
196 glob._replace('__UNESC_STAR__', '*');
198 // escape dots that follow qmarks
199 glob._replace('?.', '?\\.');
201 // remove unnecessary slashes in character classes
202 glob._replace('[^\\/]', qmark);
204 if (glob.pattern.length > 1) {
205 if (/^[\[?*]/.test(glob.pattern)) {
206 // only prepend the string if we don't want to match dotfiles
207 glob.pattern = (opts.dot ? dotfiles : nodot) + glob.pattern;
215 * Collapse repeated character sequences.
218 * collapse('a/../../../b', '../');
222 * @param {String} `str`
223 * @param {String} `ch` Character sequence to collapse
227 function collapse(str, ch) {
228 var res = str.split(ch);
229 var isFirst = res[0] === '';
230 var isLast = res[res.length - 1] === '';
231 res = res.filter(Boolean);
232 if (isFirst) res.unshift('');
233 if (isLast) res.push('');
238 * Negate slashes in exclusion ranges, per glob spec:
241 * negateSlash('[^foo]');
245 * @param {String} `str` glob pattern
249 function negateSlash(str) {
250 return str.replace(/\[\^([^\]]*?)\]/g, function(match, inner) {
251 if (inner.indexOf('/') === -1) {
252 inner = '\\/' + inner;
254 return '[^' + inner + ']';
259 * Escape imbalanced braces/bracket. This is a very
260 * basic, naive implementation that only does enough
261 * to serve the purpose.
264 function balance(str, a, b) {
265 var aarr = str.split(a);
266 var alen = aarr.join('').length;
267 var blen = str.split(b).join('').length;
270 str = aarr.join('\\' + a);
271 return str.split(b).join('\\' + b);
277 * Special patterns to be converted to regex.
278 * Heuristics are used to simplify patterns
279 * and speed up processing.
282 /* eslint no-multi-spaces: 0 */
284 var star = qmark + '*?';
285 var nodot = '(?!\\.)(?=.)';
286 var dotfileGlob = '(?:\\/|^)\\.{1,2}($|\\/)';
287 var dotfiles = '(?!' + dotfileGlob + ')(?=.)';
288 var twoStarDot = '(?:(?!' + dotfileGlob + ').)*?';
291 * Create a regex for `*`.
293 * If `dot` is true, or the pattern does not begin with
294 * a leading star, then return the simpler regex.
297 function oneStar(dotfile) {
298 return dotfile ? '(?!' + dotfileGlob + ')(?=.)' + star : (nodot + star);
301 function globstar(dotfile) {
302 if (dotfile) { return twoStarDot; }
303 return '(?:(?!(?:\\/|^)\\.).)*?';