import 'reflect-metadata'; import { ERR_NO_INITIALIZE_WITH_PRIORITY, ERR_NO_INJECTION_TOKEN } from './Strings'; import { Constructor, Type, SingletonDefinition, InjectionError, InjectionResolutionError } from './Internals'; class _Injector { private injectionQueue: any[] = [] private singletonDefinitions: SingletonDefinition[] = [] private singletonObjects: { [classname in string]: any } = {} private tokenLookupTable: { [token in string]: Constructor } = {} private initialized = false /** * Resolves instances by injecting required services * @param {Type} request * @returns {T} */ public resolve(request: Constructor): T { if (!this.initialized) { this.initialize() this.initialized = true } return this.singletonObjects[request.name] as any } public async resolveAsync(target: Type): Promise { if (!this.initialized) { this.initialize() this.initialized = true } return this.singletonObjects[target.name] as any } private initialize = () => { this.createSingletons() this.injectDependencies() this.initializeSingletons() this.cleanup() } private createSingletons = () => { this.singletonDefinitions.forEach(def => { const obj = new def.ctor() if (def.initializationPriority != undefined && !obj.initialize) { throw new InjectionError(ERR_NO_INITIALIZE_WITH_PRIORITY(def.ctor)) } this.singletonObjects[def.ctor.name] = obj }) } private injectDependencies = () => { this.injectionQueue.forEach(inj => { if (inj.token.name in this.tokenLookupTable) { //injection alias was used inj.token = this.tokenLookupTable[inj.token.name] } if (this.singletonObjects[inj.token.name]) { this.singletonObjects[inj.receiver.constructor.name][inj.key] = this.singletonObjects[inj.token.name] } else { throw new InjectionResolutionError(ERR_NO_INJECTION_TOKEN(inj.injectionType)) } }) } private initializeSingletons = () => { this.singletonDefinitions .sort((a, b) => (b.initializationPriority ?? 0) - (a.initializationPriority ?? 0)) .map(def => this.singletonObjects[def.ctor.name]) .forEach(obj => obj.initialize ? obj.initialize() : undefined) } private cleanup = () => { while (this.singletonDefinitions.length > 0) this.singletonDefinitions.pop() while (this.injectionQueue.length > 0) this.injectionQueue.pop() } } /** * The Injector stores services and resolves requested instances. */ export const Injector = new _Injector()