I cannot believe how long I struggled with getting this to work… but patience is key ;)
I needed to generate a list of all files in a specific folder, so that I wouldn’t continuously have to update the VitePress configuration file.
This is quite an ugly solution that would need an iteration or two, but whatever - it works for now.
The following dependencies were needed:
pnpm add -D glob fs
I think gray-matter should already be bundled with VitePress.
Here’s my sidebar.ts
file in docs/.vitepress
:
import { glob } from 'glob';
import fs from 'fs';
import matter from 'gray-matter';
export interface INavItem {
title: string;
id: string;
link: string;
done: boolean;
}
export default function getFiles(path: string): Promise<INavItem[]> {
return new Promise((resolve, reject) => {
let navigation: INavItem[] = [];
const filePath = path;
const files: string[] = glob.sync(filePath);
files.sort();
for (const file of files) {
const fileData = fs.readFileSync(file).toString();
const fm = matter(fileData);
let title = fm.data.title;
if (fm.data.title === undefined) {
title = file;
}
navigation.push({
title,
id: fm.data.id, // Custom data
link: file.replace('docs', ''), // Passing in the entire docs folder path, so removing this manually
done: Boolean(fm.data.done), // Also custom data
});
}
resolve(navigation);
});
}
And in docs/.vitepress/config.ts
:
import getFiles, { INavItem } from './sidebar';
const module1 = await getFiles('docs/modules/v1/**/*.md'); // Path to folder
const module2 = await getFiles('docs/modules/v2/**/*.md'); // Path to folder
// Unclear why the resolved promise result is not a valid iterable, so
// re-creating the links, seems stupid but…
function generateSidebarItems(module: any) {
const processedLinks = [];
for(let i = 0; i < module.length; i++) {
let title = '';
const moduleData: INavItem = module[i];
if (!moduleData.done) {
title += '<span style="font-size: 0.5rem; position: relative; top: -1px;">❌</span> ';
}
title += `<span class="custom-class">${moduleData.title}</span>`;
processedLinks.push({
text: title,
link: moduleData.link,
})
}
return processedLinks;
}
export default defineConfig({
// other properties
sidebar: [
{
text: 'Module 1',
collapsible: true,
collapsed,
items: generateSidebarItems(module1),
},
{
text: 'Module 2',
collapsible: true,
collapsed,
items: generateSidebarItems(module2),
},
],
})
And the markdown frontmatter looks like this:
---
title: Example module
id: exampleid
done: false
---
# {{ $frontmatter.title }}
Lorem ipsum…
Of course, there are also nicer plugins/solutions, like this repo. I also tried this code snippet from 2022 but the sidebar kept being generated/done, before it had time to read the files from disk and populate the list.
I also tried some hacky solution with using the eminent import.meta.glob
that Vite has, but it didn’t work in VitePress at the time of writing.
And I tried ”Build-Time Data Loading” which seemed like something that would have been suitable in this case, and rejoiced when I got it to work, until I realized it only worked once VitePress had an instance running, and not out-of-the-box. So fail on that attempt too.