-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
52 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,79 @@ | ||
/** | ||
* Proxies for each object. | ||
*/ | ||
const objToProxy = new WeakMap< object, object >(); | ||
|
||
/** | ||
* Namespaces for each created proxy. | ||
*/ | ||
const proxyToNs = new WeakMap< object, string >(); | ||
const ignore = new WeakSet< object >(); | ||
|
||
/** | ||
* Object types that can be proxied. | ||
*/ | ||
const supported = new Set( [ Object, Array ] ); | ||
|
||
/** | ||
* Returns a proxy to the passed object with the given handlers, assigning the | ||
* specified namespace to it. If a proxy for the passed object was created | ||
* before, that proxy is returned. | ||
* | ||
* @param namespace The namespace that will be associated to this proxy. | ||
* @param obj The object to proxify. | ||
* @param handlers Handlers that the proxy will use. | ||
* | ||
* @throws Error if the object cannot be proxified. Use {@link shouldProxy} to | ||
* check if a proxy can be created for a specific object. | ||
* | ||
* @return The created proxy. | ||
*/ | ||
export const createProxy = < T extends object >( | ||
namespace: string, | ||
obj: T, | ||
handlers: ProxyHandler< T > | ||
): T => { | ||
if ( ! shouldProxy( obj ) ) { | ||
throw Error( 'This object can be proxified.' ); | ||
throw Error( 'This object cannot be proxified.' ); | ||
} | ||
if ( ! objToProxy.has( obj ) && handlers && namespace ) { | ||
if ( ! objToProxy.has( obj ) ) { | ||
const proxy = new Proxy( obj, handlers ); | ||
ignore.add( proxy ); | ||
objToProxy.set( obj, proxy ); | ||
proxyToNs.set( proxy, namespace ); | ||
} | ||
return objToProxy.get( obj ) as T; | ||
}; | ||
|
||
/** | ||
* Returns the proxy for the given object. If there is no associated proxy, the | ||
* function returns `undefined`. | ||
* | ||
* @param obj Object from which to know the proxy. | ||
* @return Associated proxy or `undefined`. | ||
*/ | ||
export const getProxy = < T extends object >( obj: T ): T => | ||
objToProxy.get( obj ) as T; | ||
|
||
/** | ||
* Gets the namespace associated with the given proxy. | ||
* | ||
* @param proxy Proxy. | ||
* @return Namespace. | ||
*/ | ||
export const getProxyNs = ( proxy: object ): string => proxyToNs.get( proxy )!; | ||
|
||
export const shouldProxy = ( val: any ): val is Object | Array< unknown > => { | ||
if ( typeof val !== 'object' || val === null ) { | ||
/** | ||
* Checks if a given object can be proxied. | ||
* | ||
* @param candidate Object to know whether it can be proxied. | ||
* @return True if the passed instance can be proxied. | ||
*/ | ||
export const shouldProxy = ( | ||
candidate: any | ||
): candidate is Object | Array< unknown > => { | ||
if ( typeof candidate !== 'object' || candidate === null ) { | ||
return false; | ||
} | ||
return ! ignore.has( val ) && supported.has( val.constructor ); | ||
return ( | ||
! proxyToNs.has( candidate ) && supported.has( candidate.constructor ) | ||
); | ||
}; |