@amamo/mdx
On This Page

Vite 8

amamoMdx gives one Vite plugin instance one lazily created compiler. Vite owns module transforms and filesystem events; the compiler owns validated records, manifests, and generated collection files.

Configure the plugin

TypeScript
// vite.config.ts
import { amamoMdx } from '@amamo/mdx/vite'
import { defineConfig } from 'vite'

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

export default defineConfig({
  plugins: [amamoMdx(amamo)],
})

The plugin is enforce: 'pre' and sharedDuringBuild: true. When a Vite multi-environment build is configured to reuse shared plugins, client and SSR environments therefore use the same plugin compiler instead of compiling the content set independently.

Import a document

TSX
import Post, { frontmatter } from './content/posts/hello.mdx'

export function Page() {
  return (
    <article>
      <h1>{frontmatter.title}</h1>
      <Post />
    </article>
  )
}

For a recognized collection file, the transform hook ignores the incoming source string and asks the compiler for the current JavaScript module. Frontmatter has already been validated, Markdown media has been rewritten, and enabled Shiki output has been injected before Vite receives the code.

Lifecycle

Vite hook or eventCompiler action
buildStartRun one complete build().
transformRun transform(id) for recognized collection files.
dev server add / changeTransform the path and refresh generated outputs.
dev server unlinkRemove the known record and refresh generated outputs.
dev HTTP server closeDetach watcher handlers and dispose the compiler.

The first startup call is shared, so repeated build hooks do not start duplicate full builds. Compiler operations are serialized after that.

Development updates

When add, change, or unlink changes at least one generated output, the plugin invalidates collections.mjs if it is in Vite's module graph and sends a full reload. There is no document-level HMR boundary yet.

A watcher compilation failure is sent to Vite's logger and error overlay. Successful record warnings such as AMAMO_MEDIA_MISSING remain in record.diagnostics; the plugin does not print them.

Generated collections

Application code may import the generated registry:

TypeScript
import { collections } from './.amamo-mdx/collections.mjs'

const published = collections.posts.filter((document) => !document.frontmatter.draft)

Each registry item has metadata plus load: () => import(originalMdxPath). Vite processes that lazy import through the same plugin. The plugin does not read index.json; that private index exists for the Next loader.

Production builds

vite build runs the same full startup build and per-import transforms. Outputs whose bytes are unchanged keep their modification times, which lets downstream tools avoid work.

Set cache: false only when repeated native/Shiki work is acceptable. Vite can transform without persistent records, unlike the Next adapter.

Current limits

  • Generated-output changes trigger a full reload.
  • The plugin does not print IBuildResult; call the direct Compiler API when a report is required.
  • Every plugin invocation owns separate compiler state. Register the plugin once for one content config.
Copyright © 2026 白熱.