diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..87417a2 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,8 @@ +export function zip (input: string, output: string, cb?: (err: Error | null) => void): void; +export function zip (input: string, output: string, includeBaseDirectory: boolean, cb?: (err: Error | null) => void): void; + +export function zipSync (input: string, output: string, includeBaseDirectory?: boolean): void; + +export function unzip (input: string, output: string, cb?: (err: Error | null) => void): void; + +export function unzipSync (input: string, output: string): void; diff --git a/index.js b/index.js index 52fb9ad..57b96c5 100644 --- a/index.js +++ b/index.js @@ -11,74 +11,75 @@ var fs = require('fs') var os = require('os') var path = require('path') -function zip (inPath, outPath, cb) { - if (!cb) cb = function () {} - if (process.platform === 'win32') { - fs.stat(inPath, function (err, stats) { - if (err) return cb(err) - if (stats.isFile()) { - copyToTemp() - } else { - doZip() - } - }) +function zip (inPath, outPath, includeBaseDirectory, cb) { + if (typeof includeBaseDirectory === 'boolean') { + if (!cb) cb = function () {} + } else if (typeof includeBaseDirectory === 'function') { + cb = includeBaseDirectory + includeBaseDirectory = false } else { - doZip() + includeBaseDirectory = arguments.length === 2 ? false : !!includeBaseDirectory + if (!cb) cb = function () {} } - // Windows zip command cannot zip files, only directories. So move the file into - // a temporary directory before zipping. + fs.stat(inPath, function (err, stats) { + if (err) return cb(err) + if (stats.isFile()) { + includeBaseDirectory = false + copyToTemp() + } else { + doZip() + } + }) + function copyToTemp () { - fs.readFile(inPath, function (err, inFile) { + var tmpPath = path.join(os.tmpdir(), 'cross-zip-' + Date.now()) + fs.mkdir(tmpPath, { recursive: true }, function (err) { if (err) return cb(err) - var tmpPath = path.join(os.tmpdir(), 'cross-zip-' + Date.now()) - fs.mkdir(tmpPath, function (err) { + fs.copyFile(inPath, path.join(tmpPath, path.basename(inPath)), function (err) { if (err) return cb(err) - fs.writeFile(path.join(tmpPath, path.basename(inPath)), inFile, function (err) { - if (err) return cb(err) - inPath = tmpPath - doZip() - }) + + inPath = tmpPath + doZip() }) }) } // Windows zip command does not overwrite existing files. So do it manually first. function doZip () { - if (process.platform === 'win32') { - fs.rmdir(outPath, { recursive: true, maxRetries: 3 }, doZip2) - } else { - doZip2() - } + fs.rmdir(outPath, { recursive: true, maxRetries: 3 }, doZip2) } function doZip2 () { + const { args, cwd } = getZipArgs(inPath, outPath, includeBaseDirectory) var opts = { - cwd: path.dirname(inPath), + cwd: cwd, maxBuffer: Infinity } - cp.execFile(getZipCommand(), getZipArgs(inPath, outPath), opts, function (err) { + cp.execFile(getZipCommand(), args, opts, function (err) { cb(err) }) } } -function zipSync (inPath, outPath) { - if (process.platform === 'win32') { - if (fs.statSync(inPath).isFile()) { - var inFile = fs.readFileSync(inPath) - var tmpPath = path.join(os.tmpdir(), 'cross-zip-' + Date.now()) - fs.mkdirSync(tmpPath) - fs.writeFileSync(path.join(tmpPath, path.basename(inPath)), inFile) - inPath = tmpPath - } - fs.rmdirSync(outPath, { recursive: true, maxRetries: 3 }) +function zipSync (inPath, outPath, includeBaseDirectory) { + includeBaseDirectory = !!includeBaseDirectory + + if (fs.statSync(inPath).isFile()) { + includeBaseDirectory = false + var tmpPath = path.join(os.tmpdir(), 'cross-zip-' + Date.now()) + fs.mkdirSync(tmpPath, { recursive: true }) + fs.copyFileSync(inPath, path.join(tmpPath, path.basename(inPath))) + inPath = tmpPath } + fs.rmdirSync(outPath, { recursive: true, maxRetries: 3 }) + + const { args, cwd } = getZipArgs(inPath, outPath, includeBaseDirectory) var opts = { - cwd: path.dirname(inPath), + cwd: cwd, maxBuffer: Infinity } - cp.execFileSync(getZipCommand(), getZipArgs(inPath, outPath), opts) + cp.execFileSync(getZipCommand(), args, opts) } function unzip (inPath, outPath, cb) { @@ -118,18 +119,38 @@ function quotePath (pathToTransform) { return '"' + pathToTransform + '"' } -function getZipArgs (inPath, outPath) { +/** + * @param {string} inPath + * @param {string} outPath + * @param {boolean} includeBaseDirectory + * @returns {{ args: string[]; cwd: string }} + */ +function getZipArgs (inPath, outPath, includeBaseDirectory) { if (process.platform === 'win32') { - return [ - '-nologo', - '-noprofile', - '-command', '& { param([String]$myInPath, [String]$myOutPath); Add-Type -A "System.IO.Compression.FileSystem"; [IO.Compression.ZipFile]::CreateFromDirectory($myInPath, $myOutPath); exit !$? }', - '-myInPath', quotePath(inPath), - '-myOutPath', quotePath(outPath) - ] + return { + args: [ + '-nologo', + '-noprofile', + '-command', '& { param([String]$myInPath, [String]$myOutPath, [Boolean]$includeBaseDirectory); Add-Type -A "System.IO.Compression.FileSystem"; [IO.Compression.ZipFile]::CreateFromDirectory($myInPath, $myOutPath, [IO.Compression.CompressionLevel]::Fastest, $includeBaseDirectory); exit !$? }', + '-myInPath', quotePath(inPath), + '-myOutPath', quotePath(outPath), + '-includeBaseDirectory', `$${!!includeBaseDirectory}` + ], + cwd: process.cwd() + } } else { - var fileName = path.basename(inPath) - return ['-r', '-y', outPath, fileName] + if (includeBaseDirectory) { + const dir = path.dirname(inPath) + const fileName = path.basename(inPath) + return { + args: ['-r', '-y', outPath, fileName], + cwd: dir + } + } + return { + args: ['-r', '-y', outPath, '.'], + cwd: inPath + } } } diff --git a/test/zip.js b/test/zip.js index 0489bd5..98c303d 100644 --- a/test/zip.js +++ b/test/zip.js @@ -5,6 +5,8 @@ var zip = require('../') var filePath = path.join(__dirname, 'content', 'file.txt') var tmpPath = path.join(__dirname, 'tmp') +var contentPath = path.join(__dirname, 'content') +var tmpContentPath = path.join(__dirname, 'tmp', 'content') fs.mkdirSync(tmpPath, { recursive: true }) @@ -20,6 +22,23 @@ test('zipSync', function (t) { var file = fs.readFileSync(filePath) t.deepEqual(tmpFile, file) + + var contentZipPathIncludeParentFolder = path.join(tmpPath, 'file2.zip') + zip.zipSync(contentPath, contentZipPathIncludeParentFolder, true) + zip.unzipSync(contentZipPathIncludeParentFolder, tmpPath) + t.ok(fs.existsSync(tmpContentPath) && fs.statSync(tmpContentPath).isDirectory()) + t.deepEqual(fs.readFileSync(path.join(tmpContentPath, 'file.txt')), file) + fs.rmdirSync(tmpContentPath, { recursive: true }) + fs.rmdirSync(contentZipPathIncludeParentFolder, { recursive: true }) + + var contentZipPathWithoutParentFolder = path.join(tmpPath, 'file3.zip') + zip.zipSync(contentPath, contentZipPathWithoutParentFolder) + zip.unzipSync(contentZipPathWithoutParentFolder, path.join(tmpPath, 'file3')) + t.ok(!fs.existsSync(tmpContentPath)) + t.deepEqual(fs.readFileSync(path.join(tmpPath, 'file3/file.txt')), file) + fs.rmdirSync(contentZipPathWithoutParentFolder, { recursive: true }) + fs.rmdirSync(path.join(tmpPath, 'file3'), { recursive: true }) + t.end() }) @@ -45,3 +64,55 @@ test('zip', function (t) { }) }) }) + +test('zip dir with parent folder', function (t) { + t.plan(6) + + var contentZipPathIncludeParentFolder = path.join(tmpPath, 'file2.zip') + zip.zip(contentPath, contentZipPathIncludeParentFolder, true, function (err) { + t.error(err) + + zip.unzip(contentZipPathIncludeParentFolder, tmpPath, function (err) { + t.error(err) + + t.ok(fs.existsSync(tmpContentPath) && fs.statSync(tmpContentPath).isDirectory()) + var file = fs.readFileSync(filePath) + t.deepEqual(fs.readFileSync(path.join(tmpContentPath, 'file.txt')), file) + fs.rmdir(tmpContentPath, { recursive: true }, function (err) { + t.error(err) + + fs.rmdir(contentZipPathIncludeParentFolder, { recursive: true }, function (err) { + t.error(err) + }) + }) + }) + }) +}) + +test('zip dir without parent folder', function (t) { + t.plan(7) + + var contentZipPathWithoutParentFolder = path.join(tmpPath, 'file3.zip') + zip.zip(contentPath, contentZipPathWithoutParentFolder, function (err) { + t.error(err) + + zip.unzip(contentZipPathWithoutParentFolder, path.join(tmpPath, 'file3'), function (err) { + t.error(err) + + t.ok(!fs.existsSync(tmpContentPath)) + var file = fs.readFileSync(filePath) + t.deepEqual(fs.readFileSync(path.join(tmpPath, 'file3/file.txt')), file) + fs.rmdir(tmpContentPath, { recursive: true }, function (err) { + t.error(err) + + fs.rmdir(contentZipPathWithoutParentFolder, { recursive: true }, function (err) { + t.error(err) + + fs.rmdir(path.join(tmpPath, 'file3'), { recursive: true }, function (err) { + t.error(err) + }) + }) + }) + }) + }) +})