【VitePress】新增md文件后自动更新侧边栏导航

发布于:2025-04-13 ⋅ 阅读:(19) ⋅ 点赞:(0)

说在前面

  • 操作系统:windows11
  • node版本:v18.19.0
  • npm版本:10.2.3
  • vitepress版本:1.6.3
  • 完整代码:github

先看效果

  • 模板用的就是官方主题
    在这里插入图片描述
    在这里插入图片描述

代码结构

详细说明

侧边栏格式

  • 通常,侧边栏定义在config.mts
    export default {
      themeConfig: {
        sidebar: [
          {
            text: 'Guide',
            items: [
              { text: 'Introduction', link: '/introduction' },
              { text: 'Getting Started', link: '/getting-started' },
              ...
            ]
          }
        ]
      }
    }
    
    例如上述代码定义了这样子的侧边栏:
    在这里插入图片描述
  • 而现在,我们需要根据目录内容,自动生成一个侧边栏

utils

  • 在该目录下,我们实现了一个脚本,用于自动生成侧边栏内容
    import FastGlob from 'fast-glob';
    const { glob } = FastGlob
    import fs from 'fs/promises';
    import path from 'path';
    import type { DefaultTheme } from 'vitepress';
    import chokidar from 'chokidar';
    
    type SidebarConfig = Record<string, DefaultTheme.SidebarItem[]>;
    
    export async function generateAutoSidebar(): Promise<SidebarConfig> {
        const basePath = path.join(process.cwd(), 'doc/reviews');
        const branches = await glob('*/', {
            cwd: basePath,
            onlyDirectories: true,
            deep: 1
        });
    
        const sidebar: DefaultTheme.SidebarItem[] = [];
    
        for (const branchDir of branches) {
            const branchName = branchDir.replace(/\/$/, '');
            const mdFiles = await glob(`${branchDir}/*.md`, {
                cwd: basePath,
                ignore: ['**/_*.md']
            });
    
            const items: DefaultTheme.SidebarItem[] = mdFiles
                .map(file => {
                    const fileName = path.basename(file, '.md');
                    return {
                        text: `${fileName}.md`,
                        link: `/reviews/${branchDir}/${fileName}`
                    };
                })
                .sort((a, b) => {
                    const numA = parseInt(a.text.match(/\d+/)?.[0] || '0');
                    const numB = parseInt(b.text.match(/\d+/)?.[0] || '0');
                    return numA - numB;
                });
    
            sidebar.push({
                text: branchName,
                collapsed: false,
                items
            });
        }
    
        return { '/reviews/': sidebar };
    }
    
    export async function writeSidebarConfig(): Promise<void> {
        const sidebarConfig = await generateAutoSidebar();
        const configContent = `// Auto-generated sidebar config
    import type { DefaultTheme } from 'vitepress';
    
    export const sidebarConfig: DefaultTheme.Config['sidebar'] = ${JSON.stringify(sidebarConfig, null, 2)};
    `;
    
        var p = path.join(process.cwd(), 'doc/.vitepress/sidebar.generated.ts')
    
        await fs.writeFile(
            p,
            configContent
        );
    }
    
    
    writeSidebarConfig()
    
  • 通过执行tsx doc/.vitepress/utils/generateSidebar.ts --watch,将在./doc/.vitepress/目录下自动生成sidebar.generate.ts文件,以上述reviews文件夹中内容为例,生成的内容为:
    // Auto-generated sidebar config
    import type { DefaultTheme } from 'vitepress';
    
    export const sidebarConfig: DefaultTheme.Config['sidebar'] = {
      "/reviews/": [
        {
          "text": "aaa",
          "collapsed": false,
          "items": [
            {
              "text": "1.md",
              "link": "/reviews/aaa/1"
            },
            {
              "text": "2.md",
              "link": "/reviews/aaa/2"
            }
          ]
        },
        {
          "text": "bbb",
          "collapsed": false,
          "items": [
            {
              "text": "1.md",
              "link": "/reviews/bbb/1"
            }
          ]
        }
      ]
    };
    
  • 而后,在我们的config.mts中引用即可
    import { defineConfig } from 'vitepress'
    import { sidebarConfig } from './sidebar.generated.js';
    
    // https://vitepress.dev/reference/site-config
    export default defineConfig({
      title: "coding",
      description: "code review helper",
      themeConfig: {
        // https://vitepress.dev/reference/default-theme-config
        nav: [
          { text: 'Home', link: '/' },
          { text: 'Reviews', link: '/reviews' }
        ],
    
        sidebar: sidebarConfig,
      },
      async buildEnd() {
        const { writeSidebarConfig } = await import('./utils/generateSidebar.js');
        await writeSidebarConfig();
      }
    })
    

监听文件变化

  • utils/generateSidebar.ts最后添加这一段,监控该目录下的文件变化,当有变化时,会自动调用writeSidebarConfig重新生成侧边栏内容
    // 开发模式文件监听
    if (process.env.NODE_ENV === 'development' || process.argv.includes('--watch')) {
        const watcher = chokidar.watch('doc/reviews/**/*.md', {
            ignored: /(^|[/\\])\../,
            persistent: true
        });
    
        watcher
            .on('add', () => writeSidebarConfig())
            .on('unlink', () => writeSidebarConfig());
    
        process.stdin.resume();
    }
    

使用pm2管理监听进程

  • 建议在linux下使用,windows下有问题
  • 安装
    npm install -D pm2
    
  • 新建ecosystem.config.js
    module.exports = {
        apps: [
            {
                name: 'vitepress',
                script: 'npm',
                args: 'run docs:dev',
                watch: ['doc/.vitepress/sidebar.generated.ts']
            },
            {
                name: 'sidebar-watcher',
                script: 'npm',
                args: 'run dev:sidebar',
                watch: false
            }
        ]
    };
    
  • 修改package.json
    {
      "scripts": {
        "start": "pm2 start eco.config.js",
        "stop": "pm2 stop eco.config.js",
        "docs:dev": "vitepress dev doc",
        "docs:build": "vitepress build doc",
        "docs:preview": "vitepress preview doc",
        "docs:sidebar": "tsx doc/.vitepress/utils/generateSidebar.ts --watch"
      },
      "devDependencies": {
        // ...
      }
    }
    
  • 运行
    npm run start