UNPKG

metaf-resolvable

Version:

Abstract resolvable mixin and injection root for adding DI/IoC to any framework

144 lines 6.81 kB
"use strict"; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Resolver = exports.override = void 0; var utils_1 = require("./utils"); // we are using private symbol in order to force users work with override function // so override always type compatible with original value var overrideSymbol = Symbol('Overrides'); /** * Function that meant to be used as only way for specifying overrides * @description creates a `[key, value]` pair describing override * @template T type of dependency key that has to be overwritten * @param key actual key that has to be overwritten * @param value value that will be passed as dependency for this `key` * @returns object with symbol property which point to `[key, value]` pair */ function override(key, value) { var _a; return _a = {}, _a[overrideSymbol] = [key, value], _a; } exports.override = override; /** * Class that is responsible for resolving dependencies and requirements * @description contains all resolved dependencies, resolvable class implementations * and methods that used by `Resolvable` factories in order to create properly resolved * classes with dependencies * > **NOTE:** it also meant to be extended in order to provide first-class support * for some particular frameworks e.g. `react` */ var Resolver = /** @class */ (function () { /** * Creates an instance of resolver. * @param [overrides] */ function Resolver(overrides) { if (overrides === void 0) { overrides = []; } this.injectionRoot = new WeakMap(overrides.map(function (overridePair) { return overridePair[overrideSymbol]; })); this.implementations = new WeakMap(); // we want these function to have correct `this` context, // but can't use arrow functions because it breaks normal inheritance flow this.inject = this.inject.bind(this); this.resolveFor = this.resolveFor.bind(this); this.resolveRequirements = this.resolveRequirements.bind(this); this.initialize = this.initialize.bind(this); this.setOverride = this.setOverride.bind(this); } /** * Entry point for resolving requirements for particular resolvable class * @description resolves `requirements` for `classImpl` and applies them to `ApplicationRoot` * > **NOTE:** default implementation does nothing, since only Resolver for specific framework * knows how to apply requirements to particular environment * @template R type of `requirements` tuple * @template C type of `classImpl` constructor * @param requirements tuple of specific requirements that should be met by hosting application * @param classImpl particular resolvable class implementation * @returns `classImpl` or something with exactly same interface */ Resolver.prototype.resolveRequirements = function (requirements, classImpl) { return classImpl; }; /** * Entry point for resolving dependencies of particular instance * @description resolves `injections` for `instance` of `classImpl` resolvable class * @template I type of `injections` dictionary * @param classImpl class constructor that was used for creating this instance * @param [injections] dictionary of dependencies that should be resolved and initialized * @param [instance] particular class instance * @param [args] arguments that were passed to constructor function for creating `instance` * @returns dictionary of resolved and initialized dependencies */ Resolver.prototype.resolveFor = function (classImpl, injections, instance, args) { var _this = this; if (injections === void 0) { injections = {}; } if (this.implementations.has(classImpl)) return this.implementations.get(classImpl); var resolvedDependencies = {}; // TODO: remove this cast once Object.keys typings would be fixed Object.keys(injections) .forEach(function (injectionName) { return resolvedDependencies[injectionName] = _this.inject(injections[injectionName]); }); this.implementations.set(classImpl, resolvedDependencies); return resolvedDependencies; }; /** * This method is used internally to properly initialize dependency * @description initializes injectable `obj` * > **NOTE:** this function is pure, but it's possible that ancestors may need * another solution for initializing instances, because of framework * requirements, conventions and best practices * @param obj dependency that has to be initialized * @returns initialized dependency */ Resolver.prototype.initialize = function (obj) { if ((0, utils_1.isConstructor)(obj)) return new obj(); if ((0, utils_1.isCallable)(obj)) return obj(); throw new Error("Dependency expected to be constructable or callable, but got ".concat(typeof obj)); }; /** * This method is used to inject resolved `dependency` for `injectionKey` * @description Injects resolver * @template T type of `injectionKey` that maps to type of `dependency` value * @param injectionKey actual key of `dependency` * @returns resolved `dependency` instance */ Resolver.prototype.inject = function (injectionKey) { if (!this.injectionRoot.has(injectionKey)) { this.injectionRoot.set(injectionKey, this.initialize(injectionKey)); } return this.injectionRoot.get(injectionKey); }; /** * This method works with result of `override` helper function * @description Sets override for particular `injectionKey` * @template T type of `injectionKey` that maps to type of `dependency` value * @param overridePair actual `[key, value]` pair retrieved from override helper */ Resolver.prototype.setOverride = function (overridePair) { var _a = __read(overridePair[overrideSymbol], 2), key = _a[0], value = _a[1]; this.injectionRoot.set(key, value); }; return Resolver; }()); exports.Resolver = Resolver; //# sourceMappingURL=Resolver.js.map