Skip to content

Commit 2ff1faa

Browse files
committed
feat(docs): generate nav and sidebar from docs tree
1 parent c8e833a commit 2ff1faa

1 file changed

Lines changed: 96 additions & 5 deletions

File tree

docs/.vitepress/config.js

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
import fs from "node:fs";
2+
import path from "node:path";
3+
import { fileURLToPath } from "node:url";
14
import { defineConfig } from "vitepress";
25
import {
36
groupIconMdPlugin,
47
localIconLoader,
58
groupIconVitePlugin,
69
} from "vitepress-plugin-group-icons";
710

11+
const configDir = path.dirname(fileURLToPath(import.meta.url));
12+
const docsSrcDir = path.resolve(configDir, "../src");
813
const repoUrl = "https://github.com/BlueLua";
914
const siteOrigin = "https://BlueLua.github.io";
1015
const siteBasePath = "/";
@@ -31,6 +36,95 @@ const websiteJsonLd = {
3136
description: siteDescription,
3237
};
3338

39+
function listProjects() {
40+
return fs
41+
.readdirSync(docsSrcDir, { withFileTypes: true })
42+
.filter((entry) => entry.isDirectory())
43+
.map((entry) => entry.name)
44+
.filter((project) =>
45+
fs.existsSync(path.join(docsSrcDir, project, "index.md")),
46+
)
47+
.sort();
48+
}
49+
50+
function titleFromFile(file) {
51+
return file.replace(/\.md$/, "").replace(/-/g, " ");
52+
}
53+
54+
function titleFromDir(dir) {
55+
if (dir === "api") return "API";
56+
57+
return dir.replace(/-/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
58+
}
59+
60+
function pageItem(project, relativeFile) {
61+
const slug = relativeFile.replace(/\.md$/, "");
62+
const file = path.basename(relativeFile);
63+
64+
return {
65+
text: titleFromFile(file),
66+
link: `/${project}/${slug}`,
67+
};
68+
}
69+
70+
function listMarkdownFiles(project, relativeDir = "") {
71+
const dir = path.join(docsSrcDir, project, relativeDir);
72+
73+
return fs
74+
.readdirSync(dir, { withFileTypes: true })
75+
.filter((entry) => entry.isFile())
76+
.map((entry) => entry.name)
77+
.filter((file) => file.endsWith(".md") && file !== "index.md")
78+
.sort();
79+
}
80+
81+
function listNestedDocDirs(project) {
82+
const dir = path.join(docsSrcDir, project);
83+
84+
return fs
85+
.readdirSync(dir, { withFileTypes: true })
86+
.filter((entry) => entry.isDirectory())
87+
.map((entry) => entry.name)
88+
.filter((name) => listMarkdownFiles(project, name).length > 0)
89+
.sort();
90+
}
91+
92+
function buildSidebar() {
93+
return Object.fromEntries(
94+
listProjects().map((project) => {
95+
const pages = listMarkdownFiles(project);
96+
const nestedDirs = listNestedDocDirs(project);
97+
98+
return [
99+
`/${project}/`,
100+
[
101+
{
102+
text: project,
103+
items: [
104+
{ text: "Overview", link: `/${project}/` },
105+
...pages.map((file) => pageItem(project, file)),
106+
],
107+
},
108+
...nestedDirs.map((dir) => ({
109+
text: titleFromDir(dir),
110+
collapsed: false,
111+
items: listMarkdownFiles(project, dir).map((file) =>
112+
pageItem(project, `${dir}/${file}`),
113+
),
114+
})),
115+
],
116+
];
117+
}),
118+
);
119+
}
120+
121+
function buildProjectNavItems() {
122+
return listProjects().map((project) => ({
123+
text: project,
124+
link: `/${project}/`,
125+
}));
126+
}
127+
34128
export default defineConfig({
35129
srcDir: "./src",
36130
title: "BlueLua",
@@ -79,17 +173,14 @@ export default defineConfig({
79173
{ text: "Home", link: "/" },
80174
{
81175
text: "Projects",
82-
items: [
83-
{ text: "evdev", link: "evdev" },
84-
{ text: "timeutil", link: "timeutil" },
85-
{ text: "tty", link: "tty" },
86-
],
176+
items: buildProjectNavItems(),
87177
},
88178
{ text: "GitHub", link: "https://github.com/BlueLua" },
89179
{
90180
text: "🇵🇸 Free Palestine",
91181
link: "https://techforpalestine.org/learn-more",
92182
},
93183
],
184+
sidebar: buildSidebar(),
94185
},
95186
});

0 commit comments

Comments
 (0)