@wordpress/interactivity
Advanced tools
Comparing version 6.6.0 to 6.7.0
/** | ||
* Internal dependencies | ||
*/ | ||
export { proxifyState, peek } from './state'; | ||
export { proxifyState, peek, deepMerge } from './state'; | ||
export { proxifyStore } from './store'; | ||
//# sourceMappingURL=index.js.map |
@@ -5,2 +5,3 @@ /** | ||
const objToProxy = new WeakMap(); | ||
const proxyToObj = new WeakMap(); | ||
@@ -38,2 +39,3 @@ /** | ||
objToProxy.set(obj, proxy); | ||
proxyToObj.set(proxy, obj); | ||
proxyToNs.set(proxy, namespace); | ||
@@ -75,2 +77,11 @@ } | ||
}; | ||
/** | ||
* Returns the target object for the passed proxy. If the passed object is not a registered proxy, the | ||
* function returns `undefined`. | ||
* | ||
* @param proxy Proxy from which to know the target. | ||
* @return The target object or `undefined`. | ||
*/ | ||
export const getObjectFromProxy = proxy => proxyToObj.get(proxy); | ||
//# sourceMappingURL=registry.js.map |
@@ -17,3 +17,3 @@ /** | ||
*/ | ||
const NO_SCOPE = Symbol(); | ||
const NO_SCOPE = {}; | ||
@@ -20,0 +20,0 @@ /** |
/** | ||
* External dependencies | ||
*/ | ||
import { signal } from '@preact/signals'; | ||
import { batch, signal } from '@preact/signals'; | ||
@@ -9,5 +9,6 @@ /** | ||
*/ | ||
import { createProxy, getProxyFromObject, getNamespaceFromProxy, shouldProxy } from './registry'; | ||
import { createProxy, getProxyFromObject, getNamespaceFromProxy, shouldProxy, getObjectFromProxy } from './registry'; | ||
import { PropSignal } from './signals'; | ||
import { setNamespace, resetNamespace } from '../namespaces'; | ||
import { isPlainObject } from '../utils'; | ||
@@ -26,2 +27,12 @@ /** | ||
/** | ||
* Checks wether a {@link PropSignal | `PropSignal`} instance exists for the | ||
* given property in the passed proxy. | ||
* | ||
* @param proxy Proxy of a state object or array. | ||
* @param key The property key. | ||
* @return `true` when it exists; false otherwise. | ||
*/ | ||
export const hasPropSignal = (proxy, key) => proxyToProps.has(proxy) && proxyToProps.get(proxy).has(key); | ||
/** | ||
* Returns the {@link PropSignal | `PropSignal`} instance associated with the | ||
@@ -206,2 +217,60 @@ * specified prop in the passed proxy. | ||
}; | ||
/** | ||
* Internal recursive implementation for {@link deepMerge | `deepMerge`}. | ||
* | ||
* @param target The target object. | ||
* @param source The source object containing new values and props. | ||
* @param override Whether existing props should be overwritten or not (`true` | ||
* by default). | ||
*/ | ||
const deepMergeRecursive = (target, source, override = true) => { | ||
if (isPlainObject(target) && isPlainObject(source)) { | ||
for (const key in source) { | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
if (typeof desc?.get === 'function' || typeof desc?.set === 'function') { | ||
if (override || !(key in target)) { | ||
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); | ||
} | ||
} | ||
} else if (isPlainObject(source[key])) { | ||
if (!(key in target)) { | ||
target[key] = {}; | ||
} | ||
deepMergeRecursive(target[key], source[key], override); | ||
} else if (override || !(key in target)) { | ||
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); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Recursively update prop values inside the passed `target` and nested plain | ||
* objects, using the values present in `source`. References to plain objects | ||
* are kept, only updating props containing primitives or arrays. Arrays are | ||
* replaced instead of merged or concatenated. | ||
* | ||
* If the `override` parameter is set to `false`, then all values in `target` | ||
* are preserved, and only new properties from `source` are added. | ||
* | ||
* @param target The target object. | ||
* @param source The source object containing new values and props. | ||
* @param override Whether existing props should be overwritten or not (`true` | ||
* by default). | ||
*/ | ||
export const deepMerge = (target, source, override = true) => batch(() => deepMergeRecursive(getObjectFromProxy(target) || target, source, override)); | ||
//# sourceMappingURL=state.js.map |
/** | ||
* Internal dependencies | ||
*/ | ||
import { proxifyState, proxifyStore } from './proxies'; | ||
import { proxifyState, proxifyStore, deepMerge } from './proxies'; | ||
/** | ||
@@ -9,3 +9,3 @@ * External dependencies | ||
import { getNamespace } from './namespaces'; | ||
import { deepMerge, isPlainObject } from './utils'; | ||
import { isPlainObject } from './utils'; | ||
export const stores = new Map(); | ||
@@ -12,0 +12,0 @@ const rawStores = new Map(); |
@@ -326,25 +326,2 @@ /** | ||
export const isPlainObject = candidate => Boolean(candidate && typeof candidate === 'object' && candidate.constructor === Object); | ||
export const deepMerge = (target, source, override = true) => { | ||
if (isPlainObject(target) && isPlainObject(source)) { | ||
for (const key in source) { | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
if (typeof desc?.get === 'function' || typeof desc?.set === 'function') { | ||
if (override || !(key in target)) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
} | ||
} else if (isPlainObject(source[key])) { | ||
if (!target[key]) { | ||
target[key] = {}; | ||
} | ||
deepMerge(target[key], source[key], override); | ||
} else if (override || !(key in target)) { | ||
Object.defineProperty(target, key, desc); | ||
} | ||
} | ||
} | ||
}; | ||
//# sourceMappingURL=utils.js.map |
/** | ||
* Internal dependencies | ||
*/ | ||
export { proxifyState, peek } from './state'; | ||
export { proxifyState, peek, deepMerge } from './state'; | ||
export { proxifyStore } from './store'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -23,3 +23,3 @@ /** | ||
*/ | ||
export declare const getProxyFromObject: <T extends object>(obj: T) => T; | ||
export declare const getProxyFromObject: <T extends object>(obj: T) => T | undefined; | ||
/** | ||
@@ -41,2 +41,10 @@ * Gets the namespace associated with the given proxy. | ||
export declare const shouldProxy: (candidate: any) => candidate is Object | Array<unknown>; | ||
/** | ||
* Returns the target object for the passed proxy. If the passed object is not a registered proxy, the | ||
* function returns `undefined`. | ||
* | ||
* @param proxy Proxy from which to know the target. | ||
* @return The target object or `undefined`. | ||
*/ | ||
export declare const getObjectFromProxy: <T extends object>(proxy: T) => T | undefined; | ||
//# sourceMappingURL=registry.d.ts.map |
/** | ||
* Checks wether a {@link PropSignal | `PropSignal`} instance exists for the | ||
* given property in the passed proxy. | ||
* | ||
* @param proxy Proxy of a state object or array. | ||
* @param key The property key. | ||
* @return `true` when it exists; false otherwise. | ||
*/ | ||
export declare const hasPropSignal: (proxy: object, key: string) => boolean; | ||
/** | ||
* Returns the proxy associated with the given state object, creating it if it | ||
@@ -22,2 +31,17 @@ * does not exist. | ||
export declare const peek: <T extends object, K extends keyof T>(obj: T, key: K) => T[K]; | ||
/** | ||
* Recursively update prop values inside the passed `target` and nested plain | ||
* objects, using the values present in `source`. References to plain objects | ||
* are kept, only updating props containing primitives or arrays. Arrays are | ||
* replaced instead of merged or concatenated. | ||
* | ||
* If the `override` parameter is set to `false`, then all values in `target` | ||
* are preserved, and only new properties from `source` are added. | ||
* | ||
* @param target The target object. | ||
* @param source The source object containing new values and props. | ||
* @param override Whether existing props should be overwritten or not (`true` | ||
* by default). | ||
*/ | ||
export declare const deepMerge: (target: any, source: any, override?: boolean) => void; | ||
//# sourceMappingURL=state.d.ts.map |
@@ -153,3 +153,2 @@ /** | ||
export declare const isPlainObject: (candidate: unknown) => candidate is Record<string, unknown>; | ||
export declare const deepMerge: (target: any, source: any, override?: boolean) => void; | ||
//# sourceMappingURL=utils.d.ts.map |
@@ -6,2 +6,8 @@ "use strict"; | ||
}); | ||
Object.defineProperty(exports, "deepMerge", { | ||
enumerable: true, | ||
get: function () { | ||
return _state.deepMerge; | ||
} | ||
}); | ||
Object.defineProperty(exports, "peek", { | ||
@@ -8,0 +14,0 @@ enumerable: true, |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.shouldProxy = exports.getProxyFromObject = exports.getNamespaceFromProxy = exports.createProxy = void 0; | ||
exports.shouldProxy = exports.getProxyFromObject = exports.getObjectFromProxy = exports.getNamespaceFromProxy = exports.createProxy = void 0; | ||
/** | ||
@@ -12,2 +12,3 @@ * Proxies for each object. | ||
const objToProxy = new WeakMap(); | ||
const proxyToObj = new WeakMap(); | ||
@@ -45,2 +46,3 @@ /** | ||
objToProxy.set(obj, proxy); | ||
proxyToObj.set(proxy, obj); | ||
proxyToNs.set(proxy, namespace); | ||
@@ -85,3 +87,13 @@ } | ||
}; | ||
/** | ||
* Returns the target object for the passed proxy. If the passed object is not a registered proxy, the | ||
* function returns `undefined`. | ||
* | ||
* @param proxy Proxy from which to know the target. | ||
* @return The target object or `undefined`. | ||
*/ | ||
exports.shouldProxy = shouldProxy; | ||
const getObjectFromProxy = proxy => proxyToObj.get(proxy); | ||
exports.getObjectFromProxy = getObjectFromProxy; | ||
//# sourceMappingURL=registry.js.map |
@@ -23,3 +23,3 @@ "use strict"; | ||
*/ | ||
const NO_SCOPE = Symbol(); | ||
const NO_SCOPE = {}; | ||
@@ -26,0 +26,0 @@ /** |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.proxifyState = exports.peek = void 0; | ||
exports.proxifyState = exports.peek = exports.hasPropSignal = exports.deepMerge = void 0; | ||
var _signals = require("@preact/signals"); | ||
@@ -12,2 +12,3 @@ var _registry = require("./registry"); | ||
var _namespaces = require("../namespaces"); | ||
var _utils = require("../utils"); | ||
/** | ||
@@ -33,2 +34,12 @@ * External dependencies | ||
/** | ||
* Checks wether a {@link PropSignal | `PropSignal`} instance exists for the | ||
* given property in the passed proxy. | ||
* | ||
* @param proxy Proxy of a state object or array. | ||
* @param key The property key. | ||
* @return `true` when it exists; false otherwise. | ||
*/ | ||
const hasPropSignal = (proxy, key) => proxyToProps.has(proxy) && proxyToProps.get(proxy).has(key); | ||
/** | ||
* Returns the {@link PropSignal | `PropSignal`} instance associated with the | ||
@@ -45,2 +56,3 @@ * specified prop in the passed proxy. | ||
*/ | ||
exports.hasPropSignal = hasPropSignal; | ||
const getPropSignal = (proxy, key, initial) => { | ||
@@ -215,3 +227,62 @@ if (!proxyToProps.has(proxy)) { | ||
}; | ||
/** | ||
* Internal recursive implementation for {@link deepMerge | `deepMerge`}. | ||
* | ||
* @param target The target object. | ||
* @param source The source object containing new values and props. | ||
* @param override Whether existing props should be overwritten or not (`true` | ||
* by default). | ||
*/ | ||
exports.peek = peek; | ||
const deepMergeRecursive = (target, source, override = true) => { | ||
if ((0, _utils.isPlainObject)(target) && (0, _utils.isPlainObject)(source)) { | ||
for (const key in source) { | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
if (typeof desc?.get === 'function' || typeof desc?.set === 'function') { | ||
if (override || !(key in target)) { | ||
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); | ||
} | ||
} | ||
} else if ((0, _utils.isPlainObject)(source[key])) { | ||
if (!(key in target)) { | ||
target[key] = {}; | ||
} | ||
deepMergeRecursive(target[key], source[key], override); | ||
} else if (override || !(key in target)) { | ||
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); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Recursively update prop values inside the passed `target` and nested plain | ||
* objects, using the values present in `source`. References to plain objects | ||
* are kept, only updating props containing primitives or arrays. Arrays are | ||
* replaced instead of merged or concatenated. | ||
* | ||
* If the `override` parameter is set to `false`, then all values in `target` | ||
* are preserved, and only new properties from `source` are added. | ||
* | ||
* @param target The target object. | ||
* @param source The source object containing new values and props. | ||
* @param override Whether existing props should be overwritten or not (`true` | ||
* by default). | ||
*/ | ||
const deepMerge = (target, source, override = true) => (0, _signals.batch)(() => deepMergeRecursive((0, _registry.getObjectFromProxy)(target) || target, source, override)); | ||
exports.deepMerge = deepMerge; | ||
//# sourceMappingURL=state.js.map |
@@ -119,4 +119,4 @@ "use strict"; | ||
const target = rawStores.get(namespace); | ||
(0, _utils.deepMerge)(target, block); | ||
(0, _utils.deepMerge)(target.state, state); | ||
(0, _proxies.deepMerge)(target, block); | ||
(0, _proxies.deepMerge)(target.state, state); | ||
} | ||
@@ -145,3 +145,3 @@ return stores.get(namespace); | ||
}); | ||
(0, _utils.deepMerge)(st.state, state, false); | ||
(0, _proxies.deepMerge)(st.state, state, false); | ||
}); | ||
@@ -148,0 +148,0 @@ } |
@@ -6,3 +6,3 @@ "use strict"; | ||
}); | ||
exports.isPlainObject = exports.deepMerge = exports.createRootFragment = void 0; | ||
exports.isPlainObject = exports.createRootFragment = void 0; | ||
exports.kebabToCamelCase = kebabToCamelCase; | ||
@@ -349,26 +349,2 @@ exports.splitTask = void 0; | ||
exports.isPlainObject = isPlainObject; | ||
const deepMerge = (target, source, override = true) => { | ||
if (isPlainObject(target) && isPlainObject(source)) { | ||
for (const key in source) { | ||
const desc = Object.getOwnPropertyDescriptor(source, key); | ||
if (typeof desc?.get === 'function' || typeof desc?.set === 'function') { | ||
if (override || !(key in target)) { | ||
Object.defineProperty(target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true | ||
}); | ||
} | ||
} else if (isPlainObject(source[key])) { | ||
if (!target[key]) { | ||
target[key] = {}; | ||
} | ||
deepMerge(target[key], source[key], override); | ||
} else if (override || !(key in target)) { | ||
Object.defineProperty(target, key, desc); | ||
} | ||
} | ||
} | ||
}; | ||
exports.deepMerge = deepMerge; | ||
//# sourceMappingURL=utils.js.map |
@@ -5,2 +5,12 @@ <!-- Learn how to maintain this file at https://github.com/WordPress/gutenberg/tree/HEAD/packages#maintaining-changelogs. --> | ||
## 6.7.0 (2024-09-05) | ||
### Enhancements | ||
- Improve internal `deepMerge` function ([#64879](https://github.com/WordPress/gutenberg/pull/64879)). | ||
### Bug Fixes | ||
- Fix computeds without scope in Firefox ([#64825](https://github.com/WordPress/gutenberg/pull/64825)). | ||
## 6.6.0 (2024-08-21) | ||
@@ -7,0 +17,0 @@ |
{ | ||
"name": "@wordpress/interactivity", | ||
"version": "6.6.0", | ||
"version": "6.7.0", | ||
"description": "Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.", | ||
@@ -36,3 +36,3 @@ "author": "The WordPress Contributors", | ||
}, | ||
"gitHead": "ab9564947967bb3f00343130954b9efacba6cdd7" | ||
"gitHead": "c90d920de07c53dff82c5914635b56fafa503b7f" | ||
} |
/** | ||
* Internal dependencies | ||
*/ | ||
export { proxifyState, peek } from './state'; | ||
export { proxifyState, peek, deepMerge } from './state'; | ||
export { proxifyStore } from './store'; |
@@ -5,2 +5,3 @@ /** | ||
const objToProxy = new WeakMap< object, object >(); | ||
const proxyToObj = new WeakMap< object, object >(); | ||
@@ -42,2 +43,3 @@ /** | ||
objToProxy.set( obj, proxy ); | ||
proxyToObj.set( proxy, obj ); | ||
proxyToNs.set( proxy, namespace ); | ||
@@ -55,4 +57,5 @@ } | ||
*/ | ||
export const getProxyFromObject = < T extends object >( obj: T ): T => | ||
objToProxy.get( obj ) as T; | ||
export const getProxyFromObject = < T extends object >( | ||
obj: T | ||
): T | undefined => objToProxy.get( obj ) as T; | ||
@@ -86,1 +89,12 @@ /** | ||
}; | ||
/** | ||
* Returns the target object for the passed proxy. If the passed object is not a registered proxy, the | ||
* function returns `undefined`. | ||
* | ||
* @param proxy Proxy from which to know the target. | ||
* @return The target object or `undefined`. | ||
*/ | ||
export const getObjectFromProxy = < T extends object >( | ||
proxy: T | ||
): T | undefined => proxyToObj.get( proxy ) as T; |
@@ -23,3 +23,3 @@ /** | ||
*/ | ||
const NO_SCOPE = Symbol(); | ||
const NO_SCOPE = {}; | ||
@@ -26,0 +26,0 @@ /** |
/** | ||
* External dependencies | ||
*/ | ||
import { signal, type Signal } from '@preact/signals'; | ||
import { batch, signal, type Signal } from '@preact/signals'; | ||
@@ -14,5 +14,7 @@ /** | ||
shouldProxy, | ||
getObjectFromProxy, | ||
} from './registry'; | ||
import { PropSignal } from './signals'; | ||
import { setNamespace, resetNamespace } from '../namespaces'; | ||
import { isPlainObject } from '../utils'; | ||
@@ -38,2 +40,13 @@ /** | ||
/** | ||
* Checks wether a {@link PropSignal | `PropSignal`} instance exists for the | ||
* given property in the passed proxy. | ||
* | ||
* @param proxy Proxy of a state object or array. | ||
* @param key The property key. | ||
* @return `true` when it exists; false otherwise. | ||
*/ | ||
export const hasPropSignal = ( proxy: object, key: string ) => | ||
proxyToProps.has( proxy ) && proxyToProps.get( proxy )!.has( key ); | ||
/** | ||
* Returns the {@link PropSignal | `PropSignal`} instance associated with the | ||
@@ -157,3 +170,3 @@ * specified prop in the passed proxy. | ||
if ( result ) { | ||
const receiver = getProxyFromObject( target ); | ||
const receiver = getProxyFromObject( target )!; | ||
const prop = getPropSignal( receiver, key ); | ||
@@ -195,3 +208,3 @@ const { get, value } = desc; | ||
if ( result ) { | ||
const prop = getPropSignal( getProxyFromObject( target ), key ); | ||
const prop = getPropSignal( getProxyFromObject( target )!, key ); | ||
prop.setValue( undefined ); | ||
@@ -255,1 +268,80 @@ | ||
}; | ||
/** | ||
* Internal recursive implementation for {@link deepMerge | `deepMerge`}. | ||
* | ||
* @param target The target object. | ||
* @param source The source object containing new values and props. | ||
* @param override Whether existing props should be overwritten or not (`true` | ||
* by default). | ||
*/ | ||
const deepMergeRecursive = ( | ||
target: any, | ||
source: any, | ||
override: boolean = true | ||
) => { | ||
if ( isPlainObject( target ) && isPlainObject( source ) ) { | ||
for ( const key in source ) { | ||
const desc = Object.getOwnPropertyDescriptor( source, key ); | ||
if ( | ||
typeof desc?.get === 'function' || | ||
typeof desc?.set === 'function' | ||
) { | ||
if ( override || ! ( key in target ) ) { | ||
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 ); | ||
} | ||
} | ||
} else if ( isPlainObject( source[ key ] ) ) { | ||
if ( ! ( key in target ) ) { | ||
target[ key ] = {}; | ||
} | ||
deepMergeRecursive( target[ key ], source[ key ], override ); | ||
} else if ( override || ! ( key in target ) ) { | ||
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 ); | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* Recursively update prop values inside the passed `target` and nested plain | ||
* objects, using the values present in `source`. References to plain objects | ||
* are kept, only updating props containing primitives or arrays. Arrays are | ||
* replaced instead of merged or concatenated. | ||
* | ||
* If the `override` parameter is set to `false`, then all values in `target` | ||
* are preserved, and only new properties from `source` are added. | ||
* | ||
* @param target The target object. | ||
* @param source The source object containing new values and props. | ||
* @param override Whether existing props should be overwritten or not (`true` | ||
* by default). | ||
*/ | ||
export const deepMerge = ( | ||
target: any, | ||
source: any, | ||
override: boolean = true | ||
) => | ||
batch( () => | ||
deepMergeRecursive( | ||
getObjectFromProxy( target ) || target, | ||
source, | ||
override | ||
) | ||
); |
/** | ||
* Internal dependencies | ||
*/ | ||
import { proxifyState, proxifyStore } from './proxies'; | ||
import { proxifyState, proxifyStore, deepMerge } from './proxies'; | ||
/** | ||
@@ -9,3 +9,3 @@ * External dependencies | ||
import { getNamespace } from './namespaces'; | ||
import { deepMerge, isPlainObject } from './utils'; | ||
import { isPlainObject } from './utils'; | ||
@@ -12,0 +12,0 @@ export const stores = new Map(); |
/** | ||
* Internal dependencies | ||
*/ | ||
import { deepMerge, kebabToCamelCase } from '../utils'; | ||
import { kebabToCamelCase } from '../utils'; | ||
describe( 'Interactivity API', () => { | ||
describe( 'deepMerge', () => { | ||
it( 'should merge two plain objects', () => { | ||
const target = { a: 1, b: 2 }; | ||
const source = { b: 3, c: 4 }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: 1, b: 3, c: 4 } ); | ||
} ); | ||
it( 'should handle nested objects', () => { | ||
const target = { a: { x: 1 }, b: 2 }; | ||
const source = { a: { y: 2 }, c: 3 }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: { x: 1, y: 2 }, b: 2, c: 3 } ); | ||
} ); | ||
it( 'should not override existing properties when override is false', () => { | ||
const target = { a: 1, b: { x: 10 } }; | ||
const source = { a: 2, b: { y: 20 }, c: 3 }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source, false ); | ||
expect( result ).toEqual( { a: 1, b: { x: 10, y: 20 }, c: 3 } ); | ||
} ); | ||
it( 'should handle getters', () => { | ||
const target = { | ||
get a() { | ||
return 1; | ||
}, | ||
b: 1, | ||
}; | ||
const source = { | ||
a: 2, | ||
get b() { | ||
return 2; | ||
}, | ||
}; | ||
const result: Record< string, any > = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result.a ).toBe( 2 ); | ||
expect( result.b ).toBe( 2 ); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'a' )?.get | ||
).toBeUndefined(); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'b' )?.get | ||
).toBeDefined(); | ||
} ); | ||
it( 'should not execute getters when performing the deep merge', () => { | ||
let targetExecuted = false; | ||
let sourceExecuted = false; | ||
const target = { | ||
get a() { | ||
targetExecuted = true; | ||
return 1; | ||
}, | ||
}; | ||
const source = { | ||
get b() { | ||
sourceExecuted = true; | ||
return 2; | ||
}, | ||
}; | ||
const result: Record< string, any > = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( targetExecuted ).toBe( false ); | ||
expect( sourceExecuted ).toBe( false ); | ||
} ); | ||
https: it( 'should handle setters', () => { | ||
let targetValue = 1; | ||
const target = { | ||
get a() { | ||
return targetValue; | ||
}, | ||
set a( value ) { | ||
targetValue = value; | ||
}, | ||
b: 1, | ||
}; | ||
let sourceValue = 2; | ||
const source = { | ||
a: 3, | ||
get b() { | ||
return 2; | ||
}, | ||
set b( value ) { | ||
sourceValue = value; | ||
}, | ||
}; | ||
const result: Record< string, any > = {}; | ||
deepMerge( result, target ); | ||
result.a = 5; | ||
expect( targetValue ).toBe( 5 ); | ||
expect( result.a ).toBe( 5 ); | ||
deepMerge( result, source ); | ||
result.a = 6; | ||
expect( targetValue ).toBe( 5 ); | ||
result.b = 7; | ||
expect( sourceValue ).toBe( 7 ); | ||
expect( result.a ).toBe( 6 ); | ||
expect( result.b ).toBe( 2 ); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'a' )?.set | ||
).toBeUndefined(); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'b' )?.set | ||
).toBeDefined(); | ||
} ); | ||
it( 'should handle setters when overwrite is false', () => { | ||
let targetValue = 1; | ||
const target = { | ||
get a() { | ||
return targetValue; | ||
}, | ||
set a( value ) { | ||
targetValue = value; | ||
}, | ||
b: 1, | ||
}; | ||
let sourceValue = 2; | ||
const source = { | ||
a: 3, | ||
get b() { | ||
return 2; | ||
}, | ||
set b( value ) { | ||
sourceValue = value; | ||
}, | ||
}; | ||
const result: Record< string, any > = {}; | ||
deepMerge( result, target, false ); | ||
deepMerge( result, source, false ); | ||
result.a = 6; | ||
expect( targetValue ).toBe( 6 ); | ||
result.b = 7; | ||
expect( sourceValue ).toBe( 2 ); | ||
expect( result.a ).toBe( 6 ); | ||
expect( result.b ).toBe( 7 ); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'a' )?.set | ||
).toBeDefined(); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'b' )?.set | ||
).toBeUndefined(); | ||
} ); | ||
it( 'should handle getters and setters together', () => { | ||
let targetValue = 1; | ||
const target = { | ||
get a() { | ||
return targetValue; | ||
}, | ||
set a( value ) { | ||
targetValue = value; | ||
}, | ||
b: 1, | ||
}; | ||
let sourceValue = 2; | ||
const source = { | ||
get a() { | ||
return 3; | ||
}, | ||
set a( value ) { | ||
sourceValue = value; | ||
}, | ||
}; | ||
const result: Record< string, any > = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
// Test if setters and getters are copied correctly | ||
result.a = 5; | ||
expect( targetValue ).toBe( 1 ); // Should not change | ||
expect( sourceValue ).toBe( 5 ); // Should change | ||
expect( result.a ).toBe( 3 ); // Should return the getter's value | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'a' )?.get | ||
).toBeDefined(); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'a' )?.set | ||
).toBeDefined(); | ||
} ); | ||
it( 'should handle getters when overwrite is false', () => { | ||
const target = { | ||
get a() { | ||
return 1; | ||
}, | ||
b: 1, | ||
}; | ||
const source = { | ||
a: 2, | ||
get b() { | ||
return 2; | ||
}, | ||
}; | ||
const result: Record< string, any > = {}; | ||
deepMerge( result, target, false ); | ||
deepMerge( result, source, false ); | ||
expect( result.a ).toBe( 1 ); | ||
expect( result.b ).toBe( 1 ); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'a' )?.get | ||
).toBeDefined(); | ||
expect( | ||
Object.getOwnPropertyDescriptor( result, 'b' )?.get | ||
).toBeUndefined(); | ||
} ); | ||
it( 'should ignore non-plain objects', () => { | ||
const target = { a: 1 }; | ||
const source = new Date(); | ||
const result = { ...target }; | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: 1 } ); | ||
} ); | ||
it( 'should handle arrays', () => { | ||
const target = { a: [ 1, 2 ] }; | ||
const source = { a: [ 3, 4 ] }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: [ 3, 4 ] } ); | ||
} ); | ||
it( 'should handle arrays when overwrite is false', () => { | ||
const target = { a: [ 1, 2 ] }; | ||
const source = { a: [ 3, 4 ] }; | ||
const result = {}; | ||
deepMerge( result, target, false ); | ||
deepMerge( result, source, false ); | ||
expect( result ).toEqual( { a: [ 1, 2 ] } ); | ||
} ); | ||
it( 'should handle null values', () => { | ||
const target = { a: 1, b: null }; | ||
const source = { b: 2, c: null }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: 1, b: 2, c: null } ); | ||
} ); | ||
it( 'should handle undefined values', () => { | ||
const target = { a: 1, b: undefined }; | ||
const source = { b: 2, c: undefined }; | ||
const result = {}; | ||
deepMerge( result, target ); | ||
deepMerge( result, source ); | ||
expect( result ).toEqual( { a: 1, b: 2, c: undefined } ); | ||
} ); | ||
it( 'should handle undefined values when overwrite is false', () => { | ||
const target = { a: 1, b: undefined }; | ||
const source = { b: 2, c: undefined }; | ||
const result = {}; | ||
deepMerge( result, target, false ); | ||
deepMerge( result, source, false ); | ||
expect( result ).toEqual( { a: 1, b: undefined, c: undefined } ); | ||
} ); | ||
it( 'should handle deleted values when overwrite is false', () => { | ||
const target = { a: 1 }; | ||
const source = { a: 2 }; | ||
const result: Record< string, any > = {}; | ||
deepMerge( result, target, false ); | ||
delete result.a; | ||
deepMerge( result, source, false ); | ||
expect( result ).toEqual( { a: 2 } ); | ||
} ); | ||
} ); | ||
describe( 'kebabToCamelCase', () => { | ||
@@ -301,0 +8,0 @@ it( 'should work exactly as the PHP version', async () => { |
@@ -367,32 +367,1 @@ /** | ||
); | ||
export const deepMerge = ( | ||
target: any, | ||
source: any, | ||
override: boolean = true | ||
) => { | ||
if ( isPlainObject( target ) && isPlainObject( source ) ) { | ||
for ( const key in source ) { | ||
const desc = Object.getOwnPropertyDescriptor( source, key ); | ||
if ( | ||
typeof desc?.get === 'function' || | ||
typeof desc?.set === 'function' | ||
) { | ||
if ( override || ! ( key in target ) ) { | ||
Object.defineProperty( target, key, { | ||
...desc, | ||
configurable: true, | ||
enumerable: true, | ||
} ); | ||
} | ||
} else if ( isPlainObject( source[ key ] ) ) { | ||
if ( ! target[ key ] ) { | ||
target[ key ] = {}; | ||
} | ||
deepMerge( target[ key ], source[ key ], override ); | ||
} else if ( override || ! ( key in target ) ) { | ||
Object.defineProperty( target, key, desc! ); | ||
} | ||
} | ||
} | ||
}; |
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
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
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
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
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
659681
122
9241