walker.js 2.8 KB

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