a Node.js library to extract, transform and re-pack tarball entries in form of stream
npm install tar-transform
yarn add tar-transform
const tt = require("tar-transform");
const fetch = require("node-fetch");
const extractStream = tt.extract({
// boolean | "auto". default is "auto".
// indicates whether a gzipped tarball file stream is written to this stream
gzip: true,
});
const resp = await fetch(
"https://codeload.github.com/EqualMa/tar-transform/tar.gz/master",
);
resp.body.pipe(extractStream);
const entries = [];
// `for await ... of ...` is an easy way to consume a stream
// this syntax is available on Node.js >= 10
for await (const entry of extractStream) {
entries.push(entry.headers);
// NOTE: remember to resume the current entry stream to continue
entry.stream.resume();
}
console.log(entries);
const tt = require("tar-transform");
const fetch = require("node-fetch");
const { Readable } = require("stream");
const fs = require("fs");
const packStream = tt.pack({
// boolean | zlib.ZlibOptions
// indicates whether to gzip the tarball
gzip: true,
});
const imageResp = await fetch("https://github.com/EqualMa.png");
const imageSize = parseInt(imageResp.headers.get("Content-Length"));
Readable.from([
{ headers: { name: "README.md" }, content: "# tar-transform" },
{ headers: { name: "hello/world.txt" }, content: "Hello World!" },
{ headers: { name: "emptyDir", type: "directory" } },
{
headers: { name: "author-avatar.png" },
stream: imageResp.body,
},
])
.pipe(packStream)
.pipe(fs.createWriteStream("tar-transform-pack-demo.tgz"));
const tt = require("tar-transform");
const tgzStream = (
await fetch(
"https://runkit.io/equalma/tar-transform-pack/branches/master/tgz",
)
).body;
// extract a stream of tar entries from a tarball
const extractStream = tt.extract();
// transform
const transformStream = tt.transform({
onEntry(entry) {
this.push(entry);
},
});
// repack to tgz
const packStream = tt.pack({ gzip: true });
tgzStream
.pipe(extractStream)
.pipe(transformStream)
.pipe(packStream)
.pipe(require("fs").createWriteStream("tar-transform-demo.tgz"))
.on("error", console.error);
The following example prefixes the path of each entry with my-root/
const transformStream = tt.transform({
onEntry(entry) {
const headers = this.util.headersWithNewName(
entry.headers,
"my-root/" + entry.headers.name,
);
this.push({ ...entry, headers });
},
});
The following example prefixes content of *.txt
files with HACKED BY tar-transform
const transformStream = tt.transform({
async onEntry(entry) {
if (entry.headers.name.endsWith(".txt")) {
const oldContent = await this.util.stringContentOfTarEntry(entry);
const newContent = "HACKED BY tar-transform\n" + oldContent;
this.push({
headers: entry.headers,
content: newContent,
});
} else {
this.push(entry);
}
},
});
The following example removes all *.png
files
const transformStream = tt.transform({
onEntry(entry) {
if (entry.headers.name.endsWith(".png")) {
this.pass(entry);
} else {
this.push(entry);
}
},
});
The following example adds a file named file-structure.txt
containing the directory structure
const transformStream = tt.transform({
initCtx: [],
onEntry(entry) {
this.ctx.push([entry.headers.name, entry.headers.type]);
this.push(entry);
},
onEnd() {
this.push({
headers: { name: "file-structure.txt" },
content: this.ctx.map(([name, type]) => `[${type}]\t${name}`).join("\n"),
});
},
});