import * as ts from "typescript" ; /**
 * Typescript AST helper functions.
 */ export namespace Ast { export function modifiersToFlags ( modifiers ) { let flags = ts . ModifierFlags . None ; if ( modifiers ) { for ( const modifier of modifiers ) { flags |= modifierToFlag ( modifier . kind ) ; } } return flags ; } export function modifierToFlag ( token ) { switch ( token ) { case ts . SyntaxKind . StaticKeyword : return ts . ModifierFlags . Static ; case ts . SyntaxKind . PublicKeyword : return ts . ModifierFlags . Public ; case ts . SyntaxKind . ProtectedKeyword : return ts . ModifierFlags . Protected ; case ts . SyntaxKind . PrivateKeyword : return ts . ModifierFlags . Private ; case ts . SyntaxKind . AbstractKeyword : return ts . ModifierFlags . Abstract ; case ts . SyntaxKind . ExportKeyword : return ts . ModifierFlags . Export ; case ts . SyntaxKind . DeclareKeyword : return ts . ModifierFlags . Ambient ; case ts . SyntaxKind . ConstKeyword : return ts . ModifierFlags . Const ; case ts . SyntaxKind . DefaultKeyword : return ts . ModifierFlags . Default ; case ts . SyntaxKind . AsyncKeyword : return ts . ModifierFlags . Async ; case ts . SyntaxKind . ReadonlyKeyword : return ts . ModifierFlags . Readonly ; } return ts . ModifierFlags . None ; } /**
     * Gets the ModifierFlags for syntactic modifiers on the provided node. The modifier flags cache on the node is ignored.
     *
     * NOTE: This function does not use `parent` pointers and will not include modifiers from JSDoc.
     */ export function getSyntacticModifierFlagsNoCache ( node ) { let flags = modifiersToFlags ( node . modifiers ) ; if ( node . flags & ts . NodeFlags . NestedNamespace || ( node . kind === ts . SyntaxKind . Identifier && ( < ts . Identifier > node ) . isInJSDocNamespace ) ) { flags |= ts . ModifierFlags . Export ; } return flags ; } export const enum ContainerFlags { None = 0 , IsContainer = 1 << 0 , IsBlockScopedContainer = 1 << 1 , IsControlFlowContainer = 1 << 2 , IsFunctionLike = 1 << 3 , IsFunctionExpression = 1 << 4 , HasLocals = 1 << 5 , IsInterface = 1 << 6 , IsObjectLiteralOrClassExpressionMethod = 1 << 7 , IsContainerWithLocals = IsContainer | HasLocals } export function isPrototypeAccessAssignment ( expression ) { if ( expression . kind !== ts . SyntaxKind . BinaryExpression ) { return false ; } const expr = < ts . BinaryExpression > expression ; if ( expr . operatorToken . kind !== ts . SyntaxKind . EqualsToken || expr . left . kind !== ts . SyntaxKind . PropertyAccessExpression ) { return false ; } const lhs = < ts . PropertyAccessExpression > expr . left ; if ( lhs . expression . kind === ts . SyntaxKind . PropertyAccessExpression ) { const innerPropertyAccess = < ts . PropertyAccessExpression > lhs . expression ; if ( innerPropertyAccess . expression . kind === ts . SyntaxKind . Identifier && innerPropertyAccess . name . text === "prototype" ) { return true ; } } return false ; } export function isFunctionLike ( node ) { if ( node ) { switch ( node . kind ) { case ts . SyntaxKind . Constructor : case ts . SyntaxKind . FunctionExpression : case ts . SyntaxKind . FunctionDeclaration : case ts . SyntaxKind . ArrowFunction : case ts . SyntaxKind . MethodDeclaration : case ts . SyntaxKind . MethodSignature : case ts . SyntaxKind . GetAccessor : case ts . SyntaxKind . SetAccessor : case ts . SyntaxKind . CallSignature : case ts . SyntaxKind . ConstructSignature : case ts . SyntaxKind . IndexSignature : case ts . SyntaxKind . FunctionType : case ts . SyntaxKind . ConstructorType : return true ; } } return false ; } export function isObjectLiteralOrClassExpressionMethod ( node ) { return node . kind === ts . SyntaxKind . MethodDeclaration && ( node . parent . kind === ts . SyntaxKind . ObjectLiteralExpression || node . parent . kind === ts . SyntaxKind . ClassExpression ) ; } export function isInterfaceInternal ( symbol ) { return false ; } export function isClassInternal ( symbol ) { if ( symbol && ( symbol . flags & ts . SymbolFlags . Class ) ) { if ( Ast . isExportContext ( symbol ) || Ast . isAmbientContext ( symbol ) ) { return false ; } let flags = getSyntacticModifierFlagsNoCache ( symbol . valueDeclaration ) ; if ( ! ( flags & ts . ModifierFlags . Export ) ) { return true ; } } return false ; } export function isClassAbstract ( classSymbol ) { if ( classSymbol && classSymbol . valueDeclaration ) { if ( getSyntacticModifierFlagsNoCache ( classSymbol . valueDeclaration ) & ts . ModifierFlags . Abstract ) { return true ; } } return false ; } export function getClassHeritageProperties ( classNode , checker ) { let classExportProperties = [ ] ; function getHeritageExportProperties ( heritageClause , checker ) { const inheritedTypeNodes = heritageClause . types ; if ( inheritedTypeNodes ) { for ( const typeRefNode of inheritedTypeNodes ) { const inheritedType = checker . getTypeAtLocation ( typeRefNode ) ; let inheritedTypeDeclaration = inheritedType . symbol . valueDeclaration ; if ( inheritedTypeDeclaration ) { let inheritedTypeHeritageClauses = ( < ts . ClassLikeDeclaration > inheritedTypeDeclaration ) . heritageClauses ; if ( inheritedTypeHeritageClauses ) { for ( const inheritedTypeHeritageClause of inheritedTypeHeritageClauses ) { getHeritageExportProperties ( inheritedTypeHeritageClause , checker ) ; } } } const inheritedTypeProperties = inheritedType . getProperties ( ) ; for ( const propertySymbol of inheritedTypeProperties ) { if ( Ast . isExportContext ( propertySymbol ) ) { classExportProperties . push ( propertySymbol ) ; } } } } } let heritageClauses = ( < ts . ClassLikeDeclaration > classNode ) . heritageClauses ; if ( heritageClauses ) { for ( const heritageClause of heritageClauses ) { getHeritageExportProperties ( heritageClause , checker ) ; } } return classExportProperties ; } export function getClassAbstractProperties ( extendsClause , checker ) { let abstractProperties = [ ] ; const abstractTypeNodes = extendsClause . types ; for ( const abstractTypeNode of abstractTypeNodes ) { const abstractType = checker . getTypeAtLocation ( abstractTypeNode ) ; let abstractTypeSymbol = abstractType . getSymbol ( ) ; if ( abstractTypeSymbol . valueDeclaration ) { if ( getSyntacticModifierFlagsNoCache ( abstractTypeSymbol . valueDeclaration ) & ts . ModifierFlags . Abstract ) { const props = abstractType . getProperties ( ) ; for ( const prop of props ) { abstractProperties . push ( prop ) ; } } } } return abstractProperties ; } export function getImplementsProperties ( implementsClause , checker ) { let implementsProperties = [ ] ; const typeNodes = implementsClause . types ; for ( const typeNode of typeNodes ) { const type = checker . getTypeAtLocation ( typeNode ) ; const props = type . getProperties ( ) ; for ( const prop of props ) { implementsProperties . push ( prop ) ; } } return implementsProperties ; } export function getIdentifierUID ( symbol ) { if ( ! symbol ) { return undefined ; } let id = ( < any > symbol ) . id ; if ( id === undefined && symbol . valueDeclaration ) { id = ( < any > symbol . valueDeclaration ) . symbol . id ; } return id ? id . toString ( ) : undefined ; } export function getContainerFlags ( node ) { switch ( node . kind ) { case ts . SyntaxKind . ClassExpression : case ts . SyntaxKind . ClassDeclaration : case ts . SyntaxKind . EnumDeclaration : case ts . SyntaxKind . ObjectLiteralExpression : case ts . SyntaxKind . TypeLiteral : case ts . SyntaxKind . JSDocTypeLiteral : case ts . SyntaxKind . JsxAttributes : return ContainerFlags . IsContainer ; case ts . SyntaxKind . InterfaceDeclaration : return ContainerFlags . IsContainer | ContainerFlags . IsInterface ; case ts . SyntaxKind . ModuleDeclaration : case ts . SyntaxKind . TypeAliasDeclaration : case ts . SyntaxKind . MappedType : return ContainerFlags . IsContainer | ContainerFlags . HasLocals ; case ts . SyntaxKind . SourceFile : return ContainerFlags . IsContainer | ContainerFlags . IsControlFlowContainer | ContainerFlags . HasLocals ; case ts . SyntaxKind . MethodDeclaration : if ( isObjectLiteralOrClassExpressionMethod ( node ) ) { return ContainerFlags . IsContainer | ContainerFlags . IsControlFlowContainer | ContainerFlags . HasLocals | ContainerFlags . IsFunctionLike | ContainerFlags . IsObjectLiteralOrClassExpressionMethod ; } case ts . SyntaxKind . Constructor : case ts . SyntaxKind . FunctionDeclaration : case ts . SyntaxKind . MethodSignature : case ts . SyntaxKind . GetAccessor : case ts . SyntaxKind . SetAccessor : case ts . SyntaxKind . CallSignature : case ts . SyntaxKind . JSDocFunctionType : case ts . SyntaxKind . FunctionType : case ts . SyntaxKind . ConstructSignature : case ts . SyntaxKind . IndexSignature : case ts . SyntaxKind . ConstructorType : return ContainerFlags . IsContainer | ContainerFlags . IsControlFlowContainer | ContainerFlags . HasLocals | ContainerFlags . IsFunctionLike ; case ts . SyntaxKind . FunctionExpression : case ts . SyntaxKind . ArrowFunction : return ContainerFlags . IsContainer | ContainerFlags . IsControlFlowContainer | ContainerFlags . HasLocals | ContainerFlags . IsFunctionLike | ContainerFlags . IsFunctionExpression ; case ts . SyntaxKind . ModuleBlock : return ContainerFlags . IsControlFlowContainer ; case ts . SyntaxKind . PropertyDeclaration : return ( < ts . PropertyDeclaration > node ) . initializer ? ContainerFlags . IsControlFlowContainer : 0 ; case ts . SyntaxKind . CatchClause : case ts . SyntaxKind . ForStatement : case ts . SyntaxKind . ForInStatement : case ts . SyntaxKind . ForOfStatement : case ts . SyntaxKind . CaseBlock : return ContainerFlags . IsBlockScopedContainer ; case ts . SyntaxKind . Block : return isFunctionLike ( node . parent ) ? ContainerFlags . None : ContainerFlags . IsBlockScopedContainer ; } return ContainerFlags . None ; } export function getImplementsClause ( node ) { if ( node ) { let heritageClauses = ( < ts . ClassLikeDeclaration > node ) . heritageClauses ; if ( heritageClauses ) { for ( const clause of heritageClauses ) { if ( clause . token === ts . SyntaxKind . ImplementsKeyword ) { return clause ; } } } } return undefined ; } export function getExtendsClause ( node ) { if ( node ) { let heritageClauses = ( < ts . ClassLikeDeclaration > node ) . heritageClauses ; if ( heritageClauses ) { for ( const clause of heritageClauses ) { if ( clause . token === ts . SyntaxKind . ExtendsKeyword ) { return clause ; } } } } return undefined ; } export function isKeyword ( token ) { return ts . SyntaxKind . FirstKeyword <= token && token <= ts . SyntaxKind . LastKeyword ; } export function isPuncuation ( token ) { return ts . SyntaxKind . FirstPunctuation <= token && token <= ts . SyntaxKind . LastPunctuation ; } export function isTrivia ( token ) { return ts . SyntaxKind . FirstTriviaToken <= token && token <= ts . SyntaxKind . LastTriviaToken ; } export function isExportContext ( propertySymbol ) { let node = propertySymbol . valueDeclaration ; while ( node ) { if ( node . flags & ts . NodeFlags . ExportContext ) { return true ; } node = node . parent ; } return false ; } export function isAmbientContext ( propertySymbol ) { let node = propertySymbol . valueDeclaration ; while ( node ) { if ( getSyntacticModifierFlagsNoCache ( node ) & ts . ModifierFlags . Ambient ) { return true ; } node = node . parent ; } return false ; } export function isSourceCodeFile ( file ) { return ( file . kind === ts . SyntaxKind . SourceFile && ! file . isDeclarationFile ) ; } }