var util = require('util'); var minimatch = require('minimatch'); var glob = require('glob'); var Glob = glob.Glob; var EventEmitter = require('events').EventEmitter; module.exports = fileset; // Async API function fileset(include, exclude, options, cb) { if (typeof exclude === 'function') cb = exclude, exclude = ''; else if (typeof options === 'function') cb = options, options = {}; var includes = (typeof include === 'string') ? include.split(' ') : include; var excludes = (typeof exclude === 'string') ? exclude.split(' ') : exclude; var em = new EventEmitter; var remaining = includes.length; var results = []; if (!includes.length) return cb(new Error('Must provide an include pattern')); em.includes = includes.map(function(pattern) { return new fileset.Fileset(pattern, options) .on('error', cb ? cb : em.emit.bind(em, 'error')) .on('match', em.emit.bind(em, 'match')) .on('match', em.emit.bind(em, 'include')) .on('end', next.bind({}, pattern)) }); function next(pattern, matches) { results = results.concat(matches); if (!(--remaining)) { results = results.filter(function(file) { return !excludes.filter(function(glob) { var match = minimatch(file, glob, { matchBase: true }); if(match) em.emit('exclude', file); return match; }).length; }); if(cb) cb(null, results); em.emit('end', results); } } return em; } // Sync API fileset.sync = function filesetSync(include, exclude) { if (!exclude) exclude = ''; // includes / excludes, either an array or string separated by comma or whitespace var includes = (typeof include === 'string') ? include.split(/[\s,]/g) : include; var excludes = (typeof exclude === 'string') ? exclude.split(/[\s,]/g) : exclude; // Filter out any false positive '' empty strings includes = includes.filter(function(pattern) { return pattern; }); excludes = excludes.filter(function(pattern) { return pattern; }); // - todo: pass in glob options as last param var options = { matchBase: true }; // always ignore node_modules for sync api options.ignore = ['node_modules/**/*']; // First, glob match on all include patters into a single array var results = includes.map(function(include) { return glob.sync(include, options); }).reduce(function(a, b) { return a.concat(b); }, []); // Then filters out on any exclude match var ignored = excludes.map(function(exclude) { return glob.sync(exclude, options); }).reduce(function(a, b) { return a.concat(b); }, []); // And filter any exclude match results = results.filter(function(file) { return !ignored.filter(function(glob) { return minimatch(file, glob, { matchBase: true }); }).length; }); return results; }; fileset.Fileset = function Fileset(pattern, options, cb) { if (typeof options === 'function') cb = options, options = {}; if (!options) options = {}; // ignore node_modules by default, unless specified options.ignore = options.ignore || ['node_modules/**/*']; Glob.call(this, pattern, options); if (typeof cb === 'function') { this.on('error', cb); this.on('end', function(matches) { cb(null, matches); }); } }; util.inherits(fileset.Fileset, Glob);