diff --git a/apps/website/src/app/docs/packages/[package]/[version]/[item]/head.tsx b/apps/website/src/app/docs/packages/[package]/[version]/[item]/head.tsx deleted file mode 100644 index 38e350b33..000000000 --- a/apps/website/src/app/docs/packages/[package]/[version]/[item]/head.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { createApiModel, tryResolveSummaryText } from '@discordjs/scripts'; -import type { - ApiDeclaredItem, - ApiEnum, - ApiItem, - ApiItemContainerMixin, - ApiMethod, - ApiMethodSignature, - ApiProperty, - ApiPropertySignature, -} from '@microsoft/api-extractor-model'; -import { ApiItemKind } from '@microsoft/api-extractor-model'; -import type { ItemRouteParams } from './page'; -import { fetchModelJSON } from '~/app/docAPI'; -import { OVERLOAD_SEPARATOR } from '~/util/constants'; -import { findMember } from '~/util/model.server'; - -async function fetchMember({ package: packageName, version, item }: ItemRouteParams): Promise { - const modelJSON = await fetchModelJSON(packageName, version); - const model = createApiModel(modelJSON); - const pkg = model.tryGetPackageByName(packageName); - const entry = pkg?.entryPoints[0]; - - if (!entry) { - return undefined; - } - - const [memberName] = decodeURIComponent(item).split(OVERLOAD_SEPARATOR); - - return findMember(model, packageName, memberName); -} - -function resolveMemberSearchParams(packageName: string, member: ApiItem): URLSearchParams { - const params = new URLSearchParams({ - pkg: packageName, - kind: member?.kind, - name: member?.displayName, - }); - - switch (member?.kind) { - case ApiItemKind.Interface: - case ApiItemKind.Class: { - const typedMember = member as ApiItemContainerMixin; - - const properties = typedMember.members.filter((member) => - [ApiItemKind.Property, ApiItemKind.PropertySignature].includes(member.kind), - ) as (ApiProperty | ApiPropertySignature)[]; - const methods = typedMember.members.filter((member) => - [ApiItemKind.Method, ApiItemKind.Method].includes(member.kind), - ) as (ApiMethod | ApiMethodSignature)[]; - - params.append('methods', methods.length.toString()); - params.append('props', properties.length.toString()); - break; - } - - case ApiItemKind.Enum: { - const typedMember = member as ApiEnum; - params.append('members', typedMember.members.length.toString()); - break; - } - - default: - break; - } - - return params; -} - -export default async function Head({ params }: { params: ItemRouteParams }) { - const member = (await fetchMember(params))!; - const name = `discord.js${member?.displayName ? ` | ${member.displayName}` : ''}`; - const ogTitle = `${params.package ?? 'discord.js'}${member?.displayName ? ` | ${member.displayName}` : ''}`; - const searchParams = resolveMemberSearchParams(params.package, member); - const url = new URL('https://discordjs.dev/api/og_model'); - url.search = searchParams.toString(); - const ogImage = url.toString(); - const description = tryResolveSummaryText(member as ApiDeclaredItem); - - return ( - <> - {name} - - - - - - ); -} diff --git a/apps/website/src/app/docs/packages/[package]/[version]/[item]/page.tsx b/apps/website/src/app/docs/packages/[package]/[version]/[item]/page.tsx index fc09eef4a..179195470 100644 --- a/apps/website/src/app/docs/packages/[package]/[version]/[item]/page.tsx +++ b/apps/website/src/app/docs/packages/[package]/[version]/[item]/page.tsx @@ -3,16 +3,23 @@ import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; // eslint-disable-next-line n/prefer-global/process import process, { cwd } from 'node:process'; -import { createApiModel } from '@discordjs/scripts'; +import { createApiModel, tryResolveSummaryText } from '@discordjs/scripts'; import type { ApiClass, + ApiDeclaredItem, ApiEnum, ApiInterface, ApiItem, + ApiItemContainerMixin, + ApiMethod, + ApiMethodSignature, + ApiProperty, + ApiPropertySignature, ApiTypeAlias, ApiVariable, } from '@microsoft/api-extractor-model'; -import { ApiFunction } from '@microsoft/api-extractor-model'; +import { ApiItemKind, ApiFunction } from '@microsoft/api-extractor-model'; +import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; import { fetchModelJSON } from '~/app/docAPI'; import { Class } from '~/components/model/Class'; @@ -30,6 +37,79 @@ export interface ItemRouteParams { version: string; } +async function fetchHeadMember({ package: packageName, version, item }: ItemRouteParams): Promise { + const modelJSON = await fetchModelJSON(packageName, version); + const model = createApiModel(modelJSON); + const pkg = model.tryGetPackageByName(packageName); + const entry = pkg?.entryPoints[0]; + + if (!entry) { + return undefined; + } + + const [memberName] = decodeURIComponent(item).split(OVERLOAD_SEPARATOR); + + return findMember(model, packageName, memberName); +} + +function resolveMemberSearchParams(packageName: string, member: ApiItem): URLSearchParams { + const params = new URLSearchParams({ + pkg: packageName, + kind: member?.kind, + name: member?.displayName, + }); + + switch (member?.kind) { + case ApiItemKind.Interface: + case ApiItemKind.Class: { + const typedMember = member as ApiItemContainerMixin; + + const properties = typedMember.members.filter((member) => + [ApiItemKind.Property, ApiItemKind.PropertySignature].includes(member.kind), + ) as (ApiProperty | ApiPropertySignature)[]; + const methods = typedMember.members.filter((member) => + [ApiItemKind.Method, ApiItemKind.Method].includes(member.kind), + ) as (ApiMethod | ApiMethodSignature)[]; + + params.append('methods', methods.length.toString()); + params.append('props', properties.length.toString()); + break; + } + + case ApiItemKind.Enum: { + const typedMember = member as ApiEnum; + params.append('members', typedMember.members.length.toString()); + break; + } + + default: + break; + } + + return params; +} + +export async function generateMetadata({ params }: { params: ItemRouteParams }): Promise { + const member = (await fetchHeadMember(params))!; + const name = `discord.js${member?.displayName ? ` | ${member.displayName}` : ''}`; + const ogTitle = `${params.package ?? 'discord.js'}${member?.displayName ? ` | ${member.displayName}` : ''}`; + const url = new URL('https://discordjs.dev/api/og_model'); + const searchParams = resolveMemberSearchParams(params.package, member); + url.search = searchParams.toString(); + const ogImage = url.toString(); + const description = tryResolveSummaryText(member as ApiDeclaredItem); + + return { + title: name, + description: description ?? 'Discord.js API Documentation', + openGraph: { + title: ogTitle, + description: description ?? 'Discord.js API Documentation', + images: ogImage, + }, + }; +} + export async function generateStaticParams({ params: { package: packageName, version } }: { params: ItemRouteParams }) { const modelJSON = await fetchModelJSON(packageName, version); const model = createApiModel(modelJSON); diff --git a/apps/website/src/app/head.tsx b/apps/website/src/app/head.tsx deleted file mode 100644 index e8d503b73..000000000 --- a/apps/website/src/app/head.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { DESCRIPTION } from '~/util/constants'; - -export default function Head() { - return ( - <> - - - - - - - - - - - - - - - - - - discord.js - - - - ); -} diff --git a/apps/website/src/app/page.tsx b/apps/website/src/app/page.tsx index 5a9f34ab5..a3bc20574 100644 --- a/apps/website/src/app/page.tsx +++ b/apps/website/src/app/page.tsx @@ -3,7 +3,66 @@ import Image from 'next/image'; import Link from 'next/link'; import vercelLogo from '../assets/powered-by-vercel.svg'; import { SyntaxHighlighter } from '~/components/SyntaxHighlighter'; -import { CODE_EXAMPLE } from '~/util/constants'; +import { CODE_EXAMPLE, DESCRIPTION } from '~/util/constants'; + +export const metadata = { + title: 'discord.js', + description: DESCRIPTION, + viewport: { + minimumScale: 1, + initialScale: 1, + width: 'device-width', + }, + icons: { + other: [ + { + url: '/favicon-32x32.png', + sizes: '32x32', + type: 'image/png', + }, + { + url: '/favicon-16x16.png', + sizes: '16x16', + type: 'image/png', + }, + ], + apple: [ + '/apple-touch-icon.png', + { + url: '/safari-pinned-tab.svg', + rel: 'mask-icon', + }, + ], + }, + + manifest: '/site.webmanifest', + + themeColor: '#5865f2', + colorScheme: 'light dark', + + appleWebApp: { + title: 'discord.js', + }, + + applicationName: 'discord.js', + + openGraph: { + siteName: 'discord.js', + type: 'website', + title: 'discord.js', + description: DESCRIPTION, + images: 'https://discordjs.dev/api/og', + }, + + twitter: { + card: 'summary_large_image', + creator: '@iCrawlToGo', + }, + + other: { + 'msapplication-TileColor': '#090a16', + }, +}; export default function Page() { return (