构建探针

概念·build-probe

一类检查:不看源码,只看构建出来的产物。

它和单元测试的分工很清楚:

问的问题够不着的地方
单元测试这个函数算得对吗它有没有被接进管线
构建探针产物里真的是这样吗为什么

为什么需要它#

因为有一整类 bug,单元测试永远抓不到:

  • 配置写了但没接线(@astrojs/mdx 装了却没注册进 integrations)
  • 文件写好了但没人引用(fonts.css 从头到尾没被 import)
  • 处理器换了 API(老写法被接受但不执行,只发一条弃用警告)
  • 元素渲染了两遍(所有断言都问「存在吗」,没有一条问「出现几次」)

它们的共同点是:不报错、构建成功、测试全绿,只是没效果。

这个项目里的几条#

  • 内容发布探针:真的放一个 .md 与 .mdx 进去,构建,看能不能产出页面; 再放入草稿与占用 /about/ 的文章,确认前者不会泄漏、后者会明确阻断构建。 读配置不够——配置可以是对的而接线是断的
  • 链接渲染契约:正文里的 [[方括号]] 是不是真的变成了链接
  • 字体加载契约:@font-face 有没有进产物、字体总量有没有失控
  • 区块唯一性:同一区块在一页里不得出现两次
  • 子路径契约:六类载体(属性 / 脚本内路径 / 绝对 URL / 纯文本产物 / 内容清单 / NDJSON 全量导出)的 前缀是否都正确

一条纪律:新检查必须反向验证#

把 bug 放回去,确认它真的报错、退出码为 1。

这不是形式主义。这个项目里出现过三次「检查本身是坏的」, 而且失效方向是相反的:

  • 漏检:一条检查因为 readdir 在别的作用域、抛异常被 catch 吞掉, 扫了 0 个文件却报告「通过」
  • 漏检:一条检查只匹配 href="/x" 形式的相对绝对路径, 产物里 https:// 开头的 URL 全部不进正则——于是「前缀翻倍」 这个致命问题在它的注视下安然通过
  • 误报:还是那条检查。它后来改成对整段 HTML 跑正则来找属性, 于是正文里的代码示例也被当成链接——就是上一段里 href="/x" 那个写法(它此刻正作为一个反例出现在这里)。修法是把正则锚定到 标签上,只认真正的属性:<[^>]*\s(?:href|src)="…"

误报和漏检一样有害:一条会红的检查,人第一反应是关掉它。 对模板尤其致命——使用者写一篇提到 href="/foo" 的文章,构建就失败, 而失败原因和他的文章毫无关系。

一条从没失败过的检查不值得信任; 一条会无缘无故变红的检查,同样不值得信任。

覆盖面要跟着「载体」走#

同一个地址可能出现在六类地方,而每类的写法不同:

  1. href / src 属性中的相对绝对路径
  2. <script> 块里拼出来的路径(模板字符串拼的 import() 参数)
  3. https:// 开头的绝对 URL(og:image、RSS 发现、JSON-LD)
  4. 纯文本产物里的链接(llms.txt、robots.txt)
  5. JSON 内容清单里的结构化 URL
  6. NDJSON 全量导出里每条记录的站点地址与内容 URL

每漏一类,就漏掉一整类 bug。 这些覆盖不是预先画出来的清单, 而是每次遇到真实漏检后补进契约的。

相关条目:reproducible-builds、letterpress。

已复核 · 2026-09-24

「已复核」指有人在上面那个日期读过这一页并确认了当时的正文。它不表示内容一定正确,也不表示每条引用的原文都逐句支持了这里的结论—— 后者只能由人判断。正文在那之后若被改动,复核会自动失效,构建会拦下来。

这一页也有 markdown 版本:/letterpress/wiki/build-probe.md
直接打开上面的 .md 链接即可读取;在支持内容协商的部署平台上, 也可以对页面 URL 发送 Accept: text/markdown。