Skip to content

Commit c1a6f43

Browse files
fix: do not salt the hash of a chunk no minimizer would be handed
The salt makes a chunk's name vary with what its minimizer is and what it is run with, which is only true of a chunk one of them rewrites. Where `test` names something else, or every minimizer's own filter declines the name, the chunk is left alone and salting it renamed a file nothing here had touched. The name is read off the template while the hash inside it is still being computed, so every hash placeholder stands for one character: what is asked of the name is its path and extension, which no hash carries. A template a function names is unknowable, and salts as before.
1 parent 6709943 commit c1a6f43

4 files changed

Lines changed: 157 additions & 4 deletions

File tree

‎src/index.js‎

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,34 @@ const declaredStage = (compiler, implementation) => {
256256
return latest;
257257
};
258258

259+
/**
260+
* The name a chunk's JavaScript asset will take, as far as it is knowable
261+
* while the hash that name contains is still being computed.
262+
* @param {Compilation} compilation compilation
263+
* @param {import("webpack").Chunk} chunk chunk
264+
* @returns {string | undefined} the name, or undefined where a function names it
265+
*/
266+
const chunkAssetName = (compilation, chunk) => {
267+
const { outputOptions } = compilation;
268+
const template =
269+
chunk.filenameTemplate ||
270+
(chunk.canBeInitial()
271+
? outputOptions.filename
272+
: outputOptions.chunkFilename);
273+
274+
if (typeof template !== "string") {
275+
return undefined;
276+
}
277+
278+
// Every hash stands for one character it has not got yet: what is being
279+
// asked of the name is its path and extension, which no hash carries.
280+
return template
281+
.replace(/\[(?:full|chunk|content)hash(?::\d+)?]/gi, "0")
282+
.replace(/\[name]/gi, String(chunk.name || chunk.id || ""))
283+
.replace(/\[id]/gi, String(chunk.id || ""))
284+
.replace(/\[runtime]/gi, String(chunk.runtime || ""));
285+
};
286+
259287
/**
260288
* The names an implementation's work goes under in an asset's info, which is
261289
* the union where several ran as one chain.
@@ -562,6 +590,31 @@ class MinimizerPlugin {
562590
return !(exclude && (matchPart(name, exclude) || matchPart(bare, exclude)));
563591
}
564592

593+
/**
594+
* Whether any configured minimizer would be handed an asset of this name,
595+
* by the plugin's own `test`/`include`/`exclude` and then by its own filter.
596+
* @private
597+
* @param {Compiler} compiler compiler
598+
* @param {string} name asset name
599+
* @returns {boolean} true when one of them would take it
600+
*/
601+
minifiesName(compiler, name) {
602+
if (!this.matchesName(compiler, name)) {
603+
return false;
604+
}
605+
606+
const { filters } = this.options.minimizer;
607+
608+
return this.minimizers().some((implementation, i) => {
609+
const decides =
610+
filters && typeof filters[i] === "function"
611+
? filters[i]
612+
: implementation.filter;
613+
614+
return typeof decides !== "function" || decides(name, {}) !== false;
615+
});
616+
}
617+
565618
/**
566619
* @private
567620
* @param {Compiler} compiler compiler
@@ -2283,9 +2336,20 @@ class MinimizerPlugin {
22832336
options: this.options.minimizer.options,
22842337
});
22852338

2286-
// The salt is the name this plugin shipped under, and every `[contenthash]`
2287-
// is taken over it: renaming it would rename every file a user serves.
22882339
hooks.chunkHash.tap(pluginName, (chunk, hash) => {
2340+
const willBe = chunkAssetName(compilation, chunk);
2341+
2342+
// A chunk no minimizer here would be handed cannot vary with them, so
2343+
// salting it would rename a file this instance never rewrites.
2344+
if (
2345+
typeof willBe === "string" &&
2346+
!this.minifiesName(compiler, willBe)
2347+
) {
2348+
return;
2349+
}
2350+
2351+
// The salt is the name this plugin shipped under, and every
2352+
// `[fullhash]` is taken over it: renaming it would rename every file.
22892353
hash.update("TerserPlugin");
22902354
hash.update(data);
22912355
});

‎test/__snapshots__/test-option.test.js.snap‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ __webpack_require__.r(__webpack_exports__);
715715
/***/ }
716716
717717
}]);",
718-
"AsyncImportExport.js?var=__hash0__": "(()=>{"use strict";var e={};const t={};function r(o){const n=t[o];if(void 0!==n)return n.exports;const i=t[o]={exports:{}};return e[o](i,i.exports,r),i.exports}r.m=e,r.d=(e,t)=>{if(Array.isArray(t))for(var o=0;o<t.length;){var n=t[o++],i=t[o++];r.o(e,n)?0===i&&o++:0===i?Object.defineProperty(e,n,{enumerable:!0,value:t[o++]}):Object.defineProperty(e,n,{enumerable:!0,get:i})}else for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((t,o)=>(r.f[o](e,t),t),[])),r.u=e=>e+"."+e+".js?ver="+r.h(),r.h=()=>"__hash0__",r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{const e={},t="minimizer-webpack-plugin:";r.l=(o,n,i,c)=>{if(e[o])return void e[o].push(n);let a,l;if(void 0!==i){const e=document.getElementsByTagName("script");for(var s=0;s<e.length;s++){const r=e[s];if(r.getAttribute("src")==o||r.getAttribute("data-webpack")==t+i){a=r;break}}}a||(l=!0,a=document.createElement("script"),a.charset="utf-8",r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",t+i),a.src=o),e[o]=[n];const u=(t,r)=>{a.onerror=a.onload=null,clearTimeout(p);const n=e[o];if(delete e[o],a.parentNode?.removeChild(a),n?.forEach(e=>e(r)),t)return t(r)},p=setTimeout(u.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=u.bind(null,a.onerror),a.onload=u.bind(null,a.onload),l&&document.head.appendChild(a)}})(),r.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{let e;r.g.importScripts&&(e=r.g.location+"");const t=r.g.document;if(!e&&t&&("SCRIPT"===t.currentScript?.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){const r=t.getElementsByTagName("script");if(r.length){let t=r.length-1;for(;t>-1&&(!e||!/^http(s?):/.test(e));)e=r[t--].src}}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\\?.*$/,"").replace(/\\/[^\\/]+$/,"/"),r.p=e})(),(()=>{const e={988:0};r.f.j=(t,o)=>{let n=r.o(e,t)?e[t]:void 0;if(0!==n)if(n)o.push(n[2]);else{const i=new Promise((r,o)=>n=e[t]=[r,o]);o.push(n[2]=i);const c=r.p+r.u(t),a=new Error,l=o=>{if(r.o(e,t)&&(n=e[t],0!==n&&(e[t]=void 0),n)){const e=o&&("load"===o.type?"missing":o.type),r=o&&o.target&&o.target.src;a.message="Loading chunk "+t+" failed.\\n("+e+": "+r+")",a.name="ChunkLoadError",a.type=e,a.request=r,a.event=o,n[1](a)}};r.l(c,l,"chunk-"+t,t)}};const t=(t,o)=>{let[n,i,c]=o;var a,l,s=0;if(n.some(t=>0!==e[t])){for(a in i)r.o(i,a)&&(r.m[a]=i[a]);if(c)c(r)}for(t&&t(o);s<n.length;s++)l=n[s],r.o(e,l)&&e[l]&&e[l][0](),e[l]=0},o=self.webpackChunkminimizer_webpack_plugin=self.webpackChunkminimizer_webpack_plugin||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})(),r.e(389).then(r.bind(r,389)).then(()=>{console.log("Good")})})();",
718+
"AsyncImportExport.js?var=__hash0__": "(()=>{"use strict";var e={};const t={};function r(o){const n=t[o];if(void 0!==n)return n.exports;const i=t[o]={exports:{}};return e[o](i,i.exports,r),i.exports}r.m=e,r.d=(e,t)=>{if(Array.isArray(t))for(var o=0;o<t.length;){var n=t[o++],i=t[o++];r.o(e,n)?0===i&&o++:0===i?Object.defineProperty(e,n,{enumerable:!0,value:t[o++]}):Object.defineProperty(e,n,{enumerable:!0,get:i})}else for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((t,o)=>(r.f[o](e,t),t),[])),r.u=e=>e+"."+e+".js?ver="+r.h(),r.h=()=>"__hash0__",r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{const e={},t="minimizer-webpack-plugin:";r.l=(o,n,i,c)=>{if(e[o])return void e[o].push(n);let l,s;if(void 0!==i){const e=document.getElementsByTagName("script");for(var a=0;a<e.length;a++){const r=e[a];if(r.getAttribute("src")==o||r.getAttribute("data-webpack")==t+i){l=r;break}}}l||(s=!0,l=document.createElement("script"),l.charset="utf-8",r.nc&&l.setAttribute("nonce",r.nc),l.setAttribute("data-webpack",t+i),l.src=o),e[o]=[n];const u=(t,r)=>{l.onerror=l.onload=null,clearTimeout(p);const n=e[o];if(delete e[o],l.parentNode?.removeChild(l),n?.forEach(e=>e(r)),t)return t(r)},p=setTimeout(u.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=u.bind(null,l.onerror),l.onload=u.bind(null,l.onload),s&&document.head.appendChild(l)}})(),r.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{let e;r.g.importScripts&&(e=r.g.location+"");const t=r.g.document;if(!e&&t&&("SCRIPT"===t.currentScript?.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){const r=t.getElementsByTagName("script");if(r.length){let t=r.length-1;for(;t>-1&&(!e||!/^http(s?):/.test(e));)e=r[t--].src}}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\\?.*$/,"").replace(/\\/[^\\/]+$/,"/"),r.p=e})(),(()=>{const e={988:0};r.f.j=(t,o)=>{let n=r.o(e,t)?e[t]:void 0;if(0!==n)if(n)o.push(n[2]);else{const i=new Promise((r,o)=>n=e[t]=[r,o]);o.push(n[2]=i);const c=r.p+r.u(t),l=new Error,s=o=>{if(r.o(e,t)&&(n=e[t],0!==n&&(e[t]=void 0),n)){const e=o&&("load"===o.type?"missing":o.type),r=o&&o.target&&o.target.src;l.message="Loading chunk "+t+" failed.\\n("+e+": "+r+")",l.name="ChunkLoadError",l.type=e,l.request=r,l.event=o,n[1](l)}};r.l(c,s,"chunk-"+t,t)}};const t=(t,o)=>{let[n,i,c]=o;var l,s,a=0;if(n.some(t=>0!==e[t])){for(l in i)r.o(i,l)&&(r.m[l]=i[l]);if(c)c(r)}for(t&&t(o);a<n.length;a++)s=n[a],r.o(e,s)&&e[s]&&e[s][0](),e[s]=0},o=self.webpackChunkminimizer_webpack_plugin=self.webpackChunkminimizer_webpack_plugin||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})(),r.e(389).then(r.bind(r,389)).then(()=>{console.log("Good")})})();",
719719
"importExport.js?var=__hash0__": "/******/ (() => { // webpackBootstrap
720720
/******/ "use strict";
721721
@@ -766,7 +766,7 @@ __webpack_require__.r(__webpack_exports__);
766766
/***/ }
767767
768768
}]);",
769-
"AsyncImportExport.js?var=__hash0__": "(()=>{"use strict";var e={};const t={};function r(o){const n=t[o];if(void 0!==n)return n.exports;const i=t[o]={exports:{}};return e[o](i,i.exports,r),i.exports}r.m=e,r.d=(e,t)=>{if(Array.isArray(t))for(var o=0;o<t.length;){var n=t[o++],i=t[o++];r.o(e,n)?0===i&&o++:0===i?Object.defineProperty(e,n,{enumerable:!0,value:t[o++]}):Object.defineProperty(e,n,{enumerable:!0,get:i})}else for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((t,o)=>(r.f[o](e,t),t),[])),r.u=e=>e+"."+e+".js?ver="+r.h(),r.h=()=>"__hash0__",r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{const e={},t="minimizer-webpack-plugin:";r.l=(o,n,i,c)=>{if(e[o])return void e[o].push(n);let a,l;if(void 0!==i){const e=document.getElementsByTagName("script");for(var s=0;s<e.length;s++){const r=e[s];if(r.getAttribute("src")==o||r.getAttribute("data-webpack")==t+i){a=r;break}}}a||(l=!0,a=document.createElement("script"),a.charset="utf-8",r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",t+i),a.src=o),e[o]=[n];const u=(t,r)=>{a.onerror=a.onload=null,clearTimeout(p);const n=e[o];if(delete e[o],a.parentNode?.removeChild(a),n?.forEach(e=>e(r)),t)return t(r)},p=setTimeout(u.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=u.bind(null,a.onerror),a.onload=u.bind(null,a.onload),l&&document.head.appendChild(a)}})(),r.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{let e;r.g.importScripts&&(e=r.g.location+"");const t=r.g.document;if(!e&&t&&("SCRIPT"===t.currentScript?.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){const r=t.getElementsByTagName("script");if(r.length){let t=r.length-1;for(;t>-1&&(!e||!/^http(s?):/.test(e));)e=r[t--].src}}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\\?.*$/,"").replace(/\\/[^\\/]+$/,"/"),r.p=e})(),(()=>{const e={988:0};r.f.j=(t,o)=>{let n=r.o(e,t)?e[t]:void 0;if(0!==n)if(n)o.push(n[2]);else{const i=new Promise((r,o)=>n=e[t]=[r,o]);o.push(n[2]=i);const c=r.p+r.u(t),a=new Error,l=o=>{if(r.o(e,t)&&(n=e[t],0!==n&&(e[t]=void 0),n)){const e=o&&("load"===o.type?"missing":o.type),r=o&&o.target&&o.target.src;a.message="Loading chunk "+t+" failed.\\n("+e+": "+r+")",a.name="ChunkLoadError",a.type=e,a.request=r,a.event=o,n[1](a)}};r.l(c,l,"chunk-"+t,t)}};const t=(t,o)=>{let[n,i,c]=o;var a,l,s=0;if(n.some(t=>0!==e[t])){for(a in i)r.o(i,a)&&(r.m[a]=i[a]);if(c)c(r)}for(t&&t(o);s<n.length;s++)l=n[s],r.o(e,l)&&e[l]&&e[l][0](),e[l]=0},o=self.webpackChunkminimizer_webpack_plugin=self.webpackChunkminimizer_webpack_plugin||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})(),r.e(389).then(r.bind(r,389)).then(()=>{console.log("Good")})})();",
769+
"AsyncImportExport.js?var=__hash0__": "(()=>{"use strict";var e={};const t={};function r(o){const n=t[o];if(void 0!==n)return n.exports;const i=t[o]={exports:{}};return e[o](i,i.exports,r),i.exports}r.m=e,r.d=(e,t)=>{if(Array.isArray(t))for(var o=0;o<t.length;){var n=t[o++],i=t[o++];r.o(e,n)?0===i&&o++:0===i?Object.defineProperty(e,n,{enumerable:!0,value:t[o++]}):Object.defineProperty(e,n,{enumerable:!0,get:i})}else for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((t,o)=>(r.f[o](e,t),t),[])),r.u=e=>e+"."+e+".js?ver="+r.h(),r.h=()=>"__hash0__",r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{const e={},t="minimizer-webpack-plugin:";r.l=(o,n,i,c)=>{if(e[o])return void e[o].push(n);let l,s;if(void 0!==i){const e=document.getElementsByTagName("script");for(var a=0;a<e.length;a++){const r=e[a];if(r.getAttribute("src")==o||r.getAttribute("data-webpack")==t+i){l=r;break}}}l||(s=!0,l=document.createElement("script"),l.charset="utf-8",r.nc&&l.setAttribute("nonce",r.nc),l.setAttribute("data-webpack",t+i),l.src=o),e[o]=[n];const u=(t,r)=>{l.onerror=l.onload=null,clearTimeout(p);const n=e[o];if(delete e[o],l.parentNode?.removeChild(l),n?.forEach(e=>e(r)),t)return t(r)},p=setTimeout(u.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=u.bind(null,l.onerror),l.onload=u.bind(null,l.onload),s&&document.head.appendChild(l)}})(),r.r=e=>{Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{let e;r.g.importScripts&&(e=r.g.location+"");const t=r.g.document;if(!e&&t&&("SCRIPT"===t.currentScript?.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){const r=t.getElementsByTagName("script");if(r.length){let t=r.length-1;for(;t>-1&&(!e||!/^http(s?):/.test(e));)e=r[t--].src}}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\\?.*$/,"").replace(/\\/[^\\/]+$/,"/"),r.p=e})(),(()=>{const e={988:0};r.f.j=(t,o)=>{let n=r.o(e,t)?e[t]:void 0;if(0!==n)if(n)o.push(n[2]);else{const i=new Promise((r,o)=>n=e[t]=[r,o]);o.push(n[2]=i);const c=r.p+r.u(t),l=new Error,s=o=>{if(r.o(e,t)&&(n=e[t],0!==n&&(e[t]=void 0),n)){const e=o&&("load"===o.type?"missing":o.type),r=o&&o.target&&o.target.src;l.message="Loading chunk "+t+" failed.\\n("+e+": "+r+")",l.name="ChunkLoadError",l.type=e,l.request=r,l.event=o,n[1](l)}};r.l(c,s,"chunk-"+t,t)}};const t=(t,o)=>{let[n,i,c]=o;var l,s,a=0;if(n.some(t=>0!==e[t])){for(l in i)r.o(i,l)&&(r.m[l]=i[l]);if(c)c(r)}for(t&&t(o);a<n.length;a++)s=n[a],r.o(e,s)&&e[s]&&e[s][0](),e[s]=0},o=self.webpackChunkminimizer_webpack_plugin=self.webpackChunkminimizer_webpack_plugin||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})(),r.e(389).then(r.bind(r,389)).then(()=>{console.log("Good")})})();",
770770
"importExport.js?var=__hash0__": "/******/ (() => { // webpackBootstrap
771771
/******/ "use strict";
772772

‎test/generate-option.test.js‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,6 +1970,86 @@ describe("generate beside the minifier", () => {
19701970
expect(getErrors(stats)).toEqual([]);
19711971
});
19721972

1973+
it("should not rename a bundle no minimizer of its own would touch", async () => {
1974+
/**
1975+
* @param {boolean} withPlugin whether to apply the plugin
1976+
* @returns {Promise<string[]>} the emitted JavaScript names
1977+
*/
1978+
const namesFrom = async (withPlugin) => {
1979+
const compiler = getCompiler({
1980+
entry: path.resolve(__dirname, "./fixtures/images.js"),
1981+
output: {
1982+
path: path.resolve(__dirname, "./dist"),
1983+
filename: "[name].[fullhash].js",
1984+
},
1985+
module: { rules: IMAGE_RULES },
1986+
});
1987+
1988+
if (withPlugin) {
1989+
new MinimizerPlugin({
1990+
test: /\.png$/i,
1991+
generate: {
1992+
implementation: (input) => ({
1993+
code: Buffer.from(Object.values(input)[0]),
1994+
}),
1995+
type: "asset",
1996+
filename: "[path][name].copy[ext]",
1997+
},
1998+
}).apply(compiler);
1999+
}
2000+
2001+
const stats = await compile(compiler);
2002+
2003+
return Object.keys(stats.compilation.assets)
2004+
.filter((name) => name.endsWith(".js"))
2005+
.sort();
2006+
};
2007+
2008+
// `test` names images, so no minimizer here is ever handed the bundle:
2009+
// salting its hash would rename a file this instance never rewrites.
2010+
expect(await namesFrom(true)).toEqual(await namesFrom(false));
2011+
});
2012+
2013+
it("should still rename when a minimizer would be handed the bundle", async () => {
2014+
/**
2015+
* @param {EXPECTED_ANY} minimizerOptions what to run terser with
2016+
* @returns {Promise<string[]>} the emitted JavaScript names
2017+
*/
2018+
const namesFrom = async (minimizerOptions) => {
2019+
const compiler = getCompiler({
2020+
entry: path.resolve(__dirname, "./fixtures/images.js"),
2021+
output: {
2022+
path: path.resolve(__dirname, "./dist"),
2023+
filename: "[name].[fullhash].js",
2024+
},
2025+
module: { rules: IMAGE_RULES },
2026+
});
2027+
2028+
new MinimizerPlugin({
2029+
minimizerOptions,
2030+
generate: {
2031+
implementation: (input) => ({
2032+
code: Buffer.from(Object.values(input)[0]),
2033+
}),
2034+
type: "asset",
2035+
filename: "[path][name].copy[ext]",
2036+
},
2037+
}).apply(compiler);
2038+
2039+
const stats = await compile(compiler);
2040+
2041+
return Object.keys(stats.compilation.assets)
2042+
.filter((name) => name.endsWith(".js"))
2043+
.sort();
2044+
};
2045+
2046+
// The guard above must not cost the salt its job: what terser is run with
2047+
// still varies the name of what it rewrote.
2048+
expect(await namesFrom({ mangle: true })).not.toEqual(
2049+
await namesFrom({ mangle: false }),
2050+
);
2051+
});
2052+
19732053
it("should still minify when only a generator was configured", async () => {
19742054
const compiler = getCompiler({
19752055
entry: path.resolve(__dirname, "./fixtures/images.js"),

‎types/index.d.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ declare class MinimizerPlugin<T = import("terser").MinifyOptions> {
6262
* @returns {boolean} true when it is to be minified
6363
*/
6464
private matchesName;
65+
/**
66+
* Whether any configured minimizer would be handed an asset of this name,
67+
* by the plugin's own `test`/`include`/`exclude` and then by its own filter.
68+
* @private
69+
* @param {Compiler} compiler compiler
70+
* @param {string} name asset name
71+
* @returns {boolean} true when one of them would take it
72+
*/
73+
private minifiesName;
6574
/**
6675
* @private
6776
* @param {Compiler} compiler compiler

0 commit comments

Comments
 (0)