Python bindings for mdxjs-rs - Fast MDX to JavaScript compilation via Rust
Project description
mdxjs-py
Python bindings for mdxjs-rs, a Rust implementation of MDX.
⚠️ Alpha Software: This package is in early development. The API may change between versions.
Features
- Fast MDX compilation via Rust (10-100x faster than Node.js subprocess)
- Simple Python API mirroring @mdx-js/mdx
- Zero dependencies (beyond the compiled extension)
- Helpful error messages with line/column information
- Full MDX 2 support via mdxjs-rs
Installation
pip install mdxjs-py
Note: Currently only Linux x86_64 wheels are provided. For other platforms, you'll need Rust installed to build from source:
# Install Rust first
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Then install from PyPI (will compile automatically)
pip install mdxjs-py
Usage
Basic Compilation
from mdxjs_py import compile
# Compile MDX to JavaScript
mdx_content = """
# Hello World
This is **MDX** content with <Button onClick={() => alert('hi!')}>JSX</Button>!
"""
try:
js_output = compile(mdx_content)
print(js_output)
except ValueError as e:
print(f"MDX compilation failed: {e}")
Validation
Use compile() to validate MDX syntax:
from mdxjs_py import compile
def validate_mdx(content: str) -> tuple[bool, str | None]:
"""Validate MDX content.
Returns:
(is_valid, error_message)
"""
try:
compile(content)
return True, None
except ValueError as e:
return False, str(e)
# Example usage
content = "<Button>Unclosed tag"
is_valid, error = validate_mdx(content)
if not is_valid:
print(f"Invalid MDX: {error}")
# Output: Invalid MDX: Expected a closing tag for `<Button>`
Configuration Options
from mdxjs_py import compile
# With options (matches @mdx-js/mdx API)
js_output = compile(
mdx_content,
development=True, # Enable development mode
jsx_runtime="automatic", # or "classic"
jsx_import_source="react", # for automatic runtime
)
API Reference
compile(source: str, **options) -> str
Compile MDX source to JavaScript. API-compatible with @mdx-js/mdx.
Parameters:
source(str): The MDX source code to compiledevelopment(bool, optional): Enable development modejsx(bool, optional): Keep JSX (default: False, compiles to JS)jsx_runtime(str, optional): "automatic" or "classic"jsx_import_source(str, optional): Package for automatic JSX runtimepragma(str, optional): JSX pragma for classic runtimepragma_frag(str, optional): JSX pragma fragment for classic runtimepragma_import_source(str, optional): Pragma import sourceprovider_import_source(str, optional): Provider import source
Returns:
str: Compiled JavaScript code
Raises:
ValueError: If MDX compilation fails with detailed error message
compile_sync(source: str, **options) -> str
Synchronous version of compile() (alias for compatibility).
is_available() -> bool
Check if the Rust module is built and available.
Common Use Cases
1. Validate User-Generated MDX
def process_user_mdx(content: str) -> dict:
"""Process and validate user MDX content."""
try:
js_output = compile(content)
return {
"success": True,
"output": js_output
}
except ValueError as e:
# Extract line/column from error
error_str = str(e)
return {
"success": False,
"error": error_str,
"hint": "Check for unclosed tags or invalid JSX"
}
2. MDX Syntax Checking in CI/CD
import glob
from pathlib import Path
from mdxjs_py import compile
def check_all_mdx_files(directory: str) -> bool:
"""Validate all MDX files in a directory."""
all_valid = True
for mdx_path in Path(directory).glob("**/*.mdx"):
try:
with open(mdx_path, 'r') as f:
compile(f.read())
print(f"✅ {mdx_path}")
except ValueError as e:
print(f"❌ {mdx_path}: {e}")
all_valid = False
return all_valid
3. Pre-process MDX for Frontend
def prepare_mdx_for_frontend(content: str, max_retries: int = 3) -> str:
"""Validate and prepare MDX for frontend rendering."""
for attempt in range(max_retries):
try:
# Validate compilation
compile(content)
return content
except ValueError as e:
if attempt == max_retries - 1:
# Return with warning comment
return f"<!-- MDX validation warning: {e} -->\n{content}"
# Could attempt to fix common issues here
content = fix_common_mdx_issues(content)
return content
Performance
Benchmarked on an Intel i7-9750H:
import time
from mdxjs_py import compile
content = "# Heading\n\nParagraph with **bold** text.\n\n" * 100
# Single compilation
start = time.time()
compile(content)
print(f"Single: {(time.time() - start) * 1000:.2f}ms")
# Result: ~0.5ms
# Bulk compilation
start = time.time()
for _ in range(1000):
compile(content)
print(f"1000x: {time.time() - start:.2f}s")
# Result: ~0.5s (vs ~50s with Node.js subprocess)
Development
Building from Source
# Clone the repository
git clone https://github.com/SamDc73/mdxjs-py
cd mdxjs-py
# Create virtual environment
python -m venv venv
source venv/bin/activate
# Install build dependencies
pip install maturin pytest
# Build and install locally
maturin develop --release
# Run tests
pytest tests/
Architecture
This is a minimal Python binding to mdxjs-rs, designed for:
- Zero overhead - Direct 1:1 binding to mdxjs-rs functions
- API compatibility - Matches @mdx-js/mdx compile() API
- Simplicity - No abstraction layers, just the binding
Credits
This package is a thin Python wrapper around:
- mdxjs-rs by Titus Wormer - The Rust MDX implementation that does all the heavy lifting
- Built with PyO3 - Rust bindings for Python
- Packaged with maturin - Build and publish Rust Python extensions
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License - see LICENSE file for details.
This package is MIT licensed, same as mdxjs-rs.
Changelog
See CHANGELOG.md for release history.
Support
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 Distributions
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 mdxjs_py-0.1.0a2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: mdxjs_py-0.1.0a2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 2.1 MB
- Tags: CPython 3.8+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ef2b2c99ae57707a565a4a967a06157a5f3e12cb1f0dc992320af49e434b63d
|
|
| MD5 |
7d8d24667c39fe1ad25685d3e2bf25ac
|
|
| BLAKE2b-256 |
76c57866d41c1c4ed980fff211f5bfb8b6713e00f17d94e510a80afd6b873212
|
Provenance
The following attestation bundles were made for mdxjs_py-0.1.0a2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
python-publish.yml on SamDc73/mdxjs-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mdxjs_py-0.1.0a2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl -
Subject digest:
5ef2b2c99ae57707a565a4a967a06157a5f3e12cb1f0dc992320af49e434b63d - Sigstore transparency entry: 455502108
- Sigstore integration time:
-
Permalink:
SamDc73/mdxjs-py@42a2ecbd5d31c402a8cfbcd545178c57fcf18740 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/SamDc73
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@42a2ecbd5d31c402a8cfbcd545178c57fcf18740 -
Trigger Event:
workflow_dispatch
-
Statement type: