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