UNPKG

@lakutata/core

Version:

Lakutata Framework Core

269 lines 11.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Container = void 0; const inversify_1 = require("inversify"); const MetadataReader_1 = require("./MetadataReader"); const events_1 = __importDefault(require("events")); class Container { constructor() { this.eventEmitter = new events_1.default(); this.rootIoC = new inversify_1.Container(); this.pluginIoC = new inversify_1.Container({ defaultScope: 'Transient' }); this.objectIoC = new inversify_1.Container({ defaultScope: 'Transient' }); this.componentIoC = new inversify_1.Container({ defaultScope: 'Singleton' }); this.moduleIoC = new inversify_1.Container({ defaultScope: 'Singleton' }); this.metadataReader = new MetadataReader_1.MetadataReader(); this.moduleMap = new Map(); this.moduleBindingMap = new Map(); this.componentMap = new Map(); this.componentBindingMap = new Map(); this.isInitializedSymbol = Symbol('isInitialized'); this.pluginIoC.parent = this.rootIoC; this.componentIoC.parent = this.pluginIoC; this.objectIoC.parent = this.componentIoC; this.moduleIoC.parent = this.componentIoC; } setParent(parentContainer) { this.rootIoC.parent = parentContainer.moduleIoC; } bindAsync(ioc, map, bindingMap, constructor, identifier, initializerFunction) { bindingMap.set(identifier, constructor); try { ioc.bind(identifier).to(constructor).inSingletonScope(); } catch (e) { } const asyncContainerModule = new inversify_1.AsyncContainerModule(async (bind, unbind) => { const moduleInstance = ioc.get(identifier); await initializerFunction.call(moduleInstance); try { unbind(identifier); bind(identifier).toConstantValue(moduleInstance); } catch (e) { bind(identifier).toConstantValue(moduleInstance); } }); const asyncContainerModules = map.get(constructor); if (asyncContainerModules) { asyncContainerModules.push(asyncContainerModule); map.set(constructor, asyncContainerModules); } else { map.set(constructor, [asyncContainerModule]); } } bindPlugin(pluginConstructor, identifier, config) { const scope = config['scope'] ? config['scope'] : 'Transient'; const pluginConfiguration = config ? config : {}; if (this.pluginIoC.isBound(identifier)) return; const binding = this.pluginIoC.bind(identifier).to(pluginConstructor); switch (scope) { case 'Transient': { binding.inTransientScope(); } break; case 'Singleton': { binding.inSingletonScope(); } break; default: { binding.inTransientScope(); } } binding.onActivation((context, injectable) => { const onActivationFunc = function () { const pluginConfigurationKeys = Object.keys(pluginConfiguration); pluginConfigurationKeys.forEach(key => { const metadataProperties = this.getConfigurablePropertyNames(); if (metadataProperties.includes(key)) { this.setProperty(key, pluginConfiguration[key], 'partial'); } }); this.onActivation(); }; onActivationFunc.call(injectable); return injectable; }); } bindObject(objectConstructor, config) { const objectConfiguration = config ? config : {}; if (this.objectIoC.isBound(objectConstructor)) { this.objectIoC.unbind(objectConstructor); } this.objectIoC.bind(objectConstructor).toSelf().onActivation((context, injectable) => { const onActivationFunc = function () { const objectConfigurationKeys = Object.keys(objectConfiguration); objectConfigurationKeys.forEach(key => { const metadataProperties = this.getConfigurablePropertyNames(); if (metadataProperties.includes(key)) { this.setProperty(key, objectConfiguration[key], 'partial'); } }); }; onActivationFunc.call(injectable); return injectable; }); } bindModule(moduleConstructor, config, identifier, callback) { const _identifier = identifier ? identifier : moduleConstructor; const moduleConfigurations = config ? config : {}; const container = this; this.bindAsync(this.moduleIoC, this.moduleMap, this.moduleBindingMap, moduleConstructor, _identifier, async function () { if (!this[container.isInitializedSymbol]) { this[container.isInitializedSymbol] = true; } else { return; } const startLoadModuleAt = Date.now(); this.container.setParent(container); const moduleConfigurationKeys = Object.keys(moduleConfigurations); moduleConfigurationKeys.forEach(key => { const metadataProperties = this.getConfigurablePropertyNames(); if (metadataProperties.includes(key)) { this.setProperty(key, moduleConfigurations[key], 'partial'); } }); this.setProperty('id', _identifier, 'overwrite'); this.bindPlugins(); await Promise.all([new Promise((resolve, reject) => { this.bindThreads().then(resolve).catch(reject); }), new Promise((resolve, reject) => { this.bindProcesses().then(resolve).catch(reject); })]); this.bindComponents(); this.bindModules(); await this.container.initialize({ componentsLoaded: async () => { await this.onComponentsLoaded(this); }, modulesLoaded: async () => { await this.onModulesLoaded(this); } }); this.container.on('moduleLoaded', (moduleId, moduleConstructor, loadTime, subModuleId) => { container.eventEmitter.emit('moduleLoaded', moduleId, moduleConstructor, loadTime, subModuleId ? `${this.id}/${subModuleId}` : this.id); }).on('componentLoaded', (componentId, componentConstructor, loadTime, subModuleId) => { container.eventEmitter.emit('componentLoaded', componentId, componentConstructor, loadTime, subModuleId ? `${this.id}/${subModuleId}` : this.id); }); await this.initialize(); this.emit('ready', this); const endLoadModuleAt = Date.now(); container.eventEmitter.emit('moduleLoaded', typeof _identifier === 'function' ? _identifier.name : _identifier, moduleConstructor, endLoadModuleAt - startLoadModuleAt); if (callback) callback(); }); } bindComponent(componentConstructor, identifier, config) { const componentConfigurations = config ? config : {}; const container = this; this.bindAsync(this.componentIoC, this.componentMap, this.componentBindingMap, componentConstructor, identifier, async function () { if (!this[container.isInitializedSymbol]) { this[container.isInitializedSymbol] = true; } else { return; } const startLoadComponentAt = Date.now(); const componentConfigurationKeys = Object.keys(componentConfigurations); componentConfigurationKeys.forEach(key => { const metadataProperties = this.getConfigurablePropertyNames(); if (metadataProperties.includes(key)) { this.setProperty(key, componentConfigurations[key], 'partial'); } }); await this.initialize(); const endLoadComponentAt = Date.now(); container.eventEmitter.emit('componentLoaded', identifier, componentConstructor, endLoadComponentAt - startLoadComponentAt); }); } getPlugin(identifier) { return this.pluginIoC.get(identifier); } getObject(objectConstructor) { return this.objectIoC.get(objectConstructor); } hasObject(objectConstructor) { return this.objectIoC.isBound(objectConstructor); } unbindObject(objectConstructor) { if (this.objectIoC.isBound(objectConstructor)) { this.objectIoC.unbind(objectConstructor); } } getModule(identifier) { return this.moduleIoC.get(identifier); } getComponent(identifier) { return this.componentIoC.get(identifier); } async loadAsync(ioc, map, bindingMap) { const m = []; const constructors = []; map.forEach((asyncContainerModule, constructor) => { const propertiesMetadata = this.metadataReader.getPropertiesMetadata(constructor); constructors.push(constructor); if (Array.isArray(propertiesMetadata)) { return; } for (const propertiesMetadataKey in propertiesMetadata) { if (propertiesMetadata.hasOwnProperty(propertiesMetadataKey)) { const metadataArray = propertiesMetadata[propertiesMetadataKey]; for (const metadata of metadataArray) { const dependencyConstructor = bindingMap.get(metadata.value); const targetIndex = constructors.indexOf(constructor); if (dependencyConstructor) { if (targetIndex > 0) { constructors.splice(targetIndex - 1, 0, dependencyConstructor); } else { constructors.unshift(dependencyConstructor); } } } } } }); const uniqueConstructors = Array.from(new Set(constructors)); for (const constructor of uniqueConstructors) { if (map.get(constructor)) { const asyncContainerModules = map.get(constructor); if (asyncContainerModules) { asyncContainerModules.forEach(asyncContainerModule => { m.push(asyncContainerModule); }); } } } await ioc.loadAsync.apply(ioc, m); } async loadModules() { await this.loadAsync(this.moduleIoC, this.moduleMap, this.moduleBindingMap); } async loadComponents() { await this.loadAsync(this.componentIoC, this.componentMap, this.componentBindingMap); } async initialize(callbacks) { await this.loadComponents(); if (callbacks?.componentsLoaded) { await callbacks.componentsLoaded(); } await this.loadModules(); if (callbacks?.modulesLoaded) { await callbacks.modulesLoaded(); } } on(a, b) { this.eventEmitter.on(a, b); return this; } } exports.Container = Container; //# sourceMappingURL=Container.js.map