import * as ts from 'byots' ; import { CompilerState } from '../types' ; const shouldAddDollerParentList = new Set ( [ ts . SyntaxKind . VariableDeclaration , ts . SyntaxKind . TemplateSpan , ts . SyntaxKind . ElementAccessExpression , ts . SyntaxKind . Parameter , ts . SyntaxKind . BinaryExpression , ts . SyntaxKind . IfStatement , ts . SyntaxKind . PrefixUnaryExpression , ts . SyntaxKind . PostfixUnaryExpression , ts . SyntaxKind . SwitchStatement , ts . SyntaxKind . ArrayLiteralExpression , ts . SyntaxKind . ReturnStatement , ts . SyntaxKind . ReturnStatement , ts . SyntaxKind . ForInStatement , ts . SyntaxKind . ForOfStatement , ts . SyntaxKind . TypeOfExpression , ts . SyntaxKind . ConditionalExpression , ts . SyntaxKind . ComputedPropertyName , ts . SyntaxKind . ImportSpecifier , ts . SyntaxKind . PropertyDeclaration , ts . SyntaxKind . EnumDeclaration , ts . SyntaxKind . ArrowFunction , ts . SyntaxKind . BindingElement , ts . SyntaxKind . WhileStatement , ts . SyntaxKind . AsExpression , ts . SyntaxKind . SpreadAssignment , ts . SyntaxKind . SpreadElement , ts . SyntaxKind . TypeAssertionExpression ] ) ; param node export function shouldAddDollar ( node , state ) { if ( isClassLike ( node , state . typeChecker ) ) { return false ; } if ( isFunctionLike ( node , state . typeChecker ) && ! isVariable ( node , state . typeChecker ) ) { return false ; } if ( node . parent && shouldAddDollerParentList . has ( node . parent . kind ) ) { return true ; } const currentNode = node as ts . Expression ; if ( node . parent && ( ts . isCallExpression ( node . parent ) || ts . isNewExpression ( node . parent ) ) && node . parent . arguments . indexOf ( currentNode ) >= 0 ) { return true ; } if ( node . parent && ts . isPropertyAssignment ( node . parent ) && node . parent . initializer === node ) { return true ; } if ( node . parent && ts . isPropertyAccessExpression ( node . parent ) && node . parent . expression === node ) { return true ; } return false ; } export function shouldAddDoubleQuote ( node ) { if ( node . parent && ( node . parent . kind === ts . SyntaxKind . PropertyAssignment || node . parent . kind === ts . SyntaxKind . EnumMember ) && ( < ts . PropertyAssignment > node . parent ) . name === node ) { return true ; } return false ; } const baseArrayBrackets = [ 'array(' , ')' ] ; const newLineArrayBrackets = [ 'array(' , ')' ] ; const arrayBracketsMap = { [ ts . ListFormat . ObjectLiteralExpressionProperties ] : baseArrayBrackets , [ ts . ListFormat . ObjectLiteralExpressionProperties | ts . ListFormat . PreferNewLine ] : newLineArrayBrackets , [ ts . ListFormat . ArrayLiteralExpressionElements ] : baseArrayBrackets , [ ts . ListFormat . ArrayLiteralExpressionElements | ts . ListFormat . PreferNewLine ] : newLineArrayBrackets } ; param format export function shouldUseArray ( format ) { return arrayBracketsMap [ format ] || '' ; } param node export function shouldUseReference ( node , typeChecker ) { const nodeType = typeChecker . getTypeAtLocation ( node ) ; return ts . isIdentifier ( node ) && ! isFunctionLike ( node , typeChecker ) && ( nodeType . flags === ts . TypeFlags . Object ) ; } const stringLikeType = new Set ( [ ts . TypeFlags . String , ts . TypeFlags . StringLiteral ] ) ; param node export function isStringLike ( node , typeChecker ) { if ( ! node ) { return false ; } const nodeType = typeChecker . getTypeAtLocation ( node ) ; return stringLikeType . has ( nodeType . getFlags ( ) ) ; } const numberLikeType = new Set ( [ ts . TypeFlags . Number , ts . TypeFlags . NumberLiteral ] ) ; param node export function isNumberLike ( node , typeChecker ) { const nodeType = typeChecker . getTypeAtLocation ( node ) ; return numberLikeType . has ( nodeType . getFlags ( ) ) ; } export function isClassLike ( node , typeChecker ) { const nodeType = typeChecker . getTypeAtLocation ( node ) ; const nodeSymbol = typeChecker . getSymbolAtLocation ( node ) ; if ( ! nodeSymbol ) { return false ; } if ( ts . SymbolFlags . Class & nodeSymbol . getFlags ( ) ) { return true ; } if ( ! nodeType . symbol ) { return false ; } return ( ts . isImportSpecifier ( node . parent ) && ( ts . SymbolFlags . Class & nodeType . symbol . getFlags ( ) ) ) || ( ( nodeType as ts . ObjectType ) . objectFlags & ts . ObjectFlags . Interface ) && nodeType . symbol . members . has ( 'prototype' as ts . __String ) || ( ! nodeSymbol . valueDeclaration && ( ts . SymbolFlags . Class & nodeType . symbol . getFlags ( ) ) && nodeType . symbol . exports . has ( 'prototype' as ts . __String ) ) ; } export function isClassInstance ( node , typeChecker ) { const nodeType = typeChecker . getTypeAtLocation ( node ) ; const nodeSymbol = typeChecker . getSymbolAtLocation ( node ) ; const baseTypeName = getBaseTypeName ( nodeType ) ; if ( baseTypeName === 'PHPClass' ) return true ; if ( baseTypeName === 'PHPArray' ) return false ; if ( ! nodeSymbol ) { return false ; } return ( nodeType . isClass ( ) && ! ( nodeSymbol . getFlags ( ) & ts . SymbolFlags . Class ) ) || ( ( nodeType as ts . ObjectType ) . objectFlags & ts . ObjectFlags . Interface ) && nodeType . symbol && nodeType . symbol . getEscapedName ( ) === 'Date' ; } function getBaseTypeName ( nodeType ) { const queue = [ nodeType ] ; while ( queue . length ) { const nodeType = queue . shift ( ) ; const baseTypes = nodeType . getBaseTypes ( ) ; if ( ! baseTypes ) continue ; for ( const baseType of baseTypes ) { const baseSymbol = baseType . getSymbol ( ) ; if ( ! baseSymbol ) continue ; const name = baseSymbol . getName ( ) ; if ( name === 'PHPClass' || name === 'PHPArray' ) return name ; else queue . push ( baseType ) ; } } return 'other' ; } export function isFunctionLike ( node , typeChecker ) { const nodeType = typeChecker . getTypeAtLocation ( node ) ; const nodeSymbol = nodeType . getSymbol ( ) ; const symbolFlags = nodeSymbol && nodeSymbol . getFlags ( ) ; return ! ! symbolFlags && ( symbolFlags & ts . SymbolFlags . Function || symbolFlags & ts . SymbolFlags . Method ) ; } /**
 * used for:
 * variable whos value is function-like but not function declaration
 */ export function isVariable ( node , typeChecker ) { const nodeType = typeChecker . getTypeAtLocation ( node ) ; const nodeSymbol = nodeType . getSymbol ( ) ; return ts . isVariableDeclaration ( nodeSymbol . valueDeclaration . parent ) ; } export function isVisibilityModifier ( node ) { return node . kind === ts . SyntaxKind . PublicKeyword || node . kind === ts . SyntaxKind . PrivateKeyword || node . kind === ts . SyntaxKind . ProtectedKeyword ; } export function isSupportedPropertyModifier ( node ) { return isVisibilityModifier ( node ) || node . kind === ts . SyntaxKind . StaticKeyword ; } export function isRegExp ( node , typeChecker ) { if ( ts . isRegularExpressionLiteral ( node ) ) { return true ; } const nodeType = typeChecker . getTypeAtLocation ( node ) ; return nodeType . symbol ?. getEscapedName ( ) === 'RegExp' ; } export function isFromImport ( node , typeChecker ) { const symbol = typeChecker . getSymbolAtLocation ( node ) ; if ( ! symbol ) { return false ; } const declarations = symbol . getDeclarations ( ) ; return declarations . length && ts . isImportSpecifier ( declarations [ 0 ] ) ; }