This commit is contained in:
nitowa
2022-07-25 04:06:21 +02:00
commit de0d8cfd8e
14 changed files with 1397 additions and 0 deletions
+21
View File
@@ -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 })
}
}
+73
View File
@@ -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()
+17
View File
@@ -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>
}