@sigi/di
Version:
Dependencies injection library for sigi framework
156 lines • 6.16 kB
JavaScript
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