diff --git a/docs/changelog.md b/docs/changelog.md index 705e529f5..9503c48f6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,6 +14,9 @@ Requires libvips v8.15.1 [#4028](https://github.com/lovell/sharp/pull/4028) [@yolopunk](https://github.com/yolopunk) +* Ensure `clone` takes a deep copy of existing options. + [#4029](https://github.com/lovell/sharp/issues/4029) + ### v0.33.2 - 12th January 2024 * Upgrade to libvips v8.15.1 for upstream bug fixes. diff --git a/lib/constructor.js b/lib/constructor.js index dd2c4e394..05b4c2db3 100644 --- a/lib/constructor.js +++ b/lib/constructor.js @@ -425,13 +425,16 @@ Object.setPrototypeOf(Sharp, stream.Duplex); function clone () { // Clone existing options const clone = this.constructor.call(); - clone.options = Object.assign({}, this.options); + const { debuglog, queueListener, ...options } = this.options; + clone.options = structuredClone(options); + clone.options.debuglog = debuglog; + clone.options.queueListener = queueListener; // Pass 'finish' event to clone for Stream-based input if (this._isStreamInput()) { this.on('finish', () => { // Clone inherits input data this._flattenBufferIn(); - clone.options.bufferIn = this.options.bufferIn; + clone.options.input.buffer = this.options.input.buffer; clone.emit('finish'); }); } diff --git a/test/unit/clone.js b/test/unit/clone.js index 690792db9..0db81ae53 100644 --- a/test/unit/clone.js +++ b/test/unit/clone.js @@ -77,4 +77,26 @@ describe('Clone', function () { assert.strictEqual(0, original.listenerCount('finish')); assert.strictEqual(0, clone.listenerCount('finish')); }); + + it('Ensure deep clone of properties, including arrays', async () => { + const alpha = await sharp({ + create: { width: 320, height: 240, channels: 3, background: 'red' } + }).toColourspace('b-w').png().toBuffer(); + + const original = sharp(); + const joiner = original.clone().joinChannel(alpha); + const negater = original.clone().negate(); + + fs.createReadStream(fixtures.inputJpg320x240).pipe(original); + const joined = await joiner.png({ effort: 1 }).toBuffer(); + const negated = await negater.png({ effort: 1 }).toBuffer(); + + const joinedMetadata = await sharp(joined).metadata(); + assert.strictEqual(joinedMetadata.channels, 4); + assert.strictEqual(joinedMetadata.hasAlpha, true); + + const negatedMetadata = await sharp(negated).metadata(); + assert.strictEqual(negatedMetadata.channels, 3); + assert.strictEqual(negatedMetadata.hasAlpha, false); + }); });