@lakutata/core
Version:
Lakutata Framework Core
269 lines • 11.8 kB
JavaScript
"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