import { strict as assert } from "assert" ; import fs from "fs" ; import path from "path" ; import ts from "typescript" ; returns export function isExported ( node ) { if ( ! node . modifiers ) { return false ; } return node . modifiers . some ( modifier => modifier . kind === ts . SyntaxKind . ExportKeyword ) ; } export function getFirstGenericArgument ( type ) { if ( ts . isTypeReferenceNode ( type ) && type . typeArguments ) { const genericArgType = type . typeArguments [ 0 ] ; return genericArgType ; } if ( ts . isHeritageClause ( type ) && type . types && type . types [ 0 ] && type . types [ 0 ] . typeArguments ) { return type . types [ 0 ] . typeArguments [ 0 ] ; } return undefined ; } /**
 * Returns the parameter declaration of a react function component or undefined if not found.
 *
 * Expects type to be a function with the following shape
 *
 * ```ts
 * (arg: SomeType) => JSX.Element
 * ```
 */ export function getFunctionComponentParameter ( type ) { if ( ! type . getCallSignatures ( ) || type . getCallSignatures ( ) . length === 0 ) { return ; } const [ callSignature ] = type . getCallSignatures ( ) ; if ( ! callSignature . getReturnType ( ) || ! callSignature . getReturnType ( ) . getSymbol ( ) || callSignature . getReturnType ( ) . getSymbol ( ) ! . getName ( ) !== "Element" ) { return ; } if ( callSignature . getParameters ( ) . length !== 1 ) { return ; } const [ parameter ] = callSignature . getParameters ( ) ; const { valueDeclaration } = parameter ; if ( ! ts . isParameter ( valueDeclaration ) ) { return ; } if ( ! valueDeclaration . type ) { return ; } return valueDeclaration ; } export function getFirstTypeArgument ( type ) { const typeArguments = ( type as any ) [ "typeArguments" ] ; if ( typeArguments && typeArguments . length > 0 ) { return typeArguments [ 0 ] ; } if ( type . aliasTypeArguments && type . aliasTypeArguments . length > 0 ) { return type . aliasTypeArguments [ 0 ] ; } return undefined ; } param pathNode export function findSourceFiles ( program , pathNode ) { const relativeImportPath = pathNode . text ; const { fileName } = pathNode . getSourceFile ( ) ; const resolvedImport = path . join ( path . dirname ( fileName ) , relativeImportPath ) ; const possibilities = [ ".ts" , ".tsx" , ".d.ts" , "/index.ts" , "/index.tsx" , "/index.d.ts" ] . map ( extension => `${ resolvedImport }${ extension }` ) ; const sourceFiles = possibilities . filter ( filePath => fs . existsSync ( filePath ) ) . map ( filePath => { const sourceFile = program . getSourceFile ( filePath ) ! ; return sourceFile ; } ) . filter ( sourceFile => { return ! ! sourceFile ; } ) ; if ( process . env . NODE_ENV !== "production" ) { assert ( sourceFiles . length > 0 , `Failed to resolve import from pathNode ${ pathNode . getText ( ) } relative to ${ fileName }. Tried the following combinations: \n${ possibilities . join ( "\n" ) }` ) ; } return sourceFiles ; } returns export function isExportStar ( exportDeclaration ) { return ! exportDeclaration . exportClause ; } export function findReactPropType ( type , checker ) { const propType = getFirstTypeArgument ( type ) ; if ( propType ) { return propType ; } const paramDeclaration = getFunctionComponentParameter ( type ) ; if ( paramDeclaration ) { return checker . getTypeAtLocation ( paramDeclaration ) ; } if ( type . isClass ( ) && type . getBaseTypes ( ) ! . length > 0 ) { const typeArg = getFirstTypeArgument ( type . getBaseTypes ( ) ! [ 0 ] ) ; if ( typeArg ) { return typeArg ; } } return undefined ; }