首页
/ 《人生进阶指南》的 SUMMARY.md 导航体系:由单一来源生成的三层目录与一致性校验

《人生进阶指南》的 SUMMARY.md 导航体系:由单一来源生成的三层目录与一致性校验

2026-09-04 10:22:12作者:管翌锬

SUMMARY.md 是《人生进阶指南》(life-level-up-guide) 的全书总目录:10 个分组、63 个条目,把"英语训练、人生复盘、AI 学习、生活系统与九十天行动"组织成一条完整阅读弧线。它并非手工维护,而是由 导航单一来源文件 通过 同步脚本 自动生成,并被 VitePress 配置 排除出站点页面、由 CI 校验与主分支锁定。读完本篇,你能掌握这本书的完整章节结构,以及"单一来源 → 多份 SUMMARY → 站点侧边栏"的生成与防漂移机制。

SUMMARY.md 是什么

SUMMARY.md 位于仓库根目录,采用经典的 Docsify 风格 Summary 文件:一个 # Summary 标题加若干 ## 分组,每个分组下是无序链接列表,链接目标形如 docs/threads/part-1/0-cefr.md。同一份目录还有两个伴生文件:

  • docs/SUMMARY.md:内容与根目录完全一致,只是链接不带 docs/ 前缀(相对 docs/ 目录);
  • docs/en/SUMMARY.md:英文版目录,链接前缀在生成时被去掉 en/

三份文件均由脚本生成,贡献指南维护指南都明确要求"编辑 docs/.vitepress/navigation.mjs,而不是手改 SUMMARY 文件",CI 会拒绝未同步的生成文件进入主分支。

全书结构:10 个分组的完整目录

下面完整继承 SUMMARY.md 的分组与条目结构(链接已保持仓库根目录相对路径),并标注每组对应的书稿职能。

开始(5 条)

入口与阅读契约层,告诉读者从哪里进入、遇到术语怎么办:

第一部:打开输入(8 条)

英语作为"基础能力"的完整训练路径,从基线到七项能力:

第二部:把自己放回生活(7 条)

个人经历与叙事方法论,强调"不把经历写成命运":

第三部:借工具放大能力(6 条)

AI 时代的方法核心:协作、注意力、作品与证据:

注意该组在目录中的排列顺序(1 → 3 → 4 → 5 → 2 → projects)与 navigation.mjs 第 44–53 行的 items 数组完全一致——目录顺序即脚本声明顺序,这也是"改导航要改源码"原则的直观体现。

第四部:实践与恢复(3 条)

第五部:行动与长期改变(1 条)

后记(1 条)

工具箱(17 条)

全书可复制的工作纸模板,是"把方法变成可见证据"的落地层:

旧文归档(5 条)

归档分组在导航中默认折叠(见下文 collapsedGroups),与"旧文只属于历史语境"的定位一致。

词表(10 条)

面向技术场景的英语词块清单:CommonGoJavaJavaScriptPHPPromptPythonSwiftRustVibe Coding

生成机制:navigation.mjs 是唯一事实来源

docs/.vitepress/navigation.mjs 定义了两个导航数组与两个工具函数,是整个目录体系的"单一来源":

1. page() 工厂函数(第 1–5 行)。每个导航条目是一个三元组:

const page = (text, link, source = `${link.replace(/^\//, "")}.md`) => ({
  text,    // 侧边栏/目录里显示的标题
  link,    // VitePress 站点路由,如 /threads/part-1/0-cefr
  source,  // docs/ 下的 Markdown 源文件,默认由 link 推导
});

source 默认由 link 推导(去掉前导 / 并补 .md),只有"目录页指向 README.md"这类例外才显式传入第三个参数,例如 page("归档说明", "/threads/archive/", "threads/archive/README.md")(第 99 行)。词表分组(第 107–113 行)则用 map 批量生成 10 个条目,并把内部名 VibeCoding 显示为 Vibe Coding

2. zhNavigationenNavigation(第 7–114 行、第 116–226 行)。两个数组的分组结构严格平行:中文 10 组对应英文 10 组(Start Here / Part I–V / Afterword / Toolkit / Archive / Word Lists),每组 items 一一对应,这是双语目录"结构不漂移"的源头保证。

3. toSidebar() 与折叠分组(第 228–236 行)toSidebar 把导航数组裁剪成 VitePress 侧边栏形状(只保留 textlink,丢弃 source),并用 collapsedGroups 集合让"工具箱 / 旧文归档 / 词表"(及英文对应名)在侧边栏中默认折叠——这些是参考型内容,不参与线性阅读。

sync-navigation.mjs:先校验,再输出

scripts/sync-navigation.mjs 的完整执行流程是"校验 → 比对 → 写入",任何一步失败都会以非零退出码结束:

第一步:结构校验 validateNavigation(第 27–48 行)。对中文、英文两套导航逐组检查:

  • 每个分组必须有 textitems 数组,否则抛出"导航分组缺少 text 或 items";
  • 每个条目必须有 textlinksource 三个字段,否则报"导航条目字段不完整";
  • seenLinks 集合检测重复 link,重复即失败;
  • resolve(DOCS, item.source) + existsSync 确认每个 source 真实存在,且必须落在 docs/ 目录内(防止路径逃逸)。

第二步:覆盖性双向校验 validateNavigationCoverage(第 53–69 行)。这是防止"孤岛页面"的关键设计:

  • markdownSources()(第 12–25 行)递归遍历 docs/,收集所有公开 Markdown——跳过 .vitepresspublicassets 三个目录,并显式跳过 SUMMARY.md 自身(避免把生成物当内容收录);
  • 正向检查:每个公开 Markdown 必须被中/英文导航之一收录,否则报"公开 Markdown 未被导航收录";
  • 反向检查:每个导航 source 必须是公开 Markdown,否则报"导航 source 不属于公开 Markdown"。

双向对齐意味着:新增一篇正文后若忘了登记导航,CI 会立刻失败;导航里写错路径,也会在 --check 阶段被拦下。

第三步:生成三份输出(第 85–89 行)summary()(第 73–83 行)把导航数组渲染为 Docsify 风格文本(# Summary + 每组 ## 标题 + 链接列表 + 空行分隔),三个输出目标的前缀处理各不相同:

const outputs = new Map([
  [join(ROOT, "SUMMARY.md"), summary(zhNavigation, "docs/")],
  [join(ROOT, "docs/SUMMARY.md"), summary(zhNavigation)],
  [join(ROOT, "docs/en/SUMMARY.md"), summary(enNavigation).replaceAll("(en/", "(")],
]);

这解释了上文观察到的路径差异:根目录版本面向仓库根,docs/ 版本面向站点源目录,英文版本则把 (en/... 归一化为 (...

第四步:幂等写入与 --check(第 91–105 行)。逐文件比对现有内容与期望内容,仅在差异时写入并打印 updated <路径>;带 --check 参数时只报错不写盘,且发现差异时 process.exit(1);全部一致时打印 "navigation summaries are in sync"。这正是 package.json 中两个脚本的分工:

  • "check:navigation"node scripts/sync-navigation.mjs --check && node scripts/sync-word-lists.mjs --check && node scripts/sync-public-assets.mjs --check(CI 门禁模式);
  • "sync":依次运行 sync-navigation.mjssync-word-lists.mjssync-public-assets.mjssync-readme.mjs(开发者同步模式,同时刷新根 README.md 镜像、英文词表镜像与 public 分享图)。

VitePress 侧的接入方式

docs/.vitepress/config.mts 展示了 SUMMARY 体系如何与站点构建解耦:

  • srcExclude: ["SUMMARY.md", "en/SUMMARY.md"](第 71 行):两份 SUMMARY 文件被排除在站点页面之外——它们是"给脚本和读者当索引用的文件",不是可访问路由。英文那份 docs/en/SUMMARY.md 因不在 docs/ 直接子路径的排除名单内实际仍可被构建,但根与 docs/ 两份 SUMMARY 明确不参与渲染;
  • sidebar: toSidebar(zhNavigation)(第 101 行,英文侧第 125 行):侧边栏直接复用同一份导航数组,保证"侧边栏 = 目录 = SUMMARY 文件"三者同源;
  • cleanUrls: truelegacyHashRedirect(第 54–62 行):旧 Docsify 时代的 #/path hash 路由会被一段内联脚本重定向到 /up/ base 下的干净路径——这是对站点从 Docsify 迁移到 VitePress 的兼容处理;
  • transformPageData(第 154–165 行)把 frontmatter 的 updated 字段映射为 lastUpdated 时间戳,与内容校验规则(下文)形成闭环。

CI 门禁:SUMMARY.md 被哪些检查覆盖

SUMMARY 文件虽然"简单",但在仓库校验层中出现在至少四处:

  1. 导航一致性npm run check:navigationsync-navigation.mjs --check,三份 SUMMARY 必须与 navigation.mjs 的生成结果逐字节一致;
  2. 陈旧内容扫描scripts/check-content.mjscheckStaleStrings 把根目录 SUMMARY.md 也纳入扫描范围(第 405–412 行),命中 #/(残留 Docsify hash 路由)、失效网盘/视频链接等 STALE_PATTERNS(第 278–298 行)即报错;
  3. frontmatter 豁免checkFrontmatterendsWith("SUMMARY.md") 的文件直接返回(第 144 行)——生成文件没有 title/description/updated frontmatter 是预期行为,不应触发"缺少 frontmatter"错误;
  4. Markdown 格式.markdownlint-cli2.mjs 对全部 *.md 运行 markdownlint-cli2(default: true,放宽行宽 MD013 等),并忽略 docs/.vitepress/dist/ 等构建产物目录。

完整的本地校验链是 npm run check,它串联 check:navigationcheck:readme(根 README 与 docs/README.md 镜像同步)、check:content(链接可达性、alt 文本、双语对偶页、标题层级与 updated 日期一致性、图片 EXIF/GPS 元数据、孤儿资产等)与 check:format。环境前提在 package.json 中明确:"engines": { "node": ">=24 <25" },配合 .nvmrc/.node-version 使用 Node 24;首次安装用 npm ci 按锁文件精确安装。

历史脉络:从 Docsify 到 VitePress 的 SUMMARY 传承

legacy/docsify-snapshot.md 说明:VitePress 迁移前的完整 Docsify 站点保留在 Git 提交 42e6faa,其中包含原始 docs/index.html 与导航,可用 git show 42e6faa:docs/index.html 只读查看,或从该提交创建临时分支部署。这解释了为什么仓库沿用 Docsify 风格的 SUMMARY.md 命名:迁移时保留了既有的目录文件格式与阅读心智,只把渲染引擎换成了 VitePress,并顺手把"手写导航"升级为"脚本生成 + CI 锁定"。维护指南中的"新增章节先判断读者任务和证据类型,再同步更新 docs/.vitepress/navigation.mjs、中英文入口和 frontmatter",就是这条传承链上的操作规范。

实操:查看、构建与本地预览

以下命令均按 MAINTENANCE.mdCONTRIBUTING.md 的记录给出(仓库为只读资料,此处仅说明使用方式):

# 1. 环境准备:Node 24,按锁文件安装
nvm use
npm ci

# 2. 本地开发服务器(VitePress dev,docs/ 为源目录)
npm run docs:dev

# 3. 生产构建 / 本地预览构建产物
npm run docs:build
npm run docs:preview

# 4. 一致性校验(CI 同款门禁,只检查不写盘)
npm run check:navigation   # 三份 SUMMARY + 词表镜像 + public 资产
npm run check             # 导航 + README 镜像 + 内容 + 格式,全量校验

# 5. 端到端烟测(Playwright)
npm run test:smoke

# 6. 仅在修改了 navigation.mjs / 中文首页 / 中文词表之后:重新同步生成文件
npm run sync

典型工作流是:改 docs/.vitepress/navigation.mjs → 跑 npm run sync 生成三份 SUMMARY 与 README 镜像 → 跑 npm run check 确认链接、双语对偶、frontmatter 与格式全部通过 → npm run docs:build 验证可构建。任何一步的失败信息都直接指向具体文件与行号,便于定位。

小结

SUMMARY.md 表面上是一份 63 条链接的书目,实质是"《人生进阶指南》阅读地图"的规范化表达:10 个分组对应序章、五部正文、后记、工具箱、归档与词表,与 docs/README.md 的"书稿主线"表格、VitePress 侧边栏完全同构。它由 navigation.mjs 单一来源生成,经 sync-navigation.mjs 的结构校验、双向覆盖校验与幂等写入产出三个副本,再被 check-content.mjs 的陈旧内容扫描与 markdownlint 共同看护。理解这套机制,就能同时回答两类问题:作为读者,"这本书讲什么、按什么顺序读";作为维护者,"新增一篇正文后,要让哪些文件一起变、跑哪些命令才算安全"。

登录后查看全文
热门项目推荐
相关项目推荐