├── .babelrc ├── .editorconfig ├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src └── index.js └── test ├── __snapshots__ └── webpack-integration.test.js.snap ├── cases ├── assetNameRegExp-no-source │ ├── a_optimize-me.css │ ├── b_optimize-me.css │ ├── c.css │ ├── expected │ │ └── file.css │ ├── index.js │ └── webpack.config.js ├── duplicate-css-exists-without-plugin │ ├── a.css │ ├── b.css │ ├── expected │ │ └── file.css │ ├── index.js │ └── webpack.config.js ├── only-assetNameRegExp-processed │ ├── a_optimize-me.css │ ├── b_optimize-me.css │ ├── c_as-is.css │ ├── expected │ │ ├── as_is.css │ │ └── optimize.css │ ├── index.js │ └── webpack.config.js └── removes-duplicate-css │ ├── a.css │ ├── b.css │ ├── expected │ └── file.css │ ├── index.js │ └── webpack.config.js ├── plugin.test.js ├── util ├── default.css ├── helpers.js └── index.js └── webpack-integration.test.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "useBuiltIns": true, 7 | "targets": { 8 | "node": "current" 9 | }, 10 | "exclude": [ 11 | "transform-async-to-generator", 12 | "transform-regenerator" 13 | ] 14 | } 15 | ] 16 | ], 17 | "plugins": [ 18 | [ 19 | "transform-object-rest-spread", 20 | { 21 | "useBuiltIns": true 22 | } 23 | ] 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [Makefile] 12 | indent_style = tab 13 | 14 | [*.md] 15 | indent_size = 4 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | jsconfig.json 3 | .DS_Store 4 | npm-debug.log 5 | /test/js 6 | 7 | logs 8 | *.log 9 | npm-debug.log* 10 | .eslintcache 11 | /dist 12 | /local 13 | /reports 14 | Thumbs.db 15 | .idea 16 | .vscode 17 | *.sublime-project 18 | *.sublime-workspace 19 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.9.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Nuno Rodrigues 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Optimize CSS Assets Webpack Plugin 2 | 3 | A Webpack plugin to optimize \ minimize CSS assets. 4 | 5 | > :warning: For webpack v5 or above please use [css-minimizer-webpack-plugin](https://github.com/webpack-contrib/css-minimizer-webpack-plugin) instead. 6 | 7 | ## What does the plugin do? 8 | 9 | It will search for CSS assets during the Webpack build and will optimize \ minimize the CSS (by default it uses [cssnano](http://github.com/ben-eb/cssnano) but a custom CSS processor can be specified). 10 | 11 | ### Solves [extract-text-webpack-plugin](http://github.com/webpack/extract-text-webpack-plugin) CSS duplication problem: 12 | 13 | Since [extract-text-webpack-plugin](http://github.com/webpack/extract-text-webpack-plugin) only bundles (merges) text chunks, if it's used to bundle CSS, the bundle might have duplicate entries (chunks can be duplicate free but when merged, duplicate CSS can be created). 14 | 15 | ## Installation: 16 | 17 | Using npm: 18 | ```shell 19 | $ npm install --save-dev optimize-css-assets-webpack-plugin 20 | ``` 21 | 22 | > :warning: For webpack v3 or below please use `optimize-css-assets-webpack-plugin@3.2.0`. The `optimize-css-assets-webpack-plugin@4.0.0` version and above supports webpack v4. 23 | 24 | ## Configuration: 25 | 26 | The plugin can receive the following options (all of them are optional): 27 | * `assetNameRegExp`: A regular expression that indicates the names of the assets that should be optimized \ minimized. The regular expression provided is run against the filenames of the files exported by the `ExtractTextPlugin` instances in your configuration, not the filenames of your source CSS files. Defaults to `/\.css$/g` 28 | * `cssProcessor`: The CSS processor used to optimize \ minimize the CSS, defaults to [`cssnano`](http://github.com/ben-eb/cssnano). This should be a function that follows `cssnano.process` interface (receives a CSS and options parameters and returns a Promise). 29 | * `cssProcessorOptions`: The options passed to the `cssProcessor`, defaults to `{}` 30 | * `cssProcessorPluginOptions`: The plugin options passed to the `cssProcessor`, defaults to `{}` 31 | * `canPrint`: A boolean indicating if the plugin can print messages to the console, defaults to `true` 32 | 33 | ## Example: 34 | 35 | ``` javascript 36 | var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 37 | module.exports = { 38 | module: { 39 | rules: [ 40 | { 41 | test: /\.css$/, 42 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader') 43 | } 44 | ] 45 | }, 46 | plugins: [ 47 | new ExtractTextPlugin('styles.css'), 48 | new OptimizeCssAssetsPlugin({ 49 | assetNameRegExp: /\.optimize\.css$/g, 50 | cssProcessor: require('cssnano'), 51 | cssProcessorPluginOptions: { 52 | preset: ['default', { discardComments: { removeAll: true } }], 53 | }, 54 | canPrint: true 55 | }) 56 | ] 57 | }; 58 | ``` 59 | 60 | ## License 61 | 62 | MIT (http://www.opensource.org/licenses/mit-license.php) 63 | 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "optimize-css-assets-webpack-plugin", 3 | "version": "5.0.8", 4 | "description": "A Webpack plugin to optimize \\ minimize CSS assets.", 5 | "keywords": [ 6 | "CSS", 7 | "duplicate", 8 | "extract-text-webpack-plugin", 9 | "minimize", 10 | "optimize", 11 | "remove", 12 | "webpack" 13 | ], 14 | "homepage": "http://github.com/NMFR/optimize-css-assets-webpack-plugin", 15 | "license": "MIT", 16 | "author": "Nuno Rodrigues", 17 | "main": "src/index.js", 18 | "repository": { 19 | "type": "git", 20 | "url": "http://github.com/NMFR/optimize-css-assets-webpack-plugin.git" 21 | }, 22 | "scripts": { 23 | "test": "jest", 24 | "test:watch": "jest --watch" 25 | }, 26 | "jest": { 27 | "testEnvironment": "node", 28 | "watchPathIgnorePatterns": [ 29 | "/test/js/*.*" 30 | ] 31 | }, 32 | "dependencies": { 33 | "cssnano": "^4.1.10", 34 | "last-call-webpack-plugin": "^3.0.0" 35 | }, 36 | "devDependencies": { 37 | "babel-core": "^6.26.3", 38 | "babel-jest": "^26.3.0", 39 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 40 | "babel-polyfill": "^6.26.0", 41 | "babel-preset-env": "^1.7.0", 42 | "css-loader": "^3.6.0", 43 | "extract-text-webpack-plugin": "next", 44 | "jest": "^26.4.2", 45 | "style-loader": "^0.20.1", 46 | "webpack": "^4.44.1" 47 | }, 48 | "peerDependencies": { 49 | "webpack": "^4.0.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const url = require('url'); 2 | 3 | const LastCallWebpackPlugin = require('last-call-webpack-plugin'); 4 | 5 | class OptimizeCssAssetsWebpackPlugin extends LastCallWebpackPlugin { 6 | constructor(options) { 7 | super({ 8 | assetProcessors: [ 9 | { 10 | phase: LastCallWebpackPlugin.PHASES.OPTIMIZE_CHUNK_ASSETS, 11 | regExp: (options && options.assetNameRegExp) || /\.css(\?.*)?$/i, 12 | processor: (assetName, asset, assets) => 13 | this.processCss(assetName, asset, assets), 14 | } 15 | ], 16 | canPrint: options && options.canPrint, 17 | }); 18 | 19 | this.options.assetNameRegExp = !options || !options.assetNameRegExp ? 20 | /\.css(\?.*)?$/i : 21 | options.assetNameRegExp; 22 | this.options.cssProcessor = !options || !options.cssProcessor ? 23 | require('cssnano') : 24 | options.cssProcessor; 25 | this.options.cssProcessorOptions = !options || options.cssProcessorOptions === undefined ? 26 | {} : 27 | options.cssProcessorOptions; 28 | this.options.cssProcessorPluginOptions = !options || options.cssProcessorPluginOptions === undefined ? 29 | {} : 30 | options.cssProcessorPluginOptions; 31 | } 32 | 33 | buildPluginDescriptor() { 34 | return { name: 'OptimizeCssAssetsWebpackPlugin' }; 35 | } 36 | 37 | processCss(assetName, asset, assets) { 38 | const parse = url.parse(assetName); 39 | const assetInfo = { 40 | path: parse.pathname, 41 | query: parse.query ? `?${parse.query}` : '', 42 | }; 43 | 44 | const css = asset.sourceAndMap ? asset.sourceAndMap() : { source: asset.source() }; 45 | const processOptions = Object.assign( 46 | { from: assetName, to: assetName }, 47 | this.options.cssProcessorOptions 48 | ); 49 | 50 | if (processOptions.map && !processOptions.map.prev) { 51 | try { 52 | let map = css.map; 53 | if (!map) { 54 | const mapJson = assets.getAsset(`${assetInfo.path}.map`); 55 | if (mapJson) { 56 | map = JSON.parse(mapJson); 57 | } 58 | } 59 | if ( 60 | map && 61 | ( 62 | (map.sources && map.sources.length > 0) || 63 | (map.mappings && map.mappings.length > 0) 64 | ) 65 | ) { 66 | processOptions.map = Object.assign({ prev: map }, processOptions.map); 67 | } 68 | } catch (err) { 69 | console.warn('OptimizeCssAssetsPlugin.processCss() Error getting previous source map', err); 70 | } 71 | } 72 | return this.options 73 | .cssProcessor.process(css.source, processOptions, this.options.cssProcessorPluginOptions) 74 | .then(r => { 75 | if (processOptions.map && r.map && r.map.toString) { 76 | assets.setAsset(`${assetInfo.path}.map${assetInfo.query}`, r.map.toString()); 77 | } 78 | return r.css; 79 | }); 80 | } 81 | } 82 | 83 | module.exports = OptimizeCssAssetsWebpackPlugin; 84 | -------------------------------------------------------------------------------- /test/__snapshots__/webpack-integration.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Webpack Integration Tests assetNameRegExp-no-source 1`] = `"body{color:red}a{color:blue}body{margin:0;color:red}p{margin:1000px}body{color:red;padding:0;margin:0}p{padding:500px;padding:1000px}"`; 4 | 5 | exports[`Webpack Integration Tests duplicate-css-exists-without-plugin 1`] = `"body{color:red}a{color:blue}body{color:red}p{color:green}"`; 6 | 7 | exports[`Webpack Integration Tests only-assetNameRegExp-processed 1`] = ` 8 | "body { 9 | color: red; 10 | padding: 0; 11 | margin: 0; 12 | } 13 | p { 14 | padding: 500px; 15 | padding: 1000px; 16 | } 17 | " 18 | `; 19 | 20 | exports[`Webpack Integration Tests only-assetNameRegExp-processed 2`] = `"a{color:#00f}body{margin:0;color:red}p{margin:1000px}"`; 21 | 22 | exports[`Webpack Integration Tests removes-duplicate-css 1`] = `"a{color:#00f}body{color:red}p{color:green}"`; 23 | -------------------------------------------------------------------------------- /test/cases/assetNameRegExp-no-source/a_optimize-me.css: -------------------------------------------------------------------------------- 1 | body{color:red}a{color:blue} -------------------------------------------------------------------------------- /test/cases/assetNameRegExp-no-source/b_optimize-me.css: -------------------------------------------------------------------------------- 1 | body{margin:0;color:red}p{margin:1000px} -------------------------------------------------------------------------------- /test/cases/assetNameRegExp-no-source/c.css: -------------------------------------------------------------------------------- 1 | body{color:red;padding:0;margin:0}p{padding:500px;padding:1000px} -------------------------------------------------------------------------------- /test/cases/assetNameRegExp-no-source/expected/file.css: -------------------------------------------------------------------------------- 1 | body{color:red}a{color:blue}body{margin:0;color:red}p{margin:1000px}body{color:red;padding:0;margin:0}p{padding:500px;padding:1000px} -------------------------------------------------------------------------------- /test/cases/assetNameRegExp-no-source/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This test is here to confirm that assetNameRegExp option will apply 4 | only to the names of the files exported byt ExtractTextPlugin 5 | 6 | */ 7 | 8 | require('./a_optimize-me.css'); 9 | require('./b_optimize-me.css'); 10 | require('./c.css'); 11 | -------------------------------------------------------------------------------- /test/cases/assetNameRegExp-no-source/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from 'extract-text-webpack-plugin'; 2 | import OptimizeCssAssetsPlugin from '../../../src/'; 3 | 4 | module.exports = { 5 | entry: './index', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.css$/, 10 | use: ExtractTextPlugin.extract({ 11 | fallback: { loader: 'style-loader' }, 12 | use: { 13 | loader: 'css-loader' 14 | } 15 | }) 16 | }, 17 | ], 18 | }, 19 | plugins: [ 20 | new ExtractTextPlugin('file.css'), 21 | new OptimizeCssAssetsPlugin({ 22 | assetNameRegExp: /optimize-me\.css/g 23 | }) 24 | ], 25 | }; 26 | -------------------------------------------------------------------------------- /test/cases/duplicate-css-exists-without-plugin/a.css: -------------------------------------------------------------------------------- 1 | body{color:red}a{color:blue} -------------------------------------------------------------------------------- /test/cases/duplicate-css-exists-without-plugin/b.css: -------------------------------------------------------------------------------- 1 | body{color:red}p{color:green} -------------------------------------------------------------------------------- /test/cases/duplicate-css-exists-without-plugin/expected/file.css: -------------------------------------------------------------------------------- 1 | body{color:red}a{color:blue}body{color:red}p{color:green} -------------------------------------------------------------------------------- /test/cases/duplicate-css-exists-without-plugin/index.js: -------------------------------------------------------------------------------- 1 | require('./a.css'); 2 | require('./b.css'); 3 | -------------------------------------------------------------------------------- /test/cases/duplicate-css-exists-without-plugin/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from "extract-text-webpack-plugin"; 2 | import OptimizeCssAssetsPlugin from "../../../src/"; 3 | 4 | module.exports = { 5 | entry: "./index", 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.css$/, 10 | use: ExtractTextPlugin.extract({ 11 | fallback: { loader: "style-loader" }, 12 | use: { 13 | loader: "css-loader" 14 | } 15 | }) 16 | } 17 | ] 18 | }, 19 | plugins: [new ExtractTextPlugin("file.css")] 20 | }; 21 | -------------------------------------------------------------------------------- /test/cases/only-assetNameRegExp-processed/a_optimize-me.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: red; 3 | } 4 | a { 5 | color: blue; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/only-assetNameRegExp-processed/b_optimize-me.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | color: red; 4 | } 5 | p { 6 | margin: 1000px; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/only-assetNameRegExp-processed/c_as-is.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: red; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | p { 7 | padding: 500px; 8 | padding: 1000px; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/only-assetNameRegExp-processed/expected/as_is.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: red; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | p { 7 | padding: 500px; 8 | padding: 1000px; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/only-assetNameRegExp-processed/expected/optimize.css: -------------------------------------------------------------------------------- 1 | a{color:#00f}body{margin:0;color:red}p{margin:1000px} -------------------------------------------------------------------------------- /test/cases/only-assetNameRegExp-processed/index.js: -------------------------------------------------------------------------------- 1 | require('./a_optimize-me.css'); 2 | require('./b_optimize-me.css'); 3 | require('./c_as-is.css'); 4 | -------------------------------------------------------------------------------- /test/cases/only-assetNameRegExp-processed/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from "extract-text-webpack-plugin"; 2 | import OptimizeCssAssetsPlugin from "../../../src/"; 3 | 4 | const notToProcess = new ExtractTextPlugin("as_is.css"); 5 | const toProcess = new ExtractTextPlugin("optimize.css"); 6 | 7 | module.exports = { 8 | entry: "./index", 9 | module: { 10 | rules: [ 11 | { 12 | test: /as-is\.css$/, 13 | use: notToProcess.extract({ 14 | fallback: { loader: "style-loader" }, 15 | use: { 16 | loader: "css-loader" 17 | } 18 | }) 19 | }, 20 | { 21 | test: /optimize-me\.css$/, 22 | use: toProcess.extract({ 23 | fallback: { loader: "style-loader" }, 24 | use: { 25 | loader: "css-loader" 26 | } 27 | }) 28 | } 29 | ] 30 | }, 31 | plugins: [ 32 | notToProcess, 33 | toProcess, 34 | new OptimizeCssAssetsPlugin({ 35 | assetNameRegExp: /optimize\.css/g 36 | }) 37 | ] 38 | }; 39 | -------------------------------------------------------------------------------- /test/cases/removes-duplicate-css/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: red; 3 | } 4 | a { 5 | color: blue; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/removes-duplicate-css/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: red; 3 | } 4 | p { 5 | color: green; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/removes-duplicate-css/expected/file.css: -------------------------------------------------------------------------------- 1 | a{color:#00f}body{color:red}p{color:green} -------------------------------------------------------------------------------- /test/cases/removes-duplicate-css/index.js: -------------------------------------------------------------------------------- 1 | require('./a.css'); 2 | require('./b.css'); 3 | -------------------------------------------------------------------------------- /test/cases/removes-duplicate-css/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from "extract-text-webpack-plugin"; 2 | import OptimizeCssAssetsPlugin from "../../../src/"; 3 | 4 | module.exports = { 5 | entry: "./index", 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.css$/, 10 | use: ExtractTextPlugin.extract({ 11 | fallback: { loader: "style-loader" }, 12 | use: { 13 | loader: "css-loader" 14 | } 15 | }) 16 | } 17 | ] 18 | }, 19 | plugins: [new ExtractTextPlugin("file.css"), new OptimizeCssAssetsPlugin()] 20 | }; 21 | -------------------------------------------------------------------------------- /test/plugin.test.js: -------------------------------------------------------------------------------- 1 | import OptimizeCssAssetsPlugin from '../src/'; 2 | 3 | describe('plugin test', () => { 4 | it('does not throw when called', () => { 5 | expect(() => { 6 | new OptimizeCssAssetsPlugin(); 7 | }).not.toThrow(); 8 | }); 9 | 10 | it('can override default parameters', () => { 11 | const assetNameRegExp = /\.optimize\.css$/ 12 | const cssProcessor = {}; 13 | const cssProcessorOptions = { discardComments: { removeAll: true } }; 14 | const canPrint = false; 15 | const plugin = new OptimizeCssAssetsPlugin({ 16 | assetNameRegExp, 17 | cssProcessor, 18 | cssProcessorOptions, 19 | canPrint 20 | }); 21 | expect(plugin.options.assetNameRegExp).toEqual(assetNameRegExp); 22 | expect(plugin.options.cssProcessor).toEqual(cssProcessor); 23 | expect(plugin.options.cssProcessorOptions).toEqual(cssProcessorOptions); 24 | expect(plugin.options.canPrint).toEqual(canPrint); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/util/default.css: -------------------------------------------------------------------------------- 1 | html{display:none} -------------------------------------------------------------------------------- /test/util/helpers.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import ExtractTextPlugin from "extract-text-webpack-plugin"; 4 | 5 | export function readFileOrEmpty(path) { 6 | try { 7 | return fs.readFileSync(path, "utf-8"); 8 | } catch (e) { 9 | return ""; 10 | } 11 | } 12 | 13 | export const defaultConfig = { 14 | entry: "./index", 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.css$/, 19 | use: ExtractTextPlugin.extract({ 20 | fallback: { loader: "style-loader" }, 21 | use: { 22 | loader: "css-loader" 23 | } 24 | }) 25 | } 26 | ] 27 | }, 28 | plugins: [], 29 | context: __dirname, 30 | output: { 31 | filename: "destination.js", 32 | path: path.resolve(__dirname, "../", "js", "default-exports") 33 | } 34 | }; 35 | 36 | export function checkForWebpackErrors({ err, stats, done }) { 37 | if (err) return done(err); 38 | if (stats.hasErrors()) return done(new Error(stats.toString())); 39 | } 40 | -------------------------------------------------------------------------------- /test/util/index.js: -------------------------------------------------------------------------------- 1 | require('./default.css'); 2 | -------------------------------------------------------------------------------- /test/webpack-integration.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-dynamic-require, global-require */ 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | import webpack from 'webpack'; 5 | import ExtractTextPlugin from 'extract-text-webpack-plugin'; 6 | import OptimizeCssAssetsPlugin from '../src/'; 7 | import { readFileOrEmpty, defaultConfig, checkForWebpackErrors } from './util/helpers'; 8 | 9 | const cases = process.env.CASES ? process.env.CASES.split(',') : fs.readdirSync(path.join(__dirname, 'cases')); 10 | 11 | describe('Webpack Integration Tests', () => { 12 | cases.forEach((testCase) => { 13 | if (/^_skip_/.test(testCase)) return; 14 | it(testCase, (done) => { 15 | const testDirectory = path.join(__dirname, 'cases', testCase); 16 | const outputDirectory = path.join(__dirname, 'js', testCase); 17 | const expectedDirectory = path.join(testDirectory, 'expected'); 18 | 19 | const configFile = path.join(testDirectory, 'webpack.config.js'); 20 | const config = Object.assign( 21 | fs.existsSync(configFile) ? require(configFile) : { entry: { test: './index.js' } }, 22 | { 23 | context: testDirectory, 24 | output: { 25 | filename: '[name].js', 26 | path: outputDirectory 27 | } 28 | } 29 | ); 30 | 31 | webpack(config, (err, stats) => { 32 | checkForWebpackErrors({ err, stats, done }); 33 | fs.readdirSync(expectedDirectory).forEach((file) => { 34 | const expectedFile = readFileOrEmpty(path.join(expectedDirectory, file)); 35 | const actualFile = readFileOrEmpty(path.join(outputDirectory, file)); 36 | expect(actualFile).toEqual(expectedFile); 37 | expect(actualFile).toMatchSnapshot(); 38 | }); 39 | done(); 40 | }); 41 | }); 42 | }); 43 | 44 | it('calls cssProcessor with correct arguments', (done) => { 45 | const destination = 'destination.css'; 46 | const expectedCss = readFileOrEmpty(__dirname + '/util/default.css'); 47 | const cssProcessorOptions = { discardComments: { removeAll: true } }; 48 | const cssProcessor = { 49 | process: (actualCss, options) => { 50 | expect(options).toEqual(expect.objectContaining(cssProcessorOptions)); 51 | expect(actualCss).toEqual(expectedCss); 52 | return Promise.resolve({ css: actualCss }); 53 | } 54 | }; 55 | const plugin = new OptimizeCssAssetsPlugin({ cssProcessor, cssProcessorOptions }); 56 | const config = Object.assign(defaultConfig, {plugins: [plugin, new ExtractTextPlugin(destination)]}); 57 | 58 | webpack(config, (err, stats) => { 59 | checkForWebpackErrors({ err, stats, done }); 60 | done(); 61 | }); 62 | }); 63 | 64 | it('writes processed css to destination', (done) => { 65 | const destination = 'destination.css'; 66 | const expectedCss = '.inifinity-pool{overflow:hidden;}'; 67 | const fakeCssProcessor = { 68 | process: jest.fn().mockReturnValue(Promise.resolve({ css: expectedCss })) 69 | }; 70 | const plugin = new OptimizeCssAssetsPlugin({ cssProcessor: fakeCssProcessor }); 71 | const config = Object.assign(defaultConfig, {plugins: [plugin, new ExtractTextPlugin(destination)]}); 72 | 73 | webpack(config, (err, stats) => { 74 | checkForWebpackErrors({ err, stats, done }); 75 | const actualCss = readFileOrEmpty(__dirname + '/js/default-exports/destination.css'); 76 | 77 | expect(fakeCssProcessor.process).toHaveBeenCalled(); 78 | expect(actualCss).toEqual(expectedCss); 79 | done(); 80 | }); 81 | }); 82 | }); 83 | --------------------------------------------------------------------------------