| 1 | module.exports = Walker
|
|---|
| 2 |
|
|---|
| 3 | var path = require('path')
|
|---|
| 4 | , fs = require('fs')
|
|---|
| 5 | , util = require('util')
|
|---|
| 6 | , EventEmitter = require('events').EventEmitter
|
|---|
| 7 | , makeError = require('makeerror')
|
|---|
| 8 |
|
|---|
| 9 | /**
|
|---|
| 10 | * To walk a directory. It's complicated (but it's async, so it must be fast).
|
|---|
| 11 | *
|
|---|
| 12 | * @param root {String} the directory to start with
|
|---|
| 13 | */
|
|---|
| 14 | function Walker(root) {
|
|---|
| 15 | if (!(this instanceof Walker)) return new Walker(root)
|
|---|
| 16 | EventEmitter.call(this)
|
|---|
| 17 | this._pending = 0
|
|---|
| 18 | this._filterDir = function() { return true }
|
|---|
| 19 | this.go(root)
|
|---|
| 20 | }
|
|---|
| 21 | util.inherits(Walker, EventEmitter)
|
|---|
| 22 |
|
|---|
| 23 | /**
|
|---|
| 24 | * Errors of this type are thrown when the type of a file could not be
|
|---|
| 25 | * determined.
|
|---|
| 26 | */
|
|---|
| 27 | var UnknownFileTypeError = Walker.UnknownFileTypeError = makeError(
|
|---|
| 28 | 'UnknownFileTypeError',
|
|---|
| 29 | 'The type of this file could not be determined.'
|
|---|
| 30 | )
|
|---|
| 31 |
|
|---|
| 32 | /**
|
|---|
| 33 | * Setup a function to filter out directory entries.
|
|---|
| 34 | *
|
|---|
| 35 | * @param fn {Function} a function that will be given a directory name, which
|
|---|
| 36 | * if returns true will include the directory and it's children
|
|---|
| 37 | */
|
|---|
| 38 | Walker.prototype.filterDir = function(fn) {
|
|---|
| 39 | this._filterDir = fn
|
|---|
| 40 | return this
|
|---|
| 41 | }
|
|---|
| 42 |
|
|---|
| 43 | /**
|
|---|
| 44 | * Process a file or directory.
|
|---|
| 45 | */
|
|---|
| 46 | Walker.prototype.go = function(entry) {
|
|---|
| 47 | var that = this
|
|---|
| 48 | this._pending++
|
|---|
| 49 |
|
|---|
| 50 | fs.lstat(entry, function(er, stat) {
|
|---|
| 51 | if (er) {
|
|---|
| 52 | that.emit('error', er, entry, stat)
|
|---|
| 53 | that.doneOne()
|
|---|
| 54 | return
|
|---|
| 55 | }
|
|---|
| 56 |
|
|---|
| 57 | if (stat.isDirectory()) {
|
|---|
| 58 | if (!that._filterDir(entry, stat)) {
|
|---|
| 59 | that.doneOne()
|
|---|
| 60 | } else {
|
|---|
| 61 | fs.readdir(entry, function(er, files) {
|
|---|
| 62 | if (er) {
|
|---|
| 63 | that.emit('error', er, entry, stat)
|
|---|
| 64 | that.doneOne()
|
|---|
| 65 | return
|
|---|
| 66 | }
|
|---|
| 67 |
|
|---|
| 68 | that.emit('entry', entry, stat)
|
|---|
| 69 | that.emit('dir', entry, stat)
|
|---|
| 70 | files.forEach(function(part) {
|
|---|
| 71 | that.go(path.join(entry, part))
|
|---|
| 72 | })
|
|---|
| 73 | that.doneOne()
|
|---|
| 74 | })
|
|---|
| 75 | }
|
|---|
| 76 | } else if (stat.isSymbolicLink()) {
|
|---|
| 77 | that.emit('entry', entry, stat)
|
|---|
| 78 | that.emit('symlink', entry, stat)
|
|---|
| 79 | that.doneOne()
|
|---|
| 80 | } else if (stat.isBlockDevice()) {
|
|---|
| 81 | that.emit('entry', entry, stat)
|
|---|
| 82 | that.emit('blockDevice', entry, stat)
|
|---|
| 83 | that.doneOne()
|
|---|
| 84 | } else if (stat.isCharacterDevice()) {
|
|---|
| 85 | that.emit('entry', entry, stat)
|
|---|
| 86 | that.emit('characterDevice', entry, stat)
|
|---|
| 87 | that.doneOne()
|
|---|
| 88 | } else if (stat.isFIFO()) {
|
|---|
| 89 | that.emit('entry', entry, stat)
|
|---|
| 90 | that.emit('fifo', entry, stat)
|
|---|
| 91 | that.doneOne()
|
|---|
| 92 | } else if (stat.isSocket()) {
|
|---|
| 93 | that.emit('entry', entry, stat)
|
|---|
| 94 | that.emit('socket', entry, stat)
|
|---|
| 95 | that.doneOne()
|
|---|
| 96 | } else if (stat.isFile()) {
|
|---|
| 97 | that.emit('entry', entry, stat)
|
|---|
| 98 | that.emit('file', entry, stat)
|
|---|
| 99 | that.doneOne()
|
|---|
| 100 | } else {
|
|---|
| 101 | that.emit('error', UnknownFileTypeError(), entry, stat)
|
|---|
| 102 | that.doneOne()
|
|---|
| 103 | }
|
|---|
| 104 | })
|
|---|
| 105 | return this
|
|---|
| 106 | }
|
|---|
| 107 |
|
|---|
| 108 | Walker.prototype.doneOne = function() {
|
|---|
| 109 | if (--this._pending === 0) this.emit('end')
|
|---|
| 110 | return this
|
|---|
| 111 | }
|
|---|