Skip to content

wikilink 插件

自研的 VitePress 插件,把 Obsidian 风格的 <span class="wikilink-dead" title="未找到: 页面名" style="color:#c33;border-bottom:1px dashed #c33">页面名</span> 双链语法,在静态站构建时自动转成 VitePress 可点击的内部链接。

源码位置:/opt/wiki-site/docs/.vitepress/wikilink-plugin.mjs(部署在 ECS)

它解决什么问题

<span class="wikilink-dead" title="未找到: wikilink" style="color:#c33;border-bottom:1px dashed #c33">wikilink</span> 是 Obsidian 的核心语法,让人写笔记时只需要管"链到哪页"而不管"具体 URL"。但 VitePress 默认不认这个语法,会把 [Hermes Agents](/entities/Hermes%20Agents) 当成普通文字渲染。

如果不解决,团队就要在两个语法之间二选一:

  • <span class="wikilink-dead" title="未找到: xxx" style="color:#c33;border-bottom:1px dashed #c33">xxx</span> → Obsidian 可点,wiki.86lux.net 死链
  • [xxx](xxx.md) → wiki.86lux.net 可点,Obsidian 失去图谱视图

插件的存在让两边都可点,Obsidian 与 VitePress 双兼容

它怎么工作

markdown 输入:
  本系统由 [Hermes Agents](/entities/Hermes%20Agents) 消费 [GitHub](/entities/GitHub) 上的 wiki。

插件运行时(VitePress build 阶段):
  1. 扫描整个 docs/ 目录,建一个 "页面名 → URL" 映射表
     例:Hermes Agents → /entities/Hermes%20Agents
         GitHub        → /entities/GitHub
  2. 对每个 markdown 文件,正则匹配 <span class="wikilink-dead" title="未找到: xxx" style="color:#c33;border-bottom:1px dashed #c33">xxx</span> 或 <span class="wikilink-dead" title="未找到: xxx" style="color:#c33;border-bottom:1px dashed #c33">alias</span>
  3. 替换为 markdown 内联链接:
     [Hermes Agents](/entities/Hermes%20Agents) → [Hermes Agents](/entities/Hermes%20Agents)
  4. URL 路径段做 encodeURIComponent,保证含空格/中文的页名也能正确生成 URL
  5. 找不到的 wikilink 渲染为带红色虚线的 <span class="wikilink-dead">,
     visual flag 提示作者"这是个死链"

最终 HTML:
  <p>本系统由 <a href="/entities/Hermes%20Agents">Hermes Agents</a>
     消费 <a href="/entities/GitHub">GitHub</a> 上的 wiki。</p>

关键设计

  • 构建时一次性扫描:不是运行时算,对静态站性能零影响
  • basename 兜底:找不到精确路径时降级为按文件 basename 查找
  • 死链不挂掉构建:用红虚线视觉提示,但不报错,作者改完再 build
  • URL 编码兼容:含空格的页名(如 Hermes Agents)会被编码为 Hermes%20Agents,VitePress markdown 渲染才能正确生成 <a> 标签

调试历史

部署当天踩过两个坑:

坑 1:build 失败 "Element is missing end tag"

某份 markdown 文件里有 <rel><分类> 这种文本占位符,被 Vue 的 markdown 编译器误认为 HTML 标签,没找到闭合。

修复:fix-angle-brackets.py 把所有 <xxx> 字面占位符替换为 〈xxx〉(中文角括号),既保留语义又不被 HTML 解析器吞。

[Hermes Agents](/entities/Hermes%20Agents) → 插件正确转出 [Hermes Agents](Hermes Agents),但 markdown 渲染器看到 URL 里有空格,不当成链接。

修复:在插件里对 URL 路径段调用 encodeURIComponent

javascript
const safeUrl = url.split('/').map(s => s ? encodeURIComponent(s) : s).join('/')
return `[${display}](${safeUrl})`

它不是什么

  • ❌ 不是 VitePress 官方插件(自研,不在 npm 上)
  • ❌ 不替代 markdown-it 的 link 解析(只在 link 解析之前做替换,把 wikilink 转成标准 markdown link 后让 markdown-it 自己处理)
  • ❌ 不影响 Obsidian 的渲染(Obsidian 自己有原生 wikilink 支持,与本插件互不干扰)

关联

TeamOS · docs as code · canon=权威知识,drafts=候选区