@travetto/di
Version:
Dependency registration/management and injection support.
79 lines (67 loc) • 2.66 kB
text/typescript
import type { RegistryAdapter } from '@travetto/registry';
import { type Class, classConstruct, describeFunction, safeAssign } from '@travetto/runtime';
import { CONSTRUCTOR_PROPERTY, SchemaRegistryIndex } from '@travetto/schema';
import { type InjectableConfig, getDefaultQualifier, type InjectableCandidate } from '../types.ts';
function combineInjectableCandidates<T extends InjectableCandidate>(base: T, ...overrides: Partial<T>[]): typeof base {
for (const override of overrides) {
safeAssign(base, override);
}
return base;
}
function combineClasses<T extends InjectableConfig>(base: T, ...overrides: Partial<T>[]): typeof base {
for (const override of overrides) {
Object.assign(base, {
...base,
...override,
candidates: {
...base.candidates,
...override.candidates,
}
});
}
return base;
}
export class DependencyRegistryAdapter implements RegistryAdapter<InjectableConfig> {
#cls: Class;
#config: InjectableConfig;
constructor(cls: Class) {
this.#cls = cls;
}
register(...data: Partial<InjectableConfig<unknown>>[]): InjectableConfig<unknown> {
this.#config ??= { class: this.#cls, candidates: {} };
return combineClasses(this.#config, ...data);
}
registerFactory(method: string, ...data: Partial<InjectableCandidate<unknown>>[]): InjectableCandidate {
const { candidates } = this.register();
candidates[method] ??= {
class: this.#cls,
method,
enabled: true,
factory: undefined!,
candidateType: undefined!,
};
return combineInjectableCandidates(candidates[method], ...data);
}
registerClass(...data: Partial<InjectableCandidate<unknown>>[]): InjectableCandidate {
return this.registerFactory(CONSTRUCTOR_PROPERTY, ...data, {
factory: (...args: unknown[]) => classConstruct(this.#cls, args),
candidateType: this.#cls,
});
}
get(): InjectableConfig<unknown> {
return this.#config;
}
finalize(): void {
for (const method of Object.keys(this.#config.candidates)) {
const candidate = this.#config.candidates[method];
const candidateType = SchemaRegistryIndex.get(candidate.class).getMethodReturnType(method);
candidate.candidateType = candidateType;
candidate.qualifier ??= getDefaultQualifier(candidateType);
}
}
getCandidateConfigs(): InjectableCandidate[] {
return Object.values(this.#config.candidates)
.filter(item => (item.enabled ?? true) === true || (typeof item.enabled === 'function' && item.enabled()))
.filter(item => item.method !== CONSTRUCTOR_PROPERTY || !describeFunction(item.candidateType)?.abstract);
}
}