---
title: "Content Compiler Plugins"
description: "Fumadocs compiler plugins extend the core build pipelinepackages/mdx/src/core.ts to transform documentation source collections, inject metadata, and emit virtual assets for bundlers. By interceptin..."
last_updated: "2026-10-12T07:53:59.700425+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/mdx-pipeline/content-compiler-plugins"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/asyncapi/src/server/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/server/index.tsx)
- [packages/mdx/src/plugins/last-modified.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts)
- [packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts)
- [packages/content-collections/src/configuration.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/content-collections/src/configuration.ts)
- [packages/mdx/src/plugins/json-schema.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts)
- [packages/mdx/src/core.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/core.ts)
- [packages/preview/src/lib/md.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/preview/src/lib/md.ts)
- [packages/mdx/src/vite/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/vite/index.ts)
- [packages/doc-gen/src/remark-docgen.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/doc-gen/src/remark-docgen.ts)
- [packages/mdx/src/runtime/server.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/server.ts)
- [packages/local-md/src/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/local-md/src/index.ts)
- [packages/local-md/src/md/compiler.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/local-md/src/md/compiler.ts)
- [packages/asyncapi/src/generate-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/generate-file.ts)
- [packages/mdx/src/runtime/dynamic.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts)
- [packages/mdx/src/webpack/meta.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/webpack/meta.ts)
- [packages/content/src/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/content/src/index.ts)
- [packages/core/src/source/storage/content.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/source/storage/content.ts)
- [packages/openapi/src/utils/pages/builder.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/utils/pages/builder.ts)
- [packages/python/src/convert.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/python/src/convert.ts)
- [packages/openapi/src/generate-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/generate-file.ts)
- [packages/core/src/source/loader.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/source/loader.ts)
- [packages/obsidian/src/mdx/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/obsidian/src/mdx/index.ts)
- [packages/shadcn/src/manual-installation.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/shadcn/src/manual-installation.ts)
- [packages/core/src/mdx-plugins/remark-structure.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-structure.ts)
- [packages/shared/compile-package-translations.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/shared/compile-package-translations.ts)
- [packages/mdx-remote/src/compile.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx-remote/src/compile.ts)
- [packages/story/src/vite/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/story/src/vite/index.ts)
- [packages/core/src/source/plugins/slugs.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/source/plugins/slugs.ts)
- [packages/mdx/src/loaders/mdx/remark-include.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/loaders/mdx/remark-include.ts)
- [packages/content-collections/src/resolve-plugins.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/content-collections/src/resolve-plugins.ts)
</details>

## Overview

Fumadocs compiler plugins extend the core build pipeline[packages/mdx/src/core.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/core.ts#L176-L185) to transform documentation source collections, inject metadata, and emit virtual assets for bundlers. By intercepting lifecycle hooks across unified processors and bundler configurations, these plugins coordinate everything from static index generation and frontmatter validation to version control timestamp extraction and schema serialization.

Sources: [packages/mdx/src/core.ts:176-185](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/core.ts#L176-L185)

## Compiler Plugin Architecture

### Plugin Interfaces and Execution Lifecycle Hooks

Fumadocs MDX compiler plugins implement structured contracts defined across core plugin modules[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L34-L39), [packages/mdx/src/plugins/last-modified.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L40-L93). The `IndexFilePlugin` interface allows plugins like last-modified to inject custom type configurations and server options directly into the compilation context[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L34-L39), [packages/mdx/src/plugins/last-modified.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L46-L65). Plugins hook into document parsing via `vfile` handlers, intercept collection config initialization, and configure watcher behaviors on development servers.

Sources: [packages/mdx/src/plugins/index-file.ts:34-39](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L34-L39), [packages/mdx/src/plugins/last-modified.ts:40-93](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L40-L93)

| Plugin Hook | Target Context / Argument | Purpose in Compiler Pipeline |
| :--- | :--- | :--- |
| `config` | `PluginContext` | Initializes collection filters, dynamic collection lists, and version control functions. |
| `configureServer` | Vite Dev Server | Sets up file watchers, invalidates filesystem caches, and triggers targeted re-emits on file additions or deletions[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L90-L120), [packages/mdx/src/plugins/json-schema.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L27-L67). |
| `emit` | `PluginContext` | Asynchronously generates virtual file entries, schema files, or index outputs for bundler resolution[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L121-L155), [packages/mdx/src/plugins/json-schema.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L68-L98). |
| `generateTypeConfig` | `PluginContext` | Returns TypeScript type definition strings for internal type config merging. |
| `serverOptions` | `ServerOptions` | Mutates runtime server configurations, such as registering passthrough properties like `lastModified`[packages/mdx/src/plugins/last-modified.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L60-L65). |
| `doc.vfile` | Unified `VFile` | Inspects and transforms document virtual files during compilation, injecting metadata or frontmatter exports[packages/mdx/src/plugins/last-modified.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L78-L91). |

Sources: [packages/mdx/src/plugins/index-file.ts:34-39](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L34-L39), [packages/mdx/src/plugins/json-schema.ts:27-98](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L27-L98), [packages/mdx/src/plugins/last-modified.ts:40-93](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L40-L93)

> [!IMPORTANT]
> During server configuration watchers, plugins inspect dynamic collections via `isDynamic` to decide whether change events trigger full re-emits or skip file generation depending on the active `target` (`default` or `vite`)[[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L55-L120).

Sources: [packages/mdx/src/plugins/index-file.ts:55-120](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L55-L120)

### Emit Entry Contracts and Virtual Output Generation

Plugins coordinate asset generation through the `EmitEntry` contract, returning a target file path and stringified content[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L121-L155), [packages/mdx/src/plugins/json-schema.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L68-L98). The `index-file` plugin builds server, dynamic, and browser entry points by evaluating collection structures and invoking code generators[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L121-L155). Simultaneously, the `jsonSchema` plugin iterates over collections to serialize Zod schemas into JSON schema representations[packages/mdx/src/plugins/json-schema.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L68-L98).

Sources: [packages/mdx/src/plugins/index-file.ts:121-155](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L121-L155), [packages/mdx/src/plugins/json-schema.ts:68-98](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L68-L98)]

## Virtual Index File Code Generation

### Plugin Options and Configuration Generation

The `indexFile` plugin accepts options defined by `IndexFilePluginOptions`, supporting properties such as `target` (`'default'` or `'vite'`), `addJsExtension` (appending `.js` extensions to imports for ESM without bundler resolution), `browser` (generating browser entry points, defaulting to `true`), and `dynamic` (generating dynamic compilation entry points, defaulting to `true`). 

```typescript
export interface IndexFilePluginOptions {
  target?: 'default' | 'vite';
  addJsExtension?: boolean;
  browser?: boolean;
  dynamic?: boolean;
}
```
Sources: [packages/mdx/src/plugins/index-file.ts:13-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L13-L32)

During setup, `generateConfigs` iterates through registered core plugins, invoking optional `serverOptions` hooks and accumulating TypeScript type definitions into an intersection string (`tc`) anchored by `import("fumadocs-mdx/runtime/types").InternalTypeConfig`.

```typescript
function generateConfigs(core: Core): {
  serverOptions: ServerOptions;
  tc: string;
} {
  const serverOptions: ServerOptions = {};
  const typeConfigs: string[] = ['import("fumadocs-mdx/runtime/types").InternalTypeConfig'];
  const ctx = core.getPluginContext();

  for (const plugin of core.getPlugins()) {
    const indexFilePlugin = plugin['index-file'];
    if (!indexFilePlugin) continue;

    indexFilePlugin.serverOptions?.call(ctx, serverOptions);
    const config = indexFilePlugin.generateTypeConfig?.call(ctx);
    if (config) typeConfigs.push(config);
  }

  return {
    serverOptions,
    tc: typeConfigs.join(' & '),
  };
}
```
Sources: [packages/mdx/src/plugins/index-file.ts:62-83](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L62-L83)

### Call-Chain Execution Walkthrough

The virtual index generation pipeline executes sequentially across modular compiler layers to emit output targets.

1. `mdx`: Initializes the Vite plugin wrapper, resolves workspace roots, and provisions the Vite core configuration[packages/mdx/src/vite/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/vite/index.ts#L40-L68).
2. `createViteCore`: Instantiates the core builder environment with `environment: 'vite'` and registers the `indexFile` plugin configured with the `'vite'` target[packages/mdx/src/vite/index.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/vite/index.ts#L144-L159).
3. `indexFile`: Evaluates active documentation and meta collections during its `emit` lifecycle hook, orchestrating file generation tasks[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L121-L155).
4. `toEmitEntry`: Wraps content generators by instantiating code generator instances via `createCodegen` and yielding serialized `EmitEntry` objects containing file paths and stringified outputs[packages/mdx/src/plugins/index-file.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L125-L146).

Sources: [packages/mdx/src/plugins/index-file.ts:121-155](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L121-L155), [packages/mdx/src/vite/index.ts:40-68](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/vite/index.ts#L40-L68), [packages/mdx/src/vite/index.ts:144-159](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/vite/index.ts#L144-L159)

```mermaid
sequenceDiagram
    participant mdx as mdx (vite/index.ts)
    participant createViteCore as createViteCore (vite/index.ts)
    participant indexFile as indexFile (plugins/index-file.ts)
    participant toEmitEntry as toEmitEntry (plugins/index-file.ts)

    mdx->>createViteCore: createViteCore(options)
    createViteCore->>indexFile: indexFile({...})
    indexFile->>toEmitEntry: toEmitEntry(path, content)
    toEmitEntry-->>indexFile: EmitEntry { path, content }
```
Sources: [packages/mdx/src/plugins/index-file.ts:121-155](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L121-L155), [packages/mdx/src/vite/index.ts:40-68](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/vite/index.ts#L40-L68), [packages/mdx/src/vite/index.ts:144-159](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/vite/index.ts#L144-L159)

> [!WARNING]
> Dynamic collections are evaluated at runtime by checking `isDynamic`, which returns true if a collection type equals `'docs'` with `collection.docs.dynamic` set, or if type equals `'doc'` with `collection.dynamic` enabled. Changes to these collections bypass standard Vite glob caching and force full file re-emissions.
Sources: [packages/mdx/src/plugins/index-file.ts:55-120](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L55-L120)

### Dynamic Compilation and Runtime Execution

The runtime dynamic module manages lazy entries through `dynamic`, accepting config exports, core options, and server configurations to provision collection loaders[packages/mdx/src/runtime/dynamic.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L47-L58).

```typescript
export async function dynamic<Config, TC extends InternalTypeConfig>(
  configExports: Config,
  coreOptions: CoreOptions,
  serverOptions?: ServerOptions,
) {
  const core = createCore(coreOptions);
  await core.init({
    config: buildConfig(configExports as Record<string, unknown>, process.cwd()),
  });

  const create = server<Config, TC>(serverOptions);
// ...
```
Sources: [packages/mdx/src/runtime/dynamic.ts:47-58](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L47-L58)

The `convertLazyEntries` helper maps incoming lazy entries by reading file contents from `info.fullPath`, stripping frontmatter via the `frontmatter` utility, building MDX AST outputs through `buildMDX`, and hydrating compiled modules using `executeMdx` with an asynchronous function constructor[packages/mdx/src/runtime/dynamic.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L67-L95).

```typescript
    async function compile({ info, data }: LazyEntry<unknown>) {
      let content = await fs.readFile(info.fullPath, 'utf-8');
      content = frontmatter(content).content;

      const compiled = await buildMDX(core, collection, {
        filePath: info.fullPath,
        source: content,
        frontmatter: data as Record<string, unknown>,
        isDevelopment: false,
        environment: 'runtime',
      });

      return (await executeMdx(String(compiled.value), {
        baseUrl: pathToFileURL(info.fullPath),
      })) as CompiledMDXProperties;
    }
```
Sources: [packages/mdx/src/runtime/dynamic.ts:71-86](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/runtime/dynamic.ts#L71-L86)

## Git Last Modified Timestamps

### Plugin Configuration and Version Control Resolution

The last-modified plugin injects a `lastModified` property into document page exports by resolving timestamps through version control systems or custom handler functions. The `LastModifiedPluginOptions` interface accepts a `versionControl` option, which can be explicitly set to `'git'` or provided as a custom `VersionControlFn` callback accepting a file path and returning a promise resolving to a `Date`, `null`, or `undefined`. An optional `filter` predicate limits timestamp injection to specified collections by name.

```typescript
export interface LastModifiedPluginOptions {
  versionControl?: 'git' | VersionControlFn;
  filter?: (collection: string) => boolean;
}
```
Sources: [packages/mdx/src/plugins/last-modified.ts:9-27](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L9-L27)

During the plugin initialization and configuration lifecycle, the current working directory (`cwd`) is resolved from workspace configuration or the process current directory. When `versionControl` is configured as `'git'`, the internal function `fn` binds to `getGitTimestamp`, passing both the target file path and the resolved working directory.

```typescript
    config() {
      const { workspace } = this.core.getOptions();
      const cwd = workspace ? path.resolve(workspace.dir) : process.cwd();

      switch (versionControl) {
        case 'git':
          fn = (v) => getGitTimestamp(v, cwd);
          break;
        default:
          fn = versionControl;
      }
    },
```
Sources: [packages/mdx/src/plugins/last-modified.ts:66-77](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L66-L77)

> [!NOTE]
> When executing git-based timestamp resolution within containerized CI or deployment environments such as Vercel, shallow clones will omit commit history. Users must configure the `VERCEL_DEEP_CLONE` environment variable to `true` to ensure full commit logs are available for extraction.
Sources: [packages/mdx/src/plugins/last-modified.ts:12-16](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L12-L16)

### Child Process Execution and Caching

To prevent redundant filesystem and process execution overhead across repeated document compilation passes, `getGitTimestamp` maintains a module-scoped cache mapping file paths to promises of `Date | null`. When a file lookup occurs, the cache is checked first before spawning a child process.

```typescript
const cache = new Map<string, Promise<Date | null>>();
```
Sources: [packages/mdx/src/plugins/last-modified.ts:6-6](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L6-L6)

When a cache miss occurs, the function executes the `git` command via `tinyexec` (`x`), requesting the single latest commit (`-1`) with pretty formatting (`--ai`) for the path relative to the working directory. If the process exits with a non-zero exit code, or if the resulting date string parses to an invalid time, `null` is returned and cached accordingly.

```typescript
async function getGitTimestamp(file: string, cwd: string): Promise<Date | null> {
  const cached = cache.get(file);
  if (cached) return cached;

  const timePromise = (async () => {
    const out = await x('git', ['log', '-1', '--pretty="%ai"', path.relative(cwd, file)], {
      nodeOptions: {
        cwd,
      },
    });

    if (out.exitCode !== 0) return null;
    const date = new Date(out.stdout);
    return isNaN(date.getTime()) ? null : date;
  })();

  cache.set(file, timePromise);
  return timePromise;
}
```
Sources: [packages/mdx/src/plugins/last-modified.ts:95-113](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L95-L113)

During virtual file processing (`vfile`), the plugin evaluates the collection filter, retrieves the resolved timestamp for `this.filePath`, and populates `file.data['mdx-export']` with an export object defining `name: 'lastModified'` and the corresponding `value` date. Concurrently, index file generation hooks push `lastModified` into server option passthroughs and emit TypeScript type definitions extending `DocData` with an optional `lastModified?: Date` property for all matching collections.

```typescript
    doc: {
      async vfile(file) {
        if (!filter(this.collection.name)) return;

        const timestamp = await fn(this.filePath);
        if (timestamp) {
          file.data['mdx-export'] ??= [];
          file.data['mdx-export'].push({
            name: 'lastModified',
            value: timestamp,
          });
        }
      },
    },
```
Sources: [packages/mdx/src/plugins/last-modified.ts:78-91](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L78-L91)

## JSON Schema Generation

### Schema Definition and Path Resolution

The `jsonSchema` plugin generates local JSON schema definitions for collection frontmatter and meta structures, relying exclusively on Zod for schema parsing. The plugin accepts a single options object defined by `JSONSchemaOptions`, containing the `insert` boolean flag which controls whether a `$schema` property is injected into newly created JSON files.
Sources: [packages/mdx/src/plugins/json-schema.ts:7-21](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L7-L21)

Schema paths are resolved uniformly using the internal `getSchemaPath` function, which maps a given schema identifier string into a standard relative output path under the `json-schema/` directory.
Sources: [packages/mdx/src/plugins/json-schema.ts:22-24](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L22-L24)

| Option Key | Type | Default | Purpose |
| :--- | :--- | :--- | :--- |
| `insert` | `boolean` | `false` | Inserts the `$schema` field pointing to the generated schema file when new JSON or meta files are created |

Sources: [packages/mdx/src/plugins/json-schema.ts:7-14](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L7-L14)

```typescript
  function getSchemaPath(name: string) {
    return `json-schema/${name}.json`;
  }
```
Sources: [packages/mdx/src/plugins/json-schema.ts:22-24](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L22-L24)

### Server Watcher and File Injection

When the development server is configured and `insert` is enabled, the plugin registers a file watcher listener for the `'add'` event. Upon detecting a newly added file, the watcher iterates through all registered collections to identify whether the file belongs to a standalone `meta` collection or the meta schema of a `docs` collection.
Sources: [packages/mdx/src/plugins/json-schema.ts:27-44](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L27-L44)

> [!WARNING]
> If a newly created JSON file already contains an existing `$schema` property, the watcher aborts execution and skips file modification entirely to prevent overwriting user-defined schema references.
Sources: [packages/mdx/src/plugins/json-schema.ts:55-55](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L55-L55)

If a match is found and parsing succeeds without errors, the watcher computes a relative path from the target file's directory to the absolute JSON schema file location. It then injects the `$schema` property at the root of the parsed JSON object and writes the formatted result back to the filesystem.
Sources: [packages/mdx/src/plugins/json-schema.ts:45-66](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L45-L66)

```typescript
        if ('$schema' in obj) return;
        const schemaPath = path.join(
          outDir,
          getSchemaPath(parent ? `${parent.name}.meta` : match.name),
        );
        const updated = {
          $schema: path.relative(path.dirname(file), schemaPath),
          ...obj,
        };

        await fs.writeFile(file, JSON.stringify(updated, null, 2));
```
Sources: [packages/mdx/src/plugins/json-schema.ts:55-65](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L55-L65)

### Emit Hook and Schema Serialization

The `emit()` lifecycle hook iterates across all core collections to harvest Zod schemas defined on documentation items and metadata files, serializing them into emitter entry objects.
Sources: [packages/mdx/src/plugins/json-schema.ts:68-98](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L68-L98)

```typescript
      for (const collection of this.core.getCollections()) {
        if (collection.type === 'docs') {
          if (collection.meta.schema instanceof z.ZodType) {
            onSchema(`${collection.name}.meta`, collection.meta.schema);
          }

          if (collection.docs.schema instanceof z.ZodType) {
            onSchema(`${collection.name}.docs`, collection.docs.schema);
          }
        } else if (collection.schema instanceof z.ZodType) {
          onSchema(collection.name, collection.schema);
        }
      }
```
Sources: [packages/mdx/src/plugins/json-schema.ts:83-95](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L83-L95)

The internal `onSchema` helper invokes `z.toJSONSchema` with configured options forcing input mode (`io: 'input'`) and setting unrepresentable types to `'any'`, then stores the resulting stringified JSON schemas inside the emitted file collection.
Sources: [packages/mdx/src/plugins/json-schema.ts:71-81](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L71-L81)

```typescript
      function onSchema(name: string, schema: z.ZodSchema) {
        files.push({
          path: getSchemaPath(name),
          content: JSON.stringify(
            z.toJSONSchema(schema, {
              io: 'input',
              unrepresentable: 'any',
            }),
          ),
        });
      }
```
Sources: [packages/mdx/src/plugins/json-schema.ts:71-81](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/json-schema.ts#L71-L81)

## Filesystem Caching and Codegen Utilities

Compiler plugins manage incremental updates and file invalidation using dedicated caching utilities and version control timestamp maps. For example, the `index-file` plugin maintains a filesystem cache instance (`indexFileCache`) to track file modifications and invalidate entries when filesystem watchers trigger change events.
Sources: [packages/mdx/src/plugins/index-file.ts:6-6](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L6-L6), [packages/mdx/src/plugins/index-file.ts:49-49](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L49-L49), [packages/mdx/src/plugins/index-file.ts:93-94](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L93-L94)

Similarly, the git last-modified plugin uses a module-level `Map` to cache asynchronous timestamp promises for each file path, preventing redundant process spawns during document compilation.
Sources: [packages/mdx/src/plugins/last-modified.ts:6-6](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L6-L6), [packages/mdx/src/plugins/last-modified.ts:95-96](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L95-L96), [packages/mdx/src/plugins/last-modified.ts:111-112](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/last-modified.ts#L111-L112)

```typescript
server.watcher.on('all', async (event, file) => {
  indexFileCache.delete(file);
```
Sources: [packages/mdx/src/plugins/index-file.ts:93-94](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L93-L94)

Code generation and path normalization helpers abstract away target-specific output differences and import specifier formatting. The `indexFile` plugin constructs an emitter context using a configurable code generator that accepts target options, output directories, and extension preferences for ESM environments.
Sources: [packages/mdx/src/plugins/index-file.ts:13-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L13-L32), [packages/mdx/src/plugins/index-file.ts:129-134](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L129-L134)

> [!NOTE]
> When generating server index files, the compiler formats configuration import paths using `codegen.formatImportPath` to ensure correct resolution across different bundler targets and workspace layouts.
Sources: [packages/mdx/src/plugins/index-file.ts:162-163](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/mdx/src/plugins/index-file.ts#L162-L163)

## Related

- [MDX Loader Pipeline](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/mdx-pipeline/mdx-loader-pipeline)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/llms.txt) for all pages in this wiki.
