feat(website): render tsdoc examples (#8494)

This commit is contained in:
Suneet Tipirneni
2022-08-16 10:33:49 -04:00
committed by GitHub
parent c99b808882
commit 7116647947
22 changed files with 382 additions and 209 deletions

View File

@@ -1,6 +1,6 @@
import type { ApiEnum, ApiModel } from '@microsoft/api-extractor-model'; import type { ApiEnum, ApiModel } from '@microsoft/api-extractor-model';
import { DocItem } from './DocItem'; import { DocItem } from './DocItem';
import { CommentNodeContainer } from './comment/CommentNodeContainer'; import { nodeContainer } from './comment/CommentNodeContainer';
import { genToken, TokenDocumentation } from '~/util/parse.server'; import { genToken, TokenDocumentation } from '~/util/parse.server';
export interface EnumMemberData { export interface EnumMemberData {
@@ -18,9 +18,7 @@ export class DocEnum extends DocItem<ApiEnum> {
this.members = item.members.map((member) => ({ this.members = item.members.map((member) => ({
name: member.name, name: member.name,
initializerTokens: member.initializerExcerpt?.spannedTokens.map((token) => genToken(this.model, token)) ?? [], initializerTokens: member.initializerExcerpt?.spannedTokens.map((token) => genToken(this.model, token)) ?? [],
summary: member.tsdocComment summary: member.tsdocComment ? nodeContainer(member.tsdocComment.summarySection, model, member) : null,
? new CommentNodeContainer(member.tsdocComment.summarySection, model, member).toJSON()
: null,
})); }));
} }

View File

@@ -1,5 +1,7 @@
import type { ApiModel, ApiDeclaredItem } from '@microsoft/api-extractor-model'; import type { ApiModel, ApiDeclaredItem } from '@microsoft/api-extractor-model';
import { CommentNodeContainer } from './comment/CommentNodeContainer'; import { createCommentNode } from './comment';
import type { AnyDocNodeJSON } from './comment/CommentNode';
import type { DocNodeContainerJSON } from './comment/CommentNodeContainer';
import type { ReferenceData } from '~/util/model.server'; import type { ReferenceData } from '~/util/model.server';
import { resolveName, genReference, TokenDocumentation, genToken } from '~/util/parse.server'; import { resolveName, genReference, TokenDocumentation, genToken } from '~/util/parse.server';
@@ -13,9 +15,10 @@ export class DocItem<T extends ApiDeclaredItem = ApiDeclaredItem> {
public readonly excerpt: string; public readonly excerpt: string;
public readonly excerptTokens: TokenDocumentation[] = []; public readonly excerptTokens: TokenDocumentation[] = [];
public readonly kind: string; public readonly kind: string;
public readonly remarks: CommentNodeContainer | null; public readonly remarks: DocNodeContainerJSON | null;
public readonly summary: CommentNodeContainer | null; public readonly summary: DocNodeContainerJSON | null;
public readonly containerKey: string; public readonly containerKey: string;
public readonly comment: AnyDocNodeJSON | null;
public constructor(model: ApiModel, item: T) { public constructor(model: ApiModel, item: T) {
this.item = item; this.item = item;
@@ -26,12 +29,13 @@ export class DocItem<T extends ApiDeclaredItem = ApiDeclaredItem> {
this.excerpt = item.excerpt.text; this.excerpt = item.excerpt.text;
this.excerptTokens = item.excerpt.spannedTokens.map((token) => genToken(model, token)); this.excerptTokens = item.excerpt.spannedTokens.map((token) => genToken(model, token));
this.remarks = item.tsdocComment?.remarksBlock this.remarks = item.tsdocComment?.remarksBlock
? new CommentNodeContainer(item.tsdocComment.remarksBlock.content, model, item.parent) ? (createCommentNode(item.tsdocComment.remarksBlock, model, item.parent) as DocNodeContainerJSON)
: null; : null;
this.summary = item.tsdocComment?.summarySection this.summary = item.tsdocComment?.summarySection
? new CommentNodeContainer(item.tsdocComment.summarySection, model, item.parent) ? (createCommentNode(item.tsdocComment.summarySection, model, item.parent) as DocNodeContainerJSON)
: null; : null;
this.containerKey = item.containerKey; this.containerKey = item.containerKey;
this.comment = item.tsdocComment ? createCommentNode(item.tsdocComment, model, item.parent) : null;
} }
public get path() { public get path() {
@@ -54,13 +58,14 @@ export class DocItem<T extends ApiDeclaredItem = ApiDeclaredItem> {
return { return {
name: this.name, name: this.name,
referenceData: this.referenceData, referenceData: this.referenceData,
summary: this.summary?.toJSON() ?? null, summary: this.summary,
excerpt: this.excerpt, excerpt: this.excerpt,
excerptTokens: this.excerptTokens, excerptTokens: this.excerptTokens,
kind: this.kind, kind: this.kind,
remarks: this.remarks?.toJSON() ?? null, remarks: this.remarks,
path: this.path, path: this.path,
containerKey: this.containerKey, containerKey: this.containerKey,
comment: this.comment,
}; };
} }
} }

View File

@@ -0,0 +1,18 @@
import type { ApiModel, ApiItem } from '@microsoft/api-extractor-model';
import type { DocBlock } from '@microsoft/tsdoc';
import { createCommentNode } from '.';
import { blockTag, DocBlockTagJSON } from './CommentBlockTag';
import { AnyDocNodeJSON, DocNodeJSON, node } from './CommentNode';
export interface DocBlockJSON extends DocNodeJSON {
content: AnyDocNodeJSON[];
tag: DocBlockTagJSON;
}
export function block(block: DocBlock, model: ApiModel, parentItem?: ApiItem) {
return {
...node(block),
content: block.content.nodes.map((node) => createCommentNode(node, model, parentItem)),
tag: blockTag(block.blockTag),
};
}

View File

@@ -0,0 +1,13 @@
import type { DocBlockTag } from '@microsoft/tsdoc';
import { type DocNodeJSON, node } from './CommentNode';
export interface DocBlockTagJSON extends DocNodeJSON {
tagName: string;
}
export function blockTag(blockTag: DocBlockTag): DocBlockTagJSON {
return {
...node(blockTag),
tagName: blockTag.tagName,
};
}

View File

@@ -0,0 +1,13 @@
import type { DocCodeSpan } from '@microsoft/tsdoc';
import { DocNodeJSON, node } from './CommentNode';
export interface DocCodeSpanJSON extends DocNodeJSON {
code: string;
}
export function codeSpan(codeSpan: DocCodeSpan): DocCodeSpanJSON {
return {
...node(codeSpan),
code: codeSpan.code,
};
}

View File

@@ -1,22 +1,28 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; import type { DocNode, DocNodeKind } from '@microsoft/tsdoc';
import type { DocNode } from '@microsoft/tsdoc'; import type { DocBlockJSON } from './CommentBlock';
import type { DocCodeSpanJSON } from './CommentCodeSpan';
import type { DocNodeContainerJSON } from './CommentNodeContainer';
import type { DocFencedCodeJSON } from './FencedCodeCommentNode';
import type { DocLinkTagJSON } from './LinkTagCommentNode';
import type { DocPlainTextJSON } from './PlainTextCommentNode';
import type { DocCommentJSON } from './RootComment';
export class CommentNode<T extends DocNode = DocNode> { export interface DocNodeJSON {
public readonly node: T; kind: DocNodeKind;
public readonly kind: string; }
public readonly model: ApiModel;
public readonly parentItem: ApiItem | null; export type AnyDocNodeJSON =
| DocNodeJSON
public constructor(node: T, model: ApiModel, parentItem?: ApiItem) { | DocPlainTextJSON
this.node = node; | DocNodeContainerJSON
this.kind = node.kind; | DocLinkTagJSON
this.model = model; | DocFencedCodeJSON
this.parentItem = parentItem ?? null; | DocBlockJSON
} | DocCommentJSON
| DocCodeSpanJSON;
public toJSON() {
return { export function node(node: DocNode): DocNodeJSON {
kind: this.kind, return {
}; kind: node.kind as DocNodeKind,
} };
} }

View File

@@ -1,20 +1,19 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocNodeContainer } from '@microsoft/tsdoc'; import type { DocNodeContainer } from '@microsoft/tsdoc';
import { createCommentNode } from '.'; import { createCommentNode } from '.';
import { CommentNode } from './CommentNode'; import { AnyDocNodeJSON, DocNodeJSON, node } from './CommentNode';
export class CommentNodeContainer<T extends DocNodeContainer = DocNodeContainer> extends CommentNode<DocNodeContainer> { export interface DocNodeContainerJSON extends DocNodeJSON {
public readonly nodes: CommentNode[]; nodes: AnyDocNodeJSON[];
}
public constructor(container: T, model: ApiModel, parentItem?: ApiItem) {
super(container, model, parentItem); export function nodeContainer(
this.nodes = container.nodes.map((node) => createCommentNode(node, model, parentItem)); container: DocNodeContainer,
} model: ApiModel,
parentItem?: ApiItem,
public override toJSON() { ): DocNodeContainerJSON {
return { return {
...super.toJSON(), ...node(container),
nodes: this.nodes.map((node) => node.toJSON()), nodes: container.nodes.map((node) => createCommentNode(node, model, parentItem)),
}; };
}
} }

View File

@@ -1,22 +1,15 @@
import type { ApiModel, ApiItem } from '@microsoft/api-extractor-model';
import type { DocFencedCode } from '@microsoft/tsdoc'; import type { DocFencedCode } from '@microsoft/tsdoc';
import { CommentNode } from './CommentNode'; import { DocNodeJSON, node } from './CommentNode';
export class FencedCodeCommentNode extends CommentNode<DocFencedCode> { export interface DocFencedCodeJSON extends DocNodeJSON {
public readonly code: string; code: string;
public readonly language: string; language: string;
}
public constructor(node: DocFencedCode, model: ApiModel, parentItem?: ApiItem) {
super(node, model, parentItem); export function fencedCode(fencedCode: DocFencedCode) {
this.code = node.code; return {
this.language = node.language; ...node(fencedCode),
} language: fencedCode.language,
code: fencedCode.code,
public override toJSON() { };
return {
...super.toJSON(),
code: this.code,
language: this.language,
};
}
} }

View File

@@ -1,8 +1,14 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model'; import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocDeclarationReference, DocLinkTag } from '@microsoft/tsdoc'; import type { DocDeclarationReference, DocLinkTag } from '@microsoft/tsdoc';
import { CommentNode } from './CommentNode'; import { DocNodeJSON, node } from './CommentNode';
import { generatePath, resolveName } from '~/util/parse.server'; import { generatePath, resolveName } from '~/util/parse.server';
export interface DocLinkTagJSON extends DocNodeJSON {
text: string | null;
codeDestination: LinkTagCodeLink | null;
urlDestination: string | null;
}
export function genToken( export function genToken(
model: ApiModel, model: ApiModel,
ref: DocDeclarationReference, ref: DocDeclarationReference,
@@ -31,24 +37,16 @@ export interface LinkTagCodeLink {
path: string; path: string;
} }
export class LinkTagCommentNode extends CommentNode<DocLinkTag> { export function linkTagNode(linkNode: DocLinkTag, model: ApiModel, parentItem?: ApiItem): DocLinkTagJSON {
public readonly codeDestination: LinkTagCodeLink | null; const codeDestination =
public readonly text: string | null; linkNode.codeDestination && parentItem ? genToken(model, linkNode.codeDestination, parentItem) : null;
public readonly urlDestination: string | null; const text = linkNode.linkText ?? null;
const urlDestination = linkNode.urlDestination ?? null;
public constructor(node: DocLinkTag, model: ApiModel, parentItem?: ApiItem) { return {
super(node, model, parentItem); ...node(linkNode),
this.codeDestination = node.codeDestination ? genToken(model, node.codeDestination, this.parentItem) : null; text,
this.text = node.linkText ?? null; codeDestination,
this.urlDestination = node.urlDestination ?? null; urlDestination,
} };
public override toJSON() {
return {
...super.toJSON(),
text: this.text,
codeDestination: this.codeDestination,
urlDestination: this.urlDestination,
};
}
} }

View File

@@ -1,19 +1,13 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocPlainText } from '@microsoft/tsdoc'; import type { DocPlainText } from '@microsoft/tsdoc';
import { CommentNode } from './CommentNode'; import { DocNodeJSON, node } from './CommentNode';
export class PlainTextCommentNode extends CommentNode<DocPlainText> { export interface DocPlainTextJSON extends DocNodeJSON {
public readonly text: string; text: string;
}
public constructor(node: DocPlainText, model: ApiModel, parentItem?: ApiItem) {
super(node, model, parentItem); export function plainTextNode(plainTextNode: DocPlainText): DocPlainTextJSON {
this.text = node.text; return {
} ...node(plainTextNode),
text: plainTextNode.text,
public override toJSON() { };
return {
...super.toJSON(),
text: this.text,
};
}
} }

View File

@@ -0,0 +1,20 @@
import type { ApiItem, ApiModel } from '@microsoft/api-extractor-model';
import type { DocComment } from '@microsoft/tsdoc';
import { createCommentNode } from '.';
import { block, DocBlockJSON } from './CommentBlock';
import { DocNodeJSON, node } from './CommentNode';
export interface DocCommentJSON extends DocNodeJSON {
summary: DocNodeJSON[];
remarks: DocNodeJSON[];
customBlocks: DocBlockJSON[];
}
export function comment(comment: DocComment, model: ApiModel, parentItem?: ApiItem): DocCommentJSON {
return {
...node(comment),
summary: comment.summarySection.nodes.map((node) => createCommentNode(node, model, parentItem)),
remarks: comment.remarksBlock?.content.nodes.map((node) => createCommentNode(node, model, parentItem)) ?? [],
customBlocks: comment.customBlocks.map((_block) => block(_block, model, parentItem)),
};
}

View File

@@ -1,22 +1,43 @@
import type { ApiModel, ApiItem } from '@microsoft/api-extractor-model'; import type { ApiModel, ApiItem } from '@microsoft/api-extractor-model';
import type { DocNode, DocPlainText, DocLinkTag, DocParagraph, DocFencedCode } from '@microsoft/tsdoc'; import {
import { CommentNode } from './CommentNode'; type DocNode,
import { CommentNodeContainer } from './CommentNodeContainer'; type DocPlainText,
import { FencedCodeCommentNode } from './FencedCodeCommentNode'; type DocLinkTag,
import { LinkTagCommentNode } from './LinkTagCommentNode'; type DocParagraph,
import { PlainTextCommentNode } from './PlainTextCommentNode'; type DocFencedCode,
DocNodeKind,
type DocBlock,
DocComment,
DocCodeSpan,
} from '@microsoft/tsdoc';
import { block } from './CommentBlock';
import { codeSpan } from './CommentCodeSpan';
import type { AnyDocNodeJSON } from './CommentNode';
import { node as _node } from './CommentNode';
import { nodeContainer } from './CommentNodeContainer';
import { fencedCode } from './FencedCodeCommentNode';
import { linkTagNode } from './LinkTagCommentNode';
import { plainTextNode } from './PlainTextCommentNode';
import { comment } from './RootComment';
export function createCommentNode(node: DocNode, model: ApiModel, parentItem?: ApiItem): CommentNode { export function createCommentNode(node: DocNode, model: ApiModel, parentItem?: ApiItem): AnyDocNodeJSON {
switch (node.kind) { switch (node.kind) {
case 'PlainText': case DocNodeKind.PlainText:
return new PlainTextCommentNode(node as DocPlainText, model, parentItem); return plainTextNode(node as DocPlainText);
case 'LinkTag': case DocNodeKind.LinkTag:
return new LinkTagCommentNode(node as DocLinkTag, model, parentItem); return linkTagNode(node as DocLinkTag, model, parentItem);
case 'Paragraph': case DocNodeKind.Paragraph:
return new CommentNodeContainer(node as DocParagraph, model, parentItem); case DocNodeKind.Section:
case 'FencedCode': return nodeContainer(node as DocParagraph, model, parentItem);
return new FencedCodeCommentNode(node as DocFencedCode, model, parentItem); case DocNodeKind.FencedCode:
return fencedCode(node as DocFencedCode);
case DocNodeKind.CodeSpan:
return codeSpan(node as DocCodeSpan);
case DocNodeKind.Block:
return block(node as DocBlock, model, parentItem);
case DocNodeKind.Comment:
return comment(node as DocComment, model, parentItem);
default: default:
return new CommentNode(node, model, parentItem); return _node(node);
} }
} }

View File

@@ -1,8 +1,9 @@
import { Group, Stack, Title } from '@mantine/core'; import { Group, Stack, Title } from '@mantine/core';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { CommentSection } from './Comment';
import { HyperlinkedText } from './HyperlinkedText'; import { HyperlinkedText } from './HyperlinkedText';
import { TSDoc } from './tsdoc/TSDoc';
import type { DocItem } from '~/DocModel/DocItem'; import type { DocItem } from '~/DocModel/DocItem';
import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode';
import type { TokenDocumentation } from '~/util/parse.server'; import type { TokenDocumentation } from '~/util/parse.server';
export enum CodeListingSeparatorType { export enum CodeListingSeparatorType {
@@ -16,6 +17,7 @@ export function CodeListing({
summary, summary,
typeTokens, typeTokens,
children, children,
comment,
}: { }: {
name: string; name: string;
summary?: ReturnType<DocItem['toJSON']>['summary']; summary?: ReturnType<DocItem['toJSON']>['summary'];
@@ -23,6 +25,7 @@ export function CodeListing({
separator?: CodeListingSeparatorType; separator?: CodeListingSeparatorType;
children?: ReactNode; children?: ReactNode;
className?: string | undefined; className?: string | undefined;
comment?: AnyDocNodeJSON | null;
}) { }) {
return ( return (
<Stack key={name}> <Stack key={name}>
@@ -35,7 +38,8 @@ export function CodeListing({
<HyperlinkedText tokens={typeTokens} /> <HyperlinkedText tokens={typeTokens} />
</Title> </Title>
</Group> </Group>
{summary && <CommentSection node={summary} />} {summary && <TSDoc node={summary} />}
{comment && <TSDoc node={comment} />}
{children} {children}
</Stack> </Stack>
); );

View File

@@ -1,85 +0,0 @@
import { Anchor, Box, Text } from '@mantine/core';
import Link from 'next/link';
import type { ReactNode } from 'react';
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import type { CommentNode } from '~/DocModel/comment/CommentNode';
import type { CommentNodeContainer } from '~/DocModel/comment/CommentNodeContainer';
import type { FencedCodeCommentNode } from '~/DocModel/comment/FencedCodeCommentNode';
import type { LinkTagCommentNode } from '~/DocModel/comment/LinkTagCommentNode';
import type { PlainTextCommentNode } from '~/DocModel/comment/PlainTextCommentNode';
export function CommentSection({ node }: { node: ReturnType<CommentNode['toJSON']> }): JSX.Element {
const createNode = (node: ReturnType<CommentNode['toJSON']>, idx?: number): ReactNode => {
switch (node.kind) {
case 'PlainText':
return (
<Text key={idx} span>
{(node as ReturnType<PlainTextCommentNode['toJSON']>).text}
</Text>
);
case 'Paragraph':
return (
<Text key={idx} inline>
{(node as ReturnType<CommentNodeContainer['toJSON']>).nodes.map((node, idx) => createNode(node, idx))}
</Text>
);
case 'SoftBreak':
return <br key={idx} />;
case 'LinkTag': {
const { codeDestination, urlDestination, text } = node as ReturnType<LinkTagCommentNode['toJSON']>;
if (codeDestination) {
return (
<Link key={idx} href={codeDestination.path} passHref>
<Anchor component="a" className="font-mono">
{text ?? codeDestination.name}
</Anchor>
</Link>
);
}
if (urlDestination) {
return (
<Link key={idx} href={urlDestination} passHref>
<Anchor component="a" className="font-mono">
{text ?? urlDestination}
</Anchor>
</Link>
);
}
return null;
}
case 'FencedCodeBlock': {
const { language, code } = node as ReturnType<FencedCodeCommentNode['toJSON']>;
return (
<SyntaxHighlighter
key={idx}
wrapLines
wrapLongLines
language={language}
style={vscDarkPlus}
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
>
{code}
</SyntaxHighlighter>
);
}
default:
break;
}
return null;
};
return (
<Box>
{node.kind === 'Paragraph' || node.kind === 'Section' ? (
<>{(node as CommentNodeContainer).nodes.map((node, idx) => createNode(node, idx))}</>
) : (
createNode(node)
)}
</Box>
);
}

View File

@@ -5,11 +5,12 @@ import { VscListSelection, VscSymbolParameter } from 'react-icons/vsc';
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter'; import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import { CodeListingSeparatorType } from './CodeListing'; import { CodeListingSeparatorType } from './CodeListing';
import { CommentSection } from './Comment';
import { HyperlinkedText } from './HyperlinkedText'; import { HyperlinkedText } from './HyperlinkedText';
import { Section } from './Section'; import { Section } from './Section';
import { TypeParamTable } from './TypeParamTable'; import { TypeParamTable } from './TypeParamTable';
import { TSDoc } from './tsdoc/TSDoc';
import type { DocItem } from '~/DocModel/DocItem'; import type { DocItem } from '~/DocModel/DocItem';
import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode';
import { generateIcon } from '~/util/icon'; import { generateIcon } from '~/util/icon';
import type { TokenDocumentation, TypeParameterData } from '~/util/parse.server'; import type { TokenDocumentation, TypeParameterData } from '~/util/parse.server';
@@ -22,6 +23,7 @@ export interface DocContainerProps {
extendsTokens?: TokenDocumentation[] | null; extendsTokens?: TokenDocumentation[] | null;
implementsTokens?: TokenDocumentation[][]; implementsTokens?: TokenDocumentation[][];
typeParams?: TypeParameterData[]; typeParams?: TypeParameterData[];
comment?: AnyDocNodeJSON | null;
} }
export function DocContainer({ export function DocContainer({
@@ -46,7 +48,7 @@ export function DocContainer({
</Title> </Title>
<Section title="Summary" icon={<VscListSelection />} padded dense={matches}> <Section title="Summary" icon={<VscListSelection />} padded dense={matches}>
{summary ? <CommentSection node={summary} /> : <Text>No summary provided.</Text>} {summary ? <TSDoc node={summary} /> : <Text>No summary provided.</Text>}
</Section> </Section>
<Box px="xs" pb="xs"> <Box px="xs" pb="xs">

View File

@@ -1,7 +1,7 @@
import { Group, Stack, Title } from '@mantine/core'; import { Group, Stack, Title } from '@mantine/core';
import { CommentSection } from './Comment';
import { HyperlinkedText } from './HyperlinkedText'; import { HyperlinkedText } from './HyperlinkedText';
import { ParameterTable } from './ParameterTable'; import { ParameterTable } from './ParameterTable';
import { TSDoc } from './tsdoc/TSDoc';
import type { DocMethod } from '~/DocModel/DocMethod'; import type { DocMethod } from '~/DocModel/DocMethod';
import type { DocMethodSignature } from '~/DocModel/DocMethodSignature'; import type { DocMethodSignature } from '~/DocModel/DocMethodSignature';
@@ -32,7 +32,8 @@ export function MethodItem({ data }: { data: MethodResolvable }) {
</Stack> </Stack>
</Group> </Group>
<Group sx={{ display: data.summary || data.parameters.length ? 'block' : 'none' }} mb="lg"> <Group sx={{ display: data.summary || data.parameters.length ? 'block' : 'none' }} mb="lg">
{data.summary ? <CommentSection node={data.summary} /> : null} {data.summary ? <TSDoc node={data.summary} /> : null}
{data.comment ? <TSDoc node={data.comment} /> : null}
{data.parameters.length ? <ParameterTable data={data.parameters} /> : null} {data.parameters.length ? <ParameterTable data={data.parameters} /> : null}
</Group> </Group>
</Stack> </Stack>

View File

@@ -6,7 +6,13 @@ export function PropertyList({ data }: { data: ReturnType<DocProperty['toJSON']>
return ( return (
<Stack> <Stack>
{data.map((prop) => ( {data.map((prop) => (
<CodeListing key={prop.name} name={prop.name} typeTokens={prop.propertyTypeTokens} summary={prop.summary} /> <CodeListing
key={prop.name}
name={prop.name}
typeTokens={prop.propertyTypeTokens}
summary={prop.summary}
comment={prop.comment}
/>
))} ))}
</Stack> </Stack>
); );

View File

@@ -12,6 +12,7 @@ export function Class({ data }: { data: ReturnType<DocClass['toJSON']> }) {
typeParams={data.typeParameterData} typeParams={data.typeParameterData}
extendsTokens={data.extendsTokens} extendsTokens={data.extendsTokens}
implementsTokens={data.implementsTokens} implementsTokens={data.implementsTokens}
comment={data.comment}
> >
<PropertiesSection data={data.properties} /> <PropertiesSection data={data.properties} />
<MethodsSection data={data.methods} /> <MethodsSection data={data.methods} />

View File

@@ -0,0 +1,40 @@
import { StandardTags } from '@microsoft/tsdoc';
import type { ReactNode } from 'react';
export interface BlockProps {
children: ReactNode;
title: string;
}
export function Block({ children, title }: BlockProps) {
return (
<div>
<h3 className="m-0">{title}</h3>
{children}
</div>
);
}
export interface BlockCommentProps {
tagName: string;
children: ReactNode;
index?: number | undefined;
}
export interface ExampleBlockProps {
children: ReactNode;
exampleIndex?: number | undefined;
}
export function ExampleBlock({ children, exampleIndex }: ExampleBlockProps): JSX.Element {
return <Block title={`Example ${exampleIndex ? exampleIndex : ''}`}>{children}</Block>;
}
export function BlockComment({ children, tagName, index }: BlockCommentProps): JSX.Element {
switch (tagName.toUpperCase()) {
case StandardTags.example.tagNameWithUpperCase:
return <ExampleBlock exampleIndex={index}>{children}</ExampleBlock>;
default: // TODO: Support more blocks in the future.
return <></>;
}
}

View File

@@ -0,0 +1,124 @@
import { Anchor, Box, Text } from '@mantine/core';
import { DocNodeKind, StandardTags } from '@microsoft/tsdoc';
import Link from 'next/link';
import type { ReactNode } from 'react';
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import { BlockComment } from './BlockComment';
import type { DocBlockJSON } from '~/DocModel/comment/CommentBlock';
import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode';
import type { DocNodeContainerJSON } from '~/DocModel/comment/CommentNodeContainer';
import type { DocFencedCodeJSON } from '~/DocModel/comment/FencedCodeCommentNode';
import type { DocLinkTagJSON } from '~/DocModel/comment/LinkTagCommentNode';
import type { DocPlainTextJSON } from '~/DocModel/comment/PlainTextCommentNode';
import type { DocCommentJSON } from '~/DocModel/comment/RootComment';
export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
let numberOfExamples = 0;
let exampleIndex = 0;
const createNode = (node: AnyDocNodeJSON, idx?: number): ReactNode => {
switch (node.kind) {
case DocNodeKind.PlainText:
return (
<Text key={idx} span>
{(node as DocPlainTextJSON).text}
</Text>
);
case DocNodeKind.Paragraph:
return (
<Text key={idx} inline>
{(node as DocNodeContainerJSON).nodes.map((node, idx) => createNode(node, idx))}
</Text>
);
case DocNodeKind.SoftBreak:
return <br key={idx} />;
case DocNodeKind.LinkTag: {
const { codeDestination, urlDestination, text } = node as DocLinkTagJSON;
if (codeDestination) {
return (
<Link key={idx} href={codeDestination.path} passHref>
<Anchor component="a" className="font-mono">
{text ?? codeDestination.name}
</Anchor>
</Link>
);
}
if (urlDestination) {
return (
<Link key={idx} href={urlDestination} passHref>
<Anchor component="a" className="font-mono">
{text ?? urlDestination}
</Anchor>
</Link>
);
}
return null;
}
case DocNodeKind.CodeSpan: {
const { code } = node as DocFencedCodeJSON;
return (
<pre key={idx} className="inline">
{code}
</pre>
);
}
case DocNodeKind.FencedCode: {
const { language, code } = node as DocFencedCodeJSON;
return (
<SyntaxHighlighter
key={idx}
wrapLines
wrapLongLines
language={language}
style={vscDarkPlus}
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
>
{code}
</SyntaxHighlighter>
);
}
case DocNodeKind.Block: {
const { tag } = node as DocBlockJSON;
if (tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase) {
exampleIndex++;
}
const index = numberOfExamples > 1 ? exampleIndex : undefined;
return (
<BlockComment tagName={tag.tagName} key={idx} index={index}>
{(node as DocBlockJSON).content.map((node, idx) => createNode(node, idx))}
</BlockComment>
);
}
case DocNodeKind.Comment: {
const comment = node as DocCommentJSON;
// Cheat a bit by finding out how many comments we have beforehand...
numberOfExamples = comment.customBlocks.filter(
(block) => block.tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase,
).length;
return <div>{comment.customBlocks.map((node, idx) => createNode(node, idx))}</div>;
}
default:
break;
}
return null;
};
return (
<Box>
{node.kind === 'Paragraph' || node.kind === 'Section' ? (
<>{(node as DocNodeContainerJSON).nodes.map((node, idx) => createNode(node, idx))}</>
) : (
createNode(node)
)}
</Box>
);
}

View File

@@ -4,6 +4,7 @@ export type Awaitable<T> = T | Promise<T>;
/** /**
* Yields the numbers in the given range as an array * Yields the numbers in the given range as an array
*
* @example * @example
* ``` * ```
* range({ start: 3, end: 5 }); // [3, 4, 5] * range({ start: 3, end: 5 }); // [3, 4, 5]

View File

@@ -79,6 +79,7 @@ export interface OptionalWebSocketManagerOptions {
/** /**
* The ids of the shards this WebSocketManager should manage. * The ids of the shards this WebSocketManager should manage.
* Use `null` to simply spawn 0 through `shardCount - 1` * Use `null` to simply spawn 0 through `shardCount - 1`
*
* @example * @example
* ``` * ```
* const manager = new WebSocketManager({ * const manager = new WebSocketManager({