Skip to content

Commit d8b03ad

Browse files
committed
blog
1 parent a6f31e4 commit d8b03ad

29 files changed

+4497
-327
lines changed

components/alert.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import styles from './alert.module.css';
2+
import { clsx } from 'clsx';
3+
4+
export default function Alert({ children, type }) {
5+
return (
6+
<div
7+
className={clsx({
8+
[styles.success]: type === 'success',
9+
[styles.error]: type === 'error',
10+
})}
11+
>
12+
{children}
13+
</div>
14+
);
15+
}

components/alert.module.css

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.success {
2+
color: green;
3+
}
4+
.error {
5+
color: red;
6+
}

components/date.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { parseISO, format } from 'date-fns';
2+
3+
export default function Date({ dateString }) {
4+
const date = parseISO(dateString);
5+
return <time dateTime={dateString}>{format(date, 'LLLL d, yyyy')}</time>;
6+
}

components/layout.js

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import Head from 'next/head';
2+
import Image from 'next/image';
3+
import styles from './layout.module.css';
4+
import utilStyles from '../styles/utils.module.css';
5+
import Link from 'next/link';
6+
7+
const name = 'Real';
8+
export const siteTitle = 'Next.js Sample Website';
9+
10+
export default function Layout({ children, home }) {
11+
return (
12+
<div className={styles.container}>
13+
<Head>
14+
<link rel="icon" href="/favicon.ico" />
15+
<meta
16+
name="description"
17+
content="Learn how to build a personal website using Next.js"
18+
/>
19+
<meta
20+
property="og:image"
21+
content={`https://og-image.vercel.app/${encodeURI(
22+
siteTitle
23+
)}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
24+
/>
25+
<meta name="og:title" content={siteTitle} />
26+
<meta name="twitter:card" content="summary_large_image" />
27+
</Head>
28+
<header className={styles.header}>
29+
{home ? (
30+
<>
31+
<Image
32+
priority
33+
src="/images/profile.jpg"
34+
className={utilStyles.borderCircle}
35+
height={144}
36+
width={144}
37+
alt=""
38+
/>
39+
<Link href="/posts/first-post">
40+
<h1 className={utilStyles.heading2Xl}>{name}</h1>
41+
</Link>
42+
</>
43+
) : (
44+
<>
45+
<Link href="/">
46+
<Image
47+
priority
48+
src="/images/profile.jpg"
49+
className={utilStyles.borderCircle}
50+
height={108}
51+
width={108}
52+
alt=""
53+
/>
54+
</Link>
55+
<h2 className={utilStyles.headingLg}>
56+
<Link href="/" className={utilStyles.colorInherit}>
57+
{name}
58+
</Link>
59+
</h2>
60+
</>
61+
)}
62+
</header>
63+
<main>{children}</main>
64+
{!home && (
65+
<div className={styles.backToHome}>
66+
<Link href="/">← Back to home</Link>
67+
</div>
68+
)}
69+
</div>
70+
);
71+
}

components/layout.module.css

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.container {
2+
max-width: 36rem;
3+
padding: 0 1rem;
4+
margin: 3rem auto 6rem;
5+
}
6+
7+
.header {
8+
display: flex;
9+
flex-direction: column;
10+
align-items: center;
11+
}
12+
13+
.backToHome {
14+
margin: 3rem 0 0;
15+
}

lib/posts.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import matter from 'gray-matter';
4+
import { remark } from 'remark';
5+
import html from 'remark-html';
6+
7+
const postsDirectory = path.join(process.cwd(), 'posts');
8+
9+
export function getSortedPostsData() {
10+
// Get file names under /posts
11+
const fileNames = fs.readdirSync(postsDirectory);
12+
const allPostsData = fileNames.map((fileName) => {
13+
// Remove ".md" from file name to get id
14+
const id = fileName.replace(/\.md$/, '');
15+
16+
// Read markdown file as string
17+
const fullPath = path.join(postsDirectory, fileName);
18+
const fileContents = fs.readFileSync(fullPath, 'utf8');
19+
20+
// Use gray-matter to parse the post metadata section
21+
const matterResult = matter(fileContents);
22+
23+
// Combine the data with the id
24+
return {
25+
id,
26+
...matterResult.data,
27+
};
28+
});
29+
// Sort posts by date
30+
return allPostsData.sort((a, b) => {
31+
if (a.date < b.date) {
32+
return 1;
33+
} else {
34+
return -1;
35+
}
36+
});
37+
}
38+
39+
export function getAllPostIds() {
40+
const fileNames = fs.readdirSync(postsDirectory);
41+
42+
// Returns an array that looks like this:
43+
// [
44+
// {
45+
// params: {
46+
// id: 'ssg-ssr'
47+
// }
48+
// },
49+
// {
50+
// params: {
51+
// id: 'pre-rendering'
52+
// }
53+
// }
54+
// ]
55+
return fileNames.map((fileName) => {
56+
return {
57+
params: {
58+
id: ['prefix', fileName.replace(/\.md$/, '')],
59+
},
60+
};
61+
});
62+
}
63+
64+
export async function getPostData(id) {
65+
const fullPath = path.join(postsDirectory, `${id[1]}.md`);
66+
67+
const fileContents = fs.readFileSync(fullPath, 'utf8');
68+
69+
// Use gray-matter to parse the post metadata section
70+
const matterResult = matter(fileContents);
71+
72+
// Use remark to convert markdown into HTML string
73+
const processedContent = await remark().use(html).process(matterResult.content);
74+
const contentHtml = processedContent.toString();
75+
76+
// Combine the data with the id and contentHtml
77+
return {
78+
id,
79+
contentHtml,
80+
...matterResult.data,
81+
};
82+
}

next-env.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/basic-features/typescript for more information.

next.config.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const path = require('path');
2+
3+
module.exports = {
4+
sassOptions: {
5+
includePaths: [path.join(__dirname, 'styles')],
6+
},
7+
};

0 commit comments

Comments
 (0)