UNPKG

ts-ioc-container

Version:
54 lines (53 loc) 2.86 kB
import { createHookContext } from './HookContext'; import { isConstructor, promisify } from '../utils'; import { UnexpectedHookResultError } from '../errors/UnexpectedHookResultError'; import { toInjectFn } from '../injector/inject'; const isHookClassConstructor = (execute) => { return isConstructor(execute) && execute.prototype.execute; }; export const toHookFn = (execute) => isHookClassConstructor(execute) ? (context) => context.scope.resolveOne(execute).execute(context) : execute; export const hook = (key, ...fns) => (target, propertyKey) => { const hooks = Reflect.hasMetadata(key, target.constructor) ? Reflect.getMetadata(key, target.constructor) : new Map(); hooks.set(propertyKey, (hooks.get(propertyKey) ?? []).concat(fns)); Reflect.defineMetadata(key, hooks, target.constructor); }; export function getHooks(target, key) { return Reflect.hasMetadata(key, target.constructor) ? Reflect.getMetadata(key, target.constructor) : new Map(); } export function hasHooks(target, key) { return Reflect.hasMetadata(key, target.constructor); } export const runHooks = (target, key, { scope, createContext = createHookContext, predicate = () => true, }) => { const hooks = Array.from(getHooks(target, key).entries()).filter(([methodName]) => predicate(methodName)); const runMethodHooks = (methodName, executions) => { const context = createContext(target, scope, methodName); for (const execute of executions) { const result = execute(context); if (result instanceof Promise) { throw new UnexpectedHookResultError(`Hook ${methodName} returned a promise, use runHooksAsync instead`); } } }; for (const [methodName, executions] of hooks) { runMethodHooks(methodName, executions.map(toHookFn)); } }; export const runHooksAsync = (target, key, { scope, createContext = createHookContext, predicate = () => true, }) => { const hooks = Array.from(getHooks(target, key).entries()).filter(([methodName]) => predicate(methodName)); const runMethodHooks = async (methodName, executions) => { const context = createContext(target, scope, methodName); for (const execute of executions) { await promisify(execute(context)); } }; return Promise.all(hooks.map(([methodName, executions]) => runMethodHooks(methodName, executions.map(toHookFn)))); }; export const injectProp = (fn) => (context) => context.setProperty(toInjectFn(fn)); export const onConstruct = (fn) => hook('onConstruct', fn); export const runOnConstructHooks = (target, scope) => runHooks(target, 'onConstruct', { scope }); export const onDispose = hook('onDispose', (context) => { context.invokeMethod({}); }); export const runOnDisposeHooks = (target, scope) => runHooks(target, 'onDispose', { scope });