UNPKG

@sigi/di

Version:

Dependencies injection library for sigi framework

156 lines 6.16 kB
import { ReflectiveProvider } from './injector-provider'; import { InjectionProvider } from './provider'; export class Injector { constructor(parent = null) { this.parent = parent; this.provider = new InjectionProvider(); this.resolvedProviders = new Map(); this.providersCache = new Map(); } addProvider(provider) { return this.provider.addProvider(provider); } addProviders(providers) { for (const provider of providers) { this.provider.addProvider(provider); } return this; } getInstance(provider) { return this.getInstanceInternal(provider, true); } resolveAndInstantiate(provider) { return this.getInstanceInternal(provider, false); } createChild(providers) { const childInjector = new Injector(this); childInjector.addProviders(providers); return childInjector; } resolveReflectiveProvider(provider) { let reflectiveProvider = null; if (this.provider.findProviderByToken(provider.provide ?? provider)) { if (this.providersCache.has(provider)) { return this.providersCache.get(provider); } reflectiveProvider = new ReflectiveProvider(provider); this.providersCache.set(provider, reflectiveProvider); } return reflectiveProvider; } getInstanceInternal(provider, useCache) { let injector = this; let instance = null; let reflectiveProvider = null; const deps = this.findDeps(provider); while (injector) { reflectiveProvider = injector.resolveReflectiveProvider(provider); if (!reflectiveProvider) { injector = injector.parent; continue; } if (injector.resolvedProviders.has(reflectiveProvider)) { if (deps) { if (useCache && (injector === this || this.checkDependenciesClean(injector, deps))) { instance = injector.resolvedProviders.get(reflectiveProvider); } else { instance = this.resolveByReflectiveProvider(reflectiveProvider, useCache, this); if (useCache) { this.provider.addProvider(provider); this.providersCache.set(provider, reflectiveProvider); this.resolvedProviders.set(reflectiveProvider, instance); } } } else { instance = useCache ? injector.resolvedProviders.get(reflectiveProvider) : this.resolveByReflectiveProvider(reflectiveProvider, false, this); } break; } instance = injector.resolveByReflectiveProvider(reflectiveProvider, useCache, this); if (instance) { if (useCache) { this.provider.addProvider(provider); this.providersCache.set(provider, reflectiveProvider); this.resolvedProviders.set(reflectiveProvider, instance); } break; } injector = injector.parent; } if (!instance) { reflectiveProvider = new ReflectiveProvider(provider); throw new TypeError(`No provider for ${reflectiveProvider.name}!`); } return instance; } resolveByReflectiveProvider(reflectiveProvider, useCache = true, leaf = this) { let instance = null; const { provider, name } = reflectiveProvider; if (typeof provider === 'function') { const deps = Reflect.getMetadata('design:paramtypes', provider) ?? []; const depsInstance = deps.map((dep) => leaf.getInstanceInternal(leaf.findExisting(dep), useCache)); instance = new provider(...depsInstance); } else if ('useValue' in provider) { instance = provider.useValue; } else if ('useClass' in provider) { instance = leaf.getInstanceInternal(provider.useClass, useCache); } else if ('useFactory' in provider) { let deps = []; if (provider.deps) { deps = provider.deps.map((dep) => leaf.getInstanceInternal(leaf.findExisting(dep), useCache)); } instance = provider.useFactory(...deps); } else if ('useExisting' in provider) { instance = leaf.getInstanceInternal(this.findExisting(provider.useExisting), useCache); } if (!instance) { throw new TypeError(`Can not resolve ${name}, it's not a valid provider`); } return instance; } findExisting(token) { let provider = null; let injector = this; while (injector) { provider = injector.provider.findProviderByToken(token); if (provider) { break; } injector = injector.parent; } if (!provider) { throw new TypeError(`No provider for ${token.name ?? token.toString()}`); } return provider; } findDeps(provider) { return typeof provider === 'function' ? Reflect.getMetadata('design:paramtypes', provider) : provider.useClass ? Reflect.getMetadata('design:paramtypes', provider.useClass) : provider.deps ? provider.deps : null; } checkDependenciesClean(leaf, deps) { return deps.every((dep) => { const depInLeaf = leaf.findExisting(dep); const depInRoot = this.findExisting(dep); const isEqual = depInLeaf === depInRoot; const deps = this.findDeps(depInLeaf); if (deps) { return this.checkDependenciesClean(leaf, deps) && isEqual; } return isEqual; }); } } //# sourceMappingURL=injector.js.map