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 const renderers: React.ComponentProps<typeof ReactMarkdown>['renderers'] = {
27  blockquote: ({ children }) => (
28    <Quote>
29      {React.Children.map(children, child =>
30        child.type.name === 'paragraph' ? child.props.children : child
31      )}
32    </Quote>
33  ),
34  code: ({ value, language }) => <Code className={`language-${language}`}>{value}</Code>,
35  heading: ({ children }) => <H4>{children}</H4>,
36  inlineCode: ({ value }) => <InlineCode>{value}</InlineCode>,
37  list: ({ children }) => <UL>{children}</UL>,
38  listItem: ({ children }) => <LI>{children}</LI>,
39  link: ({ href, children }) => <InternalLink href={href}>{children}</InternalLink>,
40  paragraph: ({ children }) => (children ? <P>{children}</P> : null),
41  strong: ({ children }) => <B>{children}</B>,
42  text: ({ value }) => (value ? <span>{value}</span> : null),
43};
44
45export const inlineRenderers: React.ComponentProps<typeof ReactMarkdown>['renderers'] = {
46  ...renderers,
47  paragraph: ({ children }) => (children ? <span>{children}</span> : null),
48};
49
50export const resolveTypeName = ({
51  elementType,
52  name,
53  type,
54  types,
55  typeArguments,
56}: TypeDefinitionData): string | JSX.Element => {
57  if (name) {
58    if (type === 'reference') {
59      if (typeArguments) {
60        if (name === 'Promise') {
61          return (
62            <span>
63              {'Promise<'}
64              {typeArguments.map(resolveTypeName)}
65              {'>'}
66            </span>
67          );
68        } else {
69          return `${typeArguments.map(resolveTypeName)}`;
70        }
71      } else {
72        if (name === 'Date') {
73          return name;
74        } else {
75          return (
76            <InternalLink href={`#${name.toLowerCase()}`} key={`type-link-${name}`}>
77              {name}
78            </InternalLink>
79          );
80        }
81      }
82    } else {
83      return name;
84    }
85  } else if (elementType?.name) {
86    if (type === 'array') {
87      return elementType.name + '[]';
88    }
89    return elementType.name + type;
90  } else if (type === 'union' && types?.length) {
91    return types.map((t: TypeDefinitionTypesData) => `${t.name || t.value}`).join(' | ');
92  }
93  return 'undefined';
94};
95
96export const renderParam = ({ comment, name, type }: MethodParamData): JSX.Element => (
97  <LI key={`param-${name}`}>
98    <B>
99      {name} (<InlineCode>{resolveTypeName(type)}</InlineCode>)
100    </B>
101    <CommentTextBlock comment={comment} renderers={inlineRenderers} withDash />
102  </LI>
103);
104
105type CommentTextBlockProps = {
106  comment?: CommentData;
107  renderers?: React.ComponentProps<typeof ReactMarkdown>['renderers'];
108  withDash?: boolean;
109};
110
111export const CommentTextBlock: React.FC<CommentTextBlockProps> = ({
112  comment,
113  renderers,
114  withDash,
115}) => {
116  const shortText = comment?.shortText?.trim().length ? (
117    <ReactMarkdown renderers={renderers}>{comment.shortText}</ReactMarkdown>
118  ) : null;
119  const text = comment?.text?.trim().length ? (
120    <ReactMarkdown renderers={renderers}>{comment.text}</ReactMarkdown>
121  ) : null;
122  return (
123    <>
124      {withDash && (shortText || text) ? ' - ' : null}
125      {shortText}
126      {text}
127    </>
128  );
129};
130