|
| 1 | +/** Forked from https://github.com/webpack/compression-webpack-plugin. */ |
| 2 | +const async = require('async'); |
| 3 | +const url = require('url'); |
| 4 | + |
| 5 | +const RawSource = require('webpack-sources/lib/RawSource'); |
| 6 | + |
| 7 | + |
| 8 | +export interface CompressionPluginOptions { |
| 9 | + algorithm?: string; |
| 10 | + asset?: string; |
| 11 | + level?: number; |
| 12 | + flush?: boolean; |
| 13 | + chunkSize?: number; |
| 14 | + test?: RegExp | RegExp[]; |
| 15 | + windowBits?: number; |
| 16 | + memLevel?: number; |
| 17 | + strategy?: number; |
| 18 | + dictionary?: any; |
| 19 | + threshold?: number; |
| 20 | + minRatio?: number; |
| 21 | +} |
| 22 | + |
| 23 | + |
| 24 | +export class CompressionPlugin { |
| 25 | + private asset = '[path].gz[query]'; |
| 26 | + private algorithm: Function; |
| 27 | + private compressionOptions: any = {}; |
| 28 | + private test: RegExp[]; |
| 29 | + private threshold: number = 0; |
| 30 | + private minRatio: number = 0.8; |
| 31 | + |
| 32 | + constructor(options: CompressionPluginOptions = {}) { |
| 33 | + if (options.hasOwnProperty('asset')) { |
| 34 | + this.asset = options.asset; |
| 35 | + } |
| 36 | + |
| 37 | + const algorithm = options.hasOwnProperty('algorithm') ? options.algorithm : 'gzip'; |
| 38 | + |
| 39 | + const zlib = require('zlib'); |
| 40 | + |
| 41 | + this.compressionOptions = {}; |
| 42 | + this.algorithm = zlib[algorithm]; |
| 43 | + if (!this.algorithm) { |
| 44 | + throw new Error(`Algorithm not found in zlib: "${algorithm}".`); |
| 45 | + } |
| 46 | + |
| 47 | + this.compressionOptions = { |
| 48 | + level: options.level || 9, |
| 49 | + flush: options.flush, |
| 50 | + chunkSize: options.chunkSize, |
| 51 | + windowBits: options.windowBits, |
| 52 | + memLevel: options.memLevel, |
| 53 | + strategy: options.strategy, |
| 54 | + dictionary: options.dictionary |
| 55 | + }; |
| 56 | + |
| 57 | + if (options.hasOwnProperty('test')) { |
| 58 | + if (Array.isArray(options.test)) { |
| 59 | + this.test = options.test as RegExp[]; |
| 60 | + } else { |
| 61 | + this.test = [options.test as RegExp]; |
| 62 | + } |
| 63 | + } |
| 64 | + if (options.hasOwnProperty('threshold')) { |
| 65 | + this.threshold = options.threshold; |
| 66 | + } |
| 67 | + if (options.hasOwnProperty('minRatio')) { |
| 68 | + this.minRatio = options.minRatio; |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + apply(compiler: any) { |
| 73 | + compiler.plugin('this-compilation', (compilation: any) => { |
| 74 | + compilation.plugin('optimize-assets', (assets: any, callback: Function) => { |
| 75 | + async.forEach(Object.keys(assets), (file: string, callback: Function) => { |
| 76 | + if (this.test.every((t) => !t.test(file))) { |
| 77 | + return callback(); |
| 78 | + } |
| 79 | + |
| 80 | + const asset = assets[file]; |
| 81 | + let content = asset.source(); |
| 82 | + if (!Buffer.isBuffer(content)) { |
| 83 | + content = new Buffer(content, 'utf-8'); |
| 84 | + } |
| 85 | + |
| 86 | + const originalSize = content.length; |
| 87 | + if (originalSize < this.threshold) { |
| 88 | + return callback(); |
| 89 | + } |
| 90 | + |
| 91 | + this.algorithm(content, this.compressionOptions, (err: Error, result: string) => { |
| 92 | + if (err) { |
| 93 | + return callback(err); |
| 94 | + } |
| 95 | + if (result.length / originalSize > this.minRatio) { |
| 96 | + return callback(); |
| 97 | + } |
| 98 | + |
| 99 | + const parse = url.parse(file); |
| 100 | + const newFile = this.asset |
| 101 | + .replace(/\[file]/g, file) |
| 102 | + .replace(/\[path]/g, parse.pathname) |
| 103 | + .replace(/\[query]/g, parse.query || ''); |
| 104 | + |
| 105 | + assets[newFile] = new RawSource(result); |
| 106 | + callback(); |
| 107 | + }); |
| 108 | + }, callback); |
| 109 | + }); |
| 110 | + }); |
| 111 | + } |
| 112 | +} |
0 commit comments