diff --git a/README.md b/README.md index 398087b..78a5eca 100644 --- a/README.md +++ b/README.md @@ -56,10 +56,8 @@ been saved to disk. * `saveTo` A path to save the buffer to. * `filter` A function that is called for all items to determine whether or not they should be added to the zip buffer. Function is called with the `fullPath` and a `stats` object ([fs.Stats](http://nodejs.org/api/fs.html#fs_class_fs_stats)). Return true to add the item; false otherwise. To include files within directories, directories must also pass this filter. * `each` A function that is called everytime a file or directory is added to the zip. - -## TODO - -* Add an option to not add empty directories if there are no valid children inside +* `omitEmptyDirs` Prevents empty directories from being included in the zip file. If the root folder to pack is empty, the whole operation will fail. +* `allowEmptyRoot` Lets the empty root directory be included in the zip file, if the`omitEmptyDirs` is set to `true`. ## license diff --git a/index.js b/index.js index 3fa84a0..a2e76be 100644 --- a/index.js +++ b/index.js @@ -41,6 +41,14 @@ function zipBuffer (rootDir, options, callback) { dive(rootDir, function (err) { if (err) return callback(err); + if (options.omitEmptyDirs) { + if (!removeEmptyDirs(zip)) { + if (!options.allowEmptyRoot) { + return callback(new Error('Empty root directory not allowed.')); + } + } + } + callback(null, zip.generate({ compression: 'DEFLATE', type: 'nodebuffer' @@ -92,4 +100,16 @@ function zipBuffer (rootDir, options, callback) { } }); } + + function removeEmptyDirs (parentZip) { + var folders = parentZip.folder(/./); + for (var i = 0; i < folders.length; ++i) { + var folderName = path.basename(folders[i].name); + var childZip = parentZip.folder(folderName); + if (!removeEmptyDirs(childZip)) { + parentZip.remove(folderName); + } + } + return parentZip.file(/./).length > 0 || parentZip.folder(/./).length > 0; + } } diff --git a/test/zip-dir.test.js b/test/zip-dir.test.js index 6c927dc..b070ff6 100644 --- a/test/zip-dir.test.js +++ b/test/zip-dir.test.js @@ -178,6 +178,59 @@ describe("zip-dir", function () { }); }); }); + + describe('if empty directories should be omitted', function () { + afterEach(cleanUp); + + it('empty root directory is not allowed by default', function (done) { + zipDir(sampleZipPath, { + filter: nothing, omitEmptyDirs: true + }, function (err) { + expect(err).to.be.ok; + done(); + }); + }); + + it('empty root directory can be allowed', function (done) { + zipDir(sampleZipPath, { + saveTo: xpiPath, + filter: nothing, + omitEmptyDirs: true, + allowEmptyRoot: true + }, function () { + fs.stat(xpiPath, function (err, stat) { + expect(err).to.not.be.ok; + expect(stat.size).to.equal(22); + done(); + }); + }); + }); + + it('empty nested directory will be omitted', function (done) { + zipAndUnzip({ + saveTo: xpiPath, filter: belowDir, omitEmptyDirs: true + }, function () { + var files = [ + 'file1.json', + 'tiny.gif' + ]; + files.forEach(compareFiles); + + fs.stat(path.join(outputPath, 'dir'), function (err, stat) { + expect(err).to.be.ok; + done(); + }); + }); + + function belowDir (name) { + return !/\/dir\//.test(name) + } + }); + + function nothing () { + return false + } + }); }); function compareFiles (file) {