-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate-sidebar.js
More file actions
128 lines (110 loc) · 4.21 KB
/
generate-sidebar.js
File metadata and controls
128 lines (110 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
const fs = require("fs").promises;
const path = require("path");
// 命令行参数处理:获取要扫描的文件夹路径,默认是当前目录
const targetDir = process.argv[2] || process.cwd();
/**
* 递归扫描目录,收集符合条件的Markdown文件
* @param {string} dir - 要扫描的目录
* @param {string} baseDir - 基础目录,用于计算相对路径
* @returns {Promise<Array>} - 文件结构数组
*/
async function scanDirectory(dir, baseDir = dir) {
const results = [];
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
// 忽略以_开头的文件和目录
if (entry.name.startsWith("_")) {
continue;
}
const fullPath = path.join(dir, entry.name);
const relativePath = path.relative(baseDir, fullPath);
// 对名称进行URL编码(处理非法字符),但保留原始名称用于显示
const encodedName = encodeURIComponent(entry.name);
const encodedRelativePath = relativePath
.split(path.sep)
.map((segment) => encodeURIComponent(segment))
.join("/"); // 使用/作为路径分隔符,符合URL规范
if (entry.isDirectory()) {
// 递归处理子目录
const subDirFiles = await scanDirectory(fullPath, baseDir);
if (subDirFiles.length > 0) {
results.push({
type: "directory",
name: entry.name, // 原始名称用于显示
encodedName: encodedName, // 编码后的名称用于路径
path: encodedRelativePath, // 编码后的相对路径
children: subDirFiles,
});
}
} else if (entry.isFile() && path.extname(entry.name) === ".md") {
// 处理Markdown文件
const fileNameWithoutExt = path.basename(entry.name, ".md");
results.push({
type: "file",
name: fileNameWithoutExt, // 原始名称用于显示
encodedName: encodeURIComponent(fileNameWithoutExt), // 编码后的名称
path: encodedRelativePath, // 编码后的相对路径
originalPath: relativePath, // 保留原始路径用于参考
});
}
}
// 排序逻辑:
// 1. 文件夹(directory)排在文件(file)前面
// 2. 同一类型的按名称首字母顺序排序(支持国际化)
return results.sort((a, b) => {
// 先按类型排序:文件夹在前,文件在后
if (a.type !== b.type) {
return a.type === "directory" ? -1 : 1;
}
// 再按名称首字母排序,使用localeCompare确保正确的字母顺序
return a.name.localeCompare(b.name, undefined, {
sensitivity: "base", // 不区分大小写
numeric: true, // 正确处理包含数字的名称
});
});
}
/**
* 将文件结构转换为docsify侧边栏格式的Markdown
* @param {Array} items - 文件结构数组
* @param {number} level - 层级,用于生成缩进
* @returns {string} - 侧边栏Markdown内容
*/
function generateSidebarMarkdown(items, level = 0) {
const indent = " ".repeat(level);
let markdown = "";
for (const item of items) {
if (item.type === "directory") {
// 目录项:显示原始名称,使用编码后的路径
markdown += `${indent}- [${item.name}](${item.path}/)\n`;
// 递归处理子项,层级+1
markdown += generateSidebarMarkdown(item.children, level + 1);
} else if (item.type === "file" && item.name !== "README") {
// 文件项:显示原始名称,使用编码后的路径
markdown += `${indent}- [${item.name}](${item.path})\n`;
}
}
return markdown;
}
/**
* 主函数:扫描目录并生成侧边栏文件
*/
async function main() {
try {
console.log(`开始扫描目录: ${targetDir}`);
// 扫描目录获取文件结构
const fileStructure = await scanDirectory(targetDir);
// 生成侧边栏Markdown内容
const sidebarContent = generateSidebarMarkdown(fileStructure);
// 输出文件路径
const outputPath = path.join(process.cwd(), "_sidebar.md");
// 写入文件
await fs.writeFile(outputPath, sidebarContent);
console.log(`侧边栏已生成: ${outputPath}`);
console.log("生成完成!");
} catch (error) {
console.error("生成侧边栏时出错:", error.message);
process.exit(1);
}
}
// 执行主函数
main();