# mdxjs-py
Python bindings for [mdxjs-rs](https://github.com/wooorm/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
```bash
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:
```bash
# 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
```python
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:
```python
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
```python
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
```python
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
```python
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
```python
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:
```python
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
```bash
# 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](https://github.com/wooorm/mdxjs-rs)** by [Titus Wormer](https://github.com/wooorm) - The Rust MDX implementation that does all the heavy lifting
- Built with **[PyO3](https://pyo3.rs)** - Rust bindings for Python
- Packaged with **[maturin](https://maturin.rs)** - Build and publish Rust Python extensions
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
MIT License - see [LICENSE](LICENSE) file for details.
This package is MIT licensed, same as mdxjs-rs.
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for release history.
## Support
- 🐛 [Report bugs](https://github.com/SamDc73/mdxjs-py/issues)
- 💡 [Request features](https://github.com/SamDc73/mdxjs-py/issues)
- 📖 [Documentation](https://github.com/SamDc73/mdxjs-py#readme)
Raw data
{
"_id": null,
"home_page": "https://github.com/SamDc73/mdxjs-py",
"name": "mdxjs-py",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "mdx, markdown, jsx, compiler, rust, mdxjs",
"author": null,
"author_email": null,
"download_url": null,
"platform": null,
"description": "# mdxjs-py\n\nPython bindings for [mdxjs-rs](https://github.com/wooorm/mdxjs-rs), a Rust implementation of MDX.\n\n**\u26a0\ufe0f Alpha Software**: This package is in early development. The API may change between versions.\n\n## Features\n\n- Fast MDX compilation via Rust (10-100x faster than Node.js subprocess)\n- Simple Python API mirroring @mdx-js/mdx\n- Zero dependencies (beyond the compiled extension)\n- Helpful error messages with line/column information\n- Full MDX 2 support via mdxjs-rs\n\n## Installation\n\n```bash\npip install mdxjs-py\n```\n\n**Note**: Currently only Linux x86_64 wheels are provided. For other platforms, you'll need Rust installed to build from source:\n\n```bash\n# Install Rust first\ncurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh\n\n# Then install from PyPI (will compile automatically)\npip install mdxjs-py\n```\n\n## Usage\n\n### Basic Compilation\n\n```python\nfrom mdxjs_py import compile\n\n# Compile MDX to JavaScript\nmdx_content = \"\"\"\n# Hello World\n\nThis is **MDX** content with <Button onClick={() => alert('hi!')}>JSX</Button>!\n\"\"\"\n\ntry:\n js_output = compile(mdx_content)\n print(js_output)\nexcept ValueError as e:\n print(f\"MDX compilation failed: {e}\")\n```\n\n### Validation\n\nUse `compile()` to validate MDX syntax:\n\n```python\nfrom mdxjs_py import compile\n\ndef validate_mdx(content: str) -> tuple[bool, str | None]:\n \"\"\"Validate MDX content.\n\n Returns:\n (is_valid, error_message)\n \"\"\"\n try:\n compile(content)\n return True, None\n except ValueError as e:\n return False, str(e)\n\n# Example usage\ncontent = \"<Button>Unclosed tag\"\nis_valid, error = validate_mdx(content)\nif not is_valid:\n print(f\"Invalid MDX: {error}\")\n # Output: Invalid MDX: Expected a closing tag for `<Button>`\n```\n\n### Configuration Options\n\n```python\nfrom mdxjs_py import compile\n\n# With options (matches @mdx-js/mdx API)\njs_output = compile(\n mdx_content,\n development=True, # Enable development mode\n jsx_runtime=\"automatic\", # or \"classic\"\n jsx_import_source=\"react\", # for automatic runtime\n)\n```\n\n## API Reference\n\n### `compile(source: str, **options) -> str`\n\nCompile MDX source to JavaScript. API-compatible with @mdx-js/mdx.\n\n**Parameters:**\n\n- `source` (str): The MDX source code to compile\n- `development` (bool, optional): Enable development mode\n- `jsx` (bool, optional): Keep JSX (default: False, compiles to JS)\n- `jsx_runtime` (str, optional): \"automatic\" or \"classic\"\n- `jsx_import_source` (str, optional): Package for automatic JSX runtime\n- `pragma` (str, optional): JSX pragma for classic runtime\n- `pragma_frag` (str, optional): JSX pragma fragment for classic runtime\n- `pragma_import_source` (str, optional): Pragma import source\n- `provider_import_source` (str, optional): Provider import source\n\n**Returns:**\n\n- `str`: Compiled JavaScript code\n\n**Raises:**\n\n- `ValueError`: If MDX compilation fails with detailed error message\n\n### `compile_sync(source: str, **options) -> str`\n\nSynchronous version of `compile()` (alias for compatibility).\n\n### `is_available() -> bool`\n\nCheck if the Rust module is built and available.\n\n## Common Use Cases\n\n### 1. Validate User-Generated MDX\n\n```python\ndef process_user_mdx(content: str) -> dict:\n \"\"\"Process and validate user MDX content.\"\"\"\n try:\n js_output = compile(content)\n return {\n \"success\": True,\n \"output\": js_output\n }\n except ValueError as e:\n # Extract line/column from error\n error_str = str(e)\n return {\n \"success\": False,\n \"error\": error_str,\n \"hint\": \"Check for unclosed tags or invalid JSX\"\n }\n```\n\n### 2. MDX Syntax Checking in CI/CD\n\n```python\nimport glob\nfrom pathlib import Path\nfrom mdxjs_py import compile\n\ndef check_all_mdx_files(directory: str) -> bool:\n \"\"\"Validate all MDX files in a directory.\"\"\"\n all_valid = True\n\n for mdx_path in Path(directory).glob(\"**/*.mdx\"):\n try:\n with open(mdx_path, 'r') as f:\n compile(f.read())\n print(f\"\u2705 {mdx_path}\")\n except ValueError as e:\n print(f\"\u274c {mdx_path}: {e}\")\n all_valid = False\n\n return all_valid\n```\n\n### 3. Pre-process MDX for Frontend\n\n```python\ndef prepare_mdx_for_frontend(content: str, max_retries: int = 3) -> str:\n \"\"\"Validate and prepare MDX for frontend rendering.\"\"\"\n for attempt in range(max_retries):\n try:\n # Validate compilation\n compile(content)\n return content\n except ValueError as e:\n if attempt == max_retries - 1:\n # Return with warning comment\n return f\"<!-- MDX validation warning: {e} -->\\n{content}\"\n # Could attempt to fix common issues here\n content = fix_common_mdx_issues(content)\n\n return content\n```\n\n## Performance\n\nBenchmarked on an Intel i7-9750H:\n\n```python\nimport time\nfrom mdxjs_py import compile\n\ncontent = \"# Heading\\n\\nParagraph with **bold** text.\\n\\n\" * 100\n\n# Single compilation\nstart = time.time()\ncompile(content)\nprint(f\"Single: {(time.time() - start) * 1000:.2f}ms\")\n# Result: ~0.5ms\n\n# Bulk compilation\nstart = time.time()\nfor _ in range(1000):\n compile(content)\nprint(f\"1000x: {time.time() - start:.2f}s\")\n# Result: ~0.5s (vs ~50s with Node.js subprocess)\n```\n\n## Development\n\n### Building from Source\n\n```bash\n# Clone the repository\ngit clone https://github.com/SamDc73/mdxjs-py\ncd mdxjs-py\n\n# Create virtual environment\npython -m venv venv\nsource venv/bin/activate\n\n# Install build dependencies\npip install maturin pytest\n\n# Build and install locally\nmaturin develop --release\n\n# Run tests\npytest tests/\n```\n\n### Architecture\n\nThis is a minimal Python binding to mdxjs-rs, designed for:\n\n- **Zero overhead** - Direct 1:1 binding to mdxjs-rs functions\n- **API compatibility** - Matches @mdx-js/mdx compile() API\n- **Simplicity** - No abstraction layers, just the binding\n\n## Credits\n\nThis package is a thin Python wrapper around:\n\n- **[mdxjs-rs](https://github.com/wooorm/mdxjs-rs)** by [Titus Wormer](https://github.com/wooorm) - The Rust MDX implementation that does all the heavy lifting\n- Built with **[PyO3](https://pyo3.rs)** - Rust bindings for Python\n- Packaged with **[maturin](https://maturin.rs)** - Build and publish Rust Python extensions\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\nThis package is MIT licensed, same as mdxjs-rs.\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for release history.\n\n## Support\n\n- \ud83d\udc1b [Report bugs](https://github.com/SamDc73/mdxjs-py/issues)\n- \ud83d\udca1 [Request features](https://github.com/SamDc73/mdxjs-py/issues)\n- \ud83d\udcd6 [Documentation](https://github.com/SamDc73/mdxjs-py#readme)\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Python bindings for mdxjs-rs - Fast MDX to JavaScript compilation via Rust",
"version": "0.1.0a2",
"project_urls": {
"Changelog": "https://github.com/SamDc73/mdxjs-py/blob/main/CHANGELOG.md",
"Documentation": "https://github.com/SamDc73/mdxjs-py#readme",
"Homepage": "https://github.com/SamDc73/mdxjs-py",
"Issues": "https://github.com/SamDc73/mdxjs-py/issues",
"Repository": "https://github.com/SamDc73/mdxjs-py"
},
"split_keywords": [
"mdx",
" markdown",
" jsx",
" compiler",
" rust",
" mdxjs"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "76c57866d41c1c4ed980fff211f5bfb8b6713e00f17d94e510a80afd6b873212",
"md5": "7d8d24667c39fe1ad25685d3e2bf25ac",
"sha256": "5ef2b2c99ae57707a565a4a967a06157a5f3e12cb1f0dc992320af49e434b63d"
},
"downloads": -1,
"filename": "mdxjs_py-0.1.0a2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"has_sig": false,
"md5_digest": "7d8d24667c39fe1ad25685d3e2bf25ac",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 2143432,
"upload_time": "2025-09-01T06:52:34",
"upload_time_iso_8601": "2025-09-01T06:52:34.827703Z",
"url": "https://files.pythonhosted.org/packages/76/c5/7866d41c1c4ed980fff211f5bfb8b6713e00f17d94e510a80afd6b873212/mdxjs_py-0.1.0a2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-01 06:52:34",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "SamDc73",
"github_project": "mdxjs-py",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "mdxjs-py"
}