| | |
| | |
| | |
| | |
| |
|
| | import { readdir, readFile, stat } from "fs/promises"; |
| | import { join, dirname } from "path"; |
| | import { fileURLToPath } from "url"; |
| | import { parseMarkdown, extractFrontMatter } from "../utils/markdownParser.js"; |
| | import { siteConfig } from "../../shared/config.js"; |
| | import type { Post, PostSummary } from "../../shared/types.js"; |
| |
|
| | const __filename = fileURLToPath(import.meta.url); |
| | const __dirname = dirname(__filename); |
| |
|
| | const POSTS_DIRECTORY = join(__dirname, "../../../", siteConfig.files.postsDirectoryName); |
| |
|
| | const postsCache: Map<string, Post> = new Map(); |
| |
|
| | const isMarkdownFile = (filename: string): boolean => { |
| | return siteConfig.files.markdownExtensionPattern.test(filename); |
| | }; |
| |
|
| | const extractSlugFromFilename = (filename: string): string => { |
| | return filename.replace(siteConfig.files.markdownExtensionPattern, ""); |
| | }; |
| |
|
| | const compareDateDescending = (a: PostSummary, b: PostSummary): number => { |
| | return new Date(b.frontMatter.date).getTime() - new Date(a.frontMatter.date).getTime(); |
| | }; |
| |
|
| | const postToSummary = (post: Post): PostSummary => ({ |
| | slug: post.slug, |
| | frontMatter: post.frontMatter, |
| | lastModified: post.lastModified, |
| | }); |
| |
|
| | export const getPostsDirectory = (): string => { |
| | return POSTS_DIRECTORY; |
| | }; |
| |
|
| | export const initializePostService = async (): Promise<void> => { |
| | await loadAllPosts(); |
| | }; |
| |
|
| | export const loadAllPosts = async (): Promise<void> => { |
| | try { |
| | const files = await readdir(POSTS_DIRECTORY); |
| | const markdownFiles = files.filter(isMarkdownFile); |
| |
|
| | postsCache.clear(); |
| |
|
| | for (const file of markdownFiles) { |
| | await loadPost(file); |
| | } |
| |
|
| | console.log(siteConfig.logs.postsLoaded(postsCache.size)); |
| | } catch (error) { |
| | console.error(siteConfig.errors.loadingPosts(error)); |
| | } |
| | }; |
| |
|
| | export const loadPost = async (filename: string): Promise<void> => { |
| | try { |
| | const filePath = join(POSTS_DIRECTORY, filename); |
| | const [fileContent, fileStat] = await Promise.all([ |
| | readFile(filePath, "utf-8"), |
| | stat(filePath), |
| | ]); |
| |
|
| | const slug = extractSlugFromFilename(filename); |
| |
|
| | const post: Post = { |
| | slug, |
| | frontMatter: extractFrontMatter(fileContent), |
| | content: parseMarkdown(fileContent), |
| | rawContent: fileContent, |
| | lastModified: fileStat.mtimeMs, |
| | }; |
| |
|
| | postsCache.set(slug, post); |
| | } catch (error) { |
| | console.error(siteConfig.errors.loadingPost(filename, error)); |
| | } |
| | }; |
| |
|
| | export const removePost = (filename: string): void => { |
| | const slug = extractSlugFromFilename(filename); |
| | postsCache.delete(slug); |
| | console.log(siteConfig.logs.postRemoved(slug)); |
| | }; |
| |
|
| | export const getAllPosts = (): PostSummary[] => { |
| | return Array.from(postsCache.values()).map(postToSummary).sort(compareDateDescending); |
| | }; |
| |
|
| | export const getPostBySlug = (slug: string): Post | null => { |
| | return postsCache.get(slug) || null; |
| | }; |