Markdown to Typst converter with multiple parser support
Project description
md2typst
A robust Markdown to Typst converter in Python with support for multiple Markdown parsers.
Features
- Multiple parser backends: Choose from markdown-it-py, mistune, or marko at runtime
- GFM support: Tables, strikethrough, and other GitHub Flavored Markdown extensions
- Math support:
$...$and$$...$$rendered via mitex, enabled by default - Mermaid diagrams:
```mermaidcode blocks rendered via mmdr - Auto-imports: Required Typst packages are automatically imported based on content
- Direct PDF output:
md2pdfcommand converts Markdown to PDF in one step (requirestypstCLI) - Configurable: TOML configuration files, CLI options, and YAML front matter
- Extensible: Plugin support for parser-specific extensions
- Well-tested: Comprehensive test suite with TCK validation against CommonMark
Installation
# Using pip
pip install md2typst
# Using uv
uv add md2typst
Quick Start
Command Line
# Convert a file (writes input.typ by default)
md2typst input.md
# Explicit output path
md2typst input.md -o output.typ
# Output to stdout
md2typst input.md -o -
# Convert from stdin (outputs to stdout)
echo "# Hello **World**" | md2typst
# Use a specific parser
md2typst --parser mistune input.md
# Convert directly to PDF (requires typst CLI)
md2pdf input.md
md2pdf input.md -o custom-output.pdf
# List available parsers
md2typst --list-parsers
Python API
from md2typst import convert
# Simple conversion
typst = convert("# Hello **World**")
print(typst)
# Output: = Hello *World*
# With specific parser
typst = convert("~~deleted~~", parser="mistune")
print(typst)
# Output: #strike[deleted]
# With configuration
from md2typst import convert_with_config
from md2typst.config import Config
config = Config(parser="marko", plugins=["gfm"])
typst = convert_with_config("| A | B |\n|---|---|\n| 1 | 2 |", config)
Supported Parsers
| Parser | CLI Name | Description |
|---|---|---|
| markdown-it-py | markdown-it |
Default. CommonMark compliant, extensible |
| mistune | mistune |
Fast, pure Python |
| marko | marko |
CommonMark compliant, extensible |
All parsers have GFM extensions (tables, strikethrough) enabled by default.
Markdown to Typst Mapping
| Markdown | Typst |
|---|---|
# Heading |
= Heading |
## Heading 2 |
== Heading 2 |
*italic* |
_italic_ |
**bold** |
*bold* |
~~strike~~ |
#strike[strike] |
`code` |
`code` |
[text](url) |
#link("url")[text] |
 |
#image("url", alt: "alt") |
> quote |
#block(...)[quote] |
$E=mc^2$ |
#mi("E=mc^2") |
$$...\int...$$ |
#mitex(\...`)` |
--- |
#line(length: 100%) |
| GFM tables | #table(...) |
```mermaid |
#mermaid("...") |
Configuration
Configuration is loaded from multiple sources (highest priority first):
- CLI arguments (
--parser,--plugin) - Explicit config file (
--config path/to/config.toml) .md2typst.tomlin the current or parent directories[tool.md2typst]section inpyproject.toml
Example Configuration
.md2typst.toml:
parser = "mistune"
plugins = ["strikethrough", "table"]
[parser_options]
html = true
pyproject.toml:
[tool.md2typst]
parser = "markdown-it"
plugins = ["gfm"]
Front Matter
Markdown files can include YAML front matter for metadata, stylesheets, and raw Typst preamble:
---
title: My Document
author: Jane Doe
stylesheet: my-style
preamble: |
#set text(lang: "fr", hyphenate: true)
#set par(justify: true)
#show heading.where(level: 1): it => { it; v(0.5em) }
---
# Hello World
This generates:
#let doc-title = "My Document"
#let doc-author = "Jane Doe"
#import "my-style.typ": *
#set text(lang: "fr", hyphenate: true)
#set par(justify: true)
#show heading.where(level: 1): it => { it; v(0.5em) }
= Hello World
The output ordering is: variables, stylesheet imports, package imports, preamble, then content.
Math
Dollar-sign math syntax is enabled by default (markdown-it and mistune parsers). The mitex package import is added automatically when math is detected.
Inline: $E = mc^2$
Display:
$$
\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
$$
Mermaid Diagrams
Fenced code blocks with language mermaid are converted to native Typst diagrams using the mmdr package. The import is added automatically.
```mermaid
graph LR
A[Start] --> B{Decision}
B -->|Yes| C[OK]
B -->|No| D[End]
```
CLI Options
md2typst --help
Options:
-o, --output FILE Output file (default: <input>.typ, or stdout for stdin)
-p, --parser NAME Parser to use (markdown-it, mistune, marko)
--plugin NAME Load parser plugin (can be repeated)
--stylesheet NAME Import Typst stylesheet (can be repeated)
--config FILE Path to configuration file
--list-parsers List available parsers
--show-config Show effective configuration
Development
Setup
git clone https://github.com/user/md2typst.git
cd md2typst
uv sync
Running Tests
# Run all tests (benchmarks skipped by default)
uv run pytest
# Run by category
uv run pytest -m unit # Unit tests (fast)
uv run pytest -m integration # Integration tests
uv run pytest -m e2e # End-to-end tests
uv run pytest -m benchmark # Benchmark tests
Test Structure
tests/
├── a_unit/ # Unit tests (AST, generator)
├── b_integration/ # Integration tests (parsers, config, TCK)
├── c_e2e/ # End-to-end tests
├── d_benchmark/ # Performance benchmarks
└── fixtures/ # Test fixtures (CommonMark, GFM)
Code Quality
# Type checking
uv run mypy src/
# Linting
uv run ruff check src/
# Formatting
uv run ruff format src/
Architecture
Markdown Input → Parser → AST → Generator → Typst Output
The converter uses a parser-agnostic AST (Abstract Syntax Tree) that decouples parsing from code generation. This allows:
- Swapping parsers without changing the generator
- Consistent output regardless of parser choice
- Easy extension with new parsers
License
MIT
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file md2typst-0.3.1.tar.gz.
File metadata
- Download URL: md2typst-0.3.1.tar.gz
- Upload date:
- Size: 22.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
951cff9db1fb6c8db3fdad88443c8304a3bbbe05a9f1864de79b9b54940fa133
|
|
| MD5 |
b2c536ac258684a9d210381269b4dec5
|
|
| BLAKE2b-256 |
3fdf671486c8b4ce0735c94735176cc659f899b12a0ea1f50b3b677721f9d0e2
|
File details
Details for the file md2typst-0.3.1-py3-none-any.whl.
File metadata
- Download URL: md2typst-0.3.1-py3-none-any.whl
- Upload date:
- Size: 29.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dfa4990e006becc868f0bb83c159b6d608914b823b83ab6182efb81f16d92923
|
|
| MD5 |
94943d653e1aef1e520ef12f20100393
|
|
| BLAKE2b-256 |
e1563923b61df7fc68087f712b29dec2d62bfe619d6993ac3803adb3d9d28093
|