|
|
|
import fs from "fs/promises";
|
|
|
|
import path from "path";
|
|
|
|
|
|
|
|
import matter from "gray-matter";
|
|
|
|
import { MDXRemoteSerializeResult } from "next-mdx-remote";
|
|
|
|
import { serialize } from "next-mdx-remote/serialize";
|
|
|
|
|
|
|
|
const EVENTS_PATH = path.join("content", "events");
|
|
|
|
const TERMS = ["winter", "spring", "fall"];
|
|
|
|
|
|
|
|
export async function getEventYears(): Promise<string[]> {
|
|
|
|
return (await fs.readdir(EVENTS_PATH, { withFileTypes: true }))
|
|
|
|
.filter((dirent) => dirent.isDirectory())
|
|
|
|
.map((dirent) => dirent.name)
|
|
|
|
.sort();
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function getEventTermsByYear(year: string): Promise<string[]> {
|
|
|
|
return (
|
|
|
|
await fs.readdir(path.join(EVENTS_PATH, year), { withFileTypes: true })
|
|
|
|
)
|
|
|
|
.filter((dirent) => dirent.isDirectory() && TERMS.includes(dirent.name))
|
|
|
|
.map((dirent) => dirent.name)
|
|
|
|
.sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
interface Metadata {
|
|
|
|
name: string;
|
|
|
|
short: string;
|
|
|
|
date: string;
|
|
|
|
online: boolean;
|
|
|
|
location: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface Event {
|
|
|
|
content: MDXRemoteSerializeResult<Record<string, unknown>>;
|
|
|
|
metadata: Metadata;
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function getEventBySlug(
|
|
|
|
year: string,
|
|
|
|
term: string,
|
|
|
|
slug: string
|
|
|
|
): Promise<Event> {
|
|
|
|
const raw = await fs.readFile(
|
|
|
|
path.join(EVENTS_PATH, year, term, `${slug}.md`),
|
|
|
|
"utf-8"
|
|
|
|
);
|
|
|
|
const { content, data: metadata } = matter(raw);
|
|
|
|
|
|
|
|
return {
|
|
|
|
content: await serialize(content),
|
|
|
|
metadata: metadata as Metadata,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function getEventsByTerm(
|
|
|
|
year: string,
|
|
|
|
term: string
|
|
|
|
): Promise<string[]> {
|
|
|
|
return (await fs.readdir(path.join(EVENTS_PATH, year, term)))
|
|
|
|
.filter((name) => name.endsWith(".md"))
|
|
|
|
.map((name) => name.slice(0, -".md".length));
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function getUpcomingEvents(): Promise<Event[]> {
|
|
|
|
const today = new Date();
|
|
|
|
const currentYear = today.getFullYear();
|
|
|
|
const currentTerm = Math.trunc(today.getMonth() / 4);
|
|
|
|
const nextYear = currentTerm < 2 ? currentYear : currentYear + 1;
|
|
|
|
const nextTerm = (currentTerm + 1) % 3;
|
|
|
|
|
|
|
|
const events: Event[] = (
|
|
|
|
await Promise.all(
|
|
|
|
[
|
|
|
|
{ year: currentYear.toString(), term: currentTerm },
|
|
|
|
{ year: nextYear.toString(), term: nextTerm },
|
|
|
|
].map(async ({ year, term }) => {
|
|
|
|
try {
|
|
|
|
const eventsInTerm = await getEventsByTerm(year, TERMS[term]);
|
|
|
|
return await Promise.all(
|
|
|
|
eventsInTerm.map((slug) => getEventBySlug(year, TERMS[term], slug))
|
|
|
|
);
|
|
|
|
} catch (error) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
})
|
|
|
|
)
|
|
|
|
).flat();
|
|
|
|
|
|
|
|
return events
|
|
|
|
.filter((ev) => new Date(ev.metadata.date).getTime() >= Date.now())
|
|
|
|
.sort((a, b) => {
|
|
|
|
return (
|
|
|
|
new Date(a.metadata.date).getTime() -
|
|
|
|
new Date(b.metadata.date).getTime()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|