import * as NodePath from "path" ; import * as tsc from "typescript" ; import { HashMap } from "tstl/container/HashMap" ; import { IController } from "../structures/IController" ; import { IRoute } from "../structures/IRoute" ; import { GenericAnalyzer } from "./GenericAnalyzer" ; import { ImportAnalyzer } from "./ImportAnalyzer" ; export namespace ControllerAnalyzer { export function analyze ( checker , sourceFile , controller ) { const ret = [ ] ; tsc . forEachChild ( sourceFile , node => { if ( tsc . isClassDeclaration ( node ) && node . name ?. escapedText === controller . name ) { ret . push ( ... _Analyze_controller ( checker , controller , node ) ) ; return ; } } ) ; return ret ; } function _Analyze_controller ( checker , controller , classNode ) { const ret = [ ] ; const classType = checker . getTypeAtLocation ( classNode ) as tsc . InterfaceType ; const genericDict = GenericAnalyzer . analyze ( checker , classNode ) ; for ( const property of classType . getProperties ( ) ) if ( property . declarations ) for ( const declaration of property . declarations ) { if ( ! tsc . isMethodDeclaration ( declaration ) ) continue ; const identifier = declaration . name ; if ( ! tsc . isIdentifier ( identifier ) ) continue ; const func = controller . functions . find ( f => f . name === identifier . escapedText ) ; if ( func !== undefined ) ret . push ( _Analyze_function ( checker , controller , genericDict , func , declaration ) ) ; } return ret ; } function _Analyze_function ( checker , controller , genericDict , func , declaration ) { const signature = checker . getSignatureFromDeclaration ( declaration ) ; if ( signature === undefined ) throw new Error ( `Error on ControllerAnalyzer._Analyze_function(): unable to get the ignature from the ${ controller . name }.${ func . name }().` ) ; const importDict = new HashMap ( ) ; const parameters = func . parameters . map ( param => _Analyze_parameter ( checker , genericDict , importDict , controller , func . name , param , declaration . parameters [ param . index ] ) ) ; const output = ImportAnalyzer . analyze ( checker , genericDict , importDict , checker . getReturnTypeOfSignature ( signature ) ) ; const imports = importDict . toJSON ( ) . map ( pair => [ pair . first , pair . second . toJSON ( ) ] ) ; let path = NodePath . join ( controller . path , func . path ) . split ( "\\" ) . join ( "/" ) ; if ( path [ 0 ] !== "/" ) path = "/" + path ; if ( path [ path . length - 1 ] === "/" && path !== "/" ) path = path . substr ( 0 , path . length - 1 ) ; return { ... func , path , parameters , output , imports , symbol : `${ controller . name }.${ func . name }()` , comments : signature . getDocumentationComment ( undefined ) , tags : signature . getJsDocTags ( ) } ; } function _Analyze_parameter ( checker , genericDict , importDict , controller , funcName , param , declaration ) { const symbol = checker . getSymbolAtLocation ( declaration . name ) ! ; const type = checker . getTypeOfSymbolAtLocation ( symbol , declaration ) ; const name = symbol . getEscapedName ( ) . toString ( ) ; if ( ( param . category === "query" || param . category === "body" ) && param . field !== undefined ) throw new Error ( `Error on ${ controller . name }.${ funcName }(): parameter ${ name } is specifying a field ${ param . field } of the request ${ param . category } message, however, Nestia does not support the field specialization for the request ${ param . category } message. Erase the ${ controller . name }.${ funcName }()#${ name } parameter and re-define a new decorator accepting full structured message.` ) ; return { name , category : param . category , field : param . field , encrypted : param . encrypted , type : ImportAnalyzer . analyze ( checker , genericDict , importDict , type ) } ; } }