mcpField
B
blume-migrate

blume-migrate

github.com/haydenbleasel开发1.8k188 次安装3 天前更新
代码仓库

关于 blume-migrate

blume-migrate 是将现有文档仓库迁移到 Blume 的 Agent 技能。它根据源框架选择对应映射指南,转换内容、导航和配置,并记录无法迁移或被舍弃的部分。

主要功能

  1. 01识别多种文档源框架
  2. 02按框架参考指南映射内容
  3. 03转换配置、导航与页面结构
  4. 04处理链接、资源路径和重定向
  5. 05报告舍弃或近似转换的内容

适用场景

  1. 01将 Mintlify 或 Docusaurus 文档迁移至 Blume
  2. 02把既有导航结构转换为 Blume 页面组织
  3. 03迁移中修正前置元数据与内部链接
  4. 04评估源站无法直接映射的功能

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/haydenbleasel/blume --skill blume-migrate

安装后在 Claude Code 中输入/blume-migrate确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
docsify.md
docus.md
docusaurus.md
fern.md
fumadocs.md
gitbook.md
github-wiki.md
jekyll.md
mdbook.md
mintlify.md
mkdocs.md
monorepo.md
nextra.md
readme.md
redocly.md
starlight.md
vitepress.md
vuepress.md
scripts/
docsify-codemod.mjs
docus-codemod.mjs
fern-codemod.mjs
github-wiki-codemod.mjs
include-excerpts.mjs
jekyll-codemod.mjs
mintlify-codemod.mjs
mkdocs-codemod.mjs
operation-routes.mjs
pin-heading-ids.mjs
readme-codemod.mjs

详细介绍

迁移到 Blume

Blume 是一个基于 Astro/Vite、以 Markdown 为先的文档框架。将 Markdown/MDX 文件放入文件夹,即可获得导航、搜索、主题、Open Graph 图片和组件库,无需应用脚手架代码——框架就是模板。无需克隆起始模板;项目只需管理自己的内容和一个 blume.config.ts。

你的任务是将源文档仓库转换为符合 Blume 惯例的项目,而不是逐字逐项地照搬。阅读此文件,检测源框架,打开对应的 references/<framework>.md 查看具体映射,然后按以下流程迭代。报告所有删除或近似处理的内容。

在整个技能(包括 references/ 文件)中,<skill> 表示包含此 SKILL.md 的目录的绝对路径——请根据你读取此文件的位置解析(例如 node_modules/blume/skills/blume-migrate 或 .claude/skills/blume-migrate)。这是一个需要替换的占位符,切勿将其当作字面路径。

迁移理念

  • 目标是符合 Blume 惯例,而非机械移植。 优先使用文件系统派生导航,而不是穷举式的显式 navigation.sidebar。优先使用 ::: 指令,而不是 JSX 提示框。优先采用 Blume 默认值,而不是在配置中重复声明。
  • 每个字段都有默认值;{} 是有效配置。 仅映射源中_明确声明_的内容。如果源使用框架默认值,就不要写入配置。
  • 删除 Blume 没有对应功能的外观元素——并说明原因。 Navbar CTA、自定义主题、有条件的重定向以及无法映射的图标,应向用户报告,而不是悄悄丢弃或伪造。
  • 转换,而非保留。 Blume 的页面 frontmatter 架构是严格的——未知键会导致构建错误。源中专有的 frontmatter 键必须映射为 Blume 键或移除(并报告),绝不能抱着“或许会通过校验”的想法将其留下。

迁移工作流

  1. 检测源框架并阅读其参考文件:
    • docs.json / mint.json → Mintlify(references/mintlify.md)——导航最深入,且由配置声明。
    • docusaurus.config.* → Docusaurus(references/docusaurus.md)。
    • meta.json + fumadocs-* 依赖项(内容位于 content/docs/ 下)→ Fumadocs(references/fumadocs.md)。
    • _meta.{js,ts,json} + nextra 依赖项 → Nextra(references/nextra.md)。
    • 调用 starlight({…}) 的 astro.config.* → Starlight(references/starlight.md)。
    • .vitepress/config.{js,ts,mjs,mts}(通常位于 docs/.vitepress/)或 vitepress 依赖项 → VitePress(references/vitepress.md)。.vuepress/ 是 VuePress(见下一条)。
    • .vuepress/config.{js,ts,mjs}(v1 还支持 config.yml / config.toml;通常位于 docs/.vuepress/)、vuepress.config.* 或 vuepress 依赖项 → VuePress(references/vuepress.md)。它在 Markdown 层面基于 references/vitepress.md,两者共用该层,因此两个文件都要读。
    • docus 依赖项(通常位于 docs/package.json 中,旁边有 extends: ['docus'] 的 nuxt.config.ts),或适用于 1.x 的 @nuxt-themes/docus → Docus(references/docus.md)。页面使用 MDC,即 Nuxt Content 的组件语法:::note … :: 不是 Blume 指令,Blume 会将其原样显示为文本(BLUME_MDC_SYNTAX 会在对应行发出警告)。
    • mkdocs.yml / mkdocs.yaml(也包括 Zensical 的 zensical.toml 和 ProperDocs 的 properdocs.yml)→ MkDocs / Material for MkDocs(references/mkdocs.md):导航在配置中声明,使用 Python-Markdown 扩展语法(!!! 提示框、=== 选项卡、--8<-- 片段)以及 Python 依赖项,因此通常没有 package.json。先运行 node <skill>/scripts/mkdocs-codemod.mjs --write <content-dir>,然后在首次构建后运行 pin-heading-ids.mjs。
    • fern/docs.yml(与 fern/fern.config.json 同目录)→ Fern(references/fern.md)。没有 docs.yml 的 fern/ 文件夹只用于生成 SDK:文档位于别处。
    • .gitbook.yaml / .gitbook.yml、gitbook-docs.yaml,或与 .gitbook/ 文件夹同目录的 SUMMARY.md → GitBook(references/gitbook.md)。book.json + SUMMARY.md 表示旧版 GitBook CLI 或 HonKit(使用同一参考文件中的专属章节);book.toml + src/SUMMARY.md 表示 mdBook(references/mdbook.md)。
    • book.toml(位于书籍根目录,代码仓库中通常在 docs/、book/ 或 guide/;章节位于其 [book] src 指定的目录中,并与 SUMMARY.md 同目录)→ mdBook(references/mdbook.md)。所有 URL 都以 .html 结尾,SUMMARY.md 是完整导航,{{#include}} 会从整个仓库提取带锚点的代码:先构建旧版书籍,因为它提供 URL 列表、旧 ID,以及唯一记录了 include 内容的资料。
    • sidebars.yaml 或 @redocly/realm 系列依赖项(通常与 redocly.yaml 同目录,页面中使用 Markdoc {% … %} 标签)→ Redocly(references/redocly.md)。GitBook 页面也会使用 {% … %} 标签,因此先检查 GitBook 文件。若 redocly.yaml 只包含 lint/bundle 键(extends、rules、decorators、apis),则它是 Redocly CLI 配置,而非文档站点:保留它,并根据其他信号检测框架。
    • docs/、reference/、recipes/ 或 custom_pages/ 中的 _order.yaml 文件(ReadMe 的双向 Git 同步),或在页面旁运行 readmeio/rdme 的 CI 步骤,且页面带有 category / parentDoc(或 category.uri / parent.uri)前置元数据(由 rdme 上传)→ ReadMe(references/readme.md)。ReadMe 将设置、重定向、术语表和变量保存在控制面板中,而非仓库里;变更日志也只存在于 main 分支。
    • 设置了 window.$docsify 的 index.html(通常位于 docs/,与 _sidebar.md 同目录),或 package.json 中有 docsify-cli → Docsify(references/docsify.md)。先运行 node <skill>/scripts/docsify-codemod.mjs --write --snapshot .docsify-old --group-folders <docs-dir>,然后在首次构建后运行 pin-heading-ids.mjs --old .docsify-old --map docsify-routes.json。其默认 URL 位于 # 之后(/#/page),任何服务端重定向都无法匹配;参考文件会添加客户端脚本。
    • _config.yml(根目录或 docs/)→ Jekyll(references/jekyll.md);若 _config.yml 或 Gemfile 中有 just-the-docs,则为 Just the Docs。先用项目本地的 bundle 构建旧站点:导航来自前置元数据(parent / nav_order),并根据旧构建的侧边栏重新生成,URL 以 / 或 .html 结尾。旁边有 _toc.yml 则表示 Jupyter Book,而非 Jekyll。先从 Jekyll 源目录运行 node <skill>/scripts/jekyll-codemod.mjs --old <old build> --write .,然后在首次构建后运行 pin-heading-ids.mjs --old <old build>。
    • <repo>.wiki.git 的克隆仓库(根目录有 Home.md,通常还有 _Sidebar.md 和 _Footer.md,链接使用 [[Page Name]],且没有配置文件)→ GitHub Wiki(references/github-wiki.md)。此情况无法通过信号自动检测:源类型已知。其旧 URL 仍位于 github.com,无法进行重定向,因此参考文件会为旧 wiki 编写路由表和链接存根,而非 redirects。先运行 node <skill>/scripts/github-wiki-codemod.mjs routes --repo <owner>/<repo> --write,对 wiki-routes.json 分类,然后运行 convert --repo <owner>/<repo> --write。
    • 其他情况 → 直接应用本文件中的思路;没有特定框架的参考文件,因此需要手动盘点。
    • 还要注意宿主仓库,与源框架无关: pnpm/Turbo 工作区、非 docs/ 内容布局或 Vercel 部署都需要集成步骤(content.root 范围限定、minimumReleaseAge、锁文件、vercel.json、Astro/Vite 补丁)——这些都在 references/monorepo.md 中。目标不是一个简单的单包文档文件夹时,务必阅读该文件。
  2. 在更改任何内容之前盘点仓库(正文只有 <meta http-equiv="refresh"> 的页面是重定向,不是页面:为其旧 URL 添加重定向,并在报告中说明):配置文件、内容树、导航定义、片段/局部文件/include、静态资源、OpenAPI/AsyncAPI 规范和 GraphQL 架构、重定向、i18n 区域设置、自定义组件和图标用法。记录哪些内容是显式声明的,哪些依赖默认值。旧站点意外发布的页面(.github/ 模板、点文件夹中的文件、规划笔记)不属于内容:不要纳入,不要添加重定向,并在报告中列出。
  3. 编写 blume.config.ts,使用 blume 中的 defineConfig。只映射已声明的字段(见参考文件的映射表);其他所有内容都依赖默认值。最精简的配置为 defineConfig({ title: "…" })。
  4. 重组内容。 将 blume.config.ts 放在运行旧文档构建命令的 package.json 所在目录中;该目录可能是 packages/docs 这样的工作区包,而不是仓库根目录。选择 content.root(默认为 docs)——检查 .md / .mdx 文件实际所在位置,不要假定有 docs/ 文件夹。 许多仓库将内容直接放在应用目录下(apps/docs/api/、.../getting-started/),没有 docs/ 子文件夹;遇到这种情况时,将 content.root 设为该目录,并将 content.include 限定在实际内容文件夹,而不是让 content.root: "." 扫描所有内容(见 references/monorepo.md §1)。使用数字前缀排序(01-intro.mdx),使用 (group)/ 文件夹分组但不增加 URL 段,仅在文件系统排序不足以表达意图时添加 meta.ts(defineMeta)。如果源项目已通过侧边文件声明每个文件夹的导航——例如 Fumadocs 的 meta.json、Nextra 的 _meta.*——就属于这种情况:将每个文件转换为 meta.ts,保留其标题/图标/顺序/折叠设置,而不是丢弃它并寄希望于文件名能重现原有意图。文件系统推断只适用于没有声明文件夹级导航的文件夹,绝不是丢弃已有声明的理由。 如果某个分组只存在于源导航配置中,而其页面在磁盘上是平铺的,则将它转换为 (group)/ 文件夹:这会添加侧边栏分组并保留所有 URL,无需重定向(将页面移入更深层文件夹会改变 URL,并破坏相对资源路径)。只有当源导航确实无法通过文件来表达时,才使用显式的 navigation.sidebar。按选项卡将页面重组为文件夹会改变 URL——在过程中记录每个旧路径 → 新路径;第 5 步会将它们转换为 redirects。
  5. 改写页面。 将前置元数据映射到 Blume 的严格架构;将提示框 JSX 转换为 ::: 指令——指令(以及数学、mermaid、软件包安装代码围栏)仅适用于 MDX,因此需要它们的 .md 页面必须改名为 .mdx;重命名组件;将片段/局部文件转换为 <include> 语句或直接内联(Blume 不支持基于导入的 include:import Snippet from "…" 加 <Snippet /> 必须改为其中一种方式;如果源项目包含文件的一部分(mdBook ANCHOR、VitePress #region、MkDocs 代码片段行范围)或内容根目录之外的文件,请将 <skill>/scripts/include-excerpts.mjs 复制到项目中,并在每次 dev、build 和 validate 前运行,而不是粘贴项目编译的代码:它会一次性报告所有缺失的锚点或区域,而且恢复源文件中的标记比使用容易悄然过时的行范围更好);修复资源路径;将内部链接改写为新路由(包括 OpenAPI/GraphQL 操作链接——参见 API 参考章节,其 slug 与大多数源项目不同——以及 OpenAPI 描述中的链接;Blume 会渲染并验证这些链接,但重定向 from 会掩盖过时链接;将写死旧站点自身域名的链接改为根相对路径);为第 4 步中每个移动过的路由添加 redirects 条目(采用哈希路由的源项目,例如 Docsify,还需要参考文件中的客户端重定向,因为 redirects 只匹配路径);将所有图标名称转换为 Lucide(Blume 只支持 Lucide,不支持 FontAwesome/Tabler)。删除正文中重复的 H1(title 会渲染 H1;正文从 ## 开始),并修复指向其锚点(#<h1-slug>)的链接,因为该锚点已不存在:删除片段。blume validate --strict 还会报告旧站点从未检查过的片段;逐一修复或删除,并在报告中说明。没有前置元数据 title 的页面会使用开头的 # H1 作为标题,并且只显示一次,因此可以保留;不过将其移入 title 仍是惯用写法。如果源项目有手动维护的变更日志,且仓库在 GitHub 上开源,请提议用 githubReleases() 数据源替换它(见下方“变更日志”),而不是移植已有条目。对于 Mintlify,先运行随附的 codemod——node <skill>/scripts/mintlify-codemod.mjs --write <content-dir> 会以确定性方式重新映射图标并删除/重命名不受支持的前置元数据键,同时报告其他需要手动处理的内容(未知图标、OpenAPI 占位标记;见 references/mintlify.md)。对于 Docus,先运行随附的 codemod:node <skill>/scripts/docus-codemod.mjs --write content 会像 remark-mdc 一样配对 MDC 块,转换 Nuxt UI 文本组件、代码围栏标签、图标、前置元数据和 .navigation.yml 文件,让 MDX 文本安全,并列出所有 REVIEW 项(自定义组件和落地页组件保持原样;请根据线上站点检查它们,不要只依赖本地构建;见 references/docus.md)。对于 VitePress,先运行随附的 codemod:node <skill>/scripts/vitepress-codemod.mjs --write <srcDir> 会转换容器(按照 markdown-it 的配对方式配对)、代码组、提示框、围栏标签、<<< 导入和 include、.html 链接、徽章和 H1 标题,并列出所有待处理的 REVIEW 项(见 references/vitepress.md)。转换任何源项目组件之前,先检查其构建输出:主题从未注册的组件在源站点中渲染为空内容(``),因此转换后会显示读者从未见过的内容;请在报告中说明。对于 mdBook,首先转换所有 {{#…}} 辅助项:带锚点、行范围以及位于根目录外的代码会成为生成的摘录;单行文件会成为 variables;然后去掉隐藏的 # 行和 rust,… 围栏属性,并转换 admonish 块(见 references/mdbook.md)。对于 Jekyll,转换所有 Kramdown IAL({: .note })和 Liquid 标签:在 .md 中,它们会作为纯文本发布(Liquid 标签会发出 BLUME_TEMPLATE_TAG 警告,IAL 会发出 BLUME_MD_ATTRIBUTE_LIST 警告);在 .mdx 中则会导致构建失败(blume check 会将每个 IAL 的行标记为 BLUME_MDX_ATTRIBUTE_LIST,将 Liquid 标签的行标记为 BLUME_MDX_SYNTAX;如果包含的局部文件中有 Liquid 标签,则标记该标签在局部文件中的行)。先运行随附的 codemod:它会转换提示框 IAL、页内目录、H1、前置元数据、Markdown include、Liquid 链接和变量;将 Just the Docs 侧边栏重建为文件夹、slug 和 meta.ts;并将重定向和变量写入 jekyll-migration.json(见 references/jekyll.md)。对于 GitHub Wiki,运行随附的 codemod(先 routes,再 convert):它会生成路由表(每个页面名称对应一个平铺路由),并转换代码之外的所有 [[…]] 链接,否则 Blume 会将它们显示为纯文本(BLUME_WIKILINK_UNSUPPORTED 只会对指向页面名称或含有 | 的链接发出警告;Markdown 页面将链接文本写在前面:[[text|Page]])。对于 Redocly,转换所有 Markdoc {% … %} 标签:Blume 不会解析它们,因此未转换的标签在 .md 中会作为纯文本发布(BLUME_TEMPLATE_TAG 会在对应行发出警告),并导致 .mdx 构建失败。对于 VuePress,先将所有 README.md 重命名为 index,然后运行 VitePress codemod(VuePress 内容的限制见 references/vuepress.md),再搜索带连字符的主题标签(<code-group>、<code-block>、<router-link>):它们会作为未知 HTML 元素通过 .mdx,且不会产生诊断。对于 MkDocs,在转换前为每个页面决定使用 .md 还是 .mdx:MDX 不接受 HTML 注释、<https://…> 自动链接、未闭合的空元素标签,以及 Python-Markdown 可接受的 {…} 属性列表(blume check 会在对应行标记:MDX 无法解析的内容为 BLUME_MDX_SYNTAX,后两项分别为 BLUME_MDX_UNCLOSED_ELEMENT 和 BLUME_MDX_ATTRIBUTE_LIST);代码片段路径相对于片段的 base_path(默认值为运行 MkDocs 的位置)解析,而不是相对于页面(见 references/mkdocs.md)。对于 Fern,URL 来自 docs.yml,而非文件路径:移动任何内容之前,先运行 node <skill>/scripts/fern-codemod.mjs --sitemap old-urls.txt(先试运行,再加上 --write);首次构建后运行其 endpoints 步骤,将 operation-routes.mjs 的输出与 Fern 的旧 API URL 合并,并写入 fern-redirects.json;使用 fern export 将 Fern Definition 导出为 OpenAPI(见 references/fern.md)。对于 GitBook,先将每个页面放在其旧 URL 对应的位置(GitBook 的 URL 取决于 SUMMARY.md,而非文件树),然后转换所有 {% … %} 标签:在 .md 中,未转换的标签会作为纯文本发布(BLUME_TEMPLATE_TAG 会发出警告);在 .mdx 中则会导致构建失败。对于 ReadMe,先运行随附的 codemod:node <skill>/scripts/readme-codemod.mjs --write --site <hub origin> --glossary <saved hub page> . 会将每个页面保留在原有的平铺 URL 上,转换提示框、doc: / ref: 链接(blume validate 会将任何遗留链接标记为 BLUME_UNSUPPORTED_LINK_SCHEME)、ReadMe 组件和前置元数据,写入 meta.ts、覆盖项和重定向,并列出需要人工处理的内容;首次构建后,其 --routes 步骤会添加端点重定向和逐标签排序(见 references/readme.md)。粘贴的控件中的 JSX 事件处理程序(onClick={…})可以顺利构建,但在静态页面上不会运行:BLUME_MDX_EVENT_HANDLER 会逐一标记它们,因此应将控件移入 island(islands/),或将其删除并在报告中说明。
  6. 采用 package.json。 将 dev / build / start 改为 blume dev / blume build / blume preview,移除旧框架依赖项,并添加 blume。只有配置文件而无清单的源项目(例如仅有 docs.json 的 Mintlify)需要新建一个清单。**在 pnpm 工作区中:如果 pnpm-workspace.yaml / .npmrc 设置了 minimumReleaseAge,只将 blume 添加到 minimumReleaseAgeExclude(不要关闭安全限制),以便安装刚发布的版本。Yarn 的 npmMinimalAgeGate 也会以同样方式拒绝新发布的 blume:将其添加到 npmPreapprovedPackages。务必在同一项更改中使用仓库自身的包管理器重新生成锁文件(从工作区根目录运行 pnpm install、yarn install、npm install 或 bun install;检查 packageManager 和锁文件名称),并与 package.json 一同提交——CI 和托管平台会基于冻结锁文件进行安装,因此过期的锁文件会在构建启动前导致失败。在内容根目录创建第一个新文件(如 meta.ts 或局部文件)后,运行 git check-ignore -v <content root>/meta.ts:如果仓库的 .gitignore 规则涵盖内容根目录,所有新文件都会在 git add 时被排除,而旧页面仍处于跟踪状态,因此需要修正规则,或说明每个新文件都需要使用 git add -f。Blume 需要 Node 22.19 或更高版本:在同一项更改中更新 .nvmrc / .node-version、engines.node 和所有 CI node-version。pnpm 11 及更高版本只有在 pnpm-workspace.yaml 批准后才会运行依赖项的构建脚本:添加 allowBuilds: { esbuild: true }(以及 blume init 写入的 "@scarf/scarf": false),除非工作区已列出 esbuild;并保留其余条目。如果文档包有自己的 .gitignore,就在其中添加 .blume/、dist/ 和 .env.local;否则添加到根目录的 .gitignore(blume init 的做法;vercel() 构建会自行添加 .vercel/),并在将资源移入 public/ 之前删除任何忽略 public/ 的行。blume build 会清空 <project>/dist:**如果宿主仓库已将 dist/ 用于其他用途(Python wheel、库构建),请将 Blume 项目放在单独文件夹中(docs/)。如果仓库使用 Prettier,请将其 *.md 覆盖规则(.prettierrc、.editorconfig)扩展到 *.mdx,并确保 {/* */} 注释位于单行内。重命名或移动页面后,在整个仓库中搜索其旧路径(发布配置、CI、bot 和忽略文件)。还要检查 Blume 项目目录中的任何 tsconfig.json:Blume 构建会读取该文件,因此如果某个 extends 仅在根工作区安装后才能解析,或 include 指向旧框架的文件夹,blume build 就会失败。先安装工作区并修复路径;如果该文件仅供旧框架使用,则将其删除(Nuxt 的 tsconfig.json 如果 extends 或 references ./.nuxt/*,会触发 BLUME_TSCONFIG_EXTENDS;postinstall: nuxt prepare 脚本也应随 Nuxt 依赖项一同移除)。**如果仓库使用(或用户希望使用)Ultracite 进行格式化:**其 oxfmt 格式化程序会破坏你刚写的 ::: 指令,除非你加入随附的 assets/[email protected],并在 patchedDependencies 中注册它——参见 references/monorepo.md §6。另见 references/monorepo.md §2–3。
  7. 接入宿主仓库并部署(较复杂的仓库)。 宿主平台保存在控制面板中的设置(Netlify 的发布目录、构建命令、NODE_VERSION)无法提交到仓库:请在总结中列出。在 .gitignore 中只替换文档自身的条目,并保留其他包会用到的条目。对于使用 Vercel 的 monorepo,提供考虑根目录的安装/构建方案和 apps/docs/vercel.json:静态构建时使用 outputDirectory: "dist";使用 deployment: vercel() 时不要设置 outputDirectory,因为其构建会写入 .vercel/output(将 .vercel/output/** 添加到 Turborepo 输出中)。告知用户无法提交到仓库的设置(Vercel Root Directory、Node 22,以及 AI_GATEWAY_API_KEY 等服务器密钥)。如果工作区固定了 Vite 版本,且 blume build 在 Astro/Vite 内部崩溃,则应用 pnpm-patch 解决方案。所有可直接复制粘贴的内容都在 references/monorepo.md §4–5。
  8. 验证。 运行 blume build(前置元数据架构、重复路由、配置——默认情况下,任何错误诊断都会导致构建失败;绝不要传入 --no-strict,该选项会强行构建,并悄然丢弃前置元数据无效或无法解析的 .mdx 页面)以及 blume validate --strict(内部链接、标题锚点、资源、重定向目标——链接检查器位于 validate 中,而非 build),修复诊断,然后运行 blume dev 进行视觉检查。最后撰写总结,说明迁移、丢弃和近似处理的内容,以及所做的每项仓库特定修改(pnpm-workspace、vercel.json、配置 glob),并说明原因,以及留给用户手动完成的步骤(Astro 补丁、Vercel 控制面板设置)。

Blume 的思维模型

对大多数来源而言,尤其是 Mintlify,最大的转变在于:导航由文件系统生成,而不是在配置中声明。

导航即文件树
  • 文件夹变为分组,文件变为页面。 页面的侧边栏标签为其 frontmatter title;分组的标签为人性化处理后的文件夹名称。
  • 排序按优先级从高到低解析: 显式的 navigation.sidebar(替换整个树)→ 文件夹的 meta.ts pages 数组 → 页面的 frontmatter sidebar.order → 文件系统(index 优先,然后按数字文件名前缀排序,例如 01-,最后按字母顺序)。
  • meta.ts 用于细化单个文件夹(defineMeta({ title, icon, order, collapsed, pages, display, directory }))。pages 数组按 slug 列出子项(去掉数字前缀、括号和扩展名;如果某个条目没有对应项,会发出 BLUME_META_UNKNOWN_PAGE 警告并提示可能的 slug,因此请填写正确的 slug);未列出的子项会按其自身的 sidebar.order 排序,然后按文件系统顺序排列(index 仍排在最前)——因此不完整的 pages 列表也安全,但如果源项目声明了完整顺序,就应列出所有子项。directory 会沿树向下继承:对于不应显示卡片列表的嵌套文件夹,设置 directory: "none"。在 flat 分组中,pages 无法将散落页面与子分组交错排列,在任何模式下也无法在侧边栏顶层交错排列:如果源项目存在交错结构(mdBook、MkDocs navigation.sections),请使用 display: "group",这样顺序会保留在分组内部,并说明顶层有哪些内容发生了移动。
  • 侧边栏渲染模式:全局默认值,支持按文件夹覆盖。 blume.config.ts 中的 navigation.sidebar.display 可以是 "flat"(默认)、"group"(可折叠)或 "page"(进入子面板),并一次性设置所有分组的模式。文件夹可以覆盖自身分组的模式:在其 meta.ts 中设置 display,或者——当文件夹有 index 页面时,可在该索引页面的 frontmatter 中使用 sidebar.display 简写。优先级:索引页面 frontmatter → meta.ts → 全局配置 → flat;覆盖只应用于对应分组(嵌套子分组按自身的优先级链解析)。因此,源项目每个类别的折叠/进入子面板模式会迁移到各自文件夹——只有当整个侧边栏都要改变时,才使用全局模式。在显式的 navigation.sidebar 下,配置项自身的 display 字段是唯一的分组级控制方式(此时会忽略 frontmatter/meta display,并发出 BLUME_SIDEBAR_DISPLAY_IGNORED 警告)。
  • 显式的 navigation.sidebar 会完全取代文件系统生成导航。 仅当文件无法表达所需导航结构时才使用它。其条目可以是页面路由字符串、分组({ label, items })或链接({ label, href })。
  • 配置声明的嵌套结构在磁盘上没有对应结构——应将其实体化,否则会被静默扁平化。 当源项目(Mintlify groups、Nextra _meta、Docusaurus 侧边栏等)声明了嵌套分组时,其页面通常平铺在同一个文件夹中,分组关系仅存在于配置里。基于文件系统生成的导航会看到平铺的文件夹,并丢弃内部嵌套分组。要保留嵌套结构,首先使用 (group)/ 文件夹:将页面移入 <folder>/(<group>)/,并使用 meta.ts 设置标签、顺序和 collapsed。带括号的文件夹会添加侧边栏分组,但不会增加 URL 路径段,因此无需更改路由或设置重定向;其他文件夹中的页面也可以通过保留旧路由的 slug 加入该分组。若分组的落地页是同级文件,也可以保留所有 URL:将落地页移至 (group)/index.md,并设置 slug: <its old route>,这样带 slug 的索引页就会成为分组链接。将页面移入更深一层的文件夹会破坏相对链接和资源路径(./img.png、../other.md),因此需要重新设置它们的路径。(Docsify 是个例外:无论路径如何,其旧 #/ URL 都会由脚本重新映射,因此它的迁移方案会将页面移入实际文件夹。)只有在上述方式无法表达所需结构时,才将页面移入真实子文件夹(URL 会改变:添加 redirects),或者在显式的 navigation.sidebar 中声明分组,这样无需移动文件即可嵌套现有路由。清点资源时,应递归遍历配置中的 pages/导航数组,并记录配置嵌套深度超过磁盘目录深度的位置;这个差距正是会丢失的部分。
标签页和选择器
  • navigation.tabs({ label, path, icon? })会渲染在页眉顶部的分区,并按路由限定侧边栏范围——标签页 path 下的文件夹会成为该分区,因此除了标签页本身无需额外配置;内容应按每个标签页一个文件夹来组织。源项目的顶层标签页(Mintlify navigation.tabs、顶层产品/分区切换器)映射为这些页眉标签页——请保留为标签页;不要将其扁平化到单个全局 navigation.sidebar 中。 Blume 会根据URL 前缀选择当前标签页(为路由添加前缀的最长标签页 path),因此标签页中的每个页面都必须位于该标签页的单个 path 下;源项目中混有任意路由的标签页无法直接迁移——要么将其页面移到同一个前缀下(路由变更 → 添加 redirects),要么接受最接近的结构,并在报告中说明选择(详情见 references/mintlify.md)。筛选会双向生效:对于位于标签页 path 下方的路由,侧边栏只显示该标签页的文件夹(当前路由位于标签页下方时,该标签页也会高亮);对于根路由或未归属标签页的路由(或 path 为 / 的标签页),标签页文件夹会隐藏,侧边栏只显示不属于任何标签页的独立页面(完整树结构会作为后备,因此侧边栏不会空白)。对迁移的影响:添加标签页后,首页侧边栏会自动隐藏分区内容——这是预期行为,并非页面丢失;不要手动为此添加排除项。
  • navigation.selectors({ kind, label, items: [{ label, path, icon?, description?, tag? }] },kind = dropdown/product/version/language)通过根据当前路由确定选项的页眉下拉菜单,对整个站点进行划分(产品、版本)。
  • navigation.actions([{ label, href }])会在页眉中、图标按钮左侧放置普通链接;navigation.cta({ label, href },单数)是页眉唯一的实心行动号召按钮。源项目的页眉栏应配置在此处——“Log in”/“Status”链接放入 actions,“Sign up”/“Get started”按钮放入 cta。http(s) 类型的 href 会在新标签页中打开;内部路由会在构建时根据页面进行验证,因此由同一主机上的其他应用提供服务的路由必须写成绝对 URL。两者在手机上(sm 以下)都会隐藏,但没有导航切换按钮的页面上的 cta 除外;文档页面中必须在手机上保留的链接应放入 featured。
  • navigation.featured({ label, href, icon? })会将链接固定在侧边栏顶部,位于所有分区上方——适用于博客、更新日志或支持页面等需要始终能一键访问的页面。这是不受标签页范围限制的例外:与生成的树状结构不同,精选链接会在所有路由和断点下显示。href 可指向任意位置——外部 URL 会在新标签页中打开,内部路由(/contact)会在构建时根据页面进行验证。与其他位置一样,icon 可以是 Lucide 名称(或图片路径/URL/内联 SVG)。源项目中始终可见的页眉/实用链接(Mintlify 锚点、博客/联系链接)应配置在此处——参见 references/mintlify.md。
路由和路径
  • 路由是相对于 content.root 的内容路径,其中会去除数字前缀(01-intro.mdx → /intro;版本号或以日期 YYYY-MM-DD、D-M-YYYY/M-D-YYYY 或 YYYY-MM 开头的名称会保留完整名称:12-05-2022.mdx → /12-05-2022;旧站点中其他以数字开头但保留完整名称的名称,应通过 slug 固定),且**(group)/ 文件夹不会增加路径段**。index 文件映射到其文件夹的路由。Frontmatter 中的 slug 会覆盖生成的路由。README.md 是路径为 /README 的普通页面,不是其文件夹的首页:请将其重命名为 index.md(Docsify、MkDocs、GitBook 和 VuePress 源项目都使用 README 作为首页)。
blume.config.ts 结构

defineConfig({...})——所有字段均为可选,并且都有默认值:

  • 站点: title、description、logo(SVG 字符串,或 { image: string | { light, dark, alt }, text, href })、banner(字符串,或 { content, link: { href, text }, dismissible, id }——没有颜色/类型)。Logo 会显示在页眉中 title 旁边,因此,字标 Logo 会重复显示品牌名(“Acme Acme”)——设置 text: "" 可只显示标志。**优先使用字符串形式,而不是 { light, dark }:**如果本地有 Logo SVG,且它是单色的(纯黑或纯白),请将其中的 fill/stroke 改为 currentColor,并使用 logo: "/logo.svg"——这样它会继承主题的文本颜色,自动适配浅色/深色模式,无需准备单独的浅色/深色文件。
  • theme:accent(两种模式共用的颜色字符串,或按模式设置的 { light, dark })、action(颜色)、mode(light/dark/system)、radius、fonts({ body, display, mono }——每项可以是精选 Google 字体的 slug、适用于任意 Google/Fontsource/Bunny/Fontshare 字体系列的 { name, provider?, weights? } 对象,或用于本地字体文件的 { name, variants: [{ src, weight?, style? }] })、background 和 backgroundImage(每项可以是字符串,或按模式设置的 { light, dark })。旧版的 accentDark/backgroundDark/backgroundImageDark 字段已合并到这些按模式设置的对象中——单个字符串仍会应用于两种模式,因此只有在两种模式不同的情况下才使用 { light, dark }。每种颜色都必须是 CSS 颜色或强调色预设(来源中的纯 "3b82f6" 需要加上 #),否则会显示 BLUME_THEME_COLOR_INVALID 警告,浏览器也会忽略该值。没有 theme.strict,也没有 theme.css 配置字段——自定义 CSS 应放在项目根目录的 theme.css 文件中(会自动读取),来源中的“严格外观”标志也应删除。
  • content:root(默认值为 "docs",相对于运行 blume 的项目目录)、include/exclude(相对于 content.root 的 glob 数组;默认值为 ["**/*.{md,mdx}"] / ["**/_*", "**/.*"]——设置 exclude 会在这些默认值基础上追加,因此局部文件和点目录不会发布;若要有意发布以下划线 _ 开头的文件或点文件,请用 ! 列出默认规则:exclude: ["!**/_*"])、sources(从 blume/sources 导入的适配器数组:filesystem({ root, include, exclude })、obsidian({ vault })、githubReleases({ owner, repo })、notion({ database })、sanity({ projectId, dataset, query })、contentful({ space, contentType })、payload({ url, collection })、strapi({ url, contentType })、mdxRemote({ github })、custom(source);所有接受选项对象的工厂还接受 prefix 和 pollInterval,而 custom(source) 接受一个自行设置 prefix 的 ContentSource 实例。1.x 的 { type: "…" } 对象已移除——将 type 改为工厂调用,并将其他字段作为选项传入;但 { type: "custom", source } 除外,它应改为 custom(source),并将实例作为唯一参数。root/include/exclude 是单个 filesystem() 的简写,不能与 sources 并列使用——请将它们移到 filesystem() 条目中。OpenAPI/AsyncAPI/GraphQL 不属于这些适配器;它们是顶层 reference 列表中的适配器)、pages(自定义 .astro 目录)、defaultType。如果文档直接位于项目目录下(没有 docs/ 子目录),请将 root 设置为该目录,并将 include 限定到实际的内容文件夹,而不是扫描所有内容——参见 references/monorepo.md §1。
  • basePath(顶层):整个站点的挂载路径(例如 "/docs"),会添加到每条路由前面,但在侧边栏中不可见(没有包装分组)。对于所有文档都通过某个前缀提供服务的来源,这是正确的配置项(Docusaurus 的 routeBasePath、Fumadocs 的 /docs baseUrl)——它与会添加导航分组的每个来源的 prefix,以及用于主机子目录的 deployment.base 不同。
  • navigation:tabs、selectors、actions 和 cta(页眉链接和一个填充样式按钮)、featured(固定在每条路由侧边栏上方的链接)、sidebar({ display, items }——display 是上述全局渲染模式;items 是显式树结构)、repo(true/false,或者在文档仓库为私有且必须保持 github 未设置时,用于页脚 GitHub 链接的绝对 GitHub URL)。除非别无选择,否则避免显式设置 navigation.sidebar——尽量使用根据文件系统生成的侧边栏。只有当文件树大致符合预期的侧边栏布局时,它才有效,因此应先调整文件夹结构;只有当文件无法表达所需结构时,才使用 sidebar.items(参见上文“配置声明的嵌套”)。
  • search——从 blume/search 导入的适配器:orama()(默认值,因此使用它时可完全省略 search)、flexsearch()、pagefind()、algolia({ appId, apiKey, indexName })、oramaCloud({ endpoint, apiKey, indexId? })、typesense({ host, collection, apiKey })、mixedbread({ storeId }),或者用 false 禁用。可直接传入适配器(search: algolia({…})),也可以在同时设置精选链接或索引选项时使用 search: { provider, popular, indexing }。Blume 1.x 的 search.provider 字符串和 search.algolia/oramaCloud/typesense/mixedbread 凭据块已移除——如果来源配置(或旧版 blume.config.ts)中有 provider: "algolia", algolia: { appId, indexName, searchApiKey },请改写为 search: algolia({ appId, indexName, apiKey: searchApiKey })(现在所有接受搜索专用密钥的适配器都使用 apiKey;管理密钥应保留在环境变量中)。ai(助手、“在聊天中打开”——ai.assistant.provider 接受从 blume/ai 导入的适配器描述符:gateway({ model })(默认值为 openai/gpt-5.5)、openai({ model })(或者,适用于任何兼容 OpenAI 的端点,使用 openai({ baseUrl, name, model, apiKeyEnv }))、anthropic({ model })、gemini({ model })、grok({ model })、openrouter({ model, reasoning })、llmgateway({ model }) 或 inkeep({ model })。每个适配器都接受 model、apiKeyEnv、headers 和原样透传的 providerOptions(headers 的值会原样写入生成的路由源码,因此只能用于非机密的静态标头——Bearer 令牌或其他任何凭据都应放在 apiKeyEnv 指定的环境变量中,绝不可放在 headers 中)。除 inkeep() 外,每个适配器也接受 reasoning:Inkeep 使用自己的回答流程,因此 inkeep({ reasoning }) 会因包含无法识别的键而导致验证失败——请从来源中删除此处的 reasoning 设置,并在报告中说明。ai.assistant 上没有扁平的 provider/model/apiKeyEnv/baseUrl/headers/reasoning 字段——以这种方式配置 AI 助手的来源(包括 Blume < 2.0)都应映射为一次适配器调用。enabled、instructions、retrieval、suggestions、cors 和 endpoint 属于 ai.assistant 本身;没有 ai.ask——Blume 2.0.0 及更早版本使用此名称,因此旧版 blume.config.ts 应将整个配置块移到 ai.assistant)、agents(llms.txt、JSON API、MCP 服务、已发布的技能、发现清单、robots 内容信号)、reference(从 blume/reference 导入的适配器列表——openapi({ spec | sources, route, … })、asyncapi({ … })、graphql({ spec, endpoint, … })——不要使用 1.x 的 openapi/asyncapi/graphql 配置块)、redirects、seo、markdown、analytics(从 blume/analytics 导入的适配器列表——每个提供方对应一个工厂(posthog({ key, host })、googleAnalytics({ id })、plausible({ domain, host })、mixpanel({ token, region })、segment({ key }) 等;文档页面列出了全部适配器)、vercel(),以及用于没有专用适配器的服务的 script({ src | content, strategy, attributes })——不要使用以提供方为键的对象)、deployment、i18n、toc、lastModified、github({ owner, repo, branch?, dir?, host?, api? }——如果来源的编辑 URL 位于 github.com 以外的 GitHub Enterprise 域名上,请设置 host)。
  • analytics——来自 blume/analytics 的适配器,包括用于来源自定义 head 脚本的 script()。如果来源的 head 脚本中包含已知分析代码片段(gtag 加载器与 gtag('config') 配对、GTM、Plausible 等),应使用对应提供方的工厂,而不是 script()。适配器只会在生产构建中加载,并会在配置了 consent 时等待用户同意,因此,页面运行所需的脚本(支持组件、小部件、表单嵌入)应放到 PageFooter 布局插槽组件中(defineComponents)。
  • deployment 是从 blume/deploy 导入的主机适配器——vercel()、netlify()、cloudflare() 或 node()——也可以是用于任意主机静态构建的普通 { site, base }(默认是静态构建)。指定主机适配器会切换到该主机上的服务器输出(若要在该主机上保持静态构建,请传入 output: "static");可命名的选项为 site、base 和 output,其他选项则会原样转发给底层的 @astrojs/* 适配器。没有 deployment.adapter 字符串,也没有 deployment.output 字段:需要服务器端渲染的来源(助手、MCP 服务、Mixedbread 搜索、API Playground 代理)应导入 import { vercel } from "blume/deploy",并使用 deployment: vercel();静态来源不需要配置——除非其文档通过子路径提供服务,此时应使用 deployment: { base: "/docs" }(关于何时添加 site,请参见下文)。请注意,cloudflare 和 vercel 也从 blume/analytics 导出——如果配置同时使用两者,请为其中一个设置别名(import { cloudflare as cloudflareDeploy } from "blume/deploy")。
  • **除非目标主机是 Blume 能自动检测的主机,否则请将来源的站点 URL 保留为 deployment.site。**Blume 只会从 Vercel 和 Netlify 的平台构建环境中填入 site(使用稳定的生产域名),并会在 blume dev 期间使用开发服务器的 localhost URL;在这两个主机上,请不要设置 site,让自动检测来确定已部署的 URL。其他所有情况——Cloudflare Pages(它只提供每次部署对应的 CF_PAGES_URL)、GitHub Pages、S3 或其他静态主机、自定义 CDN、Cloudflare Workers、node() 服务器——都无法自动检测;如果未设置 site,站点地图、OG 图片、RSS 订阅、AI 目录和绝对规范 URL 都会被静默省略。因此,如果来源配置中有 url/site 字段,而目标不是上述两个主机之一,请保留该值:静态构建使用 deployment: { site: "https://…" },主机适配器则使用其 site 选项(node({ site }))。如果无法判断站点将部署到何处,请保留该值,并在报告中说明。
  • **网站图标通过文件名约定配置,不使用配置项。**将 icon.{svg,png,ico} 或 favicon.{svg,png,ico}(以及 apple-icon.png 或 apple-touch-icon.png)放在项目根目录或 public/ 中——Blume 会自动检测。没有 favicon 配置字段。来源中以 { light, dark } 给出的网站图标应映射为一对文件:将浅色文件复制为约定名称(例如 public/icon.png),并将深色文件作为其 -dark 同级文件——使用相同目录和扩展名,并在扩展名前添加 -dark(public/icon-dark.png)。如果两个文件格式不同,请转换其中一个,使其扩展名一致;只有与已解析图标完全匹配的同级文件才会被读取。 该架构从 blume/schema 导出;完整字段参考位于已安装的 blume 包的 docs/configuration/ 目录中(如何找到该目录,请参阅下文的“完整文档”)。
图标只能使用 Lucide

Blume 会在所有接受图标的位置解析 不带前缀的 kebab-case Lucide 名称,包括 frontmatter icon、sidebar.icon、meta.ts icon、navigation.tabs/selectors 图标,以及 Card/Step/Icon/等组件的 props。它不支持 FontAwesome 或 Tabler,也没有 iconType prop。名称必须采用 kebab-case(例如 book-open,而非 BookOpen)——PascalCase 的 React 组件名称(在 Fumadocs/lucide-react 源码中很常见)无法解析,因而不会渲染。从使用其他图标集(Mintlify 和 Fern 使用 FontAwesome)的源迁移时,请将每个名称映射到最接近的 Lucide 图标;如果找不到对应图标,请移除该图标并报告。写入图标名称前,请在 lucide.dev/icons 确认其存在。

页面 frontmatter(严格模式——未知键会导致构建失败)
---
title: Install # renders as the page H1 — remove any duplicate H1 in the body
description: Install Blume and scaffold your first project.
type: doc # doc (default); blog and changelog drive feeds, other values only mean something under content.types
sidebar:
  label: Install # overrides title in the sidebar
  order: 2
  icon: download
  badge: New
  hidden: false
seo:
  title: …
  description: …
  image: /og/install.png
  canonical: https://…
  noindex: false
search:
  exclude: false
  tags: [api]
slug: install # override the generated route
draft: false
lastModified: 2026-06-20 # pin the "last updated" date
---

另外也有效的有:date/authors(博客/更新日志信息流)、changelog(更新日志元数据)、deprecated,以及顶层简写 hidden(对应 sidebar.hidden)和 noindex(对应 seo.noindex)。

编写功能(在 .mdx 中无需导入)
  • 丰富功能仅适用于 MDX。 指令、package-install、mermaid 和数学公式都接入了 MDX 处理器;在普通 .md 文件中,:::note 会保持为字面文本——构建仍会成功,只会在对应行显示 BLUME_MD_DIRECTIVE 警告。迁移时,将任何使用(或应使用)这些功能的 .md 文件重命名为 .mdx。 MDX 对文本的要求比 Markdown 更严格:任何后面紧跟非空格字符、但并未开启标签的 <(<7、<50 ms、<= 5、List<T>),以及单独出现的 { 或 } 都会导致构建失败,因此在重命名的页面中要将它们转义(使用 &lt; 或 \<,以及 \{)。使用 lastModified: "git" 时,迁移所编辑的每个页面都会显示迁移提交的日期;如果网站显示日期,请在报告中说明这一点。仅重命名或移动的页面会保留原日期(Blume 会跟踪重命名)。这对 Docusaurus/Starlight 源文件影响最大,因为它们的 .md 内容中到处都是 ::: 提示框。普通 Markdown(标题、表格、带标题/高亮的围栏代码)在 .md 中没有问题。
  • 使用指令编写提示框: :::note、:::tip、:::warning、:::danger、:::info、:::success,可选标题放在方括号中,并会渲染其中的行内 Markdown::::warning[Heads up]。别名:caution→warning、error→danger、important→note、warn→warning。需要自定义图标或色调的提示框应使用 <Callout type icon color title>(icon 使用 Lucide 图标,color 使用任意 CSS 颜色);指令无法设置这些属性。**markdown-it 源(VitePress、VuePress、Docusaurus v2)使用 ::: tip Title:冒号后有空格时,它永远不会被识别为指令,即使在 .mdx 中也是如此,而会渲染为字面文本(.mdx 中只有在提示框名称处才会显示 BLUME_DIRECTIVE_SPACED_NAME 警告);在已打开的 ::: 容器内,它会关闭该容器并丢弃其文本(BLUME_DIRECTIVE_CLOSING_TEXT);名称后的标题也会被丢弃(:::tip Title,BLUME_DIRECTIVE_OPENING_TEXT)。**请规范为 :::tip[Title]。MDC 的双冒号块(Docus,以及任何 Nuxt Content 网站中的 ::note … ::)对 Blume 来说是叶级指令,会作为文本输出(BLUME_MDC_SYNTAX);MDC 的 [text]{.class} 跨度会导致 MDX 构建失败(BLUME_MDX_ATTRIBUTE_LIST 会指出对应行)。GitHub 提示(> [!NOTE],GitHub 的五种提示,以及作为 danger 处理的 [!CAUTION])在 .mdx 中会渲染为提示框;标记后的文本会作为标题。在 .md 中,它们会保留为显示标记的引用块(BLUME_MD_GITHUB_ALERT),因此请将这些页面重命名为 .mdx。
  • 无需导入的 MDX 组件: Callout、Card/CardGroup、Columns/Column、Steps/Step、Tabs/Tab、Accordion/AccordionItem、Expandable、FileTree、Tree/Tree.Folder/Tree.File、CodeGroup、Frame、Panel、Tooltip、Tile、Badge、Icon、TypeTable/AutoTypeTable、Color、YouTube、Visibility、GithubInfo、Component、CodeBlock、Diff、Prompt、Math、Changelog,以及手写的端点字段 ParamField/ResponseField/RequestExample/ResponseExample(遵循 Mintlify 的语义:<ParamField path|query|header|body="name"> 根据字段位置命名字段;参见 docs/references/api-pages.mdx)。(不包含在内——请转换掉:<Warning>/<Note>/<Info>/<Tip>/<Check>/<Error> → 对应的 ::: 指令,以及 RequestField。像 Fern 和 Mintlify 那样,不带 code 属性、用作围栏包裹器的 <CodeBlock> 会按原样渲染围栏,但会忽略自身的 title:请移除包裹器,并将标题写入围栏元数据中。用于显示子内容、但未提供子内容且使用了其他工具属性的组件(<Badge type="tip" text="beta" />)会渲染为空,并显示 BLUME_UNKNOWN_PROP 警告:请将内容放在标签之间。目标写法请参阅参考文件。)
  • 围栏代码增强功能: ```package-install → 包管理器选项卡;```mermaid → 渲染的图表;代码块标题(```ts server.ts)、行号(lineNumbers)和高亮({1,4-5}、// [!code ++])。
  • 容易导致迁移问题的围栏和标题: Shiki 不识别的围栏语言会以纯文本形式渲染,并显示 BLUME_UNKNOWN_CODE_LANGUAGE 警告(大小写无关:JSON 和 json 的高亮效果相同):请修正语言标识符,或用 text 表示纯文本。用逗号将语言和属性连在一起的写法(```rust,ignore)也属于此类。若用空格分隔,rustdoc 和 mdBook 的关键字(ignore、no_run、should_panic、edition2021、editable 等)会显示 BLUME_CODE_FENCE_OPTION 警告,且不会出现在标题中;但任何其他第二个单词都会成为代码块标题:请只保留语言标识符。其他工具的围栏选项(hl_lines="2 3"、linenums="1"、showLineNumbers、Mintlify 的 lines、wordWrap、filename="…")不起作用,不会出现在标题中,并会显示 BLUME_CODE_FENCE_OPTION 警告;请使用 Blume 的写法({2,3}、lineNumbers、wrap、title="…")。带选项但没有语言的围栏必须补上语言(```text title="x" lineNumbers),否则第一个选项会被当作语言。语言标识符之后的每个单词都会成为标题;方括号包裹的标题(```ts [file.ts],来自 VitePress 和 MDC)会去掉方括号;紧随其后的范围([file.ts]{2})会高亮对应行。Expandable、AccordionItem 和提示框标题会渲染行内 Markdown(代码、强调、链接);Step 标题是纯字符串,会按原样显示 Markdown,且没有锚点,因此请将带锚点的标题作为 <Step> 内的第一行。原始 HTML <img src> 和 <a href> 永远不会经过优化:指向页面旁文件的相对路径会与页面一起发布(与 [spec](./spec.pdf) 和 <Card img="./cover.png"> 一样);根路径会像 Markdown 链接一样添加 deployment.base;blume validate 会检查这两种情况。但指向页面的相对 <a href> 会根据页面 URL 解析,而非根据页面所在文件夹解析。请将图片转换为 Markdown,以便进行优化;页面链接则转换为 Markdown 链接或根路由。
  • 数学公式: $$…$$ 在 .mdx 中无需任何配置即可渲染(不存在 markdown.math 字段)——可单独成行作为块级公式,也可在句子中作为行内公式。单个 $ 始终会保留为字面文本,因此请将每个行内 $…$ 原地转换为 $$…$$,放在句子中(不要另起一行),并检查每一对符号确实是数学公式,而非货币金额或 shell 变量。
OpenAPI

reference: [openapi({ sources: [{ spec, label?, route? }] })] — 从 blume/reference 导入的 openapi() 适配器(spec 是单源简写)——会为每个操作生成一个真实页面,并免费提供路由、侧边栏、搜索和 OG 图片。引用不会自动获得页眉标签——请添加一个指向适配器 route 的 navigation.tabs 条目(引用路由是有效的标签目标),否则无法从页眉访问 API 引用。切勿手动迁移生成的 API 引用页面(源文件中的每个端点占位页面):删除它们,并将 openapi() 指向规范。若要保留某个源的 Scalar 嵌入内容,请在 reference 中用 scalar({ spec, theme?, …scalarOptions })(同样从 blume/reference 导入)代替 openapi():它会将 OpenAPI 或 AsyncAPI 文档渲染为每个源一个嵌入页面,将所有未明确命名的键原样传递给 Scalar,并且不接受原生显示选项(codeSamples、expandSchemas、playground)。openapi()/asyncapi() 没有 renderer 选项——使用该选项会导致验证失败。Blume 1.x 的顶层 openapi/asyncapi/graphql 块已移除——如果源配置(或旧版 blume.config.ts)中包含 openapi: { enabled: true, ... },请将其改写为 reference 中的条目,并删除 enabled;带有 renderer: "scalar" 的 1.x 块应改为单独的 scalar({ … }) 条目,并保留该块的 route、sources 和 noindex,同时将其 theme 以及 scalar: { … } 对象中的键直接传给 scalar()(scalar({ spec, theme: "purple", localization }))。

  • 默认将规范纳入仓库。 远程 spec: URL 会让每次构建都依赖在构建时获取规范——这会在 CI、离线环境或代理后造成单点故障,且获取失败会跳过整个引用。建议将规范提交到仓库(openapi/<name>.json),并让 spec 指向本地路径;如果源站将其放在 public/openapi.json,请移动它(如果读者需要下载,则放到 public/specs/openapi.json):该 URL 是 Blume 发布其自身文档 API 描述的位置(BLUME_PUBLIC_OPENAPI_JSON)。如果保留 URL,请明确说明,并考虑添加一个 prebuild 步骤,在刷新本地副本时提供回退方案。
  • 操作路由有自己的 slug 规则——<route>/<slugified-tag>/<slugified-operationId>(例如,标签 Models、ID listModels → /api-reference/models/list-models)。驼峰式或帕斯卡式标签或操作 ID 会在词界处分割成 kebab-case,然后再生成 slug(InboxesThreads → inboxes-threads,getHTTPResponse → get-http-response),因此不要只将其转为小写;如果操作没有 operationId,则使用其方法和路径(GET /pets/{id} → get-pets-id)。slug 只需在其标签内保持唯一:同一标签中的第二个 list 会加上其方法(list-post)。这通常与源站的端点链接不一致(Mintlify 等平台采用不同的 kebab-case 规则),因此请重写所有指向操作的入站链接。blume validate 会像解析其他路由一样解析操作页面,因此能发现遗漏的链接。
  • 规范在其自身路径上提供服务时(Redocly 会在 /apis/x/openapi 提供 apis/x/openapi.yaml):设置源站的 route,这样在侧边栏允许的情况下,概览页可以保留旧 URL(源站的引用说明了文件夹路由何时更合适);操作、标签和分区 URL 仍需要重定向。
  • 操作 URL 重定向来自旧站点地图,每个操作对应一个精确条目,应通过生成而非手动输入来创建:先构建一次,然后运行 node <skill>/scripts/operation-routes.mjs dist,它会打印每个引用中的端点(GET /pets/{id})及其对应的 Blume 路由,这些信息取自构建结果(--spec <route>=<file.json> 可添加 operationId);通过规范将每个旧 URL 与其端点匹配。在添加重定向前,先重写入站链接:blume validate 会接受任何重定向的 from 作为有效链接目标,因此重定向存在后,它就不会再标记旧格式的链接。
  • 标签名称和顺序来自规范。 标签的 x-displayName 用作其分组名称;标签按规范的 tags 列表排列,操作则按规范中写出的路径和方法排列。系统不会读取 x-tagGroups:如果源站按其排序,请为每个标签添加 <route>/<tag-slug>/meta.ts → defineMeta({ order }),并将其位置设为展平后的 x-tagGroups 中的位置。标签文件夹的 meta.ts 会与生成的设置合并,因此只需设置有差异的内容(title、order 或用于调整操作顺序的 pages 列表)。在有范围限制的 content.include 下,添加一个能匹配标签文件夹的 glob,否则系统永远不会读取该文件(BLUME_META_OUTSIDE_INCLUDE)。
  • 附加在生成的端点页面上的源站说明文字(ReadMe 和 Mintlify 支持此功能)应放在对应操作的 description 中,并通过 overlay 设置:Markdown 和 ::: 指令会在操作页面上渲染,但 JSX 不会(< 和 { 会被转义)。
  • 以非 OpenAPI 格式定义的 API(例如 Fern Definition)需要先导出为 OpenAPI;源站的引用说明了具体方法。
  • Try it 请求会直接从读者的浏览器发出。 对每个 API 服务器检查 CORS(curl -si -X OPTIONS -H 'Origin: https://<docs-host>' -H 'Access-Control-Request-Method: GET' <server>/<path>);将没有匹配的 access-control-allow-origin 的服务器列为后续处理项(在服务器输出型主机上,playground: { proxy: true } 可解决此问题),或者对这些服务器关闭 playground。
  • 保留手写的概念性页面。 源站通常会在同一标签页中,将手写的“Introduction/Authentication”页面与端点组放在一起。放置在 openapi() 适配器 route 下的常规内容页面会合并到引用标签页的侧边栏中——因此请保留这些页面(身份验证、错误、速率限制),只删除逐个端点的占位页面。
GraphQL

reference: [graphql({ spec, endpoint? })] 是 GraphQL 对应的配置:Blume 会将架构(SDL 文本或 introspection JSON 结果,本地路径或 URL)转换为每个根字段一个页面(按 Queries/Mutations/Subscriptions 分组),以及每个命名类型一个页面(Objects、Input Objects、Enums、Interfaces、Unions、自定义 Scalars)。上述所有 OpenAPI 规则同样适用:绝不要手动迁移源项目生成的 GraphQL 参考页面(将其删除,并将 spec 指向架构),将远程架构存储在本地,将手写的概念页面放在 route 下(默认为 /graphql),添加 navigation.tabs 条目,并重写指向这些页面的链接——路由格式为 <route>/<slugified-group>/<slugified-name>(例如 /graphql/queries/pets、/graphql/objects/pet),blume validate 会像解析其他页面一样解析它们。与 openapi() 适配器的区别:

  • 将 endpoint 设为在线 GraphQL URL。 架构不像 OpenAPI 文档,不会指定服务器——endpoint 是 Try It 交互式操作区和代码示例的目标(不设置时,它们会显示占位 URL;而 playground: { proxy: true } 会在构建时发出警告,因为代理没有可允许的来源)。使用多个架构时,配置为 sources: [{ spec, endpoint?, label?, route? }];单个源的 endpoint 会覆盖适配器级别的设置。
  • 不支持 Scalar 嵌入——GraphQL 架构没有对应的 scalar();graphql() 也不接受 expandSchemas。参考内容始终由 Blume 渲染(Scalar 嵌入只读取 OpenAPI 和 AsyncAPI 文档)。源项目的 GraphQL 操场/浏览器嵌入(GraphiQL、Apollo Explorer)没有直接对应功能,内置的 Try It 面板除外;如果该嵌入提供了面板不具备的功能,请报告。
  • 只接受 SDL 和 introspection JSON。 如果源项目通过编程方式构建架构(代码中的 GraphQLSchema 实例),必须将其打印为 SDL(使用 graphql 中的 printSchema)并提交;请报告此转换。
更新日志

如果源项目有手动维护的更新日志(changelog.mdx、按日期组织的条目文件夹、Mintlify <Update> 区块、ReadMe 的 changelogs/ 文件夹),且其发行说明存放在该仓库的 GitHub releases 中,请提议用 githubReleases() 内容源替代——发行说明会自动成为更新日志,无需维护文件。这只是提议,并非自动重写:有些团队会维护经过整理的更新日志,内容与 GitHub releases 并非一一对应,因此在删除页面前,请先确认发行说明是唯一可信来源。

将其与文件系统内容源一同添加到 content.sources 下(两者均从 blume/sources 导入;设置 sources 后,内容根目录移入 filesystem()):

import { filesystem, githubReleases } from "blume/sources";

content: {
  sources: [
    filesystem({ include: ["docs/**/*.mdx"], root: "." }),
    githubReleases({
      owner: "haydenbleasel",
      repo: "ultracite",
      prefix: "changelog",
    }),
  ],
},
  • 每个发行版本都会在 /<prefix>/ 下生成一个 type: changelog 页面(prefix: "changelog" → /changelog/…);省略 prefix 即挂载到根目录。
  • 可选字段:limit(限制生成的发行版本数量,按时间从新到旧排列,默认值为 100)、prereleases(包含预发行版)、drafts(包含草稿——需要具有仓库写入权限的令牌)、pollInterval(开发环境轮询间隔,单位为秒;省略则在当前会话期间冻结)。
  • 私有仓库会从 GITHUB_TOKEN 读取令牌;令牌绝不会直接写入配置。该适配器会声明此令牌,因此未设置时,blume dev/build 会发出警告;公开仓库无需令牌也能正常运行。
  • 接入内容源后,删除旧的更新日志页面(并添加从旧路由到新 /<prefix>/… slug 的 redirects)。如果原来有标题栏/侧边栏链接,请使用 navigation.featured 固定该链接。若要在列表上方保留旧更新日志的简介,请编写 changelog/index.mdx(mode: center),包含简介和 <Changelog />;后者会渲染生成的发行版本列表。
重定向是静态的

在 blume.config.ts 中使用 redirects: [{ from, to, status? }] 数组,可在调整路由结构时映射旧 URL——Blume 会自行处理这些重定向,因此任何会移动页面的重组(每个标签页一个文件夹、物化嵌套分组、重命名 slug、将页面提升为索引页)只需在这里添加一条记录即可修复,无需配置托管服务。**重组是这类问题的主要来源:**第 4 步中移动的每个页面(每个标签页一个文件夹、重命名 slug、将页面提升为索引页)都需要添加一条记录,否则旧 URL 会返回 404。status 默认为 301(永久重定向——浏览器会无限期缓存);这适用于真正的页面迁移,但绝不要对可能撤销的重定向使用 301/308。模式会按原样传递::name 表示一个路径段,:name* 或末尾的 * 表示其余路径({ from: "/beta/:slug*", to: "/v2/:slug*" })。模式不能同时匹配某个页面;如果模式覆盖了保留的页面,构建就会失败(BLUME_REDIRECT_MATCHES_PAGE),因此请缩小匹配范围。模式无法表达的规则(正则参数、标头或 Cookie 条件)应移至托管服务级配置(_redirects、vercel.json);并报告这些规则。

如果重定向的 from 与某个页面的路由相同(包括生成的参考概览页),就会发出警告 BLUME_REDIRECT_MATCHES_PAGE,且重定向优先生效:该页面永远不会发布。请删除重定向或移动页面。页面的 Markdown 镜像(/x.md、/x.mdx、首页的 /index.md)也同样如此:Agent 将无法访问它,因此请删除重定向。也绝不要将页面自身的文件夹 URL 设为重定向来源(如 /x/,常见于源站提供带尾斜杠 URL 的情况):它同样会覆盖页面。对于 /x/index.html 的重定向(源站提供 .html URL),它无害但没有必要:静态托管服务会通过 x/index.html 在该路径提供页面,因此静态构建不会为它写入重定向页面,只有托管服务文件(_redirects、vercel.json)会保留这条规则。redirects 只匹配路径:页面 URL 位于 # 之后时,不会到达重定向规则(参见 references/docsify.md)。如果旧站点托管在你无法控制的平台(例如 GitHub wiki),就无法将重定向指向其 URL:不要承诺提供重定向(参见 references/github-wiki.md,了解可行的处理方式)。尾斜杠无需单独设置重定向:blume dev 和 blume preview 会对页面和重定向来源一视同仁,将 /a/ 以 301 重定向到 /a(保留查询参数),与 Vercel 的行为一致;其他静态托管服务会通过 a/index.html 提供 /a/,而服务端构建(vercel()、node())则会让重定向的 from 匹配 /a/。静态托管服务的重定向规则是否匹配带斜杠的 from 取决于具体服务,因此旧链接使用了尾斜杠时,请在预览部署中检查一个链接。在 Netlify 上,请设置 deployment: netlify({ output: "static" }):只有该构建的 _redirects 规则会强制生效(301!),否则 Netlify 会以 200 状态提供 Astro 在每个 from 路径写入的 meta-refresh 页面,而不是真正的 301(直接使用 netlify() 会切换为服务端输出)。

验证与报告

  1. 运行 blume build — 它会验证 frontmatter 架构、重复路由和配置,并默认在出现任何错误诊断时失败(不要传入 --no-strict:该选项仍会构建,并静默丢弃 front matter 无效或无法解析的 .mdx 页面)。然后运行 blume validate --strict — 链接、标题锚点、资源和重定向目标在这里验证,而不是在 build 中验证(添加 --external 还会检查出站 HTTP 链接)。OpenAPI 操作页面是真实路由,validate 也会检查它们,因此指向这些页面的失效链接也会被发现。持续迭代,直到两项检查都通过。将 build(或 blume check)打印的所有关于页面中存在其他工具语法的警告(BLUME_TEMPLATE_TAG、BLUME_MDC_SYNTAX、BLUME_WIKILINK_UNSUPPORTED、BLUME_MD_*、BLUME_CODE_FENCE_OPTION、BLUME_MDX_* 等)视为遗漏的转换:每条警告都会指出文件和行号,以及 Blume 中对应的写法,而 validate --strict 会因这些问题失败。validate 会按照页面渲染后的内容读取链接:被更早出现的同标签链接定义覆盖的链接定义不会被检查(它是无效文本;请删除),注释中的链接也不会被检查。validate --strict 还会因 OpenAPI 规范警告(BLUME_OPENAPI_*)而失败,例如未声明的路径参数、localhost 服务器,或 3.1 规范中的 nullable。这些问题无法通过编辑内容解决:请在源文件的 overlays 中使用 OpenAPI Overlay 修复,以免改动生成的规范或第三方规范,并将问题报告给源文件维护者处理;对于 Blume 没有生成器的 codeSamples ID(BLUME_OPENAPI_UNKNOWN_CODE_SAMPLE),则应从配置中删除。如果添加了重定向,请在构建后运行 blume audit --only redirects:对于 to 指向不存在目标的重定向,validate 会发出警告(位置为 blume.config.ts 中对应的行),但如果 to 是另一个重定向的 from,则会接受。因此,只有 audit 会在构建后逐条跟踪重定向,并报告循环和链式重定向。若要检查所有旧 URL,可向 blume dev 或 blume preview 发起请求(curl -sL;两者都会应用所有重定向,包括模式重定向,并返回对应的状态码),也可以根据 dist/ 和重定向规则检查(未设置主机适配器时使用 dist/blume-redirects.json,否则使用主机的 _redirects 或 vercel.json)。**旧标题锚点:**每种源站生成标题 ID 的方式都与 Blume 不同。首次构建后,运行 node <skill>/scripts/pin-heading-ids.mjs --old <old site URL or its built-HTML dir>(页面迁移时使用 --map dist/blume-redirects.json,未设置主机适配器时构建该文件,使用 --write 应用更改,使用 --only-linked 仅固定有链接指向的 ID),然后重建,直到报告 0;在服务器构建中,它会自行读取 dist/client(ReadMe 和 GitHub wiki 除外:它们的标题 ID 已与 Blume 匹配,ReadMe hub 会对抓取请求限流,而且脚本无法读取 wiki 页面)。任何剩余的 BLUME_BROKEN_ANCHOR 在旧站上原本就已失效,因此请将明显的链接改为正确目标,删除其余链接的片段,并报告这些问题。
  2. 运行 blume dev,并从视觉上检查网站(如果 blume dev 或 preview --port N 中指定的 N 已被占用,它们会改用下一个空闲端口,因此请求 URL 前请先读取它们打印的 Local http://… 行)——检查导航结构、选项卡、主题和渲染后的组件。
  3. 撰写迁移摘要,涵盖:迁移了哪些内容(配置、N 个页面、导航、API 参考)、舍弃了哪些内容(导航栏 CTA、自定义主题、条件重定向、无法映射的图标、不受支持的组件),以及建议的后续操作(使用 blume eject 完全掌控,使用 blume add 将组件引入项目并进行自定义)。

完整文档

映射详情位于 references/ 中:每种源框架对应一个文件(mintlify.md、docusaurus.md、fumadocs.md、nextra.md、starlight.md、vitepress.md、vuepress.md、mkdocs.md、mdbook.md、docus.md、fern.md、gitbook.md、readme.md、redocly.md、docsify.md、jekyll.md、github-wiki.md),此外还有用于宿主仓库集成的 monorepo.md(内容布局检测、pnpm minimumReleaseAge、重新生成冻结锁文件、Vercel monorepo 配方,以及 Astro/Vite 补丁)。Mintlify 图标和 frontmatter 处理流程由 scripts/mintlify-codemod.mjs 自动执行(零依赖、确定性、幂等;使用 --write 应用更改)。Docsify 内容处理流程由 scripts/docsify-codemod.mjs 自动执行(零依赖、确定性、幂等;使用 --write 应用更改,使用 --snapshot <dir> 记录旧标题 ID,供 pin-heading-ids.mjs 使用,使用 --group-folders 为每个侧边栏分组创建一个文件夹及路由表)。Fern 的文件放置、frontmatter、组件和重定向处理流程由 scripts/fern-codemod.mjs 自动执行(零依赖、确定性、幂等;使用 --write 应用更改,首次构建后运行 endpoints)。ReadMe 内容处理流程由 scripts/readme-codemod.mjs 自动执行(零依赖、确定性、幂等;使用 --write 应用更改,构建后使用 --routes <file> 处理端点重定向)。MkDocs 内容处理流程由 scripts/mkdocs-codemod.mjs 自动执行(零依赖、确定性、幂等;使用 --write 应用更改)。Jekyll 内容和导航处理流程由 scripts/jekyll-codemod.mjs 自动执行(零依赖、确定性;仅运行一次,写入 jekyll-migration.json,并拒绝处理已有该文件的文件夹;使用 --write 应用更改,使用 --old <build> 处理 URL 和侧边栏,使用 --callouts 处理 _config.yml 未指定任何提示框的站点)。GitHub wiki 处理流程由 scripts/github-wiki-codemod.mjs 自动执行(零依赖、确定性、幂等;routes 写入路由表,convert --write 应用转换,check 根据构建产物验证路由表和旧 URL,stubs 为旧 wiki 写入链接存根)。Docus 内容处理流程由 scripts/docus-codemod.mjs 自动执行(零依赖、确定性、幂等;使用 --write 应用更改)。VitePress 内容处理流程由 scripts/vitepress-codemod.mjs 自动执行(零依赖、确定性、幂等;使用 --write 应用更改,使用 --redirects <old build> 生成 .html 重定向列表)。重定向所需的操作路由来自 scripts/operation-routes.mjs(零依赖、只读;读取已构建的 dist/);部分文件和根目录外的引用由 scripts/include-excerpts.mjs 生成(零依赖、确定性;复制到项目中后,它会在每次构建前运行);旧标题 ID 由 scripts/pin-heading-ids.mjs 固定(零依赖;默认报告,使用 --write 应用更改)。权威的 Blume 文档随已安装的软件包一同打包,位于其 docs/ 目录中——不一定在仓库根目录:在工作区 monorepo 中(尤其是 pnpm),该软件包位于依赖它的工作区的 node_modules 中(例如 apps/docs/node_modules/blume/docs);从依赖该软件包的目录运行 node -e "console.log(require.resolve('blume/package.json'))",即可打印确切位置。(在 Blume 仓库检出中,文档源文件位于 apps/docs/content/docs。)以下页面最为相关:

  • configuration/index.mdx — blume.config.ts 中的所有字段。
  • content/navigation.mdx — 侧边栏、标签和选择器模型。
  • content/meta.mdx — meta.ts 和显示模式。
  • content/syntax.mdx — 指令、代码功能和数学公式。
  • content/components.mdx — 组件库和 API。
  • content/frontmatter.mdx — 严格的页面架构。

评论

支持 Markdown