simple-ioc-container
Version:
Simple DI Container
270 lines (223 loc) • 9.88 kB
JavaScript
;
var _typeof2 = require("babel-runtime/helpers/typeof");
var _typeof3 = _interopRequireDefault(_typeof2);
var _toConsumableArray2 = require("babel-runtime/helpers/toConsumableArray");
var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck");
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require("babel-runtime/helpers/createClass");
var _createClass3 = _interopRequireDefault(_createClass2);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _require = require("./constants"),
CONTAINER_TYPE_VALUE = _require.CONTAINER_TYPE_VALUE,
CONTAINER_TYPE_FACTORY = _require.CONTAINER_TYPE_FACTORY,
CONTAINER_TYPE_SERVICE = _require.CONTAINER_TYPE_SERVICE;
var checkableTypes = [CONTAINER_TYPE_VALUE, CONTAINER_TYPE_FACTORY, CONTAINER_TYPE_SERVICE];
/**
* Creates a new Injector.
* @class
* @property {object} proxy - Proxy of an Injector instance for simple access to
* dependencies via properties.
*/
module.exports = function () {
/**
* Initializes the Injector with a container.
* @constructs
*
* @param {Map|Object} container - Container for dependencies.
* @param {...Object} dependencies - See {@link Injector#register}.
* (optional)
*/
function Injector(container) {
(0, _classCallCheck3.default)(this, Injector);
/**
* @protected
* @property {object} __container - contains a container of
* dependencies.
*/
Object.defineProperty(this, "__container", {
value: container,
__proto__: null
});
this.proxy = new Proxy(this, {
get: function get(target, prop) {
return target.get(prop);
}
});
for (var _len = arguments.length, dependencies = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
dependencies[_key - 1] = arguments[_key];
}
this.register.apply(this, dependencies);
}
/**
* Registers dependence(ies) in a container with specified params.
*
* @param {...Object} dependencies - Params of a dependence. Description:
* {
* key(required): {string} dependence key;
* type(required): {enum} ["class", "value", "singleton"];
* value(required): {*};
* args(optional): {Array} arguments to bind to a constructor.
* force(optional): {bool} registers forcefully if true, otherwise
* doesn't. Default false;
* onRegister(optional): {callback} Invokes if dependence is
* successfully registered.
* }
*
* @returns {Injector} Injector instance.
* @throws Error
*/
(0, _createClass3.default)(Injector, [{
key: "register",
value: function register() {
var _this = this;
for (var _len2 = arguments.length, dependencies = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
dependencies[_key2] = arguments[_key2];
}
dependencies.forEach(function (params) {
var key = params.key,
type = params.type,
force = params.force,
onRegister = params.onRegister;
if (!key) {
throw new Error("Key is not specified.");
} else if (!checkableTypes.includes(type)) {
throw new Error("Type is invalidated.");
} else if (!("value" in params)) {
throw new Error("Value is not specified.");
}
if (force) {
_this.__container.delete(key);
_this.__container.set(key, params);
} else {
if (!_this.__container.has(key)) {
_this.__container.set(key, params);
} else {
return console.warn("Dependence key '" + key + "' has already registered. " + "You should specify flag 'force'.");
}
}
if (onRegister && typeof onRegister == "function") {
onRegister();
}
});
return this;
}
/**
* Resolves all dependencies before invoke one of them by a key.
* @protected
*
* @param {Object} dependence - Dependence params.
* See {@link Injector#register}.
* @param {Array} args - Arguments to bind to a constructor.
* @param {Boolean} needConstructor - Returns constructor if true
* otherwise instance. Default false.
*
* @returns {*} Dependence.
*/
}, {
key: "__resolve",
value: function __resolve(dependence) {
var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var needConstructor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
switch (dependence.type) {
case CONTAINER_TYPE_VALUE:
return dependence.value;
case CONTAINER_TYPE_FACTORY:
return needConstructor ? this.getConstructor(null, dependence.value) : new (Function.prototype.bind.apply(this.__issueProxyConstructor(dependence), [null].concat((0, _toConsumableArray3.default)(args))))();
case CONTAINER_TYPE_SERVICE:
if ((0, _typeof3.default)(dependence.value) == "object") {
return needConstructor ? dependence.value.constructor : dependence.value;
} else if (needConstructor) {
return this.getConstructor(null, dependence.value);
}
this.register({
key: dependence.key,
type: CONTAINER_TYPE_SERVICE,
value: new (Function.prototype.bind.apply(this.__issueProxyConstructor(dependence), [null].concat((0, _toConsumableArray3.default)(args))))(),
force: true
});
return this.__container.get(dependence.key).value;
}
}
/**
* Issues a constructor for a dependence.
* @protected
*
* @param {Object} dependence - Dependence params.
* See {@link Injector#register}.
* @returns {*} Constructor with bound args.
*/
}, {
key: "__issueProxyConstructor",
value: function __issueProxyConstructor(dependence) {
var Constructor = this.getConstructor(null, dependence.value);
var proxy = this.proxy;
return new Proxy(Constructor, {
construct: function construct(target, args) {
target.prototype._di = proxy;
return new (Function.prototype.bind.apply(target, [null].concat((0, _toConsumableArray3.default)(args))))();
}
});
}
/**
* Returns a dependence by a key.
*
* @param {*} key - Key to get a dependence for.
* @param {...*} args - Arguments to bind to a constructor.
*
* @returns {*} Dependence.
*/
}, {
key: "get",
value: function get(key) {
try {
for (var _len3 = arguments.length, args = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
args[_key3 - 1] = arguments[_key3];
}
return this.__resolve(this.__container.get(key), args);
} catch (e) {
console.error("Undefined key \"" + key + "\"");
console.error(e);
}
}
/**
* Returns a dependence constructor.
* Note: a dependence has to have a constructor.
*
* @param {*} key - Key to get a dependence for.
* @param {*} value - Custom constructor or path to a constructor. (inner)
*
* @returns {Function} Dependence constructor.
* @throws Error
*/
}, {
key: "getConstructor",
value: function getConstructor(key) {
var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
try {
var target = value ? value : this.__resolve(this.__container.get(key), [], true);
if (typeof target == "function") {
return target;
} else if (typeof target == "string") {
var _constructor = require(target);
if (_constructor && _constructor.__esModule) {
_constructor = target.default;
}
if (typeof _constructor == "function") {
return _constructor;
}
} else if ((typeof target === "undefined" ? "undefined" : (0, _typeof3.default)(target)) == "object") {
if (value.__esModule) {
return value.default;
}
return target.constructor;
}
throw new Error("This is not a constructor.");
} catch (e) {
console.error("Undefined key \"" + key + "\"");
console.error(e);
}
}
}]);
return Injector;
}();