@wordpress/interactivity
Version:
Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.
8 lines (7 loc) • 16.2 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/proxies/state.ts"],
"sourcesContent": ["/**\n * External dependencies\n */\nimport { batch, signal, type Signal } from '@preact/signals';\n\n/**\n * Internal dependencies\n */\nimport {\n\tcreateProxy,\n\tgetProxyFromObject,\n\tgetNamespaceFromProxy,\n\tshouldProxy,\n\tgetObjectFromProxy,\n} from './registry';\nimport { PropSignal } from './signals';\nimport { setNamespace, resetNamespace } from '../namespaces';\nimport { isPlainObject } from '../utils';\n\n/**\n * Set of built-in symbols.\n */\nconst wellKnownSymbols = new Set(\n\tObject.getOwnPropertyNames( Symbol )\n\t\t.map( ( key ) => Symbol[ key ] )\n\t\t.filter( ( value ) => typeof value === 'symbol' )\n);\n\n/**\n * Relates each proxy with a map of {@link PropSignal} instances, representing\n * the proxy's accessed properties.\n */\nconst proxyToProps: WeakMap<\n\tobject,\n\tMap< string | symbol, PropSignal >\n> = new WeakMap();\n\n/**\n * Checks whether a {@link PropSignal | `PropSignal`} instance exists for the\n * given property in the passed proxy.\n *\n * @param proxy Proxy of a state object or array.\n * @param key The property key.\n * @return `true` when it exists; false otherwise.\n */\nexport const hasPropSignal = ( proxy: object, key: string ) =>\n\tproxyToProps.has( proxy ) && proxyToProps.get( proxy )!.has( key );\n\n/**\n * Returns the {@link PropSignal | `PropSignal`} instance associated with the\n * specified prop in the passed proxy.\n *\n * The `PropSignal` instance is generated if it doesn't exist yet, using the\n * `initial` parameter to initialize the internal signals.\n *\n * @param proxy Proxy of a state object or array.\n * @param key The property key.\n * @param initial Initial data for the `PropSignal` instance.\n * @return The `PropSignal` instance.\n */\nconst getPropSignal = (\n\tproxy: object,\n\tkey: string | number | symbol,\n\tinitial?: PropertyDescriptor\n) => {\n\tif ( ! proxyToProps.has( proxy ) ) {\n\t\tproxyToProps.set( proxy, new Map() );\n\t}\n\tkey = typeof key === 'number' ? `${ key }` : key;\n\tconst props = proxyToProps.get( proxy )!;\n\tif ( ! props.has( key ) ) {\n\t\tconst ns = getNamespaceFromProxy( proxy );\n\t\tconst prop = new PropSignal( proxy );\n\t\tprops.set( key, prop );\n\t\tif ( initial ) {\n\t\t\tconst { get, value } = initial;\n\t\t\tif ( get ) {\n\t\t\t\tprop.setGetter( get );\n\t\t\t} else {\n\t\t\t\tprop.setValue(\n\t\t\t\t\tshouldProxy( value ) ? proxifyState( ns, value ) : value\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\treturn props.get( key )!;\n};\n\n/**\n * Relates each proxied object (i.e., the original object) with a signal that\n * tracks changes in the number of properties.\n */\nconst objToIterable = new WeakMap< object, Signal< number > >();\n\n/**\n * When this flag is `true`, it avoids any signal subscription, overriding state\n * props' \"reactive\" behavior.\n */\nlet peeking = false;\n\nexport const PENDING_GETTER = Symbol( 'PENDING_GETTER' );\n\n/**\n * Handlers for reactive objects and arrays in the state.\n */\nconst stateHandlers: ProxyHandler< object > = {\n\tget( target: object, key: string | symbol, receiver: object ): any {\n\t\t/*\n\t\t * The property should not be reactive for the following cases:\n\t\t * 1. While using the `peek` function to read the property.\n\t\t * 2. The property exists but comes from the Object or Array prototypes.\n\t\t * 3. The property key is a known symbol.\n\t\t */\n\t\tif (\n\t\t\tpeeking ||\n\t\t\t( ! target.hasOwnProperty( key ) && key in target ) ||\n\t\t\t( typeof key === 'symbol' && wellKnownSymbols.has( key ) )\n\t\t) {\n\t\t\treturn Reflect.get( target, key, receiver );\n\t\t}\n\n\t\t// At this point, the property should be reactive.\n\t\tconst desc = Object.getOwnPropertyDescriptor( target, key );\n\t\tconst prop = getPropSignal( receiver, key, desc );\n\t\tconst result = prop.getComputed().value;\n\n\t\tif ( result === PENDING_GETTER ) {\n\t\t\tthrow PENDING_GETTER;\n\t\t}\n\n\t\t/*\n\t\t * Check if the property is a synchronous function. If it is, set the\n\t\t * default namespace. Synchronous functions always run in the proper scope,\n\t\t * which is set by the Directives component.\n\t\t */\n\t\tif ( typeof result === 'function' ) {\n\t\t\tconst ns = getNamespaceFromProxy( receiver );\n\t\t\treturn ( ...args: unknown[] ) => {\n\t\t\t\tsetNamespace( ns );\n\t\t\t\ttry {\n\t\t\t\t\treturn result.call( receiver, ...args );\n\t\t\t\t} finally {\n\t\t\t\t\tresetNamespace();\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\treturn result;\n\t},\n\n\tset(\n\t\ttarget: object,\n\t\tkey: string,\n\t\tvalue: unknown,\n\t\treceiver: object\n\t): boolean {\n\t\tsetNamespace( getNamespaceFromProxy( receiver ) );\n\t\ttry {\n\t\t\treturn Reflect.set( target, key, value, receiver );\n\t\t} finally {\n\t\t\tresetNamespace();\n\t\t}\n\t},\n\n\tdefineProperty(\n\t\ttarget: object,\n\t\tkey: string,\n\t\tdesc: PropertyDescriptor\n\t): boolean {\n\t\tconst isNew = ! ( key in target );\n\t\tconst result = Reflect.defineProperty( target, key, desc );\n\n\t\tif ( result ) {\n\t\t\tconst receiver = getProxyFromObject( target )!;\n\t\t\tconst prop = getPropSignal( receiver, key );\n\t\t\tconst { get, value } = desc;\n\t\t\tif ( get ) {\n\t\t\t\tprop.setGetter( get );\n\t\t\t} else {\n\t\t\t\tconst ns = getNamespaceFromProxy( receiver );\n\t\t\t\tprop.setValue(\n\t\t\t\t\tshouldProxy( value ) ? proxifyState( ns, value ) : value\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif ( isNew && objToIterable.has( target ) ) {\n\t\t\t\tobjToIterable.get( target )!.value++;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Modify the `length` property value only if the related\n\t\t\t * `PropSignal` exists, which means that there are subscriptions to\n\t\t\t * this property.\n\t\t\t */\n\t\t\tif (\n\t\t\t\tArray.isArray( target ) &&\n\t\t\t\tproxyToProps.get( receiver )?.has( 'length' )\n\t\t\t) {\n\t\t\t\tconst length = getPropSignal( receiver, 'length' );\n\t\t\t\tlength.setValue( target.length );\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t},\n\n\tdeleteProperty( target: object, key: string ): boolean {\n\t\tconst result = Reflect.deleteProperty( target, key );\n\n\t\tif ( result ) {\n\t\t\tconst prop = getPropSignal( getProxyFromObject( target )!, key );\n\t\t\tprop.setValue( undefined );\n\n\t\t\tif ( objToIterable.has( target ) ) {\n\t\t\t\tobjToIterable.get( target )!.value++;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t},\n\n\townKeys( target: object ): ( string | symbol )[] {\n\t\tif ( ! objToIterable.has( target ) ) {\n\t\t\tobjToIterable.set( target, signal( 0 ) );\n\t\t}\n\t\t/*\n\t\t *This subscribes to the signal while preventing the minifier from\n\t\t * deleting this line in production.\n\t\t */\n\t\t( objToIterable as any )._ = objToIterable.get( target )!.value;\n\t\treturn Reflect.ownKeys( target );\n\t},\n};\n\n/**\n * Returns the proxy associated with the given state object, creating it if it\n * does not exist.\n *\n * @param namespace The namespace that will be associated to this proxy.\n * @param obj The object to proxify.\n *\n * @throws Error if the object cannot be proxified. Use {@link shouldProxy} to\n * check if a proxy can be created for a specific object.\n *\n * @return The associated proxy.\n */\nexport const proxifyState = < T extends object >(\n\tnamespace: string,\n\tobj: T\n): T => {\n\treturn createProxy( namespace, obj, stateHandlers ) as T;\n};\n\n/**\n * Reads the value of the specified property without subscribing to it.\n *\n * @param obj The object to read the property from.\n * @param key The property key.\n * @return The property value.\n */\nexport const peek = < T extends object, K extends keyof T >(\n\tobj: T,\n\tkey: K\n): T[ K ] => {\n\tpeeking = true;\n\ttry {\n\t\treturn obj[ key ];\n\t} finally {\n\t\tpeeking = false;\n\t}\n};\n\n/**\n * Internal recursive implementation for {@link deepMerge | `deepMerge`}.\n *\n * @param target The target object.\n * @param source The source object containing new values and props.\n * @param override Whether existing props should be overwritten or not (`true`\n * by default).\n */\nconst deepMergeRecursive = (\n\ttarget: any,\n\tsource: any,\n\toverride: boolean = true\n) => {\n\t// If target is not a plain object and the source is, we don't need to merge\n\t// them because the source will be used as the new value of the target.\n\tif ( ! ( isPlainObject( target ) && isPlainObject( source ) ) ) {\n\t\treturn;\n\t}\n\n\tlet hasNewKeys = false;\n\n\tfor ( const key in source ) {\n\t\tconst isNew = ! ( key in target );\n\t\thasNewKeys = hasNewKeys || isNew;\n\n\t\tconst desc = Object.getOwnPropertyDescriptor( source, key )!;\n\t\tconst proxy = getProxyFromObject( target );\n\t\tconst propSignal =\n\t\t\t!! proxy &&\n\t\t\thasPropSignal( proxy, key ) &&\n\t\t\tgetPropSignal( proxy, key );\n\n\t\t// Handle getters and setters\n\t\tif (\n\t\t\ttypeof desc.get === 'function' ||\n\t\t\ttypeof desc.set === 'function'\n\t\t) {\n\t\t\tif ( override || isNew ) {\n\t\t\t\t// Because we are setting a getter or setter, we need to use\n\t\t\t\t// Object.defineProperty to define the property on the target object.\n\t\t\t\tObject.defineProperty( target, key, {\n\t\t\t\t\t...desc,\n\t\t\t\t\tconfigurable: true,\n\t\t\t\t\tenumerable: true,\n\t\t\t\t} );\n\t\t\t\t// Update the getter in the property signal if it exists\n\t\t\t\tif ( desc.get && propSignal ) {\n\t\t\t\t\tpropSignal.setPendingGetter( desc.get );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle nested objects\n\t\t} else if ( isPlainObject( source[ key ] ) ) {\n\t\t\tconst targetValue = Object.getOwnPropertyDescriptor( target, key )\n\t\t\t\t?.value;\n\t\t\tif ( isNew || ( override && ! isPlainObject( targetValue ) ) ) {\n\t\t\t\t// Create a new object if the property is new or needs to be overridden\n\t\t\t\ttarget[ key ] = {};\n\t\t\t\tif ( propSignal ) {\n\t\t\t\t\t// Create a new proxified state for the nested object\n\t\t\t\t\tconst ns = getNamespaceFromProxy( proxy );\n\t\t\t\t\tpropSignal.setValue(\n\t\t\t\t\t\tproxifyState( ns, target[ key ] as Object )\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tdeepMergeRecursive( target[ key ], source[ key ], override );\n\t\t\t}\n\t\t\t// Both target and source are plain objects, merge them recursively\n\t\t\telse if ( isPlainObject( targetValue ) ) {\n\t\t\t\tdeepMergeRecursive( target[ key ], source[ key ], override );\n\t\t\t}\n\n\t\t\t// Handle primitive values and non-plain objects\n\t\t} else if ( override || isNew ) {\n\t\t\tObject.defineProperty( target, key, desc );\n\t\t\tif ( propSignal ) {\n\t\t\t\tconst { value } = desc;\n\t\t\t\tconst ns = getNamespaceFromProxy( proxy );\n\t\t\t\t// Proxify the value if necessary before setting it in the signal\n\t\t\t\tpropSignal.setValue(\n\t\t\t\t\tshouldProxy( value ) ? proxifyState( ns, value ) : value\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( hasNewKeys && objToIterable.has( target ) ) {\n\t\tobjToIterable.get( target )!.value++;\n\t}\n};\n\n/**\n * Recursively updates prop values inside the passed `target` and nested plain\n * objects, using the values present in `source`. References to plain objects\n * are kept, only updating props containing primitives or arrays. Arrays are\n * replaced instead of merged or concatenated.\n *\n * If the `override` parameter is set to `false`, then all values in `target`\n * are preserved, and only new properties from `source` are added.\n *\n * @param target The target object.\n * @param source The source object containing new values and props.\n * @param override Whether existing props should be overwritten or not (`true`\n * by default).\n */\nexport const deepMerge = (\n\ttarget: any,\n\tsource: any,\n\toverride: boolean = true\n) =>\n\tbatch( () =>\n\t\tdeepMergeRecursive(\n\t\t\tgetObjectFromProxy( target ) || target,\n\t\t\tsource,\n\t\t\toverride\n\t\t)\n\t);\n"],
"mappings": "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",
"names": []
}