From 8af59fc466a13f4f6813226cff21a951db375069 Mon Sep 17 00:00:00 2001 From: "Carlos G.B." Date: Tue, 27 Aug 2024 10:28:13 +0200 Subject: [PATCH] fix(helpers): only deep merge plain objects (#20284) Co-authored-by: Kael fixes #20278 --- .../vuetify/src/util/__tests__/helpers.spec.ts | 10 ++++++++++ packages/vuetify/src/util/helpers.ts | 17 +++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/vuetify/src/util/__tests__/helpers.spec.ts b/packages/vuetify/src/util/__tests__/helpers.spec.ts index 3cc83f69be9..8eddf040cf6 100644 --- a/packages/vuetify/src/util/__tests__/helpers.spec.ts +++ b/packages/vuetify/src/util/__tests__/helpers.spec.ts @@ -288,6 +288,16 @@ describe('helpers', () => { it('should use arrayFn function if provided', () => { expect(mergeDeep({ a: ['foo'] }, { a: ['bar'] }, (a, b) => [...a, ...b])).toEqual({ a: ['foo', 'bar'] }) }) + + it('should not recursively merge non-plain objects', () => { + const plain = { a: 'foo' } + const div = document.createElement('div') + const span = document.createElement('span') + + expect(mergeDeep({ a: plain }, { a: div }).a).toBe(div) + expect(mergeDeep({ a: div }, { a: plain }).a).toBe(plain) + expect(mergeDeep({ a: div }, { a: span }).a).toBe(span) + }) }) describe('destructComputed', () => { diff --git a/packages/vuetify/src/util/helpers.ts b/packages/vuetify/src/util/helpers.ts index 089e4ca8418..bd8eddea62e 100644 --- a/packages/vuetify/src/util/helpers.ts +++ b/packages/vuetify/src/util/helpers.ts @@ -135,6 +135,14 @@ export function isObject (obj: any): obj is Record { return obj !== null && typeof obj === 'object' && !Array.isArray(obj) } +export function isPlainObject (obj: any): obj is Record { + let proto + return obj !== null && typeof obj === 'object' && ( + (proto = Object.getPrototypeOf(obj)) === Object.prototype || + proto === null + ) +} + export function refElement (obj?: ComponentPublicInstance | HTMLElement): HTMLElement | undefined { if (obj && '$el' in obj) { const el = obj.$el as HTMLElement @@ -487,17 +495,14 @@ export function mergeDeep ( const targetProperty = target[key] // Only continue deep merging if - // both properties are objects - if ( - isObject(sourceProperty) && - isObject(targetProperty) - ) { + // both properties are plain objects + if (isPlainObject(sourceProperty) && isPlainObject(targetProperty)) { out[key] = mergeDeep(sourceProperty, targetProperty, arrayFn) continue } - if (Array.isArray(sourceProperty) && Array.isArray(targetProperty) && arrayFn) { + if (arrayFn && Array.isArray(sourceProperty) && Array.isArray(targetProperty)) { out[key] = arrayFn(sourceProperty, targetProperty) continue