Skip to content

Commit 57ba6c9

Browse files
committed
comments
1 parent 1862f2f commit 57ba6c9

File tree

10 files changed

+94
-56
lines changed

10 files changed

+94
-56
lines changed

app/(main)/blog/[slug]/page.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import { join } from "path"
33

44
export const dynamicParams = false
55

6+
// runs at build time, generates a list of all possible dynamic routes, REQUIRED for static export
67
export function generateStaticParams() {
8+
// list all blog post files
79
const posts = readdirSync(join(process.cwd(), "/blog-posts"))
8-
10+
11+
// generate slugs from filename by stripping extension
912
const slugs = posts.map((post) => ({
1013
slug: post.split(".")[0]
1114
}))
@@ -14,8 +17,12 @@ export function generateStaticParams() {
1417
}
1518

1619
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
20+
// get url segment
1721
const { slug } = await params
22+
23+
// dynamically import post content based on slug as react component
1824
const { default: Post } = await import(`@/blog-posts/${slug}.mdx`)
19-
25+
26+
// render content as react component
2027
return <Post />
2128
}

app/(main)/blog/client-page.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import Link from "next/link";
55
import styles from "@/styles/blog.module.css"
66
import { useState } from "react";
77

8+
9+
// component to display information about a single post
810
const Post = ({ data }: { data: PostMeta & { slug: string } }) => (
911
<div className={styles.post}>
1012
<div style={{ width: "50%" }}>
@@ -24,13 +26,17 @@ const Post = ({ data }: { data: PostMeta & { slug: string } }) => (
2426
)
2527

2628
const ClientPage = ({ posts }: { posts: (PostMeta & { slug: string })[] }) => {
27-
29+
30+
// stateful search query var
2831
const [query, setQuery] = useState("")
29-
32+
33+
// post data from server component
3034
let results = posts
31-
35+
36+
// filter posts if a search query is present
3237
if (query && query.trim().length > 0) {
3338
const lcQuery = query.toLowerCase();
39+
// check for substring matches in post titles and in post tags
3440
results = posts.filter((post) => post.title.toLowerCase().includes(lcQuery) || post.tags.some(tag => tag.toLowerCase().includes(lcQuery)));
3541
}
3642

app/(main)/blog/page.tsx

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,17 @@
1-
import { readdirSync } from "fs";
2-
import { join } from "path";
3-
import { PostMeta } from "./types";
41
import ClientPage from "./client-page";
2+
import {indexBlogPosts} from "@/lib/blog";
53

4+
// server component, executed at build time to use filesystem APIs in indexBlogPosts()
65
const Page = async () => {
7-
8-
const files = readdirSync(join(process.cwd(), "/blog-posts"))
9-
10-
const posts = await Promise.all(files.map(async (file) => {
11-
const { meta } = await import(`@/blog-posts/${file}`) as { meta: PostMeta }
12-
13-
return {...meta, slug: file.split(".")[0]}
14-
}))
15-
16-
const sortedPosts = posts.sort((a, b) => {
17-
const dateA = new Date(a.published).getTime()
18-
const dateB = new Date(b.published).getTime()
19-
20-
return dateB - dateA
21-
})
6+
7+
// get blog posts
8+
const posts = await indexBlogPosts()
229

2310
return (
2411
<main>
2512
<h1>Palouse RoboSub Blog</h1>
26-
<ClientPage posts={sortedPosts} />
13+
{/* pass post data to client component */}
14+
<ClientPage posts={posts} />
2715
</main>
2816
)
2917
}

app/(main)/blog/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// required metadata for all blog posts in order to index correctly
12
export type PostMeta = {
23
title: string,
34
author: string,

app/(main)/layout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import "./main.css";
44
import Header from "@/components/header";
55
import Footer from "@/components/footer";
66

7+
// preload fonts
78
const montserrat = Montserrat({
89
subsets: ["latin"],
910
weight: "variable"
@@ -15,6 +16,8 @@ const jetBrainsMono = JetBrains_Mono({
1516
variable: "--font-jetbrains"
1617
})
1718

19+
20+
// html head data
1821
export const metadata: Metadata = {
1922
title: "Palouse RoboSub",
2023
description: "The official Palouse RoboSub team website, run by WSU",

app/(main)/main.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* prism.js syntax highlight stylesheet */
12
@import "prismjs/themes/prism-tomorrow.css";
23

34
* {
@@ -44,6 +45,7 @@ h2 {
4445
position: relative;
4546
}
4647

48+
/* header accent mark */
4749
h2::before {
4850
content: "";
4951
height: 6px;
@@ -57,6 +59,7 @@ h3 {
5759
position: relative;
5860
}
5961

62+
/* header accent mark */
6063
h3::before {
6164
content: "";
6265
height: 4px;
@@ -72,6 +75,7 @@ a {
7275
text-decoration: none;
7376
}
7477

78+
/* underline animation for links */
7579
a::before {
7680
content: "";
7781
position: absolute;
@@ -112,6 +116,7 @@ tr:nth-child(even) {
112116
background-color: #ffffff;
113117
}
114118

119+
/* override default monospace font in code blocks */
115120
code, pre {
116121
font-family: "JetBrains Mono" !important;
117122
}

app/(main)/page.tsx

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,12 @@ import Image from "next/image";
22
import styles from "@/styles/home.module.css"
33
import Link from "next/link";
44
import { ArrowRight } from "lucide-react";
5-
import { readdirSync } from "fs";
6-
import { join } from "path";
7-
import { PostMeta } from "./blog/types";
5+
import {indexBlogPosts} from "@/lib/blog";
86

97
const Page = async () => {
108

11-
const files = readdirSync(join(process.cwd(), "/blog-posts"))
12-
13-
const posts = await Promise.all(files.map(async (file) => {
14-
const { meta } = await import(`@/blog-posts/${file}`) as { meta: PostMeta }
15-
16-
return {...meta, slug: file.split(".")[0]}
17-
}))
18-
19-
const sortedPosts = posts.sort((a, b) => {
20-
const dateA = new Date(a.published).getTime()
21-
const dateB = new Date(b.published).getTime()
22-
23-
return dateB - dateA
24-
})
9+
// get blog posts
10+
const posts = await indexBlogPosts()
2511

2612
return (
2713
<main className={styles.main}>
@@ -50,18 +36,19 @@ const Page = async () => {
5036
<Link href="/blog" className={styles.link}>Check the blog out here <ArrowRight/></Link>
5137
</div>
5238
<div>
39+
{/* show 3 newest blog posts */}
5340
<h4 style={{ margin: "0" }}>Recent Posts</h4>
5441
<div className={styles.post}>
55-
<Link href={`/blog/${sortedPosts[0].slug}`}>{sortedPosts[0].title}</Link>
56-
<div>{sortedPosts[0].published}</div>
42+
<Link href={`/blog/${posts[0].slug}`}>{posts[0].title}</Link>
43+
<div>{posts[0].published}</div>
5744
</div>
5845
<div className={styles.post}>
59-
<Link href={`/blog/${sortedPosts[1].slug}`}>{sortedPosts[1].title}</Link>
60-
<div>{sortedPosts[1].published}</div>
46+
<Link href={`/blog/${posts[1].slug}`}>{posts[1].title}</Link>
47+
<div>{posts[1].published}</div>
6148
</div>
6249
{/*<div className={styles.post}>
63-
<Link href={`/blog/${sortedPosts[2].slug}`}>{sortedPosts[2].title}</Link>
64-
<div>{sortedPosts[0].published}</div>
50+
<Link href={`/blog/${posts[2].slug}`}>{posts[2].title}</Link>
51+
<div>{posts[0].published}</div>
6552
</div>*/}
6653
</div>
6754
</div>

lib/blog.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {PostMeta} from "@/app/(main)/blog/types";
2+
import {readdirSync} from "fs";
3+
import {join} from "path";
4+
5+
const indexBlogPosts = async () => {
6+
// get all post files
7+
const files = readdirSync(join(process.cwd(), "/blog-posts"))
8+
9+
// collect metadata from all posts
10+
const posts = await Promise.all(files.map(async (file) => {
11+
const { meta } = await import(`@/blog-posts/${file}`) as { meta: PostMeta }
12+
13+
return {...meta, slug: file.split(".")[0]}
14+
}))
15+
16+
// sort posts by date published
17+
const sortedPosts = posts.sort((a, b) => {
18+
const dateA = new Date(a.published).getTime()
19+
const dateB = new Date(b.published).getTime()
20+
21+
return dateB - dateA
22+
})
23+
24+
return sortedPosts;
25+
}
26+
27+
export {indexBlogPosts};

mdx-components.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import type { MDXComponents } from "mdx/types";
22

33
const components: MDXComponents = {
4+
// wrap the default export of an MDX file in <main>
45
wrapper: ({ children }) => <main>{children}</main>,
6+
// override input elements from MDX files
57
input: (props) => {
68
if (props.type === "checkbox" && props.disabled) {
9+
// add color to disabled checkboxes
710
return (
811
<input
912
type="checkbox"
@@ -12,6 +15,7 @@ const components: MDXComponents = {
1215
/>
1316
);
1417
}
18+
// leave all other input elements unaffected
1519
return <input {...props} />;
1620
},
1721
};

next.config.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,33 @@ import createMDX from "@next/mdx"
33

44
const nextConfig: NextConfig = {
55
images: {
6-
remotePatterns: [
7-
{
8-
protocol: "https",
9-
hostname: "picsum.photos",
10-
pathname: "**"
11-
}
12-
],
6+
// disable automatic image optimization, REQUIRED for static export
137
unoptimized: true
148
},
15-
pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'],
9+
10+
// add mdx to list of valid page extensions
11+
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
12+
13+
// builds generate static files
1614
output: "export",
1715
};
1816

1917
const withMDX = createMDX({
20-
extension: /\.(md|mdx)$/,
18+
extension: /\.(mdx)$/,
2119
options: {
20+
/*
21+
* remark-gfm enables github flavored markdown parsing
22+
*
23+
* remark-prism enables language-aware syntax highlighting in code blocks using prismjs
24+
*/
2225
remarkPlugins: ["remark-gfm", "remark-prism"],
26+
/*
27+
* rehype-slug automatically adds id attributes to headings
28+
*
29+
* rehype-autolink-headings automatically generates links to headings, which are currently
30+
* overwritten by my css. this will be fixed in the future
31+
*
32+
*/
2333
rehypePlugins: ["rehype-slug", "rehype-autolink-headings"]
2434
}
2535
})

0 commit comments

Comments
 (0)