@wordpress/interactivity
Advanced tools
Comparing version 6.8.2 to 6.8.3
@@ -246,39 +246,43 @@ /** | ||
const deepMergeRecursive = (target, source, override = true) => { | ||
if (isPlainObject(target) && isPlainObject(source)) { | ||
let hasNewKeys = false; | ||
for (const key in source) { | ||
const isNew = !(key in target); | ||
hasNewKeys = hasNewKeys || isNew; | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
if (typeof desc?.get === 'function' || typeof desc?.set === 'function') { | ||
if (override || isNew) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
const proxy = getProxyFromObject(target); | ||
if (desc?.get && proxy && hasPropSignal(proxy, key)) { | ||
const propSignal = getPropSignal(proxy, key); | ||
propSignal.setGetter(desc.get); | ||
} | ||
if (!(isPlainObject(target) && isPlainObject(source))) { | ||
return; | ||
} | ||
let hasNewKeys = false; | ||
for (const key in source) { | ||
const isNew = !(key in target); | ||
hasNewKeys = hasNewKeys || isNew; | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
const proxy = getProxyFromObject(target); | ||
const propSignal = !!proxy && hasPropSignal(proxy, key) && getPropSignal(proxy, key); | ||
if (typeof desc.get === 'function' || typeof desc.set === 'function') { | ||
if (override || isNew) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
if (desc.get && propSignal) { | ||
propSignal.setGetter(desc.get); | ||
} | ||
} else if (isPlainObject(source[key])) { | ||
if (isNew) { | ||
target[key] = {}; | ||
} | ||
} else if (isPlainObject(source[key])) { | ||
if (isNew || override && !isPlainObject(target[key])) { | ||
target[key] = {}; | ||
if (propSignal) { | ||
propSignal.setValue(target[key]); | ||
} | ||
} | ||
if (isPlainObject(target[key])) { | ||
deepMergeRecursive(target[key], source[key], override); | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
const proxy = getProxyFromObject(target); | ||
if (desc?.value && proxy && hasPropSignal(proxy, key)) { | ||
const propSignal = getPropSignal(proxy, key); | ||
propSignal.setValue(desc.value); | ||
} | ||
} | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
if (propSignal) { | ||
propSignal.setValue(desc.value); | ||
} | ||
} | ||
if (hasNewKeys && objToIterable.has(target)) { | ||
objToIterable.get(target).value++; | ||
} | ||
} | ||
if (hasNewKeys && objToIterable.has(target)) { | ||
objToIterable.get(target).value++; | ||
} | ||
}; | ||
@@ -285,0 +289,0 @@ |
@@ -255,39 +255,43 @@ "use strict"; | ||
const deepMergeRecursive = (target, source, override = true) => { | ||
if ((0, _utils.isPlainObject)(target) && (0, _utils.isPlainObject)(source)) { | ||
let hasNewKeys = false; | ||
for (const key in source) { | ||
const isNew = !(key in target); | ||
hasNewKeys = hasNewKeys || isNew; | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
if (typeof desc?.get === 'function' || typeof desc?.set === 'function') { | ||
if (override || isNew) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
const proxy = (0, _registry.getProxyFromObject)(target); | ||
if (desc?.get && proxy && hasPropSignal(proxy, key)) { | ||
const propSignal = getPropSignal(proxy, key); | ||
propSignal.setGetter(desc.get); | ||
} | ||
if (!((0, _utils.isPlainObject)(target) && (0, _utils.isPlainObject)(source))) { | ||
return; | ||
} | ||
let hasNewKeys = false; | ||
for (const key in source) { | ||
const isNew = !(key in target); | ||
hasNewKeys = hasNewKeys || isNew; | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
const proxy = (0, _registry.getProxyFromObject)(target); | ||
const propSignal = !!proxy && hasPropSignal(proxy, key) && getPropSignal(proxy, key); | ||
if (typeof desc.get === 'function' || typeof desc.set === 'function') { | ||
if (override || isNew) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
if (desc.get && propSignal) { | ||
propSignal.setGetter(desc.get); | ||
} | ||
} else if ((0, _utils.isPlainObject)(source[key])) { | ||
if (isNew) { | ||
target[key] = {}; | ||
} | ||
} else if ((0, _utils.isPlainObject)(source[key])) { | ||
if (isNew || override && !(0, _utils.isPlainObject)(target[key])) { | ||
target[key] = {}; | ||
if (propSignal) { | ||
propSignal.setValue(target[key]); | ||
} | ||
} | ||
if ((0, _utils.isPlainObject)(target[key])) { | ||
deepMergeRecursive(target[key], source[key], override); | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
const proxy = (0, _registry.getProxyFromObject)(target); | ||
if (desc?.value && proxy && hasPropSignal(proxy, key)) { | ||
const propSignal = getPropSignal(proxy, key); | ||
propSignal.setValue(desc.value); | ||
} | ||
} | ||
} else if (override || isNew) { | ||
Object.defineProperty(target, key, desc); | ||
if (propSignal) { | ||
propSignal.setValue(desc.value); | ||
} | ||
} | ||
if (hasNewKeys && objToIterable.has(target)) { | ||
objToIterable.get(target).value++; | ||
} | ||
} | ||
if (hasNewKeys && objToIterable.has(target)) { | ||
objToIterable.get(target).value++; | ||
} | ||
}; | ||
@@ -294,0 +298,0 @@ |
@@ -5,2 +5,6 @@ <!-- Learn how to maintain this file at https://github.com/WordPress/gutenberg/tree/HEAD/packages#maintaining-changelogs. --> | ||
### Bug Fixes | ||
- Correctly handle lazily added, deeply nested properties with `deepMerge()` ([#65465](https://github.com/WordPress/gutenberg/pull/65465)). | ||
## 6.8.0 (2024-09-19) | ||
@@ -7,0 +11,0 @@ |
{ | ||
"name": "@wordpress/interactivity", | ||
"version": "6.8.2", | ||
"version": "6.8.3", | ||
"description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.", | ||
@@ -40,3 +40,3 @@ "author": "The WordPress Contributors", | ||
}, | ||
"gitHead": "3686e926862c54c6326fe97277e0a19f8bbfafdd" | ||
"gitHead": "21b3c2e9abda74edab42455041edc6e82fccc388" | ||
} |
@@ -303,46 +303,53 @@ /** | ||
) => { | ||
if ( isPlainObject( target ) && isPlainObject( source ) ) { | ||
let hasNewKeys = false; | ||
for ( const key in source ) { | ||
const isNew = ! ( key in target ); | ||
hasNewKeys = hasNewKeys || isNew; | ||
if ( ! ( isPlainObject( target ) && isPlainObject( source ) ) ) { | ||
return; | ||
} | ||
const desc = Object.getOwnPropertyDescriptor( source, key ); | ||
if ( | ||
typeof desc?.get === 'function' || | ||
typeof desc?.set === 'function' | ||
) { | ||
if ( override || isNew ) { | ||
Object.defineProperty( target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true, | ||
} ); | ||
let hasNewKeys = false; | ||
const proxy = getProxyFromObject( target ); | ||
if ( desc?.get && proxy && hasPropSignal( proxy, key ) ) { | ||
const propSignal = getPropSignal( proxy, key ); | ||
propSignal.setGetter( desc.get ); | ||
} | ||
} | ||
} else if ( isPlainObject( source[ key ] ) ) { | ||
if ( isNew ) { | ||
target[ key ] = {}; | ||
} | ||
for ( const key in source ) { | ||
const isNew = ! ( key in target ); | ||
hasNewKeys = hasNewKeys || isNew; | ||
deepMergeRecursive( target[ key ], source[ key ], override ); | ||
} else if ( override || isNew ) { | ||
Object.defineProperty( target, key, desc! ); | ||
const desc = Object.getOwnPropertyDescriptor( source, key )!; | ||
const proxy = getProxyFromObject( target ); | ||
const propSignal = | ||
!! proxy && | ||
hasPropSignal( proxy, key ) && | ||
getPropSignal( proxy, key ); | ||
const proxy = getProxyFromObject( target ); | ||
if ( desc?.value && proxy && hasPropSignal( proxy, key ) ) { | ||
const propSignal = getPropSignal( proxy, key ); | ||
propSignal.setValue( desc.value ); | ||
if ( | ||
typeof desc.get === 'function' || | ||
typeof desc.set === 'function' | ||
) { | ||
if ( override || isNew ) { | ||
Object.defineProperty( target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true, | ||
} ); | ||
if ( desc.get && propSignal ) { | ||
propSignal.setGetter( desc.get ); | ||
} | ||
} | ||
} else if ( isPlainObject( source[ key ] ) ) { | ||
if ( isNew || ( override && ! isPlainObject( target[ key ] ) ) ) { | ||
target[ key ] = {}; | ||
if ( propSignal ) { | ||
propSignal.setValue( target[ key ] ); | ||
} | ||
} | ||
if ( isPlainObject( target[ key ] ) ) { | ||
deepMergeRecursive( target[ key ], source[ key ], override ); | ||
} | ||
} else if ( override || isNew ) { | ||
Object.defineProperty( target, key, desc ); | ||
if ( propSignal ) { | ||
propSignal.setValue( desc.value ); | ||
} | ||
} | ||
} | ||
if ( hasNewKeys && objToIterable.has( target ) ) { | ||
objToIterable.get( target )!.value++; | ||
} | ||
if ( hasNewKeys && objToIterable.has( target ) ) { | ||
objToIterable.get( target )!.value++; | ||
} | ||
@@ -349,0 +356,0 @@ }; |
@@ -9,3 +9,3 @@ /** | ||
*/ | ||
import { proxifyContext, proxifyState } from '../'; | ||
import { proxifyContext, proxifyState, deepMerge } from '../'; | ||
@@ -281,2 +281,62 @@ describe( 'Interactivity API', () => { | ||
} ); | ||
it( 'should handle deeply nested properties that are initially undefined', () => { | ||
const fallback: any = proxifyContext( | ||
proxifyState( 'test', {} ), | ||
{} | ||
); | ||
const context: any = proxifyContext( | ||
proxifyState( 'test', {} ), | ||
fallback | ||
); | ||
let deepValue: any; | ||
const spy = jest.fn( () => { | ||
deepValue = context.a?.b?.c?.d; | ||
} ); | ||
effect( spy ); | ||
// Initial call, the deep value is undefined | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( deepValue ).toBeUndefined(); | ||
// Add a deeply nested object to the context | ||
context.a = { b: { c: { d: 'test value' } } }; | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( deepValue ).toBe( 'test value' ); | ||
// Reading the value directly should also work | ||
expect( context.a.b.c.d ).toBe( 'test value' ); | ||
} ); | ||
it( 'should handle deeply nested properties that are initially undefined and merged with deepMerge', () => { | ||
const fallbackState = proxifyState( 'test', {} ); | ||
const fallback: any = proxifyContext( fallbackState, {} ); | ||
const contextState = proxifyState( 'test', {} ); | ||
const context: any = proxifyContext( contextState, fallback ); | ||
let deepValue: any; | ||
const spy = jest.fn( () => { | ||
deepValue = context.a?.b?.c?.d; | ||
} ); | ||
effect( spy ); | ||
// Initial call, the deep value is undefined | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( deepValue ).toBeUndefined(); | ||
// Use deepMerge to add a deeply nested object to the context | ||
deepMerge( contextState, { | ||
a: { b: { c: { d: 'test value' } } }, | ||
} ); | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( deepValue ).toBe( 'test value' ); | ||
// Reading the value directly should also work | ||
expect( context.a.b.c.d ).toBe( 'test value' ); | ||
} ); | ||
} ); | ||
@@ -283,0 +343,0 @@ |
@@ -392,3 +392,76 @@ /* eslint-disable eslint-comments/disable-enable-pair */ | ||
} ); | ||
it( 'should handle deeply nested properties that are initially undefined', () => { | ||
const target: any = proxifyState( 'test', {} ); | ||
let deepValue: any; | ||
const spy = jest.fn( () => { | ||
deepValue = target.a?.b?.c?.d; | ||
} ); | ||
effect( spy ); | ||
// Initial call, the deep value is undefined | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( deepValue ).toBeUndefined(); | ||
// Use deepMerge to add a deeply nested object to the target | ||
deepMerge( target, { a: { b: { c: { d: 'test value' } } } } ); | ||
// The effect should be called again | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( deepValue ).toBe( 'test value' ); | ||
// Reading the value directly should also work | ||
expect( target.a.b.c.d ).toBe( 'test value' ); | ||
} ); | ||
it( 'should overwrite values that become objects', () => { | ||
const target: any = proxifyState( 'test', { message: 'hello' } ); | ||
let message: any; | ||
const spy = jest.fn( () => ( message = target.message ) ); | ||
effect( spy ); | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( message ).toBe( 'hello' ); | ||
deepMerge( target, { | ||
message: { content: 'hello', fontStyle: 'italic' }, | ||
} ); | ||
expect( spy ).toHaveBeenCalledTimes( 2 ); | ||
expect( message ).toEqual( { | ||
content: 'hello', | ||
fontStyle: 'italic', | ||
} ); | ||
expect( target.message ).toEqual( { | ||
content: 'hello', | ||
fontStyle: 'italic', | ||
} ); | ||
} ); | ||
it( 'should not overwrite values that become objects if `override` is false', () => { | ||
const target: any = proxifyState( 'test', { message: 'hello' } ); | ||
let message: any; | ||
const spy = jest.fn( () => ( message = target.message ) ); | ||
effect( spy ); | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( message ).toBe( 'hello' ); | ||
deepMerge( | ||
target, | ||
{ message: { content: 'hello', fontStyle: 'italic' } }, | ||
false | ||
); | ||
expect( spy ).toHaveBeenCalledTimes( 1 ); | ||
expect( message ).toBe( 'hello' ); | ||
expect( target.message ).toBe( 'hello' ); | ||
expect( target.message.content ).toBeUndefined(); | ||
expect( target.message.fontStyle ).toBeUndefined(); | ||
} ); | ||
} ); | ||
} ); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
699203
10095