Overview
The MDX compiler module provides a reusable compiler for processing MDX documents with built-in support for frontmatter, GFM (GitHub Flavored Markdown), and custom remark/rehype plugins.
Types
MdxSource
Represents the source content for an MDX document.
type MdxSource = string | Uint8Array;
MdxArtifact
The result of compiling an MDX document.
interface MdxArtifact<TFrontmatter = Record<string, unknown>> {
compiled?: string | undefined;
frontmatter: TFrontmatter;
}
The compiled JavaScript code (function body). Undefined when compiling with frontmatterOnly: true.
The parsed and validated frontmatter data
MdxCompileOptions
Options for compiling MDX documents.
interface MdxCompileOptions {
frontmatterOnly?: boolean | undefined;
mdxOptions?: MdxJsCompileOptions;
}
If true, only parse frontmatter without compiling the MDX content. Defaults to false.
Additional options passed to @mdx-js/mdx compiler
HastNode
Represents a node in the HTML Abstract Syntax Tree.
import type { Node } from "hast";
type HastNode = Node;
HastElement
Represents an element node in the HTML Abstract Syntax Tree.
import type { Element } from "hast";
type HastElement = Element;
MdxCompiler class
Constructor
Creates a new MDX compiler instance.
new MdxCompiler(mdxOptions?: MdxJsCompileOptions)
Default options for all compilations with this compiler instance
Example:
import { MdxCompiler } from "@temelj/mdx";
const compiler = new MdxCompiler({
development: process.env.NODE_ENV === "development"
});
Adds a remark plugin to the compiler pipeline.
withRemarkPlugin<TOptions>(
plugin: Plugin<[TOptions?], HastNode, HastNode>,
options?: TOptions
): MdxCompiler
Optional configuration for the plugin
The compiler instance for chaining
Example:
import { MdxCompiler } from "@temelj/mdx";
import remarkMath from "remark-math";
const compiler = new MdxCompiler()
.withRemarkPlugin(remarkMath);
withRehypePlugin
Adds a rehype plugin to the compiler pipeline.
withRehypePlugin<TOptions>(
plugin: Plugin<[TOptions?], HastNode, HastNode>,
options?: TOptions
): MdxCompiler
Optional configuration for the plugin
The compiler instance for chaining
Example:
import { MdxCompiler } from "@temelj/mdx";
import rehypeSlug from "rehype-slug";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
const compiler = new MdxCompiler()
.withRehypePlugin(rehypeSlug)
.withRehypePlugin(rehypeAutolinkHeadings, {
behavior: "wrap"
});
compile
Compiles an MDX document with optional frontmatter schema validation.
compile<TFrontmatterSchema extends z.ZodSchema>(
source: MdxSource,
options?: MdxCompileOptions,
frontmatterSchema?: TFrontmatterSchema
): Promise<MdxArtifact<z.output<TFrontmatterSchema>>>
The MDX source code as a string or Uint8Array
Optional Zod schema for validating and typing frontmatter
The compiled artifact with frontmatter and optional compiled code
Example:
import { MdxCompiler } from "@temelj/mdx";
import { z } from "zod";
const compiler = new MdxCompiler();
const source = `---
title: Hello World
author: John Doe
---
# {frontmatter.title}
Written by {frontmatter.author}
`;
const frontmatterSchema = z.object({
title: z.string(),
author: z.string()
});
const artifact = await compiler.compile(source, {}, frontmatterSchema);
console.log(artifact.frontmatter.title); // "Hello World"
console.log(artifact.compiled); // Compiled JavaScript function body
Built-in plugins
The compiler includes these plugins by default:
- remarkFrontmatterPlugin: Parses YAML frontmatter
- remarkGfmPlugin: GitHub Flavored Markdown support (tables, task lists, strikethrough, etc.)
Common use cases
Basic MDX compilation
import { MdxCompiler } from "@temelj/mdx";
const compiler = new MdxCompiler();
const source = `
# Hello World
This is **MDX** content.
`;
const { compiled } = await compiler.compile(source);
// Use compiled code with MDX runtime
Frontmatter-only parsing
import { MdxCompiler } from "@temelj/mdx";
import { z } from "zod";
const compiler = new MdxCompiler();
const source = `---
title: My Post
date: 2024-01-01
tags: ["typescript", "mdx"]
---
# Content here...
`;
const schema = z.object({
title: z.string(),
date: z.string(),
tags: z.array(z.string())
});
const { frontmatter } = await compiler.compile(
source,
{ frontmatterOnly: true },
schema
);
console.log(frontmatter.title); // "My Post"
console.log(frontmatter.tags); // ["typescript", "mdx"]
With custom plugins
import { MdxCompiler } from "@temelj/mdx";
import remarkMath from "remark-math";
import rehypeKatex from "rehype-katex";
import rehypePrism from "rehype-prism-plus";
const compiler = new MdxCompiler()
.withRemarkPlugin(remarkMath)
.withRehypePlugin(rehypeKatex)
.withRehypePlugin(rehypePrism, {
ignoreMissing: true
});
const source = `
# Math Example
Inline math: $E = mc^2$
Block math:
$$
\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}
$$
\`\`\`typescript
const hello = "world";
\`\`\`
`;
const { compiled } = await compiler.compile(source);
Content management system
import { MdxCompiler } from "@temelj/mdx";
import { z } from "zod";
import fs from "fs/promises";
const postSchema = z.object({
title: z.string(),
description: z.string(),
publishedAt: z.string().datetime(),
author: z.string(),
tags: z.array(z.string())
});
class ContentManager {
private compiler = new MdxCompiler();
async loadPost(path: string) {
const source = await fs.readFile(path, "utf-8");
return await this.compiler.compile(source, {}, postSchema);
}
async listPosts(directory: string) {
const files = await fs.readdir(directory);
const posts = [];
for (const file of files) {
if (file.endsWith(".mdx")) {
const source = await fs.readFile(`${directory}/${file}`, "utf-8");
const { frontmatter } = await this.compiler.compile(
source,
{ frontmatterOnly: true },
postSchema
);
posts.push({ file, ...frontmatter });
}
}
return posts.sort((a, b) =>
new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime()
);
}
}
Static site generator
import { MdxCompiler } from "@temelj/mdx";
import { z } from "zod";
const pageSchema = z.object({
title: z.string(),
layout: z.enum(["default", "blog", "docs"]).default("default")
});
class SiteGenerator {
private compiler = new MdxCompiler()
.withRehypePlugin(rehypeSlug)
.withRehypePlugin(rehypeAutolinkHeadings);
async compilePage(source: string) {
const { compiled, frontmatter } = await this.compiler.compile(
source,
{},
pageSchema
);
return {
code: compiled!,
meta: frontmatter
};
}
async buildSite(pages: Record<string, string>) {
const compiled = new Map();
for (const [path, source] of Object.entries(pages)) {
compiled.set(path, await this.compilePage(source));
}
return compiled;
}
}
Exported plugins
import {
remarkFrontmatterPlugin,
remarkGfmPlugin
} from "@temelj/mdx";
// Use with other compilers