@amamo/mdx
本页目录

编译器 API

当你自己的脚本或构建系统需要管理生命周期时,使用 createCompiler。Vite 和 Next 共用相同 的编译器行为,但每个创建出的实例都有自己的记录、队列和高亮器。

创建并关闭编译器

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

import amamo from './amamo.config.mjs'

const compiler = await createCompiler(amamo)
try {
  const result = await compiler.build()
  console.log(result)
} finally {
  await compiler.dispose()
}

创建阶段会归一化配置,并在启用高亮时初始化 Shiki。直到第一个需要原生 batch 的 build()transform(),原生绑定才会被加载。

TypeScript
interface ICompiler {
  build(): Promise<IBuildResult>
  dispose(): Promise<void>
  remove(file: string): Promise<number>
  transform(file: string): Promise<ITransformResult>
}

同一编译器上的操作会串行执行。并发的 build() 共用一次进行中的构建,增量操作在队列中等待。

build()

TypeScript
const result = await compiler.build()

一次完整构建会:

  1. 按确定性顺序遍历所有集合。
  2. 推导 locale、slug 和 key,并拒绝重复 key。
  3. 读取全部源码和可选的最后修改时间。
  4. 复用兼容的缓存记录,或执行 Rust/Shiki 编译流水线。
  5. 用完整结果替换编译器的内存记录集。
  6. 写入变化的 manifest 和共享生成文件,再清理过期缓存。
TypeScript
interface IBuildResult {
  cached: number
  compiled: number
  discovered: number
  outputsWritten: number
}

outputsWritten 统计字节发生变化的输出文件。没有变化的 warm build 可以返回 0,并保持所有 输出文件的修改时间不变;缓存记录写入不计入这个数字。

transform(file)

TypeScript
const result = await compiler.transform('/absolute/path/content/posts/hello.mdx')

transform 编译一个属于集合的文件,替换它的记录,重写受影响的共享产物,并清理不在当前 编译器状态中的缓存记录。

TypeScript
interface ITransformResult {
  cached: boolean
  code: string
  map: null
  outputsWritten: number
  record: IDocumentRecord
}
  • code 是编译后的 JavaScript 模块体。
  • map 目前始终为 null
  • record.frontmatter 包含完整的、通过校验的 frontmatter 对象。
  • record.derived 包含启用的阅读时间和最后修改时间。
  • record.hash 是源字节的 BLAKE3 digest。
  • record.cacheKey 还包含配置、平台、路径和可选修改时间元数据。
  • record.diagnostics 包含成功结果的 warning,例如 warn 策略下的媒体缺失。

请使用绝对路径。相对路径从进程工作目录解析,不会自动从 config.root 解析。

当共享产物必须保留所有文档时,请先运行 build()。在全新编译器上直接调用 transform(), 会创建只包含该文件的状态。

remove(file)

TypeScript
const outputsWritten = await compiler.remove('/absolute/path/content/posts/deleted.mdx')

remove 删除这个编译器已经知道的记录,更新共享产物,清理缓存,并返回变化的输出文件数量。 未知路径是 no-op,返回 0

dispose()

TypeScript
await compiler.dispose()

释放操作会清理编译器资源并销毁 Shiki。它是幂等的;之后再调用 build、transform 或 remove 会以 AMAMO_COMPILER_DISPOSED 拒绝。

错误与诊断

不同边界的失败形式不同:

  • 非法 object schema 或其它配置数据会抛出 TypeError,消息中带 AMAMO_CONFIG_* code。
  • 原生解析、schema、媒体、缓存和 manifest 失败会变成名为 AmamoMdxError 的错误,并带 diagnostics 数组。
  • Shiki 初始化和未知语言失败是普通 Error
  • 文件系统、watcher 和 Next loader 失败也是普通 Error

IDiagnostic@amamo/mdx 导出:

TypeScript
interface IDiagnostic {
  code: string
  file?: string
  hint?: string
  message: string
  range?: {
    start: { line: number; column: number; offset: number }
    end: { line: number; column: number; offset: number }
  }
  severity: 'error' | 'warning'
}

这个类型为 rangehint 预留了位置,但目前不会填充它们。batch 会在第一篇失败文档或 第一个失败阶段停止,而不会聚合全部失败。Schema 消息可能引用提交的 frontmatter 值。

共享生成文件

构建完成后,generatedDirectory 包含:

  • collections.mjs:按顺序排列的 { derived, frontmatter, key, locale, slug, load } 数组。
  • collections.d.ts:集合注册表的配套声明产物。
  • index.json:Next loader 使用的源码路径、缓存键、缓存目录和配置指纹。

注册表的 load() 会 import 原始 MDX 源码。宿主 bundler 仍需配置 Vite 插件、Next loader 或 其它兼容的 MDX transform。

Copyright © 2026 白熱.