66 lines
1.6 KiB
TypeScript
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;
|
|
}
|