diff --git a/README.md b/README.md index 53ecf80..2901c84 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ This plugin uses [cssnano](https://cssnano.co) to optimize and minify your CSS. -Just like [optimize-css-assets-webpack-plugin](https://github.com/NMFR/optimize-css-assets-webpack-plugin) but more accurate with source maps and assets using query string, allows to cache and works in parallel mode. +Just like [optimize-css-assets-webpack-plugin](https://github.com/NMFR/optimize-css-assets-webpack-plugin) but more accurate with source maps and assets using query string, allows caching and works in parallel mode. ## Getting Started @@ -169,8 +169,8 @@ Default: `true` Use multi-process parallel running to improve the build speed. Default number of concurrent runs: `os.cpus().length - 1`. -> ℹ️ Parallelization can speedup your build significantly and is therefore **highly recommended**. -> If a parallelization is enabled, the packages in `minimizerOptions` must be required via strings (`packageName` or `require.resolve(packageName)`). Read more in [`minimizerOptions`](#minimizerOptions) +> ℹ️ Parallelization can speed up your build significantly and is therefore **highly recommended**. +> If a parallelization is enabled, the packages in `minimizerOptions` must be required via strings (`packageName` or `require.resolve(packageName)`). Read more in [`minimizerOptions`](#minimizeroptions) #### `Boolean` @@ -215,8 +215,8 @@ module.exports = { Type: `Function|Array` Default: `CssMinimizerPlugin.cssnanoMinify` -Allows to override default minify function. -By default plugin uses [cssnano](https://github.com/cssnano/cssnano) package. +Allows overriding default minify function. +By default, plugin uses [cssnano](https://github.com/cssnano/cssnano) package. Useful for using and testing unpublished versions or forks. Possible options: @@ -224,6 +224,7 @@ Possible options: - CssMinimizerPlugin.cssnanoMinify - CssMinimizerPlugin.cssoMinify - CssMinimizerPlugin.cleanCssMinify +- CssMinimizerPlugin.esbuildMinify - `async (data, inputMap, minimizerOptions) => {return {code: "a{color: red}", map: "...", warnings: [], errors: []}}` > ⚠️ **Always use `require` inside `minify` function when `parallel` option enabled**. @@ -345,7 +346,7 @@ module.exports = { Type: `Object` Default: `{ to: assetName, from: assetName }` -Allows to specify options [`processoptions`](https://postcss.org/api/#processoptions) for the cssnano. +Allows filtering options [`processoptions`](https://postcss.org/api/#processoptions) for the cssnano. The `parser`,` stringifier` and `syntax` can be either a function or a string indicating the module that will be imported. > ⚠️ **If a function is passed, the `parallel` option must be disabled.**. @@ -392,7 +393,7 @@ module.exports = { Type: `Function<(warning, file, source) -> Boolean>` Default: `() => true` -Allow to filter css-minimizer warnings (By default [cssnano](https://github.com/cssnano/cssnano)). +Allow filtering css-minimizer warnings (By default [cssnano](https://github.com/cssnano/cssnano)). Return `true` to keep the warning, a falsy value (`false`/`null`/`undefined`) otherwise. > ⚠️ The `source` argument will contain `undefined` if you don't use source maps. @@ -481,11 +482,6 @@ module.exports = { ### Using custom minifier [csso](https://github.com/css/csso) -By default plugin uses [cssnano](https://github.com/cssnano/cssnano) package. -It is possible to use another minify function. - -> ⚠️ **Always use `require` inside `minify` function when `parallel` option enabled**. - **webpack.config.js** ```js @@ -495,28 +491,9 @@ module.exports = { minimize: true, minimizer: [ new CssMinimizerPlugin({ - minify: async (data, inputMap) => { - const csso = require("csso"); - const sourcemap = require("source-map"); - - const [[filename, input]] = Object.entries(data); - const minifiedCss = csso.minify(input, { - filename: filename, - sourceMap: true, - }); - - if (inputMap) { - minifiedCss.map.applySourceMap( - new sourcemap.SourceMapConsumer(inputMap), - filename - ); - } - - return { - code: minifiedCss.css, - map: minifiedCss.map.toJSON(), - }; - }, + minify: CssMinimizerPlugin.cssoMinify, + // Uncomment this line for options + // minimizerOptions: { restructure: false }, }), ], }, @@ -543,7 +520,7 @@ module.exports = { }; ``` -### Using custom minifier [csso](https://github.com/css/csso) +### Using custom minifier [esbuild](https://github.com/evanw/esbuild) **webpack.config.js** @@ -554,9 +531,7 @@ module.exports = { minimize: true, minimizer: [ new CssMinimizerPlugin({ - minify: CssMinimizerPlugin.cssoMinify, - // Uncomment this line for options - // minimizerOptions: { restructure: false }, + minify: CssMinimizerPlugin.esbuildMinify, }), ], }, diff --git a/package-lock.json b/package-lock.json index aba45bc..8e2b3fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "csso": "^4.2.0", "del": "^6.0.0", "del-cli": "^4.0.0", + "esbuild": "^0.13.3", "eslint": "^7.30.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "^2.24.0", @@ -65,6 +66,9 @@ }, "csso": { "optional": true + }, + "esbuild": { + "optional": true } } }, @@ -5499,6 +5503,242 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.3.tgz", + "integrity": "sha512-98xovMLKnyhv3gcReUuAEi5Ig1rK6SIgvsJuBIcfwzqGSEHsV8UJjMlmkhHoHMf9XZybMpE9Zax8AA8f7i2hlQ==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "optionalDependencies": { + "esbuild-android-arm64": "0.13.3", + "esbuild-darwin-64": "0.13.3", + "esbuild-darwin-arm64": "0.13.3", + "esbuild-freebsd-64": "0.13.3", + "esbuild-freebsd-arm64": "0.13.3", + "esbuild-linux-32": "0.13.3", + "esbuild-linux-64": "0.13.3", + "esbuild-linux-arm": "0.13.3", + "esbuild-linux-arm64": "0.13.3", + "esbuild-linux-mips64le": "0.13.3", + "esbuild-linux-ppc64le": "0.13.3", + "esbuild-openbsd-64": "0.13.3", + "esbuild-sunos-64": "0.13.3", + "esbuild-windows-32": "0.13.3", + "esbuild-windows-64": "0.13.3", + "esbuild-windows-arm64": "0.13.3" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.3.tgz", + "integrity": "sha512-jc9E8vGTHkzb0Vwl74H8liANV9BWsqtzLHaKvcsRgf1M+aVCBSF0gUheduAKfDsbDMT0judeMLhwBP34EUesTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/esbuild-darwin-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.3.tgz", + "integrity": "sha512-8bG3Zq+ZNuLlIJebOO2+weI7P2LVf33sOzaUfHj8MuJ+1Ixe4KtQxfYp7qhFnP6xP2ToJaYHxGUfLeiUCEz9hw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.3.tgz", + "integrity": "sha512-5E81eImYtTgh8pY7Gq4WQHhWkR/LvYadUXmuYeZBiP+3ADZJZcG60UFceZrjqNPaFOWKr/xmh4aNocwagEubcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.3.tgz", + "integrity": "sha512-ou+f91KkTGexi8HvF/BdtsITL6plbciQfZGys7QX6/QEwyE96PmL5KnU6ZQwoU7E99Ts6Sc9bUDq8HXJubKtBA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.3.tgz", + "integrity": "sha512-F1zV7nySjHswJuvIgjkiG5liZ63MeazDGXGKViTCeegjZ71sAhOChcaGhKcu6vq9+vqZxlfEi1fmXlx6Pc3coQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/esbuild-linux-32": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.3.tgz", + "integrity": "sha512-mHHc2v6uLrHH4zaaq5RB/5IWzgimEJ1HGldzf1qtGI513KZWfH0HRRQ8p1di4notJgBn7tDzWQ1f34ZHy69viQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.3.tgz", + "integrity": "sha512-FJ1De2O89mrOuqtaEXu41qIYJU6R41F+OA6vheNwcAQcX8fu0aiA13FJeLABq29BYJuTVgRj3cyC8q+tz19/dQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.3.tgz", + "integrity": "sha512-9BJNRtLwBh3OP22cln9g3AJdbAQUcjRHqA4BScx9k4RZpGqPokFr548zpeplxWhcwrIjT8qPebwH9CrRVy8Bsw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.3.tgz", + "integrity": "sha512-Cauhr45KSo+wRUojs+1qfycQqQCAXTOvsWvkZ6xmEMAXLAm+f8RQGDQeP8CAf8Yeelnegcn6UNdvzdzLHhWDFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.3.tgz", + "integrity": "sha512-YVzJUGCncuuLm2boYyVeuMFsak4ZAhdiBwi0xNDZCC8sy+tS6Boe2mzcrD2uubv5JKAUOrpN186S1DtU4WgBgw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.3.tgz", + "integrity": "sha512-GU6CqqKtJEoyxC2QWHiJtmuOz9wc/jMv8ZloK2WwiGY5yMvAmM3PI103Dj7xcjebNTHBqITTUw/aigY1wx5A3w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.3.tgz", + "integrity": "sha512-HVpkgpn4BQt4BPDAjTOpeMub6mzNWw6Y3gaLQJrpbO24pws6ZwYkY24OI3/Uo3LDCbH6856MM81JxECt92OWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/esbuild-sunos-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.3.tgz", + "integrity": "sha512-XncBVOtnEfUbPV4CaiFBxh38ychnBfwCxuTm9iAqcHzIwkmeNRN5qMzDyfE1jyfJje+Bbt6AvIfz6SdYt8/UEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ] + }, + "node_modules/esbuild-windows-32": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.3.tgz", + "integrity": "sha512-ZlgDz7d1nk8wQACi+z8IDzNZVUlN9iprAme+1YSTsfFDlkyI8jeaGWPk9EQFNY7rJzsLVYm6eZ2mhPioc7uT5A==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.3.tgz", + "integrity": "sha512-YX7KvRez3TR+GudlQm9tND/ssj2FsF9vb8ZWzAoZOLxpPzE3y+3SFJNrfDzzQKPzJ0Pnh9KBP4gsaMwJjKHDhw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.3.tgz", + "integrity": "sha512-nP7H0Y2a6OJd3Qi1Q8sehhyP4x4JoXK4S5y6FzH2vgaJgiyEurzFxjUufGdMaw+RxtxiwD/uRndUgwaZ2JD8lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -17750,6 +17990,142 @@ "is-symbol": "^1.0.2" } }, + "esbuild": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.13.3.tgz", + "integrity": "sha512-98xovMLKnyhv3gcReUuAEi5Ig1rK6SIgvsJuBIcfwzqGSEHsV8UJjMlmkhHoHMf9XZybMpE9Zax8AA8f7i2hlQ==", + "dev": true, + "requires": { + "esbuild-android-arm64": "0.13.3", + "esbuild-darwin-64": "0.13.3", + "esbuild-darwin-arm64": "0.13.3", + "esbuild-freebsd-64": "0.13.3", + "esbuild-freebsd-arm64": "0.13.3", + "esbuild-linux-32": "0.13.3", + "esbuild-linux-64": "0.13.3", + "esbuild-linux-arm": "0.13.3", + "esbuild-linux-arm64": "0.13.3", + "esbuild-linux-mips64le": "0.13.3", + "esbuild-linux-ppc64le": "0.13.3", + "esbuild-openbsd-64": "0.13.3", + "esbuild-sunos-64": "0.13.3", + "esbuild-windows-32": "0.13.3", + "esbuild-windows-64": "0.13.3", + "esbuild-windows-arm64": "0.13.3" + } + }, + "esbuild-android-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.13.3.tgz", + "integrity": "sha512-jc9E8vGTHkzb0Vwl74H8liANV9BWsqtzLHaKvcsRgf1M+aVCBSF0gUheduAKfDsbDMT0judeMLhwBP34EUesTA==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.13.3.tgz", + "integrity": "sha512-8bG3Zq+ZNuLlIJebOO2+weI7P2LVf33sOzaUfHj8MuJ+1Ixe4KtQxfYp7qhFnP6xP2ToJaYHxGUfLeiUCEz9hw==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.13.3.tgz", + "integrity": "sha512-5E81eImYtTgh8pY7Gq4WQHhWkR/LvYadUXmuYeZBiP+3ADZJZcG60UFceZrjqNPaFOWKr/xmh4aNocwagEubcA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.13.3.tgz", + "integrity": "sha512-ou+f91KkTGexi8HvF/BdtsITL6plbciQfZGys7QX6/QEwyE96PmL5KnU6ZQwoU7E99Ts6Sc9bUDq8HXJubKtBA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.13.3.tgz", + "integrity": "sha512-F1zV7nySjHswJuvIgjkiG5liZ63MeazDGXGKViTCeegjZ71sAhOChcaGhKcu6vq9+vqZxlfEi1fmXlx6Pc3coQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.13.3.tgz", + "integrity": "sha512-mHHc2v6uLrHH4zaaq5RB/5IWzgimEJ1HGldzf1qtGI513KZWfH0HRRQ8p1di4notJgBn7tDzWQ1f34ZHy69viQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.13.3.tgz", + "integrity": "sha512-FJ1De2O89mrOuqtaEXu41qIYJU6R41F+OA6vheNwcAQcX8fu0aiA13FJeLABq29BYJuTVgRj3cyC8q+tz19/dQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.13.3.tgz", + "integrity": "sha512-9BJNRtLwBh3OP22cln9g3AJdbAQUcjRHqA4BScx9k4RZpGqPokFr548zpeplxWhcwrIjT8qPebwH9CrRVy8Bsw==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.13.3.tgz", + "integrity": "sha512-Cauhr45KSo+wRUojs+1qfycQqQCAXTOvsWvkZ6xmEMAXLAm+f8RQGDQeP8CAf8Yeelnegcn6UNdvzdzLHhWDFg==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.13.3.tgz", + "integrity": "sha512-YVzJUGCncuuLm2boYyVeuMFsak4ZAhdiBwi0xNDZCC8sy+tS6Boe2mzcrD2uubv5JKAUOrpN186S1DtU4WgBgw==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.13.3.tgz", + "integrity": "sha512-GU6CqqKtJEoyxC2QWHiJtmuOz9wc/jMv8ZloK2WwiGY5yMvAmM3PI103Dj7xcjebNTHBqITTUw/aigY1wx5A3w==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.13.3.tgz", + "integrity": "sha512-HVpkgpn4BQt4BPDAjTOpeMub6mzNWw6Y3gaLQJrpbO24pws6ZwYkY24OI3/Uo3LDCbH6856MM81JxECt92OWjA==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.13.3.tgz", + "integrity": "sha512-XncBVOtnEfUbPV4CaiFBxh38ychnBfwCxuTm9iAqcHzIwkmeNRN5qMzDyfE1jyfJje+Bbt6AvIfz6SdYt8/UEQ==", + "dev": true, + "optional": true + }, + "esbuild-windows-32": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.13.3.tgz", + "integrity": "sha512-ZlgDz7d1nk8wQACi+z8IDzNZVUlN9iprAme+1YSTsfFDlkyI8jeaGWPk9EQFNY7rJzsLVYm6eZ2mhPioc7uT5A==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.13.3.tgz", + "integrity": "sha512-YX7KvRez3TR+GudlQm9tND/ssj2FsF9vb8ZWzAoZOLxpPzE3y+3SFJNrfDzzQKPzJ0Pnh9KBP4gsaMwJjKHDhw==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.13.3.tgz", + "integrity": "sha512-nP7H0Y2a6OJd3Qi1Q8sehhyP4x4JoXK4S5y6FzH2vgaJgiyEurzFxjUufGdMaw+RxtxiwD/uRndUgwaZ2JD8lg==", + "dev": true, + "optional": true + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", diff --git a/package.json b/package.json index e12fca9..492acd7 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,9 @@ }, "csso": { "optional": true + }, + "esbuild": { + "optional": true } }, "dependencies": { @@ -72,6 +75,7 @@ "csso": "^4.2.0", "del": "^6.0.0", "del-cli": "^4.0.0", + "esbuild": "^0.13.3", "eslint": "^7.30.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "^2.24.0", @@ -91,6 +95,9 @@ "keywords": [ "cssnano", "css", + "csso", + "clean-css", + "esbuild", "webpack", "webpack-plugin", "minimize", diff --git a/src/index.js b/src/index.js index 71f87e0..e408374 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,12 @@ import serialize from "serialize-javascript"; import pLimit from "p-limit"; import { Worker } from "jest-worker"; -import { cssnanoMinify, cssoMinify, cleanCssMinify } from "./utils"; +import { + cssnanoMinify, + cssoMinify, + cleanCssMinify, + esbuildMinify, +} from "./utils"; import * as schema from "./options.json"; import { minify as minifyFn } from "./minify"; @@ -484,5 +489,6 @@ class CssMinimizerPlugin { CssMinimizerPlugin.cssnanoMinify = cssnanoMinify; CssMinimizerPlugin.cssoMinify = cssoMinify; CssMinimizerPlugin.cleanCssMinify = cleanCssMinify; +CssMinimizerPlugin.esbuildMinify = esbuildMinify; export default CssMinimizerPlugin; diff --git a/src/utils.js b/src/utils.js index bd64f8a..d66aa87 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,6 @@ /* istanbul ignore next */ async function cssnanoMinify( - data, + input, inputSourceMap, minimizerOptions = { preset: "default" } ) { @@ -32,7 +32,7 @@ async function cssnanoMinify( } }; - const [[name, input]] = Object.entries(data); + const [[name, code]] = Object.entries(input); const postcssOptions = { to: name, from: name, @@ -78,7 +78,7 @@ async function cssnanoMinify( // eslint-disable-next-line global-require const cssnano = require("cssnano"); const result = await postcss([cssnano(minimizerOptions)]).process( - input, + code, postcssOptions ); @@ -90,11 +90,11 @@ async function cssnanoMinify( } /* istanbul ignore next */ -async function cssoMinify(data, inputSourceMap, minimizerOptions) { +async function cssoMinify(input, inputSourceMap, minimizerOptions) { // eslint-disable-next-line global-require,import/no-extraneous-dependencies const csso = require("csso"); - const [[filename, input]] = Object.entries(data); - const result = csso.minify(input, { + const [[filename, code]] = Object.entries(input); + const result = csso.minify(code, { filename, sourceMap: Boolean(inputSourceMap), ...minimizerOptions, @@ -107,14 +107,14 @@ async function cssoMinify(data, inputSourceMap, minimizerOptions) { } /* istanbul ignore next */ -async function cleanCssMinify(data, inputSourceMap, minimizerOptions) { +async function cleanCssMinify(input, inputSourceMap, minimizerOptions) { // eslint-disable-next-line global-require,import/no-extraneous-dependencies const CleanCSS = require("clean-css"); - const [[name, input]] = Object.entries(data); + const [[name, code]] = Object.entries(input); const result = await new CleanCSS({ sourceMap: Boolean(inputSourceMap), ...minimizerOptions, - }).minify({ [name]: { styles: input } }); + }).minify({ [name]: { styles: code } }); return { code: result.styles, @@ -123,4 +123,45 @@ async function cleanCssMinify(data, inputSourceMap, minimizerOptions) { }; } -export { cssnanoMinify, cssoMinify, cleanCssMinify }; +/* istanbul ignore next */ +async function esbuildMinify(input, sourceMap, minimizerOptions) { + const buildEsbuildOptions = (esbuildOptions = {}) => { + // Need deep copy objects to avoid https://github.com/terser/terser/issues/366 + return { + loader: "css", + minify: true, + legalComments: "inline", + ...esbuildOptions, + sourcemap: false, + }; + }; + + // eslint-disable-next-line import/no-extraneous-dependencies, global-require + const esbuild = require("esbuild"); + + // Copy `swc` options + const esbuildOptions = buildEsbuildOptions(minimizerOptions); + + // Let `swc` generate a SourceMap + if (sourceMap) { + esbuildOptions.sourcemap = true; + esbuildOptions.sourcesContent = false; + } + + const [[filename, code]] = Object.entries(input); + + esbuildOptions.sourcefile = filename; + + const result = await esbuild.transform(code, esbuildOptions); + + return { + code: result.code, + // eslint-disable-next-line no-undefined + map: result.map ? result.map : undefined, + warnings: result.warnings + ? result.warnings.map((item) => item.toString()) + : [], + }; +} + +export { cssnanoMinify, cssoMinify, cleanCssMinify, esbuildMinify }; diff --git a/test/__snapshots__/minify-option.test.js.snap b/test/__snapshots__/minify-option.test.js.snap index c75434d..cd9cc2e 100644 --- a/test/__snapshots__/minify-option.test.js.snap +++ b/test/__snapshots__/minify-option.test.js.snap @@ -199,6 +199,30 @@ exports[`"minify" option should work with "CssMinimizerPlugin.cssoMinify" minifi exports[`"minify" option should work with "CssMinimizerPlugin.cssoMinify" minifier: warning 1`] = `Array []`; +exports[`"minify" option should work with "CssMinimizerPlugin.esbuildMinify" minifier and generate source maps: assets 1`] = ` +Object { + "foo.css": "body{color:red}a{color:#00f} + +/*# sourceMappingURL=foo.css.map*/", + "foo.css.map": "{\\"version\\":3,\\"file\\":\\"foo.css\\",\\"mappings\\":\\"AAAA,KACE,UAEF,EACE\\",\\"sources\\":[\\"webpack:///./foo.css\\"],\\"sourcesContent\\":[\\"body {\\\\n color: red;\\\\n}\\\\na {\\\\n color: blue;\\\\n}\\"],\\"names\\":[],\\"sourceRoot\\":\\"\\"}", +} +`; + +exports[`"minify" option should work with "CssMinimizerPlugin.esbuildMinify" minifier and generate source maps: error 1`] = `Array []`; + +exports[`"minify" option should work with "CssMinimizerPlugin.esbuildMinify" minifier and generate source maps: warning 1`] = `Array []`; + +exports[`"minify" option should work with "CssMinimizerPlugin.esbuildMinify" minifier: assets 1`] = ` +Object { + "foo.css": "body{font-weight:bold}body{color:red}body a{text-align:center} +", +} +`; + +exports[`"minify" option should work with "CssMinimizerPlugin.esbuildMinify" minifier: error 1`] = `Array []`; + +exports[`"minify" option should work with "CssMinimizerPlugin.esbuildMinify" minifier: warning 1`] = `Array []`; + exports[`"minify" option should work with "clean-css" minifier: assets 1`] = ` Object { "foo.css": "body{color:red}a{color:#00f}", diff --git a/test/minify-option.test.js b/test/minify-option.test.js index 88242fa..fa28aea 100644 --- a/test/minify-option.test.js +++ b/test/minify-option.test.js @@ -446,6 +446,59 @@ describe('"minify" option', () => { expect(getWarnings(stats)).toMatchSnapshot("warning"); }); + it('should work with "CssMinimizerPlugin.esbuildMinify" minifier', async () => { + const compiler = getCompiler({ + entry: { + foo: `${__dirname}/fixtures/sourcemap/foo.scss`, + }, + module: { + rules: [ + { + test: /.s?css$/i, + use: [ + MiniCssExtractPlugin.loader, + { loader: "css-loader", options: { sourceMap: true } }, + { loader: "sass-loader", options: { sourceMap: true } }, + ], + }, + ], + }, + }); + + new CssMinimizerPlugin({ + minify: CssMinimizerPlugin.esbuildMinify, + }).apply(compiler); + + const stats = await compile(compiler); + + expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot( + "assets" + ); + expect(getErrors(stats)).toMatchSnapshot("error"); + expect(getWarnings(stats)).toMatchSnapshot("warning"); + }); + + it('should work with "CssMinimizerPlugin.esbuildMinify" minifier and generate source maps', async () => { + const compiler = getCompiler({ + devtool: "source-map", + entry: { + foo: `${__dirname}/fixtures/foo.css`, + }, + }); + + new CssMinimizerPlugin({ + minify: CssMinimizerPlugin.esbuildMinify, + }).apply(compiler); + + const stats = await compile(compiler); + + expect(readAssets(compiler, stats, /\.css(\.map)?$/)).toMatchSnapshot( + "assets" + ); + expect(getErrors(stats)).toMatchSnapshot("error"); + expect(getWarnings(stats)).toMatchSnapshot("warning"); + }); + it('should work with "CssMinimizerPlugin.cssnanoMinify", "CssMinimizerPlugin.cssoMinify" and "CssMinimizerPlugin.cleanCssMinify" minifiers', async () => { const compiler = getCompiler({ entry: {