diff --git a/packages/website/src/DocModel/ApiNodeJSONEncoder.ts b/packages/website/src/DocModel/ApiNodeJSONEncoder.ts index 526f91912..4bfc9febd 100644 --- a/packages/website/src/DocModel/ApiNodeJSONEncoder.ts +++ b/packages/website/src/DocModel/ApiNodeJSONEncoder.ts @@ -150,30 +150,30 @@ export interface ApiConstructorJSON extends ApiItemJSON, ApiParameterListJSON { // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class ApiNodeJSONEncoder { - public static encode(model: ApiModel, node: ApiItem) { + public static encode(model: ApiModel, node: ApiItem, version: string) { if (!(node instanceof ApiDeclaredItem)) { throw new Error(`Cannot serialize node of type ${node.kind}`); } switch (node.kind) { case ApiItemKind.Class: - return this.encodeClass(model, node as ApiClass); + return this.encodeClass(model, node as ApiClass, version); case ApiItemKind.Function: - return this.encodeFunction(model, node as ApiFunction); + return this.encodeFunction(model, node as ApiFunction, version); case ApiItemKind.Interface: - return this.encodeInterface(model, node as ApiInterface); + return this.encodeInterface(model, node as ApiInterface, version); case ApiItemKind.TypeAlias: - return this.encodeTypeAlias(model, node as ApiTypeAlias); + return this.encodeTypeAlias(model, node as ApiTypeAlias, version); case ApiItemKind.Enum: - return this.encodeEnum(model, node as ApiEnum); + return this.encodeEnum(model, node as ApiEnum, version); case ApiItemKind.Variable: - return this.encodeVariable(model, node as ApiVariable); + return this.encodeVariable(model, node as ApiVariable, version); default: throw new Error(`Unknown API item kind: ${node.kind}`); } } - public static encodeItem(model: ApiModel, item: ApiDeclaredItem): ApiItemJSON { + public static encodeItem(model: ApiModel, item: ApiDeclaredItem, version: string): ApiItemJSON { const path = []; for (const _item of item.getHierarchy()) { switch (_item.kind) { @@ -189,36 +189,41 @@ export class ApiNodeJSONEncoder { return { kind: item.kind, name: resolveName(item), - referenceData: genReference(item), + referenceData: genReference(item, version), excerpt: item.excerpt.text, - excerptTokens: item.excerpt.spannedTokens.map((token) => genToken(model, token)), + excerptTokens: item.excerpt.spannedTokens.map((token) => genToken(model, token, version)), remarks: item.tsdocComment?.remarksBlock - ? (createCommentNode(item.tsdocComment.remarksBlock, model, item.parent) as DocNodeContainerJSON) + ? (createCommentNode(item.tsdocComment.remarksBlock, model, version, item.parent) as DocNodeContainerJSON) : null, summary: item.tsdocComment?.summarySection - ? (createCommentNode(item.tsdocComment.summarySection, model, item.parent) as DocNodeContainerJSON) + ? (createCommentNode(item.tsdocComment.summarySection, model, version, item.parent) as DocNodeContainerJSON) : null, deprecated: item.tsdocComment?.deprecatedBlock - ? (createCommentNode(item.tsdocComment.deprecatedBlock, model, item.parent) as DocNodeContainerJSON) + ? (createCommentNode(item.tsdocComment.deprecatedBlock, model, version, item.parent) as DocNodeContainerJSON) : null, path, containerKey: item.containerKey, - comment: item.tsdocComment ? createCommentNode(item.tsdocComment, model, item.parent) : null, + comment: item.tsdocComment ? createCommentNode(item.tsdocComment, model, version, item.parent) : null, }; } public static encodeParameterList( model: ApiModel, item: ApiParameterListMixin & ApiDeclaredItem, + version: string, ): { parameters: ApiParameterJSON[] } { return { - parameters: item.parameters.map((param) => genParameter(model, param)), + parameters: item.parameters.map((param) => genParameter(model, param, version)), }; } - public static encodeTypeParameterList(model: ApiModel, item: ApiTypeParameterListMixin & ApiDeclaredItem) { + public static encodeTypeParameterList( + model: ApiModel, + item: ApiTypeParameterListMixin & ApiDeclaredItem, + version: string, + ) { return { - typeParameters: item.typeParameters.map((param) => generateTypeParamData(model, param, item.parent)), + typeParameters: item.typeParameters.map((param) => generateTypeParamData(model, param, version, item.parent)), }; } @@ -226,35 +231,40 @@ export class ApiNodeJSONEncoder { model: ApiModel, item: ApiPropertyItem, parent: ApiItemContainerMixin, + version: string, ): ApiPropertyItemJSON { return { - ...this.encodeItem(model, item), - ...this.encodeInheritanceData(item, parent), - propertyTypeTokens: item.propertyTypeExcerpt.spannedTokens.map((token) => genToken(model, token)), + ...this.encodeItem(model, item, version), + ...this.encodeInheritanceData(item, parent, version), + propertyTypeTokens: item.propertyTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)), readonly: item.isReadonly, optional: item.isOptional, }; } - public static encodeInheritanceData(item: ApiDeclaredItem, parent: ApiItemContainerMixin): ApiInheritableJSON { + public static encodeInheritanceData( + item: ApiDeclaredItem, + parent: ApiItemContainerMixin, + version: string, + ): ApiInheritableJSON { return { inheritanceData: item.parent && item.parent.containerKey !== parent.containerKey ? { parentKey: item.parent.containerKey, parentName: item.parent.displayName, - path: generatePath(item.parent.getHierarchy()), + path: generatePath(item.parent.getHierarchy(), version), } : null, }; } - public static encodeFunction(model: ApiModel, item: ApiFunction) { + public static encodeFunction(model: ApiModel, item: ApiFunction, version: string) { return { - ...this.encodeItem(model, item), - ...this.encodeParameterList(model, item), - ...this.encodeTypeParameterList(model, item), - returnTypeTokens: item.returnTypeExcerpt.spannedTokens.map((token) => genToken(model, token)), + ...this.encodeItem(model, item, version), + ...this.encodeParameterList(model, item, version), + ...this.encodeTypeParameterList(model, item, version), + returnTypeTokens: item.returnTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)), overloadIndex: item.overloadIndex, }; } @@ -263,23 +273,29 @@ export class ApiNodeJSONEncoder { model: ApiModel, item: ApiMethodSignature, parent: ApiItemContainerMixin, + version: string, ): ApiMethodSignatureJSON { return { - ...this.encodeFunction(model, item), - ...this.encodeInheritanceData(item, parent), + ...this.encodeFunction(model, item, version), + ...this.encodeInheritanceData(item, parent, version), optional: item.isOptional, }; } - public static encodeMethod(model: ApiModel, item: ApiMethod, parent: ApiItemContainerMixin): ApiMethodJSON { + public static encodeMethod( + model: ApiModel, + item: ApiMethod, + parent: ApiItemContainerMixin, + version: string, + ): ApiMethodJSON { return { - ...this.encodeMethodSignature(model, item, parent), + ...this.encodeMethodSignature(model, item, parent, version), static: item.isStatic, visibility: item.isProtected ? Visibility.Protected : Visibility.Public, }; } - public static encodeClass(model: ApiModel, item: ApiClass): ApiClassJSON { + public static encodeClass(model: ApiModel, item: ApiClass, version: string): ApiClassJSON { const extendsExcerpt = item.extendsType?.excerpt; const methods: ApiMethodJSON[] = []; @@ -290,10 +306,10 @@ export class ApiNodeJSONEncoder { for (const member of item.findMembersWithInheritance().items) { switch (member.kind) { case ApiItemKind.Method: - methods.push(this.encodeMethod(model, member as ApiMethod, item)); + methods.push(this.encodeMethod(model, member as ApiMethod, item, version)); break; case ApiItemKind.Property: - properties.push(this.encodeProperty(model, member as ApiPropertyItem, item)); + properties.push(this.encodeProperty(model, member as ApiPropertyItem, item, version)); break; case ApiItemKind.Constructor: constructor = member as ApiConstructor; @@ -304,48 +320,49 @@ export class ApiNodeJSONEncoder { } return { - ...this.encodeItem(model, item), - ...this.encodeTypeParameterList(model, item), - constructor: constructor ? this.encodeConstructor(model, constructor) : null, - extendsTokens: extendsExcerpt ? extendsExcerpt.spannedTokens.map((token) => genToken(model, token)) : [], + ...this.encodeItem(model, item, version), + ...this.encodeTypeParameterList(model, item, version), + constructor: constructor ? this.encodeConstructor(model, constructor, version) : null, + extendsTokens: extendsExcerpt ? extendsExcerpt.spannedTokens.map((token) => genToken(model, token, version)) : [], implementsTokens: item.implementsTypes.map((excerpt) => - excerpt.excerpt.spannedTokens.map((token) => genToken(model, token)), + excerpt.excerpt.spannedTokens.map((token) => genToken(model, token, version)), ), methods, properties, }; } - public static encodeTypeAlias(model: ApiModel, item: ApiTypeAlias): ApiTypeAliasJSON { + public static encodeTypeAlias(model: ApiModel, item: ApiTypeAlias, version: string): ApiTypeAliasJSON { return { - ...this.encodeItem(model, item), - ...this.encodeTypeParameterList(model, item), - typeTokens: item.typeExcerpt.spannedTokens.map((token) => genToken(model, token)), + ...this.encodeItem(model, item, version), + ...this.encodeTypeParameterList(model, item, version), + typeTokens: item.typeExcerpt.spannedTokens.map((token) => genToken(model, token, version)), }; } - public static encodeEnum(model: ApiModel, item: ApiEnum): ApiEnumJSON { + public static encodeEnum(model: ApiModel, item: ApiEnum, version: string): ApiEnumJSON { return { - ...this.encodeItem(model, item), + ...this.encodeItem(model, item, version), members: item.members.map((member) => ({ name: member.name, - initializerTokens: member.initializerExcerpt?.spannedTokens.map((token) => genToken(model, token)) ?? [], - summary: member.tsdocComment ? nodeContainer(member.tsdocComment.summarySection, model, member) : null, + initializerTokens: + member.initializerExcerpt?.spannedTokens.map((token) => genToken(model, token, version)) ?? [], + summary: member.tsdocComment ? nodeContainer(member.tsdocComment.summarySection, model, version, member) : null, })), }; } - public static encodeInterface(model: ApiModel, item: ApiInterface): ApiInterfaceJSON { + public static encodeInterface(model: ApiModel, item: ApiInterface, version: string): ApiInterfaceJSON { const methods: ApiMethodSignatureJSON[] = []; const properties: ApiPropertyItemJSON[] = []; for (const member of item.findMembersWithInheritance().items) { switch (member.kind) { case ApiItemKind.MethodSignature: - methods.push(this.encodeMethodSignature(model, member as ApiMethodSignature, item)); + methods.push(this.encodeMethodSignature(model, member as ApiMethodSignature, item, version)); break; case ApiItemKind.PropertySignature: - properties.push(this.encodeProperty(model, member as ApiPropertySignature, item)); + properties.push(this.encodeProperty(model, member as ApiPropertySignature, item, version)); break; default: break; @@ -353,28 +370,28 @@ export class ApiNodeJSONEncoder { } return { - ...this.encodeItem(model, item), - ...this.encodeTypeParameterList(model, item), + ...this.encodeItem(model, item, version), + ...this.encodeTypeParameterList(model, item, version), extendsTokens: item.extendsTypes.map((excerpt) => - excerpt.excerpt.spannedTokens.map((token) => genToken(model, token)), + excerpt.excerpt.spannedTokens.map((token) => genToken(model, token, version)), ), methods, properties, }; } - public static encodeVariable(model: ApiModel, item: ApiVariable): ApiVariableJSON { + public static encodeVariable(model: ApiModel, item: ApiVariable, version: string): ApiVariableJSON { return { - ...this.encodeItem(model, item), - typeTokens: item.variableTypeExcerpt.spannedTokens.map((token) => genToken(model, token)), + ...this.encodeItem(model, item, version), + typeTokens: item.variableTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)), readonly: item.isReadonly, }; } - public static encodeConstructor(model: ApiModel, item: ApiConstructor): ApiConstructorJSON { + public static encodeConstructor(model: ApiModel, item: ApiConstructor, version: string): ApiConstructorJSON { return { - ...this.encodeItem(model, item), - ...this.encodeParameterList(model, item), + ...this.encodeItem(model, item, version), + ...this.encodeParameterList(model, item, version), protected: item.isProtected, }; } diff --git a/packages/website/src/DocModel/TypeParameterMixin.ts b/packages/website/src/DocModel/TypeParameterMixin.ts index 74fb155ac..075d642b1 100644 --- a/packages/website/src/DocModel/TypeParameterMixin.ts +++ b/packages/website/src/DocModel/TypeParameterMixin.ts @@ -13,16 +13,19 @@ export interface TypeParameterData { export function generateTypeParamData( model: ApiModel, typeParam: TypeParameter, + version: string, parentItem?: ApiItem, ): TypeParameterData { - const constraintTokens = typeParam.constraintExcerpt.spannedTokens.map((token) => genToken(model, token)); - const defaultTokens = typeParam.defaultTypeExcerpt.spannedTokens.map((token) => genToken(model, token)); + const constraintTokens = typeParam.constraintExcerpt.spannedTokens.map((token) => genToken(model, token, version)); + const defaultTokens = typeParam.defaultTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)); return { name: typeParam.name, constraintTokens, defaultTokens, optional: typeParam.isOptional, - commentBlock: typeParam.tsdocTypeParamBlock ? block(typeParam.tsdocTypeParamBlock, model, parentItem) : null, + commentBlock: typeParam.tsdocTypeParamBlock + ? block(typeParam.tsdocTypeParamBlock, model, version, parentItem) + : null, }; } diff --git a/packages/website/src/DocModel/comment/CommentBlock.ts b/packages/website/src/DocModel/comment/CommentBlock.ts index 8d7f88db3..408d13ae6 100644 --- a/packages/website/src/DocModel/comment/CommentBlock.ts +++ b/packages/website/src/DocModel/comment/CommentBlock.ts @@ -9,10 +9,10 @@ export interface DocBlockJSON extends DocNodeJSON { tag: DocBlockTagJSON; } -export function block(block: DocBlock, model: ApiModel, parentItem?: ApiItem) { +export function block(block: DocBlock, model: ApiModel, version: string, parentItem?: ApiItem) { return { ...node(block), - content: block.content.nodes.map((node) => createCommentNode(node, model, parentItem)), + content: block.content.nodes.map((node) => createCommentNode(node, model, version, parentItem)), tag: blockTag(block.blockTag), }; } diff --git a/packages/website/src/DocModel/comment/CommentNodeContainer.ts b/packages/website/src/DocModel/comment/CommentNodeContainer.ts index bcd513a24..90a4eddbd 100644 --- a/packages/website/src/DocModel/comment/CommentNodeContainer.ts +++ b/packages/website/src/DocModel/comment/CommentNodeContainer.ts @@ -10,10 +10,11 @@ export interface DocNodeContainerJSON extends DocNodeJSON { export function nodeContainer( container: DocNodeContainer, model: ApiModel, + version: string, parentItem?: ApiItem, ): DocNodeContainerJSON { return { ...node(container), - nodes: container.nodes.map((node) => createCommentNode(node, model, parentItem)), + nodes: container.nodes.map((node) => createCommentNode(node, model, version, parentItem)), }; } diff --git a/packages/website/src/DocModel/comment/LinkTagCommentNode.ts b/packages/website/src/DocModel/comment/LinkTagCommentNode.ts index e835f52d2..e5921abce 100644 --- a/packages/website/src/DocModel/comment/LinkTagCommentNode.ts +++ b/packages/website/src/DocModel/comment/LinkTagCommentNode.ts @@ -13,6 +13,7 @@ export function genToken( model: ApiModel, ref: DocDeclarationReference, context: ApiItem | null, + version: string, ): LinkTagCodeLink | null { const item = model.resolveDeclarationReference(ref, context ?? undefined).resolvedApiItem ?? null; @@ -23,7 +24,7 @@ export function genToken( return { name: resolveName(item), kind: item.kind, - path: generatePath(item.getHierarchy()), + path: generatePath(item.getHierarchy(), version), }; } @@ -33,14 +34,19 @@ export interface LinkTagCodeLink { path: string; } -export function linkTagNode(linkNode: DocLinkTag, model: ApiModel, parentItem?: ApiItem): DocLinkTagJSON { +export function linkTagNode( + linkNode: DocLinkTag, + model: ApiModel, + version: string, + parentItem?: ApiItem, +): DocLinkTagJSON { // If we weren't provided a parent object, fallback to the package entrypoint. const packageEntryPoint = linkNode.codeDestination?.importPath ? model.getAssociatedPackage()?.findEntryPointsByPath(linkNode.codeDestination.importPath)[0] : null; const codeDestination = linkNode.codeDestination - ? genToken(model, linkNode.codeDestination, parentItem ?? packageEntryPoint ?? null) + ? genToken(model, linkNode.codeDestination, parentItem ?? packageEntryPoint ?? null, version) : null; const text = linkNode.linkText ?? null; const urlDestination = linkNode.urlDestination ?? null; diff --git a/packages/website/src/DocModel/comment/ParamBlock.ts b/packages/website/src/DocModel/comment/ParamBlock.ts index e54ba8827..a9558a14f 100644 --- a/packages/website/src/DocModel/comment/ParamBlock.ts +++ b/packages/website/src/DocModel/comment/ParamBlock.ts @@ -6,9 +6,14 @@ export interface DocParamBlockJSON extends DocBlockJSON { name: string; } -export function paramBlock(paramBlock: DocParamBlock, model: ApiModel, parentItem?: ApiItem): DocParamBlockJSON { +export function paramBlock( + paramBlock: DocParamBlock, + model: ApiModel, + version: string, + parentItem?: ApiItem, +): DocParamBlockJSON { return { - ...block(paramBlock, model, parentItem), + ...block(paramBlock, model, version, parentItem), name: paramBlock.parameterName, }; } diff --git a/packages/website/src/DocModel/comment/RootComment.ts b/packages/website/src/DocModel/comment/RootComment.ts index 459fc940b..7564e0aa7 100644 --- a/packages/website/src/DocModel/comment/RootComment.ts +++ b/packages/website/src/DocModel/comment/RootComment.ts @@ -11,12 +11,14 @@ export interface DocCommentJSON extends DocNodeJSON { customBlocks: DocBlockJSON[]; } -export function comment(comment: DocComment, model: ApiModel, parentItem?: ApiItem): DocCommentJSON { +export function comment(comment: DocComment, model: ApiModel, version: string, 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)) ?? [], - deprecated: comment.deprecatedBlock?.content.nodes.map((node) => createCommentNode(node, model, parentItem)) ?? [], - customBlocks: comment.customBlocks.map((_block) => block(_block, model, parentItem)), + summary: comment.summarySection.nodes.map((node) => createCommentNode(node, model, version, parentItem)), + remarks: + comment.remarksBlock?.content.nodes.map((node) => createCommentNode(node, model, version, parentItem)) ?? [], + deprecated: + comment.deprecatedBlock?.content.nodes.map((node) => createCommentNode(node, model, version, parentItem)) ?? [], + customBlocks: comment.customBlocks.map((_block) => block(_block, model, version, parentItem)), }; } diff --git a/packages/website/src/DocModel/comment/index.ts b/packages/website/src/DocModel/comment/index.ts index a151f0f7a..8443bd83b 100644 --- a/packages/website/src/DocModel/comment/index.ts +++ b/packages/website/src/DocModel/comment/index.ts @@ -22,25 +22,30 @@ import { paramBlock } from './ParamBlock'; import { plainTextNode } from './PlainTextCommentNode'; import { comment } from './RootComment'; -export function createCommentNode(node: DocNode, model: ApiModel, parentItem?: ApiItem): AnyDocNodeJSON { +export function createCommentNode( + node: DocNode, + model: ApiModel, + version: string, + parentItem?: ApiItem, +): AnyDocNodeJSON { switch (node.kind) { case DocNodeKind.PlainText: return plainTextNode(node as DocPlainText); case DocNodeKind.LinkTag: - return linkTagNode(node as DocLinkTag, model, parentItem); + return linkTagNode(node as DocLinkTag, model, version, parentItem); case DocNodeKind.Paragraph: case DocNodeKind.Section: - return nodeContainer(node as DocParagraph, model, parentItem); + return nodeContainer(node as DocParagraph, model, version, 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); + return block(node as DocBlock, model, version, parentItem); case DocNodeKind.ParamBlock: - return paramBlock(node as DocParamBlock, model, parentItem); + return paramBlock(node as DocParamBlock, model, version, parentItem); case DocNodeKind.Comment: - return comment(node as DocComment, model, parentItem); + return comment(node as DocComment, model, version, parentItem); default: return _node(node); } diff --git a/packages/website/src/pages/docs/[...slug].tsx b/packages/website/src/pages/docs/[...slug].tsx index f997030e2..a6f7383ed 100644 --- a/packages/website/src/pages/docs/[...slug].tsx +++ b/packages/website/src/pages/docs/[...slug].tsx @@ -1,7 +1,7 @@ import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { Box } from '@mantine/core'; -import { ApiFunction } from '@microsoft/api-extractor-model'; +import { ApiFunction, ApiPackage } from '@microsoft/api-extractor-model'; import Head from 'next/head'; import type { GetStaticPaths, GetStaticProps } from 'next/types'; import type { @@ -31,7 +31,8 @@ export const getStaticPaths: GetStaticPaths = async () => { await Promise.all( packages.map(async (packageName) => { try { - let data; + let data: any[] = []; + let versions: string[] = []; if (process.env.NEXT_PUBLIC_LOCAL_DEV) { const res = await readFile( join(__dirname, '..', '..', '..', '..', '..', packageName, 'docs', 'docs.api.json'), @@ -40,9 +41,44 @@ export const getStaticPaths: GetStaticPaths = async () => { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment data = JSON.parse(res); } else { - const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/main.api.json`); + const response = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - data = await res.json(); + versions = await response.json(); + + for (const version of versions) { + const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/${version}.api.json`); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + data = [...data, await res.json()]; + } + } + + if (Array.isArray(data)) { + const models = data.map((d) => createApiModel(d)); + const pkgs = models.map((model) => findPackage(model, packageName)) as ApiPackage[]; + + return [ + { params: { slug: ['packages', packageName, 'main'] } }, + ...versions.map((version) => ({ params: { slug: ['packages', packageName, version] } })), + ...pkgs + .map((pkg, idx) => + getMembers(pkg) + // Filtering out enum `RESTEvents` because of interface with similar name `RestEvents` + // causing next.js export to error + .filter((member) => member.name !== 'RESTEvents') + .map((member) => { + if (member.kind === 'Function' && member.overloadIndex && member.overloadIndex > 1) { + return { + params: { + slug: ['packages', packageName, versions[idx]!, `${member.name}:${member.overloadIndex}`], + }, + }; + } + + return { params: { slug: ['packages', packageName, versions[idx]!, member.name] } }; + }), + ) + .flat(), + ]; } const model = createApiModel(data); @@ -67,7 +103,7 @@ export const getStaticPaths: GetStaticPaths = async () => { }), ]; } catch { - return { params: { slug: ['', '', '', ''] } }; + return { params: { slug: [] } }; } }), ) @@ -102,7 +138,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { const model = createApiModel(data); const pkg = findPackage(model, packageName); - let { containerKey, name } = findMember(model, packageName, memberName) ?? {}; + let { containerKey, name } = findMember(model, packageName, memberName, branchName) ?? {}; if (name && overloadIndex) { containerKey = ApiFunction.getContainerKey(name, parseInt(overloadIndex, 10)); } @@ -111,8 +147,9 @@ export const getStaticProps: GetStaticProps = async ({ params }) => { props: { packageName, data: { - members: pkg ? getMembers(pkg) : [], - member: memberName && containerKey ? findMemberByKey(model, packageName, containerKey) ?? null : null, + members: pkg ? getMembers(pkg, branchName) : [], + member: + memberName && containerKey ? findMemberByKey(model, packageName, containerKey, branchName) ?? null : null, }, }, }; diff --git a/packages/website/src/pages/docs/packages/[package]/index.tsx b/packages/website/src/pages/docs/packages/[package]/index.tsx new file mode 100644 index 000000000..171030cf5 --- /dev/null +++ b/packages/website/src/pages/docs/packages/[package]/index.tsx @@ -0,0 +1,89 @@ +import { Container, UnstyledButton, createStyles, Group, ThemeIcon, Text, Stack, Box } from '@mantine/core'; +import Link from 'next/link'; +import type { GetStaticPaths, GetStaticProps } from 'next/types'; +import { VscArrowRight, VscPackage } from 'react-icons/vsc'; + +interface VersionProps { + packageName: string; + data: { + versions: string[]; + }; +} + +export const getStaticPaths: GetStaticPaths = () => { + const packages = ['builders', 'collection', 'proxy', 'rest', 'voice', 'ws']; + + const versions = packages.map((packageName) => ({ params: { package: packageName } })); + + return { + paths: versions, + fallback: true, + }; +}; + +export const getStaticProps: GetStaticProps = async ({ params }) => { + const packageName = params!.package as string | undefined; + + try { + const res = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName ?? 'builders'}`); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const data: string[] = await res.json(); + + return { + props: { + packageName, + data: { + versions: data, + }, + }, + }; + } catch (error) { + return { + props: { + error: 'FetchError', + }, + }; + } +}; + +const useStyles = createStyles((theme) => ({ + control: { + padding: theme.spacing.xs, + color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black, + + '&:hover': { + backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![6] : theme.colors.gray![0], + color: theme.colorScheme === 'dark' ? theme.white : theme.black, + }, + }, +})); + +export default function VersionsRoute(props: Partial & { error?: string }) { + const { classes } = useStyles(); + + return props.error ? ( + {props.error} + ) : ( + + + {props.data?.versions.map((version) => ( + + + + + + + + + {version} + + + + + + + )) ?? null} + + + ); +} diff --git a/packages/website/src/util/model.server.ts b/packages/website/src/util/model.server.ts index b96b9e7c9..d165979ba 100644 --- a/packages/website/src/util/model.server.ts +++ b/packages/website/src/util/model.server.ts @@ -2,7 +2,7 @@ import type { ApiEntryPoint, ApiModel } from '@microsoft/api-extractor-model'; import { findPackage } from './parse.server'; import { ApiNodeJSONEncoder } from '~/DocModel/ApiNodeJSONEncoder'; -export function findMemberByKey(model: ApiModel, packageName: string, containerKey: string) { +export function findMemberByKey(model: ApiModel, packageName: string, containerKey: string, version: string) { const pkg = findPackage(model, packageName)!; const member = (pkg.members[0] as ApiEntryPoint).tryGetMemberByKey(containerKey); @@ -10,13 +10,14 @@ export function findMemberByKey(model: ApiModel, packageName: string, containerK return undefined; } - return ApiNodeJSONEncoder.encode(model, member); + return ApiNodeJSONEncoder.encode(model, member, version); } export function findMember( model: ApiModel, packageName: string, memberName: string, + version: string, ): ReturnType | undefined { const pkg = findPackage(model, packageName)!; const member = (pkg.members[0] as ApiEntryPoint).findMembersByName(memberName)[0]; @@ -25,5 +26,5 @@ export function findMember( return undefined; } - return ApiNodeJSONEncoder.encode(model, member); + return ApiNodeJSONEncoder.encode(model, member, version); } diff --git a/packages/website/src/util/parse.server.ts b/packages/website/src/util/parse.server.ts index 62cfecb21..99cbf9981 100644 --- a/packages/website/src/util/parse.server.ts +++ b/packages/website/src/util/parse.server.ts @@ -23,7 +23,7 @@ export function findPackage(model: ApiModel, name: string): ApiPackage | undefin | undefined; } -export function generatePath(items: readonly ApiItem[]) { +export function generatePath(items: readonly ApiItem[], version: string) { let path = '/docs/packages'; for (const item of items) { switch (item.kind) { @@ -53,7 +53,7 @@ export function generatePath(items: readonly ApiItem[]) { } } - return path.replace(/@discordjs\/(.*)\/(.*)?/, '$1/main/$2'); + return path.replace(/@discordjs\/(.*)\/(.*)?/, `$1/${version}/$2`); } export function resolveDocComment(item: ApiDocumentedItem) { @@ -164,14 +164,14 @@ function createDapiTypesURL(meaning: Meaning, name: string) { } } -export function genReference(item: ApiItem) { +export function genReference(item: ApiItem, version: string) { return { name: resolveName(item), - path: generatePath(item.getHierarchy()), + path: generatePath(item.getHierarchy(), version), }; } -export function genToken(model: ApiModel, token: ExcerptToken) { +export function genToken(model: ApiModel, token: ExcerptToken, version: string) { if (token.canonicalReference) { // @ts-expect-error token.canonicalReference._navigation = '.'; @@ -197,24 +197,26 @@ export function genToken(model: ApiModel, token: ExcerptToken) { return { kind: token.kind, text: token.text, - path: item ? generatePath(item.getHierarchy()) : null, + path: item ? generatePath(item.getHierarchy(), version) : null, }; } -export function genParameter(model: ApiModel, param: Parameter): ParameterDocumentation { +export function genParameter(model: ApiModel, param: Parameter, version: string): ParameterDocumentation { return { name: param.name, isOptional: param.isOptional, - tokens: param.parameterTypeExcerpt.spannedTokens.map((token) => genToken(model, token)), - paramCommentBlock: param.tsdocParamBlock ? (createCommentNode(param.tsdocParamBlock, model) as DocBlockJSON) : null, + tokens: param.parameterTypeExcerpt.spannedTokens.map((token) => genToken(model, token, version)), + paramCommentBlock: param.tsdocParamBlock + ? (createCommentNode(param.tsdocParamBlock, model, version) as DocBlockJSON) + : null, }; } -export function getMembers(pkg: ApiPackage) { +export function getMembers(pkg: ApiPackage, version = 'main') { return pkg.members[0]!.members.map((member) => ({ name: member.displayName, kind: member.kind as string, - path: generatePath(member.getHierarchy()), + path: generatePath(member.getHierarchy(), version), containerKey: member.containerKey, overloadIndex: member.kind === 'Function' ? (member as ApiFunction).overloadIndex : null, }));