feat(website): Show constructor information (#8540)

This commit is contained in:
Suneet Tipirneni
2022-08-22 03:45:53 -04:00
committed by GitHub
parent dd44e8b6ec
commit e42fd16369
66 changed files with 689 additions and 625 deletions

View File

@@ -1,3 +1,7 @@
{
"extends": "../../.eslintrc.json"
"extends": "../../.eslintrc.json",
"plugins": ["eslint-plugin-tsdoc"],
"rules": {
"tsdoc/syntax": "warn"
}
}

View File

@@ -74,6 +74,7 @@
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.4.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-tsdoc": "^0.2.16",
"prettier": "^2.7.1",
"rollup-plugin-typescript2": "0.32.1",
"typescript": "^4.7.4",

View File

@@ -59,7 +59,7 @@ export class ActionRowBuilder<T extends AnyComponentBuilder> extends ComponentBu
}
/**
* {@inheritDoc JSONEncodable.toJSON}
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIActionRowComponent<ReturnType<T['toJSON']>> {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions

View File

@@ -23,7 +23,11 @@ export abstract class ComponentBuilder<
public readonly data: Partial<DataType>;
/**
* {@inheritDoc JSONEncodable.toJSON}
* Serializes this component to an API-compatible JSON object
*
* @remarks
* This method runs validations on the data before serializing it.
* As such, it may throw an error if the data is invalid.
*/
public abstract toJSON(): AnyAPIActionRowComponent;

View File

@@ -21,6 +21,35 @@ import { ComponentBuilder } from '../Component';
* Represents a button component
*/
export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
/**
* Creates a new button from API data
* @param data - The API data to create this button with
*
* @example
* Creating a button from an API data object
* ```ts
* const button = new ButtonBuilder({
* style: 'primary',
* label: 'Click Me',
* emoji: {
* name: ':smile:',
* id: '12345678901234567890123456789012',
* },
* custom_id: '12345678901234567890123456789012',
* });
* ```
*
* @example
* Creating a button using setters and API data
* ```ts
* const button = new ButtonBuilder({
* style: 'primary',
* label: 'Click Me',
* })
* .setEmoji({ name: ':smile:', id: '12345678901234567890123456789012' })
* .setCustomId('12345678901234567890123456789012');
* ```
*/
public constructor(data?: Partial<APIButtonComponent>) {
super({ type: ComponentType.Button, ...data });
}
@@ -38,6 +67,10 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
/**
* Sets the URL for this button
*
* @remarks
* This method is only available to buttons using the `Link` button style.
* Only three types of URL schemes are currently supported: `https://`, `http://` and `discord://`
*
* @param url - The URL to open when this button is clicked
*/
public setURL(url: string) {
@@ -48,6 +81,9 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
/**
* Sets the custom id for this button
*
* @remarks
* This method is only applicable to buttons that are not using the `Link` button style.
*
* @param customId - The custom id to use for this button
*/
public setCustomId(customId: string) {
@@ -86,7 +122,7 @@ export class ButtonBuilder extends ComponentBuilder<APIButtonComponent> {
}
/**
* {@inheritDoc JSONEncodable.toJSON}
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIButtonComponent {
validateRequiredButtonParameters(

View File

@@ -117,7 +117,7 @@ export class SelectMenuBuilder extends ComponentBuilder<APISelectMenuComponent>
}
/**
* {@inheritDoc JSONEncodable.toJSON}
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APISelectMenuComponent {
validateRequiredSelectMenuParameters(this.options, this.data.custom_id);

View File

@@ -65,7 +65,7 @@ export class SelectMenuOptionBuilder implements JSONEncodable<APISelectMenuOptio
}
/**
* {@inheritDoc JSONEncodable.toJSON}
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APISelectMenuOption {
validateRequiredSelectMenuOptionParameters(this.data.label, this.data.value);

View File

@@ -104,7 +104,7 @@ export class TextInputBuilder
}
/**
* {@inheritDoc JSONEncodable.toJSON}
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APITextInputComponent {
validateRequiredParameters(this.data.custom_id, this.data.style, this.data.label);

View File

@@ -35,7 +35,7 @@ export class ContextMenuCommandBuilder {
* 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 ContextMenuCommandBuilder.setDefaultMemberPermissions} or {@link ContextMenuCommandBuilder.setDMPermission} instead.
*/
public readonly default_permission: boolean | undefined = undefined;
@@ -86,7 +86,7 @@ export class ContextMenuCommandBuilder {
* @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 ContextMenuCommandBuilder.setDefaultMemberPermissions} or {@link ContextMenuCommandBuilder.setDMPermission} instead.
*/
public setDefaultPermission(value: boolean) {
// Assert the value matches the conditions

View File

@@ -70,6 +70,9 @@ export class ModalBuilder implements JSONEncodable<APIModalInteractionResponseCa
return this;
}
/**
* {@inheritDoc ComponentBuilder.toJSON}
*/
public toJSON(): APIModalInteractionResponseCallbackData {
validateRequiredParameters(this.data.custom_id, this.data.title, this.components);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions

View File

@@ -14,6 +14,9 @@ export class SlashCommandIntegerOption
{
public readonly type = ApplicationCommandOptionType.Integer as const;
/**
* {@inheritDoc ApplicationCommandNumericOptionMinMaxValueMixin.setMaxValue}
*/
public setMaxValue(max: number): this {
numberValidator.parse(max);
@@ -22,6 +25,9 @@ export class SlashCommandIntegerOption
return this;
}
/**
* {@inheritDoc ApplicationCommandNumericOptionMinMaxValueMixin.setMinValue}
*/
public setMinValue(min: number): this {
numberValidator.parse(min);

View File

@@ -14,6 +14,9 @@ export class SlashCommandNumberOption
{
public readonly type = ApplicationCommandOptionType.Number as const;
/**
* {@inheritDoc ApplicationCommandNumericOptionMinMaxValueMixin.setMaxValue}
*/
public setMaxValue(max: number): this {
numberValidator.parse(max);
@@ -22,6 +25,9 @@ export class SlashCommandNumberOption
return this;
}
/**
* {@inheritDoc ApplicationCommandNumericOptionMinMaxValueMixin.setMinValue}
*/
public setMinValue(min: number): this {
numberValidator.parse(min);

View File

@@ -76,7 +76,7 @@ export class EmbedBuilder {
* );
* ```
*
* @param fields The fields to add
* @param fields - The fields to add
*/
public addFields(...fields: RestOrArray<APIEmbedField>): this {
fields = normalizeArray(fields);
@@ -120,9 +120,9 @@ export class EmbedBuilder {
* embed.spliceFields(-1, 1);
* ```
*
* @param index The index to start at
* @param deleteCount The number of fields to remove
* @param fields The replacing field objects
* @param index - The index to start at
* @param deleteCount - The number of fields to remove
* @param fields - The replacing field objects
*/
public spliceFields(index: number, deleteCount: number, ...fields: APIEmbedField[]): this {
// Ensure adding these fields won't exceed the 25 field limit
@@ -144,7 +144,7 @@ export class EmbedBuilder {
*
* You can set a maximum of 25 fields.
*
* @param fields The fields to set
* @param fields - The fields to set
*/
public setFields(...fields: RestOrArray<APIEmbedField>) {
this.spliceFields(0, this.data.fields?.length ?? 0, ...normalizeArray(fields));
@@ -154,7 +154,7 @@ export class EmbedBuilder {
/**
* Sets the author of this embed
*
* @param options The options for the author
* @param options - The options for the author
*/
public setAuthor(options: EmbedAuthorOptions | null): this {
@@ -173,7 +173,7 @@ export class EmbedBuilder {
/**
* Sets the color of this embed
*
* @param color The color of the embed
* @param color - The color of the embed
*/
public setColor(color: number | RGBTuple | null): this {
// Data assertions
@@ -191,7 +191,7 @@ export class EmbedBuilder {
/**
* Sets the description of this embed
*
* @param description The description
* @param description - The description
*/
public setDescription(description: string | null): this {
// Data assertions
@@ -204,7 +204,7 @@ export class EmbedBuilder {
/**
* Sets the footer of this embed
*
* @param options The options for the footer
* @param options - The options for the footer
*/
public setFooter(options: EmbedFooterOptions | null): this {
if (options === null) {
@@ -222,7 +222,7 @@ export class EmbedBuilder {
/**
* Sets the image of this embed
*
* @param url The URL of the image
* @param url - The URL of the image
*/
public setImage(url: string | null): this {
// Data assertions
@@ -235,7 +235,7 @@ export class EmbedBuilder {
/**
* Sets the thumbnail of this embed
*
* @param url The URL of the thumbnail
* @param url - The URL of the thumbnail
*/
public setThumbnail(url: string | null): this {
// Data assertions
@@ -248,7 +248,7 @@ export class EmbedBuilder {
/**
* Sets the timestamp of this embed
*
* @param timestamp The timestamp or date
* @param timestamp - The timestamp or date
*/
public setTimestamp(timestamp: number | Date | null = Date.now()): this {
// Data assertions
@@ -261,7 +261,7 @@ export class EmbedBuilder {
/**
* Sets the title of this embed
*
* @param title The title
* @param title - The title
*/
public setTitle(title: string | null): this {
// Data assertions
@@ -274,7 +274,7 @@ export class EmbedBuilder {
/**
* Sets the URL of this embed
*
* @param url The URL
* @param url - The URL
*/
public setURL(url: string | null): this {
// Data assertions

View File

@@ -1,3 +1,7 @@
{
"extends": "../../.eslintrc.json"
"extends": "../../.eslintrc.json",
"plugins": ["eslint-plugin-tsdoc"],
"rules": {
"tsdoc/syntax": "warn"
}
}

View File

@@ -63,6 +63,7 @@
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.4.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-tsdoc": "^0.2.16",
"prettier": "^2.7.1",
"rollup-plugin-typescript2": "0.32.1",
"typescript": "^4.7.4",

View File

@@ -1,3 +1,7 @@
{
"extends": "../../.eslintrc.json"
"extends": "../../.eslintrc.json",
"plugins": ["eslint-plugin-tsdoc"],
"rules": {
"tsdoc/syntax": "warn"
}
}

View File

@@ -1,3 +1,7 @@
{
"extends": "../../.eslintrc.json"
"extends": "../../.eslintrc.json",
"plugins": ["eslint-plugin-tsdoc"],
"rules": {
"tsdoc/syntax": "warn"
}
}

View File

@@ -74,6 +74,7 @@
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.4.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-tsdoc": "^0.2.16",
"prettier": "^2.7.1",
"rollup-plugin-typescript2": "0.32.1",
"typescript": "^4.7.4",

View File

@@ -91,7 +91,7 @@ export interface RESTOptions {
/**
* Extra information to add to the user agent
*
* @defaultValue ``Node.js ${process.version}``
* @defaultValue `Node.js ${process.version}`
*/
userAgentAppendix: string;
/**

View File

@@ -65,7 +65,7 @@ export interface RequestData {
*/
body?: BodyInit | unknown;
/**
* The {@link https://undici.nodejs.org/#/docs/api/Agent Agent} to use for the request.
* The {@link https://undici.nodejs.org/#/docs/api/Agent | Agent} to use for the request.
*/
dispatcher?: Agent;
/**
@@ -174,7 +174,7 @@ export interface RequestManager {
*/
export class RequestManager extends EventEmitter {
/**
* The {@link https://undici.nodejs.org/#/docs/api/Agent Agent} for all requests
* The {@link https://undici.nodejs.org/#/docs/api/Agent | Agent} for all requests
* performed by this manager.
*/
public agent: Dispatcher | null = null;
@@ -342,7 +342,7 @@ export class RequestManager extends EventEmitter {
* @param hash - The hash for the route
* @param majorParameter - The major parameter for this handler
*
* @private
* @internal
*/
private createHandler(hash: string, majorParameter: string) {
// Create the async request queue to handle requests
@@ -487,7 +487,7 @@ export class RequestManager extends EventEmitter {
* @param endpoint - The raw endpoint to generalize
* @param method - The HTTP method this endpoint is called without
*
* @private
* @internal
*/
private static generateRouteData(endpoint: RouteLike, method: RequestMethod): RouteData {
const majorIdMatch = /^\/(?:channels|guilds|webhooks)\/(\d{16,19})/.exec(endpoint);

View File

@@ -3,13 +3,27 @@ import type { RequestOptions } from '../REST';
import type { HandlerRequestData, RouteData } from '../RequestManager';
export interface IHandler {
/**
* Queues a request to be sent
*
* @param routeId - The generalized api route with literal ids for major parameters
* @param url - The url to do the request on
* @param options - All the information needed to make a request
* @param requestData - Extra data from the user's request needed for errors and additional processing
*/
queueRequest: (
routeId: RouteData,
url: string,
options: RequestOptions,
requestData: HandlerRequestData,
) => Promise<Dispatcher.ResponseData>;
/**
* If the bucket is currently inactive (no pending requests)
*/
// eslint-disable-next-line @typescript-eslint/method-signature-style -- This is meant to be a getter returning a bool
get inactive(): boolean;
/**
* The unique id of the handler
*/
readonly id: string;
}

View File

@@ -30,7 +30,7 @@ const enum QueueType {
*/
export class SequentialHandler implements IHandler {
/**
* The unique id of the handler
* {@inheritDoc IHandler.id}
*/
public readonly id: string;
@@ -87,7 +87,7 @@ export class SequentialHandler implements IHandler {
}
/**
* If the bucket is currently inactive (no pending requests)
* {@inheritDoc IHandler.inactive}
*/
public get inactive(): boolean {
return (
@@ -161,12 +161,7 @@ export class SequentialHandler implements IHandler {
}
/**
* Queues a request to be sent
*
* @param routeId - The generalized api route with literal ids for major parameters
* @param url - The url to do the request on
* @param options - All the information needed to make a request
* @param requestData - Extra data from the user's request needed for errors and additional processing
* {@inheritDoc IHandler.queueRequest}
*/
public async queueRequest(
routeId: RouteData,

View File

@@ -1,5 +1,9 @@
{
"extends": "../../.eslintrc.json",
"plugins": ["eslint-plugin-tsdoc"],
"rules": {
"tsdoc/syntax": "warn"
},
"parserOptions": {
"project": "./tsconfig.eslint.json",
"extraFileExtensions": [".mjs"]

View File

@@ -75,6 +75,7 @@
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.4.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-tsdoc": "^0.2.16",
"jest": "^28.1.3",
"jest-websocket-mock": "^2.4.0",
"mock-socket": "^9.1.5",

View File

@@ -165,22 +165,22 @@ export type VoiceConnectionState =
export interface VoiceConnection extends EventEmitter {
/**
* Emitted when there is an error emitted from the voice connection
* @event
* @eventProperty
*/
on(event: 'error', listener: (error: Error) => void): this;
/**
* Emitted debugging information about the voice connection
* @event
* @eventProperty
*/
on(event: 'debug', listener: (message: string) => void): this;
/**
* Emitted when the state of the voice connection changes
* @event
* @eventProperty
*/
on(event: 'stateChange', listener: (oldState: VoiceConnectionState, newState: VoiceConnectionState) => void): this;
/**
* Emitted when the state of the voice connection changes to a specific status
* @event
* @eventProperty
*/
on<T extends VoiceConnectionStatus>(
event: T,

View File

@@ -154,27 +154,27 @@ export type AudioPlayerState =
export interface AudioPlayer extends EventEmitter {
/**
* Emitted when there is an error emitted from the audio resource played by the audio player
* @event
* @eventProperty
*/
on(event: 'error', listener: (error: AudioPlayerError) => void): this;
/**
* Emitted debugging information about the audio player
* @event
* @eventProperty
*/
on(event: 'debug', listener: (message: string) => void): this;
/**
* Emitted when the state of the audio player changes
* @event
* @eventProperty
*/
on(event: 'stateChange', listener: (oldState: AudioPlayerState, newState: AudioPlayerState) => void): this;
/**
* Emitted when the audio player is subscribed to a voice connection
* @event
* @eventProperty
*/
on(event: 'subscribe' | 'unsubscribe', listener: (subscription: PlayerSubscription) => void): this;
/**
* Emitted when the status of state changes to a specific status
* @event
* @eventProperty
*/
on<T extends AudioPlayerStatus>(
event: T,

View File

@@ -7,7 +7,7 @@ import { noop } from '../util/util';
/**
* Options that are set when creating a new audio resource.
*
* @template T - the type for the metadata (if any) of the audio resource
* @typeParam T - the type for the metadata (if any) of the audio resource
*/
export interface CreateAudioResourceOptions<T> {
/**
@@ -38,7 +38,7 @@ export interface CreateAudioResourceOptions<T> {
/**
* Represents an audio resource that can be played by an audio player.
*
* @template T - the type for the metadata (if any) of the audio resource
* @typeParam T - the type for the metadata (if any) of the audio resource
*/
export class AudioResource<T = unknown> {
/**
@@ -204,7 +204,7 @@ export function inferStreamType(stream: Readable): {
* @param input - The resource to play
* @param options - Configurable options for creating the resource
*
* @template T - the type for the metadata (if any) of the audio resource
* @typeParam T - the type for the metadata (if any) of the audio resource
*/
export function createAudioResource<T>(
input: string | Readable,
@@ -228,7 +228,7 @@ export function createAudioResource<T>(
* @param input - The resource to play
* @param options - Configurable options for creating the resource
*
* @template T - the type for the metadata (if any) of the audio resource
* @typeParam T - the type for the metadata (if any) of the audio resource
*/
export function createAudioResource<T extends null | undefined>(
input: string | Readable,
@@ -248,7 +248,7 @@ export function createAudioResource<T extends null | undefined>(
* @param input - The resource to play
* @param options - Configurable options for creating the resource
*
* @template T - the type for the metadata (if any) of the audio resource
* @typeParam T - the type for the metadata (if any) of the audio resource
*/
export function createAudioResource<T>(
input: string | Readable,

View File

@@ -155,7 +155,7 @@ export interface Networking extends EventEmitter {
/**
* Debug event for Networking.
*
* @event
* @eventProperty
*/
on(event: 'debug', listener: (message: string) => void): this;
on(event: 'error', listener: (error: Error) => void): this;

View File

@@ -10,13 +10,13 @@ export interface VoiceWebSocket extends EventEmitter {
/**
* Debug event for VoiceWebSocket.
*
* @event
* @eventProperty
*/
on(event: 'debug', listener: (message: string) => void): this;
/**
* Packet event.
*
* @event
* @eventProperty
*/
on(event: 'packet', listener: (packet: any) => void): this;
}

View File

@@ -4,13 +4,13 @@ import { EventEmitter } from 'node:events';
export interface SpeakingMap extends EventEmitter {
/**
* Emitted when a user starts speaking.
* @event
* @eventProperty
*/
on(event: 'start', listener: (userId: string) => void): this;
/**
* Emitted when a user ends speaking.
* @event
* @eventProperty
*/
on(event: 'end', listener: (userId: string) => void): this;
}

View File

@@ -0,0 +1,381 @@
import {
ApiModel,
ApiDeclaredItem,
ApiPropertyItem,
ApiMethod,
ApiParameterListMixin,
ApiTypeParameterListMixin,
ApiClass,
ApiFunction,
ApiItemKind,
ApiTypeAlias,
ApiEnum,
ApiInterface,
ApiMethodSignature,
ApiPropertySignature,
ApiVariable,
ApiItem,
ApiConstructor,
ApiItemContainerMixin,
} from '@microsoft/api-extractor-model';
import { generateTypeParamData } from './TypeParameterMixin';
import { Visibility } from './Visibility';
import { createCommentNode } from './comment';
import type { DocBlockJSON } from './comment/CommentBlock';
import type { AnyDocNodeJSON } from './comment/CommentNode';
import { DocNodeContainerJSON, nodeContainer } from './comment/CommentNodeContainer';
import {
generatePath,
genParameter,
genReference,
genToken,
resolveName,
TokenDocumentation,
} from '~/util/parse.server';
export interface ReferenceData {
name: string;
path: string;
}
export interface InheritanceData {
parentName: string;
path: string;
parentKey: string;
}
export interface ApiInheritableJSON {
inheritanceData: InheritanceData | null;
}
export interface ApiItemJSON {
kind: string;
name: string;
referenceData: ReferenceData;
excerpt: string;
excerptTokens: TokenDocumentation[];
remarks: DocNodeContainerJSON | null;
summary: DocNodeContainerJSON | null;
deprecated: DocNodeContainerJSON | null;
comment: AnyDocNodeJSON | null;
containerKey: string;
path: string[];
}
export interface ApiPropertyItemJSON extends ApiItemJSON, ApiInheritableJSON {
propertyTypeTokens: TokenDocumentation[];
readonly: boolean;
optional: boolean;
}
export interface ApiTypeParameterListJSON {
typeParameters: ApiTypeParameterJSON[];
}
export interface ApiTypeParameterJSON {
name: string;
constraintTokens: TokenDocumentation[];
defaultTokens: TokenDocumentation[];
optional: boolean;
commentBlock: DocBlockJSON | null;
}
export interface ApiParameterListJSON {
parameters: ApiParameterJSON[];
}
export interface ApiMethodSignatureJSON
extends ApiItemJSON,
ApiTypeParameterListJSON,
ApiParameterListJSON,
ApiInheritableJSON {
returnTypeTokens: TokenDocumentation[];
optional: boolean;
overloadIndex: number;
}
export interface ApiMethodJSON extends ApiMethodSignatureJSON {
static: boolean;
visibility: Visibility;
}
export interface ApiParameterJSON {
name: string;
isOptional: boolean;
tokens: TokenDocumentation[];
paramCommentBlock: DocBlockJSON | null;
}
export interface ApiClassJSON extends ApiItemJSON, ApiTypeParameterListJSON {
constructor: ApiConstructorJSON | null;
properties: ApiPropertyItemJSON[];
methods: ApiMethodJSON[];
extendsTokens: TokenDocumentation[];
implementsTokens: TokenDocumentation[][];
}
export interface ApiTypeAliasJSON extends ApiItemJSON, ApiTypeParameterListJSON {
typeTokens: TokenDocumentation[];
}
export interface EnumMemberData {
name: string;
initializerTokens: TokenDocumentation[];
summary: DocNodeContainerJSON | null;
}
export interface ApiEnumJSON extends ApiItemJSON {
members: EnumMemberData[];
}
export interface ApiInterfaceJSON extends ApiItemJSON, ApiTypeParameterListJSON {
properties: ApiPropertyItemJSON[];
methods: ApiMethodSignatureJSON[];
extendsTokens: TokenDocumentation[][] | null;
}
export interface ApiVariableJSON extends ApiItemJSON {
typeTokens: TokenDocumentation[];
readonly: boolean;
}
export interface ApiFunctionJSON extends ApiItemJSON, ApiTypeParameterListJSON, ApiParameterListJSON {
returnTypeTokens: TokenDocumentation[];
overloadIndex: number;
}
export interface ApiConstructorJSON extends ApiItemJSON, ApiParameterListJSON {
protected: boolean;
}
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class ApiNodeJSONEncoder {
public static encode(model: ApiModel, node: ApiItem) {
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);
case ApiItemKind.Function:
return this.encodeFunction(model, node as ApiFunction);
case ApiItemKind.Interface:
return this.encodeInterface(model, node as ApiInterface);
case ApiItemKind.TypeAlias:
return this.encodeTypeAlias(model, node as ApiTypeAlias);
case ApiItemKind.Enum:
return this.encodeEnum(model, node as ApiEnum);
case ApiItemKind.Variable:
return this.encodeVariable(model, node as ApiVariable);
default:
throw new Error(`Unknown API item kind: ${node.kind}`);
}
}
public static encodeItem(model: ApiModel, item: ApiDeclaredItem): ApiItemJSON {
const path = [];
for (const _item of item.getHierarchy()) {
switch (_item.kind) {
case 'None':
case 'EntryPoint':
case 'Model':
break;
default:
path.push(resolveName(_item));
}
}
return {
kind: item.kind,
name: resolveName(item),
referenceData: genReference(item),
excerpt: item.excerpt.text,
excerptTokens: item.excerpt.spannedTokens.map((token) => genToken(model, token)),
remarks: item.tsdocComment?.remarksBlock
? (createCommentNode(item.tsdocComment.remarksBlock, model, item.parent) as DocNodeContainerJSON)
: null,
summary: item.tsdocComment?.summarySection
? (createCommentNode(item.tsdocComment.summarySection, model, item.parent) as DocNodeContainerJSON)
: null,
deprecated: item.tsdocComment?.deprecatedBlock
? (createCommentNode(item.tsdocComment.deprecatedBlock, model, item.parent) as DocNodeContainerJSON)
: null,
path,
containerKey: item.containerKey,
comment: item.tsdocComment ? createCommentNode(item.tsdocComment, model, item.parent) : null,
};
}
public static encodeParameterList(
model: ApiModel,
item: ApiParameterListMixin & ApiDeclaredItem,
): { parameters: ApiParameterJSON[] } {
return {
parameters: item.parameters.map((param) => genParameter(model, param)),
};
}
public static encodeTypeParameterList(model: ApiModel, item: ApiTypeParameterListMixin & ApiDeclaredItem) {
return {
typeParameters: item.typeParameters.map((param) => generateTypeParamData(model, param, item.parent)),
};
}
public static encodeProperty(
model: ApiModel,
item: ApiPropertyItem,
parent: ApiItemContainerMixin,
): ApiPropertyItemJSON {
return {
...this.encodeItem(model, item),
...this.encodeInheritanceData(item, parent),
propertyTypeTokens: item.propertyTypeExcerpt.spannedTokens.map((token) => genToken(model, token)),
readonly: item.isReadonly,
optional: item.isOptional,
};
}
public static encodeInheritanceData(item: ApiDeclaredItem, parent: ApiItemContainerMixin): ApiInheritableJSON {
return {
inheritanceData:
item.parent && item.parent.containerKey !== parent.containerKey
? {
parentKey: item.parent.containerKey,
parentName: item.parent.displayName,
path: generatePath(item.parent.getHierarchy()),
}
: null,
};
}
public static encodeFunction(model: ApiModel, item: ApiFunction) {
return {
...this.encodeItem(model, item),
...this.encodeParameterList(model, item),
...this.encodeTypeParameterList(model, item),
returnTypeTokens: item.returnTypeExcerpt.spannedTokens.map((token) => genToken(model, token)),
overloadIndex: item.overloadIndex,
};
}
public static encodeMethodSignature(
model: ApiModel,
item: ApiMethodSignature,
parent: ApiItemContainerMixin,
): ApiMethodSignatureJSON {
return {
...this.encodeFunction(model, item),
...this.encodeInheritanceData(item, parent),
optional: item.isOptional,
};
}
public static encodeMethod(model: ApiModel, item: ApiMethod, parent: ApiItemContainerMixin): ApiMethodJSON {
return {
...this.encodeMethodSignature(model, item, parent),
static: item.isStatic,
visibility: item.isProtected ? Visibility.Protected : Visibility.Public,
};
}
public static encodeClass(model: ApiModel, item: ApiClass): ApiClassJSON {
const extendsExcerpt = item.extendsType?.excerpt;
const methods: ApiMethodJSON[] = [];
const properties: ApiPropertyItemJSON[] = [];
let constructor: ApiConstructor | undefined;
for (const member of item.findMembersWithInheritance().items) {
switch (member.kind) {
case ApiItemKind.Method:
methods.push(this.encodeMethod(model, member as ApiMethod, item));
break;
case ApiItemKind.Property:
properties.push(this.encodeProperty(model, member as ApiPropertyItem, item));
break;
case ApiItemKind.Constructor:
constructor = member as ApiConstructor;
break;
default:
break;
}
}
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)) : [],
implementsTokens: item.implementsTypes.map((excerpt) =>
excerpt.excerpt.spannedTokens.map((token) => genToken(model, token)),
),
methods,
properties,
};
}
public static encodeTypeAlias(model: ApiModel, item: ApiTypeAlias): ApiTypeAliasJSON {
return {
...this.encodeItem(model, item),
...this.encodeTypeParameterList(model, item),
typeTokens: item.typeExcerpt.spannedTokens.map((token) => genToken(model, token)),
};
}
public static encodeEnum(model: ApiModel, item: ApiEnum): ApiEnumJSON {
return {
...this.encodeItem(model, item),
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,
})),
};
}
public static encodeInterface(model: ApiModel, item: ApiInterface): 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));
break;
case ApiItemKind.PropertySignature:
properties.push(this.encodeProperty(model, member as ApiPropertySignature, item));
break;
default:
break;
}
}
return {
...this.encodeItem(model, item),
...this.encodeTypeParameterList(model, item),
extendsTokens: item.extendsTypes.map((excerpt) =>
excerpt.excerpt.spannedTokens.map((token) => genToken(model, token)),
),
methods,
properties,
};
}
public static encodeVariable(model: ApiModel, item: ApiVariable): ApiVariableJSON {
return {
...this.encodeItem(model, item),
typeTokens: item.variableTypeExcerpt.spannedTokens.map((token) => genToken(model, token)),
readonly: item.isReadonly,
};
}
public static encodeConstructor(model: ApiModel, item: ApiConstructor): ApiConstructorJSON {
return {
...this.encodeItem(model, item),
...this.encodeParameterList(model, item),
protected: item.isProtected,
};
}
}

View File

@@ -1,70 +0,0 @@
import {
type ApiClass,
type ApiModel,
ApiItemKind,
type ApiMethod,
type ApiPropertyItem,
} from '@microsoft/api-extractor-model';
import { DocItem } from './DocItem';
import { DocMethod } from './DocMethod';
import { DocProperty } from './DocProperty';
import { TypeParameterMixin } from './TypeParameterMixin';
import { type TokenDocumentation, genToken } from '~/util/parse.server';
export class DocClass extends TypeParameterMixin(DocItem<ApiClass>) {
public readonly extendsTokens: TokenDocumentation[] | null;
public readonly implementsTokens: TokenDocumentation[][];
public readonly methods: DocMethod[] = [];
public readonly properties: DocProperty[] = [];
public constructor(model: ApiModel, item: ApiClass) {
super(model, item);
const extendsExcerpt = item.extendsType?.excerpt;
this.extendsTokens = extendsExcerpt
? extendsExcerpt.spannedTokens.map((token) => genToken(this.model, token))
: null;
this.implementsTokens = item.implementsTypes.map((excerpt) =>
excerpt.excerpt.spannedTokens.map((token) => genToken(this.model, token)),
);
for (const member of item.findMembersWithInheritance().items) {
switch (member.kind) {
case ApiItemKind.Method: {
const method = member as ApiMethod;
if (method.parent?.containerKey !== this.containerKey) {
this.methods.push(new DocMethod(this.model, method, true));
break;
}
this.methods.push(new DocMethod(this.model, method));
break;
}
case ApiItemKind.Property: {
const property = member as ApiPropertyItem;
if (property.parent?.containerKey !== this.containerKey) {
this.properties.push(new DocProperty(this.model, property, true));
break;
}
this.properties.push(new DocProperty(this.model, property));
break;
}
default:
break;
}
}
}
public override toJSON() {
return {
...super.toJSON(),
extendsTokens: this.extendsTokens,
implementsTokens: this.implementsTokens,
methods: this.methods.map((method) => method.toJSON()),
properties: this.properties.map((prop) => prop.toJSON()),
};
}
}

View File

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

View File

@@ -1,26 +0,0 @@
import type { ApiFunction, ApiModel, ApiParameterListMixin } from '@microsoft/api-extractor-model';
import { DocItem } from './DocItem';
import { TypeParameterMixin } from './TypeParameterMixin';
import { type TokenDocumentation, genToken, genParameter, ParameterDocumentation } from '~/util/parse.server';
export class DocFunction extends TypeParameterMixin(DocItem<ApiFunction>) {
public readonly returnTypeTokens: TokenDocumentation[];
public readonly overloadIndex: number;
public readonly parameters: ParameterDocumentation[];
public constructor(model: ApiModel, item: ApiFunction) {
super(model, item);
this.returnTypeTokens = item.returnTypeExcerpt.spannedTokens.map((token) => genToken(this.model, token));
this.overloadIndex = item.overloadIndex;
this.parameters = (item as ApiParameterListMixin).parameters.map((param) => genParameter(this.model, param));
}
public override toJSON() {
return {
...super.toJSON(),
parameters: this.parameters,
returnTypeTokens: this.returnTypeTokens,
overloadIndex: this.overloadIndex,
};
}
}

View File

@@ -1,63 +0,0 @@
import { DocItem } from './DocItem';
import { DocMethodSignature } from './DocMethodSignature';
import { DocProperty } from './DocProperty';
import { TypeParameterMixin } from './TypeParameterMixin';
import {
ApiInterface,
ApiItemKind,
ApiMethodSignature,
ApiModel,
ApiPropertySignature,
} from '~/util/api-extractor.server';
import { type TokenDocumentation, genToken } from '~/util/parse.server';
export class DocInterface extends TypeParameterMixin(DocItem<ApiInterface>) {
public readonly extendsTokens: TokenDocumentation[][] | null;
public readonly methods: DocMethodSignature[] = [];
public readonly properties: DocProperty[] = [];
public constructor(model: ApiModel, item: ApiInterface) {
super(model, item);
this.extendsTokens = item.extendsTypes.map((excerpt) =>
excerpt.excerpt.spannedTokens.map((token) => genToken(this.model, token)),
);
for (const member of item.findMembersWithInheritance().items) {
switch (member.kind) {
case ApiItemKind.MethodSignature: {
const method = member as ApiMethodSignature;
if (method.parent?.containerKey !== this.containerKey) {
this.methods.push(new DocMethodSignature(this.model, method, true));
break;
}
this.methods.push(new DocMethodSignature(this.model, method));
break;
}
case ApiItemKind.PropertySignature: {
const property = member as ApiPropertySignature;
if (property.parent?.containerKey !== this.containerKey) {
this.properties.push(new DocProperty(this.model, property, true));
break;
}
this.properties.push(new DocProperty(this.model, property));
break;
}
default:
break;
}
}
}
public override toJSON() {
return {
...super.toJSON(),
extendsTokens: this.extendsTokens,
methods: this.methods.map((method) => method.toJSON()),
properties: this.properties.map((prop) => prop.toJSON()),
};
}
}

View File

@@ -1,76 +0,0 @@
import type { ApiModel, ApiDeclaredItem } from '@microsoft/api-extractor-model';
import { createCommentNode } from './comment';
import type { AnyDocNodeJSON } from './comment/CommentNode';
import type { DocNodeContainerJSON } from './comment/CommentNodeContainer';
import type { ReferenceData } from '~/util/model.server';
import { resolveName, genReference, TokenDocumentation, genToken } from '~/util/parse.server';
export type DocItemConstructor<T = DocItem> = new (...args: any[]) => T;
export class DocItem<T extends ApiDeclaredItem = ApiDeclaredItem> {
public readonly item: T;
public readonly name: string;
public readonly referenceData: ReferenceData;
public readonly model: ApiModel;
public readonly excerpt: string;
public readonly excerptTokens: TokenDocumentation[] = [];
public readonly kind: string;
public readonly remarks: DocNodeContainerJSON | null;
public readonly summary: DocNodeContainerJSON | null;
public readonly deprecated: DocNodeContainerJSON | null;
public readonly containerKey: string;
public readonly comment: AnyDocNodeJSON | null;
public constructor(model: ApiModel, item: T) {
this.item = item;
this.kind = item.kind;
this.model = model;
this.name = resolveName(item);
this.referenceData = genReference(item);
this.excerpt = item.excerpt.text;
this.excerptTokens = item.excerpt.spannedTokens.map((token) => genToken(model, token));
this.remarks = item.tsdocComment?.remarksBlock
? (createCommentNode(item.tsdocComment.remarksBlock, model, item.parent) as DocNodeContainerJSON)
: null;
this.summary = item.tsdocComment?.summarySection
? (createCommentNode(item.tsdocComment.summarySection, model, item.parent) as DocNodeContainerJSON)
: null;
this.deprecated = item.tsdocComment?.deprecatedBlock
? (createCommentNode(item.tsdocComment.deprecatedBlock, model, item.parent) as DocNodeContainerJSON)
: null;
this.containerKey = item.containerKey;
this.comment = item.tsdocComment ? createCommentNode(item.tsdocComment, model, item.parent) : null;
}
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() {
return {
name: this.name,
referenceData: this.referenceData,
summary: this.summary,
excerpt: this.excerpt,
excerptTokens: this.excerptTokens,
kind: this.kind,
remarks: this.remarks,
deprecated: this.deprecated,
path: this.path,
containerKey: this.containerKey,
comment: this.comment,
};
}
}

View File

@@ -1,40 +0,0 @@
import type { ApiMethod, ApiModel } from '@microsoft/api-extractor-model';
import { DocFunction } from './DocFunction';
import { Visibility } from './Visibility';
import { generatePath } from '~/util/parse.server';
export interface InheritanceData {
parentName: string;
path: string;
}
export class DocMethod extends DocFunction {
public readonly static: boolean;
public readonly optional: boolean;
public readonly visibility: Visibility;
public readonly inheritanceData: InheritanceData | null;
public constructor(model: ApiModel, item: ApiMethod, inherited = false) {
super(model, item);
this.static = item.isStatic;
this.optional = item.isOptional;
this.visibility = item.isProtected ? Visibility.Protected : Visibility.Public;
this.inheritanceData =
inherited && item.parent
? {
parentName: item.parent.displayName,
path: generatePath(item.parent.getHierarchy()),
}
: null;
}
public override toJSON() {
return {
...super.toJSON(),
static: this.static,
optional: this.optional,
visibility: this.visibility,
inheritanceData: this.inheritanceData,
};
}
}

View File

@@ -1,29 +0,0 @@
import type { ApiMethodSignature, ApiModel } from '@microsoft/api-extractor-model';
import { DocFunction } from './DocFunction';
import type { InheritanceData } from './DocMethod';
import { generatePath } from '~/util/parse.server';
export class DocMethodSignature extends DocFunction {
public readonly optional: boolean;
public readonly inheritanceData: InheritanceData | null;
public constructor(model: ApiModel, item: ApiMethodSignature, inherited = false) {
super(model, item);
this.optional = item.isOptional;
this.inheritanceData =
inherited && item.parent
? {
parentName: item.parent.displayName,
path: generatePath(item.parent.getHierarchy()),
}
: null;
}
public override toJSON() {
return {
...super.toJSON(),
optional: this.optional,
inheritanceData: this.inheritanceData,
};
}
}

View File

@@ -1,35 +0,0 @@
import type { ApiPropertyItem, ApiModel, ApiPropertySignature } from '@microsoft/api-extractor-model';
import { DocItem } from './DocItem';
import type { InheritanceData } from './DocMethod';
import { type TokenDocumentation, genToken, generatePath } from '~/util/parse.server';
export class DocProperty extends DocItem<ApiPropertyItem> {
public readonly propertyTypeTokens: TokenDocumentation[];
public readonly readonly: boolean;
public readonly optional: boolean;
public readonly inheritanceData: InheritanceData | null;
public constructor(model: ApiModel, item: ApiPropertyItem | ApiPropertySignature, inherited = false) {
super(model, item);
this.propertyTypeTokens = item.propertyTypeExcerpt.spannedTokens.map((token) => genToken(this.model, token));
this.readonly = item.isReadonly;
this.optional = item.isOptional;
this.inheritanceData =
inherited && item.parent
? {
parentName: item.parent.displayName,
path: generatePath(item.parent.getHierarchy()),
}
: null;
}
public override toJSON() {
return {
...super.toJSON(),
propertyTypeTokens: this.propertyTypeTokens,
readonly: this.readonly,
optional: this.optional,
inheritanceData: this.inheritanceData,
};
}
}

View File

@@ -1,20 +0,0 @@
import type { ApiModel, ApiTypeAlias } from '@microsoft/api-extractor-model';
import { DocItem } from './DocItem';
import { TypeParameterMixin } from './TypeParameterMixin';
import { type TokenDocumentation, genToken } from '~/util/parse.server';
export class DocTypeAlias extends TypeParameterMixin(DocItem<ApiTypeAlias>) {
public readonly typeTokens: TokenDocumentation[];
public constructor(model: ApiModel, item: ApiTypeAlias) {
super(model, item);
this.typeTokens = item.typeExcerpt.spannedTokens.map((token) => genToken(model, token));
}
public override toJSON() {
return {
...super.toJSON(),
typeTokens: this.typeTokens,
};
}
}

View File

@@ -1,22 +0,0 @@
import type { ApiModel, ApiVariable } from '@microsoft/api-extractor-model';
import { DocItem } from './DocItem';
import { genToken, TokenDocumentation } from '~/util/parse.server';
export class DocVariable extends DocItem<ApiVariable> {
public readonly typeTokens: TokenDocumentation[] = [];
public readonly readonly: boolean;
public constructor(model: ApiModel, item: ApiVariable) {
super(model, item);
this.typeTokens = item.variableTypeExcerpt.spannedTokens.map((token) => genToken(model, token));
this.readonly = item.isReadonly;
}
public override toJSON() {
return {
...super.toJSON(),
typeTokens: this.typeTokens,
readonly: this.readonly,
};
}
}

View File

@@ -1,5 +1,4 @@
import type { ApiItem, ApiModel, ApiTypeParameterListMixin, TypeParameter } from '@microsoft/api-extractor-model';
import type { DocItemConstructor } from './DocItem';
import type { ApiItem, ApiModel, TypeParameter } from '@microsoft/api-extractor-model';
import { block, DocBlockJSON } from './comment/CommentBlock';
import { genToken, TokenDocumentation } from '~/util/parse.server';
@@ -27,24 +26,3 @@ export function generateTypeParamData(
commentBlock: typeParam.tsdocTypeParamBlock ? block(typeParam.tsdocTypeParamBlock, model, parentItem) : null,
};
}
export function TypeParameterMixin<TBase extends DocItemConstructor>(Base: TBase) {
return class Mixed extends Base {
public readonly typeParameters: TypeParameterData[] = [];
public constructor(...args: any[]);
public constructor(model: ApiModel, item: ApiItem) {
super(model, item);
this.typeParameters = (item as ApiTypeParameterListMixin).typeParameters.map((typeParam) =>
generateTypeParamData(this.model, typeParam, item.parent),
);
}
public override toJSON() {
return {
...super.toJSON(),
typeParameterData: this.typeParameters,
};
}
};
}

View File

@@ -3,8 +3,7 @@ import type { ReactNode } from 'react';
import { HyperlinkedText } from './HyperlinkedText';
import { InheritanceText } from './InheritanceText';
import { TSDoc } from './tsdoc/TSDoc';
import type { DocItem } from '~/DocModel/DocItem';
import type { InheritanceData } from '~/DocModel/DocMethod';
import type { ApiItemJSON, InheritanceData } from '~/DocModel/ApiNodeJSONEncoder';
import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode';
import type { TokenDocumentation } from '~/util/parse.server';
@@ -30,7 +29,7 @@ export function CodeListing({
typeTokens: TokenDocumentation[];
readonly?: boolean;
optional?: boolean;
summary?: ReturnType<DocItem['toJSON']>['summary'];
summary?: ApiItemJSON['summary'];
comment?: AnyDocNodeJSON | null;
children?: ReactNode;
deprecation?: AnyDocNodeJSON | null;

View File

@@ -9,8 +9,7 @@ import { Section } from './Section';
import { TableOfContentsItems } from './TableOfContentsItems';
import { TypeParamTable } from './TypeParamTable';
import { TSDoc } from './tsdoc/TSDoc';
import type { DocClass } from '~/DocModel/DocClass';
import type { DocItem } from '~/DocModel/DocItem';
import type { ApiClassJSON, ApiItemJSON } from '~/DocModel/ApiNodeJSONEncoder';
import type { TypeParameterData } from '~/DocModel/TypeParameterMixin';
import type { AnyDocNodeJSON } from '~/DocModel/comment/CommentNode';
import { generateIcon } from '~/util/icon';
@@ -20,13 +19,13 @@ export interface DocContainerProps {
name: string;
kind: string;
excerpt: string;
summary?: ReturnType<DocItem['toJSON']>['summary'];
summary?: ApiItemJSON['summary'];
children?: ReactNode;
extendsTokens?: TokenDocumentation[] | null;
implementsTokens?: TokenDocumentation[][];
typeParams?: TypeParameterData[];
comment?: AnyDocNodeJSON | null;
methods?: ReturnType<DocClass['toJSON']>['methods'] | null;
methods?: ApiClassJSON['methods'] | null;
}
export function DocContainer({

View File

@@ -1,6 +1,6 @@
import { Anchor, Text } from '@mantine/core';
import Link from 'next/link';
import type { InheritanceData } from '~/DocModel/DocMethod';
import type { InheritanceData } from '~/DocModel/ApiNodeJSONEncoder';
export function InheritanceText({ data }: { data: InheritanceData }) {
return (

View File

@@ -3,11 +3,10 @@ import { HyperlinkedText } from './HyperlinkedText';
import { InheritanceText } from './InheritanceText';
import { ParameterTable } from './ParameterTable';
import { TSDoc } from './tsdoc/TSDoc';
import type { DocMethod } from '~/DocModel/DocMethod';
import type { DocMethodSignature } from '~/DocModel/DocMethodSignature';
import type { ApiMethodJSON, ApiMethodSignatureJSON } from '~/DocModel/ApiNodeJSONEncoder';
import { Visibility } from '~/DocModel/Visibility';
type MethodResolvable = ReturnType<DocMethod['toJSON']> | ReturnType<DocMethodSignature['toJSON']>;
type MethodResolvable = ApiMethodJSON | ApiMethodSignatureJSON;
function getShorthandName(data: MethodResolvable) {
return `${data.name}${data.optional ? '?' : ''}(${data.parameters.reduce((prev, cur, index) => {
@@ -20,7 +19,8 @@ function getShorthandName(data: MethodResolvable) {
}
export function MethodItem({ data }: { data: MethodResolvable }) {
const method = data as ReturnType<DocMethod['toJSON']>;
const method = data as ApiMethodJSON;
return (
<Stack
id={`${data.name}${data.overloadIndex && data.overloadIndex > 1 ? `:${data.overloadIndex}` : ''}`}

View File

@@ -1,14 +1,9 @@
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';
import type { ApiMethodJSON, ApiMethodSignatureJSON } from '~/DocModel/ApiNodeJSONEncoder';
export function MethodList({
data,
}: {
data: (ReturnType<DocMethod['toJSON']> | ReturnType<DocMethodSignature['toJSON']>)[];
}) {
export function MethodList({ data }: { data: (ApiMethodJSON | ApiMethodSignatureJSON)[] }) {
return (
<Stack>
{data.map((method) => (

View File

@@ -1,8 +1,8 @@
import { Stack } from '@mantine/core';
import { CodeListing } from './CodeListing';
import type { DocProperty } from '~/DocModel/DocProperty';
import type { ApiPropertyItemJSON } from '~/DocModel/ApiNodeJSONEncoder';
export function PropertyList({ data }: { data: ReturnType<DocProperty['toJSON']>[] }) {
export function PropertyList({ data }: { data: ApiPropertyItemJSON[] }) {
return (
<Stack>
{data.map((prop) => (

View File

@@ -1,18 +1,16 @@
import { Stack, Group, Badge, Title } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import { VscSymbolConstant, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc';
import { MethodList } from './MethodList';
import { ParameterTable } from './ParameterTable';
import { PropertyList } from './PropertyList';
import { Section } from './Section';
import type { DocClass } from '~/DocModel/DocClass';
import type { DocInterface } from '~/DocModel/DocInterface';
import { TSDoc } from './tsdoc/TSDoc';
import type { ApiClassJSON, ApiConstructorJSON, ApiInterfaceJSON } from '~/DocModel/ApiNodeJSONEncoder';
import type { ParameterDocumentation } from '~/util/parse.server';
export function PropertiesSection({
data,
}: {
data: ReturnType<DocClass['toJSON']>['properties'] | ReturnType<DocInterface['toJSON']>['properties'];
}) {
export function PropertiesSection({ data }: { data: ApiClassJSON['properties'] | ApiInterfaceJSON['properties'] }) {
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
return data.length ? (
@@ -22,11 +20,7 @@ export function PropertiesSection({
) : null;
}
export function MethodsSection({
data,
}: {
data: ReturnType<DocClass['toJSON']>['methods'] | ReturnType<DocInterface['toJSON']>['methods'];
}) {
export function MethodsSection({ data }: { data: ApiClassJSON['methods'] | ApiInterfaceJSON['methods'] }) {
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
return data.length ? (
@@ -45,3 +39,48 @@ export function ParametersSection({ data }: { data: ParameterDocumentation[] })
</Section>
) : null;
}
export function ConstructorSection({ data }: { data: ApiConstructorJSON }) {
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
function getShorthandName(): string {
return `constructor(${data.parameters.reduce((prev, cur, index) => {
if (index === 0) {
return `${prev}${cur.isOptional ? `[${cur.name}]` : cur.name}`;
}
return `${prev}, ${cur.isOptional ? `[${cur.name}]` : cur.name}`;
}, '')})`;
}
return data.parameters.length ? (
<Section title="Constructor" icon={<VscSymbolMethod />} padded dense={matches}>
<Stack id={`${data.name}`} className="scroll-mt-30" spacing="xs">
<Group>
<Stack>
<Group>
{data.deprecated ? (
<Badge variant="filled" color="red">
Deprecated
</Badge>
) : null}
{data.protected ? <Badge variant="filled">Protected</Badge> : null}
<Title sx={{ wordBreak: 'break-all' }} order={4} className="font-mono">
{getShorthandName()}
</Title>
</Group>
</Stack>
</Group>
<Group sx={{ display: data.summary || data.parameters.length ? 'block' : 'none' }} mb="lg">
<Stack>
{data.deprecated ? <TSDoc node={data.deprecated} /> : null}
{data.summary ? <TSDoc node={data.summary} /> : null}
{data.remarks ? <TSDoc node={data.remarks} /> : null}
{data.comment ? <TSDoc node={data.comment} /> : null}
{data.parameters.length ? <ParameterTable data={data.parameters} /> : null}
</Stack>
</Group>
</Stack>
</Section>
) : null;
}

View File

@@ -25,7 +25,7 @@ import { type PropsWithChildren, useState } from 'react';
import { VscChevronDown, VscPackage } from 'react-icons/vsc';
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
import { SidebarItems } from './SidebarItems';
import type { DocItem } from '~/DocModel/DocItem';
import type { ApiItemJSON } from '~/DocModel/ApiNodeJSONEncoder';
import type { findMember } from '~/util/model.server';
import type { getMembers } from '~/util/parse.server';
@@ -36,7 +36,7 @@ export interface SidebarLayoutProps {
member: ReturnType<typeof findMember>;
};
selectedMember?: ReturnType<DocItem['toJSON']> | undefined;
selectedMember?: ApiItemJSON | undefined;
}
export type Members = SidebarLayoutProps['data']['members'];

View File

@@ -1,7 +1,6 @@
import { createStyles, Group, Text, Box } from '@mantine/core';
import { VscListSelection } from 'react-icons/vsc';
import type { DocClass } from '~/DocModel/DocClass';
import type { DocInterface } from '~/DocModel/DocInterface';
import type { ApiClassJSON, ApiInterfaceJSON } from '~/DocModel/ApiNodeJSONEncoder';
const useStyles = createStyles((theme) => ({
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@@ -25,11 +24,7 @@ const useStyles = createStyles((theme) => ({
},
}));
export function TableOfContentsItems({
members,
}: {
members: ReturnType<DocClass['toJSON']>['methods'] | ReturnType<DocInterface['toJSON']>['methods'];
}) {
export function TableOfContentsItems({ members }: { members: ApiClassJSON['methods'] | ApiInterfaceJSON['methods'] }) {
const { classes } = useStyles();
const items = members.map((member) => {

View File

@@ -1,20 +1,21 @@
import { DocContainer } from '../DocContainer';
import { MethodsSection, PropertiesSection } from '../Sections';
import type { DocClass } from '~/DocModel/DocClass';
import { ConstructorSection, MethodsSection, PropertiesSection } from '../Sections';
import type { ApiClassJSON } from '~/DocModel/ApiNodeJSONEncoder';
export function Class({ data }: { data: ReturnType<DocClass['toJSON']> }) {
export function Class({ data }: { data: ApiClassJSON }) {
return (
<DocContainer
name={data.name}
kind={data.kind}
excerpt={data.excerpt}
summary={data.summary}
typeParams={data.typeParameterData}
typeParams={data.typeParameters}
extendsTokens={data.extendsTokens}
implementsTokens={data.implementsTokens}
comment={data.comment}
methods={data.methods}
>
{data.constructor ? <ConstructorSection data={data.constructor} /> : null}
<PropertiesSection data={data.properties} />
<MethodsSection data={data.methods} />
</DocContainer>

View File

@@ -4,9 +4,9 @@ import { VscSymbolEnumMember } from 'react-icons/vsc';
import { CodeListing, CodeListingSeparatorType } from '../CodeListing';
import { DocContainer } from '../DocContainer';
import { Section } from '../Section';
import type { DocEnum } from '~/DocModel/DocEnum';
import type { ApiEnumJSON } from '~/DocModel/ApiNodeJSONEncoder';
export function Enum({ data }: { data: ReturnType<DocEnum['toJSON']> }) {
export function Enum({ data }: { data: ApiEnumJSON }) {
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
return (

View File

@@ -1,15 +1,15 @@
import { DocContainer } from '../DocContainer';
import { ParametersSection } from '../Sections';
import type { DocFunction } from '~/DocModel/DocFunction';
import type { ApiFunctionJSON } from '~/DocModel/ApiNodeJSONEncoder';
export function Function({ data }: { data: ReturnType<DocFunction['toJSON']> }) {
export function Function({ data }: { data: ApiFunctionJSON }) {
return (
<DocContainer
name={`${data.name}${data.overloadIndex && data.overloadIndex > 1 ? ` (${data.overloadIndex})` : ''}`}
kind={data.kind}
excerpt={data.excerpt}
summary={data.summary}
typeParams={data.typeParameterData}
typeParams={data.typeParameters}
>
<ParametersSection data={data.parameters} />
</DocContainer>

View File

@@ -1,15 +1,15 @@
import { DocContainer } from '../DocContainer';
import { MethodsSection, PropertiesSection } from '../Sections';
import type { DocInterface } from '~/DocModel/DocInterface';
import type { ApiInterfaceJSON } from '~/DocModel/ApiNodeJSONEncoder';
export function Interface({ data }: { data: ReturnType<DocInterface['toJSON']> }) {
export function Interface({ data }: { data: ApiInterfaceJSON }) {
return (
<DocContainer
name={data.name}
kind={data.kind}
excerpt={data.excerpt}
summary={data.summary}
typeParams={data.typeParameterData}
typeParams={data.typeParameters}
>
<PropertiesSection data={data.properties} />
<MethodsSection data={data.methods} />

View File

@@ -1,14 +1,14 @@
import { DocContainer } from '../DocContainer';
import type { DocTypeAlias } from '~/DocModel/DocTypeAlias';
import type { ApiTypeAliasJSON } from '~/DocModel/ApiNodeJSONEncoder';
export function TypeAlias({ data }: { data: ReturnType<DocTypeAlias['toJSON']> }) {
export function TypeAlias({ data }: { data: ApiTypeAliasJSON }) {
return (
<DocContainer
name={data.name}
kind={data.kind}
excerpt={data.excerpt}
summary={data.summary}
typeParams={data.typeParameterData}
typeParams={data.typeParameters}
/>
);
}

View File

@@ -1,6 +1,6 @@
import { DocContainer } from '../DocContainer';
import type { DocVariable } from '~/DocModel/DocVariable';
import type { ApiVariableJSON } from '~/DocModel/ApiNodeJSONEncoder';
export function Variable({ data }: { data: ReturnType<DocVariable['toJSON']> }) {
export function Variable({ data }: { data: ApiVariableJSON }) {
return <DocContainer name={data.name} kind={data.kind} excerpt={data.excerpt} summary={data.summary} />;
}

View File

@@ -1,14 +1,12 @@
import { createContext } from 'react';
import type { DocItem } from '~/DocModel/DocItem';
import type { ApiItemJSON } from '~/DocModel/ApiNodeJSONEncoder';
export type DocItemJSON = ReturnType<DocItem['toJSON']>;
export const MemberContext = createContext<DocItemJSON | undefined>(undefined);
export const MemberContext = createContext<ApiItemJSON | undefined>(undefined);
export const MemberProvider = ({
member,
children,
}: {
member: DocItemJSON | undefined;
member: ApiItemJSON | undefined;
children: React.ReactNode;
}) => <MemberContext.Provider value={member}>{children}</MemberContext.Provider>;

View File

@@ -4,12 +4,14 @@ import { join } from 'node:path';
import { Box } from '@mantine/core';
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';
import type { DocFunction } from '~/DocModel/DocFunction';
import type { DocInterface } from '~/DocModel/DocInterface';
import type { DocTypeAlias } from '~/DocModel/DocTypeAlias';
import type { DocVariable } from '~/DocModel/DocVariable';
import type {
ApiClassJSON,
ApiEnumJSON,
ApiFunctionJSON,
ApiInterfaceJSON,
ApiTypeAliasJSON,
ApiVariableJSON,
} from '~/DocModel/ApiNodeJSONEncoder';
import { SidebarLayout, type SidebarLayoutProps } from '~/components/SidebarLayout';
import { Class } from '~/components/model/Class';
import { Enum } from '~/components/model/Enum';
@@ -110,8 +112,7 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
packageName,
data: {
members: pkg ? getMembers(pkg) : [],
member:
memberName && containerKey ? findMemberByKey(model, packageName, containerKey)?.toJSON() ?? null : null,
member: memberName && containerKey ? findMemberByKey(model, packageName, containerKey) ?? null : null,
},
},
};
@@ -128,17 +129,17 @@ const member = (props: any) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
switch (props.kind) {
case 'Class':
return <Class data={props as ReturnType<DocClass['toJSON']>} />;
return <Class data={props as ApiClassJSON} />;
case 'Function':
return <Function data={props as ReturnType<DocFunction['toJSON']>} />;
return <Function data={props as ApiFunctionJSON} />;
case 'Interface':
return <Interface data={props as ReturnType<DocInterface['toJSON']>} />;
return <Interface data={props as ApiInterfaceJSON} />;
case 'TypeAlias':
return <TypeAlias data={props as ReturnType<DocTypeAlias['toJSON']>} />;
return <TypeAlias data={props as ApiTypeAliasJSON} />;
case 'Variable':
return <Variable data={props as ReturnType<DocVariable['toJSON']>} />;
return <Variable data={props as ApiVariableJSON} />;
case 'Enum':
return <Enum data={props as ReturnType<DocEnum['toJSON']>} />;
return <Enum data={props as ApiEnumJSON} />;
default:
return <Box>Cannot render that item type</Box>;
}

View File

@@ -1,52 +1,6 @@
import {
ApiClass,
ApiDeclaredItem,
ApiEntryPoint,
ApiEnum,
ApiFunction,
ApiInterface,
ApiItem,
ApiItemKind,
ApiModel,
ApiTypeAlias,
ApiVariable,
} from '@microsoft/api-extractor-model';
import type { ApiEntryPoint, ApiModel } from '@microsoft/api-extractor-model';
import { findPackage } from './parse.server';
import { DocClass } from '../DocModel/DocClass';
import { DocEnum } from '../DocModel/DocEnum';
import { DocFunction } from '../DocModel/DocFunction';
import { DocInterface } from '../DocModel/DocInterface';
import { DocItem } from '../DocModel/DocItem';
import { DocTypeAlias } from '../DocModel/DocTypeAlias';
import { DocVariable } from '../DocModel/DocVariable';
export interface ReferenceData {
name: string;
path: string;
}
function createDocItem(model: ApiModel, member: ApiItem) {
if (!(member instanceof ApiDeclaredItem)) {
return undefined;
}
switch (member.kind) {
case ApiItemKind.Class:
return new DocClass(model, member as ApiClass);
case ApiItemKind.Function:
return new DocFunction(model, member as ApiFunction);
case ApiItemKind.Interface:
return new DocInterface(model, member as ApiInterface);
case ApiItemKind.TypeAlias:
return new DocTypeAlias(model, member as ApiTypeAlias);
case ApiItemKind.Variable:
return new DocVariable(model, member as ApiVariable);
case ApiItemKind.Enum:
return new DocEnum(model, member as ApiEnum);
default:
return new DocItem(model, member);
}
}
import { ApiNodeJSONEncoder } from '~/DocModel/ApiNodeJSONEncoder';
export function findMemberByKey(model: ApiModel, packageName: string, containerKey: string) {
const pkg = findPackage(model, packageName)!;
@@ -56,10 +10,14 @@ export function findMemberByKey(model: ApiModel, packageName: string, containerK
return undefined;
}
return createDocItem(model, member);
return ApiNodeJSONEncoder.encode(model, member);
}
export function findMember(model: ApiModel, packageName: string, memberName: string): DocItem | undefined {
export function findMember(
model: ApiModel,
packageName: string,
memberName: string,
): ReturnType<typeof ApiNodeJSONEncoder['encode']> | undefined {
const pkg = findPackage(model, packageName)!;
const member = (pkg.members[0] as ApiEntryPoint).findMembersByName(memberName)[0];
@@ -67,5 +25,5 @@ export function findMember(model: ApiModel, packageName: string, memberName: str
return undefined;
}
return createDocItem(model, member);
return ApiNodeJSONEncoder.encode(model, member);
}

View File

@@ -1,3 +1,7 @@
{
"extends": "../../.eslintrc.json"
"extends": "../../.eslintrc.json",
"plugins": ["eslint-plugin-tsdoc"],
"rules": {
"tsdoc/syntax": "warn"
}
}

View File

@@ -74,6 +74,7 @@
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-typescript": "^3.4.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-tsdoc": "^0.2.16",
"mock-socket": "^9.1.5",
"prettier": "^2.7.1",
"rollup-plugin-typescript2": "0.32.1",

View File

@@ -21,6 +21,9 @@ export class SimpleShardingStrategy implements IShardingStrategy {
this.throttler = new IdentifyThrottler(manager);
}
/**
* {@inheritDoc IShardingStrategy.spawn}
*/
public async spawn(shardIds: number[]) {
const strategyOptions = await managerToFetchingStrategyOptions(this.manager);
for (const shardId of shardIds) {
@@ -34,6 +37,9 @@ export class SimpleShardingStrategy implements IShardingStrategy {
}
}
/**
* {@inheritDoc IShardingStrategy.connect}
*/
public async connect() {
const promises = [];
@@ -45,6 +51,9 @@ export class SimpleShardingStrategy implements IShardingStrategy {
await Promise.all(promises);
}
/**
* {@inheritDoc IShardingStrategy.destroy}
*/
public async destroy(options?: Omit<WebSocketShardDestroyOptions, 'recover'>) {
const promises = [];
@@ -56,6 +65,9 @@ export class SimpleShardingStrategy implements IShardingStrategy {
this.shards.clear();
}
/**
* {@inheritDoc IShardingStrategy.send}
*/
public send(shardId: number, payload: GatewaySendPayload) {
const shard = this.shards.get(shardId);
if (!shard) throw new Error(`Shard ${shardId} not found`);

View File

@@ -73,6 +73,9 @@ export class WorkerShardingStrategy implements IShardingStrategy {
this.options = options;
}
/**
* {@inheritDoc IShardingStrategy.spawn}
*/
public async spawn(shardIds: number[]) {
const shardsPerWorker = this.options.shardsPerWorker === 'all' ? shardIds.length : this.options.shardsPerWorker;
const strategyOptions = await managerToFetchingStrategyOptions(this.manager);
@@ -106,6 +109,9 @@ export class WorkerShardingStrategy implements IShardingStrategy {
}
}
/**
* {@inheritDoc IShardingStrategy.connect}
*/
public async connect() {
const promises = [];
@@ -125,6 +131,9 @@ export class WorkerShardingStrategy implements IShardingStrategy {
await Promise.all(promises);
}
/**
* {@inheritDoc IShardingStrategy.destroy}
*/
public async destroy(options: Omit<WebSocketShardDestroyOptions, 'recover'> = {}) {
const promises = [];
@@ -147,6 +156,9 @@ export class WorkerShardingStrategy implements IShardingStrategy {
await Promise.all(promises);
}
/**
* {@inheritDoc IShardingStrategy.send}
*/
public send(shardId: number, data: GatewaySendPayload) {
const worker = this.#workerByShardId.get(shardId);
if (!worker) {

View File

@@ -203,7 +203,7 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
/**
* Fetches the gateway information from Discord - or returns it from cache if available
* @param force Whether to ignore the cache and force a fresh fetch
* @param force - Whether to ignore the cache and force a fresh fetch
*/
public async fetchGatewayInformation(force = false) {
if (this.gatewayInformation) {
@@ -222,7 +222,7 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> {
/**
* Updates your total shard count on-the-fly, spawning shards as needed
* @param shardCount The new shard count to use
* @param shardCount - The new shard count to use
*/
public async updateShardCount(shardCount: number | null) {
await this.strategy.destroy({ reason: 'User is adjusting their shards' });

View File

@@ -1745,6 +1745,7 @@ __metadata:
eslint-config-prettier: ^8.5.0
eslint-import-resolver-typescript: ^3.4.1
eslint-plugin-import: ^2.26.0
eslint-plugin-tsdoc: ^0.2.16
fast-deep-equal: ^3.1.3
prettier: ^2.7.1
rollup-plugin-typescript2: 0.32.1
@@ -1773,6 +1774,7 @@ __metadata:
eslint-config-prettier: ^8.5.0
eslint-import-resolver-typescript: ^3.4.1
eslint-plugin-import: ^2.26.0
eslint-plugin-tsdoc: ^0.2.16
prettier: ^2.7.1
rollup-plugin-typescript2: 0.32.1
typescript: ^4.7.4
@@ -1900,6 +1902,7 @@ __metadata:
eslint-config-prettier: ^8.5.0
eslint-import-resolver-typescript: ^3.4.1
eslint-plugin-import: ^2.26.0
eslint-plugin-tsdoc: ^0.2.16
file-type: ^17.1.6
prettier: ^2.7.1
rollup-plugin-typescript2: 0.32.1
@@ -1956,6 +1959,7 @@ __metadata:
eslint-config-prettier: ^8.5.0
eslint-import-resolver-typescript: ^3.4.1
eslint-plugin-import: ^2.26.0
eslint-plugin-tsdoc: ^0.2.16
jest: ^28.1.3
jest-websocket-mock: ^2.4.0
mock-socket: ^9.1.5
@@ -2043,6 +2047,7 @@ __metadata:
eslint-config-prettier: ^8.5.0
eslint-import-resolver-typescript: ^3.4.1
eslint-plugin-import: ^2.26.0
eslint-plugin-tsdoc: ^0.2.16
mock-socket: ^9.1.5
prettier: ^2.7.1
rollup-plugin-typescript2: 0.32.1
@@ -7925,6 +7930,16 @@ __metadata:
languageName: node
linkType: hard
"eslint-plugin-tsdoc@npm:^0.2.16":
version: 0.2.16
resolution: "eslint-plugin-tsdoc@npm:0.2.16"
dependencies:
"@microsoft/tsdoc": 0.14.1
"@microsoft/tsdoc-config": 0.16.1
checksum: 37ca88b060b90223aa938656d267eead4291d5859e790f95eb8271eb8f315c16010e500fac4ef535710350e36d7394cecb7e61fbb3635568066008e3425dcac7
languageName: node
linkType: hard
"eslint-rule-docs@npm:^1.1.5":
version: 1.1.235
resolution: "eslint-rule-docs@npm:1.1.235"