// pipe in an fstream, and it'll make a tarball. // key-value pair argument is global extended header props. module.exports = Pack var EntryWriter = require("./entry-writer.js") , Stream = require("stream").Stream , path = require("path") , inherits = require("inherits") , GlobalHeaderWriter = require("./global-header-writer.js") , collect = require("fstream").collect , eof = new Buffer(512) for (var i = 0; i < 512; i ++) eof[i] = 0 inherits(Pack, Stream) function Pack (props) { // console.error("-- p ctor") var me = this if (!(me instanceof Pack)) return new Pack(props) if (props) me._noProprietary = props.noProprietary else me._noProprietary = false me._global = props me.readable = true me.writable = true me._buffer = [] // console.error("-- -- set current to null in ctor") me._currentEntry = null me._processing = false me._pipeRoot = null me.on("pipe", function (src) { if (src.root === me._pipeRoot) return me._pipeRoot = src src.on("end", function () { me._pipeRoot = null }) me.add(src) }) } Pack.prototype.addGlobal = function (props) { // console.error("-- p addGlobal") if (this._didGlobal) return this._didGlobal = true var me = this GlobalHeaderWriter(props) .on("data", function (c) { me.emit("data", c) }) .end() } Pack.prototype.add = function (stream) { if (this._global && !this._didGlobal) this.addGlobal(this._global) if (this._ended) return this.emit("error", new Error("add after end")) collect(stream) this._buffer.push(stream) this._process() this._needDrain = this._buffer.length > 0 return !this._needDrain } Pack.prototype.pause = function () { this._paused = true if (this._currentEntry) this._currentEntry.pause() this.emit("pause") } Pack.prototype.resume = function () { this._paused = false if (this._currentEntry) this._currentEntry.resume() this.emit("resume") this._process() } Pack.prototype.end = function () { this._ended = true this._buffer.push(eof) this._process() } Pack.prototype._process = function () { var me = this if (me._paused || me._processing) { return } var entry = me._buffer.shift() if (!entry) { if (me._needDrain) { me.emit("drain") } return } if (entry.ready === false) { // console.error("-- entry is not ready", entry) me._buffer.unshift(entry) entry.on("ready", function () { // console.error("-- -- ready!", entry) me._process() }) return } me._processing = true if (entry === eof) { // need 2 ending null blocks. me.emit("data", eof) me.emit("data", eof) me.emit("end") me.emit("close") return } // Change the path to be relative to the root dir that was // added to the tarball. // // XXX This should be more like how -C works, so you can // explicitly set a root dir, and also explicitly set a pathname // in the tarball to use. That way we can skip a lot of extra // work when resolving symlinks for bundled dependencies in npm. var root = path.dirname((entry.root || entry).path) var wprops = {} Object.keys(entry.props).forEach(function (k) { wprops[k] = entry.props[k] }) if (me._noProprietary) wprops.noProprietary = true wprops.path = path.relative(root, entry.path) // actually not a matter of opinion or taste. if (process.platform === "win32") { wprops.path = wprops.path.replace(/\\/g, "/") } switch (wprops.type) { // sockets not supported case "Socket": return case "Directory": wprops.path += "/" wprops.size = 0 break case "Link": var lp = path.resolve(path.dirname(entry.path), entry.linkpath) wprops.linkpath = path.relative(root, lp) || "." wprops.size = 0 break case "SymbolicLink": var lp = path.resolve(path.dirname(entry.path), entry.linkpath) wprops.linkpath = path.relative(path.dirname(entry.path), lp) || "." wprops.size = 0 break } // console.error("-- new writer", wprops) // if (!wprops.type) { // // console.error("-- no type?", entry.constructor.name, entry) // } // console.error("-- -- set current to new writer", wprops.path) var writer = me._currentEntry = EntryWriter(wprops) writer.parent = me // writer.on("end", function () { // // console.error("-- -- writer end", writer.path) // }) writer.on("data", function (c) { me.emit("data", c) }) writer.on("header", function () { Buffer.prototype.toJSON = function () { return this.toString().split(/\0/).join(".") } // console.error("-- -- writer header %j", writer.props) if (writer.props.size === 0) nextEntry() }) writer.on("close", nextEntry) var ended = false function nextEntry () { if (ended) return ended = true // console.error("-- -- writer close", writer.path) // console.error("-- -- set current to null", wprops.path) me._currentEntry = null me._processing = false me._process() } writer.on("error", function (er) { // console.error("-- -- writer error", writer.path) me.emit("error", er) }) // if it's the root, then there's no need to add its entries, // or data, since they'll be added directly. if (entry === me._pipeRoot) { // console.error("-- is the root, don't auto-add") writer.add = null } entry.pipe(writer) } Pack.prototype.destroy = function () {} Pack.prototype.write = function () {}