wnx/commonmark-markdown-renderer
A PHP package that renders a league/commonmark AST back to Markdown, letting you parse Markdown, manipulate the document tree, and re-render it. Note that alternate Markdown syntaxes mean input and output may differ.
The core decision: build vs. buy a round-trip Markdown pipeline. Parsing Markdown to an AST is a solved, commodity problem — league/commonmark is already in most Laravel codebases. Rendering that AST back to Markdown is the leg teams always end up hand-rolling with regex, and it is the leg that breaks. This package is a ~one-class answer to that gap. The real decision it informs is not "should we use this library" but "should we stop doing string surgery on Markdown documents and move to AST-based transformation."
Roadmap items this unblocks. Any feature where a machine writes Markdown back into storage becomes materially cheaper:
Constraint that must shape the feature spec, not just the tech design. The README's own caveat — alternate Markdown syntaxes for the same element mean input and output may differ — is a product constraint. It means:
An architectural decision worth making deliberately. Because the package is a vendor dependency with 0 dependents and a single maintainer, the PM-relevant call is to mandate an internal adapter (e.g. MarkdownRoundTrip) so no application code imports Wnx\ directly. This keeps the fork-or-replace contingency a 2-day task instead of a quarter-long migration, and it is the single cheapest risk mitigation available.
What it does not decide. It does not tell you whether your Markdown corpus round-trips acceptably — only a measurement on real documents does. That is a one-day spike, and it is the gating input to the build/buy call.
Adopt when most of these hold:
league/commonmark 2.x for HTML rendering, so the dependency graph grows by one leaf rather than a new tree.str_replace manipulation of Markdown, which is the status quo this replaces and which reliably costs more over time than it appears to.Look elsewhere — or build in-house — when any of these hold:
league/commonmark or an older PHP floor, forcing a dependency bump across the estate will cost more than the feature is worth.Decision rule to give the team: if the spike shows diff rate is acceptable and the round trip is stable on repeat renders, adopt behind an adapter on a non-critical path. If either fails, stop — do not try to patch fidelity into a package with no external users.
To executives — one paragraph: This is a small, free, MIT-licensed utility that closes a specific gap in our content tooling: it lets us take Markdown apart, modify it programmatically, and write valid Markdown back out. That capability currently doesn't exist in our stack, so any feature involving bulk or automated edits to Markdown documents gets hand-built with fragile string manipulation or doesn't get built. There is no infrastructure cost, no vendor contract, and the code surface is small enough that if the maintainer walks away we can own it outright in days. The ask is one engineering day to measure how well it handles our actual documents; that measurement is the go/no-go, and it's cheap enough that the downside is bounded at a day.
To engineering — the short version: One extension class plus one renderer, plugged into a league/commonmark Environment. No facade, no service provider, no config, no migrations — nothing to wire into the framework, which is the point. Framework-agnostic, so it works identically in an artisan command, a queued job, or an admin action. The API you'll actually touch is renderDocument(). Two hard requirements from me: (1) put it behind an internal adapter — no Wnx\ imports outside that one class — so replacing or forking it later is a swap, not a migration; (2) pin the exact version and verify the league/commonmark constraint against every consuming app before rollout, since a version collision is the most likely integration snag we'll hit.
To engineering — the caveats to design around, stated plainly: It is not a byte-preserving round trip, and the README says so. Output will differ syntactically from input while meaning the same thing. That rules out change-detection-by-hash and "silent" reformatting. The other open question is extension coverage — GFM constructs and any custom nodes we've registered may be dropped or downgraded. We spike on our own corpus first, document the gaps, then decide whether to feature-flag it onto a non-critical normalisation job or vendor the renderer into app/ and patch coverage ourselves. Both are acceptable outcomes; what we are not doing is adopting it blind.
How can I help you explore Laravel packages today?