首页
/ Supabase Docs 文档站开发指南:本地环境搭建、AI 友好的 Markdown 生成管线与可访问性测试

Supabase Docs 文档站开发指南:本地环境搭建、AI 友好的 Markdown 生成管线与可访问性测试

2026-09-06 12:48:14作者:秋泉律Samson

本文以 apps/docs/DEVELOPERS.md 为主线,完整覆盖 Supabase 官方文档站(supabase.com/docs)的本地开发环境搭建、构建与代码生成管线、面向 AI/Agent 的 Markdown 产物生成机制、基于 axe-core 的 WCAG 可访问性扫描,并结合当前仓库的脚本定义、生成器源码与中间件实现,说明每一步命令背后的真实执行链路,读完即可独立完成文档站的本地运行、Markdown 生成验证与内容质量检查。

一、docs 站在仓库中的位置

文档站 apps/docs 是一个 Next.js(App Router)+ MDX 站点,部署在 basePath /docs 下,本地开发服务器默认端口为 3001。整个 monorepo 采用 Turborepo 组织,各站点可共享 packages/ 下的组件与配置。根目录 DEVELOPERS.md 给出了各站点本地开发端口一览:

站点 目录 包名 本地地址
supabase.com apps/www www http://localhost:3000
Studio apps/studio studio http://localhost:8082(需 Docker)
supabase.com/docs apps/docs docs http://localhost:3001/docs

apps/docs/DEVELOPERS.md 的定位是:通用环境搭建(依赖安装、Turborepo 用法等)以根目录主文档为准,该文档只聚焦 docs 站自身的特殊步骤。

二、前置依赖与工具链

按根 DEVELOPERS.md 的 "Install dependencies" 章节,需要准备:

  • Git
  • Node.js:版本以仓库声明为准;当前 package.jsonpackageManager 字段声明为 pnpm@11.13.1
  • pnpm:仓库强制使用(apps/docs/package.jsonpreinstall 脚本为 npx only-allow pnpm
  • make(或各操作系统下与 build-essentials 等价的工具)
  • Docker:仅在需要本地运行 Studio 时才必需;纯文档站开发可以不装

社区贡献者 fork 仓库后按常规 git clone + 根目录 pnpm install 安装依赖即可;Supabase 内部员工则直接在主仓库开分支提交 PR,不做 fork,以便 CI 自动触发、加快评审。

三、本地运行文档站(完整步骤)

apps/docs/DEVELOPERS.md 给出的五步流程,逐条对照仓库实现展开如下:

1. 完成主文档的 Local Development 流程:即在仓库根目录 pnpm install 安装全部 workspace 依赖。

2. 准备环境变量,二选一:

  • Supabase 内部员工:在 apps/docs 目录执行 pnpm run dev:secrets:pull,把内部环境变量写入 .env.local。对照 apps/docs/package.json,该脚本实际为 AWS_PROFILE=supa-dev node ../../scripts/getSecrets.js -n local/docs,即通过 AWS Secrets Manager 拉取 local/docs 命名下的密钥;

  • 社区成员:手动创建 apps/docs/.env.local,写入一行:

    NEXT_PUBLIC_IS_PLATFORM=false
    

    该标记用于关闭"平台模式",让本地站点不请求线上平台接口。

3. 启动本地站点:进入 apps/docs 执行 pnpm run dev,或在仓库根目录执行等价的 pnpm dev:docs(根 package.json 中定义为 turbo run dev --filter=docs --parallel)。

这一步实际比"跑一个 Next.js dev server"复杂得多,从 apps/docs/package.json 的脚本定义可以看到完整链路:

"predev": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples",
"dev": "run-p --race dev:next dev:watch:troubleshooting",
"dev:next": "next dev --port 3001"
  • predev 先做代码生成:codegen:graphql(拉取 GraphQL schema 并用 graphql-codegen 生成类型)、codegen:references(从 spec/ 目录的 JSON schema 生成 Reference 文档内容,含 legacy 与新管线两条路径)、codegen:examplesshx cp -r ../../examples ./examples 把仓库根部的 examples/ 示例工程复制进 docs 供页面引用);
  • devrun-p --race 并行启动 next dev --port 3001dev:watch:troubleshootingnode ./scripts/troubleshooting/watch.mjs,监听 troubleshooting 条目变化);
  • 生产构建同理:prebuild 串联 codegen:graphqlcodegen:referencescodegen:examplesbuild:federated-content(抓取联邦文档)、build:markdown(guides + reference 两类 Markdown 产物)、build:gz-archivepostbuild 再执行 build:sitemap 与静态资源上传脚本。

4. 访问 http://localhost:3001/docs —— 必须带上 /docs 后缀。原因是 apps/docs/next.config.mjs 设置了 basePath: process.env.NEXT_PUBLIC_BASE_PATH || '/docs',且站点页面扩展名为 ['ts', 'tsx', 'js', 'jsx', 'md', 'mdx'];配置中还有针对 dev/preview 环境的 redirects,将根路径 / 重定向到 /docs、把 /dashboard/*/blog/* 重定向到线上域名。apps/docs/AGENTS.md 也特别强调:裸 / 会 404,必须访问 /docs

5. 验证:本地站点视觉与线上一致即环境搭建完成。文档内容主目录是 apps/docs/content/(guides 与 troubleshooting 的 MDX 源文件均在其中)。

四、AI 友好的文档:每页 Guide 生成一个 Markdown 文件

这是 apps/docs/DEVELOPERS.md 中最有技术含量的一节:文档站会为 /docs/guides/.. 路径下的每个页面生成一份纯 Markdown 文件,让搜索引擎、LLM 与 AI Agent 能直接以 Markdown 形式获取内容。

4.1 本地验证流程

apps/docs 目录依次执行:

pnpm build:guides-markdown   # 生成 Markdown 产物
pnpm dev                     # 启动本地站点验证

产物落在 public/markdown/guides 目录,被 Git 忽略(gitignored),可随时重新生成。生产环境下该任务作为 prebuild 的一环执行(即上文 build:markdown),目的是让 Vercel 把这些文件随 middleware 与 functions 一起打包部署。这一点在 apps/docs/next.config.mjsoutputFileTracingIncludes 中有直接证据:

outputFileTracingIncludes: {
  '/api/guides-md/**/*': ['./public/markdown/guides/**/*'],
  ...
}

4.2 生成器源码:从 MDX 到"AI 可读 Markdown"

生成入口是 build:guides-markdown 指向的 apps/docs/internals/generate-guides-markdown.ts,其核心流程:

  1. 收集源文件apps/docs/internals/markdown-sources.ts 用 glob 匹配 content/guides/**/!(_)*.mdx(下划线开头的私有文件被排除)与 content/troubleshooting/!(_)*.mdx;guides 的 frontmatter 按 YAML 解析,troubleshooting 按 TOML 解析(对应 next.config.mjs 中 webpack 对 .toml 文件的解析规则);
  2. 解析 MDX 为 mdast:使用 mdast-util-from-markdown + mdxjs() + gfm() 扩展,得到可遍历的语法树;
  3. 内联 partials< $Partial path="..." /> 组件会被递归替换为 apps/docs/content/_partials/ 下对应文件的 AST(缺失或损坏的 partial 会被静默丢弃,且路径必须限制在 _partials 目录内);
  4. 组件降级为 MarkdownapplySchema 自底向上遍历,把文档站特有的 JSX 组件(AccordionItemAdmonitionAgentSetupAiPromptTabPanelStepHikeRegionsListErrorCodes 等,schema 定义在同文件的 SCHEMA 常量,实现分散在 apps/docs/internals/markdown-schema/ 目录)逐一转写成纯 Markdown 字符串;未在 schema 中登记的组件按"解包"处理——丢弃外壳、保留子内容;
  5. 输出:frontmatter 中的 title/subtitle/description 会被拼成文档头部,写入 public/markdown/guides/<slug>.md;同时生成 public/markdown/manifest.json(全部 slug 清单)和 public/markdown/guides/troubleshooting.md(所有 troubleshooting 条目的索引页)。

4.3 服务端如何分发 Markdown:middleware 与内容协商

生成的 manifest.jsonapps/docs/middleware.ts 直接导入(MARKDOWN_SLUGS),中间件对 /guides/<slug> 请求执行内容协商:

  • 若 slug 不在 manifest 中(该页没有 Markdown 变体),直接放行返回 HTML;
  • 请求路径以 .md 结尾,或 Accept 头显式偏好 text/markdown,则 rewrite 到内部路由 /api/guides-md/<slug> 返回纯 Markdown;
  • 当请求的 Accept 同时拒绝 markdown 与 html(如只接受 application/json)时返回 406 Not Acceptable,并带 Vary: Accept 头。

协商逻辑本体在 packages/common/markdown-negotiation.ts,其中有一条值得注意的设计取舍(源码注释原文大意):刻意不做 User-Agent 嗅探——按 UA 变化响应会污染"UA 不感知"的 CDN 缓存,且至少有一个主流 Agent 的阅读器在拿到它并未显式请求的 Markdown 时会直接失败。Markdown 只在两种显式信号下发:.md 后缀或 Accept 头偏好。此外,middleware 对 /reference 路径下的爬虫请求(isbot 判定)会 rewrite 到 /api/crawlers,让参考文档的抓取走专门的渲染通道。

五、可访问性检查:axe-core + Playwright

apps/docs/DEVELOPERS.md 的 Accessibility checks 一节说明:文档页会由 e2e/docs 中的 Playwright 套件扫描 WCAG 2.1 A/AA 问题,PR 只扫描本次改动影响到的页面,且扫描范围限定在正文(main article)内。

扫描当前分支改动所涉页面:

PLAYWRIGHT_BASE_URL=https://supabase.com pnpm e2e:docs:a11y

注意其中的语义差异(原文特别强调):"扫描哪些页面"由你的分支 diff 决定,但"页面内容"来自 PLAYWRIGHT_BASE_URL 指向的环境。指向生产环境时,看不到你尚未合入的编辑,刚新增的页面会 404;因此扫描自己的内容时,应把 PLAYWRIGHT_BASE_URL 指向该 PR 的 Vercel preview。

apps/docs/package.json 根目录 package.json 中的 e2e:docs:a11y 定义为 pnpm --prefix e2e/docs run e2e:docs:a11y,即委托给独立的 e2e/docs 工程。该工程的 README 补充了完整细节:

  • 初始化cd e2e/docs && pnpm exec playwright install chromium
  • 页面范围解析:默认取 origin/master 以来的提交 diff 加上暂存/未暂存工作区变更,映射规则为——apps/docs/content/guides/**/*.mdx 变更测 /docs/guides/<slug>(联邦文档分区除外),content/troubleshooting/**/*.mdx/docs/guides/troubleshooting/<slug>content/_partials/** 变更则测所有引用该 partial 的页面;解析结果有 20 页上限,防止一个共享 partial 撑爆运行时长,超限页面会被静默丢弃;
  • 手动指定页面DOCS_E2E_PAGE_PATHS=/docs/guides/... 传逗号或换行分隔的 /docs/... 路径列表;DOCS_E2E_BASE_REF 可换基准 ref;pnpm e2e:docs:all 则忽略范围与上限、扫描全部 guides 与 troubleshooting(数百页,建议 --workers=4 并行);
  • a11y 实现@a11y 标签的测试用 @axe-core/playwright 扫描,跳过规则集中在 e2e/docs/utils/axe-helpers.tsEXCLUDED_RULES。被跳过的主要原因:color-contrast 耗时最长且文档正文的对比度来自共享 design token 与页面框架(chrome),正文范围内扫不出问题;其余跳过规则针对 <html>/<head>/<body>,而正文级扫描够不着这些节点。跨域 iframe 会被跳过,第三方嵌入不会被误报为本站问题;
  • 不覆盖的部分(原文如实列出):/docs/reference/*、共享页面框架、WCAG 的大部分条目;键盘导航、焦点管理与读屏器行为仍需人工测试;
  • CI 集成:工作流 .github/workflows/docs-e2e.yml 在改动 owned docs 内容、partials 或 e2e/docs 的 PR 上运行——先解析 PR diff 得到 in-scope 页面,无改动则跳过 Playwright;apps/docs 有变更时等待 Vercel preview 就绪并将 PLAYWRIGHT_BASE_URL 指向该 preview,拿不到 preview 时宁可跳过也不回落到生产环境;draft PR 在标记 ready 前一直跳过。
  • 失败排查pnpm -C e2e/docs exec playwright show-report 查看 HTML 报告,test-results/ 下有 trace 与截图。

另据 e2e/docs/README.md,本地 dev server(http://localhost:3001)只建议用于验证线上尚不存在的未发布内容:dev 模式下 reference 页首次请求编译可能超过单测超时,/docs/guides/auth/server-side/* 存在本地特有的 404 路由问题,常规检查应优先指向部署环境。

六、配套的质量校验命令

与本文主线相关的两条辅助检查,均已在 apps/docs/package.json 中定义:

  • MDX 内容风格检查pnpm lint:mdx,即 supa-mdx-lint content --config ../../supa-mdx-lint.config.toml,对整个 content/ 树做 lint,规则(标题大小写、拼写、用词偏好等)配置在仓库根的 supa-mdx-lint.config.tomlsupa-mdx-lint/ 目录;
  • 单元测试apps/docs/AGENTS.md 推荐在跑 docs 测试前先让本地 Supabase 数据库回到已知状态:pnpm supabase statuspnpm supabase start(如未运行)→ pnpm supabase db reset --localpnpm run -F docs test:local:unwatch。它特别强调:每次跑测试前必须先重置本地库以避免状态泄漏;pnpm test 虽然会自动 supabase start/stop 包裹,但不重置库且处于 watch 模式,不能替代上述序列;单文件运行可追加路径,如 pnpm run -F docs test:local:unwatch internals/internal-links.test.ts(内部链接一致性测试的源文件见 apps/docs/internals/internal-links.ts)。

七、贡献流程

  • 仓库组织方式与写作风格规范见 apps/docs/CONTRIBUTING.md(原文 "Contributing" 一节的唯一指向);
  • PR 评审顺序、issue 认领方式等社区约定见根 DEVELOPERS.md 的 "Create a pull request" 与 "Issue assignment" 章节:项目不做 issue 指派,鼓励尽早公开沟通以避免重复 PR,按提交时间顺序评审;
  • 若你在维护一个包含文档的外部仓库并希望将其联邦(federate)进 Supabase 官方文档,可参照根 DEVELOPERS.md 的 "Federated docs" 一节及 apps/docs/scripts/federated-content/ 的抓取管线(对应 build:federated-content 脚本)创建 issue 讨论集成。
登录后查看全文
热门项目推荐
相关项目推荐