container-ioc
Version:
Dependency Injection and Inversion of Control (IoC) container
160 lines (159 loc) • 5.78 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const interfaces_1 = require("./interfaces");
const registry_data_1 = require("./registry-data");
const exceptions_1 = require("./exceptions");
const keys_1 = require("./metadata/keys");
const index_1 = require("./metadata/index");
const MetadataAnnotator = index_1.AnnotatorProvider.get();
class Container {
constructor(options) {
this.registry = new Map();
this.defaultLifeTime = Container.DEFAULT_LIFE_TIME;
if (options) {
this.parent = options.parent;
this.defaultLifeTime = options.defaultLifeTime || this.defaultLifeTime;
}
this.register({ token: Container, useValue: this });
}
register(provider) {
const normalizedProvider = this.normalizeProvider(provider);
this.registerAll(normalizedProvider);
}
resolve(token) {
return this.resolveInternal(token, this);
}
createScope() {
return new Container({ parent: this });
}
createChild() {
return this.createScope();
}
setParent(parent) {
this.parent = parent;
}
resolveInternal(token, container, traceMessage) {
traceMessage = this.buildTraceMessage(token, traceMessage);
const registryData = this.registry.get(token);
if (!registryData) {
if (!this.parent) {
throw new exceptions_1.NoProviderError(this.getTokenString(token), traceMessage);
}
return this.parent.resolveInternal(token, container, traceMessage);
}
if (registryData.instance) {
return registryData.instance;
}
const instance = this.instantiateWithFactory(registryData.factory, container, traceMessage);
if (registryData.lifeTime === interfaces_1.LifeTime.Persistent) {
registryData.instance = instance;
this.registry.set(token, registryData);
}
return instance;
}
registerAll(providers) {
providers.forEach((p) => this.registerOne(p));
}
registerOne(provider) {
const registryData = new registry_data_1.RegistryData();
if (provider.useValue) {
registryData.instance = provider.useValue;
}
else {
const factoryValue = provider.useFactory || provider.useClass;
const isClass = !!provider.useClass;
registryData.factory = {
value: factoryValue,
isClass
};
if (isClass) {
registryData.factory.inject = this.retrieveInjectionsFromClass(registryData.factory.value);
}
else {
registryData.factory.inject = this.convertTokensToInjectionMd(provider.inject);
}
registryData.lifeTime = provider.lifeTime || this.defaultLifeTime;
}
this.registry.set(provider.token, registryData);
}
convertTokensToInjectionMd(tokens) {
let injections = [];
if (tokens) {
injections = tokens.map((token, index) => {
return {
token,
parameterIndex: index
};
});
}
return injections;
}
instantiateWithFactory(factory, container, traceMessage) {
if (factory.isClass) {
const injectable = this.isInjectable(factory.value);
if (!injectable) {
throw new exceptions_1.ClassNotInjectableError(factory.value.name);
}
}
const injections = factory.inject;
const resolvedInjections = injections.map(injection => container.resolveInternal(injection.token, container, traceMessage));
const args = [];
injections.forEach((injection, index) => {
args[injection.parameterIndex] = resolvedInjections[index];
});
if (factory.isClass) {
return new factory.value(...args);
}
else {
return factory.value(...args);
}
}
normalizeProvider(provider) {
return Array.isArray(provider)
? provider.map(p => this.normalizeSingleProvider(p))
: [this.normalizeSingleProvider(provider)];
}
normalizeSingleProvider(provider) {
if (typeof provider === 'function') {
provider = { token: provider, useClass: provider };
}
else if (!(provider instanceof Object)) {
throw new exceptions_1.InvalidProviderProvidedError(provider);
}
return provider;
}
buildTraceMessage(token, message) {
const tokenStr = this.getTokenString(token);
return message ? `${message} --> ${tokenStr}` : `Trace: ${tokenStr}`;
}
getTokenString(token) {
if (typeof token === 'function') {
return token.name;
}
else if (typeof token === 'symbol') {
return this.symbol2string(token);
}
else {
return `${token}`;
}
}
symbol2string(symbol) {
const regExp = /\(([^)]+)\)/;
const matches = regExp.exec(symbol.toString());
if (matches) {
return matches[1];
}
else {
throw new Error(`Symbol name couldn't be retrieved, please check if it's not empty`);
}
}
isInjectable(cls) {
return !!(MetadataAnnotator.getMetadata(keys_1.INJECTABLE_MD_KEY, cls));
}
retrieveInjectionsFromClass(cls) {
const injections = MetadataAnnotator.getMetadata(keys_1.INJECTIONS_MD_KEY, cls) || [];
return this.convertTokensToInjectionMd(injections);
}
}
Container.DEFAULT_LIFE_TIME = interfaces_1.LifeTime.Persistent;
exports.Container = Container;