diff --git a/package.json b/package.json index b0efdadbd18b..baa958496dd2 100644 --- a/package.json +++ b/package.json @@ -46,10 +46,10 @@ "@angular/core": "2.2.3", "@angular/tsc-wrapped": "0.4.0", "angular2-template-loader": "^0.5.0", + "async": "^2.1.4", "autoprefixer": "^6.5.3", "chalk": "^1.1.3", "common-tags": "^1.3.1", - "compression-webpack-plugin": "^0.3.2", "configstore": "^2.0.0", "core-js": "^2.4.0", "css-loader": "^0.23.1", diff --git a/packages/angular-cli/lib/webpack/compression-plugin.ts b/packages/angular-cli/lib/webpack/compression-plugin.ts new file mode 100644 index 000000000000..32c627f17f58 --- /dev/null +++ b/packages/angular-cli/lib/webpack/compression-plugin.ts @@ -0,0 +1,112 @@ +/** Forked from https://github.com/webpack/compression-webpack-plugin. */ +const async = require('async'); +const url = require('url'); + +const RawSource = require('webpack-sources/lib/RawSource'); + + +export interface CompressionPluginOptions { + algorithm?: string; + asset?: string; + level?: number; + flush?: boolean; + chunkSize?: number; + test?: RegExp | RegExp[]; + windowBits?: number; + memLevel?: number; + strategy?: number; + dictionary?: any; + threshold?: number; + minRatio?: number; +} + + +export class CompressionPlugin { + private asset = '[path].gz[query]'; + private algorithm: Function; + private compressionOptions: any = {}; + private test: RegExp[]; + private threshold: number = 0; + private minRatio: number = 0.8; + + constructor(options: CompressionPluginOptions = {}) { + if (options.hasOwnProperty('asset')) { + this.asset = options.asset; + } + + const algorithm = options.hasOwnProperty('algorithm') ? options.algorithm : 'gzip'; + + const zlib = require('zlib'); + + this.compressionOptions = {}; + this.algorithm = zlib[algorithm]; + if (!this.algorithm) { + throw new Error(`Algorithm not found in zlib: "${algorithm}".`); + } + + this.compressionOptions = { + level: options.level || 9, + flush: options.flush, + chunkSize: options.chunkSize, + windowBits: options.windowBits, + memLevel: options.memLevel, + strategy: options.strategy, + dictionary: options.dictionary + }; + + if (options.hasOwnProperty('test')) { + if (Array.isArray(options.test)) { + this.test = options.test as RegExp[]; + } else { + this.test = [options.test as RegExp]; + } + } + if (options.hasOwnProperty('threshold')) { + this.threshold = options.threshold; + } + if (options.hasOwnProperty('minRatio')) { + this.minRatio = options.minRatio; + } + } + + apply(compiler: any) { + compiler.plugin('this-compilation', (compilation: any) => { + compilation.plugin('optimize-assets', (assets: any, callback: Function) => { + async.forEach(Object.keys(assets), (file: string, callback: Function) => { + if (this.test.every((t) => !t.test(file))) { + return callback(); + } + + const asset = assets[file]; + let content = asset.source(); + if (!Buffer.isBuffer(content)) { + content = new Buffer(content, 'utf-8'); + } + + const originalSize = content.length; + if (originalSize < this.threshold) { + return callback(); + } + + this.algorithm(content, this.compressionOptions, (err: Error, result: string) => { + if (err) { + return callback(err); + } + if (result.length / originalSize > this.minRatio) { + return callback(); + } + + const parse = url.parse(file); + const newFile = this.asset + .replace(/\[file]/g, file) + .replace(/\[path]/g, parse.pathname) + .replace(/\[query]/g, parse.query || ''); + + assets[newFile] = new RawSource(result); + callback(); + }); + }, callback); + }); + }); + } +} diff --git a/packages/angular-cli/models/webpack-build-production.ts b/packages/angular-cli/models/webpack-build-production.ts index ad54db5c65cc..caf2fc58f3e0 100644 --- a/packages/angular-cli/models/webpack-build-production.ts +++ b/packages/angular-cli/models/webpack-build-production.ts @@ -1,6 +1,7 @@ import * as path from 'path'; +import {CompressionPlugin} from '../lib/webpack/compression-plugin'; + const WebpackMd5Hash = require('webpack-md5-hash'); -const CompressionPlugin = require('compression-webpack-plugin'); import * as webpack from 'webpack'; const ExtractTextPlugin = require('extract-text-webpack-plugin'); @@ -67,8 +68,7 @@ export const getWebpackProdConfigPartial = function(projectRoot: string, asset: '[path].gz[query]', algorithm: 'gzip', test: /\.js$|\.html$|\.css$/, - threshold: 10240, - minRatio: 0.8 + threshold: 10240 }), new webpack.LoaderOptionsPlugin({ options: { diff --git a/packages/angular-cli/package.json b/packages/angular-cli/package.json index cbcac1da3c8f..bc0d76ec0e5d 100644 --- a/packages/angular-cli/package.json +++ b/packages/angular-cli/package.json @@ -32,9 +32,9 @@ "@angular/core": "2.2.3", "@ngtools/webpack": "^1.0.0", "angular2-template-loader": "^0.5.0", + "async": "^2.1.4", "chalk": "^1.1.3", "common-tags": "^1.3.1", - "compression-webpack-plugin": "^0.3.2", "configstore": "^2.0.0", "core-js": "^2.4.0", "css-loader": "^0.23.1",