@amamo/mdx
本页目录

配置

defineConfig 会原样返回参数。normalizeConfig 才是运行时边界:它把集合的 Zod schema 转成 JSON Schema、拒绝其它非纯数据、应用默认值、解析路径,并返回所有编译器和适配器共用的 IAmamoMDXConfig

TypeScript
import { defineConfig, z } from '@amamo/mdx'

export default defineConfig({
  collections: {
    posts: {
      directory: 'content/posts',
      schema: z.object({}),
    },
  },
})

至少需要一个集合。

顶层配置

默认值作用
rootprocess.cwd()相对配置路径的基准目录。
collections必填内容目录、schema、locale 和 slug。
mdx见下文MDX 语法、数学公式和 JSX runtime 选项。
highlight启用 Shiki语法高亮策略;设为 false 可禁用。
media启用重写Markdown 媒体 import 策略;设为 false 可禁用。
derived全部关闭阅读时间和最后修改时间。
manifests{}命名 JSON 投影。
cache启用持久化编译记录;设为 false 可禁用。
generatedDirectory.amamo-mdx注册表、声明文件和 Next 私有索引目录。

除集合的 Zod schema 外,函数、symbol、访问器、类实例、循环引用、undefinedbigint 和 非有限数字都会被拒绝。检查发生在配置模块本身已经 import 之后,因此不会沙箱化那个模块。

集合

TypeScript
import { z } from '@amamo/mdx'

collections: {
  posts: {
    directory: 'content/posts',
    extensions: ['.mdx'],
    locales: { default: 'en', names: ['en', 'zh-CN'] },
    schema: z.object({
      title: z.string(),
      draft: z.boolean().default(false),
    }),
    slug: { indexNames: ['index', 'page'] },
  },
}
默认值行为
directory必填root 解析;调用 build() 前目录必须存在。
extensions['.mdx']纳入集合的后缀;每项必须以 . 开头。
locales可选的基于文件名的 locale 映射。
schema必填校验 YAML frontmatter 的 Zod object schema。
slug.indexNames['index', 'page']推导 slug 时省略的 basename。

包内维护 Zod,并对外提供兼容的 z builder。配置边界是结构化的 IFrontmatterSchema 接口, 因此也可以传入其它兼容的 object schema。Object schema 会转换为 JSON Schema Draft 2020-12; 原始 JSON Schema 对象会被拒绝。Rust 会在校验前应用 .default() 值,但不会执行 Zod parse、 类型转换、transform 或自定义 refinement。请只使用可表示为 JSON Schema 的类型和内置检查。 诊断可能包含用户提交的值;不要把原始构建错误暴露给不可信用户。

配置 locale 后,无后缀文件使用默认 locale;page.zh-CN.mdx 这样的已知后缀会选择对应 locale。 默认 locale 必须出现在 names 中。文档身份推导如下:

text
posts/guide.mdx          -> slug "guide", key "en:guide", locale "en"
posts/guide.zh-CN.mdx    -> slug "guide", key "zh-CN:guide", locale "zh-CN"
posts/index.mdx          -> slug "/",     key "en:/",     locale "en"

没有 locale 配置时,key 就是 slug。完整构建会拒绝同一集合内的重复 key。

Next 适配器目前只注册 *.mdx。直接编译器和 Vite 插件可以处理自定义集合后缀,但内置 Next loader 不可以。

MDX

TypeScript
mdx: {
  extensions: {
    footnotes: true,
    headingIds: false,
    taskLists: true,
  },
  gfm: true,
  hardBreaks: false,
  jsxImportSource: 'react',
  math: false,
  providerImportSource: '',
}
默认值行为
extensions见下文可选 Markdown 语法。
gfmtrue启用整套 GFM;脚注与任务列表可以单独覆盖。
hardBreaksfalse把文本节点内的换行转换成 <br>
jsxImportSource'react'编译模块使用的 JSX runtime 包。
math关闭构建期数学解析与渲染;设为 {} 启用。
providerImportSource''可选的 MDX 组件 provider import;空字符串表示不导入。

手写的 ESM 和 MDX JSX 仍会进入编译模块。providerImportSource 控制 MDX 组件 provider 的 接线,不是保留或删除 JSX 的开关。

Markdown 扩展

每项受支持的扩展都可以独立启用:

默认值行为
footnotes继承 GFM引用[^id][^id]: 定义
headingIdsfalse根据静态标题文字生成 ID。
taskLists继承 GFM- [ ] 待办- [x] 完成

显式设置 footnotestaskLists 时以显式值为准,否则它们跟随 gfm。因此可以在关闭其余 GFM 语法时单独启用它们,也可以在保留表格、自动链接等 GFM 语法时单独关闭。

生成的脚注锚点会按文档增加命名空间,因此同一页面渲染多篇编译文档时不会产生重复 ID。自动 标题 ID 会把文字转成小写、保留 Unicode 字母与数字、把其余连续字符替换为连字符,并为重复 标题追加 -2-3。需要手动固定 ID 时,可以直接写 <h2 id="custom-id">标题</h2>

这里不额外定义高亮、上下标或定义列表的缩写语法;无需配置即可直接使用标准 MDX 元素 <mark><sub><sup><dl>

数学公式

TypeScript
mdx: {
  math: {
    singleDollar: true,
    macros: {
      '\\RR': '\\mathbb{R}',
    },
  },
},

数学语法需要显式启用。mdx.math: {} 会渲染行内 $x$ 与块级 $$ 公式;mdx.math: false 会把美元符号继续当成普通文字。singleDollar: false 可关闭 $x$ 行内形式,适合货币内容较多的站点。 使用默认值时,文字中的美元符号应写成 \$

macros 把单个 TeX 控制序列映射到固定展开值。每个展开值不超过 1 KiB,命令名与展开值合计 不超过 16 KiB。参数占位符以及 \\def\\newcommand 等动态定义命令会被拒绝。配置会复制到每个 公式的独立解析器中,因此定义不会在公式或文档之间泄漏。

RaTeX 在 Rust 编译器内完成解析、排版与渲染。编译模块只包含自包含 SVG 输出,不需要浏览器 数学运行时、样式表、webfont、原始 HTML 或 dangerouslySetInnerHTML。非法 TeX 产生 AMAMO_MATH_PARSE;不安全、外部引用或无法嵌入字形的 SVG 产生 AMAMO_MATH_RENDER,不会 静默回退到外部字体。KaTeX 字体未覆盖的 Unicode 字形可能使用构建机上的系统字体,因此不同 构建机生成的轮廓可能不同。只有当构建机的回退字体许可嵌入与再分发时,才应发布这些字形。

每个表达式最多包含 64 KiB TeX、100,000 个展开 token、8 MiB 或 100,000 个节点的解析 AST、 合计 100,000 个展开后的 array 单元格、10,000 个排版项、任一方向 10,000 em、16 MiB SVG 与 100,000 个 SVG 节点。语法与结构限制失败会产生 AMAMO_MATH_PARSE;排版或 SVG 限制失败会产生 AMAMO_MATH_RENDER

行内 SVG 使用 RaTeX 的 depth 对齐基线,并通过 currentColor 继承文字颜色;显式 TeX 颜色仍会 保留。外层元素提供 role="math",并把原始表达式作为无障碍标签。RaTeX 当前不生成 MathML, 因此公式文本不可选择,辅助技术接收到的是 TeX 标签而不是 MathML 树。

语法高亮

TypeScript
highlight: {
  provider: 'shiki',
  engine: 'oniguruma',
  languages: 'auto',
  themes: { light: 'vitesse-light', dark: 'vitesse-dark' },
  unknownLanguage: 'error',
  colorReplacements: {},
}
默认值行为
provider'shiki'固定字面量;目前不提供其它 provider。
engine'oniguruma'Shiki 的 onigurumajavascript 引擎。
languages'auto'按需加载,或预加载列出的内置语言。
themesVitesse 明暗主题Shiki 内置主题名。
unknownLanguage'error'拒绝未知语言,或按 'plain' 渲染。
colorReplacements{}Shiki 的颜色替换映射。

显式语言数组只是预加载这些语法,不会阻止之后按需加载其它语言。Shiki 在内存中复用语言加载; 高亮结果保存在文档缓存记录内,不存在单独的磁盘高亮缓存。

设置 highlight: false 可跳过代码块高亮。

媒体

TypeScript
media: {
  attributes: {
    audio: ['src'],
    embed: ['src'],
    img: ['src', 'srcset'],
    object: ['data'],
    source: ['src', 'srcset'],
    track: ['src'],
    video: ['src', 'poster'],
  },
  missing: 'error',
}

上表是默认属性映射。只要提供 attributes,就会替换整张默认表;请把仍需重写的标签全部列出。

由 Markdown 元素产生的相对 URL 会从源文件解析并变成静态 import。绝对路径、fragment、 协议相对 URL、data URL 和带 scheme 的 URL 会原样通过。手写的 MDX JSX(例如 <img src="./manual.png" />)不会被重写。

missing结果
'error'拒绝这次全新编译。
'warn'保留原始 URL,并向 record.diagnostics 添加 AMAMO_MEDIA_MISSING

直接编译器和 Vite 适配器不会自动打印 warning。设置 media: false 可禁用重写。

派生字段

TypeScript
derived: {
  lastModified: false,
  readingTime: false,
}

readingTime 排除代码,把每个连续 ASCII 单词或每个中日韩字符计为一个单位。分钟数为 ceil(units / 300),并且最少一分钟。

lastModified 优先使用文件最近一次 Git commit 的时间,取不到时回退到文件系统修改时间。启用 的值会出现在 record.derived 中,也会成为编译模块的具名导出。

Manifest

TypeScript
manifests: {
  public: {
    output: '.amamo-mdx/public.json',
    collections: ['posts'],
    sort: [{ field: 'publishedAt', direction: 'desc' }],
    fields: {
      key: 'key',
      title: 'title',
      publishedAt: { from: 'publishedAt', default: null },
    },
  },
  server: {
    output: '.amamo-mdx/server.json',
    key: 'key',
    fields: {
      key: 'key',
      tokenPresent: { from: 'accessToken', transform: 'exists' },
      tokenFingerprint: { from: 'accessToken', transform: 'sha256' },
    },
  },
}
默认值行为
output必填root 解析的 JSON 路径,与 generatedDirectory 无关。
collections全部集合纳入这个 manifest 的集合名。
fields必填输出字段到源投影的映射。
sort[]有序的投影字段排序键;direction 默认 asc
key不设置时输出数组;设置后输出以这个投影字段为键的对象。

配置 key 后,对应名称必须存在于投影记录中,并解析成唯一的 string、number 或 boolean。 缺失、null、object、array 和重复值都会让构建失败。缺失或 null 的排序值在升降序中都保持最后, 最终用文档 key 打破平局。

投影路径可以读取顶层 frontmatter,或以下保留值:

  • collectionfilekeylocaleslug
  • frontmatter.<path>
  • derived.<path>

字段值可以是路径字符串,也可以是 { from, transform?, default? }

Transform结果
复制源值;不存在时使用 default,再不存在则为 null
'exists'源值存在且非 null 时为 true
'sha256'字符串或 JSON 序列化值的小写 SHA-256 hex;缺失时使用 default 或 null。
'mediaUrl'绝对或外部 URL 原样通过;相对字符串映射为 root-relative URL。

mediaUrl 只是路径投影,不会检查文件存在性,也不会创建静态 import。

缓存

TypeScript
cache: {
  directory: '.amamo-mdx/cache',
}

缓存键是 BLAKE3 digest,输入包括缓存格式、原生 crate 版本、OS、架构、目标模式、归一化配置 和运行时包版本、源码路径和字节,以及已启用的 lastModified 值。命中缓存还要求之前记录的 所有媒体依赖仍然存在。

损坏记录会被删除并重新编译,同时产生 AMAMO_CACHE_CORRUPT warning。成功的完整构建会清理 已经不再被发现内容引用的记录。

设置 cache: false 可关闭持久化记录。直接 API 和 Vite 仍可编译,但 Next loader 没有缓存 文件就无法工作。

生成产物

TypeScript
generatedDirectory: '.amamo-mdx'

编译器固定在这里写三个共享产物:

  • collections.mjs
  • collections.d.ts
  • index.json

缓存和 manifest 位置彼此独立。集合、缓存、生成目录和 manifest 的相对配置路径从 root 解析, 而 Markdown 媒体路径从所在源文档解析。root 默认是 process.cwd();已存在的 root 和集合 目录会在归一化时 canonicalize。只要配置可能从其它工作目录加载,就应显式设置 root

Copyright © 2026 白熱.