fix: 文档监听与 vitepress 竞态问题

This commit is contained in:
unanmed 2026-03-08 18:28:48 +08:00
parent 5a1a0fee42
commit 5550fac160
5 changed files with 93 additions and 72 deletions

View File

@ -1,35 +1,39 @@
import fs from 'fs-extra'; import { basename, join, resolve } from 'node:path';
import path from 'path';
import chokidar from 'chokidar';
import { DefaultTheme } from 'vitepress'; import { DefaultTheme } from 'vitepress';
import { readdir, stat, writeFile } from 'node:fs/promises';
const apiDir = path.resolve('./docs/api'); const apiDir = resolve('./docs/api');
const sidebarConfigPath = path.resolve('./docs/.vitepress/apiSidebar.ts'); const sidebarConfigPath = resolve('./docs/.vitepress/apiSidebar.ts');
const weight: Record<string, number> = { const weight: Record<string, number> = {
主页: 10, 主页: 10,
函数: 5 函数: 5
}; };
function generateSidebar(): void { export async function generateSidebar(): Promise<void> {
const sidebar: DefaultTheme.SidebarItem[] = [ const sidebar: DefaultTheme.SidebarItem[] = [
{ text: '目录', link: '/api/' } { text: '目录', link: '/api/' }
]; ];
// 遍历 api 目录,查找 package 目录 // 遍历 api 目录,查找 package 目录
const packages = fs const dir = await readdir(apiDir);
.readdirSync(apiDir) const packages = [];
.filter(pkg => fs.statSync(path.join(apiDir, pkg)).isDirectory()); for (const pkg of dir) {
const stats = await stat(join(apiDir, pkg));
if (stats.isDirectory()) {
packages.push(pkg);
}
}
packages.forEach(pkg => { await Promise.all(
const pkgPath = path.join(apiDir, pkg); packages.map(async pkg => {
const files = fs const pkgPath = join(apiDir, pkg);
.readdirSync(pkgPath) const dir = await readdir(pkgPath);
.filter(file => file.endsWith('.md')); const files = dir.filter(file => file.endsWith('.md'));
const items: DefaultTheme.SidebarItem[] = files.map(file => { const items: DefaultTheme.SidebarItem[] = files.map(file => {
const filePath = `api/${pkg}/${file}`; const filePath = `api/${pkg}/${file}`;
const fileName = path.basename(file, '.md'); const fileName = basename(file, '.md');
return { return {
text: text:
@ -53,7 +57,8 @@ function generateSidebar(): void {
collapsed: true, collapsed: true,
items items
}); });
}); })
);
// 生成 sidebar.ts // 生成 sidebar.ts
const sidebarContent = `import { DefaultTheme } from 'vitepress'; const sidebarContent = `import { DefaultTheme } from 'vitepress';
@ -63,35 +68,6 @@ export default ${JSON.stringify(
null, null,
4 4
)} as DefaultTheme.SidebarItem[];`; )} as DefaultTheme.SidebarItem[];`;
fs.writeFileSync(sidebarConfigPath, sidebarContent); await writeFile(sidebarConfigPath, sidebarContent);
console.log('✅ Sidebar 配置已更新'); console.log('✅ Sidebar 配置已更新');
} }
// 初次运行
generateSidebar();
// 监听文件变动
chokidar
.watch(apiDir, { ignoreInitial: true })
.on('add', filePath => {
console.log(`📄 文件新增: ${filePath}`);
generateSidebar();
})
.on('unlink', filePath => {
console.log(`❌ 文件删除: ${filePath}`);
generateSidebar();
})
.on('addDir', dirPath => {
console.log(`📁 目录新增: ${dirPath}`);
generateSidebar();
})
.on('unlinkDir', dirPath => {
console.log(`📁 目录删除: ${dirPath}`);
generateSidebar();
})
.on('raw', (event, path, details) => {
if (event === 'rename') {
console.log(`🔄 文件或文件夹重命名: ${path}`);
generateSidebar();
}
});

View File

@ -1,7 +1,39 @@
import { defineConfig } from 'vitepress'; import { defineConfig, Plugin } from 'vitepress';
import { MermaidMarkdown, MermaidPlugin } from 'vitepress-plugin-mermaid'; import { MermaidMarkdown, MermaidPlugin } from 'vitepress-plugin-mermaid';
import api from './apiSidebar'; import api from './apiSidebar';
import { join } from 'path'; import { join } from 'path';
import { generateSidebar } from './api';
function listenSidebar(): Plugin {
return {
name: 'sidebar-listen',
configureServer(server) {
server.watcher
.on('add', filePath => {
console.log(`📄 文件新增: ${filePath}`);
generateSidebar();
})
.on('unlink', filePath => {
console.log(`❌ 文件删除: ${filePath}`);
generateSidebar();
})
.on('addDir', dirPath => {
console.log(`📁 目录新增: ${dirPath}`);
generateSidebar();
})
.on('unlinkDir', dirPath => {
console.log(`📁 目录删除: ${dirPath}`);
generateSidebar();
})
.on('raw', (event, path, _) => {
if (event === 'rename') {
console.log(`🔄 文件或文件夹重命名: ${path}`);
generateSidebar();
}
});
}
};
}
// https://vitepress.dev/reference/site-config // https://vitepress.dev/reference/site-config
export default defineConfig({ export default defineConfig({
@ -157,7 +189,7 @@ export default defineConfig({
}, },
vite: { vite: {
// @ts-expect-error 类型错误 // @ts-expect-error 类型错误
plugins: [MermaidPlugin()], plugins: [MermaidPlugin(), listenSidebar()],
optimizeDeps: { optimizeDeps: {
include: ['mermaid'] include: ['mermaid']
}, },

5
docs/.vitepress/init.ts Normal file
View File

@ -0,0 +1,5 @@
import { generateSidebar } from './api';
(() => {
generateSidebar();
})();

View File

@ -12,7 +12,7 @@
"build:packages": "vue-tsc --noEmit && tsx script/build-packages.ts", "build:packages": "vue-tsc --noEmit && tsx script/build-packages.ts",
"build:game": "tsx script/declare.ts && vue-tsc --noEmit && tsx script/build-game.ts", "build:game": "tsx script/declare.ts && vue-tsc --noEmit && tsx script/build-game.ts",
"build:lib": "vue-tsc --noEmit && tsx script/build-lib.ts", "build:lib": "vue-tsc --noEmit && tsx script/build-lib.ts",
"docs:dev": "concurrently -k -n SIDEBAR,VITEPRESS -c blue,green \"tsx docs/.vitepress/api.ts\" \"vitepress dev docs\"", "docs:dev": "tsx docs/.vitepress/init.ts && vitepress dev docs",
"docs:build": "vitepress build docs", "docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs", "docs:preview": "vitepress preview docs",
"pack:template": "tsx script/pack-template.ts" "pack:template": "tsx script/pack-template.ts"

View File

@ -7,5 +7,13 @@
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true "strict": true
}, },
"include": ["vite.config.ts", "script/**/*.ts", "mota.config.ts"] "include": [
"vite.config.ts",
"script/**/*.ts",
"docs/**/*.ts",
"docs/.vitepress/api.ts",
"docs/.vitepress/config.ts",
"docs/.vitepress/apiSidebar.ts",
"docs/.vitepress/init.ts"
]
} }