# all files / lib/ dir-writer.js

15.91% Statements 14/88
0% Branches 0/43
0% Functions 0/11
16.87% Lines 14/83

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175

    1×

    1×
    1×
    1×
    1×
    1×

    1×

    1×

    1×

    1×

    1×

    1×

    1×

    1×

     // It is expected that, when .add() returns false, the consumer
    // of the DirWriter will pause until a "drain" event occurs. Note
    // that this is *almost always going to be the case*, unless the
    // thing being written is some sort of unsupported type, and thus
    // skipped over.

    module.exports = DirWriter

    var Writer = require('./writer.js')
    var inherits = require('inherits')
    var mkdir = require('mkdirp')
    var path = require('path')
    var collect = require('./collect.js')

    inherits(DirWriter, Writer)

    function DirWriter (props) {
      var self = this
      if (!(self instanceof DirWriter)) {
        self.error('DirWriter must be called as constructor.', null, true)
      }

      // should already be established as a Directory type
      if (props.type !== 'Directory' || !props.Directory) {
        self.error('Non-directory type ' + props.type + ' ' +
          JSON.stringify(props), null, true)
      }

      Writer.call(this, props)
    }

    DirWriter.prototype._create = function () {
      var self = this
      mkdir(self._path, Writer.dirmode, function (er) {
        if (er) return self.error(er)
        // ready to start getting entries!
        self.ready = true
        self.emit('ready')
        self._process()
      })
    }

    // a DirWriter has an add(entry) method, but its .write() doesn't
    // do anything.  Why a no-op rather than a throw?  Because this
    // leaves open the door for writing directory metadata for
    // gnu/solaris style dumpdirs.
    DirWriter.prototype.write = function () {
      return true
    }

    DirWriter.prototype.end = function () {
      this._ended = true
      this._process()
    }

    DirWriter.prototype.add = function (entry) {
      var self = this

      // console.error('\tadd', entry._path, '->', self._path)
      collect(entry)
      if (!self.ready || self._currentEntry) {
        self._buffer.push(entry)
        return false
      }

      // create a new writer, and pipe the incoming entry into it.
      if (self._ended) {
        return self.error('add after end')
      }

      self._buffer.push(entry)
      self._process()

      return this._buffer.length === 0
    }

    DirWriter.prototype._process = function () {
      var self = this

      // console.error('DW Process p=%j', self._processing, self.basename)

      if (self._processing) return

      var entry = self._buffer.shift()
      if (!entry) {
        // console.error("DW Drain")
        self.emit('drain')
        if (self._ended) self._finish()
        return
      }

      self._processing = true
      // console.error("DW Entry", entry._path)

      self.emit('entry', entry)

      // ok, add this entry
      //
      // don't allow recursive copying
      var p = entry
      var pp
      do {
        pp = p._path || p.path
        if (pp === self.root._path || pp === self._path ||
          (pp && pp.indexOf(self._path) === 0)) {
          // console.error('DW Exit (recursive)', entry.basename, self._path)
          self._processing = false
          if (entry._collected) entry.pipe()
          return self._process()
        }
        p = p.parent
      } while (p)

      // console.error("DW not recursive")

      // chop off the entry's root dir, replace with ours
      var props = {
        parent: self,
        root: self.root || self,
        type: entry.type,
        depth: self.depth + 1
      }

      pp = entry._path || entry.path || entry.props.path
      if (entry.parent) {
        pp = pp.substr(entry.parent._path.length + 1)
      }
      // get rid of any ../../ shenanigans
      props.path = path.join(self.path, path.join('/', pp))

      // if i have a filter, the child should inherit it.
      props.filter = self.filter

      // all the rest of the stuff, copy over from the source.
      Object.keys(entry.props).forEach(function (k) {
        if (!props.hasOwnProperty(k)) {
          props[k] = entry.props[k]
        }
      })

      // not sure at this point what kind of writer this is.
      var child = self._currentChild = new Writer(props)
      child.on('ready', function () {
        // console.error("DW Child Ready", child.type, child._path)
        // console.error("  resuming", entry._path)
        entry.pipe(child)
        entry.resume()
      })

      // XXX Make this work in node.
      // Long filenames should not break stuff.
      child.on('error', function (er) {
        if (child._swallowErrors) {
          self.warn(er)
          child.emit('end')
          child.emit('close')
        } else {
          self.emit('error', er)
        }
      })

      // we fire _end internally *after* end, so that we don't move on
      // until any "end" listeners have had their chance to do stuff.
      child.on('close', onend)
      var ended = false
      function onend () {
        if (ended) return
        ended = true
        // console.error("* DW Child end", child.basename)
        self._currentChild = null
        self._processing = false
        self._process()
      }
    }