pack.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. // pipe in an fstream, and it'll make a tarball.
  2. // key-value pair argument is global extended header props.
  3. module.exports = Pack
  4. var EntryWriter = require("./entry-writer.js")
  5. , Stream = require("stream").Stream
  6. , path = require("path")
  7. , inherits = require("inherits")
  8. , GlobalHeaderWriter = require("./global-header-writer.js")
  9. , collect = require("fstream").collect
  10. , eof = new Buffer(512)
  11. for (var i = 0; i < 512; i ++) eof[i] = 0
  12. inherits(Pack, Stream)
  13. function Pack (props) {
  14. // console.error("-- p ctor")
  15. var me = this
  16. if (!(me instanceof Pack)) return new Pack(props)
  17. if (props) me._noProprietary = props.noProprietary
  18. else me._noProprietary = false
  19. me._global = props
  20. me.readable = true
  21. me.writable = true
  22. me._buffer = []
  23. // console.error("-- -- set current to null in ctor")
  24. me._currentEntry = null
  25. me._processing = false
  26. me._pipeRoot = null
  27. me.on("pipe", function (src) {
  28. if (src.root === me._pipeRoot) return
  29. me._pipeRoot = src
  30. src.on("end", function () {
  31. me._pipeRoot = null
  32. })
  33. me.add(src)
  34. })
  35. }
  36. Pack.prototype.addGlobal = function (props) {
  37. // console.error("-- p addGlobal")
  38. if (this._didGlobal) return
  39. this._didGlobal = true
  40. var me = this
  41. GlobalHeaderWriter(props)
  42. .on("data", function (c) {
  43. me.emit("data", c)
  44. })
  45. .end()
  46. }
  47. Pack.prototype.add = function (stream) {
  48. if (this._global && !this._didGlobal) this.addGlobal(this._global)
  49. if (this._ended) return this.emit("error", new Error("add after end"))
  50. collect(stream)
  51. this._buffer.push(stream)
  52. this._process()
  53. this._needDrain = this._buffer.length > 0
  54. return !this._needDrain
  55. }
  56. Pack.prototype.pause = function () {
  57. this._paused = true
  58. if (this._currentEntry) this._currentEntry.pause()
  59. this.emit("pause")
  60. }
  61. Pack.prototype.resume = function () {
  62. this._paused = false
  63. if (this._currentEntry) this._currentEntry.resume()
  64. this.emit("resume")
  65. this._process()
  66. }
  67. Pack.prototype.end = function () {
  68. this._ended = true
  69. this._buffer.push(eof)
  70. this._process()
  71. }
  72. Pack.prototype._process = function () {
  73. var me = this
  74. if (me._paused || me._processing) {
  75. return
  76. }
  77. var entry = me._buffer.shift()
  78. if (!entry) {
  79. if (me._needDrain) {
  80. me.emit("drain")
  81. }
  82. return
  83. }
  84. if (entry.ready === false) {
  85. // console.error("-- entry is not ready", entry)
  86. me._buffer.unshift(entry)
  87. entry.on("ready", function () {
  88. // console.error("-- -- ready!", entry)
  89. me._process()
  90. })
  91. return
  92. }
  93. me._processing = true
  94. if (entry === eof) {
  95. // need 2 ending null blocks.
  96. me.emit("data", eof)
  97. me.emit("data", eof)
  98. me.emit("end")
  99. me.emit("close")
  100. return
  101. }
  102. // Change the path to be relative to the root dir that was
  103. // added to the tarball.
  104. //
  105. // XXX This should be more like how -C works, so you can
  106. // explicitly set a root dir, and also explicitly set a pathname
  107. // in the tarball to use. That way we can skip a lot of extra
  108. // work when resolving symlinks for bundled dependencies in npm.
  109. var root = path.dirname((entry.root || entry).path);
  110. if (me._global && me._global.fromBase && entry.root && entry.root.path) {
  111. // user set 'fromBase: true' indicating tar root should be directory itself
  112. root = entry.root.path;
  113. }
  114. var wprops = {}
  115. Object.keys(entry.props || {}).forEach(function (k) {
  116. wprops[k] = entry.props[k]
  117. })
  118. if (me._noProprietary) wprops.noProprietary = true
  119. wprops.path = path.relative(root, entry.path || '')
  120. // actually not a matter of opinion or taste.
  121. if (process.platform === "win32") {
  122. wprops.path = wprops.path.replace(/\\/g, "/")
  123. }
  124. if (!wprops.type)
  125. wprops.type = 'Directory'
  126. switch (wprops.type) {
  127. // sockets not supported
  128. case "Socket":
  129. return
  130. case "Directory":
  131. wprops.path += "/"
  132. wprops.size = 0
  133. break
  134. case "Link":
  135. var lp = path.resolve(path.dirname(entry.path), entry.linkpath)
  136. wprops.linkpath = path.relative(root, lp) || "."
  137. wprops.size = 0
  138. break
  139. case "SymbolicLink":
  140. var lp = path.resolve(path.dirname(entry.path), entry.linkpath)
  141. wprops.linkpath = path.relative(path.dirname(entry.path), lp) || "."
  142. wprops.size = 0
  143. break
  144. }
  145. // console.error("-- new writer", wprops)
  146. // if (!wprops.type) {
  147. // // console.error("-- no type?", entry.constructor.name, entry)
  148. // }
  149. // console.error("-- -- set current to new writer", wprops.path)
  150. var writer = me._currentEntry = EntryWriter(wprops)
  151. writer.parent = me
  152. // writer.on("end", function () {
  153. // // console.error("-- -- writer end", writer.path)
  154. // })
  155. writer.on("data", function (c) {
  156. me.emit("data", c)
  157. })
  158. writer.on("header", function () {
  159. Buffer.prototype.toJSON = function () {
  160. return this.toString().split(/\0/).join(".")
  161. }
  162. // console.error("-- -- writer header %j", writer.props)
  163. if (writer.props.size === 0) nextEntry()
  164. })
  165. writer.on("close", nextEntry)
  166. var ended = false
  167. function nextEntry () {
  168. if (ended) return
  169. ended = true
  170. // console.error("-- -- writer close", writer.path)
  171. // console.error("-- -- set current to null", wprops.path)
  172. me._currentEntry = null
  173. me._processing = false
  174. me._process()
  175. }
  176. writer.on("error", function (er) {
  177. // console.error("-- -- writer error", writer.path)
  178. me.emit("error", er)
  179. })
  180. // if it's the root, then there's no need to add its entries,
  181. // or data, since they'll be added directly.
  182. if (entry === me._pipeRoot) {
  183. // console.error("-- is the root, don't auto-add")
  184. writer.add = null
  185. }
  186. entry.pipe(writer)
  187. }
  188. Pack.prototype.destroy = function () {}
  189. Pack.prototype.write = function () {}