Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Djot Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • php-collective/djot is a pure-PHP, Composer-installable parser for Djot — a Markdown successor/alternative authored by John MacFarlane (pandoc/CommonMark lineage) that resolves Markdown's inline-parsing ambiguities and adds first-class attributes, footnotes, definition lists, math, and tables.
  • It is a rendering library, not a framework component. There is no Laravel service provider, facade, Blade directive, or config surface, so the integration is a thin wrapper we own: a singleton binding, a DjotRenderer service, and a cached render() contract.
  • Fit is strongest where we control the author: internal docs, knowledge bases, release notes, static content, email/notification templates, and product help centers. It is a weaker fit for untrusted, user-generated content (comments, profiles) without an explicit sanitization layer.
  • 0.1.34 materially improves architectural fit for two use cases we care about: (a) Markdown interop — the package now ships MarkdownToDjot import (preserving references, entities, pointy destinations, link titles) and a Markdown output path, so Djot is no longer a one-way door; (b) extension surface — MentionsExtension now takes resolver / tagResolver closures plus a shared resolverContext, proving there is a real extension/hook model rather than a hardcoded renderer.
  • Fit is still a wrapper-shaped integration, but the wrapper is now smaller: less bespoke link-rewriting, more configuration.

Integration feasibility

  • Trivial to install (composer require php-collective/djot), MIT-licensed, no framework coupling, no known transitive dependency weight. Expectation: PHP 8.x + ext-mbstring, PSR-4 autoloading.
  • The real work is not installation; it is (a) output sanitization, (b) caching strategy, (c) parity testing against the official Djot spec test suite, and (d) editorial migration from Markdown for existing corpora — though (d) now has tooling support via MarkdownToDjot.
  • 0.1.34 answers two of our open questions and raises one new one:
    • Answered: an AST exists ("owned-AST path"), and it is a supported, tested surface — the release explicitly reconciles the AST path with the fast path.
    • Answered (partially): safe defaults exist on the import path — Markdown import keeps raw HTML inert unless trustedRawHtml: true is passed — and resolved mention/tag URLs pass a URL-safety check.
    • Raised: there are two rendering paths (a "fast HTML path" and an "authoritative parser"), and 0.1.34 contains a fix where the fast path had to fall back to the authoritative parser for emphasis/strong marker seams. That is a correctness-divergence class of bug, not a one-off typo.
  • Stars (25) and dependents (0) still tell us we would be an early adopter. Opportunity score (26.24) is unchanged: cheap to trial, unproven in production, no ecosystem to lean on.

Technical risk

  • Bus factor / longevity: small maintainer pool, no commercial backing, no downstream dependents to surface breakage. MIT means we can fork, but forking a parser is a real ongoing cost.
  • Spec churn and silent output drift: Djot is pre-1.0, and 0.1.34 is itself proof — a patch release changes rendered HTML (a fenced block containing one blank line no longer renders like an empty fence; emphasis delimiters at punctuation/whitespace boundaries now render differently in Markdown output). Pin exact versions and gate every bump behind a rendering-diff test. This is no longer a theoretical risk; it is the observed release pattern.
  • Dual-path divergence (new): the fast HTML path and the authoritative parser are separate implementations of the same semantics, and they have demonstrably disagreed. Any surface where we accept the fast path inherits a class of bug that only snapshot/conformance testing will catch.
  • Correctness/conformance: the package is a port of the reference Lua implementation, and the changelog measures fixes against djot.js, not just internal expectations — a good sign. Parity with the upstream spec test suite remains the single most important thing to verify.
  • Security: improved but not closed. Raw HTML is inert by default on Markdown import only; nothing in 0.1.34 states that Djot→HTML rendering of authored raw HTML or inline attributes is escaped by default, so assume we still need an allowlist sanitizer (HTMLPurifier or equivalent). User-supplied resolver closures are a new trust boundary — an extension resolver that builds URLs from @name must not become an SSRF/open-redirect vector.
  • Performance: the existence of a "fast path" implies the maintainers consider the authoritative parser slow enough to warrant a second implementation. That is useful — but we must benchmark both and understand the fallback trigger, because adversarial input that forces fallback repeatedly combines worst-case parsing with double work.
  • Opportunity cost: if the org is already standardized on CommonMark (league/commonmark), adopting Djot splits the content toolchain, editor support, and contributor familiarity. 0.1.34's Markdown import/export meaningfully lowers this cost — it makes coexistence and reversibility credible — but does not eliminate it.

Key questions

  1. Does it emit an AST with source positions? Answered: yes — an owned-AST path exists and inline warnings now report authored line and column inside lists, quotes, tables, divs, and footnotes.
  2. New, highest priority: what exactly triggers fast-path fallback, can the fast path be disabled outright, and what is the test coverage proving fast path ≡ authoritative parser?
  3. What is its pass rate on the official Djot spec test suite, and how is upstream (djot.js) parity tracked release-over-release?
  4. What PHP versions and extensions are required, and is there a stated support policy?
  5. Is there a safe/default mode that escapes or strips raw HTML? Partially answered for Markdown import; still open for Djot-authoring and rendered HTML output, and for inline attributes.
  6. What is the stability contract for the extension API (resolver, tagResolver, resolverContext)? Closures are a public surface we will write code against.
  7. How faithful is MarkdownToDjot on our actual corpus, and is round-tripping (Markdown → Djot → Markdown) lossless enough to use as a migration and rollback tool?
  8. What are the benchmarks on 100 KB / 1 MB documents for both paths, and how does the parser behave on malformed or adversarial input?
  9. Who maintains it, what is the release cadence, and is there a credible fork/exit plan if it stalls?
  10. Which of our surfaces actually benefit from Djot's authoring model versus just needing Markdown?

Integration Approach

Stack fit

  • Laravel 10/11/12 on PHP 8.1+ is a natural host: bind a DjotRenderer singleton in a small package/service provider, expose a Djot::toHtml(string $markdown): string facade, a Blade::directive('djot') for inline content, and a Cast/accessor for Eloquent columns.
  • Pair it with a cache: render at write time (model save/observer) into a rendered_html column or a cache key, never on every read. Rendering is a pure function of source + package version + path configuration, so cache keys must include both — 0.1.34 would otherwise leave stale HTML for every fenced block containing a blank line.
  • Use the AST for the non-HTML consumers — search indexing, TOC/anchor generation, heading extraction, link rewriting — so we don't regex over rendered HTML later. 0.1.34's positioned inline warnings make it viable to surface lint diagnostics in the CMS at authoring time.
  • Configure mention/tag resolution through MentionsExtension resolvers rather than post-processing HTML. This keeps link construction in our code (testable, URL-safe) instead of in string manipulation, and gives us one place to enforce internal-link policy.
  • Decide and document the fast-path policy in the wrapper: prefer the authoritative parser until we have a snapshot suite proving equivalence on our corpus. Optimizing for latency before correctness here is a bad trade.
  • Sanitization belongs in the wrapper, not at call sites: one SanitizerInterface, applied by default, with an explicit opt-out reserved for trusted internal and trusted raw-HTML content (mirroring the package's own trustedRawHtml posture).

Migration path

  • This release changes the migration calculus. MarkdownToDjot is now the first tool to evaluate, not editorial rewriting. Run it over a sample of the real corpus, diff the rendered output against the existing CommonMark renderer, and quantify the delta before committing to any hand-migration.
  • The Markdown output path adds a rollback story: content authored in Djot can be exported to Markdown if we exit. Validate that round-trip on our corpus; treat it as the exit ramp, not a daily-use feature.
  • Still do not replace an existing Markdown parser in place. Introduce Djot behind a feature flag on a single low-blast-radius surface (internal docs or a help center), with both renderers available.
  • The "new content in Djot, old content frozen in Markdown" path with a discriminator column remains the lowest-risk option — and is now more attractive because import tooling makes the eventual thaw cheaper if we ever want it.

Compatibility

  • Verify PHP constraint against our fleet, and confirm no conflicting transitive constraints with existing packages (league/commonmark can coexist; both are standalone).
  • Add a contract test that renders a fixed corpus and compares HTML output snapshots; run it on every dependency bump. 0.1.34 is the concrete justification — a patch release changed rendered output in at least two documented ways.
  • Add a conformance job that runs the upstream spec fixtures against our wrapper, not just the package's own tests, so we detect regressions introduced by integration code.
  • Add a fast-path equivalence test: render the same corpus with fast path on and off, assert identical output. If the package does not expose a switch, this question goes back to the maintainer before we adopt.

Sequencing

  1. Spike (1–2 days): install, parse a representative corpus, confirm AST access, extension resolvers, sanitization hooks, fast-path toggle, and rough performance.
  2. Conformance + benchmark gate: spec suite pass rate, fast-vs-authoritative equivalence, large-document timing, malformed-input behavior. Fail here and we stop.
  3. Migration feasibility (new, 1–2 days): run MarkdownToDjot over real content, diff rendered output vs. current renderer, and validate the Markdown export round-trip as an exit ramp.
  4. Wrapper package: service provider, facade, Blade directive, sanitizer, mention/tag resolvers, cache policy, snapshot tests.
  5. Pilot: one internal, author-controlled surface behind a flag; measure authoring feedback and render latency.
  6. Decide: expand only if the pilot shows a real authoring or output-quality win over CommonMark; otherwise keep it as an internal-docs-only tool.

Operational Impact

Maintenance

  • Expect to own the integration layer fully and to contribute upstream or fork if the project stalls. Pin to exact versions, subscribe to releases, and review the changelog against our snapshot tests before any bump — 0.1.34 shows patch releases can change output.
  • Because it is a port, upgrades may lag or lead upstream Djot. Decide explicitly which is canonical for us — the PHP package's behavior or the reference implementation's spec — and test against that. The changelog's repeated "matches djot.js" framing suggests the maintainers treat djot.js as canonical; we should adopt the same stance.
  • The surface we now depend on is larger than a single render() call: AST access, two rendering paths, extension resolvers, and Markdown import/export. Keep each behind a thin wrapper interface so a fork or replacement stays a contained change, but budget more integration surface than the 0.1.33-era assessment implied.
  • Watch the extension API specifically — closures with resolverContext are new in 0.1.34 and have no stated stability guarantee.

Support

  • No commercial support, no SLA, community GitHub issues only. Escalation path is internal: the team that owns the wrapper owns the bugs.
  • Document the failure policy up front: known-unsupported syntax and refused mentions/tags degrade to escaped or plain text, never to raw HTML. 0.1.34's behavior — a refused mention stays plain text in HTML, Markdown, and plain-text output, and stays text inside a link label — is exactly the contract we should codify and test against.
  • Contributor familiarity is a support cost too — most engineers know Markdown, few know Djot. Expect repeated questions about why a construct renders differently.
  • New support surface: authors will ask why a @mention didn't resolve. Resolver failures need logging and a visible-but-safe fallback.

Scaling

  • Parsing cost is CPU-bound and per-document. Pre-render at write time and cache; read paths should never parse.
  • The fast/authoritative split means we should benchmark and size against the authoritative path, treating fast path as an optimization to be enabled only after equivalence is proven on our corpus.
  • For high-volume surfaces, batch pre-render in queued jobs and store output; this also makes version upgrades a re-render job rather than a request-time risk. 0.1.34 is a rehearsal for exactly this — an upgrade requires re-rendering every document containing fenced code blocks with blank lines.
  • Large documents (manuals, exported PDFs) are the stress case — set a size ceiling and test it, rather than discovering it in production.
  • Horizontal scaling is unaffected: it is a stateless library with no external services. Resolver closures must be stateless or cache-backed to keep it that way.

Failure modes

  • XSS via raw HTML or attributes — still highest severity; mitigated on the Markdown-import path by inert-by-default raw HTML, unmitigated-by-default on the Djot-render path. Mitigate with a default-on sanitizer and an explicit trusted-content flag.
  • Fast-path / authoritative divergence (new) — the failure mode is silent and content-dependent, which is the worst combination. Mitigate with an equivalence test and a default to the authoritative parser.
  • Resolver-driven SSRF or open redirect (new) — mention/tag resolvers build URLs from authored content. Mitigate by resolving only against allowlisted internal namespaces, never interpolating arbitrary input into a fetch.
  • Pathological input / ReDoS-class slowdowns — mitigate with timeouts, input size caps, and fuzz tests before request-path exposure; specifically fuzz the fallback trigger.
  • Silent rendering drift after an upgrade — now a demonstrated risk, not a hypothetical. Mitigate with HTML snapshot tests in CI and re-render jobs on every bump.
  • Abandonment — mitigate with version pinning, an MIT fork contingency, and the Markdown-export exit plan the package now enables.
  • Author confusion producing malformed content — mitigated somewhat by positioned inline warnings; pair with a style guide and a preview/render check in the CMS.

Ramp-up

  • Developer ramp-up remains low for basic rendering, but the integration now has more knobs (AST, fast path, resolvers, import/export). Budget roughly a day for the core renderer and an additional half-day for the extension and import surfaces.
  • Author ramp-up is still the real cost. Djot looks like Markdown and is not Markdown; without a short internal style guide and editor support (still nascent compared with Markdown tooling), we will see inconsistent output and support tickets.
  • The positioned inline warnings from 0.1.34 are a genuine ramp-up lever — surface them in the CMS preview and authors get feedback at the moment of error rather than in a later support ticket. Prioritize this in the pilot.
  • Plan a half-day writing workshop and a linter/preview step for the pilot surface, and treat editor tooling gaps as a first-class adoption risk rather than an afterthought.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels