From f4354a2de3eb2efae44e65b142e060d2ad1ad13d Mon Sep 17 00:00:00 2001 From: Evorp <3vorpgaming@gmail.com> Date: Sat, 22 Jun 2024 13:19:29 -0700 Subject: [PATCH] types+don't show deprecated categories in navbar --- .vitepress/computeCategories.ts | 78 ++++++++++++++++++++++++++++++++ .vitepress/config.ts | 79 +++------------------------------ .vitepress/types.d.ts | 22 +++++++++ 3 files changed, 107 insertions(+), 72 deletions(-) create mode 100644 .vitepress/computeCategories.ts create mode 100644 .vitepress/types.d.ts diff --git a/.vitepress/computeCategories.ts b/.vitepress/computeCategories.ts new file mode 100644 index 0000000..29ed8b0 --- /dev/null +++ b/.vitepress/computeCategories.ts @@ -0,0 +1,78 @@ +import { readdirSync, readFileSync, statSync } from "fs"; +import { sep } from "path"; +import matter from "gray-matter"; +import type { DocCategory, DocFile, DocFrontmatter } from "./types"; + +/** + * Convert a string to title case + * @author Evorp + * @param str - String to convert + * @returns String in title case + */ +export function toTitleCase(str: string) { + if (!str) return str; + return str + .split(/-|_| /g) + .map((word) => word[0].toUpperCase() + word.slice(1)) + .join(" "); +} + +/** + * Return an array of all filepaths in a directory + * @author Juknum, Evorp + * @param dir - Directory to search + * @param filelist - Used for recursion + * @returns Array of file paths + */ +export function walkSync(dir: string, filelist: string[] = []) { + // add trailing slash if not present + if (!dir.endsWith(sep)) dir += sep; + for (const file of readdirSync(dir)) { + if (statSync(dir + file).isDirectory()) + // read directories inside directories recursively + filelist = walkSync(dir + file + sep, filelist); + else filelist.push(dir + file); + } + return filelist; +} + +/** + * Read pages folder and map files to a sidebar and navbar compatible format + * @author Evorp + * @param dir - Directory to read + * @returns Array of category objects + */ +export default function computeCategories(dir: string) { + return walkSync(dir) + .filter((f) => f.endsWith(".md")) + .map((fileName) => { + const file = readFileSync(fileName, { encoding: "utf8" }); + const name = fileName.replace(process.cwd(), "").replace(".md", ""); + // parse yaml frontmatter into object + const frontmatter = matter(file).data as DocFrontmatter; + return { + text: frontmatter.title, + link: name, + // fall back to the parent folder + category: frontmatter.category || toTitleCase(name.split("/").at(-2) || ""), + date: frontmatter.date, + // default false + archived: frontmatter.archived ?? false, + deprecated: frontmatter.deprecated ?? false, + } as DocFile; + }) + .reduce((acc, cur) => { + // because this isn't just an object with the names as keys we need to search + const found = acc.findIndex((v) => v.text === cur.category); + if (found === -1) acc.push({ text: cur.category, collapsed: false, items: [cur] }); + else acc[found].items?.push(cur); + return acc; + }, [] as DocCategory[]) + .map((category) => { + // collapse categories that are entirely archived/deprecated by default + if (category.items.every((i) => i.deprecated || i.archived)) + return { ...category, collapsed: true }; + return category; + }) + .sort(); +} diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 00c32e2..0f8b0e2 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -1,78 +1,13 @@ import { defineConfig } from "vitepress"; -import { readdirSync, readFileSync, statSync } from "fs"; -import { join, sep } from "path"; -import matter from "gray-matter"; import { fileURLToPath } from "url"; import metaTags from "./meta"; - -function toTitleCase(str?: string) { - if (!str) return str; - return str - .split(/-|_| /g) - .map((word) => word[0].toUpperCase() + word.slice(1)) - .join(" "); -} - -/** - * Return an array of all filepaths in a directory - * @author Juknum, Evorp - * @param dir - * @param filelist recursion - * @returns array of file paths - */ -function walkSync(dir: string, filelist: string[] = []) { - // add trailing slash if not present - if (!dir.endsWith(sep)) dir += sep; - for (const file of readdirSync(dir)) { - if (statSync(dir + file).isDirectory()) - // read directories inside directories recursively - filelist = walkSync(dir + file + sep, filelist); - else filelist.push(dir + file); - } - return filelist; -} - -function computeBars() { - return walkSync(join(process.cwd(), "pages")) - .filter((f) => f.endsWith(".md")) - .map((fileName) => { - const file = readFileSync(fileName, { encoding: "utf8" }); - const name = fileName.replace(process.cwd(), "").replace(".md", ""); - // parse yaml frontmatter into object - const frontmatter = matter(file).data; - return { - frontmatter, - text: frontmatter.title, - link: name, - date: frontmatter.date, - archived: frontmatter.archived ?? false, - deprecated: frontmatter.deprecated ?? false, - }; - }) - .reduce((acc, cur) => { - const category = cur.frontmatter.category || toTitleCase(cur.link.split("/").at(-2)); - - // delete because unused - delete (cur as any).frontmatter; - - // because this isn't just an object with the names as keys we need to search - const found = acc.findIndex((v) => v.text === category); - if (found === -1) acc.push({ text: category, collapsed: false, items: [cur] }); - else acc[found].items?.push(cur); - return acc; - }, [] as any[]) - .map((category) => { - // collapse categories that are entirely archived/deprecated by default - if (category.items.every((i: any) => i.deprecated || i.archived)) - return { ...category, collapsed: true }; - return category; - }) - .sort(); -} +import computeCategories from "./computeCategories"; +import { join } from "path"; // https://vitepress.dev/reference/site-config export default () => { - const bars = computeBars(); + // DocCategory satisfies both navbar and sidebar interfaces + const categories = computeCategories(join(process.cwd(), "pages")); return defineConfig({ title: "Faithful Docs", description: "The official site with documentation and guides related to Faithful.", @@ -86,15 +21,15 @@ export default () => { }, ], ], - // https://vitepress.dev/reference/default-theme-config themeConfig: { logo: "https://raw.githubusercontent.com/Faithful-Resource-Pack/Branding/main/site/favicon.ico", nav: [ { text: "Home", link: "/" }, - ...bars, + // don't put all archived/deprecated categories in navbar + ...categories.filter((c) => !c.collapsed), { text: "Main Site", link: "https://faithfulpack.net" }, ], - sidebar: bars, + sidebar: categories, docFooter: { prev: false, next: false, diff --git a/.vitepress/types.d.ts b/.vitepress/types.d.ts new file mode 100644 index 0000000..89d7f58 --- /dev/null +++ b/.vitepress/types.d.ts @@ -0,0 +1,22 @@ +export interface DocMeta { + category?: string; + date: string; + archived?: boolean; + deprecated?: boolean; +} + +export interface DocFrontmatter extends DocMeta { + title: string; +} + +// at this point all optional fields are filled +export interface DocFile extends Required { + text: string; + link: string; +} + +export interface DocCategory { + text: string; + collapsed: boolean; + items: DocFile[]; +}