UNPKG

@miracledevs/paradigm-web-di

Version:

A minimal dependency injection library for the web

113 lines (90 loc) 4.39 kB
import "reflect-metadata"; import { ObjectType, getObjectTypeName } from "./object-type"; import { DependencyLifeTime } from "./dependency-life-time"; import { DependencyDescriptor } from "./dependency-descriptor"; import { DependencyCollection } from "./dependency-collection"; export class DependencyContainer { private _scopedInstances: Map<ObjectType, any>; private _collection: DependencyCollection; private _parent?: DependencyContainer; public readonly name?: string; private constructor(parent: DependencyContainer | undefined, collection: DependencyCollection, name: string | undefined) { this._scopedInstances = new Map<ObjectType, any>(); this._collection = collection; this._parent = parent; this.name = name; } public static createFromCollection(collection: DependencyCollection, name?: string) { if (!collection) throw new Error("Can not create a dependency container without a dependency collection."); return new DependencyContainer(undefined, collection, name); } createScopedInjector(name?: string): DependencyContainer { return new DependencyContainer(this, this._collection, name); } resolve<T = any>(objectType: ObjectType<T>): T { try { if (objectType as any === DependencyContainer) return this as any; // this will throw if the object type is not registered. const descriptor = this._collection.get(objectType); switch (descriptor.lifeTime) { ////////////////////////////////////////////////////////////// // Singletons ////////////////////////////////////////////////////////////// // if the descriptor tells is a singleton we try to create // the instance or we return the instance we have. case DependencyLifeTime.Singleton: if (!descriptor.instance) descriptor.instance = this.createInstance(objectType, descriptor); return descriptor.instance ////////////////////////////////////////////////////////////// // Scoped ////////////////////////////////////////////////////////////// // the scoped instances are scoped singletons, singletons that // live the scoped injector instead of the global scope case DependencyLifeTime.Scoped: if (!this._parent) throw new Error("Can not instantiate a scoped type in the global container."); const instance = this.getInstance(objectType); if (!instance) { this._scopedInstances.set(objectType, this.createInstance(objectType, descriptor)); return this._scopedInstances.get(objectType); } return instance; ////////////////////////////////////////////////////////////// // Transient ////////////////////////////////////////////////////////////// case DependencyLifeTime.Transient: return this.createInstance(objectType, descriptor); default: throw new Error(`Life time parameter not recognized as a valid life time.`); } } catch (e) { throw new Error(`Couldn't instantiate the type ${getObjectTypeName(objectType)}.\n - ${(e instanceof Error? e.message : e)}`); } } private createInstance<T>(objectType: ObjectType<T>, descriptor: DependencyDescriptor): T { const parameters = [null].concat(descriptor.dependencies.map(x => this.resolve(x))) as any; return new (Function.prototype.bind.apply(objectType, parameters)); } private getInstance<T>(objectType: ObjectType<T>): T | undefined { if (this._scopedInstances.has(objectType)) return this._scopedInstances.get(objectType); if (this._parent) return this._parent.getInstance(objectType); return undefined; } }