ioc.js
Version:
Inversion Of Control for ECMA Script 7
150 lines (131 loc) • 3.69 kB
JavaScript
const prop =
{
constructor: "__constructor__",
setters: "__setters__",
methods: "__methods__"
}
export function inject(...names)
{
return function(target)
{
target[prop.constructor] = names;
return target;
}
}
export function injectSetter(name)
{
return function(target, key, descriptor)
{
target[prop.setters] = target[prop.setters] || [];
target[prop.setters].push([key, name]);
return descriptor;
}
}
export function injectMethod(...names)
{
return function(target, key, descriptor)
{
target[prop.methods] = target[prop.methods] || [];
target[prop.methods].push([key, names]);
return descriptor;
}
}
export const LifeTime =
{
perResolve: 0,
singleton: 1
}
function constructorMeta(type) { return type[prop.constructor] || []; }
function settersMeta(instance) { return instance[prop.setters] || []; }
function methodsMeta(instance) { return instance[prop.methods] || []; }
function createInstance(type, args)
{
return new (type.bind.apply(type, [type].concat(args)));
}
function injectSetters(kernel, instance)
{
const setters = settersMeta(instance);
for (let i = 0, len = setters.length; i < len; i++)
{
const [prop, name] = setters[i];
instance[prop] = kernel.resolve(name);
}
}
function injectMethods(kernel, instance)
{
const methods = methodsMeta(instance);
for (let i = 0, len = methods.length; i < len; i++)
{
const [prop, names] = methods[i];
instance[prop].apply(instance, names.map(n => kernel.resolve(n)));
}
}
function injectAllMembers(kernel, instance)
{
injectSetters(kernel, instance);
injectMethods(kernel, instance);
}
export class Kernel
{
constructor()
{
this._types = {};
this._factories = {};
}
get types() { return this._types; }
get factories() { return this._factories; }
contains(name)
{
return this.types[name] !== undefined;
}
register(name, type, lifeTime = LifeTime.perResolve)
{
this.types[name] = { type, lifeTime };
return this;
}
registerType(type, lifeTime = LifeTime.perResolve)
{
this.register(type.name, type, lifeTime);
return this;
}
registerInstance(name, instance, injectMembers = false)
{
if (injectMembers)
{
injectAllMembers(this, instance);
}
this.types[name] = {
instance,
lifeTime: LifeTime.singleton
};
return this;
}
factoryFor(name, factory)
{
this.factories[name] = factory;
return this;
}
resolve(name)
{
const item = this.types[name];
if (item.instance) return item.instance;
else
{
const
{ type, lifeTime } = item,
args = constructorMeta(type).map(n => this.resolve(n)),
factory = this.factories[name],
instance = factory ?
factory({ kernel: this, type, args }) :
createInstance(type, args);
injectAllMembers(this, instance);
if (lifeTime === LifeTime.singleton)
{
this.types[name] = { instance, lifeTime };
}
return instance;
}
}
getType(name) { return this.types[name].type; }
}
export default { inject, injectSetter, injectMethod, LifeTime, Kernel }