diff --git a/packages/builders/src/components/ActionRow.ts b/packages/builders/src/components/ActionRow.ts index 68edb04aa..5580d3f89 100644 --- a/packages/builders/src/components/ActionRow.ts +++ b/packages/builders/src/components/ActionRow.ts @@ -22,6 +22,8 @@ export type AnyComponentBuilder = MessageActionRowComponentBuilder | ModalAction /** * Represents an action row component + * + * @typeParam T - The types of components this action row holds */ export class ActionRowBuilder extends ComponentBuilder< APIActionRowComponent @@ -56,6 +58,9 @@ export class ActionRowBuilder extends ComponentBu return this; } + /** + * {@inheritDoc JSONEncodable.toJSON} + */ public toJSON(): APIActionRowComponent> { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return { diff --git a/packages/builders/src/components/Component.ts b/packages/builders/src/components/Component.ts index f8780fd46..1775a6d69 100644 --- a/packages/builders/src/components/Component.ts +++ b/packages/builders/src/components/Component.ts @@ -10,6 +10,8 @@ export type AnyAPIActionRowComponent = APIActionRowComponentTypes | APIActionRow /** * Represents a discord component + * + * @typeParam DataType - The type of internal API data that is stored within the component */ export abstract class ComponentBuilder< DataType extends Partial> = APIBaseComponent, @@ -20,6 +22,9 @@ export abstract class ComponentBuilder< */ public readonly data: Partial; + /** + * {@inheritDoc JSONEncodable.toJSON} + */ public abstract toJSON(): AnyAPIActionRowComponent; public constructor(data: Partial) { diff --git a/packages/builders/src/components/button/Button.ts b/packages/builders/src/components/button/Button.ts index 7c92593a7..853667b5e 100644 --- a/packages/builders/src/components/button/Button.ts +++ b/packages/builders/src/components/button/Button.ts @@ -85,6 +85,9 @@ export class ButtonBuilder extends ComponentBuilder { return this; } + /** + * {@inheritDoc JSONEncodable.toJSON} + */ public toJSON(): APIButtonComponent { validateRequiredButtonParameters( this.data.style, diff --git a/packages/builders/src/components/selectMenu/SelectMenu.ts b/packages/builders/src/components/selectMenu/SelectMenu.ts index e0ea41175..2eccd4692 100644 --- a/packages/builders/src/components/selectMenu/SelectMenu.ts +++ b/packages/builders/src/components/selectMenu/SelectMenu.ts @@ -116,6 +116,9 @@ export class SelectMenuBuilder extends ComponentBuilder return this; } + /** + * {@inheritDoc JSONEncodable.toJSON} + */ public toJSON(): APISelectMenuComponent { validateRequiredSelectMenuParameters(this.options, this.data.custom_id); // eslint-disable-next-line @typescript-eslint/consistent-type-assertions diff --git a/packages/builders/src/components/selectMenu/SelectMenuOption.ts b/packages/builders/src/components/selectMenu/SelectMenuOption.ts index 3f9d50203..452b46a83 100644 --- a/packages/builders/src/components/selectMenu/SelectMenuOption.ts +++ b/packages/builders/src/components/selectMenu/SelectMenuOption.ts @@ -1,4 +1,5 @@ import type { APIMessageComponentEmoji, APISelectMenuOption } from 'discord-api-types/v10'; +import type { JSONEncodable } from '../../util/jsonEncodable'; import { defaultValidator, @@ -10,7 +11,7 @@ import { /** * Represents a option within a select menu component */ -export class SelectMenuOptionBuilder { +export class SelectMenuOptionBuilder implements JSONEncodable { public constructor(public data: Partial = {}) {} /** @@ -63,6 +64,9 @@ export class SelectMenuOptionBuilder { return this; } + /** + * {@inheritDoc JSONEncodable.toJSON} + */ public toJSON(): APISelectMenuOption { validateRequiredSelectMenuOptionParameters(this.data.label, this.data.value); // eslint-disable-next-line @typescript-eslint/consistent-type-assertions diff --git a/packages/builders/src/components/textInput/TextInput.ts b/packages/builders/src/components/textInput/TextInput.ts index 9b2934a1f..16ce0ddb3 100644 --- a/packages/builders/src/components/textInput/TextInput.ts +++ b/packages/builders/src/components/textInput/TextInput.ts @@ -10,11 +10,15 @@ import { labelValidator, textInputStyleValidator, } from './Assertions'; +import type { Equatable } from '../../util/equatable'; import { isJSONEncodable, type JSONEncodable } from '../../util/jsonEncodable'; import { customIdValidator } from '../Assertions'; import { ComponentBuilder } from '../Component'; -export class TextInputBuilder extends ComponentBuilder { +export class TextInputBuilder + extends ComponentBuilder + implements Equatable | APITextInputComponent> +{ public constructor(data?: APITextInputComponent & { type?: ComponentType.TextInput }) { super({ type: ComponentType.TextInput, ...data }); } @@ -99,6 +103,9 @@ export class TextInputBuilder extends ComponentBuilder { return this; } + /** + * {@inheritDoc JSONEncodable.toJSON} + */ public toJSON(): APITextInputComponent { validateRequiredParameters(this.data.custom_id, this.data.style, this.data.label); // eslint-disable-next-line @typescript-eslint/consistent-type-assertions @@ -107,6 +114,9 @@ export class TextInputBuilder extends ComponentBuilder { } as APITextInputComponent; } + /** + * {@inheritDoc Equatable.equals} + */ public equals(other: JSONEncodable | APITextInputComponent): boolean { if (isJSONEncodable(other)) { return isEqual(other.toJSON(), this.data); diff --git a/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts b/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts index 13cc21678..39c19575d 100644 --- a/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts +++ b/packages/builders/src/interactions/slashCommands/SlashCommandBuilder.ts @@ -49,7 +49,7 @@ export class SlashCommandBuilder { * Whether the command is enabled by default when the app is added to a guild * * @deprecated This property is deprecated and will be removed in the future. - * You should use `setDefaultMemberPermissions` or `setDMPermission` instead. + * You should use {@link (SlashCommandBuilder:class).setDefaultMemberPermissions} or {@link (SlashCommandBuilder:class).setDMPermission} instead. */ public readonly default_permission: boolean | undefined = undefined; @@ -89,7 +89,7 @@ export class SlashCommandBuilder { * @param value - Whether or not to enable this command by default * * @see https://discord.com/developers/docs/interactions/application-commands#permissions - * @deprecated Use `setDefaultMemberPermissions` or `setDMPermission` instead. + * @deprecated Use {@link (SlashCommandBuilder:class).setDefaultMemberPermissions} or {@link (SlashCommandBuilder:class).setDMPermission} instead. */ public setDefaultPermission(value: boolean) { // Assert the value matches the conditions diff --git a/packages/builders/src/util/equatable.ts b/packages/builders/src/util/equatable.ts index af1c6e665..254853f54 100644 --- a/packages/builders/src/util/equatable.ts +++ b/packages/builders/src/util/equatable.ts @@ -1,3 +1,9 @@ +/** + * Represents a structure that can be checked against another + * given structure for equality + * + * @typeParam T - The type of object to compare the current object to + */ export interface Equatable { /** * Whether or not this is equal to another structure diff --git a/packages/builders/src/util/jsonEncodable.ts b/packages/builders/src/util/jsonEncodable.ts index 8e8273c38..432719be8 100644 --- a/packages/builders/src/util/jsonEncodable.ts +++ b/packages/builders/src/util/jsonEncodable.ts @@ -1,3 +1,8 @@ +/** + * Represents an object capable of representing itself as a JSON object + * + * @typeParam T - The JSON type corresponding to {@link JSONEncodable.toJSON} outputs. + */ export interface JSONEncodable { /** * Transforms this object to its JSON format diff --git a/packages/collection/src/collection.ts b/packages/collection/src/collection.ts index cc082728e..911ab1a6b 100644 --- a/packages/collection/src/collection.ts +++ b/packages/collection/src/collection.ts @@ -28,6 +28,9 @@ export interface Collection extends Map { /** * A Map with additional utility methods. This is used throughout discord.js rather than Arrays for anything that has * an ID, for significantly improved performance and ease-of-use. + * + * @typeParam K - The key type this collection holds + * @typeParam V - The value type this collection holds */ export class Collection extends Map { /** diff --git a/packages/website/src/DocModel/TypeParameterMixin.ts b/packages/website/src/DocModel/TypeParameterMixin.ts index 9fe01c638..adaf0b5d5 100644 --- a/packages/website/src/DocModel/TypeParameterMixin.ts +++ b/packages/website/src/DocModel/TypeParameterMixin.ts @@ -1,6 +1,32 @@ -import type { ApiItem, ApiModel, ApiTypeParameterListMixin } from '@microsoft/api-extractor-model'; +import type { ApiItem, ApiModel, ApiTypeParameterListMixin, TypeParameter } from '@microsoft/api-extractor-model'; import type { DocItemConstructor } from './DocItem'; -import { generateTypeParamData, TypeParameterData } from '~/util/parse.server'; +import { block, DocBlockJSON } from './comment/CommentBlock'; +import { genToken, TokenDocumentation } from '~/util/parse.server'; + +export interface TypeParameterData { + name: string; + constraintTokens: TokenDocumentation[]; + defaultTokens: TokenDocumentation[]; + optional: boolean; + commentBlock: DocBlockJSON | null; +} + +export function generateTypeParamData( + model: ApiModel, + typeParam: TypeParameter, + parentItem?: ApiItem, +): TypeParameterData { + const constraintTokens = typeParam.constraintExcerpt.spannedTokens.map((token) => genToken(model, token)); + const defaultTokens = typeParam.defaultTypeExcerpt.spannedTokens.map((token) => genToken(model, token)); + + return { + name: typeParam.name, + constraintTokens, + defaultTokens, + optional: typeParam.isOptional, + commentBlock: typeParam.tsdocTypeParamBlock ? block(typeParam.tsdocTypeParamBlock, model, parentItem) : null, + }; +} export function TypeParameterMixin(Base: TBase) { return class Mixed extends Base { @@ -10,7 +36,7 @@ export function TypeParameterMixin(Base: TBase public constructor(model: ApiModel, item: ApiItem) { super(model, item); this.typeParameters = (item as ApiTypeParameterListMixin).typeParameters.map((typeParam) => - generateTypeParamData(this.model, typeParam), + generateTypeParamData(this.model, typeParam, item.parent), ); } diff --git a/packages/website/src/DocModel/comment/LinkTagCommentNode.ts b/packages/website/src/DocModel/comment/LinkTagCommentNode.ts index bfcb105ff..e835f52d2 100644 --- a/packages/website/src/DocModel/comment/LinkTagCommentNode.ts +++ b/packages/website/src/DocModel/comment/LinkTagCommentNode.ts @@ -14,11 +14,7 @@ export function genToken( ref: DocDeclarationReference, context: ApiItem | null, ): LinkTagCodeLink | null { - if (!context) { - return null; - } - - const item = model.resolveDeclarationReference(ref, context).resolvedApiItem ?? null; + const item = model.resolveDeclarationReference(ref, context ?? undefined).resolvedApiItem ?? null; if (!item) { return null; @@ -38,8 +34,14 @@ export interface LinkTagCodeLink { } export function linkTagNode(linkNode: DocLinkTag, model: ApiModel, parentItem?: ApiItem): DocLinkTagJSON { - const codeDestination = - linkNode.codeDestination && parentItem ? genToken(model, linkNode.codeDestination, parentItem) : null; + // 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) + : null; const text = linkNode.linkText ?? null; const urlDestination = linkNode.urlDestination ?? null; diff --git a/packages/website/src/components/DocContainer.tsx b/packages/website/src/components/DocContainer.tsx index e20af04ab..335126473 100644 --- a/packages/website/src/components/DocContainer.tsx +++ b/packages/website/src/components/DocContainer.tsx @@ -1,6 +1,6 @@ import { Group, Stack, Title, Text, Box, MediaQuery, Aside, ScrollArea } from '@mantine/core'; import { useMediaQuery } from '@mantine/hooks'; -import type { ReactNode } from 'react'; +import { Fragment, ReactNode } from 'react'; import { VscListSelection, VscSymbolParameter } from 'react-icons/vsc'; import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism'; @@ -11,9 +11,10 @@ import { TypeParamTable } from './TypeParamTable'; import { TSDoc } from './tsdoc/TSDoc'; import type { DocClass } from '~/DocModel/DocClass'; import type { DocItem } from '~/DocModel/DocItem'; +import type { TypeParameterData } from '~/DocModel/TypeParameterMixin'; import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode'; import { generateIcon } from '~/util/icon'; -import type { TokenDocumentation, TypeParameterData } from '~/util/parse.server'; +import type { TokenDocumentation } from '~/util/parse.server'; export interface DocContainerProps { name: string; @@ -84,10 +85,10 @@ export function DocContainer({ {implementsTokens.map((token, idx) => ( - <> + {idx < implementsTokens.length - 1 ? ', ' : ''} - + ))} diff --git a/packages/website/src/components/MethodList.tsx b/packages/website/src/components/MethodList.tsx index bab7d7a63..69204a622 100644 --- a/packages/website/src/components/MethodList.tsx +++ b/packages/website/src/components/MethodList.tsx @@ -1,4 +1,5 @@ import { Divider, Stack } from '@mantine/core'; +import { Fragment } from 'react'; import { MethodItem } from './MethodItem'; import type { DocMethod } from '~/DocModel/DocMethod'; import type { DocMethodSignature } from '~/DocModel/DocMethodSignature'; @@ -11,13 +12,12 @@ export function MethodList({ return ( {data.map((method) => ( - <> - 1 ? `:${method.overloadIndex}` : ''}`} - data={method} - /> + 1 ? `:${method.overloadIndex}` : ''}`} + > + - + ))} ); diff --git a/packages/website/src/components/TypeParamTable.tsx b/packages/website/src/components/TypeParamTable.tsx index 73252738a..497755c6d 100644 --- a/packages/website/src/components/TypeParamTable.tsx +++ b/packages/website/src/components/TypeParamTable.tsx @@ -1,7 +1,8 @@ -import { Box } from '@mantine/core'; +import { Box, ScrollArea } from '@mantine/core'; import { HyperlinkedText } from './HyperlinkedText'; import { Table } from './Table'; -import type { TypeParameterData } from '~/util/parse.server'; +import { TSDoc } from './tsdoc/TSDoc'; +import type { TypeParameterData } from '~/DocModel/TypeParameterMixin'; export function TypeParamTable({ data }: { data: TypeParameterData[] }) { const rows = data.map((typeParam) => ({ @@ -9,22 +10,24 @@ export function TypeParamTable({ data }: { data: TypeParameterData[] }) { Constraints: , Optional: typeParam.optional ? 'Yes' : 'No', Default: , - Description: 'None', + Description: typeParam.commentBlock ? : 'None', })); const rowElements = { - Name: 'font-mono', - Constraints: 'font-mono', - Default: 'font-mono', + Name: 'font-mono whitespace-nowrap', + Constraints: 'font-mono whitespace-pre break-normal', + Default: 'font-mono whitespace-pre break-normal', }; return ( - + +
+ ); } diff --git a/packages/website/src/components/tsdoc/BlockComment.tsx b/packages/website/src/components/tsdoc/BlockComment.tsx index 287329eeb..e8201a33b 100644 --- a/packages/website/src/components/tsdoc/BlockComment.tsx +++ b/packages/website/src/components/tsdoc/BlockComment.tsx @@ -52,6 +52,7 @@ export function BlockComment({ children, tagName, index }: BlockCommentProps): J return {children}; case StandardTags.remarks.tagNameWithUpperCase: return {children}; + case StandardTags.typeParam.tagNameWithUpperCase: case StandardTags.param.tagNameWithUpperCase: return {children}; default: // TODO: Support more blocks in the future. diff --git a/packages/website/src/components/tsdoc/TSDoc.tsx b/packages/website/src/components/tsdoc/TSDoc.tsx index da8a94dd2..c644754c5 100644 --- a/packages/website/src/components/tsdoc/TSDoc.tsx +++ b/packages/website/src/components/tsdoc/TSDoc.tsx @@ -1,7 +1,7 @@ import { Anchor, Box, Code, Text } from '@mantine/core'; import { DocNodeKind, StandardTags } from '@microsoft/tsdoc'; import Link from 'next/link'; -import type { ReactNode } from 'react'; +import { Fragment, 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'; @@ -32,7 +32,7 @@ export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element { ); case DocNodeKind.SoftBreak: - return <>; + return ; case DocNodeKind.LinkTag: { const { codeDestination, urlDestination, text } = node as DocLinkTagJSON; diff --git a/packages/website/src/util/parse.server.ts b/packages/website/src/util/parse.server.ts index 8b39289ba..565284ee0 100644 --- a/packages/website/src/util/parse.server.ts +++ b/packages/website/src/util/parse.server.ts @@ -10,7 +10,6 @@ import { type ApiPropertyItem, type ExcerptToken, type Parameter, - type TypeParameter, ApiFunction, } from '@microsoft/api-extractor-model'; import type { DocNode, DocParagraph, DocPlainText } from '@microsoft/tsdoc'; @@ -25,7 +24,7 @@ export function findPackage(model: ApiModel, name: string): ApiPackage | undefin } export function generatePath(items: readonly ApiItem[]) { - let path = '/docs/main/packages/'; + let path = '/docs/main/packages'; for (const item of items) { switch (item.kind) { case ApiItemKind.Model: @@ -33,17 +32,24 @@ export function generatePath(items: readonly ApiItem[]) { case ApiItemKind.EnumMember: break; case ApiItemKind.Package: - path += `${item.displayName}/`; + path += `/${item.displayName}`; break; case ApiItemKind.Function: // eslint-disable-next-line no-case-declarations const functionItem = item as ApiFunction; - path += `${functionItem.displayName}${ + path += `/${functionItem.displayName}${ functionItem.overloadIndex && functionItem.overloadIndex > 1 ? `:${functionItem.overloadIndex}` : '' - }/`; + }`; + break; + case ApiItemKind.Property: + case ApiItemKind.Method: + case ApiItemKind.MethodSignature: + case ApiItemKind.PropertySignature: + // TODO: Take overloads into account + path += `#${item.displayName}`; break; default: - path += `${item.displayName}/`; + path += `/${item.displayName}`; } } @@ -213,22 +219,3 @@ export function getMembers(pkg: ApiPackage) { overloadIndex: member.kind === 'Function' ? (member as ApiFunction).overloadIndex : null, })); } - -export interface TypeParameterData { - name: string; - constraintTokens: TokenDocumentation[]; - defaultTokens: TokenDocumentation[]; - optional: boolean; -} - -export function generateTypeParamData(model: ApiModel, typeParam: TypeParameter): TypeParameterData { - const constraintTokens = typeParam.constraintExcerpt.spannedTokens.map((token) => genToken(model, token)); - const defaultTokens = typeParam.defaultTypeExcerpt.spannedTokens.map((token) => genToken(model, token)); - - return { - name: typeParam.name, - constraintTokens, - defaultTokens, - optional: typeParam.isOptional, - }; -}