blog/libs/post/post.ts

66 lines
1.6 KiB
TypeScript

import { join } from "$std/path/mod.ts";
import { exists } from "$std/fs/mod.ts";
import { extract } from "$std/front_matter/yaml.ts";
const POSTS_PATH = "./posts";
export interface Post {
slug: string; // This is the file name without the extension
title: string;
publishedAt: Date;
markdown: string;
}
function validateFrontMatter(attrs: unknown): attrs is {
title: string;
published_at: string;
snippet: string;
} {
if (typeof attrs !== "object" || attrs === null) {
return false;
}
// This guard is here to help typescript.
// I trust myself to write the front matter correctly :)
return true;
}
export async function getPost(slug: string): Promise<Post | null> {
const path = join(POSTS_PATH, `${slug}.md`);
if (!await exists(path)) {
return null;
}
const text = await Deno.readTextFile(path);
const { attrs, body } = extract(text);
if (!validateFrontMatter(attrs)) {
throw new Error("Invalid front matter");
}
return {
slug,
title: attrs.title,
publishedAt: new Date(attrs.published_at),
markdown: body,
};
}
export async function listPosts(): Promise<Post[]> {
const files = Deno.readDir(POSTS_PATH);
const slugs: string[] = [];
for await (const file of files) {
const slug = file.name.replace(".md", "");
slugs.push(slug);
}
const posts = await Promise.all(
slugs.map(getPost as (slug: string) => Promise<Post> // We know that the slug exists since they come from the file system
),
);
posts.sort((a, b) => b.publishedAt.getTime() - a.publishedAt.getTime());
return posts;
}