Skip to main content

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 compile
  • development (bool, optional): Enable development mode
  • jsx (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 runtime
  • pragma (str, optional): JSX pragma for classic runtime
  • pragma_frag (str, optional): JSX pragma fragment for classic runtime
  • pragma_import_source (str, optional): Pragma import source
  • provider_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mdxjs_py-0.1.0a1-cp313-cp313-manylinux_2_34_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.34+ x86-64

File details

Details for the file mdxjs_py-0.1.0a1-cp313-cp313-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for mdxjs_py-0.1.0a1-cp313-cp313-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 c176520d9cbdebb86f0ed094702846c40aff515f5c9a3e7d146e52884fe5b2fa
MD5 27752ea3a274706565f4fd327df77bbc
BLAKE2b-256 5283b811e0f3a5371f132bd53a2fbecc5eedc92fd5c86f0b9ed810d3b17d47ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for mdxjs_py-0.1.0a1-cp313-cp313-manylinux_2_34_x86_64.whl:

Publisher: python-publish.yml on SamDc73/mdxjs-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page