Twenty 文档站迁移 Mintlify 实践:从 twenty-website 到 twenty-docs 的完整落地路径
本文基于 Twenty 仓库中的 MIGRATION.md 展开,完整还原其官方文档站从旧站点(twenty-website)迁移到 Mintlify 独立包 packages/twenty-docs 的范围、组件转换规则、目录结构、本地验证与部署流程,并结合当前仓库中的 Nx 项目配置、docs.json 与导航生成脚本,深入讲解迁移完成后这套文档工程如何演进为多语言、可校验的持续化文档管线。读完后你将掌握:如何在 Nx monorepo 中托管并本地预览 Mintlify 文档站、如何复现“旧自定义组件 → Mintlify 等价组件”的转换映射,以及文档导航如何由单一基础结构文件自动生成为多语言配置。
一、迁移背景与范围:一次性搬完 69 篇 MDX 与 81 张图
MIGRATION.md 开篇以“Mintlify Migration Summary”的形式记录了这次迁移搬动的全部内容。Twenty 官方文档原先嵌在 twenty-website 站点中,采用自研 React 组件渲染;迁移后文档独立为仓库内的 packages/twenty-docs 包,由 Mintlify 托管渲染与部署。迁移范围如下表(数字以迁移文档记录为准):
| 类别 | 数量 | 说明 |
|---|---|---|
| MDX 文档文件 | 69 篇 | 从 twenty-website 复制到 twenty-docs |
| 用户指南文章 | 45 篇 | 面向产品使用者 |
| 开发者文档文章 | 22 篇 | 面向贡献者与集成开发者 |
| 入门指南 | 2 篇 | 迁移前已存在 |
| 图片与资产 | 81 张 | 用户指南截图、开发者文档配图、Logo 与品牌资产 |
导航结构随内容一并迁移:Mintlify 主配置中包含带 Tab 与嵌套分组的完整导航——迁移时 User Guide 标签页有 11 个分组(section),Developers 标签页有 6 个分组。
从当前仓库结构看,这套内容已经明显“长大”:packages/twenty-docs 下的英文 MDX 覆盖 getting-started/(11 篇)、developers/(49 篇)、user-guide/(一百余篇,含 Data Model、Data Migration、Workflows 等 15 个主题目录),并且 l/ 目录下沉淀了 12 种语言的翻译副本。这说明迁移是一次“底座铺设”:把内容与导航整体搬入 Mintlify 约定后,后续多语言与内容扩展都建立在这个结构之上。
二、组件转换映射:旧自定义组件如何落到 Mintlify 等价物
旧文档页大量使用自研 React 组件,Mintlify 只提供一套固定组件库,因此迁移的核心工作之一是逐类替换。MIGRATION.md 给出的转换规则与后续人工复核清单如下:
已完成的机械替换
| 旧组件 | 迁移方式 |
|---|---|
<ArticleWarning> |
替换为 Mintlify 的 <Warning> 提示组件 |
<ArticleLink href="...">text</ArticleLink> |
降级为原生 Markdown 链接 text |
<ArticleEditContent> |
直接删除(Mintlify 侧无需对应物) |
需要人工复核的部分
<ArticleTabs>:Mintlify 的对应组件是<Tabs>,需逐页转换;- 嵌入式 iframe / 视频:可能需要调整实现方式;
- 自定义样式元素:需逐一检查 Mintlify 兼容性。
对于“视频嵌入”这一已知难点,仓库中的实际答案是保留了一个可复用的 MDX 片段 snippets/vimeo-embed.mdx:它导出一个 VimeoEmbed 组件,内部用 69.01% 的 padding-top 撑出 16:9 比例的容器,通过 iframe 内嵌 player.vimeo.com 播放地址并开启 autoplay/loop 参数。文档页引入该片段即可替代原站点的视频组件——这正是迁移文档中“Embedded iframes/videos - May need adjustment”一条的最终落点。
三、迁移后的目录结构:从 Mintlify 约定到实际仓库
MIGRATION.md 记录的迁移期目录结构如下(原样保留,便于对照迁移意图):
packages/twenty-docs/
├── mint.json # 主配置
├── user-guide/
│ ├── getting-started/ # 7 文件
│ ├── data-model/ # 6 文件
│ ├── crm-essentials/ # 4 文件
│ ├── views/ # 2 文件
│ ├── workflows/ # 7 文件
│ ├── collaboration/ # 3 文件
│ ├── integrations-api/ # 3 文件
│ ├── reporting/ # 1 文件
│ ├── settings/ # 9 文件
│ ├── pricing/ # 1 文件
│ └── resources/ # 2 文件
├── developers/
│ ├── self-hosting/ # 5 文件
│ ├── api-and-webhooks/ # 2 文件
│ ├── frontend-development/ # 8 文件
│ ├── backend-development/ # 7 文件
│ ├── local-setup.mdx
│ └── bug-and-requests.mdx
└── public/
└── images/ # 81 张图片
对照当前仓库,可以确认两处后续演进(属于迁移完成后的正常调整,非迁移文档本身的范围):
- 主配置文件由
mint.json演变为 docs.json。当前 docs.json 顶部声明"name": "Twenty Documentation"、"theme": "almond",并配置了 logo(/logo.svg,明暗主题共用)、favicon、主色(#141414)、导航栏按钮与styling.eyebrows: "breadcrumbs";navigation字段则被扩展为languages数组,包含英文与法语、阿拉伯语等 12 种语言的完整 Tab/Group/Page 树。 - 图片目录由
public/images/收敛为 images/。当前 README 明确约定“图片放入/images/目录”,并在 MDX 中以Alt text引用,或用 Mintlify 的<Frame>组件包裹<img>。
内容目录也从迁移期的 11+6 个分组重组为现在的三大 Tab:getting-started/(Welcome + Core Concepts)、user-guide/(Overview、Data Model、Data Migration、Calendar & Emails、Workflows、AI、Layout、Dashboards、Permissions & Access、Billing、Settings、Legal 等分组)、developers/(Apps、API、Self-Host、Contribute 等分组),这些分组名可直接在 docs.json 的 en 语言块中逐一对应到真实 MDX 路径。
四、本地验证:Nx 目标驱动 Mintlify Dev Server
MIGRATION.md 的 Testing 章节给出的验证方式是在 monorepo 根目录启动本地 Mintlify 开发服务器,并在浏览器打开 3000 端口预览全部迁移后的文档:
npx nx run twenty-docs:dev
这个命令的实际执行链路可以在 project.json 中完整核对:dev 目标使用 nx:run-commands 执行器,以包目录为工作目录运行 mintlify dev;同文件还定义了另外四个目标,构成完整的本地验证矩阵:
dev:mintlify dev,启动开发服务器(默认 http://localhost:3000);validate:mintlify validate,校验文档构建是否合法,对应根目录命令npx nx run twenty-docs:validate;lint:先跑npx oxlint -c .oxlintrc.json .(通用 TS/JS 规则),再跑npx tsx scripts/lint-mdx.ts(MDX 专用规则),串行执行;test:npx vitest run --config vitest.config.mts,用于脚本层单测;fmt:Prettier 检查/修复,带缓存目录。
运行前提也写得很明确:package.json 声明 engines 为 Node ^24.5.0、Yarn ^4.0.2(npm 字段为 please-use-yarn,即强制 Yarn),mintlify 依赖版本锁定在 ^4.2.790。复现迁移文档的验证步骤时,应使用仓库统一的 Yarn 工作区环境,而不是单独 npm install。
五、部署流程:仓库即文档源
MIGRATION.md 的 Deployment 章节给出了四步上线流程,核心思想是“文档以仓库文件为唯一事实来源,Mintlify 只负责拉取与构建”:
- 将变更推送到代码仓库;
- 在 Mintlify 控制台关联该仓库;
- 将子目录(subdirectory)设置为
packages/twenty-docs——即 Mintlify 不会构建整个 monorepo,只以该包为文档根; - 之后 Mintlify 在检测到变更时自动部署,并自动生成搜索 embeddings。
这套“子目录 + 自动部署”模式与仓库内的工程配置互相印证:docs.json 中配置了 SEO canonical 指向线上文档域名,意味着自动部署后的页面会声明规范链接;而本地 validate 目标正是上线前对同一份配置做静态校验的对应手段。
六、迁移完成后的演进:导航从手写 JSON 变成生成管线
MIGRATION.md 在 Status 一节宣布迁移完成:旧的文档载体被移除,文档此后全部存放在 packages/twenty-docs。(从当前源码结构看,packages/twenty-website 包仍然存在,但从其 package.json 看它现在是基于 Next.js 的营销站点,不再承担文档职责——可以推断迁移文档所指的“removed”是旧版内嵌文档的 website 形态,而非当前这个营销站点目录。)
迁移完成后,docs.json 没有停留在手写状态,而是长出了一条“基础结构 + 翻译标签 → 生成”的管线,这是理解当前文档站导航的关键:
导航事实来源与生成脚本
- navigation/base-structure.json:唯一的事实来源(source of truth),只含英文标签与页面 slug,按
tabs → groups → pages三级组织,且支持分组嵌套(如 Workflows 的 How-Tos 下再分 CRM Automations / Connect to Other Tools / Advanced Configurations / Need More Help 四个子组)。按 README 说明,该文件不上传翻译平台。 - scripts/generate-docs-json.ts:读取 base-structure,并为每种支持语言加载
l/<language>/navigation.json中的标签映射,最终把结果写回docs.json的navigation.languages。语言清单由 navigation/supported-languages.ts 从 twenty-shared 的DOCUMENTATION_SUPPORTED_LANGUAGES常量复用而来,保证文档站语言列表与产品常量同源。 - 根目录通过 package.json 暴露为
yarn docs:generate与yarn docs:generate-navigation-template两个命令,对应上述脚本。
生成逻辑里有一段值得注意的工程约束(generate-docs-json.ts 的源码注释):Mintlify 要求每个页面路径只能出现在一种语言的导航里,否则语言切换器无法解析等价页面、会回退到第一篇页面。因此脚本只在 l/<lang>/<slug>.mdx 真实存在时才把该页面挂进对应语言(见 formatPageSlug,第 156–164 行),空的分组与 Tab 会被整体丢弃。这就是 l/ 目录中每种语言恰好是 117 篇 user-guide + 74 篇 developers + 11 篇 getting-started 的由来——翻译缺口的页面会自动从该语言导航中消失,而不是渲染出死链。
MDX 质量门禁:为翻译管线定制的 Lint
scripts/lint-mdx.ts 是迁移“人工复核清单”沉淀下来的自动化防线。它解决一个非常具体的问题:翻译平台会把正文中的 <foo> 解析成标签,导致尖括号占位符在每种语言的译文里丢失或变形;而花括号 {foo} 能安全往返(见 第 3–6 行 的注释)。脚本的判定细节包括:
- 跳过
node_modules、l、images、scripts目录,只扫描源 MDX; - 内置约 60 个合法 HTML 元素名单(
img、iframe、video等),避免误报真实元素; - 精确计算围栏代码块(支持不同长度的反引号围栏配对)与行内代码区间,代码内的占位符不计为违规;
- 命中时输出
文件:行:列并提示“reads as a tag in Crowdin, use {name} instead”,有违规则退出码 1。
配合 lint 目标中的 oxlint,MDX 内容在进入翻译管线前就有机器校验——这是对迁移文档中“Custom styled elements - Review for compatibility”这类遗留项的长期治理。
七、已知问题清单与遗留事项
MIGRATION.md 末尾的 Known Issues to Review 是迁移期的诚实存档,逐条对照当前仓库可作如下收束:
- ArticleTabs 组件可能需手工转换:Mintlify 使用
<Tabs>组件,需逐页处理——对应现在 MDX 页面中的 Tabs 用法; - 部分图片路径可能不正确:当前图片统一收敛在 images/ 下,按 README 约定以
/images/...绝对路径引用; - 自定义样式组件可能需要调整:以 snippets/ 中的可复用片段(card-title、chart-icon、vimeo-embed)+ oxlint/MDX lint 双重约束来收敛;
- 视频嵌入可能需要复核:已用
VimeoEmbed片段给出统一实现。
八、小结:一次迁移如何变成一套可持续的文档工程
回顾这次迁移的完整轨迹:先用明确的范围清单(69 篇 MDX、81 张图、完整导航)完成一次性搬迁;再用“旧组件 → Mintlify 等价物”的映射表消除渲染层差异;随后以 npx nx run twenty-docs:dev 在 3000 端口做整站预览、以 validate 做构建校验,并把子目录 packages/twenty-docs 接入 Mintlify 的自动部署。迁移结束后,仓库又把导航改造成 base-structure.json → generate-docs-json.ts → docs.json 的生成式管线,让 12 种语言共用一份结构、各取一份标签,并用专门的 MDX lint 守住翻译往返的一致性。对需要把文档站从自研组件迁移到 Mintlify(或同类托管文档平台)的团队来说,这份仓库内的完整案例提供了从范围盘点、组件转换、本地验证到多语言持续治理的可复制路径。
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 StartedRust0627
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