reactant-di
Version:
A dependency injection lib for Reactant
209 lines (206 loc) • 8.21 kB
JavaScript
import { __values, __extends, __spreadArray, __read } from './node_modules/tslib/tslib.es6.js';
import 'reflect-metadata';
import { decorate, ContainerModule, MetadataReader, Container as Container$1 } from 'inversify';
import { getMetadata, lookupOptionalIdentifier, setModulesDeps } from './util.js';
import { createCollector } from './middlewares/collector.js';
import { METADATA_KEY } from './constants.js';
import { defaultUndefinedValue } from './optional.js';
import { ModuleRef } from './moduleRef.js';
import { injectable } from './decorators/injectable.js';
import { inject } from './decorators/inject.js';
var CustomMetadataReader = /** @class */ (function (_super) {
__extends(CustomMetadataReader, _super);
function CustomMetadataReader() {
return _super !== null && _super.apply(this, arguments) || this;
}
CustomMetadataReader.prototype.getConstructorMetadata = function (constructorFunc) {
var constructorMetadata = _super.prototype.getConstructorMetadata.call(this, constructorFunc);
// TODO: hook
return constructorMetadata;
};
return CustomMetadataReader;
}(MetadataReader));
function autoBindModules() {
return new ContainerModule(function (bind, unbind, isBound) {
var e_1, _a;
var provideMeta = getMetadata(METADATA_KEY.provide);
var optionalMeta = getMetadata(METADATA_KEY.optional);
try {
for (var provideMeta_1 = __values(provideMeta), provideMeta_1_1 = provideMeta_1.next(); !provideMeta_1_1.done; provideMeta_1_1 = provideMeta_1.next()) {
var _b = __read(provideMeta_1_1.value, 2), identifier = _b[0], provide = _b[1];
// default injection without optional module.
if ((!optionalMeta.has(identifier) ||
lookupOptionalIdentifier(identifier)) &&
!isBound(identifier)) {
bind(identifier).to(provide);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (provideMeta_1_1 && !provideMeta_1_1.done && (_a = provideMeta_1.return)) _a.call(provideMeta_1);
}
finally { if (e_1) throw e_1.error; }
}
});
}
function isClassProvider(module) {
return typeof module.useClass === 'function';
}
function isFactoryProvider(module) {
return typeof module.useFactory === 'function';
}
/**
* It ensures that the parameters of all modules from the configuration are decorated.
*
* class Foo {
* constructor(bar: Bar) {}
* }
*
* Equate to:
*
* class Foo {
* constructor(@inject() bar: Bar) {}
* }
*
* @param target {object} decorated target.
*/
function autoDecorateParams(target) {
var metadata = Reflect.getMetadata(METADATA_KEY.inversifyParamtypes, target);
metadata.forEach(function (_, index) {
if (!Reflect.getMetadata(METADATA_KEY.inversifyTagged, target) ||
!Reflect.getMetadata(METADATA_KEY.inversifyTagged, target)[index]) {
inject()(target, undefined, index);
}
});
}
var Container = /** @class */ (function (_super) {
__extends(Container, _super);
function Container(options, _serviceIdentifiers) {
var _this = _super.call(this, options) || this;
_this._serviceIdentifiers = _serviceIdentifiers;
return _this;
}
/**
* get the loaded module
*/
Container.prototype.got = function (serviceIdentifier) {
try {
return this._serviceIdentifiers.has(serviceIdentifier)
? this.get(serviceIdentifier)
: undefined;
}
catch (error) {
if (process.env.NODE_ENV !== 'production') {
console.warn(error);
}
return undefined;
}
};
/**
* get loaded modules
*/
Container.prototype.gotAll = function (serviceIdentifier) {
try {
return this._serviceIdentifiers.has(serviceIdentifier)
? this.getAll(serviceIdentifier)
: undefined;
}
catch (error) {
if (process.env.NODE_ENV !== 'production') {
console.warn(error);
}
return undefined;
}
};
return Container;
}(Container$1));
function bindModules(container, modules) {
var e_2, _a;
var provideMeta = getMetadata(METADATA_KEY.provide);
var _loop_1 = function (module_1) {
if (typeof module_1 === 'function') {
// auto decorate `@injectable` for module.
if (!provideMeta.has(module_1))
decorate(injectable(), module_1);
autoDecorateParams(module_1);
container.bind(module_1).toSelf();
}
else if (typeof module_1 === 'object') {
if (isClassProvider(module_1)) {
// auto decorate `@injectable` for module.useClass
if (!provideMeta.has(module_1.useClass))
decorate(injectable(), module_1.useClass);
autoDecorateParams(module_1.useClass);
container.bind(module_1.provide).to(module_1.useClass);
}
else if (Object.hasOwnProperty.call(module_1, 'useValue')) {
container
.bind(module_1.provide)
.toConstantValue(module_1.useValue);
}
else if (isFactoryProvider(module_1)) {
container
.bind(module_1.provide)
.toFactory(function (context) {
var deps = module_1.deps || [];
var depInstances = deps.map(function (identifier) {
// TODO: refactor with `is` assertion
var provide = identifier.provide ||
identifier;
if (identifier.optional &&
!context.container.isBound(identifier.provide)) {
return undefined;
}
return context.container.get(provide);
});
return module_1.useFactory.apply(module_1, __spreadArray([], __read(depInstances), false));
});
}
else if (typeof module_1.provide === 'function') {
// auto decorate `@injectable` for module.provide
if (!provideMeta.has(module_1.provide))
decorate(injectable(), module_1.provide);
autoDecorateParams(module_1.provide);
container.bind(module_1.provide).toSelf();
}
else {
throw new Error("".concat(module_1, " option error"));
}
}
else {
throw new Error("".concat(module_1, " option error"));
}
};
try {
for (var modules_1 = __values(modules), modules_1_1 = modules_1.next(); !modules_1_1.done; modules_1_1 = modules_1.next()) {
var module_1 = modules_1_1.value;
_loop_1(module_1);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (modules_1_1 && !modules_1_1.done && (_a = modules_1.return)) _a.call(modules_1);
}
finally { if (e_2) throw e_2.error; }
}
// load modules with `@injectable` decoration, but without `@optional` decoration.
container.load(autoBindModules());
}
function createContainer(_a) {
var ServiceIdentifiers = _a.ServiceIdentifiers, _b = _a.modules, modules = _b === void 0 ? [] : _b, options = _a.options;
setModulesDeps(modules);
var container = new Container(options, ServiceIdentifiers);
container.applyCustomMetadataReader(new CustomMetadataReader());
bindModules(container, modules);
container.applyMiddleware(createCollector(ServiceIdentifiers));
if (container.isBound(ModuleRef)) {
container.unbind(ModuleRef);
}
container.bind(ModuleRef).toConstantValue(container);
container.bind(defaultUndefinedValue).toConstantValue(undefined);
return container;
}
export { Container, bindModules, createContainer };