Skip to main content

Pure-Python Markdown ↔ Atlassian Document Format (ADF) converter with lossless round-tripping

Project description

adflux

CI Release PyPI Python License: MIT

A pure-Python converter between Markdown (CommonMark + GFM) and Atlassian Document Format (ADF) — the JSON document model used by Confluence and Jira. Round-trips losslessly, including ADF-only constructs like panels, status badges, mentions, task lists, expand sections, and macros.

flowchart LR
  MD[Markdown] -->|markdown-it-py| IR((Internal IR + adf-* envelopes))
  ADF[ADF JSON] -->|mapping.yaml| IR
  IR -->|profile filter| W{writer}
  W -->|CommonMark serializer| MD2[Markdown]
  W -->|mapping.yaml| ADF2[ADF JSON]

Why adflux

ADF is JSON, but no mature pure-Python tool converts it to and from Markdown without losing information. adflux fills that gap:

  • No system dependencies. pip install adflux — no Pandoc binary, no Node, no Ruby.
  • Lossless ADF round-trips. A declarative mapping.yaml plus a small envelope convention round-trips every ADF construct, including ones the library has never seen.
  • Idiomatic Markdown output. Panels become GitHub alert blockquotes, expand sections become <details>, smart cards become autolinks. The output looks correct in GitHub, VS Code, and any standard viewer.
  • Fidelity profiles make lossy / lossless behavior explicit and selectable per call.

Install

pip install adflux

Requires Python ≥ 3.11. Linux, macOS, and Windows are CI-tested.

Quick start

from adflux import convert

# Markdown → ADF JSON
adf = convert(open("README.md").read(), src="md", dst="adf")

# ADF JSON → Markdown, dropping ADF-only constructs to plain content
md = convert(adf, src="adf", dst="md", profile="pretty-md")
adflux convert --from md  --to adf README.md > readme.adf.json
adflux convert --from adf --to md page.json
adflux validate page.adf.json

Full API and CLI reference: docs/usage.md.

Fidelity profiles

Profile Behavior on lossy targets
strict-adf Preserve every ADF construct (default). Round-trips losslessly.
pretty-md Drop ADF-only envelopes silently; keep their visible content.
fail-loud Raise UnrepresentableNodeError on the first envelope.

Worked examples: docs/profiles.md.

Markdown rendering of ADF constructs

ADF construct Rendered as
panel (info/note/warning/…) GitHub alert blockquote (> [!NOTE], > [!TIP], …)
expand <details><summary>title</summary>…</details>
inlineCard / blockCard / embedCard Autolink (<https://…>)
taskList / taskItem GFM task list (- [ ], - [x])
emoji, mention Plain text (🚀, @alice)
Everything else HTML-comment marker (<!--adf:status …-->)

The reader recognises every form on its way back, so MD → ADF → MD is stable.

How it works

  • IR: an in-memory document tree built on panflute AST classes (used purely as Python data structures — no Pandoc binary involved), with Div / Span envelopes for ADF-only constructs.
  • Envelopes: class prefix adf-<nodeType> plus a base64-JSON blob for complex attributes. A universal adf-raw fallback guarantees zero data loss.
  • Mapping: every ADF node type is described in src/adflux/formats/adf/mapping.yaml. Adding a new node type is a YAML edit.

Deeper dives: docs/design.md · docs/architecture.md · docs/profiles.md · docs/fidelity-matrix.md · docs/extending.md · docs/e2e-testing.md.

Examples

Runnable scripts in examples/:

python examples/md_to_adf.py README.md
python examples/adf_to_markdown.py
python examples/confluence_roundtrip.py

Development

git clone https://github.com/mikejhill/adflux
cd adflux
uv sync --all-groups
uv run poe check

Common tasks via poethepoet:

poe test         # unit + integration + round-trip + property tests
poe test-e2e     # live Confluence + Jira round-trip suite (requires .env)
poe lint         # ruff format --check + ruff check
poe typecheck    # ty check src
poe cov          # pytest with coverage report
poe check        # lint + typecheck + test (run before opening a PR)
poe build        # sdist + wheel into ./dist

CI runs the full matrix on Linux, macOS, and Windows for Python 3.11–3.13.

Releasing

Releases are automated. Land changes on main using Conventional Commits, then run the Release workflow from the GitHub Actions tab. go-semantic-release computes the next version, builds the sdist + wheel, creates a tagged GitHub Release, and publishes to PyPI via OIDC trusted publishing.

Contributing

Issues and pull requests are welcome. Before opening a PR:

  1. poe check must pass (lint + typecheck + tests).
  2. Use a Conventional Commit message (feat:, fix:, docs:, …).
  3. New ADF node types need a fixture in tests/roundtrip/test_node_coverage.py.

License

MIT

Project details


Download files

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

Source Distribution

adflux-0.1.0.tar.gz (64.5 kB view details)

Uploaded Source

Built Distribution

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

adflux-0.1.0-py3-none-any.whl (42.8 kB view details)

Uploaded Python 3

File details

Details for the file adflux-0.1.0.tar.gz.

File metadata

  • Download URL: adflux-0.1.0.tar.gz
  • Upload date:
  • Size: 64.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for adflux-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c131d9d920a1e8787244ccbd0230df6fc479b0d0a49f56ef0e7ace5013f6d0ff
MD5 8330ad09dd44368af72a24a1c707df99
BLAKE2b-256 8f0a5ceb0eadd1cbc0e00bfa320aa9e08ecdf0980cf72673fcbbf73de0698892

See more details on using hashes here.

Provenance

The following attestation bundles were made for adflux-0.1.0.tar.gz:

Publisher: release.yml on mikejhill/adflux

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

File details

Details for the file adflux-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: adflux-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 42.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for adflux-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cc90d199487d14800f9785eb19e9ce2e379179dba3396f7e585e3b886553c0f4
MD5 efbd56255675d8ea98d4854329d38d03
BLAKE2b-256 2e7198b92e59c6ac816ea1d3fe92558c0f93012b3f584f5315f08e444a3c0296

See more details on using hashes here.

Provenance

The following attestation bundles were made for adflux-0.1.0-py3-none-any.whl:

Publisher: release.yml on mikejhill/adflux

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