var async = require('async'); var url = require('url'); var RawSource = require('webpack-sources/lib/RawSource'); function BrotliPlugin(options) { options = options || {}; this.asset = options.asset || '[path].br[query]'; this.test = options.test || options.regExp; this.threshold = options.threshold || 0; this.minRatio = options.minRatio || 0.8; this.deleteOriginalAssets = options.deleteOriginalAssets || false; var compress = require('./compress.js')(); 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) { compress(content, brotliOptions, callback); }; } module.exports = BrotliPlugin; BrotliPlugin.prototype.apply = function (compiler) { var emit = function (compilation, callback) { var assets = compilation.assets; 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 = Buffer.from(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, fileWithoutExt: file.split('.').slice(0, -1).join('.'), ext: file.split('.').slice(-1).join(''), path: parse.pathname, query: parse.query || '' }; var newFile = this.asset.replace(/\[(file|fileWithoutExt|ext|path|query)]/g, function (p0, p1) { return sub[p1]; }); if (this.deleteOriginalAssets) { delete assets[file]; } assets[newFile] = new RawSource(result); callback(); }.bind(this)); }.bind(this), callback); }.bind(this); if (compiler.hooks) { const plugin = { name: 'BrotliPlugin' }; compiler.hooks.emit.tapAsync(plugin, emit); } else { compiler.plugin('emit', emit); } };