96 lines
3.0 KiB
TypeScript
96 lines
3.0 KiB
TypeScript
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<PostData, 'contentHtml'>[] {
|
||
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<PostData | null> {
|
||
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;
|
||
}
|
||
}
|