From fd4844ddb92159a8240e23939f7dac06536a820d Mon Sep 17 00:00:00 2001 From: Suneet Tipirneni <77477100+suneettipirneni@users.noreply.github.com> Date: Sat, 13 Aug 2022 14:14:23 -0400 Subject: [PATCH] feat(website): add support for function overloads (#8474) Co-authored-by: Noel --- packages/website/src/DocModel/DocItem.ts | 20 +++++++++ .../website/src/components/DocContainer.tsx | 14 +++--- .../website/src/components/ItemSidebar.tsx | 3 +- .../website/src/components/ListSidebar.tsx | 14 ++++-- .../website/src/components/SidebarLayout.tsx | 4 +- .../website/src/components/model/Function.tsx | 2 +- packages/website/src/contexts/member.tsx | 15 +++++++ packages/website/src/pages/docs/[...slug].tsx | 30 ++++++++++--- packages/website/src/util/model.server.ts | 43 +++++++++++-------- packages/website/src/util/parse.server.ts | 8 +++- 10 files changed, 114 insertions(+), 39 deletions(-) create mode 100644 packages/website/src/contexts/member.tsx diff --git a/packages/website/src/DocModel/DocItem.ts b/packages/website/src/DocModel/DocItem.ts index c99607e4d..062f3c2c2 100644 --- a/packages/website/src/DocModel/DocItem.ts +++ b/packages/website/src/DocModel/DocItem.ts @@ -15,6 +15,7 @@ export class DocItem { public readonly kind: string; public readonly remarks: CommentNodeContainer | null; public readonly summary: CommentNodeContainer | null; + public readonly containerKey: string; public constructor(model: ApiModel, item: T) { this.item = item; @@ -30,6 +31,23 @@ export class DocItem { this.summary = item.tsdocComment?.summarySection ? new CommentNodeContainer(item.tsdocComment.summarySection, model, item.parent) : null; + this.containerKey = item.containerKey; + } + + public get path() { + const path = []; + for (const item of this.item.getHierarchy()) { + switch (item.kind) { + case 'None': + case 'EntryPoint': + case 'Model': + break; + default: + path.push(resolveName(item)); + } + } + + return path; } public toJSON() { @@ -41,6 +59,8 @@ export class DocItem { excerptTokens: this.excerptTokens, kind: this.kind, remarks: this.remarks?.toJSON() ?? null, + path: this.path, + containerKey: this.containerKey, }; } } diff --git a/packages/website/src/components/DocContainer.tsx b/packages/website/src/components/DocContainer.tsx index 554eb055f..c35cf85a4 100644 --- a/packages/website/src/components/DocContainer.tsx +++ b/packages/website/src/components/DocContainer.tsx @@ -42,6 +42,13 @@ export function DocContainer({
+
} title="Summary" className="dark:text-white"> + {summary ? ( + + ) : ( +

No summary provided.

+ )} +
) : null}
-
} title="Summary" className="dark:text-white"> - {summary ? ( - - ) : ( -

No summary provided.

- )} -
{typeParams?.length ? (
} diff --git a/packages/website/src/components/ItemSidebar.tsx b/packages/website/src/components/ItemSidebar.tsx index a9af97cc3..5ece974c3 100644 --- a/packages/website/src/components/ItemSidebar.tsx +++ b/packages/website/src/components/ItemSidebar.tsx @@ -1,6 +1,7 @@ import { FiMenu } from 'react-icons/fi'; import { VscPackage } from 'react-icons/vsc'; import { ListSidebar } from './ListSidebar'; +import type { DocItem } from '~/DocModel/DocItem'; import type { getMembers } from '~/util/parse.server'; export interface ItemListProps { @@ -9,7 +10,7 @@ export interface ItemListProps { members: ReturnType; }; - selectedMember?: string | undefined; + selectedMember?: ReturnType | undefined; } function onMenuClick() { diff --git a/packages/website/src/components/ListSidebar.tsx b/packages/website/src/components/ListSidebar.tsx index 8c3ab1e2b..412f40c8e 100644 --- a/packages/website/src/components/ListSidebar.tsx +++ b/packages/website/src/components/ListSidebar.tsx @@ -9,12 +9,13 @@ import { } from 'react-icons/vsc'; import type { ItemListProps } from './ItemSidebar'; import { Section } from './Section'; +import type { DocItem } from '~/DocModel/DocItem'; export type Members = ItemListProps['data']['members']; export interface ListSidebarSectionProps { members: Members; - selectedMember?: string | undefined; + selectedMember?: ReturnType | undefined; title: string; } @@ -93,12 +94,12 @@ export function ListSidebar({ members, selectedMember }: ListSidebarSectionProps {groupItems[group].map((member, i) => ( ))}
diff --git a/packages/website/src/components/SidebarLayout.tsx b/packages/website/src/components/SidebarLayout.tsx index cdd5b2f6c..821e281fa 100644 --- a/packages/website/src/components/SidebarLayout.tsx +++ b/packages/website/src/components/SidebarLayout.tsx @@ -15,13 +15,13 @@ export function SidebarLayout({
{packageName && data ? ( - + ) : null}
{children}
{packageName && data?.member ? ( - + ) : null}
diff --git a/packages/website/src/components/model/Function.tsx b/packages/website/src/components/model/Function.tsx index 77bd86424..f7cd41c3a 100644 --- a/packages/website/src/components/model/Function.tsx +++ b/packages/website/src/components/model/Function.tsx @@ -9,7 +9,7 @@ export interface FunctionProps { export function Function({ data }: FunctionProps) { return ( ; + +export const MemberContext = createContext(undefined); + +export interface MemberProviderProps { + member: DocItemJSON | undefined; + children: React.ReactNode; +} + +export const MemberProvider = ({ member, children }: MemberProviderProps) => ( + {children} +); diff --git a/packages/website/src/pages/docs/[...slug].tsx b/packages/website/src/pages/docs/[...slug].tsx index c6630c032..45888a69d 100644 --- a/packages/website/src/pages/docs/[...slug].tsx +++ b/packages/website/src/pages/docs/[...slug].tsx @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-throw-literal */ +import { ApiFunction } from '@microsoft/api-extractor-model'; import type { GetStaticPaths, GetStaticProps } from 'next/types'; import type { DocClass } from '~/DocModel/DocClass'; import type { DocEnum } from '~/DocModel/DocEnum'; @@ -14,8 +15,9 @@ import { Function } from '~/components/model/Function'; import { Interface } from '~/components/model/Interface'; import { TypeAlias } from '~/components/model/TypeAlias'; import { Variable } from '~/components/model/Variable'; +import { MemberProvider } from '~/contexts/member'; import { createApiModel } from '~/util/api-model.server'; -import { findMember } from '~/util/model.server'; +import { findMember, findMemberByKey } from '~/util/model.server'; import { findPackage, getMembers } from '~/util/parse.server'; export const getStaticPaths: GetStaticPaths = async () => { @@ -38,7 +40,16 @@ export const getStaticPaths: GetStaticPaths = async () => { return [ { params: { slug: ['main', 'packages', packageName] } }, - ...getMembers(pkg!).map((member) => ({ params: { slug: ['main', 'packages', packageName, member.name] } })), + ...getMembers(pkg!).map((member) => { + if (member.kind === 'Function' && member.overloadIndex) { + return { + params: { + slug: ['main', 'packages', packageName, `${member.name}:${member.overloadIndex}`], + }, + }; + } + return { params: { slug: ['main', 'packages', packageName, member.name] } }; + }), ]; } catch { return { params: { slug: ['', '', '', ''] } }; @@ -54,7 +65,9 @@ export const getStaticPaths: GetStaticPaths = async () => { }; export const getStaticProps: GetStaticProps = async ({ params }) => { - const [branchName = 'main', , packageName = 'builders', memberName] = params!.slug as string[]; + const [branchName = 'main', , packageName = 'builders', member = 'ActionRowBuilder'] = params!.slug as string[]; + + const [memberName, overloadIndex] = member.split(':') as [string, string | undefined]; try { const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/${branchName}.api.json`); @@ -64,12 +77,17 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { const model = createApiModel(data); const pkg = findPackage(model, packageName); + let { containerKey, name } = findMember(model, packageName, memberName)!; + if (overloadIndex) { + containerKey = ApiFunction.getContainerKey(name, parseInt(overloadIndex, 10)); + } + return { props: { packageName, data: { members: pkg ? getMembers(pkg) : [], - member: memberName ? findMember(model, packageName, memberName)?.toJSON() ?? null : null, + member: memberName ? findMemberByKey(model, packageName, containerKey)?.toJSON() ?? null : null, }, }, }; @@ -108,6 +126,8 @@ export default function Slug( return props.error ? (
{props.error}
) : ( - {props.data?.member ? member(props.data.member) : null} + + {props.data?.member ? member(props.data.member) : null} + ); } diff --git a/packages/website/src/util/model.server.ts b/packages/website/src/util/model.server.ts index 9180b99f0..d6da14a18 100644 --- a/packages/website/src/util/model.server.ts +++ b/packages/website/src/util/model.server.ts @@ -5,6 +5,7 @@ import { ApiEnum, ApiFunction, ApiInterface, + ApiItem, ApiItemKind, ApiModel, ApiTypeAlias, @@ -24,10 +25,7 @@ export interface ReferenceData { path: string; } -export function findMember(model: ApiModel, packageName: string, memberName: string): DocItem | undefined { - const pkg = findPackage(model, packageName)!; - const member = (pkg.members[0] as ApiEntryPoint).findMembersByName(memberName)[0]; - +function createDocItem(model: ApiModel, member: ApiItem) { if (!(member instanceof ApiDeclaredItem)) { return undefined; } @@ -48,19 +46,26 @@ export function findMember(model: ApiModel, packageName: string, memberName: str default: return new DocItem(model, member); } - - // return { - // name: resolveName(member), - // kind: member.kind, - // summary: resolveDocComment(member), - // excerpt: member.excerpt.text, - // tokens: member.excerpt.spannedTokens.map((token) => genToken(model, token)), - // refs: [...findReferences(model, member.excerpt).values()].map(genReference), - // members: getProperties(member).map((member) => ({ - // tokens: member.excerpt.spannedTokens.map((token) => genToken(model, token)), - // summary: resolveDocComment(member), - // })), - // parameters: member instanceof ApiFunction ? member.parameters.map((param) => genParameter(model, param)) : [], - // foo: excerpt.spannedTokens.map((token) => genToken(model, token)), - // }; +} + +export function findMemberByKey(model: ApiModel, packageName: string, containerKey: string) { + const pkg = findPackage(model, packageName)!; + const member = (pkg.members[0] as ApiEntryPoint).tryGetMemberByKey(containerKey); + + if (!member) { + return undefined; + } + + return createDocItem(model, member); +} + +export function findMember(model: ApiModel, packageName: string, memberName: string): DocItem | undefined { + const pkg = findPackage(model, packageName)!; + const member = (pkg.members[0] as ApiEntryPoint).findMembersByName(memberName)[0]; + + if (!member) { + return undefined; + } + + return createDocItem(model, member); } diff --git a/packages/website/src/util/parse.server.ts b/packages/website/src/util/parse.server.ts index 6cff797e3..64534551a 100644 --- a/packages/website/src/util/parse.server.ts +++ b/packages/website/src/util/parse.server.ts @@ -11,6 +11,7 @@ import { type ExcerptToken, type Parameter, type TypeParameter, + ApiFunction, } from '@microsoft/api-extractor-model'; import type { DocNode, DocParagraph, DocPlainText } from '@microsoft/tsdoc'; import { Meaning, ModuleSource } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference'; @@ -32,6 +33,9 @@ export function generatePath(items: readonly ApiItem[]) { case ApiItemKind.Package: path += `${item.displayName}/`; break; + case ApiItemKind.Function: + path += `${item.displayName}:${(item as ApiFunction).overloadIndex}/`; + break; default: path += `${item.displayName}/`; } @@ -195,8 +199,10 @@ export function genParameter(model: ApiModel, param: Parameter): ParameterDocume export function getMembers(pkg: ApiPackage) { return pkg.members[0]!.members.map((member) => ({ name: member.displayName, - kind: member.kind, + kind: member.kind as string, path: generatePath(member.getHierarchy()), + containerKey: member.containerKey, + overloadIndex: member.kind === 'Function' ? (member as ApiFunction).overloadIndex : null, })); }