init
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { Injector } from "./Injector";
|
||||
import { Type, GenericClassDecorator, Constructor } from "./Types";
|
||||
|
||||
/**
|
||||
* @returns {GenericClassDecorator<Type<any>>}
|
||||
* @constructor
|
||||
*/
|
||||
export function Singleton(_interface?: Constructor<any>): GenericClassDecorator<Type<any>> {
|
||||
return (clazz: Type<any>) => {
|
||||
Injector['modules'].push({
|
||||
implements: _interface ?? clazz,
|
||||
ctor: clazz
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function Inject(clazz: Constructor<any>) {
|
||||
return function (prototype: Object, key: string) {
|
||||
Injector['injectionQueue'].push({ injectionType: clazz, prototype: prototype, injectIntoKey: key })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import 'reflect-metadata';
|
||||
import { Constructor, Type } from './Types';
|
||||
|
||||
class _Injector {
|
||||
|
||||
private injectionQueue: any[] = []
|
||||
private modules: { implements?: Constructor<any>, ctor: Type<any> }[] = []
|
||||
private moduleObjs: { [key in string]: any } = {}
|
||||
private initialized = false
|
||||
|
||||
/**
|
||||
* Resolves instances by injecting required services
|
||||
* @param {Type<any>} target
|
||||
* @returns {T}
|
||||
*/
|
||||
public resolve<T>(target: Type<T>): T {
|
||||
if (!this.initialized) {
|
||||
this.initialize()
|
||||
this.initialized = true
|
||||
}
|
||||
|
||||
return this.moduleObjs[target.name] as any
|
||||
}
|
||||
|
||||
public async resolveAsync<T>(target: Type<T>): Promise<T> {
|
||||
if (!this.initialized) {
|
||||
await this.initialize()
|
||||
this.initialized = true
|
||||
}
|
||||
|
||||
return this.moduleObjs[target.name] as any
|
||||
}
|
||||
|
||||
private initialize = async (async?: boolean) => {
|
||||
this.createSingletons()
|
||||
this.injectDependencies()
|
||||
if (async)
|
||||
await this.initializeSingletons()
|
||||
else
|
||||
this.initializeSingletons()
|
||||
}
|
||||
|
||||
private createSingletons = () => {
|
||||
//instantiate all non-root modules
|
||||
this.modules.forEach(m => {
|
||||
const module = new m.ctor()
|
||||
if (m.implements)
|
||||
this.moduleObjs[m.implements.name] = module
|
||||
this.moduleObjs[m.ctor.name] = module
|
||||
})
|
||||
}
|
||||
|
||||
private injectDependencies = () => {
|
||||
while (this.injectionQueue.length > 0) {
|
||||
const inj = this.injectionQueue.shift()
|
||||
|
||||
if (this.moduleObjs[inj.injectionType.name]) {
|
||||
inj.prototype[inj.injectIntoKey] = this.moduleObjs[inj.injectionType.name]
|
||||
} else {
|
||||
throw new Error("Cannot resolve injection token " + inj.injectionType.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private initializeSingletons = () => {
|
||||
Object.values(this.moduleObjs).forEach(element => element.initialize ? element.initialize() : undefined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Injector stores services and resolves requested instances.
|
||||
*/
|
||||
export const Injector = new _Injector()
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Type for what object is instances of. Also applicable to "Constructor of T" as Types/Classes/Constructors are interchangable in TS.
|
||||
*/
|
||||
export interface Type<T> {
|
||||
new(...args: any[]): T;
|
||||
}
|
||||
|
||||
export type Constructor<T> = Function & { prototype: T }
|
||||
|
||||
/**
|
||||
* Generic `ClassDecorator` type
|
||||
*/
|
||||
export type GenericClassDecorator<T> = (target: T) => void;
|
||||
|
||||
export interface ISingleton{
|
||||
initialize?(): void | Promise<void>
|
||||
}
|
||||
Reference in New Issue
Block a user