import { ComponentDoc , PropItemType } from 'react-docgen-typescript/lib/parser' ; export interface PropTypeDocumentation { name : string ; description ? : string ; enums ? : string [ ] ; type ? : string ; } export interface PropDocumentation { name : string ; type : string | PropTypeDocumentation ; required ? : boolean ; defaultValue ? : string ; description : string ; deprecated ? : boolean ; enums ? : string [ ] ; value ? : any [ ] ; } export interface PropDocumentationMap extends Record < string , PropDocumentation > { } export interface Documentable { displayName ? : string ; __docgenInfo ? : any ; } const FUNCTION_TYPE_REGEX = /.+ => .+/ ; const ENUM_TYPE_REGEX = /enum/ ; const ENUM_VALUES_REGEX = /.*\|.*/ ; const ARRAY_REGEX = /.*\[\]/ ; const OBJECT_REGEX = /\{.*\}/ ; const DEPRECATED_KEYWORD = '@deprecated' ; const escapeType = ( typeName ) => typeName . replace ( '| undefined' , '' ) ; const merge = < T extends { [ key ] : unknown ; } > ( o1 , o2 ) => { if ( ! o2 ) return o1 ; const destination = Object . assign ( { } , o1 ) ; Object . keys ( o2 ) . forEach ( key => { if ( destination [ key ] ) { Object . assign ( destination [ key ] , o2 [ key ] ) ; } else { destination [ key ] = o2 [ key ] ; } } ) ; return destination ; } ; const mapValues = < T > ( map , callback ) => { return Object . fromEntries ( Object . entries ( map ) . map ( ( [ key , value ] ) => [ key , callback ( value , key ) ] ) ) ; } ; class DocumentationGenerator { resolveType ( type ) { if ( ! type ) { return { } as PropTypeDocumentation ; } const typeName = escapeType ( type . name ) ; if ( typeName . match ( FUNCTION_TYPE_REGEX ) ) { return { name : 'function' , description : typeName } ; } if ( typeName . match ( ENUM_TYPE_REGEX ) ) { let baseShape = { } ; if ( type . value && Array . isArray ( type . value ) ) { baseShape = { enums : type . value . map ( ( { value } ) => value ) } ; } return { name : typeName , description : '' , ... baseShape } ; } if ( typeName . match ( ARRAY_REGEX ) ) { const objectType = typeName . substring ( 0 , typeName . indexOf ( '[]' ) ) ; const objectDescription = this . generateObjectDescription ( objectType ) ; return { name : '[]' , description : `${ objectDescription }[]` } ; } if ( typeName . match ( OBJECT_REGEX ) ) { const objectDescription = this . generateObjectDescription ( typeName ) ; return { name : 'object' , description : objectDescription } ; } if ( typeName . match ( ENUM_VALUES_REGEX ) ) { const enums = typeName . split ( '|' ) . map ( value => value . trim ( ) ) ; return { name : 'enum' , enums } ; } return { name : typeName , description : '' } ; } generateObjectDescription ( objectType ) { const OPENING_BRACKET = /\{\s*/ ; const CLOSING_BRACKET = /\s*\}/ ; const isComplexObjectType = OPENING_BRACKET . test ( objectType ) ; if ( isComplexObjectType ) { const objectTypeWithoutBrackets = objectType . replace ( OPENING_BRACKET , '' ) . replace ( CLOSING_BRACKET , '' ) ; const objectProps = objectTypeWithoutBrackets . split ( ';' ) ; const propsTable = objectProps . slice ( 0 , - 1 ) . map ( prop => `| ${ prop . split ( ':' ) [ 0 ] } | ${ escapeType ( prop . split ( ':' ) [ 1 ] ) } |` ) . join ( '\r\n' ) ; return `{

|                 |         |
| ----            | ------  |
${ propsTable }

}` ; } else { return objectType ; } } resolveDefaultValue ( defaultValue ) { if ( ! defaultValue ) { return '' ; } return defaultValue . value ; } merge ( docs , additionalDocs ? ) { return merge ( docs , additionalDocs ) as PropDocumentationMap ; } transform ( generatedDocumentation ) { const { props : propDocs } = generatedDocumentation ; return mapValues ( propDocs , ( propDoc , propName ) => { const { type , description , required , defaultValue } = propDoc ; return { name : propName , type : this . resolveType ( type ) , defaultValue : this . resolveDefaultValue ( defaultValue ) , description : description . replace ( DEPRECATED_KEYWORD , '' ) , deprecated : description . startsWith ( DEPRECATED_KEYWORD ) , required } ; } ) as PropDocumentationMap ; } } export default DocumentationGenerator ;