Some checks failed
Deploy Website / build-and-deploy (push) Has been cancelled
87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import matter from 'gray-matter';
|
|
|
|
const postsDirectory = path.join(process.cwd(), 'content/posts');
|
|
|
|
export type PostMetadata = {
|
|
title: string;
|
|
date: string;
|
|
description: string;
|
|
slug: string;
|
|
tags?: string[];
|
|
};
|
|
|
|
export type Post = {
|
|
metadata: PostMetadata;
|
|
content: string;
|
|
};
|
|
|
|
function readPostMetadata(data: Record<string, unknown>, slug: string): PostMetadata {
|
|
if (
|
|
typeof data.title !== 'string' ||
|
|
typeof data.date !== 'string' ||
|
|
typeof data.description !== 'string'
|
|
) {
|
|
throw new Error(`Invalid frontmatter for post: ${slug}`);
|
|
}
|
|
|
|
if (!Number.isFinite(Date.parse(data.date))) {
|
|
throw new Error(`Invalid date for post: ${slug}`);
|
|
}
|
|
|
|
return {
|
|
title: data.title,
|
|
date: data.date,
|
|
description: data.description,
|
|
slug,
|
|
tags: Array.isArray(data.tags)
|
|
? data.tags.filter((tag): tag is string => typeof tag === 'string')
|
|
: undefined,
|
|
};
|
|
}
|
|
|
|
export function getPostSlugs() {
|
|
if (!fs.existsSync(postsDirectory)) {
|
|
return [];
|
|
}
|
|
return fs
|
|
.readdirSync(postsDirectory, { withFileTypes: true })
|
|
.filter((entry) => entry.isFile())
|
|
.map((entry) => entry.name);
|
|
}
|
|
|
|
export function getPostBySlug(slug: string): Post {
|
|
const realSlug = slug.replace(/\.mdx$/, '');
|
|
|
|
if (/[\/\\]|\.\./.test(realSlug)) {
|
|
throw new Error(`Invalid slug: ${realSlug}`);
|
|
}
|
|
|
|
const fullPath = path.join(postsDirectory, `${realSlug}.mdx`);
|
|
|
|
let fileContents: string;
|
|
try {
|
|
fileContents = fs.readFileSync(fullPath, 'utf8');
|
|
} catch {
|
|
throw new Error(`Post not found: ${realSlug}`);
|
|
}
|
|
|
|
const { data, content } = matter(fileContents);
|
|
|
|
return {
|
|
metadata: readPostMetadata(data, realSlug),
|
|
content,
|
|
};
|
|
}
|
|
|
|
export function getAllPosts(): PostMetadata[] {
|
|
const slugs = getPostSlugs();
|
|
const posts = slugs
|
|
.filter((slug) => slug.endsWith('.mdx'))
|
|
.map((slug) => getPostBySlug(slug).metadata)
|
|
// Sort posts by date in descending order
|
|
.sort((post1, post2) => new Date(post2.date).getTime() - new Date(post1.date).getTime());
|
|
return posts;
|
|
}
|