jszip-cli
Version:
npm interface for JSZip
154 lines (145 loc) • 5.02 kB
JavaScript
var fs = require('fs');
var path = require('path');
var JSZip = require('jszip');
var program = require('commander');
var recursive = require('recursive-readdir');
var ProgressBar = require('progress');
var jsonfile = require('jsonfile');
program
.arguments('<entries...>')
// .option('-d, --directory', 'Input entry is a directory to compress')
.option('-o, --output <output>', 'Output file')
.option('-l, --level <compressLevel>', 'Compress level')
.option('-c, --config', 'Entry is a config file')
// .option('-r, --regex <regex>', 'Regex')
// .option('-u, --unzip', 'Unzip mode')
.action(function(filesAndDirs, options) {
if (options.config) {
jsonfile.readFile(filesAndDirs[0], (err, obj) => {
if (!err) {
startProcessing(obj.entities, Object.assign({}, options, obj.options));
} else {
console.error(`unable to read config file ${options.config}`);
}
});
} else {
startProcessing(filesAndDirs, options);
}
})
.parse(process.argv);
function startProcessing(filesAndDirs, options) {
const files = [];
const dirs = [];
let i = 0;
filesAndDirs.forEach(f => fs.stat(f.name || f, (err, stats) => {
if (!err) {
if (stats.isFile()) {
files.push(f);
} else {
dirs.push(f);
}
}
if (++i === filesAndDirs.length) {
const zip = new JSZip();
const finalize = () => compress(zip, options.output || `${filesAndDirs[0]}.zip`, options.level);
let j = 0;
processFiles(zip, files, () => {if (++j === 2 ) finalize(); })
processDirectories(zip, dirs, () => {if (++j === 2 ) finalize(); })
}
}));
}
function processFiles(zip, files, completeCallback) {
// console.log("files:", files);
if (files.length) {
readFiles(
files,
(fileInfo, data) => addFileToZip(zip, fileInfo, data, true),
() => completeCallback()
);
} else {
completeCallback();
}
}
function processDirectories(zip, dirs, completeCallback) {
// console.log("dirs:", dirs);
if (dirs.length) {
let i = 0;
dirs.forEach(dir => {
if (!dir.flatten && !dir.root) {
zip.folder(dir.name || dir);
}
recursive(dir.name || dir, (errors, files) => {
if (!errors) {
readFiles(
files,
(fileInfo, data) => addFileToZip(zip, fileInfo, data, dir.flatten, dir.root ? dir.name : ""),
() => { if (++i === dirs.length) { completeCallback(); } }
);
} else {
console.log("failed to read files", errors);
}
});
});
} else {
completeCallback();
}
}
function addFileToZip(zip, fileInfo, data, flatten, root) {
// console.log("add file", fileInfo.dir);
if (flatten) {
zip.file(fileInfo.base, data);
} else {
zip.folder(fileInfo.dir.replace(path.normalize(root || ""), "").replace(/^((..)?\\)+/, "")).file(fileInfo.base, data);
}
}
function readFiles(files, fileCallback, completeCallback) {
files.forEach((file, i) => {
var fileInfo = path.parse(file);
fs.readFile(file, (err, data) => {
if (!err) {
fileCallback(fileInfo, data);
if (i === files.length - 1) {
completeCallback();
}
} else {
console.log("filed to read file", err);
}
});
});
}
function compress(jszip, outFile, level) {
var bar = createProgressBar();
var lastPercent = 0;
jszip.generateAsync(
{ type: "nodebuffer", compression: "DEFLATE", streamFiles: true, level: level || 6 },
({ currentFile, percent }) => {
var diff = Math.floor(percent) - Math.floor(lastPercent);
if (diff) {
bar.tick(diff);
lastPercent = Math.floor(percent);
}
}
)
.then(function(content) {
fs.writeFile(outFile, content, () => console.log(" done"));
})
.catch((error) => {
console.log("failed to create zip", error);
});
}
function createProgressBar() {
var bar = new ProgressBar(' compressing [:bar] :percent :elapseds', {
complete: '=',
incomplete: ' ',
width: 20,
total: 100
});
return bar;
}
function logProgress(file, progress) {
if (!file) return;
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(`${progress}% ${file}`);
}