From 58ca7af401213bb35783f2c9cc4fae8a0fec69cf Mon Sep 17 00:00:00 2001 From: David Murdoch <187813+davidmurdoch@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:35:54 -0500 Subject: [PATCH] build(webpack): fix `--zip` in Node.js 22 by cloning assets into `Uint8Array`s before zipping (#29177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a copy of the Buffer via `Buffer.from(asset)`, as Zip will *consume* it, which breaks things if we are compiling for multiple browsers at once. `Buffer.from` uses the internal pool, so it's superior to `new Uint8Array` if we don't need to pass it off to a worker thread. Additionally, in Node.js 22+ a Buffer marked as "Untransferable" (like ours) can't be passed to a worker, which `AsyncZipDeflate` uses. See: https://github.com/101arrowz/fflate/issues/227#issuecomment-2540024304 This can probably be simplified to `zipFile.push(Buffer.from(asset), true);` if the above issue is resolved. This fix should hopefully unblock https://github.com/MetaMask/metamask-extension/pull/28368 --- .../utils/plugins/ManifestPlugin/index.ts | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/development/webpack/utils/plugins/ManifestPlugin/index.ts b/development/webpack/utils/plugins/ManifestPlugin/index.ts index bd18541cc1e3..a29219084de6 100644 --- a/development/webpack/utils/plugins/ManifestPlugin/index.ts +++ b/development/webpack/utils/plugins/ManifestPlugin/index.ts @@ -25,16 +25,6 @@ type Assets = Compilation['assets']; const NAME = 'ManifestPlugin'; const BROWSER_TEMPLATE_RE = /\[browser\]/gu; -/** - * Clones a Buffer or Uint8Array and returns it - * - * @param data - * @returns - */ -function clone(data: Buffer | Uint8Array): Buffer { - return Buffer.from(data); -} - /** * Adds the given asset to the zip file * @@ -54,12 +44,23 @@ function addAssetToZip( zip: Zip, ): void { const zipFile = compress - ? new AsyncZipDeflate(assetName, compressionOptions) - : new ZipPassThrough(assetName); + ? // AsyncZipDeflate uses workers + new AsyncZipDeflate(assetName, compressionOptions) + : // ZipPassThrough doesn't use workers + new ZipPassThrough(assetName); zipFile.mtime = mtime; zip.add(zipFile); - // use a copy of the Buffer, as Zip will consume it - zipFile.push(asset, true); + // Use a copy of the Buffer via `Buffer.from(asset)`, as Zip will *consume* + // it, which breaks things if we are compiling for multiple browsers at once. + // `Buffer.from` uses the internal pool, so it's superior to `new Uint8Array` + // if we don't need to pass it off to a worker thread. + // + // Additionally, in Node.js 22+ a Buffer marked as "Untransferable" (like + // ours) can't be passed to a worker, which `AsyncZipDeflate` uses. + // See: https://github.com/101arrowz/fflate/issues/227#issuecomment-2540024304 + // this can probably be simplified to `zipFile.push(Buffer.from(asset), true);` + // if the above issue is resolved. + zipFile.push(compress ? new Uint8Array(asset) : Buffer.from(asset), true); } /** @@ -145,7 +146,7 @@ export class ManifestPlugin { errored = true; reject(error); } else { - zipSource.add(new RawSource(clone(data))); + zipSource.add(new RawSource(Buffer.from(data))); // we've received our final bit of data, return the zipSource if (final) resolve(zipSource); } @@ -171,9 +172,7 @@ export class ManifestPlugin { if (excludeExtensions.includes(extName)) continue; addAssetToZip( - // make a copy of the asset Buffer as Zipping will *consume* it, - // which breaks things if we are compiling for multiple browsers. - clone(asset.buffer()), + asset.buffer(), assetName, ManifestPlugin.compressibleFileTypes.has(extName), compressionOptions,