refactor(changelog) migrate changelog to use fumadocs

This commit is contained in:
javayhu 2025-06-16 01:29:02 +08:00
parent 292faddc7a
commit 483a970b71
6 changed files with 93 additions and 84 deletions

View File

@ -252,43 +252,43 @@ export const pages = defineCollection({
* slug: /release/v1-0-0
* slugAsParams: v1-0-0
*/
export const releases = defineCollection({
name: 'release',
directory: 'content/release',
include: '**/*.mdx',
schema: (z) => ({
title: z.string(),
description: z.string(),
date: z.string().datetime(),
version: z.string(),
published: z.boolean().default(true),
}),
transform: async (data, context) => {
// Use Fumadocs transformMDX for consistent MDX processing
const transformedData = await transformMDX(data, context);
// export const releases = defineCollection({
// name: 'release',
// directory: 'content/release',
// include: '**/*.mdx',
// schema: (z) => ({
// title: z.string(),
// description: z.string(),
// date: z.string().datetime(),
// version: z.string(),
// 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(`release processed: ${fileName}, base=${base}, locale=${locale}`);
// // Extract locale and base from filename
// const { locale, base } = extractLocaleAndBase(fileName);
// // console.log(`release processed: ${fileName}, base=${base}, locale=${locale}`);
// Create the slug and slugAsParams
const slug = `/release/${base}`;
const slugAsParams = base;
// // Create the slug and slugAsParams
// const slug = `/release/${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,
// };
// },
// });
/**
* Helper function to extract locale and base name from filename
@ -320,5 +320,5 @@ function extractLocaleAndBase(fileName: string): {
}
export default defineConfig({
collections: [authors, categories, posts, pages, releases],
collections: [authors, categories, posts, pages],
});

View File

@ -1,4 +1,9 @@
import { defineDocs, frontmatterSchema, metaSchema } from 'fumadocs-mdx/config';
import {
defineCollections,
defineDocs,
frontmatterSchema,
metaSchema,
} from 'fumadocs-mdx/config';
import { z } from 'zod';
const customDocsSchema = frontmatterSchema.extend({
@ -11,9 +16,6 @@ const customMetaSchema = metaSchema.extend({
});
/**
* frontmatterSchema.extend causes error: Type instantiation is excessively deep,
* so we define the schema manually.
*
* https://fumadocs.dev/docs/mdx/collections#schema-1
*/
export const docs = defineDocs({
@ -25,3 +27,18 @@ export const docs = defineDocs({
schema: customMetaSchema,
},
});
/**
* Changelog releases
*/
export const releases = defineCollections({
type: 'doc',
dir: 'content/release',
schema: z.object({
title: z.string(),
description: z.string(),
date: z.string().datetime(),
version: z.string(),
published: z.boolean().default(true),
}),
});

View File

@ -1,6 +1,7 @@
import Container from '@/components/layout/container';
import { ReleaseCard } from '@/components/release/release-card';
import { changelog } from '@/lib/docs/source';
import { constructMetadata } from '@/lib/metadata';
import { getReleases } from '@/lib/release/get-releases';
import { getUrlWithLocale } from '@/lib/urls/urls';
import type { NextPageProps } from '@/types/next-page-props';
import type { Metadata } from 'next';
@ -9,7 +10,6 @@ import { getTranslations } from 'next-intl/server';
import { notFound } from 'next/navigation';
import '@/styles/mdx.css';
import Container from '@/components/layout/container';
export async function generateMetadata({
params,
@ -34,9 +34,15 @@ export default async function ChangelogPage(props: NextPageProps) {
}
const locale = params.locale as Locale;
const releases = await getReleases(locale);
const localeReleases = changelog.getPages(locale);
const publishedReleases = localeReleases
.filter((releaseItem) => releaseItem.data.published)
.sort(
(a, b) =>
new Date(b.data.date).getTime() - new Date(a.data.date).getTime()
);
if (!releases || releases.length === 0) {
if (!publishedReleases || publishedReleases.length === 0) {
notFound();
}
@ -57,16 +63,20 @@ export default async function ChangelogPage(props: NextPageProps) {
{/* Releases */}
<div className="mt-8">
{releases.map((release) => (
<ReleaseCard
key={release.slug}
title={release.title}
description={release.description}
date={release.date}
version={release.version}
content={release.body}
/>
))}
{publishedReleases.map((releaseItem) => {
const MDX = releaseItem.data.body;
return (
<ReleaseCard
key={releaseItem.data.version}
title={releaseItem.data.title}
description={releaseItem.data.description}
date={releaseItem.data.date}
version={releaseItem.data.version}
content={<MDX />}
/>
);
})}
</div>
</div>
</Container>

View File

@ -1,16 +1,16 @@
import { CustomMDXContent } from '@/components/shared/custom-mdx-content';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Separator } from '@/components/ui/separator';
import { formatDate } from '@/lib/formatter';
import { CalendarIcon, TagIcon } from 'lucide-react';
import type { ReactNode } from 'react';
interface ReleaseCardProps {
title: string;
description: string;
date: string;
version: string;
content: any; // MDX content
content: ReactNode; // React component
}
export function ReleaseCard({
@ -41,7 +41,7 @@ export function ReleaseCard({
</CardHeader>
<CardContent>
<div className="max-w-none prose prose-neutral dark:prose-invert prose-img:rounded-lg">
<CustomMDXContent code={content} />
{content}
</div>
</CardContent>
</Card>

View File

@ -1,7 +1,8 @@
import { loader } from 'fumadocs-core/source';
import { createMDXSource } from 'fumadocs-mdx';
import * as LucideIcons from 'lucide-react';
import { createElement } from 'react';
import { docs } from '../../../.source';
import { docs, releases } from '../../../.source';
import { docsI18nConfig } from './i18n';
/**
@ -29,3 +30,12 @@ export const source = loader({
return undefined;
},
});
/**
* Changelog releases source
*/
export const changelog = loader({
baseUrl: '/changelog',
i18n: docsI18nConfig,
source: createMDXSource(releases),
});

View File

@ -1,28 +0,0 @@
import { allReleases } from 'content-collections';
import type { Locale } from 'next-intl';
/**
* Gets all releases for the changelog page
* @param locale The locale to get releases for
* @returns An array of releases sorted by date (newest first)
*/
export async function getReleases(locale: Locale) {
// Find all published releases with matching locale
const releases = allReleases.filter(
(release) => release.published && release.locale === locale
);
// If no releases found with the current locale, try to find ones with any locale
if (releases.length === 0) {
const defaultReleases = allReleases.filter((release) => release.published);
// Sort by date (newest first)
return defaultReleases.sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
);
}
// Sort by date (newest first)
return releases.sort(
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
);
}