import { Container, injectable, Token } from 'ditox'; import { Controller } from './controller'; import { Query } from './query'; import { createScope, Scope } from './scope'; import { AnyObject } from './utils'; export function createController( factory: (scope: Scope) => Service & { destroy?: () => void }, ): Controller { const scope = createScope(); const controller = factory(scope); return { ...controller, destroy: () => { controller.destroy?.(); scope.destroy(); }, }; } export type ControllerFactory = ( container: Container, ) => Controller; declare type DependencyProps = { [key: string]: unknown; }; declare type TokenProps = { [K in keyof Props]: Token; }; export function declareController< Dependencies extends DependencyProps, Service extends AnyObject, >( tokens: TokenProps, factory: (deps: Dependencies, scope: Scope) => Service, ): ControllerFactory { return injectable( (deps) => createController((scope) => factory(deps as Dependencies, scope)), tokens, ); } export type ViewControllerFactory< Service extends AnyObject, Params extends Query[], > = (container: Container, ...params: Params) => Controller; export type InferredService = Factory extends ViewControllerFactory< infer Service, // eslint-disable-next-line @typescript-eslint/no-unused-vars infer Params > ? Service : never; export function declareViewController< Service extends AnyObject, Params extends Query[], >( factory: (scope: Scope, ...params: Params) => Service, ): ViewControllerFactory; export function declareViewController< Dependencies extends DependencyProps, Service extends AnyObject, Params extends Query[], >( tokens: TokenProps, factory: ( deps: Dependencies, scope: Scope, ) => ((scope: Scope, ...params: Params) => Service) | Service, ): ViewControllerFactory; export function declareViewController< Dependencies extends DependencyProps, Service extends AnyObject, Params extends Query[], Factory extends (scope: Scope, ...params: Params) => Service, FactoryWithDependencies extends | ((deps: Dependencies, scope: Scope) => Service) | (( deps: Dependencies, scope: Scope, ) => (scope: Scope, ...params: Params) => Service), >( tokensOrFactory: TokenProps | Factory, factory?: FactoryWithDependencies, ): ViewControllerFactory { return (container: Container, ...params: Params) => { if (typeof tokensOrFactory === 'function') { return createController((scope) => { return tokensOrFactory(scope, ...params); }); } return injectable((dependencies) => { return createController((scope) => { const factoryValue = factory as FactoryWithDependencies; const result = factoryValue(dependencies as Dependencies, scope); if (typeof result === 'function') { return result(scope, ...params); } return result; }); }, tokensOrFactory)(container); }; }