index.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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 expand = require('./lib/expand');
  9. var utils = require('./lib/utils');
  10. /**
  11. * The main function. Pass an array of filepaths,
  12. * and a string or array of glob patterns
  13. *
  14. * @param {Array|String} `files`
  15. * @param {Array|String} `patterns`
  16. * @param {Object} `opts`
  17. * @return {Array} Array of matches
  18. */
  19. function micromatch(files, patterns, opts) {
  20. if (!files || !patterns) return [];
  21. opts = opts || {};
  22. if (typeof opts.cache === 'undefined') {
  23. opts.cache = true;
  24. }
  25. if (!Array.isArray(patterns)) {
  26. return match(files, patterns, opts);
  27. }
  28. var len = patterns.length, i = 0;
  29. var omit = [], keep = [];
  30. while (len--) {
  31. var glob = patterns[i++];
  32. if (typeof glob === 'string' && glob.charCodeAt(0) === 33 /* ! */) {
  33. omit.push.apply(omit, match(files, glob.slice(1), opts));
  34. } else {
  35. keep.push.apply(keep, match(files, glob, opts));
  36. }
  37. }
  38. return utils.diff(keep, omit);
  39. }
  40. /**
  41. * Return an array of files that match the given glob pattern.
  42. *
  43. * This function is called by the main `micromatch` function If you only
  44. * need to pass a single pattern you might get very minor speed improvements
  45. * using this function.
  46. *
  47. * @param {Array} `files`
  48. * @param {String} `pattern`
  49. * @param {Object} `options`
  50. * @return {Array}
  51. */
  52. function match(files, pattern, opts) {
  53. if (utils.typeOf(files) !== 'string' && !Array.isArray(files)) {
  54. throw new Error(msg('match', 'files', 'a string or array'));
  55. }
  56. files = utils.arrayify(files);
  57. opts = opts || {};
  58. var negate = opts.negate || false;
  59. var orig = pattern;
  60. if (typeof pattern === 'string') {
  61. negate = pattern.charAt(0) === '!';
  62. if (negate) {
  63. pattern = pattern.slice(1);
  64. }
  65. // we need to remove the character regardless,
  66. // so the above logic is still needed
  67. if (opts.nonegate === true) {
  68. negate = false;
  69. }
  70. }
  71. var _isMatch = matcher(pattern, opts);
  72. var len = files.length, i = 0;
  73. var res = [];
  74. while (i < len) {
  75. var file = files[i++];
  76. var fp = utils.unixify(file, opts);
  77. if (!_isMatch(fp)) { continue; }
  78. res.push(fp);
  79. }
  80. if (res.length === 0) {
  81. if (opts.failglob === true) {
  82. throw new Error('micromatch.match() found no matches for: "' + orig + '".');
  83. }
  84. if (opts.nonull || opts.nullglob) {
  85. res.push(utils.unescapeGlob(orig));
  86. }
  87. }
  88. // if `negate` was defined, diff negated files
  89. if (negate) { res = utils.diff(files, res); }
  90. // if `ignore` was defined, diff ignored filed
  91. if (opts.ignore && opts.ignore.length) {
  92. pattern = opts.ignore;
  93. opts = utils.omit(opts, ['ignore']);
  94. res = utils.diff(res, micromatch(res, pattern, opts));
  95. }
  96. if (opts.nodupes) {
  97. return utils.unique(res);
  98. }
  99. return res;
  100. }
  101. /**
  102. * Returns a function that takes a glob pattern or array of glob patterns
  103. * to be used with `Array#filter()`. (Internally this function generates
  104. * the matching function using the [matcher] method).
  105. *
  106. * ```js
  107. * var fn = mm.filter('[a-c]');
  108. * ['a', 'b', 'c', 'd', 'e'].filter(fn);
  109. * //=> ['a', 'b', 'c']
  110. * ```
  111. * @param {String|Array} `patterns` Can be a glob or array of globs.
  112. * @param {Options} `opts` Options to pass to the [matcher] method.
  113. * @return {Function} Filter function to be passed to `Array#filter()`.
  114. */
  115. function filter(patterns, opts) {
  116. if (!Array.isArray(patterns) && typeof patterns !== 'string') {
  117. throw new TypeError(msg('filter', 'patterns', 'a string or array'));
  118. }
  119. patterns = utils.arrayify(patterns);
  120. var len = patterns.length, i = 0;
  121. var patternMatchers = Array(len);
  122. while (i < len) {
  123. patternMatchers[i] = matcher(patterns[i++], opts);
  124. }
  125. return function(fp) {
  126. if (fp == null) return [];
  127. var len = patternMatchers.length, i = 0;
  128. var res = true;
  129. fp = utils.unixify(fp, opts);
  130. while (i < len) {
  131. var fn = patternMatchers[i++];
  132. if (!fn(fp)) {
  133. res = false;
  134. break;
  135. }
  136. }
  137. return res;
  138. };
  139. }
  140. /**
  141. * Returns true if the filepath contains the given
  142. * pattern. Can also return a function for matching.
  143. *
  144. * ```js
  145. * isMatch('foo.md', '*.md', {});
  146. * //=> true
  147. *
  148. * isMatch('*.md', {})('foo.md')
  149. * //=> true
  150. * ```
  151. * @param {String} `fp`
  152. * @param {String} `pattern`
  153. * @param {Object} `opts`
  154. * @return {Boolean}
  155. */
  156. function isMatch(fp, pattern, opts) {
  157. if (typeof fp !== 'string') {
  158. throw new TypeError(msg('isMatch', 'filepath', 'a string'));
  159. }
  160. fp = utils.unixify(fp, opts);
  161. if (utils.typeOf(pattern) === 'object') {
  162. return matcher(fp, pattern);
  163. }
  164. return matcher(pattern, opts)(fp);
  165. }
  166. /**
  167. * Returns true if the filepath matches the
  168. * given pattern.
  169. */
  170. function contains(fp, pattern, opts) {
  171. if (typeof fp !== 'string') {
  172. throw new TypeError(msg('contains', 'pattern', 'a string'));
  173. }
  174. opts = opts || {};
  175. opts.contains = (pattern !== '');
  176. fp = utils.unixify(fp, opts);
  177. if (opts.contains && !utils.isGlob(pattern)) {
  178. return fp.indexOf(pattern) !== -1;
  179. }
  180. return matcher(pattern, opts)(fp);
  181. }
  182. /**
  183. * Returns true if a file path matches any of the
  184. * given patterns.
  185. *
  186. * @param {String} `fp` The filepath to test.
  187. * @param {String|Array} `patterns` Glob patterns to use.
  188. * @param {Object} `opts` Options to pass to the `matcher()` function.
  189. * @return {String}
  190. */
  191. function any(fp, patterns, opts) {
  192. if (!Array.isArray(patterns) && typeof patterns !== 'string') {
  193. throw new TypeError(msg('any', 'patterns', 'a string or array'));
  194. }
  195. patterns = utils.arrayify(patterns);
  196. var len = patterns.length;
  197. fp = utils.unixify(fp, opts);
  198. while (len--) {
  199. var isMatch = matcher(patterns[len], opts);
  200. if (isMatch(fp)) {
  201. return true;
  202. }
  203. }
  204. return false;
  205. }
  206. /**
  207. * Filter the keys of an object with the given `glob` pattern
  208. * and `options`
  209. *
  210. * @param {Object} `object`
  211. * @param {Pattern} `object`
  212. * @return {Array}
  213. */
  214. function matchKeys(obj, glob, options) {
  215. if (utils.typeOf(obj) !== 'object') {
  216. throw new TypeError(msg('matchKeys', 'first argument', 'an object'));
  217. }
  218. var fn = matcher(glob, options);
  219. var res = {};
  220. for (var key in obj) {
  221. if (obj.hasOwnProperty(key) && fn(key)) {
  222. res[key] = obj[key];
  223. }
  224. }
  225. return res;
  226. }
  227. /**
  228. * Return a function for matching based on the
  229. * given `pattern` and `options`.
  230. *
  231. * @param {String} `pattern`
  232. * @param {Object} `options`
  233. * @return {Function}
  234. */
  235. function matcher(pattern, opts) {
  236. // pattern is a function
  237. if (typeof pattern === 'function') {
  238. return pattern;
  239. }
  240. // pattern is a regex
  241. if (pattern instanceof RegExp) {
  242. return function(fp) {
  243. return pattern.test(fp);
  244. };
  245. }
  246. if (typeof pattern !== 'string') {
  247. throw new TypeError(msg('matcher', 'pattern', 'a string, regex, or function'));
  248. }
  249. // strings, all the way down...
  250. pattern = utils.unixify(pattern, opts);
  251. // pattern is a non-glob string
  252. if (!utils.isGlob(pattern)) {
  253. return utils.matchPath(pattern, opts);
  254. }
  255. // pattern is a glob string
  256. var re = makeRe(pattern, opts);
  257. // `matchBase` is defined
  258. if (opts && opts.matchBase) {
  259. return utils.hasFilename(re, opts);
  260. }
  261. // `matchBase` is not defined
  262. return function(fp) {
  263. fp = utils.unixify(fp, opts);
  264. return re.test(fp);
  265. };
  266. }
  267. /**
  268. * Create and cache a regular expression for matching
  269. * file paths.
  270. *
  271. * If the leading character in the `glob` is `!`, a negation
  272. * regex is returned.
  273. *
  274. * @param {String} `glob`
  275. * @param {Object} `options`
  276. * @return {RegExp}
  277. */
  278. function toRegex(glob, options) {
  279. // clone options to prevent mutating the original object
  280. var opts = Object.create(options || {});
  281. var flags = opts.flags || '';
  282. if (opts.nocase && flags.indexOf('i') === -1) {
  283. flags += 'i';
  284. }
  285. var parsed = expand(glob, opts);
  286. // pass in tokens to avoid parsing more than once
  287. opts.negated = opts.negated || parsed.negated;
  288. opts.negate = opts.negated;
  289. glob = wrapGlob(parsed.pattern, opts);
  290. var re;
  291. try {
  292. re = new RegExp(glob, flags);
  293. return re;
  294. } catch (err) {
  295. err.reason = 'micromatch invalid regex: (' + re + ')';
  296. if (opts.strict) throw new SyntaxError(err);
  297. }
  298. // we're only here if a bad pattern was used and the user
  299. // passed `options.silent`, so match nothing
  300. return /$^/;
  301. }
  302. /**
  303. * Create the regex to do the matching. If the leading
  304. * character in the `glob` is `!` a negation regex is returned.
  305. *
  306. * @param {String} `glob`
  307. * @param {Boolean} `negate`
  308. */
  309. function wrapGlob(glob, opts) {
  310. var prefix = (opts && !opts.contains) ? '^' : '';
  311. var after = (opts && !opts.contains) ? '$' : '';
  312. glob = ('(?:' + glob + ')' + after);
  313. if (opts && opts.negate) {
  314. return prefix + ('(?!^' + glob + ').*$');
  315. }
  316. return prefix + glob;
  317. }
  318. /**
  319. * Create and cache a regular expression for matching file paths.
  320. * If the leading character in the `glob` is `!`, a negation
  321. * regex is returned.
  322. *
  323. * @param {String} `glob`
  324. * @param {Object} `options`
  325. * @return {RegExp}
  326. */
  327. function makeRe(glob, opts) {
  328. if (utils.typeOf(glob) !== 'string') {
  329. throw new Error(msg('makeRe', 'glob', 'a string'));
  330. }
  331. return utils.cache(toRegex, glob, opts);
  332. }
  333. /**
  334. * Make error messages consistent. Follows this format:
  335. *
  336. * ```js
  337. * msg(methodName, argNumber, nativeType);
  338. * // example:
  339. * msg('matchKeys', 'first', 'an object');
  340. * ```
  341. *
  342. * @param {String} `method`
  343. * @param {String} `num`
  344. * @param {String} `type`
  345. * @return {String}
  346. */
  347. function msg(method, what, type) {
  348. return 'micromatch.' + method + '(): ' + what + ' should be ' + type + '.';
  349. }
  350. /**
  351. * Public methods
  352. */
  353. /* eslint no-multi-spaces: 0 */
  354. micromatch.any = any;
  355. micromatch.braces = micromatch.braceExpand = utils.braces;
  356. micromatch.contains = contains;
  357. micromatch.expand = expand;
  358. micromatch.filter = filter;
  359. micromatch.isMatch = isMatch;
  360. micromatch.makeRe = makeRe;
  361. micromatch.match = match;
  362. micromatch.matcher = matcher;
  363. micromatch.matchKeys = matchKeys;
  364. /**
  365. * Expose `micromatch`
  366. */
  367. module.exports = micromatch;