首页
/ Supabase 文档站构建管线解析:Turborepo、pnpm 生命周期钩子与 codegen 协同工作流

Supabase 文档站构建管线解析:Turborepo、pnpm 生命周期钩子与 codegen 协同工作流

2026-09-05 09:10:19作者:袁立春Spencer

本文以 apps/docs(Supabase 文档站)的构建管线为主体,结合仓库中的 turbo.jsoncpackage.json 脚本与实际 codegen 源码,完整拆解“依赖包构建 → 示例/参考文档代码生成 → Next.js 构建 → sitemap 与 CDN 上传”的全链路流程。读完本文,你能理解文档站每次 pnpm build 背后各步骤的触发顺序、Turbo 缓存策略的关键配置,以及如何为文档应用安全地新增构建步骤与环境变量。

1. 管线总览:Turborepo 与 pnpm 两层编排

apps/docs 的构建由两层机制协同完成:

  • Turborepo(根 turbo.jsoncapps/docs/turbo.jsonc):负责按依赖顺序编排工作区任务,声明 inputs/outputs/env 实现缓存;
  • pnpm 生命周期钩子apps/docs/package.json):在 next build 前后通过 prebuild/postbuild 脚本插入 codegen 与资产上传步骤。

整体数据流为:

依赖包 build(^build:common、ui、config、icons …)
      ↓
codegen:examples(复制 ../../examples → apps/docs/examples)
codegen:references(→ features/docs/generated/**)
build:markdown(guides + reference 的 .md 导出)
      ↓
docs#build(pnpm prebuild 链 → next build → pnpm postbuild 链)
      ↓
build:sitemap → upload-static-assets.sh(R2 CDN,仅生产风格部署)

从源码结构看,Turbo 层只声明到 build:markdown,而 build:federated-contentbuild:gz-archive 等步骤则由 pnpm 的 prebuildnext build 前串行执行——两层机制的职责边界清晰:跨包依赖与缓存归 Turbo,包内前后置步骤归 npm 生命周期。

2. 从仓库根目录触发的命令

package.json 中与文档站直接相关的脚本(以当前仓库实际内容为准):

"build": "turbo run build",
"build:docs": "turbo run build --filter=docs",
"dev:docs": "turbo run dev --filter=docs --parallel",
"test:docs": "turbo run test --filter=docs"
  • 全量构建pnpm buildturbo run build,按依赖序构建所有包与应用。
  • 仅构建文档站pnpm build:docsturbo run build --filter=docs,Turbo 会先解析 docs^build 依赖闭包,再执行文档站自身任务。
  • 本地开发pnpm dev:docs(或在 apps/docspnpm dev)。

运行环境前提(见根 package.jsonengines/packageManager):pnpm 11.13、Node >= 22.13,且 preinstall 通过 only-allow pnpm 强制使用 pnpm。

3. Turbo 层做了什么

3.1 根 turbo.jsonc:^build 保证依赖先构建

turbo.jsonc 定义:

"build": {
  "dependsOn": ["^build"],
  "outputs": ["dist/**", ".next/**", "!.next/cache/**/*", "!.next/dev/**/*"],
}

dependsOn: ["^build"] 意味着 docs 的所有工作区依赖(commonuiconfigiconsshared-dataai-commands 等,见 apps/docs/package.jsonworkspace:* 依赖列表)会在 docs 之前完成构建。全局缓存保留期由 cacheMaxAge: "14d" 控制。

3.2 apps/docs/turbo.jsonc:扩展并收紧 docs 任务

apps/docs/turbo.jsonc 通过 "extends": ["//"] 继承根配置,并为文档站细化任务:

"codegen:examples": {
  "inputs": ["../../examples/**"],
  "outputs": ["examples/**"],
},
"codegen:references": {
  "inputs": ["spec/**"],
  "outputs": ["features/docs/generated/**"],
},
"build:federated-content": {
  "cache": false,
  "env": [
    "DOCS_GITHUB_APP_ID",
    "DOCS_GITHUB_APP_INSTALLATION_ID",
    "DOCS_GITHUB_APP_PRIVATE_KEY",
  ],
},
"build:markdown": {
  "dependsOn": ["build:federated-content"],
  "outputs": ["public/markdown/**", "public/markdown/manifest.json"],
},
"build": {
  "dependsOn": ["^build", "codegen:examples", "codegen:references", "build:markdown"],
  "env": [ /* 约 50 个变量,见下文 */ ],
  "inputs": ["$TURBO_DEFAULT$"],
  "outputs": [".next/**", "!.next/cache/**"],
}

几个值得注意的设计决策(源码注释原文佐证):

  1. codegen:examples / codegen:references 显式声明 inputs/outputs:这样 Turbo 才能对其缓存——例如 examples/ 目录或 spec/ 目录无变化时,直接恢复产物 apps/docs/examples/**features/docs/generated/**
  2. build:federated-content 关闭缓存:任务注释明确说明“输入是远端(GitHub)内容,缓存恢复可能复活陈旧内容”,故 "cache": false。该脚本为 apps/docs/scripts/federated-content/fetch-federated-content.ts,依赖 GitHub App 三元组环境变量。
  3. build:markdown 声明 outputs:注释解释“若声明了 outputs,缓存命中时 Turbo 可恢复这些产物;否则缓存命中会跳过脚本,导致 next build 找不到生成的 markdown”。这是典型的 Turbo 缓存陷阱:有副作用/产物的任务必须声明 outputs
  4. build 任务的 env 清单:列出约 50 个影响产物的环境变量(NEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SITE_URLNEXT_PUBLIC_IS_PLATFORMVERCEL_ENVDOCS_GITHUB_APP_*OPENAI_API_KEYSUPABASE_SECRET_KEY 等)。任何影响构建产物但未列入 env 的变量都会导致 Turbo 缓存出陈旧产物——新增环境变量时必须同步加入此清单(官方文档 build-pipeline.md 将其列为改动守则之一)。

Turbo 为 docs 编排的完整顺序即:依赖包 → 示例/参考 codegen → markdown 导出 → Next 构建

4. pnpm 生命周期层:prebuild / build / postbuild

apps/docs/package.json 中实际的生命周期脚本(当前仓库版本,注意比早期文档多出一步 build:federated-content):

"prebuild": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples && pnpm build:federated-content && pnpm run build:markdown && pnpm run build:gz-archive",
"build": "next build",
"postbuild": "pnpm run build:sitemap && ./../../scripts/upload-static-assets.sh"

执行语义:turbo run build --filter=docs 最终执行 docs#build 时,pnpm 自动先跑 prebuild,再跑 next build,最后跑 postbuild

  1. prebuild 五(六)步:GraphQL codegen → 参考文档 codegen → 复制 examples → 拉取 federated 内容 → 生成 guides + reference 的 markdown 导出 → 打包 tar.gz;
  2. buildnext buildANALYZE=true 时可换成 build:analyze 做包体积分析);
  3. postbuild:生成 sitemap,随后调用仓库根目录的 scripts/upload-static-assets.sh(仅生产风格部署上传 R2)。

5. 逐个 codegen 脚本详解

脚本 实际命令 产物 / 作用
codegen:graphql tsx --conditions=react-server ./scripts/graphqlSchema.ts && graphql-codegen --config codegen.ts 拉取/固化 GraphQL schema 并用 graphql-codegen 生成类型
codegen:examples shx cp -r ../../examples ./examples 把 monorepo 根 examples/ 复制进 apps/docs/examples,供 MDX 中 $CodeSample 指令解析示例代码
codegen:references legacy + new 两段式 见下文
build:federated-content tsx … ./scripts/federated-content/fetch-federated-content.ts 通过 GitHub App 拉取远端内容并产出 JSON 工件,供 markdown 生成器读取
build:markdown build:guides-markdown && build:reference-markdown 生成 public/markdown/guides/**.mdpublic/markdown/reference/**.md
build:gz-archive tsx ./internals/generate-gz-archive.ts 打包 public/markdown/public/docs.tar.gz
build:sitemap tsx ./internals/generate-sitemap.ts 生成站点 sitemap(postbuild 阶段)

5.1 参考文档 codegen 的双轨结构

codegen:references 实为两条管线串行:

"codegen:references:legacy": "tsx features/docs/Reference.generated.script.ts",
"codegen:references:new": "pnpm run codegen:references:new:ensure && tsx scripts/build-reference-content.ts"
  • legacy 轨apps/docs/features/docs/Reference.generated.script.ts 处理 Management API——将 OpenAPI v1+v2 合并为 api.latest.* JSON;规格下载/合并由 apps/docs/spec/Makefile(Redocly)完成。相关背景见 management-api-reference.md
  • new 轨ensure 步骤先校验三份 TSDoc JSON(spec/reference/javascript/v2/supabase.jsonspec/reference/server/v1/server.jsonspec/reference/middleware/v1/middleware.json),缺失时调用 make download.* 目标下载;随后 apps/docs/scripts/build-reference-content.tsspec/reference/ 下的 TSDoc JSON 构建参考内容,输出落在 features/docs/generated/**(这正是 Turbo 声明的 codegen:references outputs)。此外还有 precodegen:references:new 钩子会顺带生成 Dart 参考(codegen:references:dart)。

5.2 markdown 导出管线

  • guidesapps/docs/internals/generate-guides-markdown.ts 遍历 content/guides/**/*.mdx,基于 mdast/micromark(GFM + MDX 扩展)、gray-matter frontmatter 解析,将大量自定义 MDX 组件(markdown-schema/ 下的 AdmonitionStepHikeTabPanelPromptPanelRegionsList 等约 30 个映射)逐一降级为纯 Markdown,并借助 internal-links.ts 重写内部链接为带 base path 的绝对路径,产物为 public/markdown/guides/**.md
  • referenceapps/docs/internals/generate-reference-markdown.tsfeatures/docs/generated/** 的参考内容执行同类导出,产物为 public/markdown/reference/**.md
  • 归档apps/docs/internals/generate-gz-archive.tstarpublic/markdown/ 全部条目排序后压缩为 public/docs.tar.gz(源码注释强调排序条目 + portable 头以保证确定性输出),随站点静态资源在 /docs/docs.tar.gz 提供服务。

这一套“运行时页面”与“纯 Markdown 导出”并行输出的结构,即文档中提到的 LLM/Agent 消费面——Agent 直接读取 markdown 与 tar.gz 而非爬取 HTML,详见 llm-agent-surface.mdapp-map.md

6. postbuild:sitemap 与 R2 CDN 上传

postbuild 的第二步是仓库根目录共享的 scripts/upload-static-assets.sh,要点(以脚本源码为准):

  • 触发条件:仅当 FORCE_ASSET_CDN=1VERCEL_ENV=production 时执行;FORCE_ASSET_CDN=-1(如 Studio 自托管场景)显式跳过。本地与 preview 部署均不上传。
  • 桶选择NEXT_PUBLIC_ENVIRONMENT=staging 时上传 frontend-assets-staging,否则 frontend-assets-prod(Cloudflare R2,通过 ASSET_CDN_S3_ENDPOINT 自定义 endpoint 走 S3 协议)。
  • 路径设计s3://<bucket>/<SITE_NAME>/<VERCEL_GIT_COMMIT_SHA 前 12 位>/_next/static,按环境 + 应用 + 提交哈希隔离,旧版本资产留存一段时间以避免切换瞬间的“抖动”。
  • 缓存策略--cache-control "public,max-age=604800,immutable"(7 天不可变缓存),同时同步 .next/staticpublic/(public 上传是因为部分文件会被 CSS 相对路径引用,需走 CDN URL)。
  • 目的(脚本头部注释):绕开 Vercel 出口流量费、规避 Cloudflare 代理的 Orange-to-Orange 超时问题、避免双重 TLS 终结带来的额外延迟。

7. 本地开发模式

apps/docs 下(或根目录 pnpm dev:docs):

pnpm dev   # http://localhost:3001/docs

相关脚本(apps/docs/package.json):

"dev": "run-p --race dev:next dev:watch:troubleshooting",
"dev:next": "next dev --port 3001",
"dev:watch:troubleshooting": "node ./scripts/troubleshooting/watch.mjs",
"predev": "pnpm run codegen:graphql && pnpm run codegen:references && pnpm run codegen:examples",
"dev:secrets:pull": "AWS_PROFILE=supa-dev node ../../scripts/getSecrets.js -n local/docs"
  • predev 先跑 GraphQL / reference codegen 与 examples 复制(不含 markdown 导出与归档,加快启动)。
  • 并发 watcherdev:watch:troubleshooting 通过 apps/docs/scripts/troubleshooting/watch.mjs 同步 troubleshooting 内容(数据源为远程 schema,见 supabase/migrationstroubleshooting_entries 相关迁移)。
  • 社区贡献者:在 .env 中设置 NEXT_PUBLIC_IS_PLATFORM=false
  • 内部环境pnpm run dev:secrets:pull 从 AWS Secrets Manager 拉取(依赖 scripts/getSecrets.js 与 AWS profile)。
  • 按需渲染:dev 模式下应用仅在被请求时构建路由,不做预渲染;preview 与 production 环境在构建期静态生成路由以保证访问速度。

8. 生产部署 vs CI

  • 生产构建:文档站由 Vercel 部署,apps/docs/vercel.json 仅一行——"buildCommand": "pnpm build"——即执行上文 docs 应用的完整 prebuild/build/postbuild 脚本链。
  • GitHub Actions:主要运行 test:docs(Turbo 编排的 vitest,DOCS_SMOKE_URL 环境变量可让 smoke 测试指向 preview 或 localhost 而非生产,见 turbo 中 test 任务的 env 声明)、lint、内容同步与 smoke 检查,并不在 CI 侧重复一套完整生产构建图。工作流面细节见 ci-and-lint.md

9. 改动守则:为什么这套结构对变更敏感

官方参考文档 build-pipeline.md 给出的三条守则,均可在仓库中找到对应落点:

  1. 新增构建步骤:优先检查 prebuild/postbuild 是否已有可复用的钩子(“复用管线,不要分叉管线”,见 adding-features.md);跨包产物应注册为 Turbo 任务并声明 inputs/outputs
  2. 新增环境变量:必须加入 apps/docs/turbo.jsoncenv 列表,否则 Turbo 哈希不包含该变量,会命中陈旧缓存——这是该管线最常见的坑。
  3. 触碰 markdown 导出:先阅读 app-map.md 中“两条管线”一节——运行时页面与 markdown 导出共享数据源,但不共享代码路径,修改 MDX 组件时两条导出路径都需回归(public/markdown/manifest.json 与 tar.gz 内容都要验证)。

此外,若行为与预期不符,官方建议直接以 apps/docs/turbo.jsoncapps/docs/package.json 的当前内容为准核对——本文所列脚本链(含 build:federated-content 这一步)即按当前仓库实际版本整理,与旧版参考文档中的简化描述可能存在差异。

10. 小结

Supabase 文档站的构建管线是一个“Turborepo 管依赖与缓存、pnpm 生命周期管前后置步骤”的教科书式 monorepo 案例:^build 保证工作区依赖先行,codegen:examples/codegen:references/build:markdown 以显式 inputs/outputs 接入 Turbo 缓存,prebuild 串起 GraphQL 类型、双轨参考文档生成、federated 内容拉取与 Markdown/tar.gz 导出,next build 完成页面构建,postbuild 收尾 sitemap 并按 VERCEL_ENV=production 条件将静态资产推上 Cloudflare R2。理解这条链路后,无论是新增文档生成步骤、接入新环境变量,还是排查“缓存命中但产物缺失”这类问题,都能定位到 apps/docs/turbo.jsoncapps/docs/package.jsonapps/docs/internals/ 下的对应脚本。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384