diff --git a/package.json b/package.json index 9b42c8a..00059e5 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "dev": "next dev -p 3003", "build": "next build", "start": "next start -p 3003", - "lint": "eslint src --max-warnings 0" + "lint": "eslint src --max-warnings 0", + "test": "echo \"No tests yet!\" && exit 0" }, "dependencies": { "gray-matter": "4.0.3", diff --git a/src/app/page.tsx b/src/app/page.tsx index b86920b..4457db5 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -4,23 +4,30 @@ * @author Naomi Carrigan */ +import type { JSX } from "react"; import { Rule } from "@/components/rule"; import { getSortedPostsData } from "@/lib/posts"; -export default function Home() { +/** + * Renders the home page. + * @returns JSX component. + */ +const Home = (): JSX.Element => { const posts = getSortedPostsData(); return (

{"Blog"}

{"Welcome to the musings of a transfem software engineer!"}

- {posts.map((post) => { - return
- -

{post.data.title}

-

{post.data.date.toLocaleDateString("en-GB",{ year: "numeric", month: "long", day: "numeric"})}

-

{post.data.summary}

-
; - })} + {posts.map((post) => { + return
+ +

{post.data.title}

+

{post.data.date.toLocaleDateString("en-GB", { day: "numeric", month: "long", year: "numeric" })}

+

{post.data.summary}

+
; + })}
); -} +}; + +export default Home; diff --git a/src/app/post/[slug]/page.tsx b/src/app/post/[slug]/page.tsx index 562b7d5..4b79230 100644 --- a/src/app/post/[slug]/page.tsx +++ b/src/app/post/[slug]/page.tsx @@ -1,23 +1,38 @@ -import { Rule } from "@/components/rule"; -import { getPostData } from "@/lib/posts"; +/** + * @copyright nhcarrigan + * @license Naomi's Public License + * @author Naomi Carrigan + */ + import Markdown from "react-markdown"; import remarkGfm from "remark-gfm"; +import type { JSX } from "react"; +import { Rule } from "@/components/rule"; +import { getPostData } from "@/lib/posts"; -export default async function Page({ - params +/** + * Renders a blog post. + * @param props - The properties of the page. + * @param props.params - The path parameters. + * @returns The JSX component. + */ +const Page = async({ + params, }: { - params: Promise<{slug: string}> -}) { - const { slug } = await params; - const post = getPostData(slug); - return ( -
-

{post.data.title}

-

{`Published ${post.data.date.toLocaleDateString("en-GB", { weekday: "long", year: "numeric", month: "long", day: "numeric"})}`}

- - {post.content} - - {"← Back to home"} -
- ) -} \ No newline at end of file + readonly params: Promise<{ slug: string }>; +}): Promise => { + const { slug } = await params; + const post = getPostData(slug); + return ( +
+

{post.data.title}

+

{`Published ${post.data.date.toLocaleDateString("en-GB", { day: "numeric", month: "long", weekday: "long", year: "numeric" })}`}

+ + {post.content} + + {"← Back to home"} +
+ ); +}; + +export default Page; diff --git a/src/lib/posts.ts b/src/lib/posts.ts index 78eed5d..57726b3 100644 --- a/src/lib/posts.ts +++ b/src/lib/posts.ts @@ -10,49 +10,71 @@ import matter from "gray-matter"; const postsDirectory = join(process.cwd(), "posts"); -export const getSortedPostsData = () => { +/** + * Reads the posts directory and returns an array of post data. + * @returns An array of parsed frontmatter and file contents. + */ +const getSortedPostsData = (): Array<{ + content: string; + data: { date: Date; summary: string; title: string }; + slug: string; +}> => { const fileNames = readdirSync(postsDirectory); const allPostsData = fileNames.map((fileName) => { const slug = fileName.replace(/\.md$/, ""); const fullPath = join(postsDirectory, fileName); const fileContents = readFileSync(fullPath, "utf8"); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- matter doesn't take a generic I guess? const matterResult = matter(fileContents) as unknown as { content: string; - data: { title: string; date: string; summary: string }; + data: { title: string; date: string; summary: string }; }; return { - slug: slug, - data: { - title: matterResult.data.title, - date: new Date(matterResult.data.date), - summary: matterResult.data.summary, - }, content: matterResult.content, + data: { + date: new Date(matterResult.data.date), + summary: matterResult.data.summary, + title: matterResult.data.title, + }, + slug: slug, }; }); return allPostsData.sort((a, b) => { if (a.data.date < b.data.date) { return 1; - } else { - return -1; } + return -1; }); }; -export const getPostData = (slug: string) => { - const fullPath = join(postsDirectory, slug + ".md"); - const fileContents = readFileSync(fullPath, "utf8"); - const matterResult = matter(fileContents) as unknown as { - content: string; - data: { title: string; date: string; summary: string }; - }; - return { - slug: slug, - data: { - title: matterResult.data.title, - date: new Date(matterResult.data.date), - summary: matterResult.data.summary, - }, - content: matterResult.content, - }; -} +/** + * Reads a single post file. + * @param slug - The slug of the post to read. + * @returns The parsed frontmatter and file contents. + */ +const getPostData = ( + slug: string, +): { + content: string; + data: { date: Date; summary: string; title: string }; + slug: string; +} => { + const fullPath = join(postsDirectory, `${slug}.md`); + const fileContents = readFileSync(fullPath, "utf8"); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- matter doesn't take a generic I guess? + const matterResult = matter(fileContents) as unknown as { + content: string; + data: { title: string; date: string; summary: string }; + }; + return { + content: matterResult.content, + data: { + date: new Date(matterResult.data.date), + summary: matterResult.data.summary, + title: matterResult.data.title, + }, + slug: slug, + }; +}; + +export { getSortedPostsData, getPostData };