UNPKG

@glandjs/core

Version:

Glands is a web framework for Node.js (@core)

122 lines (121 loc) 4.97 kB
"use strict"; 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;