UNPKG

reactant-di

Version:

A dependency injection lib for Reactant

121 lines (118 loc) 3.75 kB
import { decorate, injectable as injectable$1 } from 'inversify'; import { METADATA_KEY } from '../constants.js'; import { setMetadata } from '../util.js'; import { inject } from './inject.js'; import { optional } from './optional.js'; import { multiInject } from './multiInject.js'; import { multiOptional } from './multiOptional.js'; /** * ## Description * * You can use `@injectable()` to decorate an injectable module, which will also allow `emitDecoratorMetadata` to take effect in the decorated class, so the corresponding `@inject()` is optional. * * But if you don't want to use `@injectable()`, then the dependency injection decorator for constructors such as `@inject()` is required, and it must be imported in the corresponding `modules` startup configuration. Therefore, in most cases, it is recommended to use `@injectable()`. * * ## Example * * ```ts * @injectable() * class Bar { * getValue() { * return 'bar'; * } * } * * class Foo { * constructor(@inject() public bar: Bar) {} * } * * @injectable() * class FooBar { * constructor(public bar: Bar, public foo: Foo) {} * } * * const fooBar = testBed({ * modules: [ * Foo // `Foo` is required, but `Bar` will be injected automatically. * ], * main: FooBar, * }); * * expect(fooBar.instance.foo.getValue()).toBe('foo'); * ``` * * If you use JavaScript, then you can only use `@injectable()` to define the full dependency metadata. * * ```js * @injectable() * class Bar { * getValue() { * return 'bar'; * } * } * * @injectable() * class Foo { * getValue() { * return 'foo'; * } * } * * @injectable({ * deps: [Bar, { provide: 'foo' }], * }) * class FooBar { * constructor(bar, foo) { * this.bar = bar; * this.foo = foo; * } * } * * const fooBar = testBed({ * modules: [ * Bar, * { provide: 'foo', useClass: Foo }, * ], * main: FooBar, * }); * * expect(fooBar.instance.foo.getValue()).toBe('foo'); * ``` */ function injectable(options) { if (options === void 0) { options = {}; } return function (target) { var _a = options.deps, deps = _a === void 0 ? [] : _a; deps.forEach(function (option, index) { if (typeof option === 'function') { decorate(inject(option), target, index); } else if (toString.call(option) === '[object Object]') { if (typeof option.provide === 'undefined' || option.provide === null) { throw new Error("@injectable ".concat(option ? JSON.stringify(option) : option, " option error, please set a valid value for 'provide'")); } if (option.optional && !option.multi) { decorate(optional(option.provide), target, index); } else if (option.multi && !option.optional) { decorate(multiInject(option.provide), target, index); } else if (option.multi && option.optional) { decorate(multiOptional(option.provide), target, index); } else { decorate(inject(option.provide), target, index); } } else { throw new Error("@injectable ".concat(option === null || option === void 0 ? void 0 : option.toString(), " option error")); } }); // it has to use `Reflect.getMetadata` with metadata, it just get all injectable deps. // so add the services set for `injectable` services. setMetadata(METADATA_KEY.provide, target, target); decorate(injectable$1(), target); return target; }; } export { injectable };