import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; import { remark } from 'remark'; import html from 'remark-html'; import { unified } from 'unified'; import remarkParse from 'remark-parse'; import remarkGfm from 'remark-gfm'; import remarkRehype from 'remark-rehype'; import rehypeRaw from 'rehype-raw'; import rehypeStringify from 'rehype-stringify'; import rehypePrism from 'rehype-prism-plus'; const postsDirectory = path.join(process.cwd(), 'posts'); export interface PostData { slug: string; title: string; date: string; tags: string[]; contentHtml: string; description: string; } export function getAllPostSlugs(): string[] { const fileNames = fs.readdirSync(postsDirectory); return fileNames.map((fileName) => fileName.replace(/\.md$/, '')); } export function getSortedPostsData(): Omit[] { const fileNames = fs.readdirSync(postsDirectory); const allPostsData = fileNames.map((fileName) => { const slug = fileName.replace(/\.md$/, ''); const fullPath = path.join(postsDirectory, fileName); const fileContents = fs.readFileSync(fullPath, 'utf8'); const matterResult = matter(fileContents); return { slug, title: matterResult.data.title, date: matterResult.data.date, tags: matterResult.data.tags || [], description: matterResult.data.description || '', }; }); return allPostsData.sort((a, b) => { if (a.date < b.date) { return 1; } else { return -1; } }); } export async function getPostData(slug: string): Promise { try { const fullPath = path.join(postsDirectory, `${slug}.md`); if (!fs.existsSync(fullPath)) { console.error(`Файл не найден: ${fullPath}`); return null; } const fileContents = fs.readFileSync(fullPath, 'utf8'); const matterResult = matter(fileContents); // Используем unified pipeline с подсветкой синтаксиса const processedContent = await unified() .use(remarkParse) // парсим Markdown .use(remarkGfm) // добавляем поддержку GFM (таблицы и т.д.) .use(remarkRehype) // конвертируем в HTML AST .use(rehypePrism, { // Настройки подсветки showLineNumbers: true, // показывать номера строк ignoreMissing: true, // игнорировать неизвестные языки }) .use(rehypeStringify) // сериализуем в строку .process(matterResult.content || ''); const contentHtml = processedContent.toString(); return { slug, contentHtml, title: matterResult.data.title || 'Без названия', date: matterResult.data.date || new Date().toISOString().split('T')[0], tags: matterResult.data.tags || [], description: matterResult.data.description || '', }; } catch (error) { console.error(`Ошибка при загрузке поста ${slug}:`, error); return null; } }