add markdownParser.js
This commit is contained in:
parent
c286a2f9c2
commit
1efee5a27c
1 changed files with 110 additions and 0 deletions
110
server/utils/markdownParser.js
Normal file
110
server/utils/markdownParser.js
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const path = require('path');
|
||||||
|
const matter = require('gray-matter');
|
||||||
|
const marked = require('marked');
|
||||||
|
|
||||||
|
marked.setOptions({
|
||||||
|
breaks: true,
|
||||||
|
gfm: true
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} filePath - Path to markdown file
|
||||||
|
* @returns {Object} Parsed data with frontmatter and HTML content
|
||||||
|
*/
|
||||||
|
const parseMarkdownFile = async (filePath) => {
|
||||||
|
try {
|
||||||
|
const fileContent = await fs.readFile(filePath, 'utf-8');
|
||||||
|
const { data, content } = matter(fileContent);
|
||||||
|
const html = marked.parse(content);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
content: html,
|
||||||
|
excerpt: generateExcerpt(content),
|
||||||
|
slug: path.basename(filePath, '.md'),
|
||||||
|
date: data.date || new Date().toISOString()
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error parsing mardown file ${filePath}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate an excerpt from markdown content
|
||||||
|
* @param {string} content - Markdown content
|
||||||
|
* @param {number} length - Length of excerpt in characters
|
||||||
|
* @returns {string} Plain text excerpt
|
||||||
|
*/
|
||||||
|
const generateExcerpt = (content, length = 200) => {
|
||||||
|
// Convert markdown to plain text for excerpt
|
||||||
|
const plainText = content
|
||||||
|
.replace(/#+\s+(.*)/g, '$1') // Remove heading markers
|
||||||
|
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Extract link text
|
||||||
|
.replace(/\*\*([^*]+)\*\*/g, '$1') // Remove bold markers
|
||||||
|
.replace(/\*([^*]+)\*/g, '$1') // Remove italic markers
|
||||||
|
.replace(/`([^`]+)`/g, '$1') // Remove code markers
|
||||||
|
.replace(/```[\s\S]+?```/g, '') // Remove code blocks
|
||||||
|
.replace(/\n/g, ' ') // Replace newlines with spaces
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
return plainText.length > length
|
||||||
|
? plainText.substring(0, length) + '...'
|
||||||
|
: plainText;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all blog posts, optionally filtered by draft status
|
||||||
|
* @param {boolean} includeDrafts - Whether to include draft posts
|
||||||
|
* @returns {Array} Array of parsed blog posts
|
||||||
|
*/
|
||||||
|
const getAllPosts = async (includeDrafts = false) => {
|
||||||
|
try {
|
||||||
|
const postsDir = path.join(__dirname, '../content/posts');
|
||||||
|
const files = await fs.readdir(postsDir);
|
||||||
|
|
||||||
|
const posts = await Promise.all(
|
||||||
|
files
|
||||||
|
.filter(file => file.endsWith('.md'))
|
||||||
|
.map(async (file) => {
|
||||||
|
const filePath = path.join(postsDir, file);
|
||||||
|
return await parseMarkdownFile(filePath);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filter out drafts if needed
|
||||||
|
const filteredPosts = includeDrafts
|
||||||
|
? posts
|
||||||
|
: posts.filter(post => !post.draft);
|
||||||
|
|
||||||
|
// Sort by date, newest first
|
||||||
|
return filteredPosts.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error getting all posts:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single blog post by slug
|
||||||
|
* @param {string} slug - Post slug
|
||||||
|
* @returns {Object} Parsed blog post
|
||||||
|
*/
|
||||||
|
const getPostBySlug = async (slug) => {
|
||||||
|
try {
|
||||||
|
const postsDir = path.join(__dirname, '../content/posts');
|
||||||
|
const filePath = path.join(postsDir, `${slug}.md`);
|
||||||
|
|
||||||
|
return await parseMarkdownFile(filePath);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error getting post with slug ${slug}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
parseMarkdownFile,
|
||||||
|
getAllPosts,
|
||||||
|
getPostBySlug
|
||||||
|
};
|
Loading…
Reference in a new issue