UNPKG

@akala/core

Version:
287 lines 10.6 kB
"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