refactor(pages) migrate custom pages to using fumadocs

This commit is contained in:
javayhu 2025-06-16 23:34:22 +08:00
parent 543798e2c1
commit c477aae333
16 changed files with 94 additions and 119 deletions

View File

@ -200,42 +200,42 @@ export const posts = defineCollection({
* slug: /pages/privacy-policy
* slugAsParams: privacy-policy
*/
export const pages = defineCollection({
name: 'page',
directory: 'content/pages',
include: '**/*.mdx',
schema: (z) => ({
title: z.string(),
description: z.string(),
date: z.string().datetime(),
published: z.boolean().default(true),
}),
transform: async (data, context) => {
// Use Fumadocs transformMDX for consistent MDX processing
const transformedData = await transformMDX(data, context);
// export const pages = defineCollection({
// name: 'page',
// directory: 'content/pages',
// include: '**/*.mdx',
// schema: (z) => ({
// title: z.string(),
// description: z.string(),
// date: z.string().datetime(),
// published: z.boolean().default(true),
// }),
// transform: async (data, context) => {
// // Use Fumadocs transformMDX for consistent MDX processing
// const transformedData = await transformMDX(data, context);
// Get the filename from the path
const filePath = data._meta.path;
const fileName = filePath.split(path.sep).pop() || '';
// // Get the filename from the path
// const filePath = data._meta.path;
// const fileName = filePath.split(path.sep).pop() || '';
// Extract locale and base from filename
const { locale, base } = extractLocaleAndBase(fileName);
// console.log(`page processed: ${fileName}, base=${base}, locale=${locale}`);
// // Extract locale and base from filename
// const { locale, base } = extractLocaleAndBase(fileName);
// // console.log(`page processed: ${fileName}, base=${base}, locale=${locale}`);
// Create the slug and slugAsParams
const slug = `/pages/${base}`;
const slugAsParams = base;
// // Create the slug and slugAsParams
// const slug = `/pages/${base}`;
// const slugAsParams = base;
return {
...data,
locale,
slug,
slugAsParams,
body: transformedData.body,
toc: transformedData.toc,
};
},
});
// return {
// ...data,
// locale,
// slug,
// slugAsParams,
// body: transformedData.body,
// toc: transformedData.toc,
// };
// },
// });
/**
* Releases collection for changelog

View File

@ -1,7 +1,7 @@
---
title: Cookie Policy
description: How we use cookies and similar technologies on our website
date: 2025-03-10T00:00:00.000Z
date: "2025-03-10T00:00:00.000Z"
published: true
---

View File

@ -1,7 +1,7 @@
---
title: Cookie 政策
description: 我们如何在网站上使用 Cookie 和类似技术
date: 2025-03-10T00:00:00.000Z
date: "2025-03-10T00:00:00.000Z"
published: true
---

View File

@ -1,7 +1,7 @@
---
title: Privacy Policy
description: Our commitment to protecting your privacy and personal data
date: 2025-03-10T00:00:00.000Z
date: "2025-03-10T00:00:00.000Z"
published: true
---

View File

@ -1,7 +1,7 @@
---
title: 隐私政策
description: 我们致力于保护您的隐私和个人数据
date: 2025-03-10T00:00:00.000Z
date: "2025-03-10T00:00:00.000Z"
published: true
---

View File

@ -1,7 +1,7 @@
---
title: Terms of Service
description: The terms and conditions governing the use of our services
date: 2025-03-10T00:00:00.000Z
date: "2025-03-10T00:00:00.000Z"
published: true
---

View File

@ -1,7 +1,7 @@
---
title: 服务条款
description: 管理我们服务使用的条款和条件
date: 2025-03-10T00:00:00.000Z
date: "2025-03-10T00:00:00.000Z"
published: true
---

View File

@ -42,3 +42,17 @@ export const changelog = defineCollections({
published: z.boolean().default(true),
}),
});
/**
* Pages
*/
export const pages = defineCollections({
type: 'doc',
dir: 'content/pages',
schema: z.object({
title: z.string(),
description: z.string(),
date: z.string().datetime(),
published: z.boolean().default(true),
}),
});

View File

@ -1,6 +1,6 @@
import { CustomPage } from '@/components/page/custom-page';
import { pagesSource } from '@/lib/docs/source';
import { constructMetadata } from '@/lib/metadata';
import { getPage } from '@/lib/page/get-page';
import { getUrlWithLocale } from '@/lib/urls/urls';
import type { NextPageProps } from '@/types/next-page-props';
import type { Metadata } from 'next';
@ -14,7 +14,7 @@ export async function generateMetadata({
params: Promise<{ locale: Locale }>;
}): Promise<Metadata | undefined> {
const { locale } = await params;
const page = await getPage('cookie-policy', locale);
const page = pagesSource.getPage(['cookie-policy'], locale);
if (!page) {
console.warn(
@ -26,8 +26,8 @@ export async function generateMetadata({
const t = await getTranslations({ locale, namespace: 'Metadata' });
return constructMetadata({
title: page.title + ' | ' + t('title'),
description: page.description,
title: page.data.title + ' | ' + t('title'),
description: page.data.description,
canonicalUrl: getUrlWithLocale('/cookie', locale),
});
}
@ -39,18 +39,11 @@ export default async function CookiePolicyPage(props: NextPageProps) {
}
const locale = params.locale as string;
const page = await getPage('cookie-policy', locale);
const page = pagesSource.getPage(['cookie-policy'], locale);
if (!page) {
notFound();
}
return (
<CustomPage
title={page.title}
description={page.description}
date={page.date}
content={page.body}
/>
);
return <CustomPage page={page} />;
}

View File

@ -1,6 +1,6 @@
import { CustomPage } from '@/components/page/custom-page';
import { pagesSource } from '@/lib/docs/source';
import { constructMetadata } from '@/lib/metadata';
import { getPage } from '@/lib/page/get-page';
import { getUrlWithLocale } from '@/lib/urls/urls';
import type { NextPageProps } from '@/types/next-page-props';
import type { Metadata } from 'next';
@ -14,7 +14,7 @@ export async function generateMetadata({
params: Promise<{ locale: Locale }>;
}): Promise<Metadata | undefined> {
const { locale } = await params;
const page = await getPage('privacy-policy', locale);
const page = pagesSource.getPage(['privacy-policy'], locale);
if (!page) {
console.warn(
@ -26,8 +26,8 @@ export async function generateMetadata({
const t = await getTranslations({ locale, namespace: 'Metadata' });
return constructMetadata({
title: page.title + ' | ' + t('title'),
description: page.description,
title: page.data.title + ' | ' + t('title'),
description: page.data.description,
canonicalUrl: getUrlWithLocale('/privacy', locale),
});
}
@ -39,18 +39,11 @@ export default async function PrivacyPolicyPage(props: NextPageProps) {
}
const locale = params.locale as string;
const page = await getPage('privacy-policy', locale);
const page = pagesSource.getPage(['privacy-policy'], locale);
if (!page) {
notFound();
}
return (
<CustomPage
title={page.title}
description={page.description}
date={page.date}
content={page.body}
/>
);
return <CustomPage page={page} />;
}

View File

@ -1,6 +1,6 @@
import { CustomPage } from '@/components/page/custom-page';
import { pagesSource } from '@/lib/docs/source';
import { constructMetadata } from '@/lib/metadata';
import { getPage } from '@/lib/page/get-page';
import { getUrlWithLocale } from '@/lib/urls/urls';
import type { NextPageProps } from '@/types/next-page-props';
import type { Metadata } from 'next';
@ -14,7 +14,7 @@ export async function generateMetadata({
params: Promise<{ locale: Locale }>;
}): Promise<Metadata | undefined> {
const { locale } = await params;
const page = await getPage('terms-of-service', locale);
const page = pagesSource.getPage(['terms-of-service'], locale);
if (!page) {
console.warn(
@ -26,8 +26,8 @@ export async function generateMetadata({
const t = await getTranslations({ locale, namespace: 'Metadata' });
return constructMetadata({
title: page.title + ' | ' + t('title'),
description: page.description,
title: page.data.title + ' | ' + t('title'),
description: page.data.description,
canonicalUrl: getUrlWithLocale('/terms', locale),
});
}
@ -39,18 +39,11 @@ export default async function TermsOfServicePage(props: NextPageProps) {
}
const locale = params.locale as string;
const page = await getPage('terms-of-service', locale);
const page = pagesSource.getPage(['terms-of-service'], locale);
if (!page) {
notFound();
}
return (
<CustomPage
title={page.title}
description={page.description}
date={page.date}
content={page.body}
/>
);
return <CustomPage page={page} />;
}

View File

@ -1,5 +1,5 @@
import { ReleaseCard } from '@/components/changelog/release-card';
import Container from '@/components/layout/container';
import { ReleaseCard } from '@/components/release/release-card';
import { changelogSource } from '@/lib/docs/source';
import { constructMetadata } from '@/lib/metadata';
import { getUrlWithLocale } from '@/lib/urls/urls';

View File

@ -3,13 +3,14 @@ import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
import type { changelogSource } from '@/lib/docs/source';
import { formatDate } from '@/lib/formatter';
import type { InferPageType } from 'fumadocs-core/source';
import { CalendarIcon, TagIcon } from 'lucide-react';
import { getMDXComponents } from '../custom/mdx-components';
type ChangelogPage = ReturnType<typeof changelogSource.getPages>[number];
type ChangelogRelease = InferPageType<typeof changelogSource>;
interface ReleaseCardProps {
releaseItem: ChangelogPage;
releaseItem: ChangelogRelease;
}
export function ReleaseCard({ releaseItem }: ReleaseCardProps) {

View File

@ -1,22 +1,20 @@
import { CustomMDXContent } from '@/components/shared/custom-mdx-content';
import type { pagesSource } from '@/lib/docs/source';
import { formatDate } from '@/lib/formatter';
import type { InferPageType } from 'fumadocs-core/source';
import { CalendarIcon } from 'lucide-react';
import { getMDXComponents } from '../custom/mdx-components';
import { Card, CardContent } from '../ui/card';
type Page = InferPageType<typeof pagesSource>;
interface CustomPageProps {
title: string;
description: string;
date: string;
content: any; // MDX content
page: Page;
}
export function CustomPage({
title,
description,
date,
content,
}: CustomPageProps) {
export function CustomPage({ page }: CustomPageProps) {
const { title, description, date } = page.data;
const formattedDate = formatDate(new Date(date));
const MDX = page.data.body;
return (
<div className="max-w-4xl mx-auto space-y-8">
@ -38,7 +36,7 @@ export function CustomPage({
<Card className="mb-8">
<CardContent>
<div className="max-w-none prose prose-neutral dark:prose-invert prose-img:rounded-lg">
<CustomMDXContent code={content} />
<MDX components={getMDXComponents()} />
</div>
</CardContent>
</Card>

View File

@ -2,7 +2,7 @@ import { loader } from 'fumadocs-core/source';
import { createMDXSource } from 'fumadocs-mdx';
import * as LucideIcons from 'lucide-react';
import { createElement } from 'react';
import { changelog, docs } from '../../../.source';
import { changelog, docs, pages } from '../../../.source';
import { docsI18nConfig } from './i18n';
/**
@ -39,3 +39,12 @@ export const changelogSource = loader({
i18n: docsI18nConfig,
source: createMDXSource(changelog),
});
/**
* Pages source
*/
export const pagesSource = loader({
baseUrl: '/pages',
i18n: docsI18nConfig,
source: createMDXSource(pages),
});

View File

@ -1,26 +0,0 @@
import { allPages } from 'content-collections';
import type { Locale } from 'next-intl';
/**
* Gets a page from the content collection
* @param type The type of page to get (e.g., 'privacy-policy', 'terms-of-service')
* @param locale The locale to get the page for
* @returns The page or undefined if not found
*/
export async function getPage(type: string, locale: Locale) {
// Find page with matching slug and locale
const page = allPages.find(
(page) => page.slugAsParams === `${type}` && page.locale === locale
);
if (!page) {
// If no page found with the current locale, try to find one with any locale
const defaultPage = allPages.find(
(page) => page.slugAsParams === `${type}`
);
return defaultPage;
}
return page;
}