dir-writer.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // It is expected that, when .add() returns false, the consumer
  2. // of the DirWriter will pause until a "drain" event occurs. Note
  3. // that this is *almost always going to be the case*, unless the
  4. // thing being written is some sort of unsupported type, and thus
  5. // skipped over.
  6. module.exports = DirWriter
  7. var Writer = require('./writer.js')
  8. var inherits = require('inherits')
  9. var mkdir = require('mkdirp')
  10. var path = require('path')
  11. var collect = require('./collect.js')
  12. inherits(DirWriter, Writer)
  13. function DirWriter (props) {
  14. var self = this
  15. if (!(self instanceof DirWriter)) {
  16. self.error('DirWriter must be called as constructor.', null, true)
  17. }
  18. // should already be established as a Directory type
  19. if (props.type !== 'Directory' || !props.Directory) {
  20. self.error('Non-directory type ' + props.type + ' ' +
  21. JSON.stringify(props), null, true)
  22. }
  23. Writer.call(this, props)
  24. }
  25. DirWriter.prototype._create = function () {
  26. var self = this
  27. mkdir(self._path, Writer.dirmode, function (er) {
  28. if (er) return self.error(er)
  29. // ready to start getting entries!
  30. self.ready = true
  31. self.emit('ready')
  32. self._process()
  33. })
  34. }
  35. // a DirWriter has an add(entry) method, but its .write() doesn't
  36. // do anything. Why a no-op rather than a throw? Because this
  37. // leaves open the door for writing directory metadata for
  38. // gnu/solaris style dumpdirs.
  39. DirWriter.prototype.write = function () {
  40. return true
  41. }
  42. DirWriter.prototype.end = function () {
  43. this._ended = true
  44. this._process()
  45. }
  46. DirWriter.prototype.add = function (entry) {
  47. var self = this
  48. // console.error('\tadd', entry._path, '->', self._path)
  49. collect(entry)
  50. if (!self.ready || self._currentEntry) {
  51. self._buffer.push(entry)
  52. return false
  53. }
  54. // create a new writer, and pipe the incoming entry into it.
  55. if (self._ended) {
  56. return self.error('add after end')
  57. }
  58. self._buffer.push(entry)
  59. self._process()
  60. return this._buffer.length === 0
  61. }
  62. DirWriter.prototype._process = function () {
  63. var self = this
  64. // console.error('DW Process p=%j', self._processing, self.basename)
  65. if (self._processing) return
  66. var entry = self._buffer.shift()
  67. if (!entry) {
  68. // console.error("DW Drain")
  69. self.emit('drain')
  70. if (self._ended) self._finish()
  71. return
  72. }
  73. self._processing = true
  74. // console.error("DW Entry", entry._path)
  75. self.emit('entry', entry)
  76. // ok, add this entry
  77. //
  78. // don't allow recursive copying
  79. var p = entry
  80. var pp
  81. do {
  82. pp = p._path || p.path
  83. if (pp === self.root._path || pp === self._path ||
  84. (pp && pp.indexOf(self._path) === 0)) {
  85. // console.error('DW Exit (recursive)', entry.basename, self._path)
  86. self._processing = false
  87. if (entry._collected) entry.pipe()
  88. return self._process()
  89. }
  90. p = p.parent
  91. } while (p)
  92. // console.error("DW not recursive")
  93. // chop off the entry's root dir, replace with ours
  94. var props = {
  95. parent: self,
  96. root: self.root || self,
  97. type: entry.type,
  98. depth: self.depth + 1
  99. }
  100. pp = entry._path || entry.path || entry.props.path
  101. if (entry.parent) {
  102. pp = pp.substr(entry.parent._path.length + 1)
  103. }
  104. // get rid of any ../../ shenanigans
  105. props.path = path.join(self.path, path.join('/', pp))
  106. // if i have a filter, the child should inherit it.
  107. props.filter = self.filter
  108. // all the rest of the stuff, copy over from the source.
  109. Object.keys(entry.props).forEach(function (k) {
  110. if (!props.hasOwnProperty(k)) {
  111. props[k] = entry.props[k]
  112. }
  113. })
  114. // not sure at this point what kind of writer this is.
  115. var child = self._currentChild = new Writer(props)
  116. child.on('ready', function () {
  117. // console.error("DW Child Ready", child.type, child._path)
  118. // console.error(" resuming", entry._path)
  119. entry.pipe(child)
  120. entry.resume()
  121. })
  122. // XXX Make this work in node.
  123. // Long filenames should not break stuff.
  124. child.on('error', function (er) {
  125. if (child._swallowErrors) {
  126. self.warn(er)
  127. child.emit('end')
  128. child.emit('close')
  129. } else {
  130. self.emit('error', er)
  131. }
  132. })
  133. // we fire _end internally *after* end, so that we don't move on
  134. // until any "end" listeners have had their chance to do stuff.
  135. child.on('close', onend)
  136. var ended = false
  137. function onend () {
  138. if (ended) return
  139. ended = true
  140. // console.error("* DW Child end", child.basename)
  141. self._currentChild = null
  142. self._processing = false
  143. self._process()
  144. }
  145. }