Supabase Docs 文档站开发指南:本地环境搭建、AI 友好的 Markdown 生成管线与可访问性测试
本文以 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.json 中
packageManager字段声明为pnpm@11.13.1 - pnpm:仓库强制使用(
apps/docs/package.json的preinstall脚本为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:examples(shx cp -r ../../examples ./examples把仓库根部的 examples/ 示例工程复制进 docs 供页面引用);dev用run-p --race并行启动next dev --port 3001和dev:watch:troubleshooting(node ./scripts/troubleshooting/watch.mjs,监听 troubleshooting 条目变化);- 生产构建同理:
prebuild串联codegen:graphql、codegen:references、codegen:examples、build:federated-content(抓取联邦文档)、build:markdown(guides + reference 两类 Markdown 产物)、build:gz-archive;postbuild再执行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.mjs 的 outputFileTracingIncludes 中有直接证据:
outputFileTracingIncludes: {
'/api/guides-md/**/*': ['./public/markdown/guides/**/*'],
...
}
4.2 生成器源码:从 MDX 到"AI 可读 Markdown"
生成入口是 build:guides-markdown 指向的 apps/docs/internals/generate-guides-markdown.ts,其核心流程:
- 收集源文件:apps/docs/internals/markdown-sources.ts 用 glob 匹配
content/guides/**/!(_)*.mdx(下划线开头的私有文件被排除)与content/troubleshooting/!(_)*.mdx;guides 的 frontmatter 按 YAML 解析,troubleshooting 按 TOML 解析(对应 next.config.mjs 中 webpack 对.toml文件的解析规则); - 解析 MDX 为 mdast:使用
mdast-util-from-markdown+mdxjs()+gfm()扩展,得到可遍历的语法树; - 内联 partials:
< $Partial path="..." />组件会被递归替换为 apps/docs/content/_partials/ 下对应文件的 AST(缺失或损坏的 partial 会被静默丢弃,且路径必须限制在_partials目录内); - 组件降级为 Markdown:
applySchema自底向上遍历,把文档站特有的 JSX 组件(AccordionItem、Admonition、AgentSetup、AiPrompt、TabPanel、StepHike、RegionsList、ErrorCodes等,schema 定义在同文件的SCHEMA常量,实现分散在 apps/docs/internals/markdown-schema/ 目录)逐一转写成纯 Markdown 字符串;未在 schema 中登记的组件按"解包"处理——丢弃外壳、保留子内容; - 输出: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.json 被 apps/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.ts 的EXCLUDED_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.toml 与 supa-mdx-lint/ 目录; - 单元测试:apps/docs/AGENTS.md 推荐在跑 docs 测试前先让本地 Supabase 数据库回到已知状态:
pnpm supabase status→pnpm supabase start(如未运行)→pnpm supabase db reset --local→pnpm 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 讨论集成。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00