Relevant source files
The following files were used as context for generating this wiki page:
- packages/asyncapi/src/server/index.tsx
- packages/mdx/src/plugins/last-modified.ts
- packages/mdx/src/plugins/index-file.ts
- packages/content-collections/src/configuration.ts
- packages/mdx/src/plugins/json-schema.ts
- packages/mdx/src/core.ts
- packages/preview/src/lib/md.ts
- packages/mdx/src/vite/index.ts
- packages/doc-gen/src/remark-docgen.ts
- packages/mdx/src/runtime/server.ts
- packages/local-md/src/index.ts
- packages/local-md/src/md/compiler.ts
- packages/asyncapi/src/generate-file.ts
- packages/mdx/src/runtime/dynamic.ts
- packages/mdx/src/webpack/meta.ts
- packages/content/src/index.ts
- packages/core/src/source/storage/content.ts
- packages/openapi/src/utils/pages/builder.ts
- packages/python/src/convert.ts
- packages/openapi/src/generate-file.ts
- packages/core/src/source/loader.ts
- packages/obsidian/src/mdx/index.ts
- packages/shadcn/src/manual-installation.ts
- packages/core/src/mdx-plugins/remark-structure.ts
- packages/shared/compile-package-translations.ts
- packages/mdx-remote/src/compile.ts
- packages/story/src/vite/index.ts
- packages/core/src/source/plugins/slugs.ts
- packages/mdx/src/loaders/mdx/remark-include.ts
- packages/content-collections/src/resolve-plugins.ts
Overview
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 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
Compiler Plugin Architecture
Plugin Interfaces and Execution Lifecycle Hooks
Fumadocs MDX compiler plugins implement structured contracts defined across core plugin modulespackages/mdx/src/plugins/index-file.ts, packages/mdx/src/plugins/last-modified.ts. The IndexFilePlugin interface allows plugins like last-modified to inject custom type configurations and server options directly into the compilation contextpackages/mdx/src/plugins/index-file.ts, packages/mdx/src/plugins/last-modified.ts. 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, packages/mdx/src/plugins/last-modified.ts:40-93
| 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 deletionspackages/mdx/src/plugins/index-file.ts, packages/mdx/src/plugins/json-schema.ts. |
emit | PluginContext | Asynchronously generates virtual file entries, schema files, or index outputs for bundler resolutionpackages/mdx/src/plugins/index-file.ts, packages/mdx/src/plugins/json-schema.ts. |
generateTypeConfig | PluginContext | Returns TypeScript type definition strings for internal type config merging. |
serverOptions | ServerOptions | Mutates runtime server configurations, such as registering passthrough properties like lastModifiedpackages/mdx/src/plugins/last-modified.ts. |
doc.vfile | Unified VFile | Inspects and transforms document virtual files during compilation, injecting metadata or frontmatter exportspackages/mdx/src/plugins/last-modified.ts. |
Sources: packages/mdx/src/plugins/index-file.ts:34-39, packages/mdx/src/plugins/json-schema.ts:27-98, packages/mdx/src/plugins/last-modified.ts:40-93
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.
Sources: packages/mdx/src/plugins/index-file.ts:55-120
Emit Entry Contracts and Virtual Output Generation
Plugins coordinate asset generation through the EmitEntry contract, returning a target file path and stringified contentpackages/mdx/src/plugins/index-file.ts, packages/mdx/src/plugins/json-schema.ts. The index-file plugin builds server, dynamic, and browser entry points by evaluating collection structures and invoking code generatorspackages/mdx/src/plugins/index-file.ts. Simultaneously, the jsonSchema plugin iterates over collections to serialize Zod schemas into JSON schema representationspackages/mdx/src/plugins/json-schema.ts.
Sources: packages/mdx/src/plugins/index-file.ts:121-155, packages/mdx/src/plugins/json-schema.ts:68-98]
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).
export interface IndexFilePluginOptions {
target?: 'default' | 'vite';
addJsExtension?: boolean;
browser?: boolean;
dynamic?: boolean;
}Sources: packages/mdx/src/plugins/index-file.ts:13-32
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.
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
Call-Chain Execution Walkthrough
The virtual index generation pipeline executes sequentially across modular compiler layers to emit output targets.
mdx: Initializes the Vite plugin wrapper, resolves workspace roots, and provisions the Vite core configurationpackages/mdx/src/vite/index.ts.createViteCore: Instantiates the core builder environment withenvironment: 'vite'and registers theindexFileplugin configured with the'vite'targetpackages/mdx/src/vite/index.ts.indexFile: Evaluates active documentation and meta collections during itsemitlifecycle hook, orchestrating file generation taskspackages/mdx/src/plugins/index-file.ts.toEmitEntry: Wraps content generators by instantiating code generator instances viacreateCodegenand yielding serializedEmitEntryobjects containing file paths and stringified outputspackages/mdx/src/plugins/index-file.ts.
Sources: packages/mdx/src/plugins/index-file.ts:121-155, packages/mdx/src/vite/index.ts:40-68, packages/mdx/src/vite/index.ts:144-159
Sources: packages/mdx/src/plugins/index-file.ts:121-155, packages/mdx/src/vite/index.ts:40-68, packages/mdx/src/vite/index.ts:144-159
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
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 loaderspackages/mdx/src/runtime/dynamic.ts.
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
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 constructorpackages/mdx/src/runtime/dynamic.ts.
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
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.
export interface LastModifiedPluginOptions {
versionControl?: 'git' | VersionControlFn;
filter?: (collection: string) => boolean;
}Sources: packages/mdx/src/plugins/last-modified.ts:9-27
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.
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
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
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.
const cache = new Map<string, Promise<Date | null>>();Sources: packages/mdx/src/plugins/last-modified.ts:6-6
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.
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
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.
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
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
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
| 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
function getSchemaPath(name: string) {
return `json-schema/${name}.json`;
}Sources: packages/mdx/src/plugins/json-schema.ts:22-24
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
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
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
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
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
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
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
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
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, packages/mdx/src/plugins/index-file.ts:49-49, packages/mdx/src/plugins/index-file.ts:93-94
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, packages/mdx/src/plugins/last-modified.ts:95-96, packages/mdx/src/plugins/last-modified.ts:111-112
server.watcher.on('all', async (event, file) => {
indexFileCache.delete(file);Sources: packages/mdx/src/plugins/index-file.ts:93-94
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, packages/mdx/src/plugins/index-file.ts:129-134
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
Related
On This Page
OverviewCompiler Plugin ArchitecturePlugin Interfaces and Execution Lifecycle HooksEmit Entry Contracts and Virtual Output GenerationVirtual Index File Code GenerationPlugin Options and Configuration GenerationCall-Chain Execution WalkthroughDynamic Compilation and Runtime ExecutionGit Last Modified TimestampsPlugin Configuration and Version Control ResolutionChild Process Execution and CachingJSON Schema GenerationSchema Definition and Path ResolutionServer Watcher and File InjectionEmit Hook and Schema SerializationFilesystem Caching and Codegen UtilitiesRelated