lib/ignore.js
function listdir(dir, rules, root, included) {
  return fs.readdir(dir)
  .then(function(files) {
    return when.all(files.map(function(f) {
      f = path.join(dir, f);
      return when(fs.stat(f), function(stat) {
        return {
          path: f,
          isDirectory: stat.isDirectory()
        };
      });
    }));
  })
  .then(function(arr) {
    var files = [];
    var subdirs = [];
    arr.forEach(function(e) {
      if (e.isDirectory) {
        subdirs.push(e.path);
      } else {
        files.push(e.path);
      }
    });
    files = files.filter(function(f) {
      return filter(f, rules, root, false, included);
    });
    return when.all(subdirs.map(function(d) {
      return listdir(d, rules, root, filter(d, rules, root, true, included));
    }))
    .then(function(list) {
      list = list.reduce(function(ret, i) {
        return ret.concat(i);
      }, files);
      // include current dir only if at least one of its children is included
      if (list.length > 0) {
        list.push(dir);
      }
      return list;
    });
  });
}
function listdir(dir, rules, root, included) {
  return fs.readdir(dir)
  .then(function(files) {
    return when.all(files.map(function(f) {
      f = path.join(dir, f);
      return when(fs.stat(f), function(stat) {
        return {
          path: f,
          isDirectory: stat.isDirectory()
        };
      });
    }));
  })
  .then(function(arr) {
    var files = [];
    var subdirs = [];
    arr.forEach(function(e) {
      if (e.isDirectory) {
        subdirs.push(e.path);
      } else {
        files.push(e.path);
      }
    });
    files = files.filter(function(f) {
      return filter(f, rules, root, false, included);
    });
    return when.all(subdirs.map(function(d) {
      return listdir(d, rules, root, filter(d, rules, root, true, included));
    }))
    .then(function(list) {
      list = list.reduce(function(ret, i) {
        return ret.concat(i);
      }, files);
      // include current dir only if at least one of its children is included
      if (list.length > 0) {
        list.push(dir);
      }
      return list;
    });
  });
}
function listdir ( dir , rules , root , included ) { return fs . readdir ( dir ) . then ( function ( files ) { return when . all ( files . map ( function ( f ) { f = path . join ( dir , f ) ; return when ( fs . stat ( f ) , function ( stat ) { return { path : f , isDirectory : stat . isDirectory ( ) } ; } ) ; } ) ) ; } ) . then ( function ( arr ) { var files = [ ] ; var subdirs = [ ] ; arr . forEach ( function ( e ) { if ( e . isDirectory ) { subdirs . push ( e . path ) ; } else { files . push ( e . path ) ; } } ) ; files = files . filter ( function ( f ) { return filter ( f , rules , root , false , included ) ; } ) ; return when . all ( subdirs . map ( function ( d ) { return listdir ( d , rules , root , filter ( d , rules , root , true , included ) ) ; } ) ) . then ( function ( list ) { list = list . reduce ( function ( ret , i ) { return ret . concat ( i ) ; } , files ) ; // include current dir only if at least one of its children is included if ( list . length > 0 ) { list . push ( dir ) ; } return list ; } ) ; } ) ; }
filter a dir based on filter rules

@param {String}  dir
@param {Array}   rules
@param {Array}   root
@param {Boolean} included
@return {Promise}
filter a dir based on filter rules