Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build(webpack): fix --zip in Node.js 22 by cloning assets into Uint8Arrays before zipping #29177

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 17 additions & 18 deletions development/webpack/utils/plugins/ManifestPlugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -145,7 +146,7 @@ export class ManifestPlugin<Z extends boolean> {
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);
}
Expand All @@ -171,9 +172,7 @@ export class ManifestPlugin<Z extends boolean> {
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,
Expand Down
Loading