mdwright
mdwright is a Markdown linter and round-trip formatter for any Markdown project.
Four commitments shape the tool.
Fast. On a 79-file corpus of math-heavy technical prose, mdwright fmt-check runs ≥
50× faster than mdformat --check. The multiplier scales with file count and core count;
see Performance for the measurement, host, and reproducer.
Design choices that buy this are in Architecture.
Round-trip safe. mdwright fmt renders to the same HTML before and after; every
change in the rendered DOM is treated as a bug. Whitespace inside a paragraph may shift
(a b becomes a b), but word boundaries and the rendered tree do not. Where the
formatter cannot prove equivalence it refuses to rewrite; the
deviation table lists every exception with a reproducer.
Configurable, preserve by default. Source style choices — emphasis delimiters, list
markers, thematic breaks, link-destination angle brackets — pass through untouched.
Canonicalisation is opt-in one knob at a time in .mdwright.toml, or via
fmt.profile = "mdformat" for mdformat-compatible spelling where verified rewrites
preserve the parsed document.
Math-resilient. \( … \), \[ … \], and \begin{NAME} … \end{NAME} pass through
verbatim. The scanner identifies math regions before any other pass touches the document,
so the formatter never reflows \frac{a}{b} into \\frac{a}{b} and the linter never
flags a backslash inside \begin{align*}. See Math regions
for the design.
Who this site is for
- Users writing Markdown with math, code, or strict formatting requirements: start with Getting started.
- CI operators wiring mdwright into pre-commit, GitHub Actions, or other automation: Integration.
- Rule authors extending mdwright with project-specific lints: Extending → Lint rules.
The narrative pages (concepts, extending) explain the why; the reference pages (rules, CLI, public API, diagnostic schema) are the source-of-truth what.
Stability
mdwright is pre-1.0. The release surface, including public Rust API, CLI, configuration schema, diagnostic JSON, and lint-rule trait, is documented descriptively at Public API; minor versions may include breaking changes until 1.0, patch releases never do. See Semver policy.