当前位置: 首页 > news >正文

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

目录

  • 说在前面
  • 先看效果
  • 代码结构
  • 详细说明
    • 侧边栏格式
    • utils
    • 监听文件变化
    • 使用pm2管理监听进程

说在前面

  • 操作系统: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
    

相关文章:

  • LeetCode 1223 投骰子模拟
  • 安卓AssetManager【一】-资源的查找过程
  • 从MySQL快速上手数据仓库Hive
  • 论文阅读笔记——Multi-Token Attention
  • 华为机试—最大最小路
  • 为什么在删除数据库存在‘if exists‘语句
  • 判断两个 IP 地址是否在同一子网 C
  • Redis实现分布式定时任务
  • 畅游Diffusion数字人(23):字节最新表情+动作模仿视频生成DreamActor-M1
  • dfs和bfs算法
  • PyTorch DataLoader 参数详解
  • Autoware源码总结
  • 路由策略/策略路由之Filter-Policy
  • 基础层数据从kafka读取写入hbase的优化方案
  • 摄像头解析
  • 第一期:[特殊字符] 深入理解MyBatis[特殊字符]从JDBC到MyBatis——持久层开发的转折点[特殊字符]
  • Vue 3 和 Vue 2 的区别及优点
  • Flask+Plotly结合动态加载图形页面实践
  • cluster、update、delete在死元组清理上的作用
  • boss zp_stoken补环境
  • 什么是wordpress网站/设计公司网站
  • 软件开发平台搭建/泉州seo报价
  • 网站建设服务费怎么做会计分录/聊城优化seo
  • 东道设计学院/seo是什么品牌
  • 不同用户入口的网站样板/宁波seo外包方案
  • 衡水seo_衡水网站建设-燕丰收/免费seo免费培训