UNPKG

ts-bakery

Version:

Baked dependency injection for Typescript.

178 lines 7.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const DependencyConfigurationProvider_1 = require("../Configuration/DependencyConfigurationProvider"); const Paths_1 = require("../Util/Paths"); const DecoratorRegistry_1 = require("./DecoratorRegistry"); const ManualInjector_1 = require("./ManualInjector"); const PostResolvable_1 = require("./PostResolvable"); class DependencyResolver { constructor() { this.modules = []; this.moduleNames = new Set(); this.instances = new Map(); this.activeInstantiations = new Set(); this.registrations = new Map(); this.configurationProvider = new DependencyConfigurationProvider_1.default(); this.postResolvables = []; ManualInjector_1.default.setResolver(this); } build() { this.validatePaths(); this.registerModules(); this.buildRegistrations(); this.instantiateFromRegistrations(); this.callPostResolvables(); } manualInject(instance) { const constructor = instance['constructor']; const registration = this.registrations.get(constructor.name); if (registration == null) { throw new Error(`Unable to find dependency registration for type ${constructor.name}.`); } const injectMethod = DecoratorRegistry_1.default.getInjectMethod(registration.constructorReference); if (injectMethod == null) { throw new Error(`${constructor.name} is not marked for method injection.`); } const params = this.resolveParams(registration); injectMethod.call(instance, ...params); const postInjectMethod = DecoratorRegistry_1.default.getPostInjectMethod(registration.constructorReference); if (postInjectMethod != null) { postInjectMethod.call(instance); } } registerModule(constructor) { if (this.moduleNames.has(constructor.name)) { throw new Error(`Module with name ${constructor.name} has already been registered.`); } this.moduleNames.add(constructor.name); const module = new constructor(); this.modules.push(module); } validatePaths() { Paths_1.default.validate(); } buildRegistrations() { const dependencyMapping = this.configurationProvider.getConfiguration(); const bindings = dependencyMapping.bindings; let configIndex = 0; for (const module of this.modules) { const regs = module.buildRegistrations(); for (let config of regs) { if (this.registrations.has(config.name)) { throw new Error(`Cannot set configuration for type of name ${config.name} twice.`); } config.setBinding(bindings[configIndex]); this.registrations.set(config.name, config); configIndex++; } } } instantiateFromRegistrations() { const configs = this.registrations.values(); for (const config of configs) { this.resolveRegistration(config); } } resolveRegistration(registration) { if (registration.isLazy) { return; } const instance = this.getOrCreateInstance(registration); const postResolve = DecoratorRegistry_1.default.getPostResolveMethod(registration.constructorReference); if (postResolve != null) { this.postResolvables.push(new PostResolvable_1.default(instance, postResolve)); } } getInstance(requested, requester) { if (!requested.canBeRequestedBy(requester)) { throw new Error(`Trying to inject ${requested.name} into ${requester.name} but access has been restricted.`); } return this.getOrCreateInstance(requested); } getOrCreateInstance(registration) { if (registration.isSingle && this.instances.has(registration)) { return this.instances.get(registration)[0]; } return this.createInstance(registration); } createInstance(registration) { if (registration.isSingle && this.instances.has(registration)) { return this.instances.get(registration)[0]; } this.activeInstantiations.add(registration.name); const params = this.resolveParams(registration); const instance = this.createInstanceWithParams(registration, params); this.activeInstantiations.delete(registration.name); if (!this.instances.has(registration)) { this.instances.set(registration, []); } this.instances.get(registration).push(instance); const postInject = DecoratorRegistry_1.default.getPostInjectMethod(registration.constructorReference); if (postInject != null) { postInject.call(instance); } return instance; } createInstanceWithParams(registration, params) { const injectMethod = DecoratorRegistry_1.default.getInjectMethod(registration.constructorReference); if (injectMethod == null) { return new registration.constructorReference(...params); } else { const instance = new registration.constructorReference(); injectMethod.call(instance, ...params); return instance; } } resolveParams(registration) { const dependencyConfiguration = this.configurationProvider.getConfiguration(); const dependencyMapping = dependencyConfiguration.mappings.find(i => i.implementation == registration.name); const params = []; if (dependencyMapping == null) { return params; } for (const dependencyName of dependencyMapping.dependencies) { const dependencyConfig = this.resolveDependencyName(dependencyName, registration); if (this.activeInstantiations.has(dependencyConfig.name)) { throw new Error(`Cyclic dependency detected while instantiating ${dependencyConfig.name} at ${registration.name}.`); } const dependencyInstance = this.getInstance(dependencyConfig, registration); params.push(dependencyInstance); } return params; } resolveDependencyName(dependencyName, requester) { const matchingRegistrations = this.getMatchingRegistrations(dependencyName); matchingRegistrations.sort((a, b) => { const ari = a.restrictions.includes(requester.name); const bri = b.restrictions.includes(requester.name); if (ari != bri) { return Number(ari) - Number(bri); } return a.restrictions.length - b.restrictions.length; }); for (const registration of matchingRegistrations) { if (registration.canBeRequestedBy(requester)) { return registration; } } throw new Error('Unable to resolve dependency ' + dependencyName + ' for ' + requester.name); } getMatchingRegistrations(dependencyName) { const matching = []; const registrations = this.registrations.values(); for (const config of registrations) { if (config.matchesBinding(dependencyName)) { matching.push(config); } } return matching; } callPostResolvables() { for (const init of this.postResolvables) { init.initFunction.call(init.instance); } } } exports.default = DependencyResolver; //# sourceMappingURL=DependencyResolver.js.map