diff --git a/src/app/docs/[...categories]/page.jsx b/src/app/docs/[...categories]/page.jsx
index c34d317..ec03894 100644
--- a/src/app/docs/[...categories]/page.jsx
+++ b/src/app/docs/[...categories]/page.jsx
@@ -3,7 +3,6 @@ import parse from 'html-react-parser';
import { REPOSITORY } from '@/constants/github';
import { DOCS } from '@/constants/path';
-import Article from '@/layouts/Article';
export async function generateStaticParams() {
const paths = await fs.readdir(DOCS, {
@@ -24,7 +23,7 @@ export default async function Page({ params }) {
'utf-8',
);
- return {await markdownToJsx(markdown)};
+ return <>{await markdownToJsx(markdown)}>;
}
async function markdownToHtml(markdown) {
diff --git a/src/app/layout.jsx b/src/app/layout.jsx
index 11dbdd1..d4ff0e2 100644
--- a/src/app/layout.jsx
+++ b/src/app/layout.jsx
@@ -1,17 +1,38 @@
import { SpeedInsights } from '@vercel/speed-insights/next';
-import Aside from '@/layouts/Aside';
-import Body from '@/layouts/Body';
-import Header from '@/layouts/Header';
-import Main from '@/layouts/Main';
+import Article from '@/components/layouts/Article';
+import Aside from '@/components/layouts/Aside';
+import Body from '@/components/layouts/Body';
+import Header from '@/components/layouts/Header';
+import Main from '@/components/layouts/Main';
-export default function Layout({ children }) {
+import Categories from '@/components/aside/Categories';
+import Home from '@/components/aside/Home';
+
+import Title from '@/components/header/Title';
+
+// TODO: import '@/styles/global.scss';
+
+// TODO: export const metadata = {
+// title: 'Next.js',
+// description: 'Generated by Next.js',
+// };
+
+export default function RootLayout({ children }) {
return (
-
-
- {children}
+
+
+
+ {children}
+
+
diff --git a/src/app/page.jsx b/src/app/page.jsx
index 19e4e06..e047726 100644
--- a/src/app/page.jsx
+++ b/src/app/page.jsx
@@ -1,7 +1,5 @@
import { USER } from '@/constants/github';
-import Article from '@/layouts/Article';
-
export default function Page() {
- return {`Hello, It's ${USER.name}'s blog!`};
+ return <>{`Hello, It's ${USER.name}'s blog!`}>;
}
diff --git a/src/components/aside/Categories/Categories.jsx b/src/components/aside/Categories/Categories.jsx
new file mode 100644
index 0000000..174ff6b
--- /dev/null
+++ b/src/components/aside/Categories/Categories.jsx
@@ -0,0 +1,37 @@
+import { join } from 'path';
+
+import Link from 'next/link';
+
+import { DOCS } from '@/constants/path';
+import { getDirTree } from '@/utils/dirTree';
+
+export default async function Categories() {
+ const dirTree = await getDirTree(DOCS);
+
+ return <>{renderDirTree(dirTree)}>;
+}
+
+function renderDirTree(dirTree, basePath = '') {
+ return (
+
+ {dirTree.map(dirTreeNode => {
+ const currPath = join(basePath, dirTreeNode.name);
+
+ return (
+ -
+ {dirTreeNode.name.endsWith('.md') ? (
+
+ {dirTreeNode.name.replace('.md', '')}
+
+ ) : (
+ <>
+ {dirTreeNode.name}
+ {renderDirTree(dirTreeNode.children, currPath)}
+ >
+ )}
+
+ );
+ })}
+
+ );
+}
diff --git a/src/components/aside/Categories/index.js b/src/components/aside/Categories/index.js
new file mode 100644
index 0000000..c595e07
--- /dev/null
+++ b/src/components/aside/Categories/index.js
@@ -0,0 +1,3 @@
+import Categories from './Categories';
+
+export default Categories;
diff --git a/src/components/aside/Home/Home.jsx b/src/components/aside/Home/Home.jsx
new file mode 100644
index 0000000..d789784
--- /dev/null
+++ b/src/components/aside/Home/Home.jsx
@@ -0,0 +1,11 @@
+import Link from 'next/link';
+
+export default async function Home() {
+ return (
+
+ );
+}
diff --git a/src/components/aside/Home/index.js b/src/components/aside/Home/index.js
new file mode 100644
index 0000000..fbe3fed
--- /dev/null
+++ b/src/components/aside/Home/index.js
@@ -0,0 +1,3 @@
+import Home from './Home';
+
+export default Home;
diff --git a/src/components/header/Title/Title.jsx b/src/components/header/Title/Title.jsx
new file mode 100644
index 0000000..dfb3da6
--- /dev/null
+++ b/src/components/header/Title/Title.jsx
@@ -0,0 +1,7 @@
+import Link from 'next/link';
+
+import { USER } from '@/constants/github';
+
+export default function Title() {
+ return {USER.name};
+}
diff --git a/src/components/header/Title/index.js b/src/components/header/Title/index.js
new file mode 100644
index 0000000..46fbfca
--- /dev/null
+++ b/src/components/header/Title/index.js
@@ -0,0 +1,3 @@
+import Title from './Title';
+
+export default Title;
diff --git a/src/layouts/Article/Article.jsx b/src/components/layouts/Article/Article.jsx
similarity index 100%
rename from src/layouts/Article/Article.jsx
rename to src/components/layouts/Article/Article.jsx
diff --git a/src/layouts/Article/Article.module.scss b/src/components/layouts/Article/Article.module.scss
similarity index 100%
rename from src/layouts/Article/Article.module.scss
rename to src/components/layouts/Article/Article.module.scss
diff --git a/src/layouts/Article/index.js b/src/components/layouts/Article/index.js
similarity index 100%
rename from src/layouts/Article/index.js
rename to src/components/layouts/Article/index.js
diff --git a/src/components/layouts/Aside/Aside.jsx b/src/components/layouts/Aside/Aside.jsx
new file mode 100644
index 0000000..7a26e5f
--- /dev/null
+++ b/src/components/layouts/Aside/Aside.jsx
@@ -0,0 +1,5 @@
+import styles from './Aside.module.scss';
+
+export default function Aside({ children }) {
+ return ;
+}
diff --git a/src/layouts/Aside/Aside.module.scss b/src/components/layouts/Aside/Aside.module.scss
similarity index 81%
rename from src/layouts/Aside/Aside.module.scss
rename to src/components/layouts/Aside/Aside.module.scss
index a274c5d..af7264b 100644
--- a/src/layouts/Aside/Aside.module.scss
+++ b/src/components/layouts/Aside/Aside.module.scss
@@ -1,7 +1,6 @@
.aside {
position: sticky;
top: 0;
- grid-area: 1 / 1 / 3 / 2;
width: 300px;
height: 100vh;
overflow-y: scroll;
diff --git a/src/layouts/Aside/index.js b/src/components/layouts/Aside/index.js
similarity index 100%
rename from src/layouts/Aside/index.js
rename to src/components/layouts/Aside/index.js
diff --git a/src/layouts/Body/Body.jsx b/src/components/layouts/Body/Body.jsx
similarity index 100%
rename from src/layouts/Body/Body.jsx
rename to src/components/layouts/Body/Body.jsx
diff --git a/src/components/layouts/Body/Body.module.scss b/src/components/layouts/Body/Body.module.scss
new file mode 100644
index 0000000..86efa82
--- /dev/null
+++ b/src/components/layouts/Body/Body.module.scss
@@ -0,0 +1,17 @@
+.body {
+ display: grid;
+ grid-template-rows: 80px 1fr;
+ grid-template-columns: 300px 1fr;
+
+ > header {
+ grid-area: 1 / 2 / 2 / 3;
+ }
+
+ > aside {
+ grid-area: 1 / 1 / 3 / 2;
+ }
+
+ > main {
+ grid-area: 2 / 2 / 3 / 3;
+ }
+}
diff --git a/src/layouts/Body/index.js b/src/components/layouts/Body/index.js
similarity index 100%
rename from src/layouts/Body/index.js
rename to src/components/layouts/Body/index.js
diff --git a/src/components/layouts/Header/Header.jsx b/src/components/layouts/Header/Header.jsx
new file mode 100644
index 0000000..3e2303a
--- /dev/null
+++ b/src/components/layouts/Header/Header.jsx
@@ -0,0 +1,5 @@
+import styles from './Header.module.scss';
+
+export default function Header({ children }) {
+ return ;
+}
diff --git a/src/layouts/Header/Header.module.scss b/src/components/layouts/Header/Header.module.scss
similarity index 82%
rename from src/layouts/Header/Header.module.scss
rename to src/components/layouts/Header/Header.module.scss
index cd1459f..be34ca4 100644
--- a/src/layouts/Header/Header.module.scss
+++ b/src/components/layouts/Header/Header.module.scss
@@ -4,7 +4,6 @@
position: sticky;
top: 0;
z-index: 1;
- grid-area: 1 / 2 / 2 / 3;
font-size: 32px;
background-color: inherit;
}
diff --git a/src/layouts/Header/index.js b/src/components/layouts/Header/index.js
similarity index 100%
rename from src/layouts/Header/index.js
rename to src/components/layouts/Header/index.js
diff --git a/src/components/layouts/Main/Main.jsx b/src/components/layouts/Main/Main.jsx
new file mode 100644
index 0000000..c90f7af
--- /dev/null
+++ b/src/components/layouts/Main/Main.jsx
@@ -0,0 +1,3 @@
+export default function Main({ children }) {
+ return {children};
+}
diff --git a/src/layouts/Main/index.js b/src/components/layouts/Main/index.js
similarity index 100%
rename from src/layouts/Main/index.js
rename to src/components/layouts/Main/index.js
diff --git a/src/layouts/Aside/Aside.jsx b/src/layouts/Aside/Aside.jsx
deleted file mode 100644
index adef4c1..0000000
--- a/src/layouts/Aside/Aside.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import { promises as fs } from 'fs';
-import Link from 'next/link';
-
-import { DOCS } from '@/constants/path';
-
-import styles from './Aside.module.scss';
-
-export default async function Aside() {
- return (
-
- );
-}
-
-const dirs = await fs.readdir(DOCS, {
- encoding: 'utf-8',
- recursive: true,
-});
-
-function buildDirTree(dirs) {
- const tree = {};
-
- dirs.forEach(dir => {
- const parts = dir.split(/[/\\]/); // Use `/` or `\\` as a path seperator.
-
- let current = tree;
-
- parts.forEach((part, index) => {
- if (!current[part]) {
- current[part] = index === parts.length - 1 ? null : {};
- }
- current = current[part];
- });
- });
-
- return tree;
-}
-
-function renderDirTree(tree, basePath = '') {
- return (
-
- {Object.keys(tree).map(key => {
- const path = `${basePath}/${key}`;
-
- if (tree[key] === null) {
- return (
- -
-
- {key.replace('.md', '')}
-
-
- );
- } else {
- return (
- -
- {key}
- {renderDirTree(tree[key], path)}
-
- );
- }
- })}
-
- );
-}
diff --git a/src/layouts/Body/Body.module.scss b/src/layouts/Body/Body.module.scss
deleted file mode 100644
index 24b50d5..0000000
--- a/src/layouts/Body/Body.module.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.body {
- display: grid;
- grid-template-rows: 80px 1fr;
- grid-template-columns: 300px 1fr;
-}
diff --git a/src/layouts/Header/Header.jsx b/src/layouts/Header/Header.jsx
deleted file mode 100644
index dd8ea8a..0000000
--- a/src/layouts/Header/Header.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { USER } from '@/constants/github';
-
-import styles from './Header.module.scss';
-
-export default function Header() {
- return (
-
- );
-}
diff --git a/src/layouts/Main/Main.jsx b/src/layouts/Main/Main.jsx
deleted file mode 100644
index 7dd1d18..0000000
--- a/src/layouts/Main/Main.jsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import styles from './Main.module.scss';
-
-export default function Main({ children }) {
- return {children};
-}
diff --git a/src/layouts/Main/Main.module.scss b/src/layouts/Main/Main.module.scss
deleted file mode 100644
index 3950916..0000000
--- a/src/layouts/Main/Main.module.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.main {
- grid-area: 2 / 2 / 3 / 3;
-}
diff --git a/src/utils/dirTree.js b/src/utils/dirTree.js
new file mode 100644
index 0000000..1b6ecaa
--- /dev/null
+++ b/src/utils/dirTree.js
@@ -0,0 +1,50 @@
+import { promises as fs } from 'fs';
+import { join } from 'path';
+
+/**
+ * @typedef {object} DirTreeNode
+ *
+ * @property {string} name The name of the node.
+ * @property {Array} [children] The array of child nodes if the node is a directory. This is a recursive structure.
+ */
+
+/**
+ * Asynchronously retrieves the directory tree structure.
+ *
+ * @async
+ * @param {string} dirPath The path of the directory.
+ * @returns {Promise>} A promise that resolves to an array of `DirTreeNode`.
+ *
+ * @example
+ * // Get the directory tree structure
+ * const dirTree = await getDirTree('/path/to/dir');
+ * console.log(dirTree);
+ */
+export async function getDirTree(dirPath) {
+ // `readdir` automatically throws an error when `dirPath` is not a directory.
+ const dirents = await fs.readdir(dirPath, { withFileTypes: true });
+
+ return await Promise.all(
+ dirents.map(async dirent => ({
+ name: dirent.name,
+ ...(dirent.isDirectory()
+ ? { children: await getDirTree(join(dirPath, dirent.name)) }
+ : {}),
+ })),
+ );
+}
+
+/**
+ * Checks if a `DirTreeNode` is a directory.
+ *
+ * @param {DirTreeNode} dirTreeNode The `DirTreeNode` object.
+ * @returns {boolean} `true` if the node is a directory. otherwise, `false`.
+ *
+ * @example
+ * // Check if a node is a directory
+ * const isDir = isDirectory({ name: 'folder', children: [...] });
+ * console.log(isDir); // true
+ */
+export function isDirectory(dirTreeNode) {
+ return Boolean(dirTreeNode.children);
+}