import * as ts from "typescript" ; import { Context } from "../contexts/Context" ; import { GENERIC_PARAMS , PACKAGE_ID } from "../helpers" ; import { getGenericParametersDetails } from "../getGenericParametersDetails" ; import { log } from "../log" ; const InstanceKey = Symbol . for ( "tst-reflect.DeclarationVisitor" ) ; let instance = ( global as any ) [ InstanceKey ] || null ; export default class DeclarationVisitor { /**
     * Get singleton instance of DeclarationVisitor.
     */ static get instance ( ) : DeclarationVisitor { if ( ! instance ) { instance = Reflect . construct ( DeclarationVisitor , [ ] , Activator ) ; } return instance ; } param node param context visitDeclaration ( node , context ) { if ( ( ts . isMethodDeclaration ( node ) || ts . isFunctionDeclaration ( node ) ) ) { if ( node . body === undefined ) { if ( context . config . debugMode ) { log . info ( "Visiting declaration without body." ) ; } return undefined ; } const genericParametersDetails = getGenericParametersDetails ( node , context , [ ] ) ; if ( ! genericParametersDetails . usedGenericParameters ) { return node ; } return DeclarationVisitor . modifyDeclaration ( node ) ; } return node ; } param node private private static modifyDeclaration ( node ) { const [ modParams , modBody ] = DeclarationVisitor . getModifiedDeclarationProperties ( node . parameters , node . body ! ) ; if ( ts . isMethodDeclaration ( node ) ) { return ts . factory . updateMethodDeclaration ( node , node . decorators , node . modifiers , node . asteriskToken , node . name , node . questionToken , node . typeParameters , modParams , node . type , modBody ) ; } return ts . factory . updateFunctionDeclaration ( node , node . decorators , node . modifiers , node . asteriskToken , node . name , node . typeParameters , modParams , node . type , modBody ) ; } param parameters param body private private static getModifiedDeclarationProperties ( parameters , body ) { const lastParam = parameters [ parameters . length - 1 ] ; if ( ! lastParam || lastParam . dotDotDotToken === undefined ) { return [ [ ... parameters , ts . factory . createParameterDeclaration ( undefined , undefined , undefined , ts . factory . createIdentifier ( GENERIC_PARAMS ) ) ] , body ] ; } else if ( ts . isIdentifier ( lastParam . name ) ) { body = ts . factory . createBlock ( [ ts . factory . createVariableStatement ( undefined , [ ts . factory . createVariableDeclaration ( GENERIC_PARAMS , undefined , undefined , ts . factory . createElementAccessExpression ( ts . factory . createCallExpression ( ts . factory . createPropertyAccessExpression ( ts . factory . createIdentifier ( lastParam . name . escapedText . toString ( ) ) , ts . factory . createIdentifier ( "splice" ) ) , undefined , [ ts . factory . createBinaryExpression ( ts . factory . createPropertyAccessExpression ( ts . factory . createIdentifier ( lastParam . name . escapedText . toString ( ) ) , ts . factory . createIdentifier ( "length" ) ) , ts . SyntaxKind . MinusToken , ts . factory . createNumericLiteral ( 1 ) ) , ts . factory . createNumericLiteral ( 1 ) ] ) , 0 ) ) ] ) , ... body . statements ] ) ; return [ [ ... parameters ] , body ] ; } else if ( ts . isArrayBindingPattern ( lastParam . name ) ) { throw new Error ( `${ PACKAGE_ID }: ArrayBindingPattern not supported in generic declarations yet.` ) ; } else if ( ts . isObjectBindingPattern ( lastParam . name ) ) { throw new Error ( `${ PACKAGE_ID }: ObjectBindingPattern not supported in generic declarations yet.` ) ; } return [ [ ... parameters ] , body ] ; } } class Activator extends DeclarationVisitor { }