123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- var util = require("util");
- var Glob = require("glob").Glob;
- var EventEmitter = require("events").EventEmitter;
- // helper class to store and compare glob results
- function File(pattern1, patternId1, path1, fileId1) {
- this.pattern = pattern1;
- this.patternId = patternId1;
- this.path = path1;
- this.fileId = fileId1;
- this.include = true;
- while (this.pattern.charAt(0) === "!") {
- this.include = !this.include;
- this.pattern = this.pattern.substr(1);
- }
- }
- File.prototype.stars = /((\/\*\*)?\/\*)?\.(\w+)$/;
- // strip stars and compare pattern length
- // longest length wins
- File.prototype.compare = function(other) {
- var p1 = this.pattern.replace(this.stars, "");
- var p2 = other.pattern.replace(this.stars, "");
- if (p1.length > p2.length) {
- return this;
- } else {
- return other;
- }
- };
- File.prototype.toString = function() {
- return this.path + " (" + this.patternId + ": " + this.fileId + ": " + this.pattern + ")";
- };
- // using standard node inheritance
- util.inherits(GlobAll, EventEmitter);
- // allows the use arrays with "node-glob"
- // interatively combines the resulting arrays
- // api is exactly the same
- function GlobAll(sync, patterns, opts, callback) {
- if (opts == null) {
- opts = {};
- }
- EventEmitter.call(this);
- // init array
- if (typeof patterns === "string") {
- patterns = [patterns];
- }
- if (!(patterns instanceof Array)) {
- return (typeof callback === "function") ? callback("Invalid input") : null;
- }
- // use copy of array
- this.patterns = patterns.slice();
- // no opts provided
- if (typeof opts === "function") {
- callback = opts;
- opts = {};
- }
- // allow sync+nocallback or async+callback
- if (sync !== (typeof callback !== "function")) {
- throw new Error("should" + (sync ? " not" : "") + " have callback");
- }
- // all globs share the same stat cache
- this.statCache = opts.statCache = opts.statCache || {};
- opts.sync = sync;
- this.opts = opts;
- this.set = {};
- this.results = null;
- this.globs = [];
- this.callback = callback;
- // bound functions
- this.globbedOne = this.globbedOne.bind(this);
- }
- GlobAll.prototype.run = function() {
- this.globNext();
- return this.results;
- };
- GlobAll.prototype.globNext = function() {
- var g, pattern, include = true;
- if (this.patterns.length === 0) {
- return this.globbedAll();
- }
- pattern = this.patterns[0]; // peek!
- // check whether this is an exclude pattern and
- // strip the leading ! if it is
- if (pattern.charAt(0) === "!") {
- include = false;
- pattern = pattern.substr(1);
- }
- // run
- if (this.opts.sync) {
- // sync - callback straight away
- g = new Glob(pattern, this.opts);
- this.globs.push(g);
- this.globbedOne(null, include, g.found);
- } else {
- // async
- var self = this;
- g = new Glob(pattern, this.opts, function(err, files) {
- self.globbedOne(err, include, files);
- });
- this.globs.push(g);
- }
- };
- // collect results
- GlobAll.prototype.globbedOne = function(err, include, files) {
- // handle callback error early
- if (err) {
- if (!this.callback) {
- this.emit("error", err);
- }
- this.removeAllListeners();
- if (this.callback) {
- this.callback(err);
- }
- return;
- }
- var patternId = this.patterns.length;
- var pattern = this.patterns.shift();
- // insert each into the results set
- for (var fileId = 0; fileId < files.length; fileId++) {
- // convert to file instance
- var path = files[fileId];
- var f = new File(pattern, patternId, path, fileId);
- var existing = this.set[path];
- // new item
- if (!existing) {
- if (include) {
- this.set[path] = f;
- this.emit("match", path);
- }
- continue;
- }
- // compare or delete
- if (include) {
- this.set[path] = f.compare(existing);
- } else {
- delete this.set[path];
- }
- }
- // run next
- this.globNext();
- };
- GlobAll.prototype.globbedAll = function() {
- // map result set into an array
- var files = [];
- for (var k in this.set) {
- files.push(this.set[k]);
- }
- // sort files by index
- files.sort(function(a, b) {
- if (a.patternId < b.patternId) {
- return 1;
- }
- if (a.patternId > b.patternId) {
- return -1;
- }
- if (a.fileId >= b.fileId) {
- return 1;
- } else {
- return -1;
- }
- });
- // finally, convert back into a path string
- this.results = files.map(function(f) {
- return f.path;
- });
- this.emit("end");
- this.removeAllListeners();
- // return string paths
- if (!this.opts.sync) {
- this.callback(null, this.results);
- }
- return this.results;
- };
- // expose
- var globAll = function(array, opts, callback) {
- var g = new GlobAll(false, array, opts, callback);
- g.run(); //async, so results are empty
- return g;
- };
- // sync is actually the same function :)
- globAll.sync = function(array, opts) {
- return new GlobAll(true, array, opts).run();
- };
- module.exports = globAll;
|