@mooncake/container
Version:
DI(dependency injection) container for JavaScript and TypeScript.
102 lines (91 loc) • 3.16 kB
text/typescript
import { getInjectMetas } from './decorators';
import { Constructor, ContainerResolver, Factory, ID, InjectError } from './types';
export abstract class Registion<T> {
protected singleton: boolean
protected scope?: string
protected instance?: T
constructor(singleton: boolean, scope?: string) {
this.singleton = singleton
this.scope = scope
}
/**
* get instance of registion
*
* @param {ContainerResolver} container
* @returns {T}
* @memberof Registion
*/
getInstance (container: ContainerResolver, scope?: string): T {
if (this.instance) {
return this.instance
}
const ins = this.createInstance(container)
if (this.singleton) {
this.instance = ins
}
return ins
}
abstract createInstance (container: ContainerResolver): T
}
export class ValueRegistion<T> extends Registion<T>{
constructor(ins: T, scope?: string) {
super(true, scope)
this.instance = ins
}
createInstance (container: ContainerResolver): T {
throw new Error('should not access here')
}
}
export class ClassRegistion<T> extends Registion<T>{
readonly clazz: Constructor<T> | Function
constructor(clazz: Constructor<T> | Function, singleton: boolean, scope?: string) {
super(singleton, scope)
this.clazz = clazz
}
createInstance (container: ContainerResolver): T {
let params = [] as any[]
const injects = getInjectMetas(this.clazz) || []
for (let m of injects) {
const index = m.index!
let value!: any
if (m.resolver) {
value = m.resolver(container, m.scope || this.scope)
} else if (m.id) {
value = container.get(m.id, m.scope || this.scope)
} else {
const type = (Reflect as any).getMetadata("design:paramtypes", this.clazz)[index]
value = container.get(type, m.scope || this.scope)
}
if (m.required && [null, void 0].indexOf(value) > -1) {
throw new InjectError(`can not resolve constructor param[${m.index}]`)
}
params[index] = value
}
const ins = Reflect.construct(this.clazz, params)
container.fill(ins, this.scope)
return ins
}
}
export class FactoryRegistion<T> extends Registion<T>{
readonly factory: Factory<T>
constructor(factory: Factory<T>, singleton: boolean, scope?: string) {
super(singleton, scope)
this.factory = factory
}
createInstance (container: ContainerResolver): T {
return this.factory.create()
}
}
export class AliasRegistion<T> extends Registion<T> {
readonly srcId: ID<T>
constructor(srcId: ID<T>, scope?: string) {
super(false, scope)
this.srcId = srcId
}
getInstance (container: ContainerResolver, scope?: string): T {
return container.get(this.srcId as string, scope) as T
}
createInstance (container: ContainerResolver): T {
throw new Error('should not access here')
}
}