166 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			166 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | (function () { | ||
|  |   "use strict"; | ||
|  | 
 | ||
|  |   var fs = require('fs'), | ||
|  |     fstat = fs.lstat, | ||
|  |     Futures = require('futures'), | ||
|  |     EventEmitter = require('events').EventEmitter, | ||
|  |     upath = require('path'), | ||
|  |     // "FIFO" isn't easy to convert to camelCase and back reliably
 | ||
|  |     isFnodeTypes = [ | ||
|  |       "isFile", "isDirectory",  "isBlockDevice",  "isCharacterDevice",  "isSymbolicLink", "isFIFO", "isSocket" | ||
|  |     ], | ||
|  |     fnodeTypes = [ | ||
|  |       "file",   "directory",    "blockDevice",    "characterDevice",    "symbolicLink",   "FIFO",   "socket" | ||
|  |     ], | ||
|  |     fnodeTypesPlural = [ | ||
|  |       "files",  "directories",  "blockDevices",   "characterDevices",   "symbolicLinks",  "FIFOs",  "sockets" | ||
|  |     ]; | ||
|  | 
 | ||
|  |   // Get the current number of listeners (which may change)
 | ||
|  |   // Emit events to each listener
 | ||
|  |   // Wait for all listeners to `next()` before continueing
 | ||
|  |   // (in theory this may avoid disk thrashing)
 | ||
|  |   function emitSingleEvents(emitter, path, stats, next) { | ||
|  |     var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length; | ||
|  | 
 | ||
|  |     function nextWhenReady() { | ||
|  |       num -= 1; | ||
|  |       if (0 === num) { next(); } | ||
|  |     } | ||
|  | 
 | ||
|  |     emitter.emit(stats.type, path, stats, nextWhenReady); | ||
|  |     emitter.emit("node", path, stats, nextWhenReady); | ||
|  |     nextWhenReady(); | ||
|  |   } | ||
|  | 
 | ||
|  | 
 | ||
|  |   // Since the risk for disk thrashing among anything
 | ||
|  |   // other than files is relatively low, all types are
 | ||
|  |   // emitted at once, but all must complete before advancing
 | ||
|  |   function emitPluralEvents(emitter, path, nodes, next) { | ||
|  |     var num = 1; | ||
|  | 
 | ||
|  |     function nextWhenReady() { | ||
|  |       num -= 1; | ||
|  |       if (0 === num) { next(); } | ||
|  |     } | ||
|  | 
 | ||
|  |     fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) { | ||
|  |       if (0 === nodes[fnodeType].length) { return; } | ||
|  |       num += emitter.listeners(fnodeType).length; | ||
|  |       emitter.emit(fnodeType, path, nodes[fnodeType], nextWhenReady); | ||
|  |     }); | ||
|  |     nextWhenReady(); | ||
|  |   } | ||
|  | 
 | ||
|  | 
 | ||
|  |   // Determine each file node's type
 | ||
|  |   // 
 | ||
|  |   function sortFnodesByType(stats, fnodes) { | ||
|  |     isFnodeTypes.forEach(function (isType, i) { | ||
|  |       if (stats[isType]()) { | ||
|  |         if (stats.type) { throw new Error("is_" + type + " and " + isType); } | ||
|  |         stats.type = fnodeTypes[i]; | ||
|  |         fnodes[fnodeTypesPlural[i]].push(stats); | ||
|  |         //console.log(isType, fnodeTypesPlural[i], stats.name);
 | ||
|  |         // TODO throw to break;
 | ||
|  |       } | ||
|  |     }); | ||
|  |     /* | ||
|  |     // Won't really ever happen
 | ||
|  |     if (!stats.type) { | ||
|  |       stats.error = new Error(upath.join(path, stats.name) + ' isAnUndefinedType'); | ||
|  |     } | ||
|  |     */ | ||
|  |   } | ||
|  | 
 | ||
|  |   function create(path) { | ||
|  |     var emitter = new EventEmitter(), | ||
|  |       paths = [], | ||
|  |       path; | ||
|  | 
 | ||
|  |     function next() { | ||
|  |       // path could be local if dirHandler were anonymous
 | ||
|  |       //console.log('LEN: '+ paths.length);
 | ||
|  |       if (0 == paths.length) { | ||
|  |         emitter.emit('end'); | ||
|  |         return; | ||
|  |       } | ||
|  |       path = paths.pop(); | ||
|  |       //console.log("POP: " + path);
 | ||
|  |       fs.readdir(path, dirHandler); | ||
|  |     } | ||
|  | 
 | ||
|  |     function nodesHandler(nodes, args) { | ||
|  |       //console.log('USE: ' + path);
 | ||
|  |       var statses = []; | ||
|  | 
 | ||
|  |       var nodeGroups = {}; | ||
|  |       fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) { | ||
|  |         nodeGroups[fnodeTypePlural] = []; | ||
|  |       }); | ||
|  | 
 | ||
|  |       args.forEach(function (arg, i) { | ||
|  |         var file = nodes[i], | ||
|  |           err = arg[0], | ||
|  |           stats = arg[1]; | ||
|  | 
 | ||
|  |         if (err) { | ||
|  |           stats = { error: err, name: file }; | ||
|  |           emitter.emit('error', err, path, stats); | ||
|  |         } | ||
|  |         if (stats) { | ||
|  |           stats.name = file; | ||
|  |           sortFnodesByType(stats, nodeGroups); | ||
|  |           emitter.emit('stat', path, stats); | ||
|  |         } | ||
|  |       }); | ||
|  |       emitter.emit('stats', path, statses); | ||
|  |       nodeGroups['directories'].forEach(function (stat) { | ||
|  |         paths.push(path + '/' + stat.name); | ||
|  |         //console.log('PUSH: ' + path + '/' + stat.name);
 | ||
|  |       }); | ||
|  |       /* | ||
|  |       //console.log('USE: ' + path);
 | ||
|  |       next(); | ||
|  |       */ | ||
|  |       emitPluralEvents(emitter, path, nodeGroups, next); | ||
|  |     } | ||
|  | 
 | ||
|  |     function dirHandler(err, nodes) { | ||
|  |       //console.log("HANDLE: " + path);
 | ||
|  |       var join = Futures.join(), | ||
|  |         i; | ||
|  | 
 | ||
|  |       if (err) { | ||
|  |         emitter.emit('error', err, path); | ||
|  |       } | ||
|  |       if (!nodes || 0 == nodes.length) { | ||
|  |         //console.log('EMPTY: ' + path);
 | ||
|  |         return next(); | ||
|  |       } | ||
|  |       // TODO don't duplicate efforts
 | ||
|  |       emitter.emit('nodes', path, nodes); | ||
|  | 
 | ||
|  |       for (i = 0; i < nodes.length; i += 1) { | ||
|  |         fstat(path + '/' + nodes[i], join.deliverer()); | ||
|  |       } | ||
|  | 
 | ||
|  |       join.when(function () { | ||
|  |         var args = Array.prototype.slice.call(arguments); | ||
|  |         nodesHandler(nodes, args); | ||
|  |       }); | ||
|  |     } | ||
|  | 
 | ||
|  |     //paths.push([path]);
 | ||
|  |     paths.push(path); | ||
|  | 
 | ||
|  | 
 | ||
|  |     next(); | ||
|  |     return emitter; | ||
|  |   } | ||
|  | 
 | ||
|  |   module.exports = create; | ||
|  | }()); |