Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactivity API: Improve internal deepMerge function #64879

Merged
merged 11 commits into from
Aug 29, 2024
2 changes: 1 addition & 1 deletion packages/interactivity/src/proxies/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Internal dependencies
*/
export { proxifyState, peek } from './state';
export { proxifyState, peek, deepMerge, hasPropSignal } from './state';
export { proxifyStore } from './store';
4 changes: 4 additions & 0 deletions packages/interactivity/src/proxies/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Proxies for each object.
*/
const objToProxy = new WeakMap< object, object >();
const proxyToObj = new WeakMap< object, object >();

/**
* Namespaces for each created proxy.
Expand Down Expand Up @@ -38,6 +39,7 @@ export const createProxy = < T extends object >(
if ( ! objToProxy.has( obj ) ) {
const proxy = new Proxy( obj, handlers );
objToProxy.set( obj, proxy );
proxyToObj.set( proxy, obj );
proxyToNs.set( proxy, namespace );
}
return objToProxy.get( obj ) as T;
Expand Down Expand Up @@ -80,3 +82,5 @@ export const shouldProxy = (
! proxyToNs.has( candidate ) && supported.has( candidate.constructor )
);
};

export const getObjectFromProxy = ( proxy ) => proxyToObj.get( proxy );
72 changes: 71 additions & 1 deletion packages/interactivity/src/proxies/state.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { signal, type Signal } from '@preact/signals';
import { batch, signal, type Signal } from '@preact/signals';

/**
* Internal dependencies
Expand All @@ -11,9 +11,11 @@ import {
getProxyFromObject,
getNamespaceFromProxy,
shouldProxy,
getObjectFromProxy,
} from './registry';
import { PropSignal } from './signals';
import { setNamespace, resetNamespace } from '../namespaces';
import { isPlainObject } from '../utils';

/**
* Set of built-in symbols.
Expand All @@ -33,6 +35,17 @@ const proxyToProps: WeakMap<
Map< string | symbol, PropSignal >
> = new WeakMap();

/**
*
* @param proxy
* @param key
* @return TODO
*/
export const hasPropSignal = ( proxy, key ) =>
Boolean(
proxyToProps.has( proxy ) && proxyToProps.get( proxy )?.has( key )
);
DAreRodz marked this conversation as resolved.
Show resolved Hide resolved

/**
* Returns the {@link PropSignal | `PropSignal`} instance associated with the
* specified prop in the passed proxy.
Expand Down Expand Up @@ -248,3 +261,60 @@ export const peek = < T extends object, K extends keyof T >(
peeking = false;
}
};

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 ) ) {
DAreRodz marked this conversation as resolved.
Show resolved Hide resolved
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 );
}
}
}
}
};

export const deepMerge = (
target: any,
source: any,
override: boolean = true
) =>
batch( () =>
deepMergeRecursive(
getObjectFromProxy( target ) || target,
source,
override
)
);
Loading
Loading