UNPKG

@appolo/inject

Version:
420 lines (283 loc) 11.2 kB
"use strict"; import {IOptions} from "../interfaces/IOptions"; import {IFactory} from "../interfaces/IFactory"; import {Class, IDefinition, IParamInject} from "../interfaces/IDefinition"; import {Define} from "../define/define"; import {InjectDefineSymbol, InjectParamSymbol} from "../decorators/decorators"; import {Util} from "../utils/util"; import {Event, IEvent} from "@appolo/events"; import {Events, InjectEvent} from "../events/events"; import {_get, _getFromDefinition} from "../methods/instances/_get"; import {_register} from "../methods/definitions/_register"; import {_loadFactory} from "../methods/factories/_loadFactory"; import {_addDefinitions} from "../methods/definitions/_addDefinitions"; import {_addDefinition} from "../methods/definitions/_addDefinition"; import {_getFactory} from "../methods/factories/_getFactory"; import {_getFactoryValue} from "../methods/factories/getFactoryValue"; import {_initInitMethods} from "../methods/methods/_initInitMethods"; import {_initAlias} from "../methods/alias/_initAlias"; import {_initFactories} from "../methods/factories/_initFactories"; import {_initProperties} from "../methods/properties/_initProperties"; import {_initInstances} from "../methods/instances/_initInstances"; import {_initDefinitions} from "../methods/definitions/_initDefinitions"; import {_initBootstrapMethods} from "../methods/methods/_initBootstrapMethods"; import {_prepareProperties} from "../methods/properties/_prepareProperties"; type keyObject = { [index: string]: Object } export const IsWiredSymbol = "@__isWired__"; export class Injector { protected _definitions: { [index: string]: IDefinition }; protected _instances: keyObject; protected _factoriesObjects: { [index: string]: { [index: string]: { id: string, injector?: Injector } } }; protected _factoriesValues: { [index: string]: any }; protected _alias: { [index: string]: Object[] }; protected _aliasFactory: { [index: string]: Object[] }; protected _factories: string[]; protected _parent: Injector; protected _children: Injector[]; protected _events = new Events() protected _options: IOptions; protected _isInitialized: boolean = false; constructor() { this._instances = {}; this._definitions = {}; this._options = {}; this._factoriesObjects = {}; this._alias = {}; this._aliasFactory = {}; this._factoriesValues = {}; this._factories = []; this._children = [] } public get parent(): Injector { return this._parent } public set parent(value: Injector) { this._parent = value; value.children.push(this); } public get options(): IOptions { return this._options } public get children(): Injector[] { return this._children; } public async initialize(options?: IOptions) { if (this._isInitialized) { return; } this._options = options || {}; let definitions = {}; Object.keys(definitions || {}).forEach(id => this.addDefinition(id, definitions[id])); //we have parent so we wait until parent.initialize if (this.parent && !this._options.immediate) { return; } await (this._events.beforeInitialize as Event<void>).fireEventAsync(); await this.initDefinitions(); await this.initFactories(); await this.initInstances(); await this.initProperties(); this.initAlias(); await this.initInitMethods(); await this.initBootstrapMethods(); this._isInitialized = true; } protected initDefinitions() { return _initDefinitions.call(this); } protected initInstances() { return _initInstances.call(this); } protected initProperties() { return _initProperties.call(this); } protected initFactories() { return _initFactories.call(this); } protected initAlias() { return _initAlias.call(this); } protected initInitMethods() { return _initInitMethods.call(this); } protected initBootstrapMethods() { return _initBootstrapMethods.call(this); } public getObject<T>(objectID: string | Function, runtimeArgs?: any[]): T { return this.get<T>(objectID, runtimeArgs) } public resolve<T>(objectID: string | Function, runtimeArgs?: any[]): T { try { return this.get<T>(objectID, runtimeArgs) } catch (e) { return null; } } public async getAsync<T>(objectID: string | Function, runtimeArgs?: any[]): Promise<T> { let id = Util.getClassId(objectID); let def = this.getDefinition(id); let obj = this.get<T>(objectID, runtimeArgs); if (def.initMethodAsync) { await obj[def.initMethodAsync](); } if (def.bootstrapMethodAsync) { await obj[def.bootstrapMethodAsync](); } return obj; } public wire<T extends new (...args: any) => any>(klass: T, runtimeArgs?: ConstructorParameters<T>): InstanceType<T> { let def = Util.getClassDefinition(klass) if (!def) { return new klass(...runtimeArgs as any[]) } def = def.clone(); _prepareProperties.call(this, def.definition); let instance = _getFromDefinition.call(this, def.definition, def.definition.id, []) as InstanceType<T> return instance; } public get<T>(objectID: string | Function, runtimeArgs?: any[]): T { objectID = Util.getClassNameOrId(objectID); let def = this._definitions[objectID]; if (def && def.factory) { return this.getFactoryValue(objectID, def); } return this._get<T>(objectID as string, runtimeArgs) } public getFactoryValue<T>(objectID: string, definitions?: IDefinition): T { return _getFactoryValue.call(this, objectID, definitions) as T } public getFactory<T>(objectID: string | Function, refs?: { ids: {}, paths: string[] }): Promise<T> { return _getFactory.call(this, objectID, refs) as Promise<T> } protected _get<T>(objectID: string, runtimeArgs?: any[]): T { return _get.call(this, objectID, runtimeArgs) as T } public getInstance<T>(id: string): T { let instance = this._instances[id]; if (instance) { return instance as T; } if (this.parent) { return this.parent.getInstance(id); } return null } public hasInstance(id: string) { return !!this.getInstance(id) } public addDefinition(objectId: string, definition: IDefinition): Injector { return _addDefinition.call(this, objectId, definition); } public removeDefinition(objectId: string): Injector { delete this._definitions[objectId]; return this; } public addDefinitions(definitions: { [index: string]: IDefinition } | Map<string, IDefinition>): Injector { return _addDefinitions.call(this, definitions); } public addObject<T>(objectId: string, instance: T, silent?: boolean): Injector { return this.addInstance(objectId, instance, silent); } public addInstance<T>(objectId: string, instance: T, silent?: boolean): Injector { if (!silent && this._instances[objectId]) { console.log("Injector:object id already exists overriding: " + objectId); } this._instances[objectId] = instance; return this; } public removeInstance(objectId: string): Injector { delete this._instances[objectId]; return this; } public getObjectsByType<T>(type: Function): T[] { let output = []; for (let key in this._instances) { if (this._instances.hasOwnProperty(key) && this._instances[key] instanceof type) { output.push(this._instances[key]) } } return output; } public getInstances(): { [index: string]: { [index: string]: any } } { return this._instances; } public getDefinitions(): { [index: string]: IDefinition } { return this._definitions; } public getDefinitionsValue(): IDefinition[] { return Object.values(this._definitions); } public getAliasDefinitions(alias: string): IDefinition[] { return Object.values(this._definitions).filter(item => (item.alias || []).includes(alias)) } public getTypes(): Function[] { return this.getDefinitionsValue().map(item => item.type) } public hasDefinition(id: string): boolean { return !!this.getDefinition(id); } public getDefinition(id: string | Function): IDefinition { id = Util.getClassNameOrId(id); let def = this.getOwnDefinition(id); if (def) { return def } if (this.parent) { return this.parent.getDefinition(id); } } public hasOwnDefinition(id: string): boolean { return !!this.getOwnDefinition(id); } public getOwnDefinition(id: string | Function): IDefinition { id = Util.getClassNameOrId(id); let def = this._definitions[id]; if (def) { return def.injector && def.injector !== this ? def.injector.getDefinition(def.refName || id) : def; } } public addAlias(aliasName: string, value: any) { this.getAlias(aliasName).push(value) } public removeAlias(aliasName: string, value: any) { Util.removeFromArray(this.getAlias(aliasName), value) } public getAlias(aliasName: string): any[] { return this._alias[aliasName] = this._alias[aliasName] || (this.parent ? this.parent.getAlias(aliasName) : []) || []; } public addAliasFactory(aliasName: string, value: any) { this.getAliasFactory(aliasName).push(value); } public removeAliasFactory(aliasName: string, value: any) { Util.removeFromArray(this.getAliasFactory(aliasName), value); } public getAliasFactory(aliasName: string): any[] { return this._aliasFactory[aliasName] = this._aliasFactory[aliasName] || (this.parent ? this.parent.getAliasFactory(aliasName) : []) || []; } public getFactoryMethod(objectId: string | Function): Function { return Util.createDelegate(this.get, this, [objectId]); } public registerMulti(fns: Class[]): this { for (let i = 0, len = fns.length; i < len; i++) { this.register(fns[i]); } return this; } public register(id: string | Class, type?: Class, filePath?: string): Define { return _register.call(this, id, type, filePath); } public get events(): Events { return this._events; } protected loadFactory<T>(objectId: string): Promise<void> { return _loadFactory.call(this, objectId); } public reset() { this._instances = {}; this._definitions = {}; this._options = {}; this._alias = {}; this._aliasFactory = {}; this._factoriesObjects = {}; } }