├── test ├── src │ ├── spawned.js │ ├── hi.jpg │ └── app.js └── test.js ├── .npmignore ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── package.json ├── LICENSE ├── README.md └── index.js /test/src/spawned.js: -------------------------------------------------------------------------------- 1 | var foo = 'bar'; 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Everything 2 | * 3 | 4 | !index.js 5 | -------------------------------------------------------------------------------- /test/src/hi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erikdesjardins/zip-webpack-plugin/master/test/src/hi.jpg -------------------------------------------------------------------------------- /test/src/app.js: -------------------------------------------------------------------------------- 1 | require('spawn-loader?name=[name].js!./spawned.js'); 2 | require('file-loader?name=subdir/bye.jpg!./hi.jpg'); 3 | 4 | var a = 'b'; 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - v*.*.* 9 | pull_request: 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v1 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: '12.x' 19 | registry-url: 'https://registry.npmjs.org' 20 | - run: npm install 21 | - run: npm test 22 | - run: npm publish 23 | if: startsWith(github.ref, 'refs/tags/') 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | node_modules 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional REPL history 36 | .node_repl_history 37 | 38 | # IntelliJ 39 | *.iml 40 | .idea 41 | 42 | # dist 43 | dist/ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zip-webpack-plugin", 3 | "version": "4.0.3", 4 | "description": "Webpack plugin to zip up emitted files.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "ava test/test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/erikdesjardins/zip-webpack-plugin.git" 12 | }, 13 | "keywords": [ 14 | "webpack" 15 | ], 16 | "author": "Erik Desjardins", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/erikdesjardins/zip-webpack-plugin/issues" 20 | }, 21 | "homepage": "https://github.com/erikdesjardins/zip-webpack-plugin#readme", 22 | "dependencies": { 23 | "yazl": "^2.5.1" 24 | }, 25 | "peerDependencies": { 26 | "webpack": "^4.0.0 || ^5.0.0", 27 | "webpack-sources": "*" 28 | }, 29 | "devDependencies": { 30 | "ava": "^3.14.0", 31 | "file-loader": "^6.2.0", 32 | "mkdirp": "^1.0.4", 33 | "rimraf": "^3.0.2", 34 | "spawn-loader": "^7.0.0", 35 | "webpack": "^5.11.1", 36 | "yauzl": "^2.10.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Erik Desjardins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zip-webpack-plugin 2 | 3 | Webpack plugin to zip up emitted files. 4 | 5 | Compresses all assets into a zip file. 6 | 7 | ## Installation 8 | 9 | For Webpack 4 / 5: 10 | 11 | `npm install --save-dev zip-webpack-plugin` 12 | 13 | For Webpack 3: 14 | 15 | `npm install --save-dev zip-webpack-plugin@2.0.0` 16 | 17 | ## Usage 18 | 19 | **webpack.config.js** 20 | 21 | ```js 22 | var ZipPlugin = require('zip-webpack-plugin'); 23 | 24 | module.exports = { 25 | // ... 26 | output: { 27 | path: path.join(__dirname, 'dist'), 28 | filename: 'bundle.js' 29 | }, 30 | plugins: [ 31 | new ZipPlugin({ 32 | // OPTIONAL: defaults to the Webpack output path (above) 33 | // can be relative (to Webpack output path) or absolute 34 | path: 'zip', 35 | 36 | // OPTIONAL: defaults to the Webpack output filename (above) or, 37 | // if not present, the basename of the path 38 | filename: 'my_app.zip', 39 | 40 | // OPTIONAL: defaults to 'zip' 41 | // the file extension to use instead of 'zip' 42 | extension: 'ext', 43 | 44 | // OPTIONAL: defaults to the empty string 45 | // the prefix for the files included in the zip file 46 | pathPrefix: 'relative/path', 47 | 48 | // OPTIONAL: defaults to the identity function 49 | // a function mapping asset paths to new paths 50 | pathMapper: function(assetPath) { 51 | // put all pngs in an `images` subdir 52 | if (assetPath.endsWith('.png')) 53 | return path.join(path.dirname(assetPath), 'images', path.basename(assetPath)); 54 | return assetPath; 55 | }, 56 | 57 | // OPTIONAL: defaults to including everything 58 | // can be a string, a RegExp, or an array of strings and RegExps 59 | include: [/\.js$/], 60 | 61 | // OPTIONAL: defaults to excluding nothing 62 | // can be a string, a RegExp, or an array of strings and RegExps 63 | // if a file matches both include and exclude, exclude takes precedence 64 | exclude: [/\.png$/, /\.html$/], 65 | 66 | // yazl Options 67 | 68 | // OPTIONAL: see https://github.com/thejoshwolfe/yazl#addfilerealpath-metadatapath-options 69 | fileOptions: { 70 | mtime: new Date(), 71 | mode: 0o100664, 72 | compress: true, 73 | forceZip64Format: false, 74 | }, 75 | 76 | // OPTIONAL: see https://github.com/thejoshwolfe/yazl#endoptions-finalsizecallback 77 | zipOptions: { 78 | forceZip64Format: false, 79 | }, 80 | }) 81 | ] 82 | }; 83 | ``` 84 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Erik Desjardins 3 | * See LICENSE file in root directory for full license. 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const path = require('path'); 9 | const ModuleFilenameHelpers = require('webpack/lib/ModuleFilenameHelpers'); 10 | const webpack = require('webpack'); 11 | // Webpack 5 exposes the sources property to ensure the right version of webpack-sources is used. 12 | // require('webpack-sources') approach may result in the "Cannot find module 'webpack-sources'" error. 13 | const webpackSources = webpack.sources || require('webpack-sources'); 14 | const yazl = require('yazl'); 15 | 16 | function ZipPlugin(options) { 17 | this.options = options || {}; 18 | } 19 | 20 | ZipPlugin.prototype.apply = function(compiler) { 21 | const options = this.options; 22 | const isWebpack4 = webpack.version.startsWith('4.'); 23 | // Prefer `compiler.webpack` instead of webpack or webpack-sources import (rspack compatibility hack) 24 | const { RawSource } = compiler.webpack && compiler.webpack.sources || webpackSources; 25 | 26 | if (options.pathPrefix && path.isAbsolute(options.pathPrefix)) { 27 | throw new Error('"pathPrefix" must be a relative path'); 28 | } 29 | 30 | const process = function(compilation, callback) { 31 | // assets from child compilers will be included in the parent 32 | // so we should not run in child compilers 33 | if (compilation.compiler.isChild()) { 34 | callback(); 35 | return; 36 | } 37 | 38 | const zipFile = new yazl.ZipFile(); 39 | 40 | const pathPrefix = options.pathPrefix || ''; 41 | const pathMapper = options.pathMapper || function(x) { return x; }; 42 | 43 | // populate the zip file with each asset 44 | for (const nameAndPath of Object.keys(compilation.assets)) { 45 | 46 | // match against include and exclude, which may be strings, regexes, arrays of the previous or omitted 47 | if (!ModuleFilenameHelpers.matchObject({ include: options.include, exclude: options.exclude }, nameAndPath)) continue; 48 | 49 | const source = compilation.assets[nameAndPath].source(); 50 | 51 | zipFile.addBuffer( 52 | Buffer.isBuffer(source) ? source : (Buffer.from ? Buffer.from(source) : new Buffer(source)), 53 | path.join(pathPrefix, pathMapper(nameAndPath)), 54 | options.fileOptions 55 | ); 56 | } 57 | 58 | zipFile.end(options.zipOptions); 59 | 60 | // accumulate each buffer containing a part of the zip file 61 | const bufs = []; 62 | 63 | zipFile.outputStream.on('data', function(buf) { 64 | bufs.push(buf); 65 | }); 66 | 67 | zipFile.outputStream.on('end', function() { 68 | // default to webpack's root output path if no path provided 69 | const outputPath = options.path || compilation.options.output.path; 70 | // default to webpack root filename if no filename provided, else the basename of the output path 71 | const outputFilename = options.filename || compilation.options.output.filename || path.basename(outputPath); 72 | 73 | const extension = '.' + (options.extension || 'zip'); 74 | 75 | // combine the output path and filename 76 | const outputPathAndFilename = path.resolve( 77 | compilation.options.output.path, // ...supporting both absolute and relative paths 78 | outputPath, 79 | path.basename(outputFilename, '.zip') + extension // ...and filenames with and without a .zip extension 80 | ); 81 | 82 | // resolve a relative output path with respect to webpack's root output path 83 | // since only relative paths are permitted for keys in `compilation.assets` 84 | const relativeOutputPath = path.relative( 85 | compilation.options.output.path, 86 | outputPathAndFilename 87 | ); 88 | 89 | // add our zip file to the assets 90 | const zipFileSource = new RawSource(Buffer.concat(bufs)); 91 | if (isWebpack4) { 92 | compilation.assets[relativeOutputPath] = zipFileSource; 93 | } else { 94 | compilation.emitAsset(relativeOutputPath, zipFileSource); 95 | } 96 | 97 | callback(); 98 | }); 99 | }; 100 | 101 | if (isWebpack4) { 102 | compiler.hooks.emit.tapAsync(ZipPlugin.name, process); 103 | } else { 104 | compiler.hooks.thisCompilation.tap(ZipPlugin.name, compilation => { 105 | compilation.hooks.processAssets.tapPromise( 106 | { 107 | name: ZipPlugin.name, 108 | stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER, 109 | }, 110 | () => new Promise(resolve => process(compilation, resolve)) 111 | ); 112 | }); 113 | } 114 | }; 115 | 116 | module.exports = ZipPlugin; 117 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const { createWriteStream, readFileSync } = require('fs'); 2 | const { basename, dirname, join } = require('path'); 3 | 4 | const test = require('ava'); 5 | const webpack = require('webpack'); 6 | const rimraf = require('rimraf'); 7 | const mkdirp = require('mkdirp'); 8 | const yauzl = require('yauzl'); 9 | 10 | const ZipPlugin = require('../index'); 11 | 12 | function randomPath() { 13 | return join(__dirname, 'dist', String(Math.random()).slice(2)); 14 | } 15 | 16 | function runWithOptions({ path, filename }, options) { 17 | return new Promise((resolve, reject) => { 18 | webpack({ 19 | mode: 'development', 20 | devtool: false, 21 | entry: join(__dirname, 'src', 'app'), 22 | bail: true, 23 | output: { 24 | path, 25 | filename 26 | }, 27 | plugins: [ 28 | new ZipPlugin(options) 29 | ] 30 | }, (err, stats) => { 31 | stats.hasErrors() ? reject(stats.toString()) : resolve(stats); 32 | }); 33 | }); 34 | } 35 | 36 | test('basic', async t => { 37 | const out = randomPath(); 38 | await runWithOptions({ path: out, filename: 'bundle.js' }); 39 | 40 | const byeJpg = readFileSync(join(out, 'subdir', 'bye.jpg')); 41 | const bundleJs = readFileSync(join(out, 'bundle.js'), 'utf8'); 42 | const spawnedJs = readFileSync(join(out, 'spawned.js'), 'utf8'); 43 | const bundleJsZip = readFileSync(join(out, 'bundle.js.zip')); 44 | 45 | t.truthy(byeJpg); 46 | t.regex(bundleJs, /var a = 'b';/); 47 | t.regex(spawnedJs, /var foo = 'bar';/); 48 | t.truthy(bundleJsZip); 49 | }); 50 | 51 | async function unzip(zipFilePath, outDirPath) { 52 | const zipFile = await new Promise((resolve, reject) => { 53 | yauzl.open(zipFilePath, { lazyEntries: true }, (err, zipFile) => { 54 | err ? reject(err) : resolve(zipFile); 55 | }); 56 | }); 57 | 58 | zipFile.readEntry(); 59 | 60 | zipFile.on('entry', entry => { 61 | zipFile.openReadStream(entry, (err, readStream) => { 62 | if (err) throw err; 63 | mkdirp.sync(join(outDirPath, dirname(entry.fileName))); 64 | const writeStream = createWriteStream(join(outDirPath, entry.fileName)); 65 | readStream.pipe(writeStream); 66 | writeStream.on('close', () => zipFile.readEntry()); 67 | }); 68 | }); 69 | 70 | await new Promise((resolve, reject) => { 71 | zipFile.on('close', resolve); 72 | zipFile.on('error', reject); 73 | }); 74 | } 75 | 76 | test('roundtrip', async t => { 77 | const out = randomPath(); 78 | const outSrc = join(out, 'src'); 79 | const outDst = join(out, 'dst'); 80 | 81 | await runWithOptions({ path: outSrc, filename: 'bundle.js' }); 82 | 83 | await unzip(join(outSrc, 'bundle.js.zip'), outDst); 84 | 85 | t.is(Buffer.compare( 86 | readFileSync(join(outSrc, 'subdir', 'bye.jpg')), 87 | readFileSync(join(outDst, 'subdir', 'bye.jpg')) 88 | ), 0); 89 | t.is(Buffer.compare( 90 | readFileSync(join(outSrc, 'bundle.js')), 91 | readFileSync(join(outDst, 'bundle.js')) 92 | ), 0); 93 | t.is(Buffer.compare( 94 | readFileSync(join(outSrc, 'spawned.js')), 95 | readFileSync(join(outDst, 'spawned.js')) 96 | ), 0); 97 | }); 98 | 99 | async function roundtrip(options) { 100 | const out = randomPath(); 101 | const outSrc = join(out, 'src'); 102 | const outDst = join(out, 'dst'); 103 | 104 | await runWithOptions({ path: outSrc, filename: 'bundle.js' }, options); 105 | 106 | await unzip(join(outSrc, 'bundle.js.zip'), outDst); 107 | 108 | return outDst; 109 | } 110 | 111 | test('exclude string', async t => { 112 | const out = await roundtrip({ exclude: 'spawned.js' }); 113 | 114 | t.truthy(readFileSync(join(out, 'subdir', 'bye.jpg'))); 115 | t.truthy(readFileSync(join(out, 'bundle.js'))); 116 | t.throws(() => readFileSync(join(out, 'spawned.js'))); 117 | }); 118 | 119 | test('include string', async t => { 120 | const out = await roundtrip({ include: 'spawned.js' }); 121 | 122 | t.throws(() => readFileSync(join(out, 'subdir', 'bye.jpg'))); 123 | t.throws(() => readFileSync(join(out, 'bundle.js'))); 124 | t.truthy(readFileSync(join(out, 'spawned.js'))); 125 | }); 126 | 127 | test('exclude regex', async t => { 128 | const out = await roundtrip({ exclude: /\.jpg$/ }); 129 | 130 | t.throws(() => readFileSync(join(out, 'subdir', 'bye.jpg'))); 131 | t.truthy(readFileSync(join(out, 'bundle.js'))); 132 | t.truthy(readFileSync(join(out, 'spawned.js'))); 133 | }); 134 | 135 | test('include regex', async t => { 136 | const out = await roundtrip({ include: /\.js$/ }); 137 | 138 | t.throws(() => readFileSync(join(out, 'subdir', 'bye.jpg'))); 139 | t.truthy(readFileSync(join(out, 'bundle.js'))); 140 | t.truthy(readFileSync(join(out, 'spawned.js'))); 141 | }); 142 | 143 | test('multiple excludes', async t => { 144 | const out = await roundtrip({ exclude: [/\.jpg$/, 'bundle.js'] }); 145 | 146 | t.throws(() => readFileSync(join(out, 'subdir', 'bye.jpg'))); 147 | t.throws(() => readFileSync(join(out, 'bundle.js'))); 148 | t.truthy(readFileSync(join(out, 'spawned.js'))); 149 | }); 150 | 151 | test('multiple includes', async t => { 152 | const out = await roundtrip({ include: [/\.jpg$/, 'bundle.js'] }); 153 | 154 | t.truthy(readFileSync(join(out, 'subdir', 'bye.jpg'))); 155 | t.truthy(readFileSync(join(out, 'bundle.js'))); 156 | t.throws(() => readFileSync(join(out, 'spawned.js'))); 157 | }); 158 | 159 | test('exclude overrides include', async t => { 160 | const out = await roundtrip({ include: [/\.jpg$/, /\.js$/], exclude: ['bundle.js'] }); 161 | 162 | t.truthy(readFileSync(join(out, 'subdir', 'bye.jpg'))); 163 | t.throws(() => readFileSync(join(out, 'bundle.js'))); 164 | t.truthy(readFileSync(join(out, 'spawned.js'))); 165 | }); 166 | 167 | test('exclude dir', async t => { 168 | const out = await roundtrip({ exclude: 'subdir/' }); 169 | 170 | t.throws(() => readFileSync(join(out, 'subdir', 'bye.jpg'))); 171 | t.truthy(readFileSync(join(out, 'bundle.js'))); 172 | t.truthy(readFileSync(join(out, 'spawned.js'))); 173 | }); 174 | 175 | test('loaders not tested for include', async t => { 176 | const out = await roundtrip({ include: /file/i }); 177 | 178 | t.throws(() => readFileSync(join(out, 'subdir', 'bye.jpg'))); 179 | t.throws(() => readFileSync(join(out, 'bundle.js'))); 180 | t.throws(() => readFileSync(join(out, 'spawned.js'))); 181 | }); 182 | 183 | test('loaders not tested for exclude', async t => { 184 | const out = await roundtrip({ exclude: /file/i }); 185 | 186 | t.truthy(readFileSync(join(out, 'subdir', 'bye.jpg'))); 187 | t.truthy(readFileSync(join(out, 'bundle.js'))); 188 | t.truthy(readFileSync(join(out, 'spawned.js'))); 189 | }); 190 | 191 | test('fileOptions and zipOptions', async t => { 192 | const out = randomPath(); 193 | 194 | await runWithOptions({ path: out, filename: 'baseline.js' }, {}); 195 | 196 | // File options: uncompressed 197 | await runWithOptions({ path: out, filename: 'uncompressed.js' }, { 198 | fileOptions: { 199 | mtime: new Date('2016-01-01Z'), 200 | mode: 0o100664, 201 | forceZip64Format: true, 202 | compress: false, 203 | }, 204 | }); 205 | 206 | // Zip options: force zip64 format 207 | await runWithOptions({ path: out, filename: 'zip64.js' }, { 208 | zipOptions: { 209 | forceZip64Format: true, 210 | }, 211 | }); 212 | 213 | // Both: Set time and mode, force zip64 format for both 214 | await runWithOptions({ path: out, filename: 'both.js' }, { 215 | fileOptions: { 216 | mtime: new Date('2015-01-01Z'), 217 | mode: 0o100665, 218 | forceZip64Format: true, 219 | }, 220 | zipOptions: { 221 | forceZip64Format: true, 222 | }, 223 | }); 224 | 225 | const baselineSize = readFileSync(join(out, 'baseline.js.zip')).length; 226 | const uncompressedSize = readFileSync(join(out, 'uncompressed.js.zip')).length; 227 | const zip64Size = readFileSync(join(out, 'zip64.js.zip')).length; 228 | const bothSize = readFileSync(join(out, 'both.js.zip')).length; 229 | 230 | // Should be around 57k, mostly we want to make sure it's not empty here 231 | t.truthy(baselineSize > 50000); 232 | t.truthy(uncompressedSize > 50000); 233 | t.truthy(zip64Size > 50000); 234 | t.truthy(bothSize > 50000); 235 | 236 | // Uncompressed size is larger than (baseline) compressed size 237 | t.truthy(uncompressedSize > baselineSize); 238 | 239 | // Zip64 size is (slightly) larger than (baseline) compressed size 240 | t.truthy(zip64Size > baselineSize); 241 | // Zip64 size is (slightly) smaller than uncompressed size 242 | t.truthy(zip64Size < uncompressedSize); 243 | 244 | // Both size is (slightly) larger than Zip64 size 245 | t.truthy(bothSize > zip64Size); 246 | // Both size is (slightly) smaller than uncompressed size 247 | t.truthy(bothSize < uncompressedSize); 248 | }); 249 | 250 | test('pathPrefix', async t => { 251 | const out = await roundtrip({ pathPrefix: 'prefix' }); 252 | 253 | t.truthy(readFileSync(join(out, 'prefix', 'subdir', 'bye.jpg'))); 254 | t.truthy(readFileSync(join(out, 'prefix', 'bundle.js'))); 255 | }); 256 | 257 | test('pathPrefix - throws on absolute path', async t => { 258 | t.throws(() => { 259 | const plugin = new ZipPlugin({ pathPrefix: '/prefix' }); 260 | plugin.apply(); 261 | }); 262 | }); 263 | 264 | test('pathMapper - jpg', async t => { 265 | const out = await roundtrip({ 266 | pathMapper: p => { 267 | if (p.endsWith('.jpg')) return join(dirname(p), 'images', basename(p)); 268 | return p; 269 | } 270 | }); 271 | 272 | t.truthy(readFileSync(join(out, 'subdir', 'images', 'bye.jpg'))); 273 | t.throws(() => readFileSync(join(out, 'subdir', 'bye.jpg'))); 274 | t.truthy(readFileSync(join(out, 'bundle.js'))); 275 | t.truthy(readFileSync(join(out, 'spawned.js'))); 276 | }); 277 | 278 | test('pathMapper - js', async t => { 279 | const out = await roundtrip({ 280 | pathMapper: p => { 281 | if (p.endsWith('.js')) return join(dirname(p), 'js', basename(p)); 282 | return p; 283 | } 284 | }); 285 | 286 | t.truthy(readFileSync(join(out, 'subdir', 'bye.jpg'))); 287 | t.truthy(readFileSync(join(out, 'js', 'bundle.js'))); 288 | t.throws(() => readFileSync(join(out, 'bundle.js'))); 289 | t.truthy(readFileSync(join(out, 'js', 'spawned.js'))); 290 | t.throws(() => readFileSync(join(out, 'spawned.js'))); 291 | }); 292 | 293 | test('naming - default options, no webpack filename', async t => { 294 | const out = randomPath(); 295 | await runWithOptions({ path: out }); 296 | t.truthy(readFileSync(join(out, '[name].js.zip')), '.zip exists'); 297 | }); 298 | 299 | test('naming - default options, with webpack filename', async t => { 300 | const out = randomPath(); 301 | await runWithOptions({ path: out, filename: 'bundle.js' }); 302 | t.truthy(readFileSync(join(out, 'bundle.js.zip')), '.zip exists'); 303 | }); 304 | 305 | test('naming - specified filename with .zip, no webpack filename', async t => { 306 | const out = randomPath(); 307 | await runWithOptions({ path: out }, { filename: 'my_app.zip' }); 308 | t.truthy(readFileSync(join(out, 'my_app.zip')), '.zip exists'); 309 | }); 310 | 311 | test('naming - specified filename without .zip, no webpack filename', async t => { 312 | const out = randomPath(); 313 | await runWithOptions({ path: out }, { filename: 'my_app' }); 314 | t.truthy(readFileSync(join(out, 'my_app.zip')), '.zip exists'); 315 | }); 316 | 317 | test('naming - specified filename with .zip, with webpack filename', async t => { 318 | const out = randomPath(); 319 | await runWithOptions({ path: out, filename: 'bundle.js' }, { filename: 'my_app.zip' }); 320 | t.truthy(readFileSync(join(out, 'my_app.zip')), '.zip exists'); 321 | }); 322 | 323 | test('naming - specified filename and extension, no webpack filename', async t => { 324 | const out = randomPath(); 325 | await runWithOptions({ path: out }, { filename:'file', extension: 'ext'}) 326 | t.truthy(readFileSync(join(out, 'file.ext')), '.ext exists'); 327 | }); 328 | 329 | test('naming - specified extension, webpack filename', async t => { 330 | const out = randomPath(); 331 | await runWithOptions({ path: out, filename: 'bundle.js' }, { extension: 'ext'}) 332 | t.truthy(readFileSync(join(out, 'bundle.js.ext')), '.ext exists'); 333 | }); 334 | 335 | test('naming - specified extension, no webpack filename', async t => { 336 | const out = randomPath(); 337 | await runWithOptions({ path: out }, { extension: 'ext'}) 338 | t.truthy(readFileSync(join(out, '[name].js.ext')), '.ext exists'); 339 | }); 340 | 341 | test('naming - specified relative path and extension, no webpack filename', async t => { 342 | const out = randomPath(); 343 | await runWithOptions({ path: out }, { path: 'zip', extension: 'ext'}) 344 | t.truthy(readFileSync(join(out, 'zip', '[name].js.ext')), '.ext exists'); 345 | }); 346 | 347 | test('naming - specified relative path with slash and extension, no webpack filename', async t => { 348 | const out = randomPath(); 349 | await runWithOptions({ path: out }, { path: './zip', extension: 'ext'}) 350 | t.truthy(readFileSync(join(out, 'zip', '[name].js.ext')), '.ext exists'); 351 | }); 352 | 353 | test('naming - specified relative path, no webpack filename', async t => { 354 | const out = randomPath(); 355 | await runWithOptions({ path: out }, { path: 'zip' }); 356 | t.truthy(readFileSync(join(out, 'zip', '[name].js.zip')), '.zip exists'); 357 | }); 358 | 359 | test('naming - specified relative path with slash, no webpack filename', async t => { 360 | const out = randomPath(); 361 | await runWithOptions({ path: out }, { path: './zip' }); 362 | t.truthy(readFileSync(join(out, 'zip', '[name].js.zip')), '.zip exists'); 363 | }); 364 | 365 | test('naming - specified relative path with parent, no webpack filename', async t => { 366 | const out = randomPath(); 367 | await runWithOptions({ path: join(out, 'bin') }, { path: '../zip' }); 368 | t.truthy(readFileSync(join(out, 'zip', '[name].js.zip')), '.zip exists'); 369 | }); 370 | 371 | test('naming - specified absolute path, no webpack filename', async t => { 372 | const out = randomPath(); 373 | await runWithOptions({ path: out }, { path: join(out, 'zip') }); 374 | t.truthy(readFileSync(join(out, 'zip', '[name].js.zip')), '.zip exists'); 375 | }); 376 | 377 | test('naming - specified relative path, with webpack filename', async t => { 378 | const out = randomPath(); 379 | await runWithOptions({ path: out, filename: 'bundle.js' }, { path: 'zip' }); 380 | t.truthy(readFileSync(join(out, 'zip', 'bundle.js.zip')), '.zip exists'); 381 | }); 382 | 383 | test('naming - specified absolute path, with webpack filename', async t => { 384 | const out = randomPath(); 385 | await runWithOptions({ path: out, filename: 'bundle.js' }, { path: join(out, 'zip') }); 386 | t.truthy(readFileSync(join(out, 'zip', 'bundle.js.zip')), '.zip exists'); 387 | }); 388 | 389 | test('naming - both specified, relative, no webpack filename', async t => { 390 | const out = randomPath(); 391 | await runWithOptions({ path: out }, { path: 'zip', filename: 'archive' }); 392 | t.truthy(readFileSync(join(out, 'zip', 'archive.zip')), '.zip exists'); 393 | }); 394 | 395 | test('naming - both specified, absolute, no webpack filename', async t => { 396 | const out = randomPath(); 397 | await runWithOptions({ path: out }, { path: join(out, 'zip'), filename: 'archive' }); 398 | t.truthy(readFileSync(join(out, 'zip', 'archive.zip')), '.zip exists'); 399 | }); 400 | 401 | test('naming - both specified, relative, with webpack filename', async t => { 402 | const out = randomPath(); 403 | await runWithOptions({ path: out, filename: 'bundle.js' }, { path: 'zip', filename: 'archive' }); 404 | t.truthy(readFileSync(join(out, 'zip', 'archive.zip')), '.zip exists'); 405 | }); 406 | 407 | test('naming - both specified, absolute, with webpack filename', async t => { 408 | const out = randomPath(); 409 | await runWithOptions({ path: out, filename: 'bundle.js' }, { path: join(out, 'zip'), filename: 'archive' }); 410 | t.truthy(readFileSync(join(out, 'zip', 'archive.zip')), '.zip exists'); 411 | }); 412 | 413 | test.after(() => { 414 | rimraf.sync(join(__dirname, 'dist')); 415 | }); 416 | --------------------------------------------------------------------------------