配置
defineConfig 会原样返回参数。normalizeConfig 才是运行时边界:它把集合的 Zod schema 转成
JSON Schema、拒绝其它非纯数据、应用默认值、解析路径,并返回所有编译器和适配器共用的
IAmamoMDXConfig。
import { defineConfig, z } from '@amamo/mdx'
export default defineConfig({
collections: {
posts: {
directory: 'content/posts',
schema: z.object({}),
},
},
})至少需要一个集合。
顶层配置
| 键 | 默认值 | 作用 |
|---|---|---|
root | process.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、访问器、类实例、循环引用、undefined、bigint 和
非有限数字都会被拒绝。检查发生在配置模块本身已经 import 之后,因此不会沙箱化那个模块。
集合
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 中。文档身份推导如下:
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
mdx: {
extensions: {
footnotes: true,
headingIds: false,
taskLists: true,
},
gfm: true,
hardBreaks: false,
jsxImportSource: 'react',
math: false,
providerImportSource: '',
}| 键 | 默认值 | 行为 |
|---|---|---|
extensions | 见下文 | 可选 Markdown 语法。 |
gfm | true | 启用整套 GFM;脚注与任务列表可以单独覆盖。 |
hardBreaks | false | 把文本节点内的换行转换成 <br>。 |
jsxImportSource | 'react' | 编译模块使用的 JSX runtime 包。 |
math | 关闭 | 构建期数学解析与渲染;设为 {} 启用。 |
providerImportSource | '' | 可选的 MDX 组件 provider import;空字符串表示不导入。 |
手写的 ESM 和 MDX JSX 仍会进入编译模块。providerImportSource 控制 MDX 组件 provider 的
接线,不是保留或删除 JSX 的开关。
Markdown 扩展
每项受支持的扩展都可以独立启用:
| 键 | 默认值 | 行为 |
|---|---|---|
footnotes | 继承 GFM | 引用[^id] 与 [^id]: 定义。 |
headingIds | false | 根据静态标题文字生成 ID。 |
taskLists | 继承 GFM | - [ ] 待办 与 - [x] 完成。 |
显式设置 footnotes 或 taskLists 时以显式值为准,否则它们跟随 gfm。因此可以在关闭其余
GFM 语法时单独启用它们,也可以在保留表格、自动链接等 GFM 语法时单独关闭。
生成的脚注锚点会按文档增加命名空间,因此同一页面渲染多篇编译文档时不会产生重复 ID。自动
标题 ID 会把文字转成小写、保留 Unicode 字母与数字、把其余连续字符替换为连字符,并为重复
标题追加 -2、-3。需要手动固定 ID 时,可以直接写
<h2 id="custom-id">标题</h2>。
这里不额外定义高亮、上下标或定义列表的缩写语法;无需配置即可直接使用标准 MDX 元素
<mark>、<sub>、<sup> 与 <dl>。
数学公式
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 树。
语法高亮
highlight: {
provider: 'shiki',
engine: 'oniguruma',
languages: 'auto',
themes: { light: 'vitesse-light', dark: 'vitesse-dark' },
unknownLanguage: 'error',
colorReplacements: {},
}| 键 | 默认值 | 行为 |
|---|---|---|
provider | 'shiki' | 固定字面量;目前不提供其它 provider。 |
engine | 'oniguruma' | Shiki 的 oniguruma 或 javascript 引擎。 |
languages | 'auto' | 按需加载,或预加载列出的内置语言。 |
themes | Vitesse 明暗主题 | Shiki 内置主题名。 |
unknownLanguage | 'error' | 拒绝未知语言,或按 'plain' 渲染。 |
colorReplacements | {} | Shiki 的颜色替换映射。 |
显式语言数组只是预加载这些语法,不会阻止之后按需加载其它语言。Shiki 在内存中复用语言加载; 高亮结果保存在文档缓存记录内,不存在单独的磁盘高亮缓存。
设置 highlight: false 可跳过代码块高亮。
媒体
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 可禁用重写。
派生字段
derived: {
lastModified: false,
readingTime: false,
}readingTime 排除代码,把每个连续 ASCII 单词或每个中日韩字符计为一个单位。分钟数为
ceil(units / 300),并且最少一分钟。
lastModified 优先使用文件最近一次 Git commit 的时间,取不到时回退到文件系统修改时间。启用
的值会出现在 record.derived 中,也会成为编译模块的具名导出。
Manifest
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,或以下保留值:
collection、file、key、locale和slugfrontmatter.<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。
缓存
cache: {
directory: '.amamo-mdx/cache',
}缓存键是 BLAKE3 digest,输入包括缓存格式、原生 crate 版本、OS、架构、目标模式、归一化配置
和运行时包版本、源码路径和字节,以及已启用的 lastModified 值。命中缓存还要求之前记录的
所有媒体依赖仍然存在。
损坏记录会被删除并重新编译,同时产生 AMAMO_CACHE_CORRUPT warning。成功的完整构建会清理
已经不再被发现内容引用的记录。
设置 cache: false 可关闭持久化记录。直接 API 和 Vite 仍可编译,但 Next loader 没有缓存
文件就无法工作。
生成产物
generatedDirectory: '.amamo-mdx'编译器固定在这里写三个共享产物:
collections.mjscollections.d.tsindex.json
缓存和 manifest 位置彼此独立。集合、缓存、生成目录和 manifest 的相对配置路径从 root 解析,
而 Markdown 媒体路径从所在源文档解析。root 默认是 process.cwd();已存在的 root 和集合
目录会在归一化时 canonicalize。只要配置可能从其它工作目录加载,就应显式设置 root。