|
|
import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces";
|
|
|
import { each, key } from "./traits";
|
|
|
|
|
|
export interface DescriptorImplArgs<S, T> {
|
|
|
lifetime: ILifetime<T>;
|
|
|
|
|
|
factory: (refs: Record<key, unknown>) => NonNullable<T>;
|
|
|
|
|
|
cleanup?: (item: NonNullable<T>) => void;
|
|
|
|
|
|
overrides?: DescriptorMap<S>;
|
|
|
|
|
|
dependencies?: DepsMap<S>;
|
|
|
}
|
|
|
|
|
|
|
|
|
export class DescriptorImpl<S, T> implements Descriptor<S, T> {
|
|
|
|
|
|
private readonly _overrides?: DescriptorMap<S>;
|
|
|
|
|
|
private readonly _lifetime: ILifetime<T>;
|
|
|
|
|
|
private readonly _factory: (refs: Record<key, unknown>) => NonNullable<T>;
|
|
|
|
|
|
private readonly _cleanup?: (item: NonNullable<T>) => void;
|
|
|
|
|
|
private readonly _deps?: DepsMap<S>;
|
|
|
|
|
|
readonly hasOverrides: boolean;
|
|
|
|
|
|
constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs<S, T>) {
|
|
|
this._lifetime = lifetime;
|
|
|
this._factory = factory;
|
|
|
if (cleanup)
|
|
|
this._cleanup = cleanup;
|
|
|
if (overrides)
|
|
|
this._overrides = overrides;
|
|
|
if (dependencies)
|
|
|
this._deps = dependencies;
|
|
|
|
|
|
this.hasOverrides = !!overrides;
|
|
|
}
|
|
|
|
|
|
activate(context: IActivationContext<S>): NonNullable<T> {
|
|
|
|
|
|
const { has, get, initialize, store } = this._lifetime(context);
|
|
|
|
|
|
if (has())
|
|
|
return get();
|
|
|
|
|
|
initialize();
|
|
|
|
|
|
if (this._overrides)
|
|
|
each(this._overrides, (v, k) => context.register(k, v));
|
|
|
|
|
|
const resolve = <K extends keyof S>({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K] | null; }) => {
|
|
|
if (lazy) {
|
|
|
return "default" in opts ?
|
|
|
() => context.resolve(name, opts.default) :
|
|
|
() => context.resolve(name);
|
|
|
} else {
|
|
|
return "default" in opts ?
|
|
|
context.resolve(name, opts.default) :
|
|
|
context.resolve(name);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const deps = this._deps;
|
|
|
|
|
|
const refs = deps ?
|
|
|
Object.keys(deps)
|
|
|
.map(k => {
|
|
|
const ref = deps[k];
|
|
|
return typeof ref !== "object" ?
|
|
|
{ [k]: resolve({ name: ref }) } :
|
|
|
{ [k]: resolve(ref) };
|
|
|
})
|
|
|
.reduce((a, p) => ({ ...a, ...p }), {}) :
|
|
|
{};
|
|
|
|
|
|
try {
|
|
|
// call the factory method
|
|
|
const instance = (0,this._factory)(refs);
|
|
|
|
|
|
// store the instance
|
|
|
store(instance, this._cleanup);
|
|
|
return instance;
|
|
|
} catch(err) {
|
|
|
context.fail(err);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
toString() {
|
|
|
return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`;
|
|
|
}
|
|
|
}
|
|
|
|