Skip to content

Commit 27607d7

Browse files
committed
整理一下文档站轻量维护和健康巡检流程
这次主要补充创游世界资料库的日常轻量维护能力,把仓库健康巡检、链接检查、同步临时文件清理、VS Code 扫描优化和维护流程文档整理到一起,减少后续小改时反复完整构建、误扫缓存目录、同步冲突文件混入仓库等问题。 主要调整: - 新增仓库健康巡检脚本,用于生成轻量巡检报告 - 新增文档链接轻量检查脚本,用于检查旧域名和疑似失效本地链接 - 新增同步临时文件清理脚本,支持 dry-run 预览 - 补充 docs:check:light、docs:health:audit、docs:sync:clean 等维护命令 - 新增轻量检查与维护流程文档,明确日常小改优先使用轻量检查 - 增加 VS Code 工作区排除规则,减少 node_modules、构建产物和同步缓存带来的扫描压力 - 保持日常小改不强制完整构建,只在发布、部署或结构性改动前做完整检查
1 parent 738553f commit 27607d7

5 files changed

Lines changed: 515 additions & 1 deletion

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
title: 轻量检查与维护流程
3+
description: 说明创游世界资料库日常维护时优先使用的轻量检查命令,避免每次小改都触发大范围日期更新或完整构建。
4+
created: 2026-06-08
5+
updated: 2026-06-08
6+
search: false
7+
---
8+
9+
# 轻量检查与维护流程
10+
11+
这份文档用于记录创游世界资料库的日常轻量维护方式。
12+
13+
它的目标不是替代完整构建,而是让日常小改、UI 微调、链接检查、同步文件清理、仓库健康巡检变得更稳定、更省时间。
14+
15+
## 日常小改优先使用
16+
17+
日常小改、文案调整、少量样式优化时,优先运行:
18+
19+
pnpm run quality:check
20+
git diff --check
21+
git status -sb
22+
git diff --stat
23+
24+
如果这次改动主要集中在文档内容,也可以运行:
25+
26+
pnpm run docs:check:light
27+
git diff --check
28+
git status -sb
29+
git diff --stat
30+
31+
## 同步文件清理
32+
33+
清理前先预览:
34+
35+
pnpm run docs:sync:clean:dry
36+
37+
确认没有误删风险后再清理:
38+
39+
pnpm run docs:sync:clean
40+
41+
这个命令只用于清理 Syncthing 冲突文件、临时下载文件、系统缩略图文件和回收站目录,不应该碰源码、Git、node_modules 或构建产物。
42+
43+
## 仓库健康巡检
44+
45+
需要生成轻量巡检报告时运行:
46+
47+
pnpm run docs:health:audit
48+
49+
报告会写入:
50+
51+
docs/维护与报告/仓库健康巡检报告.md
52+
53+
报告里的中低风险问题不一定都要立刻处理,但高风险问题需要优先检查。
54+
55+
## 什么时候再完整构建
56+
57+
日常小改不需要每次都完整构建。
58+
59+
以下情况再运行完整构建或更重的检查:
60+
61+
- 发布发行版前
62+
- 大范围改动目录结构前后
63+
- 修改 VitePress 配置、构建配置或依赖后
64+
- 修改侧边栏、路由、主题入口、CI 配置后
65+
- 部署前做最终确认时
66+
67+
## 提交前建议
68+
69+
提交前至少确认:
70+
71+
pnpm run quality:check
72+
git diff --check
73+
git status -sb
74+
git diff --stat
75+
76+
如果输出没有明显错误,再使用 VS Code 或其他 Git 工具提交。

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@
3333
"docs:domain:check": "node scripts/check-site-domain.mjs",
3434
"docs:css:check": "node scripts/check-css-health.mjs",
3535
"quality:check": "node scripts/run-light-checks.mjs",
36-
"ci": "node scripts/run-ci-checks.mjs"
36+
"ci": "node scripts/run-ci-checks.mjs",
37+
"docs:health:audit": "node scripts/audit-repo-health.mjs",
38+
"docs:sync:clean": "node scripts/clean-sync-temp.mjs",
39+
"docs:sync:clean:dry": "node scripts/clean-sync-temp.mjs --dry-run",
40+
"docs:links:check:light": "node scripts/check-doc-links-light.mjs",
41+
"docs:check:light": "pnpm run docs:ui:check && pnpm run docs:links:check:light && pnpm run docs:health:audit"
3742
},
3843
"name": "cysj-data",
3944
"version": "0.1.0",

scripts/audit-repo-health.mjs

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import fs from "node:fs";
2+
import path from "node:path";
3+
4+
const root = process.cwd();
5+
const reportPath = path.join(root, "docs/维护与报告/仓库健康巡检报告.md");
6+
7+
const skipDirs = new Set([
8+
".git",
9+
"node_modules",
10+
"dist",
11+
".vite",
12+
".cache",
13+
".astro",
14+
]);
15+
16+
const guardFiles = new Set([
17+
"scripts/check-site-domain.mjs",
18+
"scripts/check-doc-links-light.mjs",
19+
"scripts/audit-repo-health.mjs",
20+
"docs/维护与报告/仓库健康巡检报告.md",
21+
]);
22+
23+
const textExts = new Set([
24+
".md",
25+
".mdx",
26+
".ts",
27+
".tsx",
28+
".js",
29+
".jsx",
30+
".mjs",
31+
".mts",
32+
".vue",
33+
".css",
34+
".json",
35+
".yml",
36+
".yaml",
37+
".txt",
38+
".html",
39+
]);
40+
41+
const suspiciousPatterns = [
42+
{
43+
name: "绝对本地路径",
44+
pattern: /[A-Z]:\\|\/storage\/emulated\/0|\/home\/|\/mnt\//i,
45+
},
46+
{
47+
name: "旧域名",
48+
pattern: /cysjdocs\.dpdns\.org|cysjdocs\.pages\.dev/i,
49+
},
50+
{
51+
name: "疑似临时标记",
52+
pattern: /TODO|FIXME||||便|/i,
53+
},
54+
{
55+
name: "冲突文件痕迹",
56+
pattern: /sync-conflict|<<<<<<<|=======|>>>>>>>/i,
57+
},
58+
];
59+
60+
function rel(file) {
61+
return path.relative(root, file).replaceAll("\\", "/");
62+
}
63+
64+
function walk(dir, output = []) {
65+
if (!fs.existsSync(dir)) return output;
66+
67+
for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
68+
const full = path.join(dir, item.name);
69+
const relative = rel(full);
70+
71+
if (item.isDirectory()) {
72+
if (skipDirs.has(item.name)) continue;
73+
if (relative.startsWith("docs/.vitepress/dist")) continue;
74+
75+
walk(full, output);
76+
continue;
77+
}
78+
79+
if (guardFiles.has(relative)) continue;
80+
81+
if (textExts.has(path.extname(item.name).toLowerCase())) {
82+
output.push(full);
83+
}
84+
}
85+
86+
return output;
87+
}
88+
89+
function read(file) {
90+
try {
91+
return fs.readFileSync(file, "utf8");
92+
} catch {
93+
return "";
94+
}
95+
}
96+
97+
function getTitle(text) {
98+
const frontmatterTitle = text.match(/^---[\s\S]*?\ntitle:\s*["']?(.+?)["']?\s*\n[\s\S]*?---/);
99+
if (frontmatterTitle) return frontmatterTitle[1].trim();
100+
101+
const h1 = text.match(/^#\s+(.+)$/m);
102+
if (h1) return h1[1].trim();
103+
104+
return "";
105+
}
106+
107+
const files = walk(root);
108+
const mdFiles = files.filter((file) => path.extname(file).toLowerCase() === ".md");
109+
110+
const stats = {
111+
totalTextFiles: files.length,
112+
totalMdFiles: mdFiles.length,
113+
totalDocsFiles: mdFiles.filter((file) => rel(file).startsWith("docs/")).length,
114+
};
115+
116+
const findings = [];
117+
const titleMap = new Map();
118+
const bigFiles = [];
119+
120+
for (const file of files) {
121+
const relative = rel(file);
122+
const text = read(file);
123+
const size = Buffer.byteLength(text, "utf8");
124+
125+
if (size > 80 * 1024) {
126+
bigFiles.push({ file: relative, size });
127+
}
128+
129+
for (const item of suspiciousPatterns) {
130+
if (item.pattern.test(text)) {
131+
findings.push({
132+
level: item.name === "旧域名" || item.name === "冲突文件痕迹" ? "高" : "中",
133+
file: relative,
134+
issue: item.name,
135+
});
136+
}
137+
}
138+
139+
if (relative.endsWith(".md") && relative.startsWith("docs/")) {
140+
const title = getTitle(text);
141+
142+
if (!title) {
143+
findings.push({
144+
level: "中",
145+
file: relative,
146+
issue: "缺少可识别标题",
147+
});
148+
} else {
149+
if (!titleMap.has(title)) titleMap.set(title, []);
150+
titleMap.get(title).push(relative);
151+
}
152+
153+
const h1Count = (text.match(/^#\s+/gm) || []).length;
154+
if (h1Count > 1) {
155+
findings.push({
156+
level: "低",
157+
file: relative,
158+
issue: `存在 ${h1Count} 个一级标题`,
159+
});
160+
}
161+
162+
if (!/^---[\s\S]*?---/.test(text)) {
163+
findings.push({
164+
level: "中",
165+
file: relative,
166+
issue: "缺少 frontmatter",
167+
});
168+
}
169+
}
170+
}
171+
172+
for (const [title, list] of titleMap.entries()) {
173+
if (list.length > 1) {
174+
for (const file of list) {
175+
findings.push({
176+
level: "低",
177+
file,
178+
issue: `标题重复:${title}`,
179+
});
180+
}
181+
}
182+
}
183+
184+
bigFiles
185+
.sort((a, b) => b.size - a.size)
186+
.slice(0, 20)
187+
.forEach((item) => {
188+
findings.push({
189+
level: item.size > 180 * 1024 ? "中" : "低",
190+
file: item.file,
191+
issue: `文件偏大:${Math.round(item.size / 1024)}KB`,
192+
});
193+
});
194+
195+
const grouped = {
196+
: findings.filter((item) => item.level === "高"),
197+
: findings.filter((item) => item.level === "中"),
198+
: findings.filter((item) => item.level === "低"),
199+
};
200+
201+
const now = new Date().toISOString();
202+
203+
const lines = [
204+
"---",
205+
"title: 仓库健康巡检报告",
206+
"description: 记录创游世界资料库的轻量巡检结果,用于发现旧域名、冲突文件、缺失标题、重复标题、过大文件和潜在维护问题。",
207+
"created: 2026-06-08",
208+
`updated: ${now.slice(0, 10)}`,
209+
"search: false",
210+
"---",
211+
"",
212+
"# 仓库健康巡检报告",
213+
"",
214+
`> 生成时间:${now}`,
215+
"",
216+
"## 巡检摘要",
217+
"",
218+
`- 文本文件数量:${stats.totalTextFiles}`,
219+
`- Markdown 文件数量:${stats.totalMdFiles}`,
220+
`- docs 文档数量:${stats.totalDocsFiles}`,
221+
`- 高风险问题:${grouped["高"].length}`,
222+
`- 中风险问题:${grouped["中"].length}`,
223+
`- 低风险问题:${grouped["低"].length}`,
224+
"",
225+
"## 高风险问题",
226+
"",
227+
grouped["高"].length
228+
? grouped["高"].map((item) => `- \`${item.file}\`:${item.issue}`).join("\n")
229+
: "- 暂无高风险问题。",
230+
"",
231+
"## 中风险问题",
232+
"",
233+
grouped["中"].length
234+
? grouped["中"].slice(0, 80).map((item) => `- \`${item.file}\`:${item.issue}`).join("\n")
235+
: "- 暂无中风险问题。",
236+
"",
237+
"## 低风险问题",
238+
"",
239+
grouped["低"].length
240+
? grouped["低"].slice(0, 120).map((item) => `- \`${item.file}\`:${item.issue}`).join("\n")
241+
: "- 暂无低风险问题。",
242+
"",
243+
"## 后续建议",
244+
"",
245+
"- 优先处理旧域名、冲突文件痕迹、缺少 frontmatter、缺少标题的问题。",
246+
"- 重复标题不一定是错误,但建议检查是否存在内容重复或导航命名不清晰。",
247+
"- 文件偏大不一定需要立刻拆分,但如果阅读体验下降,可以拆为专题页、索引页和细分页。",
248+
"- 本报告只做轻量巡检,不会替代完整构建检查。",
249+
"",
250+
];
251+
252+
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
253+
fs.writeFileSync(reportPath, lines.join("\n") + "\n", "utf8");
254+
255+
console.log(`Repository health report generated: ${path.relative(root, reportPath)}`);
256+
257+
if (grouped["高"].length > 0) {
258+
console.error(`Found ${grouped["高"].length} high-risk issue(s).`);
259+
process.exit(1);
260+
}

0 commit comments

Comments
 (0)