@glandjs/core
Version:
Glands is a web framework for Node.js (@core)
122 lines (121 loc) • 4.97 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.LifecycleScanner = void 0;
class LifecycleScanner {
constructor(modulesContainer, logger) {
this.modulesContainer = modulesContainer;
this.moduleComponentMap = new Map();
this.logger = logger?.child('LifecycleScanner');
}
scanForHooks() {
this.logger?.debug('Scanning for lifecycle hooks');
for (const [moduleToken, module] of this.modulesContainer.entries()) {
this.scanModule(moduleToken, module);
}
this.logger?.debug(`Found components with hooks in ${this.moduleComponentMap.size} modules`);
}
scanModule(moduleToken, module) {
const components = new Set();
this.moduleComponentMap.set(moduleToken, components);
if (module.metatype) {
try {
const moduleInstance = this.getModuleInstance(module);
if (moduleInstance) {
components.add({
instance: moduleInstance,
moduleToken,
componentType: 'module',
componentToken: moduleToken,
});
this.logger?.debug(`Module ${moduleToken} registered for lifecycle hooks`);
}
}
catch (error) {
this.logger?.error(`Error resolving module instance for ${moduleToken}: ${error.message}`);
}
}
this.scanComponentsForHooks(moduleToken, module.controllers, 'controller', components);
this.scanComponentsForHooks(moduleToken, module.channels, 'channel', components);
}
scanComponentsForHooks(moduleToken, components, componentType, lifecycleComponents) {
for (const [token, wrapper] of components.entries()) {
try {
const instance = wrapper.getInstance();
const componentToken = typeof token === 'function' ? token.name : String(token);
const hasHooks = this.hasLifecycleHooks(instance);
if (hasHooks) {
lifecycleComponents.add({
instance,
moduleToken,
componentType,
componentToken,
});
this.logger?.debug(`${componentType} ${componentToken} registered for lifecycle hooks`);
}
}
catch (error) {
this.logger?.error(`Error resolving ${componentType} instance: ${error.message}`);
}
}
}
getModuleInstance(module) {
const token = module.metatype;
return new module.metatype();
}
hasLifecycleHooks(instance) {
return (this.hasHook(instance, 'onModuleInit') ||
this.hasHook(instance, 'onModuleDestroy') ||
this.hasHook(instance, 'onAppBootstrap') ||
this.hasHook(instance, 'onAppShutdown') ||
this.hasHook(instance, 'onChannelInit'));
}
hasHook(instance, hook) {
return instance && typeof instance[hook] === 'function';
}
async runHook(hook, signal) {
this.logger?.debug(`Running ${hook} hooks`);
const promises = [];
for (const [moduleToken, components] of this.moduleComponentMap.entries()) {
for (const component of components) {
if (this.hasHook(component.instance, hook)) {
this.logger?.debug(`Executing ${hook} on ${component.componentType} ${component.componentToken}`);
try {
let promise;
if (hook === 'onAppShutdown') {
promise = component.instance[hook](signal);
}
else {
promise = component.instance[hook]();
}
if (promise && typeof promise.then === 'function') {
promises.push(promise);
}
}
catch (error) {
this.logger?.error(`Error running ${hook} on ${component.componentType} ${component.componentToken}: ${error.message}`);
}
}
}
}
if (promises.length > 0) {
await Promise.all(promises);
}
this.logger?.debug(`Completed ${hook} hooks`);
}
async onModuleInit() {
await this.runHook('onModuleInit');
}
async onModuleDestroy() {
await this.runHook('onModuleDestroy');
}
async onAppBootstrap() {
await this.runHook('onAppBootstrap');
}
async onAppShutdown(signal) {
await this.runHook('onAppShutdown', signal);
}
async onChannelInit() {
await this.runHook('onChannelInit');
}
}
exports.LifecycleScanner = LifecycleScanner;