From fd9abac59dc82561ef1db603165526caba6e9abe Mon Sep 17 00:00:00 2001 From: Alexey Lavinsky Date: Thu, 3 Sep 2020 18:36:50 +0300 Subject: [PATCH] fix: do not crash on the `minify` option --- README.md | 36 +++++++++++--- src/minify.js | 7 +-- test/CssMinimizerPlugin.test.js | 47 +++++++++++++++---- .../CssMinimizerPlugin.test.js.snap.webpack4 | 6 +-- .../CssMinimizerPlugin.test.js.snap.webpack5 | 6 +-- test/sourceMap-option.test.js | 25 +++++++++- test/warningsFilter-option.test.js | 4 +- 7 files changed, 104 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index ddfab24..27d884b 100644 --- a/README.md +++ b/README.md @@ -311,9 +311,9 @@ module.exports = { minimize: true, minimizer: [ new CssMinimizerPlugin({ - minify: (data) => { + sourceMap: true, + minify: (data, inputMap, minimizerOptions) => { const postcss = require('postcss'); - const { input, postcssOptions, minimizerOptions } = data; const plugin = postcss.plugin( 'custom-plugin', @@ -322,6 +322,16 @@ module.exports = { } ); + const [[filename, input]] = Object.entries(data); + + const postcssOptions = { + from: filename, + to: filename, + map: { + prev: inputMap, + }, + }; + return postcss([plugin]) .process(input, postcssOptions) .then((result) => { @@ -475,15 +485,24 @@ module.exports = { minimize: true, minimizer: [ new CssMinimizerPlugin({ - minify: ({ input, postcssOptions }) => { - // eslint-disable-next-line global-require + sourceMap: true, + 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: postcssOptions.from, + filename: filename, sourceMap: true, }); + if (inputMap) { + minifiedCss.map.applySourceMap( + new sourcemap.SourceMapConsumer(inputMap), + filename + ); + } + return { css: minifiedCss.css, map: minifiedCss.map.toJSON(), @@ -511,13 +530,16 @@ module.exports = { minimize: true, minimizer: [ new CssMinimizerPlugin({ - minify: async ({ input, postcssOptions }) => { + sourceMap: true, + minify: async (data, inputMap) => { // eslint-disable-next-line global-require const CleanCSS = require('clean-css'); + const [[filename, input]] = Object.entries(data); const minifiedCss = await new CleanCSS({ sourceMap: true }).minify({ - [postcssOptions.from]: { + [filename]: { styles: input, + sourceMap: inputMap, }, }); diff --git a/src/minify.js b/src/minify.js index 7fa8165..10e53b1 100644 --- a/src/minify.js +++ b/src/minify.js @@ -22,8 +22,9 @@ const minify = async (options) => { if (minifyFn) { const result = await minifyFn( - { input, postcssOptions, minimizerOptions }, - inputSourceMap + { [assetName]: input }, + inputSourceMap, + minimizerOptions ); return { @@ -34,7 +35,7 @@ const minify = async (options) => { } if (inputSourceMap) { - postcssOptions.map = { prev: inputSourceMap, ...map }; + postcssOptions.map = { annotation: false, prev: inputSourceMap, ...map }; } const result = await cssnano.process(input, postcssOptions, minimizerOptions); diff --git a/test/CssMinimizerPlugin.test.js b/test/CssMinimizerPlugin.test.js index ca04fda..f5a87cf 100644 --- a/test/CssMinimizerPlugin.test.js +++ b/test/CssMinimizerPlugin.test.js @@ -274,8 +274,10 @@ describe('CssMinimizerPlugin', () => { }); }); + const [[filename, input]] = Object.entries(data); + return postcss([plugin]) - .process(data.input, data.postcssOptions) + .process(input, { from: filename, to: filename }) .then((result) => { return result; }); @@ -315,8 +317,10 @@ describe('CssMinimizerPlugin', () => { }); }); + const [[filename, input]] = Object.entries(data); + return postcss([plugin]) - .process(data.input, data.postcssOptions) + .process(input, { from: filename, to: filename }) .then((result) => { return { css: result.css, @@ -612,20 +616,43 @@ describe('CssMinimizerPlugin', () => { const compiler = getCompiler({ devtool: 'source-map', entry: { - foo: `${__dirname}/fixtures/foo.css`, + 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: ({ input, postcssOptions }) => { + sourceMap: true, + minify: async (data, inputMap) => { // eslint-disable-next-line global-require const csso = require('csso'); + // eslint-disable-next-line global-require + const sourcemap = require('source-map'); + const [[filename, input]] = Object.entries(data); const minifiedCss = csso.minify(input, { - filename: postcssOptions.from, + filename, sourceMap: true, }); + if (inputMap) { + minifiedCss.map.applySourceMap( + new sourcemap.SourceMapConsumer(inputMap), + filename + ); + } + return { css: minifiedCss.css, map: minifiedCss.map.toJSON(), @@ -638,7 +665,7 @@ describe('CssMinimizerPlugin', () => { const maps = readAssets(compiler, stats, '.css.map'); Object.keys(maps).forEach((assetKey) => { - expect(maps[assetKey]).toMatchSnapshot(assetKey); + expect(normalizedSourceMap(maps[assetKey])).toMatchSnapshot(assetKey); }); expect(readAssets(compiler, stats, '.css')).toMatchSnapshot('assets'); @@ -655,13 +682,15 @@ describe('CssMinimizerPlugin', () => { }); new CssMinimizerPlugin({ - minify: async ({ input, postcssOptions }) => { + minify: async (data, inputMap) => { // eslint-disable-next-line global-require const CleanCSS = require('clean-css'); + const [[filename, input]] = Object.entries(data); const minifiedCss = await new CleanCSS({ sourceMap: true }).minify({ - [postcssOptions.from]: { + [filename]: { styles: input, + sourceMap: inputMap, }, }); @@ -678,7 +707,7 @@ describe('CssMinimizerPlugin', () => { const maps = readAssets(compiler, stats, '.css.map'); Object.keys(maps).forEach((assetKey) => { - expect(maps[assetKey]).toMatchSnapshot(assetKey); + expect(normalizedSourceMap(maps[assetKey])).toMatchSnapshot(assetKey); }); expect(readAssets(compiler, stats, '.css')).toMatchSnapshot('assets'); diff --git a/test/__snapshots__/CssMinimizerPlugin.test.js.snap.webpack4 b/test/__snapshots__/CssMinimizerPlugin.test.js.snap.webpack4 index 51ffe3a..97da3da 100644 --- a/test/__snapshots__/CssMinimizerPlugin.test.js.snap.webpack4 +++ b/test/__snapshots__/CssMinimizerPlugin.test.js.snap.webpack4 @@ -148,20 +148,20 @@ Object { exports[`CssMinimizerPlugin should work with custom clean-css minifier: error 1`] = `Array []`; -exports[`CssMinimizerPlugin should work with custom clean-css minifier: foo.css.map 1`] = `"{\\"version\\":3,\\"sources\\":[\\"webpack:///foo.css\\"],\\"names\\":[],\\"mappings\\":\\"AAAA,KACE,MAAO,IAET,EACE,MAAO\\",\\"file\\":\\"foo.css\\",\\"sourcesContent\\":[\\"body {\\\\n color: red;\\\\n}\\\\na {\\\\n color: blue;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}"`; +exports[`CssMinimizerPlugin should work with custom clean-css minifier: foo.css.map 1`] = `"{\\"version\\":3,\\"sources\\": [replaced for tests], \\"names\\":[],\\"mappings\\":\\"AAAA,KACE,MAAO,IAET,EACE,MAAO\\",\\"file\\":\\"foo.css\\",\\"sourcesContent\\":[\\"body {\\\\n color: red;\\\\n}\\\\na {\\\\n color: blue;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}"`; exports[`CssMinimizerPlugin should work with custom clean-css minifier: warning 1`] = `Array []`; exports[`CssMinimizerPlugin should work with custom csso minifier: assets 1`] = ` Object { - "foo.css": "body{color:red}a{color:#00f} + "foo.css": "body{font-weight:700;color:red}body a{text-align:center} /*# sourceMappingURL=foo.css.map*/", } `; exports[`CssMinimizerPlugin should work with custom csso minifier: error 1`] = `Array []`; -exports[`CssMinimizerPlugin should work with custom csso minifier: foo.css.map 1`] = `"{\\"version\\":3,\\"sources\\":[\\"webpack:///foo.css\\"],\\"names\\":[],\\"mappings\\":\\"AAAA,I,CACE,S,CAEF,C,CACE,U\\",\\"file\\":\\"foo.css\\",\\"sourcesContent\\":[\\"body {\\\\n color: red;\\\\n}\\\\na {\\\\n color: blue;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}"`; +exports[`CssMinimizerPlugin should work with custom csso minifier: foo.css.map 1`] = `"{\\"version\\":3,\\"sources\\": [replaced for tests], \\"names\\":[],\\"mappings\\":\\"AAAA,I,CACE,e,CCEA,S,CADF,M,CAGI,iB\\",\\"file\\":\\"foo.css\\",\\"sourcesContent\\":[\\"body {\\\\n font-weight: bold;\\\\n}\\",\\"@import 'bar';\\\\n\\\\nbody {\\\\n color: red;\\\\n a {\\\\n text-align: center;\\\\n }\\\\n}\\"],\\"sourceRoot\\":\\"\\"}"`; exports[`CssMinimizerPlugin should work with custom csso minifier: warning 1`] = `Array []`; diff --git a/test/__snapshots__/CssMinimizerPlugin.test.js.snap.webpack5 b/test/__snapshots__/CssMinimizerPlugin.test.js.snap.webpack5 index 7aabd75..06587b6 100644 --- a/test/__snapshots__/CssMinimizerPlugin.test.js.snap.webpack5 +++ b/test/__snapshots__/CssMinimizerPlugin.test.js.snap.webpack5 @@ -158,20 +158,20 @@ Object { exports[`CssMinimizerPlugin should work with custom clean-css minifier: error 1`] = `Array []`; -exports[`CssMinimizerPlugin should work with custom clean-css minifier: foo.css.map 1`] = `"{\\"version\\":3,\\"sources\\":[\\"webpack:///foo.css\\"],\\"names\\":[],\\"mappings\\":\\"AAAA,KACE,MAAO,IAET,EACE,MAAO\\",\\"sourceRoot\\":\\"\\",\\"file\\":\\"foo.css\\"}"`; +exports[`CssMinimizerPlugin should work with custom clean-css minifier: foo.css.map 1`] = `"{\\"version\\":3,\\"sources\\": [replaced for tests], \\"names\\":[],\\"mappings\\":\\"AAAA,KACE,MAAO,IAET,EACE,MAAO\\",\\"sourceRoot\\":\\"\\",\\"file\\":\\"foo.css\\"}"`; exports[`CssMinimizerPlugin should work with custom clean-css minifier: warning 1`] = `Array []`; exports[`CssMinimizerPlugin should work with custom csso minifier: assets 1`] = ` Object { - "foo.css": "body{color:red}a{color:#00f} + "foo.css": "body{font-weight:700;color:red}body a{text-align:center} /*# sourceMappingURL=foo.css.map*/", } `; exports[`CssMinimizerPlugin should work with custom csso minifier: error 1`] = `Array []`; -exports[`CssMinimizerPlugin should work with custom csso minifier: foo.css.map 1`] = `"{\\"version\\":3,\\"sources\\":[\\"webpack:///foo.css\\"],\\"names\\":[],\\"mappings\\":\\"AAAA,I,CACE,S,CAEF,C,CACE,U\\",\\"file\\":\\"foo.css\\",\\"sourcesContent\\":[\\"body {\\\\n color: red;\\\\n}\\\\na {\\\\n color: blue;\\\\n}\\\\n\\"],\\"sourceRoot\\":\\"\\"}"`; +exports[`CssMinimizerPlugin should work with custom csso minifier: foo.css.map 1`] = `"{\\"version\\":3,\\"sources\\": [replaced for tests], \\"names\\":[],\\"mappings\\":\\"AAAA,I,CACE,e,CCEA,S,CADF,M,CAGI,iB\\",\\"file\\":\\"foo.css\\",\\"sourcesContent\\":[\\"body {\\\\n font-weight: bold;\\\\n}\\",\\"@import 'bar';\\\\n\\\\nbody {\\\\n color: red;\\\\n a {\\\\n text-align: center;\\\\n }\\\\n}\\"],\\"sourceRoot\\":\\"\\"}"`; exports[`CssMinimizerPlugin should work with custom csso minifier: warning 1`] = `Array []`; diff --git a/test/sourceMap-option.test.js b/test/sourceMap-option.test.js index 356e739..3b89df6 100644 --- a/test/sourceMap-option.test.js +++ b/test/sourceMap-option.test.js @@ -354,8 +354,10 @@ describe('when applied with "sourceMap" option', () => { }); }); + const [[filename, input]] = Object.entries(data); + return postcss([plugin]) - .process(data.input, data.postcssOptions) + .process(input, { from: filename, to: filename }) .then((result) => { return result; }); @@ -367,4 +369,25 @@ describe('when applied with "sourceMap" option', () => { expect(getErrors(stats)).toMatchSnapshot('errors'); expect(getWarnings(stats)).toMatchSnapshot('warnings'); }); + + it('should do not contain sourcemap link in minified source', async () => { + const compiler = getCompiler({ + devtool: 'source-map', + entry: { + foo: `${__dirname}/fixtures/foo.css`, + }, + }); + + new CssMinimizerPlugin({ + sourceMap: false, + }).apply(compiler); + + const stats = await compile(compiler); + + const assets = readAssets(compiler, stats, '.css'); + + const [[, input]] = Object.entries(assets); + + expect(/sourceMappingURL/i.test(input)).toBe(false); + }); }); diff --git a/test/warningsFilter-option.test.js b/test/warningsFilter-option.test.js index 624f7e5..ee42a10 100644 --- a/test/warningsFilter-option.test.js +++ b/test/warningsFilter-option.test.js @@ -34,8 +34,10 @@ describe('warningsFilter option', () => { new CssMinimizerPlugin({ parallel: false, minify: (data) => { + const [[fileName, input]] = Object.entries(data); + return postcss([plugin]) - .process(data.input, data.postcssOptions) + .process(input, { from: fileName, to: fileName }) .then((result) => { return { css: result.css,