mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-17 12:03:31 +01:00
docs: allow @mixes TSDoc tag for documenting mixins (#10545)
This commit is contained in:
@@ -2,7 +2,15 @@
|
|||||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||||
// See LICENSE in the project root for license information.
|
// See LICENSE in the project root for license information.
|
||||||
|
|
||||||
import { TSDocConfiguration } from '@microsoft/tsdoc';
|
import {
|
||||||
|
type DocNode,
|
||||||
|
type DocPlainText,
|
||||||
|
DocDeclarationReference,
|
||||||
|
DocNodeKind,
|
||||||
|
TSDocConfiguration,
|
||||||
|
DocMemberReference,
|
||||||
|
DocMemberIdentifier,
|
||||||
|
} from '@microsoft/tsdoc';
|
||||||
import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||||
import { InternalError } from '@rushstack/node-core-library';
|
import { InternalError } from '@rushstack/node-core-library';
|
||||||
import type { IExcerptToken, IExcerptTokenRange } from '../index.js';
|
import type { IExcerptToken, IExcerptTokenRange } from '../index.js';
|
||||||
@@ -42,6 +50,11 @@ export interface IApiItemContainerJson extends IApiItemJson {
|
|||||||
preserveMemberOrder?: boolean;
|
preserveMemberOrder?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Mixin {
|
||||||
|
declarationReference: DocDeclarationReference;
|
||||||
|
typeParameters: IExcerptTokenRange[];
|
||||||
|
}
|
||||||
|
|
||||||
interface ExcerptTokenRangeInDeclaredItem {
|
interface ExcerptTokenRangeInDeclaredItem {
|
||||||
item: ApiDeclaredItem;
|
item: ApiDeclaredItem;
|
||||||
range: IExcerptTokenRange;
|
range: IExcerptTokenRange;
|
||||||
@@ -393,14 +406,52 @@ export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const findPlainTextNode = (node: DocNode): DocPlainText | undefined => {
|
||||||
|
switch (node.kind) {
|
||||||
|
case DocNodeKind.PlainText:
|
||||||
|
return node as DocPlainText;
|
||||||
|
default:
|
||||||
|
for (const child of node.getChildNodes()) {
|
||||||
|
const result = findPlainTextNode(child);
|
||||||
|
if (result) return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
// Interfaces can extend multiple interfaces, so iterate through all of them.
|
// Interfaces can extend multiple interfaces, so iterate through all of them.
|
||||||
|
// Also Classes can have multiple mixins
|
||||||
const extendedItems: IMappedTypeParameters[] = [];
|
const extendedItems: IMappedTypeParameters[] = [];
|
||||||
let extendsTypes: readonly HeritageType[] | undefined;
|
let extendsTypes: readonly (HeritageType | Mixin)[] | undefined;
|
||||||
|
|
||||||
switch (next.item.kind) {
|
switch (next.item.kind) {
|
||||||
case ApiItemKind.Class: {
|
case ApiItemKind.Class: {
|
||||||
const apiClass: ApiClass = next.item as ApiClass;
|
const apiClass: ApiClass = next.item as ApiClass;
|
||||||
extendsTypes = apiClass.extendsType ? [apiClass.extendsType] : [];
|
const configuration = apiClass.tsdocComment?.configuration ?? new TSDocConfiguration();
|
||||||
|
const mixins =
|
||||||
|
apiClass.tsdocComment?.customBlocks
|
||||||
|
.filter(
|
||||||
|
(block) => block.blockTag.tagName === '@mixes', // &&
|
||||||
|
// block.getChildNodes().some((node) => node.kind === DocNodeKind.PlainText),
|
||||||
|
)
|
||||||
|
.map(findPlainTextNode)
|
||||||
|
.filter((block) => block !== undefined)
|
||||||
|
.map((block) => ({
|
||||||
|
declarationReference: new DocDeclarationReference({
|
||||||
|
configuration,
|
||||||
|
memberReferences: block.text.split('.').map(
|
||||||
|
(part, index) =>
|
||||||
|
new DocMemberReference({
|
||||||
|
configuration,
|
||||||
|
hasDot: index > 0,
|
||||||
|
memberIdentifier: new DocMemberIdentifier({ configuration, identifier: part }),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
typeParameters: [] as IExcerptTokenRange[],
|
||||||
|
})) ?? [];
|
||||||
|
extendsTypes = apiClass.extendsType ? [apiClass.extendsType, ...mixins] : [...mixins];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,30 +476,38 @@ export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const extendsType of extendsTypes) {
|
for (const extendsType of extendsTypes) {
|
||||||
// We want to find the reference token associated with the actual inherited declaration.
|
let canonicalReference: DeclarationReference | DocDeclarationReference;
|
||||||
// In every case we support, this is the first reference token. For example:
|
if ('excerpt' in extendsType) {
|
||||||
//
|
// We want to find the reference token associated with the actual inherited declaration.
|
||||||
// ```
|
// In every case we support, this is the first reference token. For example:
|
||||||
// export class A extends B {}
|
//
|
||||||
// ^
|
// ```
|
||||||
// export class A extends B<C> {}
|
// export class A extends B {}
|
||||||
// ^
|
// ^
|
||||||
// export class A extends B.C {}
|
// export class A extends B<C> {}
|
||||||
// ^^^
|
// ^
|
||||||
// ```
|
// export class A extends B.C {}
|
||||||
const firstReferenceToken: ExcerptToken | undefined = extendsType.excerpt.spannedTokens.find(
|
// ^^^
|
||||||
(token: ExcerptToken) => {
|
// ```
|
||||||
return token.kind === ExcerptTokenKind.Reference && token.canonicalReference;
|
const firstReferenceToken: ExcerptToken | undefined = extendsType.excerpt.spannedTokens.find(
|
||||||
},
|
(token: ExcerptToken) => {
|
||||||
);
|
return token.kind === ExcerptTokenKind.Reference && token.canonicalReference;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (!firstReferenceToken) {
|
if (!firstReferenceToken) {
|
||||||
messages.push({
|
messages.push({
|
||||||
messageId: FindApiItemsMessageId.ExtendsClauseMissingReference,
|
messageId: FindApiItemsMessageId.ExtendsClauseMissingReference,
|
||||||
text: `Unable to analyze extends clause ${extendsType.excerpt.text} of API item ${next.item.displayName} because no canonical reference was found`,
|
text: `Unable to analyze extends clause ${extendsType.excerpt.text} of API item ${next.item.displayName} because no canonical reference was found`,
|
||||||
});
|
});
|
||||||
maybeIncompleteResult = true;
|
maybeIncompleteResult = true;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
canonicalReference = firstReferenceToken.canonicalReference!;
|
||||||
|
} else {
|
||||||
|
// extendsType is a Mixin
|
||||||
|
canonicalReference = extendsType.declarationReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiModel: ApiModel | undefined = this.getAssociatedModel();
|
const apiModel: ApiModel | undefined = this.getAssociatedModel();
|
||||||
@@ -461,10 +520,9 @@ export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canonicalReference: DeclarationReference = firstReferenceToken.canonicalReference!;
|
|
||||||
const apiItemResult: IResolveDeclarationReferenceResult = apiModel.resolveDeclarationReference(
|
const apiItemResult: IResolveDeclarationReferenceResult = apiModel.resolveDeclarationReference(
|
||||||
canonicalReference,
|
canonicalReference,
|
||||||
undefined,
|
this,
|
||||||
);
|
);
|
||||||
|
|
||||||
const apiItem: ApiItem | undefined = apiItemResult.resolvedApiItem;
|
const apiItem: ApiItem | undefined = apiItemResult.resolvedApiItem;
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
|
|
||||||
const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration();
|
const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration();
|
||||||
|
|
||||||
if (versionToDeserialize >= ApiJsonSchemaVersion.V_1004 && 'tsdocConfiguration' in jsonObject) {
|
if (versionToDeserialize >= ApiJsonSchemaVersion.V_1004 && 'tsdocConfig' in jsonObject.metadata) {
|
||||||
const tsdocConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromObject(jsonObject.metadata.tsdocConfig);
|
const tsdocConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromObject(jsonObject.metadata.tsdocConfig);
|
||||||
if (tsdocConfigFile.hasErrors) {
|
if (tsdocConfigFile.hasErrors) {
|
||||||
throw new Error(`Error loading ${apiJsonFilename}:\n` + tsdocConfigFile.getErrorSummary());
|
throw new Error(`Error loading ${apiJsonFilename}:\n` + tsdocConfigFile.getErrorSummary());
|
||||||
@@ -420,6 +420,8 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
for (const key of Object.keys(item)) {
|
for (const key of Object.keys(item)) {
|
||||||
if (key === 'dependencies') {
|
if (key === 'dependencies') {
|
||||||
result[MinifyJSONMapping.dependencies] = item.dependencies;
|
result[MinifyJSONMapping.dependencies] = item.dependencies;
|
||||||
|
} else if (key === 'tsdocConfig') {
|
||||||
|
result[MinifyJSONMapping.tsdocConfig] = item.tsdocConfig;
|
||||||
} else
|
} else
|
||||||
result[MinifyJSONMapping[key as keyof typeof MinifyJSONMapping]] =
|
result[MinifyJSONMapping[key as keyof typeof MinifyJSONMapping]] =
|
||||||
typeof item[key] === 'object' ? mapper(item[key]) : item[key];
|
typeof item[key] === 'object' ? mapper(item[key]) : item[key];
|
||||||
@@ -440,6 +442,8 @@ export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumented
|
|||||||
for (const key of Object.keys(item)) {
|
for (const key of Object.keys(item)) {
|
||||||
if (key === MinifyJSONMapping.dependencies) {
|
if (key === MinifyJSONMapping.dependencies) {
|
||||||
result.dependencies = item[MinifyJSONMapping.dependencies];
|
result.dependencies = item[MinifyJSONMapping.dependencies];
|
||||||
|
} else if (key === MinifyJSONMapping.tsdocConfig) {
|
||||||
|
result.tsdocConfig = item[MinifyJSONMapping.tsdocConfig];
|
||||||
} else
|
} else
|
||||||
result[
|
result[
|
||||||
Object.keys(MinifyJSONMapping).find(
|
Object.keys(MinifyJSONMapping).find(
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
{
|
{
|
||||||
"tagName": "@preapproved",
|
"tagName": "@preapproved",
|
||||||
"syntaxKind": "modifier"
|
"syntaxKind": "modifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tagName": "@mixes",
|
||||||
|
"syntaxKind": "block"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ import { SharedChatInputCommandSubcommands } from './mixins/SharedSubcommands.js
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A builder that creates API-compatible JSON data for chat input commands.
|
* A builder that creates API-compatible JSON data for chat input commands.
|
||||||
|
*
|
||||||
|
* @mixes CommandBuilder<RESTPostAPIChatInputApplicationCommandsJSONBody>
|
||||||
|
* @mixes SharedChatInputCommandOptions
|
||||||
|
* @mixes SharedNameAndDescription
|
||||||
|
* @mixes SharedChatInputCommandSubcommands
|
||||||
*/
|
*/
|
||||||
export class ChatInputCommandBuilder extends Mixin(
|
export class ChatInputCommandBuilder extends Mixin(
|
||||||
CommandBuilder<RESTPostAPIChatInputApplicationCommandsJSONBody>,
|
CommandBuilder<RESTPostAPIChatInputApplicationCommandsJSONBody>,
|
||||||
|
|||||||
9
packages/builders/tsdoc.json
Normal file
9
packages/builders/tsdoc.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
|
||||||
|
"tagDefinitions": [
|
||||||
|
{
|
||||||
|
"tagName": "@mixes",
|
||||||
|
"syntaxKind": "block"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user