首页
/ Supabase 文档应用工程方向指南:apps/docs 的"Docs-only、收缩面、Markdown 一对一保真"三大原则与落地规范

Supabase 文档应用工程方向指南:apps/docs 的"Docs-only、收缩面、Markdown 一对一保真"三大原则与落地规范

2026-09-06 19:11:58作者:裘晴惠Vivianne

本文基于 Supabase monorepo 中 apps/docs(官方文档站)的工程方向文档 .agents/skills/ask-the-docs/reference/docs-app-direction.md 撰写,系统梳理该文档站"Docs-only 项目、持续收缩表面、Markdown 一对一保真"的三条重构愿景、工作规范,以及对新功能开发的五条硬性约束,并结合 internals/generate-guides-markdown.ts 等源码给出可验证的实现依据。读完本文,你将能在为 Supabase 文档站贡献内容或组件时,正确选择"最小持久足迹"的实现形态,避免踩中联邦内容、搜索、Sentry 等已知脆弱区域。

文档定位:一份写给"要动 apps/docs 的人"的方向声明

docs-app-direction.md 位于 Agent 技能库 ask-the-docs 的参考资料中(文档原文),回答的核心问题是:文档应用往哪里走,以及新工作应该与什么对齐。它明确界定了自己与其他参考文档的分工:

理解这份文档的关键在于:它不是架构说明书,而是评审标准——任何触碰 apps/docs 的变更提案,都将被置于下述愿景与规范之下衡量。

重构愿景一:Docs-only project(只做文档的项目)

方向文档给出的第一条长期目标:文档项目的职责应当收缩为"只处理文档",其他功能(如特定 API 能力)应迁移到子项目中。

这一点在当前仓库结构中有直接对应。从源码结构看,apps/docs 虽然名义上是文档应用,但历史上承载了不止渲染内容的职责:apps/docs/package.json 中的 prebuild 链在构建前串起 GraphQL codegen、参考文档 codegen、示例拷贝、联邦内容抓取、Markdown 导出、gz 归档生成等一串步骤(见 package.jsonprebuild 脚本),其中 codegen:graphql 会经由 resources/ 目录下的脚本维护 /api/graphql 端点——这类"应用内 API"正是方向文档所说的"应迁移到子项目"的功能类型。

对开发者的实际含义:评审时如果一个改动让文档站"顺带"多承担一类非文档职能,它与方向相悖。

重构愿景二:持续的表面收缩(Ongoing surface reduction)

方向文档指出:大量遗留关键代码已被重构,剩余工作的方向是继续减少表面(surface area)而不是增加它

这里的"表面"在配套的 adding-features.md 中被进一步定义为一种"成本透镜":每个变更要么增加 Reach(用户可见价值),要么增加 Surface(需要持续维护的代码、配置、词汇量)。当设计讨论僵持时,应重新表述为"这个用户可见的改进是否值得其维护成本",若不能自信地回答"值得",应先削减范围而不是为设计辩护。

与这个愿景相呼应的具体约束包括(均来自 adding-features.md 的"选择最小可行形态"一节,按偏好降序):

  1. 纯内容变更(MDX 编辑、partial、数据文件),无新代码;
  2. 配置既有组件——给现有原语传新 prop、扩展配置对象;
  3. 新增"被既有组件消费的数据形态"——一个由已注册 MDX 组件读取的带类型 *.data.ts 模块;
  4. 薄的组合组件——小文件编排 <Link><GlassPanel><Heading> 等既有原语,只在 MDX 组件映射表中增加一条注册;
  5. 设计系统中新增原语——最后手段,需要对照 packages/ui / ui-patterns 论证必要性。

此外,adding-features.md 还给出了一份"先盘点再动手"的对照表:渲染 MDX 自定义组件先看 features/docs/MdxBase.shared.tsx 的组件映射;组件的 Markdown 导出看 internals/generate-guides-markdown.ts 的 schema 注册表与 internals/markdown-schema/ 下的 handler;可复用内容块看 <$Partial path="..." />content/_partials/;内部/外部链接逻辑看 lib/internal-links.tswithDocsBasePath / addBaseUrlPrefix;构建步骤看 package.jsonprebuild / postbuild 链;CI 检查看 .github/workflows/ 下既有工作流——"能扩展现有管道就不新增管道"。

重构愿景三:Markdown 一对一保真(One-to-one markdown fidelity)

这是三条愿景中最技术化的一条:对 generate-guides-markdown 的改进目标是渲染出的 guide 页面与面向 LLM 的 markdown 导出之间形成精确对应。任何在 MDX 中渲染的新组件,都应该能以相同语义序列化为 markdown。

这条愿景不是空话,仓库中已有完整的实现骨架,可以从源码直接验证:

1. 统一的处理函数契约。 generate-guides-markdown.ts 中定义了:

/**
 * A handler converts a single MDX component into a markdown string. ...
 * Any component not in the schema is treated as `({ children }) => children`,
 * i.e. the wrapper is dropped and its children are kept as-is.
 */
type ComponentHandler = (ctx: { props: Props; children: string; node: JsxNode }) => string

每个 handler 接收 { props, children, node }——props 来自 JSX 属性、children 是已递归序列化的子节点 markdown、node 是需要检查 AST 结构时的逃生舱。

2. 按组件命名的 handler 目录。 apps/docs/internals/markdown-schema/ 下目前已有 28 个 handler 文件(Accordion.tsAdmonition.tsContentListings.tsErrorCodes.tsPanel.tsTabPanel.ts 等),文件名与 JSX 元素名严格一致——这正是 adding-features.md 中"文件命名与导出名匹配"约定的落地。所有 handler 在 generate-guides-markdown.ts 顶部统一 import 并汇入 SCHEMA 对象,构成单一注册点。

3. 保真的已知缺口(aspirational, not enforced)。 known-issues.md 明确指出"一对一保真目前是理想目标而非强制约束",并列出三类实际缺口:

  • 没有 markdown-schema handler 的 MDX 组件会被默认 handler 解包(unwrap)为其子节点——从上面源码可以看到默认实现就是 const defaultHandler: ComponentHandler = ({ children }) => children,而解包结果可能与实际渲染输出不一致;
  • <$Partial> 的递归内联是静默的——缺失的 partial 直接被丢弃(inlinePartials 中对读取失败 catch 后不报错,见 generate-guides-markdown.ts);
  • 交互式、JSX 表达式密集的组件本质上无法被 markdown 忠实表达。

由此得出方向文档规则 2 的可操作版本:任何改变 guide 渲染方式的改动,要么同步改变其 markdown 序列化方式,要么能清楚说明为什么不用;而"要出现在 markdown 输出中的新组件,必须永远在 internals/markdown-schema/ 添加 handler 并在 generate-guides-markdown.ts 注册"。

4. 双管道共享同一数据注册表。 从源码结构看,同一内容需要渲染两次(MDX 运行时输出 HTML + markdown 导出),apps/docs 的负载级规则是两条管道解引用同一个 ID 键控的数据注册表:MDX 组件通过 id prop 读数据,markdown handler 用同一个 id 读同一份数据,zod schema 作为唯一真源。参考实现是 ContentListings(数据在 apps/docs/data/content-listings/,运行时组件在 apps/docs/components/ContentListings/,markdown handler 在 ContentListings.ts)。

工作规范(Working norms)

方向文档的第二部分给出三条日常工作规范:

1. 在 apps/docs/ 内工作,而不是仓库根目录。 理由更简单:终端命令更短、调试范围更清晰,且多数 docs 命令假设你已经 cd 进应用目录。从 package.json 可以看到印证:lint:mdxsupa-mdx-lint content)、build:guides-markdowntest:local:unwatch 等都是应用内命令,与根目录的 pnpm lint --filter=docspnpm typecheck 形成两级体系。

2. 鼓励使用 AI。 官方明确鼓励用 AI 解释复杂或遗留代码段落,并认为其对机械性、非创造性任务有效。这一条的元含义是:这份 ask-the-docs 技能参考集本身就是为 LLM/Agent 消费而写的一手资料。

3. 历史上下文。 对早于当前重构波次的决策,需咨询任职时间更长的维护者;而当前大多数代码有清晰的溯源(provenance)——这也是"表面收缩"策略的直接收益:重构后的代码比遗留代码更容易被新人和 Agent 安全地修改。

对新工作的五条约束(What this means for new work)

这是方向文档的执行核心。任何触碰 apps/docs 的变更提案,应逐条对照:

1. 复用既有接缝,先于新增接缝

"Markdown 导出、MDX 管道、lint、codegen、federated fetch 都是新表面会被挑战的地方。" 这里的"接缝"在 app-map.md 中有完整地图,结合 package.json 的脚本即可落地理解各接缝的真实位置:

接缝 源码位置
Markdown 导出 internals/generate-guides-markdown.ts(guides)、internals/generate-reference-markdown.ts(reference),由 build:markdown 串联
MDX 运行时管道 features/docs/(页面骨架与 MdxBase 渲染器)、components/(MDX 内组件)
Lint supa-mdx-lint(MDX 内容 lint,配置见 supa-mdx-lint.config.toml,规则集见 supa-mdx-lint/ 目录)、ESLint
Codegen codegen:graphql / codegen:references / codegen:examplesprebuild 链)
Federated fetch lib/octokit.tsgetGitHubFileContents(),经 GitHub App 凭据(DOCS_GITHUB_APP_*)抓取外部仓库 markdown

对应动作是"写代码前先盘点"("Inventory before you write"):上面表格中的每一项,若已存在近似方案,默认是扩展它,而不是在旁边另建一个。

2. 把 markdown 保真纳入设计

如前所述,改变 guide 渲染就必须考虑 markdown 序列化。具体新增一个"需要在 markdown 中表达"的组件的标准流程是(来自 app-map.md "The two pipelines"):

  1. apps/docs/components/ 写 React 组件;
  2. apps/docs/internals/markdown-schema/<SameName>.ts 添加同名 handler;
  3. generate-guides-markdown.tsSCHEMA 对象中注册;
  4. 若组件是纯视觉、应当从 markdown 中丢弃,则省略 handler——导出器会自动把未知 JSX 解包为其 children。

第 4 条与源码中的 defaultHandler 行为一一对应,也是"保真缺口"的来源:省略 handler 意味着该组件的"导出语义"隐式等于"其 children",需要开发者确认这与渲染语义一致。

3. 不要构建在坏掉的部分上

方向文档点名三处"in flux"的设施:搜索、Sentry 埋点、联邦链接处理。新特性不应依赖它们当前的形态。known-issues.md 给出了各自的细节依据:

  • 搜索:依赖独立的脚本与 embeddings(scripts/search/generate-embeddings.ts 一类),而非整合后的 markdown 文件(build:guides-markdown 输出),与主 markdown 生成管道解耦;结论是"不要基于当前搜索基础设施构建新特性,预期它会在重构中被重写";
  • Sentry:slug/404 类错误多由爬虫扫描路径、遗留 URL、站外失效入链造成,错误追踪体系相对年轻;结论是"不要在没有本地复现、referrer 核查等佐证前,把嘈杂的 Sentry 信号当作真实 bug 的证据";
  • 联邦链接处理pageMap 是手工维护的——上游改名/删除文件会导致断链,直到维护者更新本仓库的路由文件;每个联邦 section 各自实现 urlTransform,逻辑互不相同;wrappers 固定到 docs_v*.*.* 发布 tag,上游打 tag 失败会直接让构建抛错。

4. 不要扩展技术债务

方向文档列举的三笔既有债务:组件散落(component scatter)、并行管道(parallel pipelines)、自造渲染路径(bespoke render paths)——只能消化,不能追加。仓库内的证据:

  • 组件散落:apps/docs 的 React 组件分散在 components/features/docs/features/ui/layouts/ 等多个目录,没有"这就是文档组件"的规范位置;规范做法是"跟随最近的同类先例放置新组件,而不是发明新位置",且不要试图在功能 PR 里顺手重构散落;
  • 自造渲染路径:SDK 与 CLI 参考页(JavaScript SDK、CLI 等)不能走标准 MDX 管道——历史上曾尝试迁移,但页面长度超出预览环境中 AST 解析器的内存上限而失败,它们改由 spec/ 驱动、输出到 features/docs/generated/**。方向文档因此警告:在解决内存上限之前,不要提议"把所有参考页统一到 MDX 下";同时新参考内容的默认形态应是更小、模块化的页面,而非继续加长。

5. 只重构功能所必需的部分

把无关清理塞进功能 PR 会模糊评审焦点,且"反正也会被砍掉";若清理确实有价值,请另开独立的 refactor PR。配套的执行细则在 adding-features.md 的"Pre-flight"清单中:提交 PR 前确认已盘点既有原语/管道、选择了最小可行形态、共享内容的多管道渲染一致、文件与导出名匹配、无不必要 helper/import/alias、排版委托给共享原语、客户端与 MDX 代码没有 import internals/internals/ 专供构建期 markdown 生成,双侧共用的函数应放 lib/)、diff 只含与功能相关的改动,且 pnpm formatpnpm typecheck 与相关 lint job 全部通过。评审预期是多轮、逐层加深的,这是通往最小表面设计的路径,而非吹毛求疵。

联邦文档:理解"负债"与"方向"的最佳切面

五条约束中最抽象的"不要构建在坏掉的部分上",以联邦内容为例最具体。结合 federated-docs.mdapps/docs构建/请求时从七个外部仓库抓取 markdown 并渲染进原生文档外壳:pg_graphql/guides/graphql/*)、vecs/guides/ai/python/*)、setup-cli(CI 章节)、terraform-provider-supabase(部署/terraform 章节)、wrappers(数据库扩展,混合模式,固定到 docs_v*.*.* release tag)、splinter(advisors 动态列表)、agent-skills。抓取层使用 GitHub App 认证(DOCS_GITHUB_APP_IDDOCS_GITHUB_APP_INSTALLATION_IDDOCS_GITHUB_APP_PRIVATE_KEY),默认按天重新验证,抓取失败最多重试 5 次。

维护者的立场是明确的"known liability"(已知负债):不要在没有强力理由时新增联邦源,不要把联邦模式扩展到新的内容类型。判断"该联邦还是该复制进 content/guides/"的标准是:若规范文档在别的仓库且高频变化、由那个仓库的维护者拥有内容生命周期,则联邦;若需要编辑控制、一致的语气、链接稳定性,或与 Supabase 产品 UI 和交叉链接紧密耦合,则放进 content/guides/

验证与自查入口

落实上述规范时,可用的验证命令(本地/根目录两级,见 app-map.mdpackage.json):

命令 执行位置 用途
pnpm format 仓库根 Prettier,开 PR 前必跑
pnpm typecheck 仓库根 跨包 TS 检查
pnpm lint --filter=docs 仓库根 apps/docs 的 ESLint
pnpm lint:mdx apps/docs 全量 content/ 树的 MDX lint(supa-mdx-lint)
pnpm build --filter=docs 仓库根 包含 markdown 生成,失败会阻塞发布
pnpm test:local:unwatch <path> apps/docs lib/data/ schema 的 Vitest 套件,需先本地起 Supabase 并重置数据库

结语

apps/docs 的方向文档把一套"反膨胀"工程价值观写成了可执行标准:项目职责向文档收敛、代码表面只做减法、HTML 与 LLM-facing markdown 两条管道必须同步保真。对贡献者而言,最实用的三句话是:新工作先盘点既有接缝再动手;任何改变渲染的改动都要交代 markdown 序列化;搜索、Sentry、联邦链接处理与参考页内存上限是当前不可依赖、不可扩展的区域。这份方向声明与 app-map.mdadding-features.mdknown-issues.mdfederated-docs.md 共同构成 Supabase 文档站维护的完整参考体系,可作为文档基础设施类项目的治理文档范本。

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