power-di
Version:
A lightweight Dependency Injection library. Using es6 and other features, remove unnecessary concepts, easy and convenient to use.
515 lines • 21.4 kB
JavaScript
"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));
};
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 (this.config.notFoundHandler) {
var data = this.config.notFoundHandler(keyOrType);
if (data !== undefined) {
return data;
}
}
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);
}
throw new NotfoundTypeError(keyOrType, key);
};
IocContext.prototype.resolveConflict = function (type, classes, sourceCls, deep) {
var _a;
if (this.config.conflictHandler) {
var one = this.config.conflictHandler(type, classes, sourceCls);
if (one !== undefined) {
return one;
}
}
if (deep) {
return (_a = this.config.parentContext) === null || _a === void 0 ? void 0 : _a.resolveConflict(type, classes, sourceCls);
}
};
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, _a) {
var _this = this;
var _b = _a === void 0 ? {} : _a, cache = _b.cache, deep = _b.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);
}
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 key = inject.key, typeCls = inject.typeCls, optional = inject.optional, singleton = inject.singleton;
var descriptor = Object.getOwnPropertyDescriptor(instance, key);
var defaultValue = descriptor && descriptor.value;
var allowOptional = optional || defaultValue !== undefined;
if ('inject' === 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 (!allowOptional) {
err.message += "\n\tSource: ".concat(classType.name, ".").concat(key.toString());
throw err;
}
},
}),
});
}
if (['lazyInject', 'imports'].includes(inject.type)) {
var always_1 = inject.always;
Object.defineProperty(instance, key, {
configurable: true,
get: function () {
var hasErr = false;
var data = (0, guard_1.guard)(function () {
switch (inject.type) {
case 'lazyInject':
return iocSelf.get(typeCls, __assign({ sourceCls: classType }, (singleton ? {} : { forceNew: true })));
case 'imports':
return iocSelf.getImports(typeCls, { cache: !always_1 });
}
}, {
defaultValue: defaultValue,
onError: function (err) {
hasErr = true;
if (!allowOptional) {
err.message += "\n\tSource: ".concat(classType.name, ".").concat(key.toString());
throw err;
}
},
});
if (!hasErr && !always_1) {
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]();
});
};
/**
* 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 MultiImplementError = /** @class */ (function (_super) {
__extends(MultiImplementError, _super);
function MultiImplementError(type, key) {
return _super.call(this, "Has multi Classes of implement type: ".concat(type.name, "(").concat((0, utils_1.symbolString)(key), ")")) || this;
}
return MultiImplementError;
}(Error));
exports.MultiImplementError = MultiImplementError;
var NotfoundTypeError = /** @class */ (function (_super) {
__extends(NotfoundTypeError, _super);
function NotfoundTypeError(type, key) {
return _super.call(this, "Notfound type: ".concat(type.name || (0, utils_1.symbolString)(type), "(").concat((0, utils_1.symbolString)(key), ")")) || this;
}
return NotfoundTypeError;
}(Error));
exports.NotfoundTypeError = NotfoundTypeError;
var NoRegistryError = /** @class */ (function (_super) {
__extends(NoRegistryError, _super);
function NoRegistryError(key) {
return _super.call(this, "the key:[".concat((0, utils_1.symbolString)(key), "] is no registry.")) || this;
}
return NoRegistryError;
}(Error));
exports.NoRegistryError = NoRegistryError;
var AlreadyRegistryError = /** @class */ (function (_super) {
__extends(AlreadyRegistryError, _super);
function AlreadyRegistryError(key) {
return _super.call(this, "the key:[".concat((0, utils_1.symbolString)(key), "] is already registry.")) || this;
}
return AlreadyRegistryError;
}(Error));
exports.AlreadyRegistryError = AlreadyRegistryError;
//# sourceMappingURL=IocContext.js.map