www-new/lib/news.ts

142 lines
3.4 KiB
TypeScript

import fs from "fs/promises";
import path from "path";
import { parse } from "date-fns";
import matter from "gray-matter";
import truncateMarkdown from "markdown-truncate";
import { MDXRemoteSerializeResult } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";
import {
isTerm,
Term,
TERMS,
DATE_FORMAT,
getLocalDateFromEST,
TermYear,
getTermYear,
getCurrentTermYear,
} from "@/utils";
export const NEWS_PATH = path.join("content", "news");
export interface Metadata {
author: string;
date: string;
permalink: string;
}
export interface News {
content: MDXRemoteSerializeResult;
metadata: Metadata;
}
export async function getNewsYears(): Promise<string[]> {
return (await fs.readdir(NEWS_PATH, { withFileTypes: true }))
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)
.sort();
}
export async function getNewsTermsByYear(year: string): Promise<Term[]> {
return (
await fs.readdir(path.join(NEWS_PATH, year), {
withFileTypes: true,
})
)
.filter((dirent) => dirent.isDirectory() && isTerm(dirent.name))
.map((dirent) => dirent.name as Term)
.sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
}
export async function getNewsDateByTerm(
year: string,
term: Term
): Promise<string[]> {
return (await getNewsByTerm(year, term)).map(
(news) => news.split("-").slice(0, 3).join("-") // retrieves date from filename
);
}
export async function getNewsByTerm(
year: string,
term: Term
): Promise<string[]> {
return (
await fs.readdir(path.join(NEWS_PATH, year, term), {
withFileTypes: true,
})
)
.filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md"))
.map((dirent) => dirent.name.slice(0, -".md".length));
}
export async function getNewsBySlug(
year: string,
term: Term,
slug: string,
shortened = false
): Promise<News> {
const raw = await fs.readFile(
path.join(NEWS_PATH, year, term, `${slug}.md`),
"utf-8"
);
const { content: rawContent, data: metadata } = matter(raw);
const slugDate = slug.split("-").slice(0, 3).join("-");
const content: string = shortened
? truncateMarkdown(rawContent, {
limit: 150,
ellipsis: true,
})
: rawContent;
return {
content: await serialize(content),
metadata: {
...metadata,
date: getLocalDateFromEST(
parse(metadata.date as string, DATE_FORMAT, new Date())
).toString(),
permalink: `/news/${year}/${term}/${slugDate}`,
} as Metadata,
};
}
export async function getRecentNews(): Promise<News[]> {
const terms: TermYear[] = [];
// Get news for the last two terms
for (const termYear of getTermYear(getCurrentTermYear(), {
goBackwards: true,
})) {
if (terms.length >= 2) {
break;
}
terms.push(termYear);
}
const news: News[] = (
await Promise.all(
terms.map(async ({ year, term }) => {
try {
const newsInTerm = await getNewsByTerm(year.toString(), term);
return await Promise.all(
newsInTerm.map((slug) => {
return getNewsBySlug(year.toString(), term, slug, true);
})
);
} catch (error) {
return [];
}
})
)
).flat();
return news.sort((a, b) => {
return (
new Date(b.metadata.date).getTime() - new Date(a.metadata.date).getTime()
);
});
}