UNPKG

inversify

Version:

A powerful and lightweight inversion of control container for JavaScript and Node.js apps powered by TypeScript.

216 lines (215 loc) 10.8 kB
"use strict"; var plan_1 = require("./plan"); var context_1 = require("./context"); var request_1 = require("./request"); var target_1 = require("./target"); var METADATA_KEY = require("../constants/metadata_keys"); var ERROR_MSGS = require("../constants/error_msgs"); var binding_type_1 = require("../bindings/binding_type"); var utils_1 = require("../utils/utils"); var target_type_1 = require("./target_type"); var Planner = (function () { function Planner() { } Planner.prototype.createContext = function (kernel) { return new context_1.default(kernel); }; Planner.prototype.createPlan = function (context, binding, target) { var _this = this; var rootRequest = new request_1.default(binding.serviceIdentifier, context, null, binding, target); var plan = new plan_1.default(context, rootRequest); context.addPlan(plan); if (binding.type === binding_type_1.default.Instance) { var dependencies = this._getDependencies(binding.implementationType); dependencies.forEach(function (dependency) { _this._createSubRequest(rootRequest, dependency); }); } return plan; }; Planner.prototype.getBindings = function (kernel, serviceIdentifier) { var bindings = []; var _kernel = kernel; var _bindingDictionary = _kernel._bindingDictionary; if (_bindingDictionary.hasKey(serviceIdentifier)) { bindings = _bindingDictionary.get(serviceIdentifier); } else if (_kernel._parentKernel !== undefined) { bindings = this.getBindings(_kernel._parentKernel, serviceIdentifier); } return bindings; }; Planner.prototype.getActiveBindings = function (parentRequest, target) { var bindings = this.getBindings(parentRequest.parentContext.kernel, target.serviceIdentifier); var activeBindings = []; if (bindings.length > 1 && target.isArray() === false) { activeBindings = bindings.filter(function (binding) { var request = new request_1.default(binding.serviceIdentifier, parentRequest.parentContext, parentRequest, binding, target); return binding.constraint(request); }); } else { activeBindings = bindings; } return activeBindings; }; Planner.prototype._createSubRequest = function (parentRequest, target) { try { var activeBindings = this.getActiveBindings(parentRequest, target); if (activeBindings.length === 0) { var serviceIdentifier = parentRequest.parentContext.kernel.getServiceIdentifierAsString(target.serviceIdentifier); throw new Error(ERROR_MSGS.NOT_REGISTERED + " " + serviceIdentifier); } else if (activeBindings.length > 1 && target.isArray() === false) { var serviceIdentifier = parentRequest.parentContext.kernel.getServiceIdentifierAsString(target.serviceIdentifier); throw new Error(ERROR_MSGS.AMBIGUOUS_MATCH + " " + serviceIdentifier); } else { this._createChildRequest(parentRequest, target, activeBindings); } } catch (error) { if (error instanceof RangeError) { this._throwWhenCircularDependenciesFound(parentRequest.parentContext.plan.rootRequest); } else { throw new Error(error.message); } } }; Planner.prototype._createChildRequest = function (parentRequest, target, bindings) { var _this = this; var childRequest = parentRequest.addChildRequest(target.serviceIdentifier, bindings, target); var subChildRequest = childRequest; bindings.forEach(function (binding) { if (target.isArray()) { subChildRequest = childRequest.addChildRequest(binding.serviceIdentifier, binding, target); } if (binding.type === binding_type_1.default.Instance) { var subDependencies = _this._getDependencies(binding.implementationType); subDependencies.forEach(function (d, index) { _this._createSubRequest(subChildRequest, d); }); } }); }; Planner.prototype._throwWhenCircularDependenciesFound = function (request, previousServiceIdentifiers) { var _this = this; if (previousServiceIdentifiers === void 0) { previousServiceIdentifiers = []; } var parentServiceIdentifier = request.parentContext.kernel.getServiceIdentifierAsString(request.serviceIdentifier); previousServiceIdentifiers.push(parentServiceIdentifier); request.childRequests.forEach(function (childRequest) { var childServiceIdentifier = request.parentContext.kernel.getServiceIdentifierAsString(childRequest.serviceIdentifier); if (previousServiceIdentifiers.indexOf(childServiceIdentifier) === -1) { if (childRequest.childRequests.length > 0) { _this._throwWhenCircularDependenciesFound(childRequest, previousServiceIdentifiers); } else { previousServiceIdentifiers.push(childServiceIdentifier); } } else { previousServiceIdentifiers.push(childServiceIdentifier); var services = previousServiceIdentifiers.reduce(function (prev, curr) { return (prev !== "") ? prev + " -> " + curr : "" + curr; }, ""); throw new Error(ERROR_MSGS.CIRCULAR_DEPENDENCY + " " + services); } }); }; Planner.prototype._formatTargetMetadata = function (targetMetadata) { var targetMetadataMap = {}; targetMetadata.forEach(function (m) { targetMetadataMap[m.key.toString()] = m.value; }); return { inject: targetMetadataMap[METADATA_KEY.INJECT_TAG], multiInject: targetMetadataMap[METADATA_KEY.MULTI_INJECT_TAG], targetName: targetMetadataMap[METADATA_KEY.NAME_TAG], unmanaged: targetMetadataMap[METADATA_KEY.UNMANAGED_TAG] }; }; Planner.prototype._getTargets = function (func, isBaseClass) { var constructorName = utils_1.getFunctionName(func); var serviceIdentifiers = Reflect.getMetadata(METADATA_KEY.PARAM_TYPES, func); if (serviceIdentifiers === undefined) { var msg = ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION + " " + constructorName + "."; throw new Error(msg); } var constructorArgsMetadata = Reflect.getMetadata(METADATA_KEY.TAGGED, func) || []; var targets = (this._constructorArgsTargets(isBaseClass, constructorName, serviceIdentifiers, constructorArgsMetadata, func.length)).concat((this._getClassPropsTargets(func))); return targets; }; Planner.prototype._constructorArgsTargets = function (isBaseClass, constructorName, serviceIdentifiers, constructorArgsMetadata, constructorLength) { var targets = []; for (var i = 0; i < constructorLength; i++) { var targetMetadata = constructorArgsMetadata[i.toString()] || []; var metadata = this._formatTargetMetadata(targetMetadata); var serviceIndentifier = serviceIdentifiers[i]; serviceIndentifier = (metadata.inject || metadata.multiInject) ? (metadata.inject || metadata.multiInject) : serviceIndentifier; var isUnknownType = (serviceIndentifier === Object || serviceIndentifier === Function || serviceIndentifier === undefined); if (isBaseClass === false && isUnknownType) { var msg = ERROR_MSGS.MISSING_INJECT_ANNOTATION + " argument " + i + " in class " + constructorName + "."; throw new Error(msg); } var target = new target_1.default(target_type_1.default.ConstructorArgument, metadata.targetName, serviceIndentifier); target.metadata = targetMetadata; targets.push(target); } return targets; }; Planner.prototype._getClassPropsTargets = function (func) { var classPropsMetadata = Reflect.getMetadata(METADATA_KEY.TAGGED_PROP, func) || []; var targets = []; var keys = Object.keys(classPropsMetadata); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var targetMetadata = classPropsMetadata[key]; var metadata = this._formatTargetMetadata(classPropsMetadata[key]); var targetName = metadata.targetName || key; var serviceIndentifier = (metadata.inject || metadata.multiInject); var target = new target_1.default(target_type_1.default.ClassProperty, targetName, serviceIndentifier); target.metadata = targetMetadata; targets.push(target); } var baseConstructor = Object.getPrototypeOf(func.prototype).constructor; if (baseConstructor !== Object) { var baseTargets = this._getClassPropsTargets(baseConstructor); targets = targets.concat(baseTargets); } return targets; }; Planner.prototype._getDependencies = function (func) { var constructorName = utils_1.getFunctionName(func); var targets = this._getTargets(func, false); var baseClassDepencencyCount = this._baseClassDepencencyCount(func); if (targets.length < baseClassDepencencyCount) { var error = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH_1 + constructorName + ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH_2; throw new Error(error); } return targets; }; Planner.prototype._baseClassDepencencyCount = function (func) { var baseConstructor = Object.getPrototypeOf(func.prototype).constructor; if (baseConstructor !== Object) { var targets = this._getTargets(baseConstructor, true); var metadata = targets.map(function (t) { return t.metadata.filter(function (m) { return m.key === METADATA_KEY.UNMANAGED_TAG; }); }); var unmanagedCount = [].concat.apply([], metadata).length; var dependencyCount = targets.length - unmanagedCount; if (dependencyCount > 0) { return dependencyCount; } else { return this._baseClassDepencencyCount(baseConstructor); } } else { return 0; } }; return Planner; }()); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = Planner;