import express from 'express' ; import { HttpMethodMeta , ControllerMeta , getControllerMeta , getHttpMethodMetaList , getHandlerParamMetaList } from './decorators' ; import { BaseController } from './BaseController' ; import { BaseResult } from './results' ; import { Injector } from './Injector' ; export type ConfigSetter = ( app : express . Application ) => void ; export interface TachiJSOptions < C = { } > { app ? : express . Application ; before ? : ConfigSetter ; after ? : ConfigSetter ; controllers ? : any [ ] ; container ? : C ; } export function tachijs < C > ( options ) { const { app = express ( ) , controllers = [ ] , container = { } , before , after } = options ; return new TachiJSApp < C > ( app , controllers , container , before , after ) . build ( ) ; } class TachiJSApp < C > { private readonly injector : Injector < C > ; constructor ( private readonly app , private readonly controllers , readonly container , private readonly before ? , private readonly after ? ) { this . injector = new Injector ( container ) ; } build ( ) { const { app , before , after , controllers } = this ; if ( before != null ) before ( app ) ; controllers . map ( ControllerConstructor => { const controllerMeta = getControllerMeta ( ControllerConstructor ) ; if ( controllerMeta == null ) throw new Error ( `Please apply @controller decorator to "${ ControllerConstructor . name }".` ) ; const router = express . Router ( controllerMeta . routerOptions ) ; this . bindControllerRoutes ( router , ControllerConstructor , controllerMeta ) ; app . use ( controllerMeta . path , router ) ; } ) ; if ( after != null ) after ( app ) ; return app ; } bindControllerRoutes ( router , ControllerConstructor , controllerMeta ) { const methodList = getHttpMethodMetaList ( ControllerConstructor ) ; methodList . map ( methodMeta => { const handler = this . makeRequestHandler ( ControllerConstructor , methodMeta ) ; this . bindHandler ( router , controllerMeta , methodMeta , handler ) ; } ) ; } makeRequestHandler ( ControllerConstructor , methodMeta ) { return async ( req , res , next ) => { try { const controller = this . injector . instantiate ( ControllerConstructor ) ; if ( controller instanceof BaseController ) { controller . context = { req , res , inject : ( key ) => { return this . injector . inject ( key ) ; } } ; } const method = controller [ methodMeta . propertyKey ] ; const paramMetaList = getHandlerParamMetaList ( controller . constructor , methodMeta . propertyKey ) ; const args = [ ] ; await Promise . all ( paramMetaList . map ( async ( paramMeta ) => { args [ paramMeta . index ] = await paramMeta . selector ( req , res , next , paramMeta ) ; } ) ) ; const result = await method . bind ( controller ) ( ... args ) ; if ( result instanceof BaseResult ) { await result . execute ( req , res , next ) ; return ; } res . send ( result ) ; return ; } catch ( error ) { next ( error ) ; return ; } } ; } bindHandler ( router , controllerMeta , methodMeta , handler ) { const middlewares = [ ... controllerMeta . middlewares , ... methodMeta . middlewares ] ; switch ( methodMeta . method ) { case 'get' : router . get ( methodMeta . path , middlewares , handler ) ; break ; case 'post' : router . post ( methodMeta . path , middlewares , handler ) ; break ; case 'put' : router . put ( methodMeta . path , middlewares , handler ) ; break ; case 'patch' : router . patch ( methodMeta . path , middlewares , handler ) ; break ; case 'delete' : router . delete ( methodMeta . path , middlewares , handler ) ; break ; case 'options' : router . options ( methodMeta . path , middlewares , handler ) ; break ; case 'head' : router . head ( methodMeta . path , middlewares , handler ) ; break ; case 'all' : router . all ( methodMeta . path , middlewares , handler ) ; break ; default : throw new Error ( `"${ methodMeta . method }" is not a valid method.` ) ; } } }