posts.ts
import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";
const POSTS_DIR = path.join(process.cwd(), "posts");
export type PostMeta = {
slug: string;
title: string;
description: string;
date: string;
};
export function getPostSlugs(): string[] {
if (!fs.existsSync(POSTS_DIR)) return [];
return fs
.readdirSync(POSTS_DIR)
.filter((file) => /\.mdx?$/.test(file))
.map((file) => file.replace(/\.mdx?$/, ""));
}
export function getPostMeta(slug: string): PostMeta | null {
const mdxPath = path.join(POSTS_DIR, `${slug}.mdx`);
const mdPath = path.join(POSTS_DIR, `${slug}.md`);
const filePath = fs.existsSync(mdxPath) ? mdxPath : mdPath;
if (!fs.existsSync(filePath)) return null;
const raw = fs.readFileSync(filePath, "utf8");
const { data } = matter(raw);
return {
slug,
title: String(data.title ?? slug),
description: String(data.description ?? ""),
date: data.date ? String(data.date) : "",
};
}
export function getAllPostsMeta(): PostMeta[] {
return getPostSlugs()
.map((slug) => getPostMeta(slug))
.filter((meta): meta is PostMeta => meta !== null)
.sort((a, b) => (a.date < b.date ? 1 : -1));
}
export function formatDate(value: string): string {
if (!value) return "";
if (/^[A-Za-z]+ \d{4}$/.test(value)) return value;
const date = new Date(value.length <= 10 ? `${value}T00:00:00` : value);
if (Number.isNaN(date.getTime())) return value;
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
}