mdast-util-math

mdast extension to parse and serialize LaTeX-style math syntax embedded in markdown syntax trees.

Library
npm
v3.0.0
21stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
26/100Needs Attention
Development Activity0
Maintenance20
Community12
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
69/100Good
Architecture85
Code Quality92
Innovation55
Learning Curve45

mdast-util-math adds math nodes to mdast, the syntax tree format used throughout the unified/remark ecosystem. It plugs into mdast-util-from-markdown and mdast-util-to-markdown to parse LaTeX-style math delimited by single or double dollar signs into inlineMath and math nodes, and to serialize those nodes back into markdown text without losing literal dollar signs inside the math content.

The package is maintained by the syntax-tree collective and works alongside micromark-extension-math for tokenizing and mdast-util-to-hast for turning math nodes into HTML code/pre elements with math-inline/math-display classes, forming the plumbing that higher-level packages like remark-math build on top of.

What You Get

  • mathFromMarkdown() extension that turns micromark math tokens into inlineMath and math mdast nodes
  • mathToMarkdown() extension that serializes those nodes back into $…$ and $$…$$ markdown, escaping dollar runs safely
  • TypeScript types for Math, InlineMath and ToOptions, registered against @types/mdast via declaration merging
  • Automatic hast field annotations (hName/hChildren) so mdast-util-to-hast renders math as <code class=“language-math”> elements with no extra configuration

Common Use Cases

  • Adding LaTeX math support to a remark-based markdown pipeline via remark-math
  • Building a custom static site generator that needs to parse and round-trip math notation in markdown
  • Writing tooling that visits or transforms math nodes in a markdown AST, e.g. rendering with KaTeX or MathJax

Under The Hood

Architecture The package has a two-function design split across index.js and lib/index.js: mathFromMarkdown builds a from-markdown extension using enter/exit handler maps keyed to micromark token names (mathFlow, mathFlowFenceMeta, mathText, mathFlowValue, mathTextData), pushing mdast math/inlineMath nodes onto the CompileContext stack and attaching hast-facing data (hName/hChildren) directly at enter time so mdast-util-to-hast needs no separate mapping step. mathToMarkdown mirrors this with a handlers map (math, inlineMath) plus an unsafe-pattern list telling mdast-util-to-markdown’s serializer safety layer which characters need escaping in phrasing versus mathFlowMeta constructs, and computes a dynamic fence length via longestStreak to avoid clashing with dollar runs already present in the value being serialized. The only internal state is a CompileContext.data.mathFlowInside flag distinguishing the opening from the closing fence token. index.d.ts holds all type declarations plus module-augmentation blocks that register the new node types into mdast’s BlockContentMap/PhrasingContentMap and mdast-util-to-markdown’s ConstructNameMap, so the real “core abstraction” here is the extension-object contract defined by the sibling from-markdown/to-markdown libraries — if that contract changed, this package’s enter/exit/handlers shape would need to change in lockstep.

Tech Stack Plain JavaScript (100%), shipped ESM-only (type: module, single exports entry). Runtime dependencies are devlop (dev-only assert helper for invariants), longest-streak (safe fence-length computation), mdast-util-from-markdown and mdast-util-to-markdown (the sibling libraries this plugs into), unist-util-remove-position, and type-only @types/hast/@types/mdast. Dev tooling generates declarations from JSDoc via TypeScript’s checkJs + emitDeclarationOnly rather than hand-written .ts, enforces full type coverage with type-coverage, lints/formats with xo and prettier via remark-preset-wooorm, and tests with Node’s built-in test runner plus c8 for coverage. GitHub Actions runs the suite across a Node version matrix and uploads coverage to Codecov; there is no bundler or build step beyond type-declaration emission since the package ships plain JS.

Code Quality Testing uses Node’s native node:test/node:assert/strict runner in a single ~480-line test.js, organized into three top-level test() blocks (core, mathFromMarkdown, mathToMarkdown) with async sub-cases asserting exact deepEqual trees for parse cases and exact string output for serialize cases. Coverage is enforced at 100% via c8 and type coverage at 100% via type-coverage, both gating the test script in CI. Error handling relies on devlop’s assert() invariant helper, which is stripped from production builds via the development/production conditional exports, appropriate for a small structural utility with no user-facing failure modes. Style is enforced by the strict xo ESLint preset and prettier, run as part of the same test pipeline. Naming conventions mirror sibling syntax-tree/unified extension packages closely and consistently.

What Makes It Unique This package doesn’t introduce new math-rendering technology — it implements the same extension contract used by other unified/remark syntax extensions (gfm, frontmatter, etc.), mirroring their structure closely. Its one genuinely careful design choice is the dynamic delimiter-length algorithm in mathToMarkdown, which uses longestStreak to pick a fence at least one dollar sign longer than any run already present in the raw value, plus an escalating-width loop for inline math, guaranteeing lossless round-tripping of math content that itself contains literal dollar signs — an edge case naive implementations tend to skip. Beyond that, it follows standard, well-established plugin architecture for the ecosystem it belongs to.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search