refactor(website): use new metadata api instead of head.tsx (#9269)

This commit is contained in:
Suneet Tipirneni
2023-03-23 15:41:29 -04:00
committed by GitHub
parent 852fae557e
commit 03f5f1e3b6
4 changed files with 142 additions and 120 deletions

View File

@@ -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<ApiItem | undefined> {
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 (
<>
<title key="title">{name}</title>
<meta content={description ?? ''} key="description" name="description" />
<meta content={ogTitle} key="og_title" property="og:title" />
<meta content={description ?? ''} key="og_description" property="og:description" />
<meta content={ogImage} key="og_image" property="og:image" />
</>
);
}

View File

@@ -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<ApiItem | undefined> {
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<Metadata> {
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);

View File

@@ -1,28 +0,0 @@
import { DESCRIPTION } from '~/util/constants';
export default function Head() {
return (
<>
<link href="/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180" />
<link href="/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png" />
<link href="/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png" />
<link href="/site.webmanifest" rel="manifest" />
<link color="#090a16" href="/safari-pinned-tab.svg" rel="mask-icon" />
<meta content="light dark" name="color-scheme" />
<meta content="discord.js" name="apple-mobile-web-app-title" />
<meta content="discord.js" name="application-name" />
<meta content="#090a16" name="msapplication-TileColor" />
<meta content={DESCRIPTION} key="description" name="description" />
<meta content="discord.js" property="og:site_name" />
<meta content="website" property="og:type" />
<meta content="discord.js" key="og_title" property="og:title" />
<meta content={DESCRIPTION} key="og_description" property="og:description" />
<meta content="https://discordjs.dev/api/og" key="og_image" property="og:image" />
<meta content="summary_large_image" name="twitter:card" />
<meta content="@iCrawlToGo" name="twitter:creator" />
<title key="title">discord.js</title>
<meta content="minimum-scale=1, initial-scale=1, width=device-width" name="viewport" />
<meta content="#5865f2" name="theme-color" />
</>
);
}

View File

@@ -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 (