import { head } from 'lodash' ; import { isAbsolute , posix } from 'path' ; import * as ts from 'typescript' ; import { getDecoratorName , getText , getTypeArguments , isArray , isBoolean , isEnum , isInterface , isNumber , isString , isStringLiteral } from './ast-utils' ; export function getDecoratorOrUndefinedByNames ( names , decorators ) { return ( decorators || ts . createNodeArray ( ) ) . find ( ( item ) => { try { const decoratorName = getDecoratorName ( item ) ; return names . includes ( decoratorName ) ; } catch { return false ; } } ) ; } export function getTypeReferenceAsString ( type , typeChecker ) { if ( isArray ( type ) ) { const arrayType = getTypeArguments ( type ) [ 0 ] ; const elementType = getTypeReferenceAsString ( arrayType , typeChecker ) ; if ( ! elementType ) { return undefined ; } return `[${ elementType }]` ; } if ( isBoolean ( type ) ) { return Boolean . name ; } if ( isNumber ( type ) ) { return Number . name ; } if ( isString ( type ) || isStringLiteral ( type ) ) { return String . name ; } if ( isPromiseOrObservable ( getText ( type , typeChecker ) ) ) { const typeArguments = getTypeArguments ( type ) ; const elementType = getTypeReferenceAsString ( head ( typeArguments ) , typeChecker ) ; if ( ! elementType ) { return undefined ; } return elementType ; } if ( type . isClass ( ) ) { return getText ( type , typeChecker ) ; } try { const text = getText ( type , typeChecker ) ; if ( text === Date . name ) { return text ; } if ( isOptionalBoolean ( text ) ) { return Boolean . name ; } if ( isAutoGeneratedTypeUnion ( type ) || isAutoGeneratedEnumUnion ( type , typeChecker ) ) { const types = ( type as ts . UnionOrIntersectionType ) . types ; return getTypeReferenceAsString ( types [ types . length - 1 ] , typeChecker ) ; } if ( text === 'any' || text === 'unknown' || text === 'object' || isInterface ( type ) || ( type . isUnionOrIntersection ( ) && ! isEnum ( type ) ) ) { return 'Object' ; } if ( isEnum ( type ) ) { return undefined ; } if ( type . aliasSymbol ) { return 'Object' ; } return undefined ; } catch { return undefined ; } } export function isPromiseOrObservable ( type ) { return type . includes ( 'Promise' ) || type . includes ( 'Observable' ) ; } export function hasPropertyKey ( key , properties ) { return properties . filter ( ( item ) => ! isDynamicallyAdded ( item ) ) . some ( ( item ) => item . name . getText ( ) === key ) ; } export function replaceImportPath ( typeReference , fileName ) { if ( ! typeReference . includes ( 'import' ) ) { return typeReference ; } let importPath = /\(\"([^)]).+(\")/ . exec ( typeReference ) [ 0 ] ; if ( ! importPath ) { return undefined ; } importPath = convertPath ( importPath ) ; importPath = importPath . slice ( 2 , importPath . length - 1 ) ; try { if ( isAbsolute ( importPath ) ) { throw { } ; } require . resolve ( importPath ) ; return typeReference . replace ( 'import' , 'require' ) ; } catch ( _error ) { let relativePath = posix . relative ( posix . dirname ( fileName ) , importPath ) ; relativePath = relativePath [ 0 ] !== '.' ? './' + relativePath : relativePath ; const nodeModulesText = 'node_modules' ; const nodeModulePos = relativePath . indexOf ( nodeModulesText ) ; if ( nodeModulePos >= 0 ) { relativePath = relativePath . slice ( nodeModulePos + nodeModulesText . length + 1 ) ; const typesText = '@types' ; const typesPos = relativePath . indexOf ( typesText ) ; if ( typesPos >= 0 ) { relativePath = relativePath . slice ( typesPos + typesText . length + 1 ) ; } const indexText = '/index' ; const indexPos = relativePath . indexOf ( indexText ) ; if ( indexPos >= 0 ) { relativePath = relativePath . slice ( 0 , indexPos ) ; } } typeReference = typeReference . replace ( importPath , relativePath ) ; return typeReference . replace ( 'import' , 'require' ) ; } } export function isDynamicallyAdded ( identifier ) { return identifier && ! identifier . parent && identifier . pos === - 1 ; } param type param typeChecker export function isAutoGeneratedEnumUnion ( type , typeChecker ) { if ( type . isUnionOrIntersection ( ) && ! isEnum ( type ) ) { if ( ! type . types ) { return undefined ; } const undefinedTypeIndex = type . types . findIndex ( ( type ) => type . intrinsicName === 'undefined' ) ; if ( undefinedTypeIndex < 0 ) { return undefined ; } let parentType = undefined ; const isParentSymbolEqual = type . types . every ( ( item , index ) => { if ( index === undefinedTypeIndex ) { return true ; } if ( ! item . symbol ) { return false ; } if ( ! ( item . symbol as any ) . parent || item . symbol . flags !== ts . SymbolFlags . EnumMember ) { return false ; } const symbolType = typeChecker . getDeclaredTypeOfSymbol ( ( item . symbol as any ) . parent ) ; if ( symbolType === parentType || ! parentType ) { parentType = symbolType ; return true ; } return false ; } ) ; if ( isParentSymbolEqual ) { return parentType ; } } return undefined ; } param type export function isAutoGeneratedTypeUnion ( type ) { if ( type . isUnionOrIntersection ( ) && ! isEnum ( type ) ) { if ( ! type . types ) { return false ; } const undefinedTypeIndex = type . types . findIndex ( ( type ) => type . intrinsicName === 'undefined' ) ; if ( type . types . length === 2 && undefinedTypeIndex >= 0 ) { return true ; } } return false ; } export function extractTypeArgumentIfArray ( type ) { if ( isArray ( type ) ) { type = getTypeArguments ( type ) [ 0 ] ; if ( ! type ) { return undefined ; } return { type , isArray : true } ; } return { type , isArray : false } ; } param text function isOptionalBoolean ( text ) { return typeof text === 'string' && text === 'boolean | undefined' ; } param windowsPath function convertPath ( windowsPath ) { return windowsPath . replace ( /^\\\\\?\\/ , '' ) . replace ( /\\/g , '/' ) . replace ( /\/\/+/g , '/' ) ; }