@travetto/di
Version:
Dependency registration/management and injection support.
80 lines (72 loc) • 2.74 kB
text/typescript
import { asConstructable, asFull, TypedFunction, type Class } from '@travetto/runtime';
import { InjectableFactoryConfig, InjectableConfig, Dependency } from './types';
import { DependencyRegistry, ResolutionType } from './registry';
function collapseConfig<T extends { qualifier?: symbol }>(...args: (symbol | Partial<InjectConfig> | undefined)[]): T {
let out: Partial<T> = {};
if (args) {
if (Array.isArray(args)) {
for (const arg of args) {
if (typeof arg === 'symbol') {
out.qualifier = arg;
} else if (arg) {
Object.assign(out, arg);
}
}
} else {
out = args;
}
}
return asFull(out);
}
/**
* Indicate that a class is able to be injected
*
* @augments `@travetto/di:Injectable`
*/
export function Injectable(first?: Partial<InjectableConfig> | symbol, ...args: (Partial<InjectableConfig> | undefined)[]) {
return <T extends Class>(target: T): T => {
const config = {
...collapseConfig<Partial<InjectableConfig>>(first, ...args),
class: target
};
DependencyRegistry.registerClass(target, config);
return target;
};
}
export type InjectConfig = { qualifier?: symbol, optional?: boolean, resolution?: ResolutionType };
export function InjectArgs(configs?: InjectConfig[][]) {
return <T extends Class>(target: T): void => {
DependencyRegistry.registerConstructor(target, configs?.map(x => collapseConfig(...x)));
};
}
/**
* Indicate that a field is able to be injected
*
* @augments `@travetto/di:Inject`
*/
export function Inject(first?: InjectConfig | symbol, ...args: (InjectConfig | undefined)[]) {
return (target: unknown, propertyKey?: string, idx?: number | PropertyDescriptor): void => {
if (typeof idx !== 'number') { // Only register if on property
const config = collapseConfig<Dependency>(first, ...args);
DependencyRegistry.registerProperty(
asConstructable(target).constructor, propertyKey!, config
);
}
};
}
/**
* Identifies a static method that is able to produce a dependency
*
* @augments `@travetto/di:InjectableFactory`
*/
export function InjectableFactory(first?: Partial<InjectableFactoryConfig> | symbol, ...args: (Partial<InjectableFactoryConfig> | undefined)[]) {
return <T extends Class>(target: T, property: string | symbol, descriptor: TypedPropertyDescriptor<TypedFunction>): void => {
const config: InjectableFactoryConfig = collapseConfig(first, ...args);
DependencyRegistry.registerFactory({
...config,
dependencies: config.dependencies?.map(x => Array.isArray(x) ? collapseConfig(...x) : collapseConfig(x)),
fn: descriptor.value!,
id: `${target.Ⲑid}#${property.toString()}`
});
};
}