UNPKG

power-di

Version:

A lightweight Dependency Injection library. Using es6 and other features, remove unnecessary concepts, easy and convenient to use.

538 lines 22.6 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var _a, _b, _c, _d; Object.defineProperty(exports, "__esModule", { value: true }); exports.AlreadyRegistryError = exports.NoRegistryError = exports.NotfoundTypeError = exports.MultiImplementError = exports.IocContext = exports.DefaultRegisterOption = exports.Config = void 0; var utils_1 = require("./utils"); var metadata_1 = require("./class/metadata"); var ClassLoader_1 = require("./class/ClassLoader"); var guard_1 = require("./utils/guard"); var aspect_1 = require("./decorator/aspect"); var Config = /** @class */ (function () { function Config() { /** auto register class self, when class not found. default: false */ this.autoRegisterSelf = false; /** constructor inject, MUST in TypeScript with emitDecoratorMetadata and use decorator with class, default: true */ this.constructorInject = true; /** use class loader for autowired default: true */ this.useClassLoader = true; } return Config; }()); exports.Config = Config; exports.DefaultRegisterOption = { singleton: true, autoNew: true, }; /** ioc context */ var IocContext = /** @class */ (function () { function IocContext(config) { if (config === void 0) { config = {}; } this.config = config; this.components = new Map(); this.setConfig(Object.assign(new Config(), config)); } Object.defineProperty(IocContext.prototype, "classLoader", { get: function () { return this._classLoader; }, enumerable: false, configurable: true }); Object.defineProperty(IocContext, "DefaultInstance", { get: function () { return (this.defaultInstance || ((this.defaultInstance = new IocContext()), this.defaultInstance)); }, enumerable: false, configurable: true }); /** * merge config * @param config new partial config */ IocContext.prototype.setConfig = function (config) { Object.assign(this.config, config); this._classLoader = this.config.useClassLoader === true ? ClassLoader_1.classLoader : this.config.useClassLoader === false ? undefined : this.config.useClassLoader; }; /** * remove instance of key * @param keyOrType key */ IocContext.prototype.remove = function (keyOrType) { var key = (0, utils_1.getGlobalType)(keyOrType); var store = this.components.get(key); if (store) { this.preDestroyInstance(store); return this.components.delete(key); } return false; }; /** clear all */ IocContext.prototype.clear = function () { var _this = this; Array.from(this.components.values()).forEach(function (ele) { _this.preDestroyInstance(ele); }); this.components.clear(); }; /** * get instance of key * @param keyOrType key * @param opt */ IocContext.prototype.get = function (keyOrType, opt) { if (opt === void 0) { opt = {}; } var key = (0, utils_1.getGlobalType)(keyOrType); var store = this.components.get(key); if (store) { return this.returnValue(store, opt.forceNew); } if (opt.useClassLoader !== false && this.classLoader) { var target = this.findClassByClassLoader(keyOrType, key, { sourceCls: opt.sourceCls, deep: opt.deep !== false, }); if (target) { if (target.base) { this.register(target.type); } return this.get(target.type, __assign(__assign({}, (target.ref ? __assign(__assign({}, opt), { sourceType: opt.sourceType || keyOrType }) : opt)), { useClassLoader: target.final ? false : true })); } } if (this.config.parentContext && opt.deep !== false) { if (!this.config.newInstanceInThisContext || this.config.parentContext.has(key, true, true)) { return this.config.parentContext.get(opt.sourceType || keyOrType, opt); } } var canAutoRegister = (0, utils_1.isClass)(keyOrType) && (this.config.autoRegisterSelf || (0, metadata_1.getMetadata)(keyOrType).injectable); if (canAutoRegister) { this.register(keyOrType); return this.get(keyOrType, opt); } if (this.config.notFoundHandler) { var data = this.config.notFoundHandler(keyOrType); if (data !== undefined) { return data; } } throw new NotfoundTypeError(keyOrType, key); }; IocContext.prototype.resolveConflict = function (type, classes, sourceCls, deep) { var _e; if (this.config.conflictHandler) { var one = this.config.conflictHandler(type, classes, sourceCls); if (one !== undefined) { return one; } } if (deep) { return (_e = this.config.parentContext) === null || _e === void 0 ? void 0 : _e.resolveConflict(type, classes, sourceCls, deep); } }; IocContext.prototype.findClassByClassLoader = function (type, key, opt) { var _this = this; var classes = this.classLoader.getImplementClasses(type); if (classes.length === 0) { return; } if (classes.length === 1) { // class loader is only responsible for matching and not for registration. return { type: classes[0].type, ref: true }; } // if an instance of one of the classes already exists, the match takes precedence var instances = classes.filter(function (ele) { return _this.has(ele.type, true); }); if (instances.length === 1) { return { type: instances[0].type, final: true }; } var resolved = this.resolveConflict(type, classes, opt.sourceCls ? { type: opt.sourceCls, info: this.classLoader.getClassInfo(opt.sourceCls), } : undefined, opt.deep); if (resolved !== undefined) { return { type: resolved, final: true }; } // BaseClass has @injectable if (this.isInjectableBaseClass(type)) { return { type: type, base: true }; } throw new MultiImplementError(type, key); }; IocContext.prototype.isInjectableBaseClass = function (type) { return (0, utils_1.isClass)(type) && (0, metadata_1.getMetadata)(type).injectable; }; /** * get instances for key * @param keyOrType key (super class or interface, use `@classInfo`) * @param opt */ IocContext.prototype.getImports = function (keyOrType, _e) { var _this = this; var _f = _e === void 0 ? {} : _e, cache = _f.cache, deep = _f.deep; var type = keyOrType; if (cache && this.has(type)) { return this.get(type); } if (!this.classLoader) { return []; } var data = this.classLoader.getImplementClasses(type).map(function (clsInfo) { return _this.get(clsInfo.type, { useClassLoader: false, deep: deep }); }); if (cache) { this.register(data, type); } return data; }; /** * instance of key in context * @param keyOrType key * @param deep deep search from parent context * @param useClassLoader use classLoader */ IocContext.prototype.has = function (keyOrType, deep, useClassLoader) { if (deep === void 0) { deep = false; } if (useClassLoader === void 0) { useClassLoader = false; } var key = (0, utils_1.getGlobalType)(keyOrType); if (this.components.has(key)) { return true; } if (useClassLoader && this.config.useClassLoader && this.classLoader) { var target = this.findClassByClassLoader(keyOrType, key, { deep: deep, }); if (target) { return this.has(target.type, deep); } } if (deep && this.config.parentContext) { return this.config.parentContext.has(key, deep, useClassLoader); } return false; }; IocContext.prototype.replace = function (keyOrType, newData, options, registerIfNotExist) { if (registerIfNotExist === void 0) { registerIfNotExist = false; } var key = (0, utils_1.getGlobalType)(keyOrType); var data = this.components.get(key); if (data) { this.components.set(key, this.newStore(newData, options || data.options)); } else if (registerIfNotExist) { this.register(newData, keyOrType, options); } else { throw new NoRegistryError(key); } }; /** * register key * @param data value of key (maybe instance, class, factory function or value) * @param key key * @param options register option */ IocContext.prototype.register = function (data, key, options) { if (options === void 0) { options = exports.DefaultRegisterOption; } if (key) { if (!this.canBeKey(key)) { throw new Error('key require string, symbol or class.'); } } else { if (!this.canBeKey(data)) { throw new Error('when data is not string, symbol or class, require a key.'); } } var dataType = (key && (0, utils_1.getGlobalType)(key)) || (data && (0, utils_1.getGlobalType)(data)); if (this.components.has(dataType)) { throw new AlreadyRegistryError(dataType); } options = __assign(__assign({}, exports.DefaultRegisterOption), options); var store = this.newStore(data, options); this.components.set(dataType, store); }; /** * complete instance inject * @param instance * @param opt */ IocContext.prototype.inject = function (instance, opt) { var _this = this; if (opt === void 0) { opt = { autoRunPostConstruct: true, }; } var iocSelf = this; var classType = instance.constructor; (0, metadata_1.getMetadataField)(classType, 'injects').forEach(function (inject) { var type = inject.type, key = inject.key, typeCls = inject.typeCls, singleton = inject.singleton; var descriptor = Object.getOwnPropertyDescriptor(instance, key); var defaultValue = descriptor && descriptor.value; var isAllowOptional = function () { var _e, _f, _g; return (_g = (_e = inject.optional) !== null && _e !== void 0 ? _e : (_f = _this.config.defaultInjectOptions) === null || _f === void 0 ? void 0 : _f.optional) !== null && _g !== void 0 ? _g : defaultValue !== undefined; }; if ('inject' === type) { Object.defineProperty(instance, key, { configurable: true, writable: true, value: (0, guard_1.guard)(function () { return _this.get(typeCls, __assign({ sourceCls: classType }, (singleton ? {} : { forceNew: true }))); }, { defaultValue: defaultValue, onError: function (err) { if (isAllowOptional() && isError(err, NotfoundTypeError)) { return; } err.message += "\n\tSource: ".concat(classType.name, ".").concat(key.toString()); throw err; }, }), }); } if (['lazyInject', 'imports'].includes(type)) { Object.defineProperty(instance, key, { configurable: true, get: function () { var _e, _f, _g; var always = (_g = (_e = inject.always) !== null && _e !== void 0 ? _e : (_f = iocSelf.config.defaultInjectOptions) === null || _f === void 0 ? void 0 : _f.always) !== null && _g !== void 0 ? _g : false; var hasErr = false; var data = (0, guard_1.guard)(function () { switch (type) { case 'lazyInject': return iocSelf.get(typeCls, __assign({ sourceCls: classType }, (singleton ? {} : { forceNew: true }))); case 'imports': return iocSelf.getImports(typeCls, { cache: !always }); } }, { defaultValue: defaultValue, onError: function (err) { hasErr = true; if (isAllowOptional() && isError(err, NotfoundTypeError)) { return; } err.message += "\n\tSource: ".concat(classType.name, ".").concat(key.toString()); throw err; }, }); if (!hasErr && !always) { Object.defineProperty(this, key, { configurable: true, writable: true, value: data, }); } return data; }, set: function (value) { Object.defineProperty(this, key, { configurable: true, writable: true, value: value, }); }, }); } }); (0, metadata_1.getMetadataField)(classType, 'aspects').forEach(function (aspect) { var oriFn = instance[aspect.key]; var newFn = (0, aspect_1.genAspectWrapper)(_this, aspect.point, oriFn); Object.defineProperty(instance, aspect.key, { configurable: true, value: newFn, }); }); if (opt.autoRunPostConstruct) { this.runPostConstruct(instance); } }; IocContext.prototype.runPostConstruct = function (instance) { Array.from(new Set((0, metadata_1.getMetadataField)(instance.constructor, 'postConstruct').map(function (p) { return p.key; }))).forEach(function (key) { instance[key](); }); }; IocContext.prototype.runPreDestroy = function (instance) { Array.from(new Set((0, metadata_1.getMetadataField)(instance.constructor, 'preDestroy').map(function (p) { return p.key; }))).forEach(function (key) { instance[key](); }); if (this.config.destroyInstanceHook) { this.config.destroyInstanceHook(instance, this); } }; /** * create child context, inherit this context * @param config */ IocContext.prototype.createChildContext = function (config) { return new IocContext(__assign(__assign(__assign({}, this.config), (config || {})), { parentContext: this })); }; /** * run preDestroy method of instance * @param store instance store */ IocContext.prototype.preDestroyInstance = function (store) { if (!store.inited) { return; } this.runPreDestroy(store.value); }; IocContext.prototype.newStore = function (data, options) { var _this = this; var dataIsFunction = typeof data === 'function'; var dataIsClass = dataIsFunction && (0, utils_1.isClass)(data); var needFactory = dataIsFunction && options.autoNew; return { inited: needFactory ? false : true, factory: needFactory ? function () { if (dataIsClass) { var ClsType_1 = data; var args = []; if (_this.config.constructorInject) { var paramTypes = (0, utils_1.getReflectMetadata)('design:paramtypes', ClsType_1); if (paramTypes) { args = paramTypes.map(function (type) { if (type === ClsType_1 || type === undefined || type === null || type === Number || type === Error || type === Object || type === String || type === Boolean || type === Array || type === Function) { return null; } return _this.get(type); }); } } var value = new (ClsType_1.bind.apply(ClsType_1, __spreadArray([void 0], args, false)))(); _this.inject(value); return _this.config.createInstanceHook ? _this.config.createInstanceHook(value, _this) : value; } else { var func = data; return func(_this); } } : undefined, value: needFactory ? undefined : data, options: options, }; }; IocContext.prototype.canBeKey = function (obj) { var type = typeof obj; // ie11 symbol is object if (type === 'object') { return obj.toString() !== '[object Object]'; } return ['function', 'string', 'symbol'].includes(type); }; IocContext.prototype.returnValue = function (data, forceNew) { if (forceNew === void 0) { forceNew = false; } if (data.options.singleton && !forceNew) { return data.inited ? data.value : ((data.inited = true), (data.value = data.factory()), data.value); } else { // TODO use WeakMap collection for destroy return data.factory(); } }; IocContext.prototype.toJSON = function () { return { type: 'power-di.IocContext', message: 'NoSerializable' }; }; return IocContext; }()); exports.IocContext = IocContext; var ErrorTypeSymbol = Symbol('ErrorType'); function isError(err, ErrorType) { return err instanceof ErrorType || err[ErrorTypeSymbol] === ErrorType; } var MultiImplementError = /** @class */ (function (_super) { __extends(MultiImplementError, _super); function MultiImplementError(type, key) { var _this = _super.call(this, "Has multi Classes of implement type: ".concat(type.name, "(").concat((0, utils_1.symbolString)(key), ")")) || this; _this[_a] = MultiImplementError; return _this; } return MultiImplementError; }(Error)); exports.MultiImplementError = MultiImplementError; _a = ErrorTypeSymbol; var NotfoundTypeError = /** @class */ (function (_super) { __extends(NotfoundTypeError, _super); function NotfoundTypeError(type, key) { var _this = _super.call(this, "Notfound type: ".concat(type.name || (0, utils_1.symbolString)(type), "(").concat((0, utils_1.symbolString)(key), ")")) || this; _this[_b] = NotfoundTypeError; return _this; } return NotfoundTypeError; }(Error)); exports.NotfoundTypeError = NotfoundTypeError; _b = ErrorTypeSymbol; var NoRegistryError = /** @class */ (function (_super) { __extends(NoRegistryError, _super); function NoRegistryError(key) { var _this = _super.call(this, "the key:[".concat((0, utils_1.symbolString)(key), "] is no registry.")) || this; _this[_c] = NoRegistryError; return _this; } return NoRegistryError; }(Error)); exports.NoRegistryError = NoRegistryError; _c = ErrorTypeSymbol; var AlreadyRegistryError = /** @class */ (function (_super) { __extends(AlreadyRegistryError, _super); function AlreadyRegistryError(key) { var _this = _super.call(this, "the key:[".concat((0, utils_1.symbolString)(key), "] is already registry.")) || this; _this[_d] = AlreadyRegistryError; return _this; } return AlreadyRegistryError; }(Error)); exports.AlreadyRegistryError = AlreadyRegistryError; _d = ErrorTypeSymbol; //# sourceMappingURL=IocContext.js.map