php-collective/djot
PHP parser and renderer for Djot, the lightweight markup language created by CommonMark's author. Convert Djot markup to HTML through a clean, extensible API—a modern alternative to Markdown offering more consistent syntax, better footnotes, and richer built-in features.
MarkdownToDjot for existing Markdown corpora and an HTML importer for content currently stored as rendered markup. That changes the migration conversation from "we'd have to write a converter" to "we have a converter starting point and need a validation project." It also unlocks ingestion use cases: importing READMEs, past CMS bodies, HTML email, or legacy forum posts into a canonical Djot store.@mentions, #tags): MentionsExtension now accepts optional resolver and tagResolver closures plus a shared resolverContext, turning @name and #tag into host-app URLs. Architecturally this is the right shape — URL and permission logic stays in your app, the parser only identifies and substitutes. Tags are opt-in (Djot has no inline tag syntax), and refused mentions fall back to plain text across HTML, Markdown, and plain-text output. This supports a decision like "do we build notifications/mention-linking in-house or standardize on the parser's extension hook?" — but only if mention syntax is a settled product requirement, which it usually isn't at format-selection time.Consider it when:
MarkdownToDjot now preserves references, entities, pointy destinations and link titles, and keeps raw HTML inert by default (new MarkdownToDjot(trustedRawHtml: true) for trusted input). The HTML importer now keeps significant inline whitespace, treats blockquote inline runs as runs, renders nested quotation marks faithfully, and distinguishes authored ASCII punctuation from typographic punctuation. Still verify against your corpus; these are the exact class of bug that only shows up on real documents.MentionsExtension resolvers keep host logic in the host app and default to safe plain-text on refusal.Look elsewhere when:
league/commonmark, which has years of hardening, extension ecosystem, and security review behind it.trustedRawHtml: true is an explicit footgun that must never be set on user-supplied data.Gating checks before adoption: run the full Djot spec test suite / conformance corpus yourself, and re-run it after every upgrade — much of this release is parity work against djot.js, which means parity is being established incrementally. Confirm the public API stability you need (AST access vs HTML-only output; whether warnings are machine-readable enough for editor integration). Verify PHP version support against your runtime. Check commit cadence over the last 6 months. Decide and document which parsing path production uses. Pin the version and record a fork-and-maintain contingency.
For executives (one paragraph): We have a recurring, low-differentiation cost: parsing and rendering the markup our authors and users write, plus the one-off cost of ever getting old content into a new format. Today that means either a pile of Markdown extensions or a homegrown parser we must maintain forever. This package is a small, MIT-licensed PHP parser for Djot — a newer light markup language from the author of Pandoc and CommonMark — that lets us standardize on one well-specified format, buy the parsing instead of building it, and now includes converters so the migration question has an answer that isn't "write your own." The upside is a smaller maintenance burden, a cleaner authoring model, a portable plain-text content format, and usable author-facing error locations. The honest risk is unchanged and mostly maturity: it is early-stage (pre-1.0) with a thin community, and this release shows us the parser still has two internal rendering paths that can disagree. So we would treat it as a contained, reversible bet — a spike behind an interface, no migration of existing content until a fidelity comparison passes, and a fork contingency because the license permits it. Cost to evaluate is days, not quarters.
For engineering (the ask):
MarkupRenderer interface so the parser is swappable and never leaks into domain code. Same for import: wrap MarkdownToDjot and the HTML importer behind an Importer interface.MarkdownToDjot(trustedRawHtml: true) on user-supplied content, and keep raw HTML inert on import until the sanitization policy is written down.MentionsExtension, keep resolvers pure and dependency-free (they run inside parsing), and confirm the "refused mention renders as plain text" behavior matches the product requirement for private/deleted accounts.league/commonmark — no sunk cost.How can I help you explore Laravel packages today?