Skip to content

Commit 139ff4e

Browse files
authored
fix(extract-comments): decode Buffer sources before merging (#731)
An existing comments file emitted by another plugin can be backed by a Buffer, which has no split method. Decode it as UTF-8 before merging.
1 parent 649b48d commit 139ff4e

5 files changed

Lines changed: 101 additions & 4 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"minimizer-webpack-plugin": patch
3+
---
4+
5+
fix(extract-comments): decode Buffer sources before merging

‎src/index.js‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,12 +1032,19 @@ class TerserPlugin {
10321032
let source = await cache.getPromise(name, eTag);
10331033

10341034
if (!source) {
1035+
const prevValue = prevSource.source();
1036+
const extractedValue = extractedCommentsSource.source();
1037+
10351038
source = new ConcatSource(
10361039
[
10371040
...new Set([
1038-
.../** @type {string} */ (prevSource.source()).split("\n\n"),
1039-
.../** @type {string} */ (
1040-
extractedCommentsSource.source()
1041+
...(typeof prevValue === "string"
1042+
? prevValue
1043+
: prevValue.toString("utf8")
1044+
).split("\n\n"),
1045+
...(typeof extractedValue === "string"
1046+
? extractedValue
1047+
: extractedValue.toString("utf8")
10411048
).split("\n\n"),
10421049
]),
10431050
].join("\n\n"),

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6088,6 +6088,65 @@ exports[`extractComments option should match snapshot when no condition, preserv
60886088
60896089
exports[`extractComments option should match snapshot when no condition, preserve only \`/@license/i\` comments and extract "some" comments: warnings 1`] = `[]`;
60906090
6091+
exports[`extractComments option should work with the existing licenses file, when it is a Buffer: assets 1`] = `
6092+
{
6093+
"chunks/203.203.js": "/*! For license information please see ../licenses.txt */
6094+
(self.webpackChunkminimizer_webpack_plugin=self.webpackChunkminimizer_webpack_plugin||[]).push([[203],{203(e){e.exports=Math.random()}}]);",
6095+
"filename/four.js": "/*! For license information please see ../licenses.txt */
6096+
(()=>{var t={250(t){t.exports=Math.random()}};const o={};(function r(n){const s=o[n];if(void 0!==s)return s.exports;const e=o[n]={exports:{}};return t[n](e,e.exports,r),e.exports})(250)})();",
6097+
"filename/one.js": "/*! For license information please see ../licenses.txt */
6098+
(()=>{var e={855(e,t,r){r.e(203).then(r.t.bind(r,203,23)),e.exports=Math.random()}};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,(()=>{const e=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;let t;r.t=function(o,n){if(1&n&&(o=this(o)),8&n)return o;if("object"==typeof o&&o){if(4&n&&o.__esModule)return o;if(16&n&&"function"==typeof o.then)return o}const i=Object.create(null);r.r(i);const c={};t=t||[null,e({}),e([]),e(e)];for(var a=2&n&&o;("object"==typeof a||"function"==typeof a)&&!~t.indexOf(a);a=e(a))Object.getOwnPropertyNames(a).forEach(e=>c[e]=()=>o[e]);return c.default=()=>o,r.d(i,c),i}})(),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=>"chunks/"+e+"."+e+".js",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={101: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(855)})();",
6099+
"filename/three.js": "/*! For license information please see ../licenses.txt */
6100+
(()=>{var t={35(t){t.exports=Math.random()}};const o={};(function r(n){const s=o[n];if(void 0!==s)return s.exports;const e=o[n]={exports:{}};return t[n](e,e.exports,r),e.exports})(35)})();",
6101+
"filename/two.js": "/*! For license information please see ../licenses.txt */
6102+
(()=>{var t={12(t){t.exports=Math.random()}};const o={};(function r(n){const s=o[n];if(void 0!==s)return s.exports;const e=o[n]={exports:{}};return t[n](e,e.exports,r),e.exports})(12)})();",
6103+
"licenses.txt": "// Existing Comment
6104+
6105+
/*! Legal Comment */
6106+
6107+
/** @license Copyright 2112 Moon. **/
6108+
6109+
6110+
/**
6111+
* Duplicate comment in difference files.
6112+
* @license MIT
6113+
*/
6114+
6115+
6116+
/*! Legal Foo */
6117+
6118+
/**
6119+
* @preserve Copyright 2009 SomeThirdParty.
6120+
* Here is the full license text and copyright
6121+
* notice for this file. Note that the notice can span several
6122+
* lines and is only terminated by the closing star and slash:
6123+
*/
6124+
6125+
/**
6126+
* Utility functions for the foo package.
6127+
* @license Apache-2.0
6128+
*/
6129+
6130+
// @lic
6131+
6132+
/**
6133+
* Duplicate comment in same file.
6134+
* @license MIT
6135+
*/
6136+
6137+
6138+
/**
6139+
* Information.
6140+
* @license MIT
6141+
*/
6142+
",
6143+
}
6144+
`;
6145+
6146+
exports[`extractComments option should work with the existing licenses file, when it is a Buffer: errors 1`] = `[]`;
6147+
6148+
exports[`extractComments option should work with the existing licenses file, when it is a Buffer: warnings 1`] = `[]`;
6149+
60916150
exports[`extractComments option should work with the existing licenses file: assets 1`] = `
60926151
{
60936152
"chunks/203.203.js": "/*! For license information please see ../licenses.txt */

‎test/extractComments-option.test.js‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,26 @@ describe("extractComments option", () => {
631631
expect(getWarnings(stats)).toMatchSnapshot("warnings");
632632
});
633633

634+
it("should work with the existing licenses file, when it is a Buffer", async () => {
635+
new ExistingCommentsFile({ asBuffer: true }).apply(compiler);
636+
new MinimizerPlugin({
637+
extractComments: {
638+
filename: "licenses.txt",
639+
},
640+
}).apply(compiler);
641+
642+
const stats = await compile(compiler);
643+
644+
const licenses = readAsset("licenses.txt", compiler, stats);
645+
646+
expect(licenses).toContain("// Existing Comment");
647+
expect(licenses).toContain("/*! Legal Comment */");
648+
649+
expect(readsAssets(compiler, stats)).toMatchSnapshot("assets");
650+
expect(getErrors(stats)).toMatchSnapshot("errors");
651+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
652+
});
653+
634654
it("should keep the comments of every asset sharing a file, when they are not adjacent", async () => {
635655
// Assets reach the comments file in name order, so `b` sits between the two
636656
// that share `shared.txt`.

‎test/helpers/ExistingCommentsFile.js‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
import webpack from "webpack";
22

33
export default class ExistingCommentsFile {
4+
constructor({ asBuffer = false } = {}) {
5+
this.asBuffer = asBuffer;
6+
}
7+
48
apply(compiler) {
59
const plugin = { name: this.constructor.name };
610

711
compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
812
compilation.hooks.additionalAssets.tap(plugin, () => {
13+
const contents = "// Existing Comment";
14+
915
compilation.assets["licenses.txt"] = new webpack.sources.RawSource(
10-
"// Existing Comment",
16+
this.asBuffer ? Buffer.from(contents) : contents,
1117
);
1218
});
1319
});

0 commit comments

Comments
 (0)