@akala/core
Version:
287 lines • 10.6 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const reflect_1 = require("./reflect");
const debug_1 = __importDefault(require("debug"));
const promiseHelpers_1 = require("./promiseHelpers");
const events_1 = require("events");
var log = debug_1.default('akala:core:injector');
function ctorToFunction() {
var args = [null];
for (var i = 0; i < arguments.length; i++)
args[i + 1] = arguments[i];
return new (Function.prototype.bind.apply(this, args));
}
class Injector {
constructor(parent) {
this.parent = parent;
this.notifier = new events_1.EventEmitter();
this.inspecting = false;
this.browsingForJSON = false;
this.injectables = {};
if (this.parent == null)
this.parent = exports.defaultInjector;
this.register('$injector', this);
}
setInjectables(value) {
this.injectables = value;
}
keys() {
return Object.keys(this.injectables);
}
merge(i) {
var self = this;
Object.getOwnPropertyNames(i.injectables).forEach(function (property) {
if (property != '$injector')
self.registerDescriptor(property, Object.getOwnPropertyDescriptor(i.injectables, property));
});
}
notify(name, value) {
if (typeof value == 'undefined')
value = Object.getOwnPropertyDescriptor(this.injectables, name);
if (this.notifier.listenerCount(name) > 0)
this.notifier.emit(name, value);
if (this.parent)
this.parent.notify(name, value);
}
onResolve(name, handler) {
if (!handler)
return new Promise((resolve, reject) => {
this.onResolve(name, resolve);
});
var value = this.resolve(name);
if (value !== undefined && value !== null) {
handler(value);
return;
}
this.notifier.once(name, (prop) => {
if (prop.get)
handler(prop.get());
else
handler(prop.value);
});
if (this.parent)
this.parent.onResolve(name, handler);
}
inject(a, ...b) {
if (typeof a == 'function')
return this.injectWithName(a['$inject'] || reflect_1.getParamNames(a), a);
var self = this;
return function (c) {
if (typeof b == 'undefined')
b = [];
b.unshift(a);
var oldf = self.injectWithName(b, c.value);
c.value = function () {
return oldf.apply(this, arguments);
};
};
}
injectAsync(a, ...b) {
if (typeof a == 'function')
return this.injectWithNameAsync(a['$inject'] || reflect_1.getParamNames(a), a);
if (typeof b == 'undefined')
b = [];
b.unshift(a);
var self = this;
return function (c) {
var f = c.value;
c.value = function () {
return self.injectWithNameAsync(b, f);
};
};
}
injectNew(ctor) {
return this.inject(ctorToFunction.bind(ctor));
}
resolve(param) {
log('resolving ' + param);
if (typeof (this.injectables[param]) != 'undefined') {
log(`resolved ${param}`);
log.extend('verbose')(`resolved ${param} to ${this.injectables[param]}`);
return this.injectables[param];
}
var indexOfDot = param.indexOf('.');
if (~indexOfDot) {
var keys = param.split('.');
return keys.reduce((result, key, i) => {
if (result instanceof Proxy)
return result[key];
if (result instanceof Injector)
return result.resolve(key);
if (promiseHelpers_1.isPromiseLike(result))
return result.then((result) => { return result[key]; });
if (result === this.injectables && typeof (result[key]) == 'undefined' && this.parent) {
return this.parent.resolve(key);
}
return result && result[key];
}, this.injectables);
}
if (this.parent) {
log('trying parent injector');
return this.parent.resolve(param);
}
return null;
}
resolveAsync(name) {
return this.onResolve(name);
log('resolving ' + name);
if (typeof (this.injectables[name]) != 'undefined') {
log('resolved ' + name + ' to %o', this.injectables[name]);
return this.injectables[name];
}
if (this.parent) {
log('trying parent injector');
return this.parent.resolveAsync(name);
}
return this.onResolve(name);
}
inspect() {
if (this.inspecting)
return;
this.inspecting = true;
console.log(this.injectables);
this.inspecting = false;
}
toJSON() {
console.log(arguments);
var wasBrowsingForJSON = this.browsingForJSON;
this.browsingForJSON = true;
if (!wasBrowsingForJSON)
return this.injectables;
this.browsingForJSON = wasBrowsingForJSON;
return undefined;
}
injectNewWithName(toInject, ctor) {
return this.injectWithName(toInject, ctorToFunction.bind(ctor));
}
injectWithNameAsync(toInject, a) {
if (!toInject || toInject.length == 0)
return Promise.resolve(a());
var paramNames = reflect_1.getParamNames(a);
var self = this;
var wait = false;
return new Promise((resolve, reject) => {
if (paramNames.length == toInject.length || paramNames.length == 0) {
if (toInject.length == paramNames.length && paramNames.length == 0)
resolve(a.call(null));
else {
var args = [];
for (var param of toInject) {
args[args.length] = self.resolveAsync(param);
if (promiseHelpers_1.isPromiseLike(args[args.length - 1]))
wait = true;
}
if (wait)
return Promise.all(args.map(function (v) {
if (promiseHelpers_1.isPromiseLike(v))
return v;
return Promise.resolve(v);
})).then((args) => { resolve(a.apply(null, args)); });
else
resolve(a.apply(null, args));
}
}
else
reject('the number of arguments does not match the number of injected parameters');
});
}
injectWithName(toInject, a) {
var self = this;
if (toInject && toInject.length > 0) {
var paramNames = reflect_1.getParamNames(a);
if (paramNames.length == toInject.length || paramNames.length == 0) {
if (toInject.length == paramNames.length && paramNames.length == 0)
return a;
return function (instance) {
var args = [];
for (var param of toInject) {
args[args.length] = self.resolve(param);
}
return a.apply(instance, args);
};
}
}
return function (instance, ...otherArgs) {
var args = [];
var unknownArgIndex = 0;
for (var param of toInject) {
var resolved = self.resolve(param);
if (resolved && paramNames && paramNames.indexOf(param) == args.length)
args[args.length] = resolved;
else if (typeof (otherArgs[unknownArgIndex]) != 'undefined')
args[args.length] = otherArgs[unknownArgIndex++];
else
args[args.length] = resolved;
}
if (otherArgs && otherArgs.length > unknownArgIndex) {
args.concat(otherArgs.slice(unknownArgIndex));
}
return a.apply(instance, args);
};
}
exec(...toInject) {
var self = this;
return function (f) {
return self.injectWithName(toInject, f)(this);
};
}
unregister(name) {
var registration = Object.getOwnPropertyDescriptor(this.injectables, name);
if (registration)
delete this.injectables[name];
}
register(name, value, override) {
if (typeof (value) != 'undefined' && value !== null)
this.registerDescriptor(name, { value: value, enumerable: true, configurable: true }, override);
return value;
}
registerFactory(name, value, override) {
this.register(name + 'Factory', value, override);
this.registerDescriptor(name, {
get: function () {
return value();
}, enumerable: true, configurable: true
}, override);
return value;
}
factory(name, override) {
var inj = this;
return function (fact) {
return inj.registerFactory(name, fact, override);
};
}
service(name, override, ...toInject) {
var inj = this;
var singleton;
if (typeof toInject == 'undefined')
toInject = [];
if (typeof override == 'string') {
toInject.unshift(override);
override = false;
}
return function (fact) {
inj.registerDescriptor(name, {
get() {
if (singleton)
return singleton;
return singleton = inj.injectNewWithName(toInject, fact)();
}
});
};
}
registerDescriptor(name, value, override) {
log('registering ' + name);
if (!override && typeof (this.injectables[name]) != 'undefined')
throw new Error('There is already a registered item for ' + name);
if (typeof (this.injectables[name]) !== 'undefined')
this.unregister(name);
Object.defineProperty(this.injectables, name, value);
this.notify(name, value);
}
}
exports.Injector = Injector;
exports.defaultInjector = new Injector();
//# sourceMappingURL=injector.js.map