UNPKG

container-ioc

Version:

Dependency Injection and Inversion of Control (IoC) container

160 lines (159 loc) 5.78 kB
"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;