77 lines
2.4 KiB
TypeScript
77 lines
2.4 KiB
TypeScript
import { format } from "date-fns";
|
|
import { marked } from "marked";
|
|
|
|
export type NewsArticle = {
|
|
title: string;
|
|
date: string;
|
|
summary: string;
|
|
image?: string;
|
|
slug: string;
|
|
body: string;
|
|
html: string;
|
|
};
|
|
|
|
type Frontmatter = Partial<Pick<NewsArticle, "title" | "date" | "summary" | "image" | "slug">>;
|
|
|
|
const newsModules = import.meta.glob("/src/content/news/*.md", {
|
|
eager: true,
|
|
as: "raw",
|
|
});
|
|
|
|
const parseFrontmatter = (raw: string): { frontmatter: Frontmatter; body: string } => {
|
|
const frontmatterMatch = raw.match(/^---\s*([\s\S]*?)\s*---\s*([\s\S]*)$/);
|
|
if (!frontmatterMatch) {
|
|
return { frontmatter: {}, body: raw.trim() };
|
|
}
|
|
|
|
const [, frontmatterBlock, body] = frontmatterMatch;
|
|
const frontmatter: Frontmatter = {};
|
|
|
|
frontmatterBlock.split("\n").forEach((line) => {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith("#")) return;
|
|
const [key, ...rest] = trimmed.split(":");
|
|
if (!key || rest.length === 0) return;
|
|
const value = rest.join(":").trim().replace(/^["']|["']$/g, "");
|
|
if (key in frontmatter) return;
|
|
(frontmatter as Record<string, string>)[key.trim()] = value;
|
|
});
|
|
|
|
return { frontmatter, body: body.trim() };
|
|
};
|
|
|
|
export const newsArticles: NewsArticle[] = Object.entries(newsModules)
|
|
.map(([path, raw]) => {
|
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
const slug =
|
|
frontmatter.slug ||
|
|
path.split("/").pop()?.replace(/\.md$/, "") ||
|
|
"";
|
|
|
|
return {
|
|
title: frontmatter.title || slug,
|
|
date: frontmatter.date || "",
|
|
summary: frontmatter.summary || "",
|
|
image: frontmatter.image,
|
|
slug,
|
|
body,
|
|
html: marked.parse(body),
|
|
};
|
|
})
|
|
.sort((a, b) => {
|
|
const aDate = Date.parse(a.date);
|
|
const bDate = Date.parse(b.date);
|
|
if (Number.isNaN(aDate) || Number.isNaN(bDate)) {
|
|
return b.title.localeCompare(a.title);
|
|
}
|
|
return bDate - aDate;
|
|
});
|
|
|
|
export const getNewsArticle = (slug: string): NewsArticle | undefined =>
|
|
newsArticles.find((article) => article.slug === slug);
|
|
|
|
export const formatNewsDate = (date: string): string => {
|
|
const parsed = Date.parse(date);
|
|
if (Number.isNaN(parsed)) return date;
|
|
return format(new Date(parsed), "MMM yyyy");
|
|
};
|