1import React from 'react'; 2import ReactMarkdown from 'react-markdown'; 3 4import { Code, InlineCode } from '~/components/base/code'; 5import { H4 } from '~/components/base/headings'; 6import { InternalLink } from '~/components/base/link'; 7import { LI, UL } from '~/components/base/list'; 8import { B, P, Quote } from '~/components/base/paragraph'; 9import { 10 CommentData, 11 MethodParamData, 12 TypeDefinitionData, 13 TypeDefinitionTypesData, 14} from '~/components/plugins/api/APIDataTypes'; 15 16export enum TypeDocKind { 17 Enum = 4, 18 Variable = 32, 19 Function = 64, 20 Class = 128, 21 Interface = 256, 22 Property = 1024, 23 TypeAlias = 4194304, 24} 25 26export type MDRenderers = React.ComponentProps<typeof ReactMarkdown>['renderers']; 27 28export const mdRenderers: MDRenderers = { 29 blockquote: ({ children }) => ( 30 <Quote> 31 {React.Children.map(children, child => 32 child.type.name === 'paragraph' ? child.props.children : child 33 )} 34 </Quote> 35 ), 36 code: ({ value, language }) => <Code className={`language-${language}`}>{value}</Code>, 37 heading: ({ children }) => <H4>{children}</H4>, 38 inlineCode: ({ value }) => <InlineCode>{value}</InlineCode>, 39 list: ({ children }) => <UL>{children}</UL>, 40 listItem: ({ children }) => <LI>{children}</LI>, 41 link: ({ href, children }) => <InternalLink href={href}>{children}</InternalLink>, 42 paragraph: ({ children }) => (children ? <P>{children}</P> : null), 43 strong: ({ children }) => <B>{children}</B>, 44 text: ({ value }) => (value ? <span>{value}</span> : null), 45}; 46 47export const mdInlineRenderers: MDRenderers = { 48 ...mdRenderers, 49 paragraph: ({ children }) => (children ? <span>{children}</span> : null), 50}; 51 52const nonLinkableTypes = ['Date', 'Uint8Array']; 53 54export const resolveTypeName = ({ 55 elementType, 56 name, 57 type, 58 types, 59 typeArguments, 60 declaration, 61}: TypeDefinitionData): string | JSX.Element => { 62 if (name) { 63 if (type === 'reference') { 64 if (typeArguments) { 65 if (name === 'Promise') { 66 return ( 67 <span> 68 {'Promise<'} 69 {typeArguments.map(resolveTypeName)} 70 {'>'} 71 </span> 72 ); 73 } else { 74 return `${typeArguments.map(resolveTypeName)}`; 75 } 76 } else { 77 if (nonLinkableTypes.includes(name)) { 78 return name; 79 } else { 80 return ( 81 <InternalLink href={`#${name.toLowerCase()}`} key={`type-link-${name}`}> 82 {name} 83 </InternalLink> 84 ); 85 } 86 } 87 } else { 88 return name; 89 } 90 } else if (elementType?.name) { 91 if (type === 'array') { 92 return elementType.name + '[]'; 93 } 94 return elementType.name + type; 95 } else if (type === 'union' && types?.length) { 96 return types 97 .map((t: TypeDefinitionTypesData) => 98 t.type === 'array' ? `${t.elementType?.name}[]` : `${t.name || t.value}` 99 ) 100 .join(' | '); 101 } else if (declaration?.signatures) { 102 return `() => ${resolveTypeName(declaration.signatures[0].type)}`; 103 } 104 return 'undefined'; 105}; 106 107export const renderParam = ({ comment, name, type }: MethodParamData): JSX.Element => ( 108 <LI key={`param-${name}`}> 109 <B> 110 {name} (<InlineCode>{resolveTypeName(type)}</InlineCode>) 111 </B> 112 <CommentTextBlock comment={comment} renderers={mdInlineRenderers} withDash /> 113 </LI> 114); 115 116export type CommentTextBlockProps = { 117 comment?: CommentData; 118 renderers?: MDRenderers; 119 withDash?: boolean; 120}; 121 122export const CommentTextBlock: React.FC<CommentTextBlockProps> = ({ 123 comment, 124 renderers = mdRenderers, 125 withDash, 126}) => { 127 const shortText = comment?.shortText?.trim().length ? ( 128 <ReactMarkdown renderers={renderers}>{comment.shortText}</ReactMarkdown> 129 ) : null; 130 const text = comment?.text?.trim().length ? ( 131 <ReactMarkdown renderers={renderers}>{comment.text}</ReactMarkdown> 132 ) : null; 133 return ( 134 <> 135 {withDash && (shortText || text) ? ' - ' : null} 136 {shortText} 137 {text} 138 </> 139 ); 140}; 141