Microformats2 (mf2) parser and deterministic renderer powered by JustHTML.
- Python 70%
- TypeScript 29.2%
- JavaScript 0.5%
- Makefile 0.3%
| src | chore: release 0.2.1 | |
| tests | feat: show pronouns after name | |
| .gitignore | Remove old file | |
| .gitmodules | chore: use submodule for tests | |
| .pre-commit-config.yaml | Initial version | |
| LICENSE.md | First version | |
| Makefile | feat: npm package | |
| pyproject.toml | chore: release 0.2.1 | |
| README.md | Update docs | |
| uv.lock | chore: release 0.2.1 | |
mf2dom
Microformats2 (mf2) parser and deterministic renderer powered by JustHTML.
mf2dom focuses on:
- Correct mf2 parsing (validated against the official
microformats-testssuite) - Deterministic HTML rendering and stable round-trips (
HTML -> mf2 -> HTML) - A small runtime surface area (no network I/O, no BeautifulSoup)
Installation
pip install mf2dom
Requires Python 3.11+.
Quickstart
Parse mf2 JSON from HTML:
import mf2dom
html = '<a class="h-card u-url p-name" href="/me">Alice</a>'
doc = mf2dom.parse(html, base_url="https://example.com/")
print(doc["items"])
The parsed document is a dict with items, rels, and rel-urls keys (mf2 JSON shape).
Render mf2 JSON back into canonical HTML:
html2 = mf2dom.render(doc)
Async parsing (offloads to a thread):
doc = await mf2dom.parse_async(html, base_url="https://example.com/")
API
mf2dom.parse(html, *, base_url=None, url=None) -> dicthtmlcan be a string/bytes, ajusthtml.JustHTMLinstance, or a JustHTML root node.base_urlcontrols resolution of relative URLs (preferred).urlis a deprecated alias.
mf2dom.parse_async(...)isparse(...)viaasyncio.to_thread(...).mf2dom.render(doc) -> strrenders a deterministic HTML representation of an mf2 document.
JavaScript/TypeScript Renderer
A TypeScript implementation of the renderer is available in src/mf2dom-ts/ for client-side rendering (e.g., with sql-wasm.js).
Installation
cd src/mf2dom-ts
npm install
npm run build
Usage
ES Module:
import { render, renderItemElement, renderItems } from './dist/mf2dom.esm.js';
// Render full document to HTML string
const html = render(mf2Document);
// Render to DOM element (most efficient for client-side)
const main = render(mf2Document, { asElement: true });
document.body.appendChild(main);
// Render single item
const card = renderItemElement(hCard);
// Batch render with DocumentFragment (efficient for lists)
const fragment = renderItems(entries);
container.appendChild(fragment);
Browser script tag:
<script src="dist/mf2dom.min.js"></script>
<script>
const html = mf2dom.render(doc);
// or
const el = mf2dom.render(doc, { asElement: true });
</script>
Build Output
| File | Size | Use Case |
|---|---|---|
dist/mf2dom.esm.js |
5.2kb | ES modules (bundlers, modern browsers) |
dist/mf2dom.cjs |
5.7kb | CommonJS (Node.js) |
dist/mf2dom.min.js |
5.7kb | Browser <script> tag |
Why mf2dom vs mf2py?
Both libraries parse microformats, but they optimize for different use cases:
- Choose
mf2domif you need deterministic rendering, stable round-trips, and a smaller/no-network runtime surface (useful for normalization, caching, and "canonical mf2 HTML" fixtures). - Choose
mf2pyif you need URL fetching, microformats1 compatibility, metaformats support, or wider Python version support.
Testing & correctness
- Official parsing fixtures:
tests/test_official_microformats_suite.pyruns the upstreammicroformats-testsJSON fixtures. - Coverage gate:
pyproject.tomlenforces 100% branch coverage.
To run the official fixture suite locally, check out microformats-tests as a sibling directory:
git clone https://github.com/microformats/microformats-tests ../microformats-tests
Development (uv)
uv sync --group dev
uv run pytest
uv run coverage run -m pytest && uv run coverage report
uv run pre-commit install
License
AGPL 3