diff --git a/lib/blog.ts b/lib/blog.ts
index 0b301f2c..78559715 100644
--- a/lib/blog.ts
+++ b/lib/blog.ts
@@ -4,6 +4,31 @@ import matter from 'gray-matter';
import { mapSlugToFileSlug } from './slugs';
import { config } from '@/lib/config';
+export function extractExcerpt(content: string): string {
+ if (!content) return '';
+ // Remove frontmatter if present (though matter() usually strips it out)
+ let text = content.replace(/^---[\s\S]*?---/, '');
+ // Remove MDX component imports and usages
+ text = text.replace(/<[^>]+>/g, '');
+ text = text.replace(/^[ \t]*import\s+.*$/gm, '');
+ text = text.replace(/^[ \t]*export\s+.*$/gm, '');
+ // Remove markdown headings
+ text = text.replace(/^#+.*$/gm, '');
+ // Extract first paragraph or combined lines
+ const paragraphs = text
+ .split(/\n\s*\n/)
+ .filter((p) => p.trim() && !p.trim().startsWith('---') && !p.trim().startsWith('#'));
+ if (paragraphs.length === 0) return '';
+
+ const excerpt = paragraphs[0]
+ .replace(/[*_`]/g, '') // remove markdown bold/italic/code
+ .replace(/\[(.*?)\]\(.*?\)/g, '$1') // replace links with their text
+ .replace(/\s+/g, ' ')
+ .trim();
+
+ return excerpt.length > 200 ? excerpt.slice(0, 197) + '...' : excerpt;
+}
+
export interface PostFrontmatter {
title: string;
date: string;
@@ -46,7 +71,10 @@ export async function getPostBySlug(slug: string, locale: string): Promise
{
const { data, content } = matter(fileContent);
return {
slug: file.replace(/\.mdx$/, ''),
- frontmatter: data as PostFrontmatter,
+ frontmatter: {
+ ...data,
+ excerpt: data.excerpt || extractExcerpt(content),
+ } as PostFrontmatter,
content,
};
})
@@ -95,7 +126,10 @@ export async function getAllPostsMetadata(locale: string): Promise