ts-ioc-container
Version:
Fast, lightweight TypeScript dependency injection container with a clean API, scoped lifecycles, decorators, tokens, hooks, lazy injection, customizable providers, and no global container objects.
55 lines (54 loc) • 2.29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.hook = exports.toHookFn = void 0;
exports.getHooks = getHooks;
exports.hasHooks = hasHooks;
const basic_1 = require("../utils/basic");
const proxy_1 = require("../utils/proxy");
const isHookClassConstructor = (execute) => {
return basic_1.Is.constructor(execute) && execute.prototype.execute;
};
const toHookFn = (execute) => isHookClassConstructor(execute) ? (context) => context.scope.resolve(execute).execute(context) : execute;
exports.toHookFn = toHookFn;
const getReflectionTarget = (target) => {
return (0, proxy_1.isProxy)(target) ? (0, proxy_1.getProxyTarget)(target) : target;
};
// Walk the constructor's prototype chain (most-derived first) collecting each class.
const getConstructorChain = (ctor) => {
const chain = [];
let current = ctor;
while (typeof current === 'function' && current !== Function.prototype) {
chain.push(current);
current = Object.getPrototypeOf(current);
}
return chain;
};
// Get hooks metadata, merging hooks declared on parent (extended-from) classes.
// Hooks are collected from base to derived so a derived class's hooks for the same
// method name take precedence over (replace) the parent's.
function getHooks(target, key) {
const reflectionTarget = getReflectionTarget(target);
const merged = new Map();
for (const ctor of getConstructorChain(reflectionTarget.constructor).reverse()) {
const ownHooks = Reflect.getOwnMetadata(key, ctor);
if (ownHooks) {
for (const [methodName, fns] of ownHooks) {
merged.set(methodName, fns);
}
}
}
return merged;
}
function hasHooks(target, key) {
const reflectionTarget = getReflectionTarget(target);
return getConstructorChain(reflectionTarget.constructor).some((ctor) => Reflect.hasOwnMetadata(key, ctor));
}
// Hook decorator
const hook = (key, ...fns) => (target, propertyKey) => {
const hooks = Reflect.hasOwnMetadata(key, target.constructor)
? Reflect.getOwnMetadata(key, target.constructor)
: new Map();
hooks.set(propertyKey, (hooks.get(propertyKey) ?? []).concat(fns));
Reflect.defineMetadata(key, hooks, target.constructor);
};
exports.hook = hook;