diff --git a/src/index.js b/src/index.js index 3bf5e6af..0bd13fd4 100644 --- a/src/index.js +++ b/src/index.js @@ -186,133 +186,146 @@ class TerserPlugin { const optimizeFn = async (compilation, chunks) => { const processedAssets = new WeakSet(); + const matchObject = ModuleFilenameHelpers.matchObject.bind( + // eslint-disable-next-line no-undefined + undefined, + this.options + ); + const additionalChunkAssets = Array.from( + compilation.additionalChunkAssets || [] + ); + const filteredChunks = Array.from(chunks).filter( + (chunk) => this.options.chunkFilter && this.options.chunkFilter(chunk) + ); + const chunksFiles = filteredChunks.reduce( + (acc, chunk) => acc.concat(Array.from(chunk.files || [])), + [] + ); + const files = [].concat(additionalChunkAssets).concat(chunksFiles); const tasks = []; - const { chunkFilter } = this.options; + files.forEach((file) => { + if (!matchObject(file)) { + return; + } - Array.from(chunks) - .filter((chunk) => chunkFilter && chunkFilter(chunk)) - .reduce((acc, chunk) => acc.concat(Array.from(chunk.files || [])), []) - .concat(Array.from(compilation.additionalChunkAssets || [])) - .filter(ModuleFilenameHelpers.matchObject.bind(null, this.options)) - .forEach((file) => { - let inputSourceMap; + let inputSourceMap; - const asset = compilation.assets[file]; + const asset = compilation.assets[file]; - if (processedAssets.has(asset)) { - return; - } - - try { - let input; + if (processedAssets.has(asset)) { + return; + } - if (this.options.sourceMap && asset.sourceAndMap) { - const { source, map } = asset.sourceAndMap(); + try { + let input; - input = source; + if (this.options.sourceMap && asset.sourceAndMap) { + const { source, map } = asset.sourceAndMap(); - if (TerserPlugin.isSourceMap(map)) { - inputSourceMap = map; - } else { - inputSourceMap = map; + input = source; - compilation.warnings.push( - new Error(`${file} contains invalid source map`) - ); - } + if (TerserPlugin.isSourceMap(map)) { + inputSourceMap = map; } else { - input = asset.source(); - inputSourceMap = null; + inputSourceMap = map; + + compilation.warnings.push( + new Error(`${file} contains invalid source map`) + ); } + } else { + input = asset.source(); + inputSourceMap = null; + } - // Handling comment extraction - let commentsFilename = false; + // Handling comment extraction + let commentsFilename = false; - if (this.options.extractComments) { - commentsFilename = - this.options.extractComments.filename || - '[file].LICENSE[query]'; + if (this.options.extractComments) { + commentsFilename = + this.options.extractComments.filename || '[file].LICENSE[query]'; - // Todo remove this in next major release - if (typeof commentsFilename === 'function') { - commentsFilename = commentsFilename.bind(null, file); - } + // Todo remove this in next major release + if (typeof commentsFilename === 'function') { + commentsFilename = commentsFilename.bind(null, file); + } - let query = ''; - let filename = file; + let query = ''; + let filename = file; - const querySplit = filename.indexOf('?'); + const querySplit = filename.indexOf('?'); - if (querySplit >= 0) { - query = filename.substr(querySplit); - filename = filename.substr(0, querySplit); - } + if (querySplit >= 0) { + query = filename.substr(querySplit); + filename = filename.substr(0, querySplit); + } - const lastSlashIndex = filename.lastIndexOf('/'); + const lastSlashIndex = filename.lastIndexOf('/'); - const basename = - lastSlashIndex === -1 - ? filename - : filename.substr(lastSlashIndex + 1); + const basename = + lastSlashIndex === -1 + ? filename + : filename.substr(lastSlashIndex + 1); - const data = { filename, basename, query }; + const data = { filename, basename, query }; - commentsFilename = compilation.getPath(commentsFilename, data); - } + commentsFilename = compilation.getPath(commentsFilename, data); + } - if ( - commentsFilename && - TerserPlugin.hasAsset(commentsFilename, compilation.assets) - ) { - // Todo make error and stop uglifing in next major release - compilation.warnings.push( - new Error( - `The comment file "${TerserPlugin.removeQueryString( - commentsFilename - )}" conflicts with an existing asset, this may lead to code corruption, please use a different name` - ) - ); - } + if ( + commentsFilename && + TerserPlugin.hasAsset(commentsFilename, compilation.assets) + ) { + // Todo make error and stop uglifing in next major release + compilation.warnings.push( + new Error( + `The comment file "${TerserPlugin.removeQueryString( + commentsFilename + )}" conflicts with an existing asset, this may lead to code corruption, please use a different name` + ) + ); + } - const task = { - file, - input, - inputSourceMap, - commentsFilename, - extractComments: this.options.extractComments, - terserOptions: this.options.terserOptions, - minify: this.options.minify, + const task = { + file, + input, + inputSourceMap, + commentsFilename, + extractComments: this.options.extractComments, + terserOptions: this.options.terserOptions, + minify: this.options.minify, + }; + + if (this.options.cache) { + const defaultCacheKeys = { + terser: terserPackageJson.version, + // eslint-disable-next-line global-require + 'terser-webpack-plugin': require('../package.json').version, + 'terser-webpack-plugin-options': this.options, + nodeVersion: process.version, + filename: file, + contentHash: crypto + .createHash('md4') + .update(input) + .digest('hex'), }; - if (this.options.cache) { - const defaultCacheKeys = { - terser: terserPackageJson.version, - node_version: process.version, - // eslint-disable-next-line global-require - 'terser-webpack-plugin': require('../package.json').version, - 'terser-webpack-plugin-options': this.options, - hash: crypto - .createHash('md4') - .update(input) - .digest('hex'), - }; - - task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file); - } - - tasks.push(task); - } catch (error) { - compilation.errors.push( - TerserPlugin.buildError( - error, - file, - TerserPlugin.buildSourceMap(inputSourceMap), - new RequestShortener(compiler.context) - ) - ); + task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file); } - }); + + tasks.push(task); + } catch (error) { + compilation.errors.push( + TerserPlugin.buildError( + error, + file, + TerserPlugin.buildSourceMap(inputSourceMap), + new RequestShortener(compiler.context) + ) + ); + } + }); if (tasks.length === 0) { return Promise.resolve(); diff --git a/test/__snapshots__/cache-option.test.js.snap b/test/__snapshots__/cache-option.test.js.snap index fe3b5a10..a12f9e7f 100644 --- a/test/__snapshots__/cache-option.test.js.snap +++ b/test/__snapshots__/cache-option.test.js.snap @@ -1,5 +1,33 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`cache option should match snapshot and invalid cache when entry point was renamed: assets 1`] = ` +Object { + "five.js": "!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\\"a\\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\\"\\",n(n.s=4)}({4:function(e,t){e.exports=function(){console.log(7)}}});", + "four.js": "!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\\"a\\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\\"\\",r(r.s=3)}({3:function(e,t){e.exports=class{}}});", + "one.js": "!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\\"a\\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\\"\\",n(n.s=0)}([function(e,t){e.exports=function(){console.log(7)}}]);", + "three.js": "!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\\"a\\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\\"\\",r(r.s=2)}({2:function(e,t){e.exports=()=>/test/}});", + "two.js": "!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\\"a\\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\\"\\",r(r.s=1)}([,function(e,t){e.exports=\\"string\\"}]);", +} +`; + +exports[`cache option should match snapshot and invalid cache when entry point was renamed: assets 2`] = ` +Object { + "five.js": "!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\\"a\\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\\"\\",n(n.s=4)}({4:function(e,t){e.exports=function(){console.log(7)}}});", + "four.js": "!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\\"a\\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\\"\\",r(r.s=3)}({3:function(e,t){e.exports=class{}}});", + "onne.js": "!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\\"a\\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\\"\\",n(n.s=0)}([function(e,t){e.exports=function(){console.log(7)}}]);", + "three.js": "!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\\"a\\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\\"\\",r(r.s=2)}({2:function(e,t){e.exports=()=>/test/}});", + "two.js": "!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\\"a\\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\\"\\",r(r.s=1)}([,function(e,t){e.exports=\\"string\\"}]);", +} +`; + +exports[`cache option should match snapshot and invalid cache when entry point was renamed: errors 1`] = `Array []`; + +exports[`cache option should match snapshot and invalid cache when entry point was renamed: errors 2`] = `Array []`; + +exports[`cache option should match snapshot and invalid cache when entry point was renamed: warnings 1`] = `Array []`; + +exports[`cache option should match snapshot and invalid cache when entry point was renamed: warnings 2`] = `Array []`; + exports[`cache option should match snapshot for errors into the "cacheKeys" option: errors 1`] = ` Array [ "Error: five.js from Terser diff --git a/test/cache-option.test.js b/test/cache-option.test.js index b06382c5..9da00247 100644 --- a/test/cache-option.test.js +++ b/test/cache-option.test.js @@ -20,8 +20,11 @@ const otherCacheDir = findCacheDir({ name: 'other-cache-directory' }); const otherOtherCacheDir = findCacheDir({ name: 'other-other-cache-directory', }); +const otherOtherOtherCacheDir = findCacheDir({ + name: 'other-other-other-cache-directory', +}); -// jest.setTimeout(30000); +jest.setTimeout(30000); describe('cache option', () => { let compiler; @@ -43,6 +46,7 @@ describe('cache option', () => { removeCache(uniqueOtherDirectory), removeCache(otherCacheDir), removeCache(otherOtherCacheDir), + removeCache(otherOtherOtherCacheDir), ]); }); @@ -53,6 +57,7 @@ describe('cache option', () => { removeCache(uniqueOtherDirectory), removeCache(otherCacheDir), removeCache(otherOtherCacheDir), + removeCache(otherOtherOtherCacheDir), ]); }); @@ -296,4 +301,68 @@ describe('cache option', () => { expect(errors).toMatchSnapshot('errors'); expect(warnings).toMatchSnapshot('warnings'); }); + + it('should match snapshot and invalid cache when entry point was renamed', async () => { + const cacacheGetSpy = jest.spyOn(cacache, 'get'); + const cacachePutSpy = jest.spyOn(cacache, 'put'); + + const getCacheDirectorySpy = jest + .spyOn(TaskRunner, 'getCacheDirectory') + .mockImplementation(() => { + return otherOtherOtherCacheDir; + }); + + new TerserPlugin({ cache: true }).apply(compiler); + + const stats = await compile(compiler); + + const errors = stats.compilation.errors.map(cleanErrorStack); + const warnings = stats.compilation.warnings.map(cleanErrorStack); + + expect(errors).toMatchSnapshot('errors'); + expect(warnings).toMatchSnapshot('warnings'); + expect(getAssets(stats, compiler)).toMatchSnapshot('assets'); + + const countAssets = Object.keys(stats.compilation.assets).length; + + // Try to found cached files, but we don't have their in cache + expect(cacacheGetSpy).toHaveBeenCalledTimes(countAssets); + // Put files in cache + expect(cacachePutSpy).toHaveBeenCalledTimes(countAssets); + + cacache.get.mockClear(); + cacache.put.mockClear(); + + compiler = createCompiler({ + entry: { + onne: `${__dirname}/fixtures/cache.js`, + two: `${__dirname}/fixtures/cache-1.js`, + three: `${__dirname}/fixtures/cache-2.js`, + four: `${__dirname}/fixtures/cache-3.js`, + five: `${__dirname}/fixtures/cache-4.js`, + }, + }); + + new TerserPlugin({ cache: true }).apply(compiler); + + const newStats = await compile(compiler); + + const newErrors = newStats.compilation.errors.map(cleanErrorStack); + const newWarnings = newStats.compilation.warnings.map(cleanErrorStack); + + expect(newErrors).toMatchSnapshot('errors'); + expect(newWarnings).toMatchSnapshot('warnings'); + + expect(getAssets(newStats, compiler)).toMatchSnapshot('assets'); + + const newCountAssets = Object.keys(newStats.compilation.assets).length; + + // Now we have cached files so we get them and don't put new + expect(cacacheGetSpy).toHaveBeenCalledTimes(newCountAssets); + expect(cacachePutSpy).toHaveBeenCalledTimes(1); + + cacacheGetSpy.mockRestore(); + cacachePutSpy.mockRestore(); + getCacheDirectorySpy.mockRestore(); + }); });