@wordpress/hooks
Version:
WordPress hooks library.
101 lines (88 loc) • 2.6 kB
text/typescript
/**
* Internal dependencies
*/
import type { Hooks, StoreKey } from './types';
export type RunHook = (
hookName: string,
...args: unknown[]
) => undefined | unknown;
/**
* Returns a function which, when invoked, will execute all callbacks
* registered to a hook of the specified type, optionally returning the final
* value of the call chain.
*
* @param hooks Hooks instance.
* @param storeKey
* @param returnFirstArg Whether each hook callback is expected to return its first argument.
* @param async Whether the hook callback should be run asynchronously
*
* @return Function that runs hook callbacks.
*/
function createRunHook(
hooks: Hooks,
storeKey: StoreKey,
returnFirstArg: boolean,
async: boolean
): RunHook {
return function runHook( hookName, ...args ) {
const hooksStore = hooks[ storeKey ];
if ( ! hooksStore[ hookName ] ) {
hooksStore[ hookName ] = {
handlers: [],
runs: 0,
};
}
hooksStore[ hookName ].runs++;
const handlers = hooksStore[ hookName ].handlers;
// The following code is stripped from production builds.
if ( 'production' !== process.env.NODE_ENV ) {
// Handle any 'all' hooks registered.
if ( 'hookAdded' !== hookName && hooksStore.all ) {
handlers.push( ...hooksStore.all.handlers );
}
}
if ( ! handlers || ! handlers.length ) {
return returnFirstArg ? args[ 0 ] : undefined;
}
const hookInfo = {
name: hookName,
currentIndex: 0,
};
async function asyncRunner() {
try {
hooksStore.__current.add( hookInfo );
let result = returnFirstArg ? args[ 0 ] : undefined;
while ( hookInfo.currentIndex < handlers.length ) {
const handler = handlers[ hookInfo.currentIndex ];
result = await handler.callback.apply( null, args );
if ( returnFirstArg ) {
args[ 0 ] = result;
}
hookInfo.currentIndex++;
}
return returnFirstArg ? result : undefined;
} finally {
hooksStore.__current.delete( hookInfo );
}
}
function syncRunner() {
try {
hooksStore.__current.add( hookInfo );
let result = returnFirstArg ? args[ 0 ] : undefined;
while ( hookInfo.currentIndex < handlers.length ) {
const handler = handlers[ hookInfo.currentIndex ];
result = handler.callback.apply( null, args );
if ( returnFirstArg ) {
args[ 0 ] = result;
}
hookInfo.currentIndex++;
}
return returnFirstArg ? result : undefined;
} finally {
hooksStore.__current.delete( hookInfo );
}
}
return ( async ? asyncRunner : syncRunner )();
};
}
export default createRunHook;