expand.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*!
  2. * micromatch <https://github.com/jonschlinkert/micromatch>
  3. *
  4. * Copyright (c) 2014-2015, Jon Schlinkert.
  5. * Licensed under the MIT License.
  6. */
  7. 'use strict';
  8. var utils = require('./utils');
  9. var Glob = require('./glob');
  10. /**
  11. * Expose `expand`
  12. */
  13. module.exports = expand;
  14. /**
  15. * Expand a glob pattern to resolve braces and
  16. * similar patterns before converting to regex.
  17. *
  18. * @param {String|Array} `pattern`
  19. * @param {Array} `files`
  20. * @param {Options} `opts`
  21. * @return {Array}
  22. */
  23. function expand(pattern, options) {
  24. if (typeof pattern !== 'string') {
  25. throw new TypeError('micromatch.expand(): argument should be a string.');
  26. }
  27. var glob = new Glob(pattern, options || {});
  28. var opts = glob.options;
  29. if (!utils.isGlob(pattern)) {
  30. glob.pattern = glob.pattern.replace(/([\/.])/g, '\\$1');
  31. return glob;
  32. }
  33. glob.pattern = glob.pattern.replace(/(\+)(?!\()/g, '\\$1');
  34. glob.pattern = glob.pattern.split('$').join('\\$');
  35. if (typeof opts.braces !== 'boolean' && typeof opts.nobraces !== 'boolean') {
  36. opts.braces = true;
  37. }
  38. if (glob.pattern === '.*') {
  39. return {
  40. pattern: '\\.' + star,
  41. tokens: tok,
  42. options: opts
  43. };
  44. }
  45. if (glob.pattern === '*') {
  46. return {
  47. pattern: oneStar(opts.dot),
  48. tokens: tok,
  49. options: opts
  50. };
  51. }
  52. // parse the glob pattern into tokens
  53. glob.parse();
  54. var tok = glob.tokens;
  55. tok.is.negated = opts.negated;
  56. // dotfile handling
  57. if ((opts.dotfiles === true || tok.is.dotfile) && opts.dot !== false) {
  58. opts.dotfiles = true;
  59. opts.dot = true;
  60. }
  61. if ((opts.dotdirs === true || tok.is.dotdir) && opts.dot !== false) {
  62. opts.dotdirs = true;
  63. opts.dot = true;
  64. }
  65. // check for braces with a dotfile pattern
  66. if (/[{,]\./.test(glob.pattern)) {
  67. opts.makeRe = false;
  68. opts.dot = true;
  69. }
  70. if (opts.nonegate !== true) {
  71. opts.negated = glob.negated;
  72. }
  73. // if the leading character is a dot or a slash, escape it
  74. if (glob.pattern.charAt(0) === '.' && glob.pattern.charAt(1) !== '/') {
  75. glob.pattern = '\\' + glob.pattern;
  76. }
  77. /**
  78. * Extended globs
  79. */
  80. // expand braces, e.g `{1..5}`
  81. glob.track('before braces');
  82. if (tok.is.braces) {
  83. glob.braces();
  84. }
  85. glob.track('after braces');
  86. // expand extglobs, e.g `foo/!(a|b)`
  87. glob.track('before extglob');
  88. if (tok.is.extglob) {
  89. glob.extglob();
  90. }
  91. glob.track('after extglob');
  92. // expand brackets, e.g `[[:alpha:]]`
  93. glob.track('before brackets');
  94. if (tok.is.brackets) {
  95. glob.brackets();
  96. }
  97. glob.track('after brackets');
  98. // special patterns
  99. glob._replace('[!', '[^');
  100. glob._replace('(?', '(%~');
  101. glob._replace(/\[\]/, '\\[\\]');
  102. glob._replace('/[', '/' + (opts.dot ? dotfiles : nodot) + '[', true);
  103. glob._replace('/?', '/' + (opts.dot ? dotfiles : nodot) + '[^/]', true);
  104. glob._replace('/.', '/(?=.)\\.', true);
  105. // windows drives
  106. glob._replace(/^(\w):([\\\/]+?)/gi, '(?=.)$1:$2', true);
  107. // negate slashes in exclusion ranges
  108. if (glob.pattern.indexOf('[^') !== -1) {
  109. glob.pattern = negateSlash(glob.pattern);
  110. }
  111. if (opts.globstar !== false && glob.pattern === '**') {
  112. glob.pattern = globstar(opts.dot);
  113. } else {
  114. glob.pattern = balance(glob.pattern, '[', ']');
  115. glob.escape(glob.pattern);
  116. // if the pattern has `**`
  117. if (tok.is.globstar) {
  118. glob.pattern = collapse(glob.pattern, '/**');
  119. glob.pattern = collapse(glob.pattern, '**/');
  120. glob._replace('/**/', '(?:/' + globstar(opts.dot) + '/|/)', true);
  121. glob._replace(/\*{2,}/g, '**');
  122. // 'foo/*'
  123. glob._replace(/(\w+)\*(?!\/)/g, '$1[^/]*?', true);
  124. glob._replace(/\*\*\/\*(\w)/g, globstar(opts.dot) + '\\/' + (opts.dot ? dotfiles : nodot) + '[^/]*?$1', true);
  125. if (opts.dot !== true) {
  126. glob._replace(/\*\*\/(.)/g, '(?:**\\/|)$1');
  127. }
  128. // 'foo/**' or '{**,*}', but not 'foo**'
  129. if (tok.path.dirname !== '' || /,\*\*|\*\*,/.test(glob.orig)) {
  130. glob._replace('**', globstar(opts.dot), true);
  131. }
  132. }
  133. // ends with /*
  134. glob._replace(/\/\*$/, '\\/' + oneStar(opts.dot), true);
  135. // ends with *, no slashes
  136. glob._replace(/(?!\/)\*$/, star, true);
  137. // has 'n*.' (partial wildcard w/ file extension)
  138. glob._replace(/([^\/]+)\*/, '$1' + oneStar(true), true);
  139. // has '*'
  140. glob._replace('*', oneStar(opts.dot), true);
  141. glob._replace('?.', '?\\.', true);
  142. glob._replace('?:', '?:', true);
  143. glob._replace(/\?+/g, function(match) {
  144. var len = match.length;
  145. if (len === 1) {
  146. return qmark;
  147. }
  148. return qmark + '{' + len + '}';
  149. });
  150. // escape '.abc' => '\\.abc'
  151. glob._replace(/\.([*\w]+)/g, '\\.$1');
  152. // fix '[^\\\\/]'
  153. glob._replace(/\[\^[\\\/]+\]/g, qmark);
  154. // '///' => '\/'
  155. glob._replace(/\/+/g, '\\/');
  156. // '\\\\\\' => '\\'
  157. glob._replace(/\\{2,}/g, '\\');
  158. }
  159. // unescape previously escaped patterns
  160. glob.unescape(glob.pattern);
  161. glob._replace('__UNESC_STAR__', '*');
  162. // escape dots that follow qmarks
  163. glob._replace('?.', '?\\.');
  164. // remove unnecessary slashes in character classes
  165. glob._replace('[^\\/]', qmark);
  166. if (glob.pattern.length > 1) {
  167. if (/^[\[?*]/.test(glob.pattern)) {
  168. // only prepend the string if we don't want to match dotfiles
  169. glob.pattern = (opts.dot ? dotfiles : nodot) + glob.pattern;
  170. }
  171. }
  172. return glob;
  173. }
  174. /**
  175. * Collapse repeated character sequences.
  176. *
  177. * ```js
  178. * collapse('a/../../../b', '../');
  179. * //=> 'a/../b'
  180. * ```
  181. *
  182. * @param {String} `str`
  183. * @param {String} `ch` Character sequence to collapse
  184. * @return {String}
  185. */
  186. function collapse(str, ch) {
  187. var res = str.split(ch);
  188. var isFirst = res[0] === '';
  189. var isLast = res[res.length - 1] === '';
  190. res = res.filter(Boolean);
  191. if (isFirst) res.unshift('');
  192. if (isLast) res.push('');
  193. return res.join(ch);
  194. }
  195. /**
  196. * Negate slashes in exclusion ranges, per glob spec:
  197. *
  198. * ```js
  199. * negateSlash('[^foo]');
  200. * //=> '[^\\/foo]'
  201. * ```
  202. *
  203. * @param {String} `str` glob pattern
  204. * @return {String}
  205. */
  206. function negateSlash(str) {
  207. return str.replace(/\[\^([^\]]*?)\]/g, function(match, inner) {
  208. if (inner.indexOf('/') === -1) {
  209. inner = '\\/' + inner;
  210. }
  211. return '[^' + inner + ']';
  212. });
  213. }
  214. /**
  215. * Escape imbalanced braces/bracket. This is a very
  216. * basic, naive implementation that only does enough
  217. * to serve the purpose.
  218. */
  219. function balance(str, a, b) {
  220. var aarr = str.split(a);
  221. var alen = aarr.join('').length;
  222. var blen = str.split(b).join('').length;
  223. if (alen !== blen) {
  224. str = aarr.join('\\' + a);
  225. return str.split(b).join('\\' + b);
  226. }
  227. return str;
  228. }
  229. /**
  230. * Special patterns to be converted to regex.
  231. * Heuristics are used to simplify patterns
  232. * and speed up processing.
  233. */
  234. /* eslint no-multi-spaces: 0 */
  235. var qmark = '[^/]';
  236. var star = qmark + '*?';
  237. var nodot = '(?!\\.)(?=.)';
  238. var dotfileGlob = '(?:\\/|^)\\.{1,2}($|\\/)';
  239. var dotfiles = '(?!' + dotfileGlob + ')(?=.)';
  240. var twoStarDot = '(?:(?!' + dotfileGlob + ').)*?';
  241. /**
  242. * Create a regex for `*`.
  243. *
  244. * If `dot` is true, or the pattern does not begin with
  245. * a leading star, then return the simpler regex.
  246. */
  247. function oneStar(dotfile) {
  248. return dotfile ? '(?!' + dotfileGlob + ')(?=.)' + star : (nodot + star);
  249. }
  250. function globstar(dotfile) {
  251. if (dotfile) { return twoStarDot; }
  252. return '(?:(?!(?:\\/|^)\\.).)*?';
  253. }