UNPKG

strikejs-react

Version:

A state management framework for ReactJS applications.

255 lines (240 loc) 7.4 kB
'use strict'; /** * Extracts the names of the parameters from functions * * @export * @param {Function} fn the function to extract its parameters' names. * @returns {Array<string>} array of parameters names */ export function extractArgumentsFromFunction(fn: Function): any { let deps: any; fn.toString() .replace(/^function[\s]+?[\S]+?\((.*?)\)/, function(e: string, v: string, k: number) { deps = (v.trim().length > 0 && v.trim().split(/[\s,]+/)) || []; return e; }) return deps; } /** * A dependency injection module inspired by AngularJS's dependency injection. * * @export * @class Injector */ export class Injector { /** * an object literal containing all registered components. * * @type {Dictionary<any>} */ components: Dictionary<any>; /** * an object literal containing instances of the registered components. * * @type {Dictionary<any>} */ instances: Dictionary<any>; /** * Used internally to resolve dependencies. */ private stack: Array<any>; /** * Creates an instance of Injector. */ constructor(){ this.components = {}; this.instances = {}; this.stack = []; } /** * Adds an instance to the list of registered instances within the module. * * @param {string} name the name of the instance * @param {*} c the instance, this can be a primitive, function, or an object. * @returns the registered instance. */ public addInstance(name:string,c:any){ return this.instances[name] = c; } /** * Adds a component to the list of registered components within the module. * ES6 class components should implement a static function `factory` and should include * a static member `$inject` including a list of dependencies. The module will resolve the required * dependencies and pass them to the static `factory` method which should return an instance of the * compnent. * * @param {string} name the name of the component. * @param {*} c the component to register * @returns the component. */ public addComponent(name:string,c:any){ return this.components[name] = c; } /** * Checks whether a component exists or not * * @param {string} name the name of the component. * @returns {boolean} true if the component exists false otherwise. */ public hasComponent(name:string):boolean{ return this.components[name]; } /** * Checks whether an instance is registered or not. * * @param {string} name the name of the component. * @returns {boolean} returns the instance or undefined otherwise. */ public hasInstance(name:string):boolean{ return this.instances[name]; } /** * Given a function that requires access to some components, this method injects the function with the required * * @param {Function|ServiceFunction} fn the function to inject * @param {*} [ctx] (description) * @param {...any[]} args (description) * @returns (description) */ public injectFunction(fn:any,ctx?:any,...args:any[]){ if (typeof fn !== "function"){ throw new Error("Injector: provided argument is not a function"); } let a: any, all: Array<any> = [],ccc:any = ctx || null; fn.$inject = fn.$inject || extractArgumentsFromFunction(fn); if (!fn.$inject || fn.$inject.length === 0){ return fn.factory ? fn.factory() : fn(); } while ((a = fn.$inject.shift())) { all.push(this.get(a)); } return fn.factory ? fn.factory.apply(ccc, [].concat(all,Array.prototype.slice.call(args,0))) : fn.apply(ccc, [].concat(all,Array.prototype.slice.call(args,0))); } private _inject(name:string, c:any):any{ let a: any, all:Array<any> = []; if (!c.$inject || c.$inject.length === 0){ return this.addInstance(name, c.factory? c.factory():c()); } if (this.stack.indexOf(name) !== -1){ throw new Error('Circular dependency: ' + this.stack.join(' -> ') + ' -> ' + name); } this.stack.push(name); while((a=c.$inject.shift())){ all.push(this.get(a)); } this.stack.pop(); return this.instances[name] = c.factory?c.factory.apply(null,all):c.apply(null, all); } /** * (description) * * @param {string} name (description) * @returns {*} (description) */ public get(name:string):any{ if (this.instances[name]){ return this.instances[name]; } if (!this.components[name]){ throw new Error('Component: '+name+' could not be found'); } return this._inject(name, this.components[name]); } /** * (description) * * @param {string} name (description) * @param {Object} o (description) * @returns {Injector} (description) */ public register(name: string, o: Object): Injector; /** * (description) * * @param {string} name (description) * @param {number} n (description) * @returns {Injector} (description) */ public register(name: string, n: number): Injector; /** * (description) * * @param {string} name (description) * @param {Function} fn (description) * @returns {Injector} (description) */ public register(name: string, fn: Function): Injector; /** * (description) * * @param {string} name (description) * @param {Array<any>} array (description) * @returns {Injector} (description) */ public register(name:string, array: Array<any>): Injector; /** * (description) * * @param {Array<any>} array (description) * @returns {Injector} (description) */ public register(array: Array<any>): Injector; /** * (description) * * @returns {Injector} (description) */ public register():Injector { let name: string, callback: any, deps:[string], temp:any; if (arguments.length === 0){ throw new Error('Injector: no agruments provided.'); } if (arguments.length === 2) { if (typeof arguments[0] !== "string") { throw new Error('Injector: first argument must be of type string.'); } if (arguments[1] === null) { throw new Error('Injector: second argument cannot be null'); } name = arguments[0]; callback = arguments[1]; if (typeof callback === "string" || typeof callback === "number" || (typeof callback === "object" && !(callback instanceof Array))) { this.addInstance(name, callback); return this; } }else if (arguments.length === 1){ temp = arguments[0]; if (typeof temp === "function"){ if (!temp.name){ throw new Error('Injector: anonymous functions are not supported.'); } name = temp.name; callback = temp; }else if (temp instanceof Array){ if (typeof temp[temp.length - 1] !== "function" || !temp[temp.length -1].name){ throw new Error('Injector: last item in Array is not a function or function has no name.'); } callback = temp[temp.length - 1]; name = callback.name; }else{ throw new Error('Injector: unknown parameter set provided'); } } callback.$inject = callback.$inject || (typeof callback.factory === "function" && extractArgumentsFromFunction(callback.factory)) || (extractArgumentsFromFunction(callback)); this.addComponent(name, callback); return this; } } export interface DependencyContainer{ get<T>(key:string):T|Promise<T>; }