fileset.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. var util = require('util');
  2. var minimatch = require('minimatch');
  3. var glob = require('glob');
  4. var Glob = glob.Glob;
  5. var EventEmitter = require('events').EventEmitter;
  6. module.exports = fileset;
  7. // Async API
  8. function fileset(include, exclude, options, cb) {
  9. if (typeof exclude === 'function') cb = exclude, exclude = '';
  10. else if (typeof options === 'function') cb = options, options = {};
  11. var includes = (typeof include === 'string') ? include.split(' ') : include;
  12. var excludes = (typeof exclude === 'string') ? exclude.split(' ') : exclude;
  13. var em = new EventEmitter;
  14. var remaining = includes.length;
  15. var results = [];
  16. if (!includes.length) return cb(new Error('Must provide an include pattern'));
  17. em.includes = includes.map(function(pattern) {
  18. return new fileset.Fileset(pattern, options)
  19. .on('error', cb ? cb : em.emit.bind(em, 'error'))
  20. .on('match', em.emit.bind(em, 'match'))
  21. .on('match', em.emit.bind(em, 'include'))
  22. .on('end', next.bind({}, pattern))
  23. });
  24. function next(pattern, matches) {
  25. results = results.concat(matches);
  26. if (!(--remaining)) {
  27. results = results.filter(function(file) {
  28. return !excludes.filter(function(glob) {
  29. var match = minimatch(file, glob, { matchBase: true });
  30. if(match) em.emit('exclude', file);
  31. return match;
  32. }).length;
  33. });
  34. if(cb) cb(null, results);
  35. em.emit('end', results);
  36. }
  37. }
  38. return em;
  39. }
  40. // Sync API
  41. fileset.sync = function filesetSync(include, exclude) {
  42. if (!exclude) exclude = '';
  43. // includes / excludes, either an array or string separated by comma or whitespace
  44. var includes = (typeof include === 'string') ? include.split(/[\s,]/g) : include;
  45. var excludes = (typeof exclude === 'string') ? exclude.split(/[\s,]/g) : exclude;
  46. // Filter out any false positive '' empty strings
  47. includes = includes.filter(function(pattern) { return pattern; });
  48. excludes = excludes.filter(function(pattern) { return pattern; });
  49. // - todo: pass in glob options as last param
  50. var options = { matchBase: true };
  51. // always ignore node_modules for sync api
  52. options.ignore = ['node_modules/**/*'];
  53. // First, glob match on all include patters into a single array
  54. var results = includes.map(function(include) {
  55. return glob.sync(include, options);
  56. }).reduce(function(a, b) {
  57. return a.concat(b);
  58. }, []);
  59. // Then filters out on any exclude match
  60. var ignored = excludes.map(function(exclude) {
  61. return glob.sync(exclude, options);
  62. }).reduce(function(a, b) {
  63. return a.concat(b);
  64. }, []);
  65. // And filter any exclude match
  66. results = results.filter(function(file) {
  67. return !ignored.filter(function(glob) {
  68. return minimatch(file, glob, { matchBase: true });
  69. }).length;
  70. });
  71. return results;
  72. };
  73. fileset.Fileset = function Fileset(pattern, options, cb) {
  74. if (typeof options === 'function') cb = options, options = {};
  75. if (!options) options = {};
  76. // ignore node_modules by default, unless specified
  77. options.ignore = options.ignore || ['node_modules/**/*'];
  78. Glob.call(this, pattern, options);
  79. if (typeof cb === 'function') {
  80. this.on('error', cb);
  81. this.on('end', function(matches) { cb(null, matches); });
  82. }
  83. };
  84. util.inherits(fileset.Fileset, Glob);