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
50const nonLinkableTypes = ['Date', 'Uint8Array'];
51
52export const resolveTypeName = ({
53  elementType,
54  name,
55  type,
56  types,
57  typeArguments,
58}: TypeDefinitionData): string | JSX.Element => {
59  if (name) {
60    if (type === 'reference') {
61      if (typeArguments) {
62        if (name === 'Promise') {
63          return (
64            <span>
65              {'Promise<'}
66              {typeArguments.map(resolveTypeName)}
67              {'>'}
68            </span>
69          );
70        } else {
71          return `${typeArguments.map(resolveTypeName)}`;
72        }
73      } else {
74        if (nonLinkableTypes.includes(name)) {
75          return name;
76        } else {
77          return (
78            <InternalLink href={`#${name.toLowerCase()}`} key={`type-link-${name}`}>
79              {name}
80            </InternalLink>
81          );
82        }
83      }
84    } else {
85      return name;
86    }
87  } else if (elementType?.name) {
88    if (type === 'array') {
89      return elementType.name + '[]';
90    }
91    return elementType.name + type;
92  } else if (type === 'union' && types?.length) {
93    return types.map((t: TypeDefinitionTypesData) => `${t.name || t.value}`).join(' | ');
94  }
95  return 'undefined';
96};
97
98export const renderParam = ({ comment, name, type }: MethodParamData): JSX.Element => (
99  <LI key={`param-${name}`}>
100    <B>
101      {name} (<InlineCode>{resolveTypeName(type)}</InlineCode>)
102    </B>
103    <CommentTextBlock comment={comment} renderers={inlineRenderers} withDash />
104  </LI>
105);
106
107type CommentTextBlockProps = {
108  comment?: CommentData;
109  renderers?: React.ComponentProps<typeof ReactMarkdown>['renderers'];
110  withDash?: boolean;
111};
112
113export const CommentTextBlock: React.FC<CommentTextBlockProps> = ({
114  comment,
115  renderers,
116  withDash,
117}) => {
118  const shortText = comment?.shortText?.trim().length ? (
119    <ReactMarkdown renderers={renderers}>{comment.shortText}</ReactMarkdown>
120  ) : null;
121  const text = comment?.text?.trim().length ? (
122    <ReactMarkdown renderers={renderers}>{comment.text}</ReactMarkdown>
123  ) : null;
124  return (
125    <>
126      {withDash && (shortText || text) ? ' - ' : null}
127      {shortText}
128      {text}
129    </>
130  );
131};
132