Skip to content

Commit

Permalink
fix: messages deepCopy mutates src arguments (#1947)
Browse files Browse the repository at this point in the history
* fix: messages `deepCopy` mutates `src` arguments

* fix: `deepCopy` should never merge arrays
  • Loading branch information
BobbieGoede authored Sep 16, 2024
1 parent 28a83ba commit 19054e0
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 1 deletion.
9 changes: 8 additions & 1 deletion packages/shared/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ export function deepCopy(src: any, des: any): void {
while (stack.length) {
const { src, des } = stack.pop()!

// using `Object.keys` which skips prototype properties
Object.keys(src).forEach(key => {
if (isNotObjectOrIsArray(src[key]) || isNotObjectOrIsArray(des[key])) {
// if src[key] is an object/array, set des[key]
// to empty object/array to prevent setting by reference
if (isObject(src[key]) && !isObject(des[key])) {
des[key] = Array.isArray(src[key]) ? [] : {}
}

if (isNotObjectOrIsArray(des[key]) || isNotObjectOrIsArray(src[key])) {
// replace with src[key] when:
// src[key] or des[key] is not an object, or
// src[key] or des[key] is an array
Expand Down
50 changes: 50 additions & 0 deletions packages/shared/test/messages.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { deepCopy } from '../src/index'

test('deepCopy merges without mutating src argument', () => {
const msg1 = {
hello: 'Greetings',
about: {
title: 'About us'
},
overwritten: 'Original text',
fruit: [{ name: 'Apple' }]
}
const copy1 = structuredClone(msg1)

const msg2 = {
bye: 'Goodbye',
about: {
content: 'Some text'
},
overwritten: 'New text',
fruit: [{ name: 'Strawberry' }],
// @ts-ignore
car: ({ plural }) => plural(['car', 'cars'])
}

const merged = {}

deepCopy(msg1, merged)
deepCopy(msg2, merged)

expect(merged).toMatchInlineSnapshot(`
{
"about": {
"content": "Some text",
"title": "About us",
},
"bye": "Goodbye",
"car": [Function],
"fruit": [
{
"name": "Strawberry",
},
],
"hello": "Greetings",
"overwritten": "New text",
}
`)

// should not mutate source object
expect(msg1).toStrictEqual(copy1)
})

0 comments on commit 19054e0

Please sign in to comment.