refactor(website,guide): cloudflare workers support (#11204)

This commit is contained in:
Noel
2025-10-25 02:17:59 +02:00
committed by GitHub
parent 756eac6bb1
commit 08b87d9087
62 changed files with 5143 additions and 1709 deletions

View File

@@ -3,8 +3,6 @@
import { ImageResponse } from 'next/og';
import { resolveKind } from '@/util/resolveNodeKind';
export const runtime = 'edge';
export const size = {
width: 1_200,
height: 630,
@@ -12,6 +10,22 @@ export const size = {
export const contentType = 'image/png';
async function loadGoogleFont(font: string, text: string) {
const url = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent(text)}`;
const css = await (await fetch(url)).text();
// eslint-disable-next-line prefer-named-capture-group
const resource = /src: url\((.+)\) format\('(opentype|truetype)'\)/.exec(css);
if (resource) {
const response = await fetch(resource[1]!);
if (response.status === 200) {
return response.arrayBuffer();
}
}
throw new Error('failed to load font data');
}
export default async function Image({
params,
}: {
@@ -19,14 +33,6 @@ export default async function Image({
}) {
const { item, packageName, version } = await params;
const [fontDataBold, fontDataBlack] = await Promise.all([
fetch(new URL('../../../../../../assets/Geist-Bold.ttf', import.meta.url), {
next: { revalidate: 604_800 },
}).then(async (res) => res.arrayBuffer()),
fetch(new URL('../../../../../../assets/Geist-Black.ttf', import.meta.url), {
next: { revalidate: 604_800 },
}).then(async (res) => res.arrayBuffer()),
]);
const normalizeItem = item.split(encodeURIComponent(':')).join('.').toLowerCase();
const isMain = version === 'main';
@@ -107,13 +113,13 @@ export default async function Image({
fonts: [
{
name: 'Geist',
data: fontDataBold,
data: await loadGoogleFont('Geist:wght@700', node.displayName),
weight: 700,
style: 'normal',
},
{
name: 'Geist',
data: fontDataBlack,
data: await loadGoogleFont('Geist:wght@900', node.displayName),
weight: 900,
style: 'normal',
},

View File

@@ -1,15 +1,12 @@
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import rehypeShikiFromHighlighter from '@shikijs/rehype/core';
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { MDXRemote } from 'next-mdx-remote-client/rsc';
import remarkGfm from 'remark-gfm';
import { SafeMdxRenderer } from 'safe-mdx';
import { mdxParse } from 'safe-mdx/parse';
import { DocItem } from '@/components/DocItem';
import { PACKAGES_WITH_ENTRY_POINTS } from '@/util/constants';
import { SyntaxHighlighter } from '@/components/SyntaxHighlighter';
// import { PACKAGES_WITH_ENTRY_POINTS } from '@/util/constants';
import { fetchNode } from '@/util/fetchNode';
import { parseDocsPathParams } from '@/util/parseDocsPathParams';
import { getSingletonHighlighter } from '@/util/shiki.bundle';
export async function generateMetadata({
params,
@@ -52,48 +49,38 @@ export default async function Page({
const { entryPoints: parsedEntrypoints, foundItem } = parseDocsPathParams(item);
if (!foundItem) {
const hasEntryPoint = PACKAGES_WITH_ENTRY_POINTS.includes(packageName);
// const hasEntryPoint = PACKAGES_WITH_ENTRY_POINTS.includes(packageName);
if (hasEntryPoint) {
return <>Placeholder</>;
}
// if (hasEntryPoint) {
// return <>Placeholder</>;
// }
let fileContent: string;
try {
fileContent = await readFile(join(process.cwd(), `src/assets/readme/${packageName}/home-README.md`), 'utf8');
} catch (error: any) {
if ('code' in error && error.code === 'ENOENT') {
notFound();
}
throw error;
fileContent = await fetch(`${process.env.CF_R2_README_BUCKET_URL}/${packageName}/home-README.md`).then(
async (res) => res.text(),
);
} catch {
notFound();
}
const mdast = mdxParse(fileContent);
return (
<div className="prose prose-neutral dark:prose-invert prose-a:[&>img]:inline-block prose-a:[&>img]:m-0 prose-a:[&>img[height='44']]:h-11 prose-p:my-2 prose-pre:py-3 prose-pre:rounded-sm prose-pre:px-0 prose-pre:border prose-pre:border-[#d4d4d4] dark:prose-pre:border-[#404040] prose-code:font-normal prose-a:text-[#5865F2] prose-a:no-underline prose-a:hover:text-[#3d48c3] dark:prose-a:hover:text-[#7782fa] mx-auto max-w-screen-xl px-6 py-6 break-words [&_code_span:last-of-type:empty]:hidden [&_div[align='center']_p_a+a]:ml-2">
<MDXRemote
options={{
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [
[
rehypeShikiFromHighlighter,
await getSingletonHighlighter({
langs: ['typescript', 'javascript', 'shellscript'],
themes: ['github-light', 'github-dark-dimmed'],
}),
{
themes: {
light: 'github-light',
dark: 'github-dark-dimmed',
},
},
],
],
},
<SafeMdxRenderer
markdown={fileContent}
mdast={mdast}
renderNode={(node) => {
if (node.type === 'code') {
const language = node.lang ?? 'text';
return <SyntaxHighlighter code={node.value} lang={language} />;
}
return undefined;
}}
source={fileContent}
/>
</div>
);

View File

@@ -2,8 +2,6 @@
import { ImageResponse } from 'next/og';
export const runtime = 'edge';
export const size = {
width: 1_200,
height: 630,
@@ -11,11 +9,23 @@ export const size = {
export const contentType = 'image/png';
export default async function Image() {
const fontData = await fetch(new URL('../assets/Geist-Black.ttf', import.meta.url), { cache: 'force-cache' }).then(
async (res) => res.arrayBuffer(),
);
async function loadGoogleFont(font: string, text: string) {
const url = `https://fonts.googleapis.com/css2?family=${font}&text=${encodeURIComponent(text)}`;
const css = await (await fetch(url)).text();
// eslint-disable-next-line prefer-named-capture-group
const resource = /src: url\((.+)\) format\('(opentype|truetype)'\)/.exec(css);
if (resource) {
const response = await fetch(resource[1]!);
if (response.status === 200) {
return response.arrayBuffer();
}
}
throw new Error('failed to load font data');
}
export default async function Image() {
return new ImageResponse(
(
<div tw="flex bg-[#121214] h-full w-full">
@@ -39,7 +49,7 @@ export default async function Image() {
fonts: [
{
name: 'Geist',
data: fontData,
data: await loadGoogleFont('Geist:wght@900', 'The most popular way to build Discord bots.'),
weight: 900,
style: 'normal',
},

View File

@@ -43,12 +43,12 @@ export function CmdK({ dependencies }: { readonly dependencies: string[] }) {
value={item.id}
>
{resolveKind(item.kind)}
<div className="flex flex-grow flex-col">
<div className="flex grow flex-col">
<span className="font-semibold wrap-anywhere">{item.name}</span>
<span className={cx('truncate text-sm', isMobile ? 'max-w-[30ch]' : 'max-w-[40ch]')}>{item.summary}</span>
<span className={cx('truncate text-xs', isMobile ? 'max-w-[30ch]' : 'max-w-[40ch]')}>{item.path}</span>
</div>
<ArrowRight aria-hidden className="flex-shrink-0" />
<ArrowRight aria-hidden className="shrink-0" />
</Command.Item>
)) ?? [];

View File

@@ -1,9 +1,9 @@
import Cloudflare from 'cloudflare';
// import Cloudflare from 'cloudflare';
import { ENV } from './env';
const client = new Cloudflare({
apiToken: process.env.CF_D1_DOCS_API_KEY,
});
// const client = new Cloudflare({
// apiToken: process.env.CF_D1_DOCS_API_KEY,
// });
export async function fetchVersions(packageName: string) {
if (ENV.IS_LOCAL_DEV) {
@@ -11,13 +11,32 @@ export async function fetchVersions(packageName: string) {
}
try {
const { result } = await client.d1.database.query(process.env.CF_D1_DOCS_ID!, {
account_id: process.env.CF_ACCOUNT_ID!,
sql: `select version from documentation where name = ? order by version desc;`,
params: [packageName],
});
// const { result } = await client.d1.database.query(process.env.CF_D1_DOCS_ID!, {
// account_id: process.env.CF_ACCOUNT_ID!,
// sql: `select version from documentation where name = ? order by version desc;`,
// params: [packageName],
// });
return (result[0]?.results as { version: string }[] | undefined) ?? [];
// return (result[0]?.results as { version: string }[] | undefined) ?? [];
const response = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${process.env.CF_ACCOUNT_ID}/d1/database/${process.env.CF_D1_DOCS_ID}/query`,
{
headers: {
Authorization: `Bearer ${process.env.CF_D1_DOCS_API_KEY}`,
'Content-Type': 'application/json',
},
method: 'POST',
body: JSON.stringify({
sql: `select version from documentation where name = ? order by version desc;`,
params: [packageName],
}),
},
);
const data = await response.json();
return data.result[0]?.results;
} catch {
return [];
}