|
|
import { Descriptor, ILifetime, IActivationContext, DescriptorMap, DescriptorDepsMap } from "../typings/interfaces";
|
|
|
import { ActivationError, ActivationItem } from "./ActivationError";
|
|
|
import { each, key } from "./traits";
|
|
|
|
|
|
export interface DescriptorImplArgs<S, T> {
|
|
|
|
|
|
readonly lifetime: ILifetime<T>;
|
|
|
|
|
|
readonly factory: (refs: Record<key, unknown>) => NonNullable<T>;
|
|
|
|
|
|
readonly cleanup?: (item: NonNullable<T>) => void;
|
|
|
|
|
|
readonly overrides?: DescriptorMap<S>;
|
|
|
|
|
|
readonly dependencies?: DescriptorDepsMap<S>;
|
|
|
}
|
|
|
|
|
|
export class DescriptorImpl<S, T> implements Descriptor<S, T> {
|
|
|
|
|
|
private readonly _overrides: DescriptorMap<S> | undefined;
|
|
|
|
|
|
private readonly _lifetime: ILifetime<T>;
|
|
|
|
|
|
private readonly _factory: (refs: Record<key, unknown>) => NonNullable<T>;
|
|
|
|
|
|
private readonly _cleanup: (item: NonNullable<T>) => void;
|
|
|
|
|
|
private readonly _dependencies: DescriptorDepsMap<S> | undefined;
|
|
|
|
|
|
constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs<S, T>) {
|
|
|
this._lifetime = lifetime;
|
|
|
this._factory = factory;
|
|
|
this._cleanup = cleanup ? cleanup : () => { };
|
|
|
this._overrides = overrides;
|
|
|
this._dependencies = dependencies;
|
|
|
}
|
|
|
|
|
|
activate(context: IActivationContext<S>, stack: ActivationItem[]): NonNullable<T> {
|
|
|
|
|
|
const slot = this._lifetime(context);
|
|
|
if (slot.has())
|
|
|
return slot.get();
|
|
|
|
|
|
if (!slot.initialize())
|
|
|
throw new Error("Cyclic reference detected");
|
|
|
|
|
|
const instance = this._overrides ?
|
|
|
context.withOverrides(this._overrides, () => this._activate(context, stack)) :
|
|
|
this._activate(context, stack);
|
|
|
|
|
|
slot.store(instance, this._cleanup);
|
|
|
|
|
|
return instance;
|
|
|
}
|
|
|
|
|
|
private _activate(context: IActivationContext<S>, stack: ActivationItem[]) {
|
|
|
const refs: Record<key, unknown> = {};
|
|
|
if (this._dependencies) {
|
|
|
const resolve = <K extends keyof S>({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K]; }) => {
|
|
|
if (lazy) {
|
|
|
return "default" in opts ?
|
|
|
() => context.resolve(name, stack, opts.default) :
|
|
|
() => context.resolve(name, stack);
|
|
|
} else {
|
|
|
return "default" in opts ?
|
|
|
context.resolve(name, stack, opts.default) :
|
|
|
context.resolve(name, stack);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// can throw activation exception
|
|
|
each(this._dependencies, (v, k) => {
|
|
|
refs[k] = resolve(v);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
// call the factory method
|
|
|
return (0, this._factory)(refs);
|
|
|
} catch (e) {
|
|
|
throw new ActivationError("Error creating instance", stack, e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
toString() {
|
|
|
return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`;
|
|
|
}
|
|
|
}
|
|
|
|