/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { Compiler } from 'webpack'; import { RawSource, SourceMapSource } from 'webpack-sources'; const CleanCSS = require('clean-css'); interface Chunk { files: string[]; } export interface CleanCssWebpackPluginOptions { sourceMap: boolean; } export class CleanCssWebpackPlugin { constructor(private options: Partial = {}) {} apply(compiler: Compiler): void { compiler.plugin('compilation', (compilation: any) => { compilation.plugin('optimize-chunk-assets', (chunks: Array, callback: (err?: Error) => void) => { const cleancss = new CleanCSS({ compatibility: 'ie9', level: 2, inline: false, returnPromise: true, sourceMap: this.options.sourceMap, }); const files: string[] = [...compilation.additionalChunkAssets]; chunks.forEach(chunk => { if (chunk.files && chunk.files.length > 0) { files.push(...chunk.files); } }); const actions = files .filter(file => file.endsWith('.css')) .map(file => { const asset = compilation.assets[file]; if (!asset) { return Promise.resolve(); } let content: string; let map: any; if (asset.sourceAndMap) { const sourceAndMap = asset.sourceAndMap(); content = sourceAndMap.source; map = sourceAndMap.map; } else { content = asset.source(); } if (content.length === 0) { return Promise.resolve(); } return Promise.resolve() .then(() => cleancss.minify(content, map)) .then((output: any) => { let hasWarnings = false; if (output.warnings && output.warnings.length > 0) { compilation.warnings.push(...output.warnings); hasWarnings = true; } if (output.errors && output.errors.length > 0) { output.errors .forEach((error: string) => compilation.errors.push(new Error(error))); return; } // generally means invalid syntax so bail if (hasWarnings && output.stats.minifiedSize === 0) { return; } let newSource; if (output.sourceMap) { newSource = new SourceMapSource( output.styles, file, output.sourceMap.toString(), content, map, ); } else { newSource = new RawSource(output.styles); } compilation.assets[file] = newSource; }); }); Promise.all(actions) .then(() => callback()) .catch(err => callback(err)); }); }); } }