UNPKG

@triviality/core

Version:
97 lines (87 loc) 3.52 kB
/** * Context for creating new service factories. */ import { ServiceFactory, ServiceTag, SF } from '../ServiceFactory'; import { once } from '../lib'; import { ServiceFunctionReferenceContainer } from '../Container/ServiceFunctionReferenceContainer'; import { TaggedServiceFactoryReference } from '../Value/TaggedServiceFactoryReference'; import { getCurrentContainer, getCurrentFeatureFactory, inCurrentContext } from './GlobalContext'; import { InternalServiceFactoryReference } from '../Value/InternalServiceFactoryReference'; import { ServiceFunctionReferenceContainerInterface } from '../Container/ServiceFunctionReferenceContainerInterface'; import { MergedTaggedServiceFunctionReference } from '../Value/MergedTaggedServiceFunctionReference'; export interface ReferenceContext { /** * Makes a service reference so they can be overridden or decorated when passed as return value * to the service factory. * * @example * const sf = ({reference}) => { * const someService = reference(() => 'hi!'); * return {someService, otherService: () => someService() } * } * * Now someService can be overridden, the service in otherService also overridden. */ reference<S>(sf: ServiceFactory<S>): ServiceFactory<S>; } export const SERVICE_REFERENCE = Symbol.for('SERVICE_REFERENCE'); export const asReference = <T>(sf: SF<T>): SF<T> => { let factory: SF<T> = once(sf); const result: any = () => factory(); /** * Only used when services not called by container factory. */ if (!inCurrentContext()) { return result; } const currentContainer = getCurrentContainer(); /** * Multiple dependency can reference this internal dependency. */ const referencedDependency = new InternalServiceFactoryReference({ factory: sf, feature: getCurrentFeatureFactory(), }); factory = currentContainer.add(referencedDependency); /** * When the service factory is returned by a SF, this function will be called. */ result[SERVICE_REFERENCE] = (builder: ServiceFunctionReferenceContainer, tag: ServiceTag) => { const dependency = new TaggedServiceFactoryReference({ tag, factory, feature: getCurrentFeatureFactory(), }); referencedDependency.addReferenceProxy(dependency); factory = builder.add(dependency); }; return result; }; export const asMergeReference = <T>(reference: () => TaggedServiceFactoryReference): SF<T> => { const result: any = () => { return reference().getProxy()(); }; result[SERVICE_REFERENCE] = (builder: ServiceFunctionReferenceContainer, tag: ServiceTag) => { const dependency = new MergedTaggedServiceFunctionReference( reference(), tag, getCurrentFeatureFactory(), ); /** * Does this re-assignment have any use? */ builder.add(dependency); }; return result; }; export const isServiceReferenced = (factory: any | SF<any>) => { return typeof factory[SERVICE_REFERENCE] === 'function'; }; export const handleServiceReferenced = <T>(builder: ServiceFunctionReferenceContainerInterface, factory: SF<T>, tag: ServiceTag): SF<T> => { return (factory as any)[SERVICE_REFERENCE](builder, tag); }; export const wrapReturnAsReference: <T extends ((...args: any[]) => ServiceFactory<any>)>(toWrap: T) => T = ((toWrap: any) => (...args: any[]): ServiceFactory<any> => asReference(toWrap(...args))) as any; export const createServiceFactoryReferenceContext = (): ReferenceContext => ({ reference: asReference, });