import * as ts from 'typescript' ; import { ProgramError } from './errors' ; export const LITERAL_KINDS = [ ts . SyntaxKind . LiteralType , ts . SyntaxKind . NumericLiteral , ts . SyntaxKind . StringLiteral , ts . SyntaxKind . TrueKeyword , ts . SyntaxKind . FalseKeyword ] ; export const AMBIENT_KINDS = [ ts . SyntaxKind . InterfaceDeclaration , ts . SyntaxKind . TypeAliasDeclaration , ] ; export function asArray < T > ( value ) { return Array . isArray ( value ) ? value : ! value ? [ ] : [ value ] ; } export function asNewArray < T > ( value ) { return Array . isArray ( value ) ? value . length > 0 ? [ ... value ] : [ ] : ! value ? [ ] : [ value ] ; } export function arrayFromNodeArray < T extends ts . Node > ( value ) { return ! value ? [ ] : [ ... value ] ; } export function hasTypeParameters ( node ) { return Array . isArray ( ( node as any ) . typeParameters ) && ( node as any ) . typeParameters . length > 0 ; } export function hasProperty ( obj , prop ) { return obj && obj . hasOwnProperty ( prop ) ; } export function hasArrayProperty ( obj , prop ) { return hasProperty ( obj , prop ) && Array . isArray ( obj [ prop ] ) ; } export function hasNonEmptyArrayProperty ( obj , prop ) { return hasArrayProperty ( obj , prop ) && obj [ prop ] . length > 0 ; } export function hasKind ( node , kind ) { if ( ! node ) { return false ; } if ( node . kind === kind ) { return true ; } return ts . forEachChild ( node , n => hasKind ( n , kind ) ) || false ; } export function hasModifier ( node , modifier ) { for ( let flag of node . modifiers || [ ] ) { if ( flag . kind === modifier ) { return true ; } } return false ; } export function hasFlag ( node , flag ) { return ! ! ( node . flags & flag ) ; } export function setParent ( node ) { if ( ! node ) return node ; ts . forEachChild ( node , n => { ( n . parent as any ) = node ; setParent ( n ) ; } ) ; return node ; } export function isAnyKeyword ( node ) { if ( ! node ) { return false ; } if ( node . kind === ts . SyntaxKind . AnyKeyword ) { return true ; } return false ; } export function isSynthesized ( node ) { return ( node . flags & ts . NodeFlags . Synthesized ) === ts . NodeFlags . Synthesized ; } export function isAmbient ( node ) { do { if ( isKind ( node , ... AMBIENT_KINDS ) ) { return true ; } } while ( node = node . parent ) ; return false ; } export function isAmbientDeclaration ( node ) { do { if ( hasModifier ( node , ts . SyntaxKind . DeclareKeyword ) ) { return true ; } } while ( node = node . parent ) ; return false ; } export function isPartOfTypeNode ( node ) { while ( node && node . parent ) { if ( ts . isTypeNode ( node ) ) { return true ; } node = node . parent ; } return false ; } export function isBindingPattern ( node ) { return ts . isArrayBindingPattern ( node ) || ts . isObjectBindingPattern ( node ) ; } export function isStatic ( node ) { return ! Array . isArray ( node . modifiers ) ? false : node . modifiers . findIndex ( ( el ) => el . kind === ts . SyntaxKind . StaticKeyword ) !== - 1 ; } export function isTypeParameter ( node ) { const nodeName = getEntityNameText ( node . typeName ) ; while ( node = node . parent as any ) { if ( ( node as any ) . typeParameters && ( node as any ) . typeParameters . length > 0 ) { if ( ( node as any ) . typeParameters . find ( ( param ) => { return param . name . text === nodeName ; } ) ) { return true ; } } } return false ; } export function isTypeParameterOf ( node , typeParameters ) { if ( ! ts . isTypeReferenceNode ( node ) ) { return false ; } const nodeName = getEntityNameText ( node . typeName ) ; for ( let typeParameter of typeParameters ) { if ( typeParameter . name . text === nodeName ) { return true ; } } return false ; } export function isTypeParameterOfClass ( node ) { let current = node as ts . Node ; while ( current = current . parent ) { if ( ts . isClassDeclaration ( current ) ) { if ( isTypeParameterOf ( node , arrayFromNodeArray ( current . typeParameters ) ) ) { return current ; } return null ; } } return null ; } export function isInStaticContext ( node ) { let current = node as ts . Node ; while ( current = current . parent ) { if ( isStatic ( current ) ) { return true ; } } return false ; } export function isSuperStatement ( node ) { return ts . isExpressionStatement ( node ) && ts . isCallExpression ( node . expression ) && node . expression . expression . kind === ts . SyntaxKind . SuperKeyword ; } export function isKind ( node , ... kind ) { return kind . indexOf ( node . kind ) !== - 1 ; } export function isBindingName ( node ) { return ts . isIdentifier ( node ) || this . isBindingPattern ( node ) ; } export function isLiteral ( node ) { return LITERAL_KINDS . indexOf ( node . kind ) !== - 1 ; } export function getHash ( str ) { var hash = 5381 , i = str . length ; while ( i ) { hash = ( hash * 33 ) ^ str . charCodeAt ( -- i ) ; } return hash >>> 0 ; } export function getHashedDeclarationName ( name , fileName ) { return `${ name }.${ getHash ( fileName ) }` ; } export function getIdentifierOfEntityName ( node ) { if ( ts . isIdentifier ( node ) ) { return node ; } return node . right ; } export function getBaseIdentifierOfEntityName ( node ) { while ( ts . isQualifiedName ( node ) ) { node = node . left ; } return node ; } export function getExtendsClause ( node ) { return node . heritageClauses && node . heritageClauses . find ( clause => { if ( clause . token === ts . SyntaxKind . ExtendsKeyword ) { return true ; } } ) ; } export function getImplementsClause ( node ) { return node . heritageClauses && node . heritageClauses . find ( clause => { if ( clause . token === ts . SyntaxKind . ImplementsKeyword ) { return true ; } } ) ; } export function getExpression ( node ) { while ( ( node as any ) . expression ) { node = ( node as any ) . expression ; } return node as ts . Expression ; } export function getPropertyAccessExpressionTextOrFail ( node ) { let text = '' ; while ( ts . isPropertyAccessExpression ( node ) ) { text += `.${ node . name . text }` ; node = node . expression as ts . PropertyAccessExpression ; } if ( ( node as ts . Node ) . kind !== ts . SyntaxKind . Identifier ) { throw new ProgramError ( 'Can\'t get text of property access expression that contains other expressions than property access expression.' ) ; } text = `${ ( node as ts . Identifier ) . text }${ text }` ; return text ; } export function getIdentifierOfPropertyAccessExpressionOrFail ( node ) { let expression = node . expression ; while ( expression . kind === ts . SyntaxKind . PropertyAccessExpression ) { expression = ( expression as ts . PropertyAccessExpression ) . expression ; } if ( expression . kind !== ts . SyntaxKind . Identifier ) { throw new ProgramError ( 'Can\'t get identifier of property access expression that contains other expressions than property access expression.' ) ; } return expression as ts . Identifier ; } export function getEntityNameText ( node ) { if ( ts . isIdentifier ( node ) ) { return node . text ; } const left = getEntityNameText ( node . left ) ; const right = node . right . text ; return `${ left }.${ right }` ; } export function insertBeforeSuper ( statements , insert , offset = 0 ) { const index = statements . findIndex ( statement => isSuperStatement ( statement ) ) ; insert = asArray ( insert ) ; if ( index !== - 1 ) { statements . splice ( index + offset , 0 , ... insert ) ; } else { statements . unshift ( ... insert ) ; } return statements ; } export function insertAfterSuper ( statements , insert , offset = 0 ) { return this . insertBeforeSuper ( statements , insert , 1 ) ; } export function declarationCanHaveTypeAnnotation ( node ) { let current = node ; if ( ts . isVariableDeclaration ( current ) && current . parent ) { current = current . parent ; } if ( ts . isVariableDeclarationList ( current ) && current . parent ) { current = current . parent ; } switch ( current . kind ) { case ts . SyntaxKind . ForOfStatement : case ts . SyntaxKind . ForInStatement : case ts . SyntaxKind . CatchClause : case ts . SyntaxKind . ImportClause : return false ; } return true ; } export function extendsClauseHasTypeArguments ( node ) { return node && node . types && node . types [ 0 ] && node . types [ 0 ] . typeArguments && node . types [ 0 ] . typeArguments . length > 0 ; } export function canHaveType ( node ) { if ( ! node ) { return false ; } switch ( node . kind ) { case ts . SyntaxKind . VariableDeclaration : case ts . SyntaxKind . ObjectBindingPattern : case ts . SyntaxKind . ArrayBindingPattern : case ts . SyntaxKind . Parameter : case ts . SyntaxKind . PropertySignature : case ts . SyntaxKind . PropertyDeclaration : case ts . SyntaxKind . MethodSignature : case ts . SyntaxKind . CallSignature : case ts . SyntaxKind . ConstructSignature : case ts . SyntaxKind . IndexSignature : case ts . SyntaxKind . MethodDeclaration : case ts . SyntaxKind . GetAccessor : case ts . SyntaxKind . FunctionExpression : case ts . SyntaxKind . ArrowFunction : case ts . SyntaxKind . FunctionDeclaration : return true ; } return false ; } export function annotateWithAny ( node ) { switch ( node . kind ) { case ts . SyntaxKind . VariableDeclaration : if ( ! declarationCanHaveTypeAnnotation ( node ) ) { return false ; } case ts . SyntaxKind . ObjectBindingPattern : case ts . SyntaxKind . ArrayBindingPattern : case ts . SyntaxKind . Parameter : case ts . SyntaxKind . PropertySignature : case ts . SyntaxKind . PropertyDeclaration : case ts . SyntaxKind . MethodSignature : case ts . SyntaxKind . CallSignature : case ts . SyntaxKind . ConstructSignature : case ts . SyntaxKind . IndexSignature : case ts . SyntaxKind . MethodDeclaration : case ts . SyntaxKind . GetAccessor : case ts . SyntaxKind . FunctionExpression : case ts . SyntaxKind . ArrowFunction : case ts . SyntaxKind . FunctionDeclaration : ( node as any ) . type = ts . createKeywordTypeNode ( ts . SyntaxKind . AnyKeyword ) ; ( node as any ) . type . parent = node ; return true ; } return false ; }