import 'reflect-metadata'; import { Constructor, Type } from './Types'; class _Injector { private injectionQueue: any[] = [] private modules: { implements?: Constructor, ctor: Type }[] = [] private moduleObjs: { [key in string]: any } = {} private initialized = false /** * Resolves instances by injecting required services * @param {Type} target * @returns {T} */ public resolve(target: Type): T { if (!this.initialized) { this.initialize() this.initialized = true } return this.moduleObjs[target.name] as any } public async resolveAsync(target: Type): Promise { if (!this.initialized) { await this.initialize() this.initialized = true } return this.moduleObjs[target.name] as any } private initialize = async (async?: boolean) => { this.createSingletons() this.injectDependencies() if (async) await this.initializeSingletons() else this.initializeSingletons() } private createSingletons = () => { //instantiate all non-root modules this.modules.forEach(m => { const module = new m.ctor() if (m.implements) this.moduleObjs[m.implements.name] = module this.moduleObjs[m.ctor.name] = module }) } private injectDependencies = () => { while (this.injectionQueue.length > 0) { const inj = this.injectionQueue.shift() if (this.moduleObjs[inj.injectionType.name]) { inj.prototype[inj.injectIntoKey] = this.moduleObjs[inj.injectionType.name] } else { throw new Error("Cannot resolve injection token " + inj.injectionType.name) } } } private initializeSingletons = () => { Object.values(this.moduleObjs).forEach(element => element.initialize ? element.initialize() : undefined); } } /** * The Injector stores services and resolves requested instances. */ export const Injector = new _Injector()