UNPKG

@clawject/di

Version:

<p align="center"> <a href="https://clawject.com/" target="_blank"><img src="https://clawject.com/img/logo.svg" align="center" alt="Clawject Logo" width="120" height="120" /></a> </p>

201 lines (200 loc) 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ApplicationBeanFactory = void 0; const ApplicationBean_1 = require("./ApplicationBean"); const BeanIdProvider_1 = require("./BeanIdProvider"); const ObjectFactoryImpl_1 = require("../ObjectFactoryImpl"); const MetadataStorage_1 = require("../MetadataStorage"); const ApplicationBeanDependency_1 = require("./ApplicationBeanDependency"); const ApplicationBeanFinder_1 = require("./ApplicationBeanFinder"); const Utils_1 = require("../Utils"); const RuntimeErrors_1 = require("../api/RuntimeErrors"); class ApplicationBeanFactory { constructor(applicationConfigurationFactory, beanProcessors, scopeRegister) { this.applicationConfigurationFactory = applicationConfigurationFactory; this.beanProcessors = beanProcessors; this.scopeRegister = scopeRegister; this.exposedBeanNameToApplicationBeanDependency = new Map(); this.applicationBeans = []; this.configurationIdToBeanClassPropertyToApplicationBean = new Map(); this.scopeToScopedApplicationBeans = new Map(); this.applicationBeanFinder = new ApplicationBeanFinder_1.ApplicationBeanFinder(this.configurationIdToBeanClassPropertyToApplicationBean, applicationConfigurationFactory); } async init(applicationMetadata) { await this.createApplicationBeans(applicationMetadata); this.fillExposedBeans(applicationMetadata); await this.initBeans(); } async postInit() { const singletonScope = this.scopeRegister.getScope('singleton'); await this.initScopedBeans(singletonScope); const lifecycleInit = this.applicationBeans.map(async (applicationBean) => { if (applicationBean.isLifecycleFunction) { if (applicationBean.parentConfiguration.metadata.lifecycle["POST_CONSTRUCT" /* LifecycleKind.POST_CONSTRUCT */].includes(applicationBean.beanClassProperty)) { await applicationBean.getInjectionValue(); } } }); await Promise.all(lifecycleInit); } async destroy() { await Promise.all(this.applicationBeans.map(async (applicationBean) => { if (applicationBean.isLifecycleFunction) { if (applicationBean.parentConfiguration.metadata.lifecycle["PRE_DESTROY" /* LifecycleKind.PRE_DESTROY */].includes(applicationBean.beanClassProperty)) { await applicationBean.getInjectionValue(); } } })); await Promise.all(this.applicationBeans.map(async (bean) => { if (bean.isLifecycleFunction) { return; } const beanScope = bean.getScope(); const removedScopedObject = beanScope.remove(bean.name); if (removedScopedObject === null) { return; } //TODO: check warning await this.onLifecycle(await removedScopedObject, "PRE_DESTROY" /* LifecycleKind.PRE_DESTROY */); })); } async getExposedBean(beanName) { const exposedBeans = Utils_1.Utils.getValueSafe(this.exposedBeanNameToApplicationBeanDependency, beanName); if (exposedBeans === Utils_1.Utils.EMPTY_VALUE) { throw new RuntimeErrors_1.RuntimeErrors.ExposedBeanNotFoundError(`No exposed bean found by exposed name: ${beanName}`); } const value = await exposedBeans.getValue(); return value.value; } async getExposedBeans() { const data = Promise.all(Array.from(this.exposedBeanNameToApplicationBeanDependency.entries()) .map(async ([beanName, exposedBeans]) => [beanName, (await exposedBeans.getValue()).value])); return Object.fromEntries(await data); } async createApplicationBeans(applicationMetadata) { const resultPromise = this.applicationConfigurationFactory.mapConfigurations(async (applicationConfiguration) => { const beanDependenciesMetadataByConfiguration = applicationMetadata.beanDependenciesMetadata[applicationConfiguration.index]; if (!beanDependenciesMetadataByConfiguration) { throw new RuntimeErrors_1.RuntimeErrors.IllegalUsageError('No bean dependencies metadata found'); } const beanClassPropertyToApplicationBean = new Map(); this.configurationIdToBeanClassPropertyToApplicationBean.set(applicationConfiguration.id, beanClassPropertyToApplicationBean); const beanNameToDependenciesMetadata = new Map(Object.values(beanDependenciesMetadataByConfiguration.map(it => { const applicationBeanDependencies = it.dependencies .map(it => new ApplicationBeanDependency_1.ApplicationBeanDependency(it, this.applicationBeanFinder)); return [it.classPropertyName, applicationBeanDependencies]; }))); const initPromises = Object.entries(applicationConfiguration.metadata.beans).map(async ([beanClassProperty, beanMetadata]) => { const beanDependenciesMetadata = beanNameToDependenciesMetadata.get(beanClassProperty) ?? null; let beanClassConstructor = null; if (beanMetadata.kind === 2 /* BeanKind.CLASS_CONSTRUCTOR */) { const classProperty = applicationConfiguration.instance[beanClassProperty]; if (Utils_1.Utils.isPromise(classProperty)) { const resolvedClassProperty = await classProperty; beanClassConstructor = resolvedClassProperty.constructor; } else { beanClassConstructor = classProperty.constructor; } } const applicationBean = new ApplicationBean_1.ApplicationBean(BeanIdProvider_1.BeanIdProvider.getAndInc(), applicationConfiguration, beanClassProperty, beanMetadata, beanDependenciesMetadata, beanClassConstructor, this.scopeRegister); beanClassPropertyToApplicationBean.set(beanClassProperty, applicationBean); this.applicationBeans.push(applicationBean); if (!applicationBean.isLifecycleFunction) { const scope = applicationBean.getScope(); const scopedBeans = this.scopeToScopedApplicationBeans.get(scope) ?? []; if (!this.scopeToScopedApplicationBeans.has(scope)) { this.scopeToScopedApplicationBeans.set(scope, scopedBeans); } scopedBeans.push(applicationBean); } }); await Promise.all(initPromises); }); await Promise.all(resultPromise); } async initScopedBeans(scope) { const scopedBeans = this.scopeToScopedApplicationBeans.get(scope) ?? []; await Promise.all(scopedBeans.map(async (it) => { if (it.isLifecycleFunction || it.lazy) { return; } return it.getScopedBeanValue(); })); } fillExposedBeans(applicationMetadata) { applicationMetadata.exposedBeansMetadata.forEach((exposedBeanMetadata) => { this.exposedBeanNameToApplicationBeanDependency.set(exposedBeanMetadata.qualifiedName, new ApplicationBeanDependency_1.ApplicationBeanDependency(exposedBeanMetadata.metadata, this.applicationBeanFinder)); }); } async initBeans() { await Promise.all(this.applicationBeans.map(async (applicationBean) => { const rawFactory = await this.getBeanFactoryFunction(applicationBean); let processedFactory = rawFactory; if (!applicationBean.isLifecycleFunction) { processedFactory = this.beanProcessors.reduce((acc, processor) => { if (processor.processFactory) { return processor.processFactory({ beanMetadata: applicationBean.toMetadata(), factory: acc, }); } return acc; }, rawFactory); } const objectFactory = new ObjectFactoryImpl_1.ObjectFactoryImpl(() => { return this.createBeanInstance(applicationBean, processedFactory); }); applicationBean.init(objectFactory); })); this.beanProcessors.forEach(processor => processor.onBeansInitialized?.()); } async createBeanInstance(applicationBean, factory) { const dependencyInjectionPromises = applicationBean.dependencies?.map(it => { return it.getValue(); }); const dependencyInjectionValues = await Promise.all(dependencyInjectionPromises ?? []); const result = await factory(...dependencyInjectionValues.map(it => it.value)); this.registerDestructionCallbackIfNeeded(applicationBean, result); await this.onLifecycle(result, "POST_CONSTRUCT" /* LifecycleKind.POST_CONSTRUCT */); return result; } getBeanFactoryFunction(applicationBean) { const configurationInstance = applicationBean.parentConfiguration.instance; switch (applicationBean.beanMetadata.kind) { case 1 /* BeanKind.FACTORY_METHOD */: case 5 /* BeanKind.LIFECYCLE_METHOD */: return configurationInstance[applicationBean.beanClassProperty].bind(configurationInstance); case 2 /* BeanKind.CLASS_CONSTRUCTOR */: { const classPropertyValue = configurationInstance[applicationBean.beanClassProperty]; if (Utils_1.Utils.isPromise(classPropertyValue)) { return classPropertyValue.then(it => it.factory); } return classPropertyValue.factory; } case 3 /* BeanKind.FACTORY_ARROW_FUNCTION */: case 6 /* BeanKind.LIFECYCLE_ARROW_FUNCTION */: return configurationInstance[applicationBean.beanClassProperty]; case 4 /* BeanKind.VALUE_EXPRESSION */: return () => configurationInstance[applicationBean.beanClassProperty]; } } registerDestructionCallbackIfNeeded(applicationBean, instance) { if (applicationBean.isLifecycleFunction) { return; } const componentMetadata = MetadataStorage_1.MetadataStorage.getComponentMetadataByClassInstance(instance); const hasLifecyclePreDestroy = componentMetadata !== null && componentMetadata.lifecycle.PRE_DESTROY.length > 0; if (hasLifecyclePreDestroy) { applicationBean.getScope().registerDestructionCallback(applicationBean.name, async () => { await this.onLifecycle(instance, "PRE_DESTROY" /* LifecycleKind.PRE_DESTROY */); }); } } async onLifecycle(beanInstance, lifecycleKind) { await Promise.all(MetadataStorage_1.MetadataStorage.getComponentMetadataByClassInstance(beanInstance)?.lifecycle[lifecycleKind].map(async (methodName) => { await beanInstance[methodName].call(beanInstance); }) ?? []); } } exports.ApplicationBeanFactory = ApplicationBeanFactory;