mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 00:23:30 +01:00
* fix(website): resolve linkTags in summaries * fix: case body as block * fix: add discord-api-types support * fix: remove urlDestination when undefined * fix: breaks to if/else --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
194 lines
4.7 KiB
TypeScript
194 lines
4.7 KiB
TypeScript
import { stat, mkdir, writeFile } from 'node:fs/promises';
|
|
import { join } from 'node:path';
|
|
import { cwd } from 'node:process';
|
|
import {
|
|
type ApiItem,
|
|
ApiPackage,
|
|
ApiModel,
|
|
ApiDeclaredItem,
|
|
ApiItemContainerMixin,
|
|
ApiItemKind,
|
|
} from '@discordjs/api-extractor-model';
|
|
import { generatePath } from '@discordjs/api-extractor-utils';
|
|
import { DocNodeKind } from '@microsoft/tsdoc';
|
|
import type { DocLinkTag, DocCodeSpan, DocNode, DocParagraph, DocPlainText } from '@microsoft/tsdoc';
|
|
import { request } from 'undici';
|
|
|
|
export interface MemberJSON {
|
|
kind: string;
|
|
name: string;
|
|
path: string;
|
|
summary: string | null;
|
|
}
|
|
|
|
export const PACKAGES = [
|
|
'discord.js',
|
|
'brokers',
|
|
'builders',
|
|
'collection',
|
|
'core',
|
|
'formatters',
|
|
'next',
|
|
'proxy',
|
|
'rest',
|
|
'util',
|
|
'voice',
|
|
'ws',
|
|
];
|
|
let idx = 0;
|
|
|
|
/**
|
|
* Attempts to resolve the summary text for the given item.
|
|
*
|
|
* @param item - The API item to resolve the summary text for.
|
|
*/
|
|
export function tryResolveSummaryText(item: ApiDeclaredItem): string | null {
|
|
if (!item?.tsdocComment) {
|
|
return null;
|
|
}
|
|
|
|
const { summarySection } = item.tsdocComment;
|
|
|
|
let retVal = '';
|
|
|
|
// Recursively visit the nodes in the summary section.
|
|
const visitTSDocNode = (node: DocNode) => {
|
|
switch (node.kind) {
|
|
case DocNodeKind.CodeSpan:
|
|
retVal += (node as DocCodeSpan).code;
|
|
break;
|
|
case DocNodeKind.PlainText:
|
|
retVal += (node as DocPlainText).text;
|
|
break;
|
|
case DocNodeKind.LinkTag: {
|
|
const { codeDestination, urlDestination, linkText } = node as DocLinkTag;
|
|
if (codeDestination) {
|
|
const declarationReference = item.getAssociatedModel()?.resolveDeclarationReference(codeDestination, item);
|
|
if (declarationReference?.resolvedApiItem) {
|
|
const foundItem = declarationReference.resolvedApiItem;
|
|
retVal += linkText ?? foundItem.displayName;
|
|
} else {
|
|
const typeName = codeDestination.memberReferences.map((ref) => ref.memberIdentifier?.identifier).join('.');
|
|
retVal += typeName;
|
|
}
|
|
} else {
|
|
retVal += linkText ?? urlDestination;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case DocNodeKind.Section:
|
|
case DocNodeKind.Paragraph: {
|
|
for (const child of (node as DocParagraph).nodes) {
|
|
visitTSDocNode(child);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default: // We'll ignore all other nodes.
|
|
break;
|
|
}
|
|
};
|
|
|
|
for (const node of summarySection.nodes) {
|
|
visitTSDocNode(node);
|
|
}
|
|
|
|
if (retVal === '') {
|
|
return null;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
export function visitNodes(item: ApiItem, tag: string) {
|
|
const members: (MemberJSON & { id: number })[] = [];
|
|
|
|
for (const member of item.members) {
|
|
if (!(member instanceof ApiDeclaredItem)) {
|
|
continue;
|
|
}
|
|
|
|
if (member.kind === ApiItemKind.Constructor || member.kind === ApiItemKind.Namespace) {
|
|
continue;
|
|
}
|
|
|
|
if (ApiItemContainerMixin.isBaseClassOf(member)) {
|
|
members.push(...visitNodes(member, tag));
|
|
}
|
|
|
|
members.push({
|
|
id: idx++,
|
|
name: member.displayName,
|
|
kind: member.kind,
|
|
summary: tryResolveSummaryText(member) ?? '',
|
|
path: generatePath(member.getHierarchy(), tag),
|
|
});
|
|
}
|
|
|
|
return members;
|
|
}
|
|
|
|
export async function writeIndexToFileSystem(
|
|
members: ReturnType<typeof visitNodes>,
|
|
packageName: string,
|
|
tag = 'main',
|
|
) {
|
|
const dir = 'searchIndex';
|
|
|
|
try {
|
|
(await stat(join(cwd(), 'public', dir))).isDirectory();
|
|
} catch {
|
|
await mkdir(join(cwd(), 'public', dir));
|
|
}
|
|
|
|
await writeFile(
|
|
join(cwd(), 'public', dir, `${packageName}-${tag}-index.json`),
|
|
JSON.stringify(members, undefined, 2),
|
|
);
|
|
}
|
|
|
|
export async function fetchVersions(pkg: string) {
|
|
const response = await request(`https://docs.discordjs.dev/api/info?package=${pkg}`);
|
|
return response.body.json() as Promise<string[]>;
|
|
}
|
|
|
|
export async function fetchVersionDocs(pkg: string, version: string) {
|
|
const response = await request(`https://docs.discordjs.dev/docs/${pkg}/${version}.api.json`);
|
|
return response.body.json();
|
|
}
|
|
|
|
export async function generateAllIndices({
|
|
fetchPackageVersions = fetchVersions,
|
|
fetchPackageVersionDocs = fetchVersionDocs,
|
|
writeToFile = true,
|
|
} = {}) {
|
|
const indices: Record<any, any>[] = [];
|
|
|
|
for (const pkg of PACKAGES) {
|
|
const versions = await fetchPackageVersions(pkg);
|
|
|
|
for (const version of versions) {
|
|
idx = 0;
|
|
|
|
const data = await fetchPackageVersionDocs(pkg, version);
|
|
const model = new ApiModel();
|
|
model.addMember(ApiPackage.loadFromJson(data));
|
|
const members = visitNodes(model.tryGetPackageByName(pkg)!.entryPoints[0]!, version);
|
|
|
|
const sanitizePackageName = pkg.replaceAll('.', '-');
|
|
const sanitizeVersion = version.replaceAll('.', '-');
|
|
|
|
if (writeToFile) {
|
|
await writeIndexToFileSystem(members, sanitizePackageName, sanitizeVersion);
|
|
} else {
|
|
indices.push({ index: `${sanitizePackageName}-${sanitizeVersion}`, data: members });
|
|
}
|
|
}
|
|
}
|
|
|
|
return indices;
|
|
}
|