Leadtype
Reference

Convert

The leadtype/convert entry point flattens MDX into clean markdown via the markdown transform stack.

import {
  convertAllMdx,
  convertMdxToMarkdown,
  writeMdxFileAsMarkdown,
} from "leadtype/convert";

The CLI (leadtype generate) calls convertAllMdx internally — use these APIs directly when you need custom transform order, in-memory output, or per-file control.

convertAllMdx

Batch convert a docs tree:

import { convertAllMdx } from "leadtype/convert";
import { defaultMarkdownTransforms, includeMarkdown } from "leadtype/markdown";

await convertAllMdx({
  srcDir: "docs",
  outDir: "public/docs",
  markdownTransforms: [includeMarkdown, ...defaultMarkdownTransforms],
  enrichFrontmatterFromGit: true,
});
OptionDescription
srcDirRoot directory of .mdx files.
outDirDestination for .md output. The directory structure mirrors srcDir.
markdownTransformsTransform array. Usually [includeMarkdown?, ...defaultMarkdownTransforms]. Order matters.
enrichFrontmatterFromGitWhen true, adds lastModified and lastAuthor from git history.
pruneWhen true, deletes .md files under outDir whose source page was deleted or renamed. Default false.
pruneKeepGlob patterns (relative to outDir) for .md files prune must keep.

The function is concurrent — large trees process in parallel.

Pruning orphaned output

Renaming or deleting a source page leaves its old .md behind in outDir — a live URL with stale content that leaks into sitemaps, link checks, and search indexing. prune: true garbage-collects those orphans after each successful batch:

await convertAllMdx({ srcDir: "docs", outDir: "public/docs", prune: true });

Prune semantics, in order of what protects you:

  • Only .md files are candidates. Images, JSON artifacts, and anything else sharing outDir are never touched.
  • Skipped on partial failure. If any page fails to convert, the expected output set is incomplete, so the run warns and deletes nothing.
  • Skipped when srcDir resolves to zero pages. A typoed srcDir must not wipe outDir; the run warns instead.
  • Exempt files. Use pruneKeep globs for .md files other tools write into the same outDir (generated sitemaps, agent page mirrors, locale aliases): pruneKeep: ["sitemap.md", "mirrors/**"]. Nothing is exempted implicitly — a deleted source page prunes regardless of its filename.
  • Symlinks are never followed, so a link inside outDir can't cause deletions outside it.
  • Locked against concurrent runs. While pruning, the run holds the same per-outDir lock as leadtype generate, so a prune can't delete output a concurrent run just wrote. Set LEADTYPE_NO_LOCK=1 to opt out.

Directories emptied by pruning are removed as well.

convertMdxToMarkdown

Convert one file in memory and get back the markdown plus parsed frontmatter:

const result = await convertMdxToMarkdown(
  "docs/quickstart.mdx",
  defaultMarkdownTransforms,
  /* enrichFrontmatterFromGit */ false,
);

console.log(result.markdown);
console.log(result.frontmatter);

Use this when you want to render the output yourself, validate it before writing, or feed it to another pipeline stage without touching disk.

writeMdxFileAsMarkdown

Convert one file and write the output:

await writeMdxFileAsMarkdown("docs/quickstart.mdx", {
  outDir: "public/docs",
  markdownTransforms: defaultMarkdownTransforms,
});

Behavior notes

  • Frontmatter is preserved. YAML blocks at the top of the source .mdx survive into the output .md.
  • Synthesized frontmatter. A page without frontmatter gets title (and sometimes description) inferred from the rendered markdown.
  • Tables and Mermaid blocks are compacted after rendering so agent output stays clean.
  • Conversion is concurrent — there is no global state.

Pairing with markdown transforms

In most setups, pair convertAllMdx with the default transform stack and includeMarkdown if your docs use partials:

import { defaultMarkdownTransforms, includeMarkdown } from "leadtype/markdown";

markdownTransforms: [includeMarkdown, ...defaultMarkdownTransforms];

Place includeMarkdown before the defaults so includes expand into the source AST before component flattening sees them. See Markdown transforms for the full stack and order rationale.

The include plugin supports tags such as <include src="./shared/auth.mdx" /> and section reuse with <include src="./shared/auth.mdx#session-flow" />. Section reuse expects a lowercase <section id="session-flow"> block in the included file.