diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a019322 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Ilia Saulenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc28740 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# brotli plugin for webpack + +This plugin compresses assets with [Brotli](https://github.com/google/brotli) compression algorithm using [iltorb](https://github.com/MayhemYDG/iltorb#brotliparams) library for serving it with [ngx_brotli](https://github.com/google/ngx_brotli) or such. + +## Usage + +``` javascript +var BrotliPlugin = require('brotli-webpack-plugin'); +module.exports = { + plugins: [ + new CompressionPlugin({ + asset: '[path].br[query]', + test: /\.(js|css|html|svg)$/, + threshold: 10240, + minRatio: 0.8 + }) + ] +} +``` + +Arguments: + +* `asset`: The target asset name. `[file]` is replaced with the original asset file name. `[path]` is replaced with the path of the original asset and `[query]` with the query. Defaults to `'[path].br[query]'`. +* `test`: All assets matching this RegExp are processed. Defaults to every asset. +* `threshold`: Only assets bigger than this size (in bytes) are processed. Defaults to `0`. +* `minRatio`: Only assets that compress better that this ratio are processed. Defaults to `0.8`. + +Optional arguments for Brotli (see [iltorb](https://github.com/MayhemYDG/iltorb#brotliparams) doc for details): +* `mode`: Default: 0, +* `quality`: Default: 11, +* `lgwin`: Default: 22, +* `lgblock`: Default: 0, +* `enable_dictionary`: Default: true, +* `enable_transforms`: Default: false, +* `greedy_block_split`: Default: false, +* `enable_context_modeling`: Default: false + +## License + +Heavily copy-pasted from [webpack/compression-webpack-plugin](https://github.com/webpack/compression-webpack-plugin) by [Tobias Koppers](https://github.com/sokra). + +Licensed under [MIT](./LICENSE). diff --git a/index.js b/index.js new file mode 100644 index 0000000..083eeef --- /dev/null +++ b/index.js @@ -0,0 +1,81 @@ +var async = require('async'); +var url = require('url'); + +var RawSource = require('webpack-sources/lib/RawSource'); + +function CompressionPlugin(options) { + options = options || {}; + this.asset = options.asset || '[path].gz[query]'; + this.test = options.test || options.regExp; + this.threshold = options.threshold || 0; + this.minRatio = options.minRatio || 0.8; + + try { + var brotli = require('iltorb'); + } catch (err) { + throw new Error('iltorb not found'); + } + + var brotliOptions = { + mode: options.hasOwnProperty('mode') ? options.mode : 0, + quality: options.hasOwnProperty('quality') ? options.quality : 11, + lgwin: options.hasOwnProperty('lgwin') ? options.lgwin : 22, + lgblock: options.hasOwnProperty('lgblock') ? options.lgblock : 0, + enable_dictionary: options.hasOwnProperty('enable_dictionary') ? options.enable_dictionary : true, + enable_transforms: options.hasOwnProperty('enable_transforms') ? options.enable_transforms : false, + greedy_block_split: options.hasOwnProperty('greedy_block_split') ? options.greedy_block_split : false, + enable_context_modeling: options.hasOwnProperty('enable_context_modeling') ? options.enable_context_modeling : false + }; + + this.compress = function (content, callback) { + brotli.compress(content, brotliOptions, callback); + }; +} +module.exports = CompressionPlugin; + +CompressionPlugin.prototype.apply = function (compiler) { + compiler.plugin('this-compilation', function (compilation) { + compilation.plugin('optimize-assets', function (assets, callback) { + async.forEach(Object.keys(assets), function (file, callback) { + if (this.test && !this.test.test(file)) { + return callback(); + } + + var asset = assets[file]; + var content = asset.source(); + if (!Buffer.isBuffer(content)) { + content = new Buffer(content, 'utf-8'); + } + + var originalSize = content.length; + if (originalSize < this.threshold) { + return callback(); + } + + this.compress(content, function (err, result) { + if (err) { + return callback(err); + } + + if (result.length / originalSize > this.minRatio) { + return callback(); + } + + var parse = url.parse(file); + var sub = { + file: file, + path: parse.pathname, + query: parse.query || '' + }; + + var newFile = this.asset.replace(/\[(file|path|query)\]/g, function (p0, p1) { + return sub[p1]; + }); + + assets[newFile] = new RawSource(result); + callback(); + }.bind(this)); + }.bind(this), callback); + }.bind(this)); + }.bind(this)); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..0519873 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "brotli-webpack-plugin", + "version": "0.1.0", + "author": { + "name": "Ilia Saulenko", + "email": "hi@mynameiswhm.ru", + "url": "https://github.com/mynameiswhm" + }, + "description": "Prepare Brotli-compressed versions of assets to serve them with Content-Encoding: br", + "dependencies": { + "async": "0.2.x", + "iltorb": "^1.0.12", + "webpack-sources": "^0.1.0" + }, + "homepage": "https://github.com/ngsru/brotli-webpack-plugin", + "repository": { + "type": "git", + "url": "https://github.com/ngsru/brotli-webpack-plugin.git" + }, + "license": "MIT" +}