npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

remark-mdat

v2.2.2

Published

A remark plugin implementing the Markdown Autophagic Template (MDAT) system.

Readme

remark-mdat

NPM Package remark-mdat License: MIT CI

A remark plugin implementing the Markdown Autophagic Template (MDAT) system.

[!NOTE]

Please see The mdat package for a higher-level CLI tool and library with a collection of built-in expansion rules.

Table of contents

Overview

This is a remark plugin that automates the inline expansion of placeholder HTML comments with dynamic content in Markdown, making it easy to keep readme files and other documentation in sync with an external single source of truth.

The plugin finds placeholder comments in a Markdown file like this:

<!-- title -->

And expands them with the data of your choosing. In this case, it reads the title field a nearby package.json:

<!-- title -->

# remark-mdat

<!-- /title -->

This plugin powers the higher-level mdat package, which is a better place to start if you just want to expand some comments and aren't working directly with remark processing pipelines.

Getting started

Dependencies

This library is ESM only and requires Node 20.19.6+. It's designed to work with Remark 15. remark-mdat is implemented in TypeScript and bundles a complete set of type definitions.

Installation

pnpm add remark-mdat

Usage

API

Core plugin

This package's default export implements the unified Plugin type.

The plugin is integrated into a remark process chain via the .use() method:

import { remark } from 'remark'
import remarkMdat from 'remark-mdat'

remark().use(remarkMdat)

Options

The plugin accepts an optional Options object with a rules field. Rules is a Record<string, Rule> where each key is a keyword matching an HTML comment in the Markdown file (e.g. title matches <!-- title -->).

HTML comments using code-style notation (<!-- // ... -->, <!-- # ... -->, <!-- /* ... */ -->) are ignored and will not be treated as mdat keywords. Rule keywords cannot start with /, *, or #.

A Rule value can take several forms:

const rules: Rules = {
  // String: direct replacement
  greeting: 'Hello, world!',

  // Function: dynamic content (sync or async)
  time: () => new Date().toDateString(),

  // Function with arguments: receives parsed options from the comment
  personalGreeting: (options) => `Hello, ${options.name}!`,

  // Object: with processing priority
  title: {
    order: 1, // Runs after other rules (default is 0)
    content: () => getTitle(), // String, function, or array
  },

  // Array: compound rule combining multiple sub-rules
  header: ['# My Project', () => getDescription()],

  // Function with context: access the document tree, frontmatter, and file path
  toc: (_options, context) => generateTocFromTree(context.tree),
}

Rule context

Rule content functions receive a RuleContext object as their second argument, providing access to the document being processed:

type RuleContext = {
  /** File path of the source document, if known. */
  filePath: string | undefined
  /** Parsed YAML frontmatter from the document, if present. */
  frontmatter: Record<string, unknown> | undefined
  /** The full mdast AST of the document. Do not mutate. */
  tree: Root
}

Frontmatter is automatically extracted if available. If the document has no frontmatter block, context.frontmatter remains undefined.

const rules: Rules = {
  // Access frontmatter values
  title: (_options, context) => `# ${context.frontmatter?.title ?? 'Untitled'}`,

  // Use the file path
  source: (_options, context) => `Source: ${context.filePath ?? 'unknown'}`,

  // Traverse the AST
  toc: (_options, context) => generateTocFromTree(context.tree),
}

Passing arguments to rules

Arguments are passed using function-call syntax: <!-- keyword(...) -->. The value inside the parentheses is parsed as JSON5, which means unquoted keys and single quotes are allowed.

Options as JSON5 (unquoted keys, single quotes):

<!-- greeting({name: 'Alice', shout: true}) -->

Options as strict JSON:

<!-- greeting({"name": "Alice", "shout": true}) -->

Single primitive value:

<!-- repeat(3) -->

Prefer object arguments over single primitive values for all but the most contextually clear argument values.

Any JSON5 value is supported: objects, arrays, strings, numbers, and booleans. Comments without parentheses receive an empty object {} as their options.

For simplicity's sake, only a single argument position is supported. If you need pass multiple arguments, wrap them in an object. For security's sake, only JSON5 / JSON values are permitted in keyword arguments, no JavaScript is evaluated.

Examples

Basic

remark-mdat includes one test rule by default, <!-- mdat -->.

import { remark } from 'remark'
import remarkMdat from 'remark-mdat'

const markdownInput = '<!-- mdat -->'
const markdownOutput = await remark().use(remarkMdat).process(markdownInput)

console.log(markdownOutput.toString())

// Logs:
// <!-- mdat -->
//
// Powered by the Markdown Autophagic Template system: [mdat](https://github.com/kitschpatrol/mdat).
//
// <!-- /mdat -->

With rules

If you wanted to replace <!-- time --> comments in your Markdown file with the current time, you could pass in a rule:

import type { Rules } from 'remark-mdat'
import { remark } from 'remark'
import remarkMdat from 'remark-mdat'

// Create the rules
const rules: Rules = {
  time: new Date().toDateString(),
}

const markdownInput = '<!-- time -->'

// Pass the rules to remarkMdat
const markdownOutput = await remark().use(remarkMdat, { rules }).process(markdownInput)

console.log(markdownOutput.toString())

// Logs:
// <!-- time -->
//
// Mon April 01 2026
//
// <!-- /time -->

See the mdat package for a higher-level API and CLI that can operate directly on files or strings. It also provides dynamic rule loading and configuration resolution, and bundles a collection of rules convenient for use in readme files.

Utilities

The plugin bundles a number of mdast utilities designed to operate directly on syntax trees. These are exported to support customized Unified.js processors and enforce modularity and separation of concerns in mdat's internal implementation, but you do not need to use them directly — all functionality is encapsulated in the single remarkMdat plugin export.

The remark-mdat plugin chains these utilities together to accommodate the typical use case of end-to-end expansion of mdat comments. For now, the individual utility transformers are not published individually to NPM, and are instead bundled with remark-mdat.

Errors and warnings are reported inline during expansion via VFile messages, following remark ecosystem conventions. Use reporterMdat to extract and format these messages for console output.

  • mdast-util-mdat

    Composite transformer function performing end-to-end mdat comment expansion on Markdown ASTs by chaining the other utility functions described below.

    Exported as mdat(tree: Root, file: VFile, rules: Rules): Promise<void>

    Utilities wrapped by mdast-util-mdat:

    • mdast-util-mdat-split

      Transformer function that splits multi-comment HTML nodes into individual mdast nodes, allowing inline mdat expansion comments.

      Exported as mdatSplit(tree: Root, file: VFile): void

    • mdast-util-mdat-collapse

      Transformer function that resets all mdat comment expansions in a file, collapsing expanded comments back into single-line placeholders.

      Exported as mdatCollapse(tree: Root, file: VFile): void

    • mdast-util-mdat-expand

      Transformer function that expands mdat comments (e.g. <!-- title -->) in a Markdown file according to the provided rules. Reports errors for rules that throw or return empty content, and warnings for comments with no matching rule.

      Exported as mdatExpand(tree: Root, file: VFile, rules: Rules): Promise<void>

  • mdast-util-mdat-strip

    Transformer function that strips all mdat comment nodes (both opening and closing) from the tree, preserving any content between them. Code-style comments (<!-- // ... -->, <!-- # ... -->, <!-- /* ... */ -->) are left untouched. Useful for producing a final Markdown document with all mdat scaffolding removed.

    Exported as mdatStrip(tree: Root, file: VFile): void

  • mdast-util-mdat-diff

    Compares an original document against an expanded document per-tag, identifying which mdat comments have stale content, are unexpanded, missing, or are up to date. Reports results via VFile messages (source: 'diff') and returns structured MdatDiffResult[]. Both trees should have mdatSplit applied before calling. Useful for implementing check commands that report which specific tags need updating.

    Exported as mdatDiff(originalTree: Root, originalFile: VFile, expandedTree: Root, expandedFile: VFile): MdatDiffResult[]

Migrating from 1.x to 2.x

Version 2.0 simplifies and solidifies the API by removing several configuration options and validation features that added complexity without sufficient benefit. The core expansion behavior is unchanged — the plugin still matches HTML comments to rules and expands them — but the way you configure it has changed.

Simplified options

In 1.x, the plugin accepted an options object with multiple fields to customize parsing and generation:

// 1.x
remark().use(remarkMdat, {
  rules: { title: () => '# My Title' },
  addMetaComment: true,
  closingPrefix: '/',
  keywordPrefix: 'mm-',
  metaCommentIdentifier: '+',
})

In 2.x, the configuration options for parsing and generation have been removed. The Options object now contains only a rules field:

// 2.x
remark().use(remarkMdat, {
  rules: { title: () => '# My Title' },
})

If you were importing MdatOptions, MdatExpandOptions, MdatCheckOptions, or MdatCleanOptions, replace them with Options (for plugin configuration) or Rules (for the rules record).

Removed options

The following plugin options have been removed entirely:

| Removed option | Migration | | ----------------------- | ------------------------------------------------------------------------ | | addMetaComment | Remove. Auto-generated warning comments are no longer supported. | | metaCommentIdentifier | Remove. The <!--+ ... +--> meta comment syntax is gone. | | closingPrefix | Remove. The closing prefix is now always / (e.g. <!-- /keyword -->). | | keywordPrefix | Remove. Keyword prefixing / namespacing is no longer supported. |

Removed rule properties

| Removed property | Migration | | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | required | Remove. All rules are treated equally. Missing comments produce a warning instead of an error. | | order | Remove. The 1.x order property enforced comment position in the document. In 2.x, order controls processing priority only (default: 0). |

Changed rule properties

| Changed property | Migration | | ------------------ | ------------------ | | applicationOrder | Change to order. |

Renamed clean to collapse

The former mdast-util-mdat-clean / mdatClean is now mdast-util-mdat-collapse / mdatCollapse since this is more clearly the opposite of "expand", and aligns with language used in the Mdat CLI tool. (Note that mdatClean is still available as a deprecated alias, but it will be removed in 3.0.)

Removed validation utility

The mdast-util-mdat-check utility and its export mdatCheck have been removed. Validation logic (missing rules, empty content, rule errors) is now handled inline during expansion by mdatExpand, which reports issues as VFile messages. Use reporterMdat to format and display these messages.

For use cases where you need to check whether a file's expansions are up to date without modifying it (similar to validate in 1.x), use the new mdatDiff utility. It compares an original document against a freshly expanded version per-tag, identifying stale, unexpanded, or missing comments and reporting structured results.

Rule function signature change

In 1.x, rule content functions received the mdast tree directly as the second argument:

// 1.x
const rules = {
  toc: (_options, tree) => generateTocFromTree(tree),
}

In 2.x, the second argument is a RuleContext object containing the tree, parsed frontmatter, and file path:

// 2.x
const rules = {
  toc: (_options, context) => generateTocFromTree(context.tree),
}

Stricter argument syntax

In 1.x, the argument parser was very permissive — parentheses were optional, bare key-value pairs were auto-wrapped in braces, and space-separated arguments worked:

<!-- greeting name: "Alice" -->
<!-- greeting ({name: "Alice"}) -->
<!-- greeting({name: "Alice"}) -->

In 2.x, arguments must use function-call syntax with parentheses. The content inside the parentheses is parsed as JSON5:

<!-- greeting({name: 'Alice'}) -->

Bare or space-separated arguments like <!-- greeting name: "Alice" --> will no longer be parsed — the extra text after the keyword is ignored and the rule receives an empty {} options object.

As a trade-off for the stricter syntax, primitive values are now supported as arguments: <!-- repeat(3) -->, <!-- show("hello") -->.

Comment-style comments are ignored

HTML comments using code-style prefixes (<!-- // ... -->, <!-- # ... -->, <!-- /* ... */ -->) are now ignored by the parser, so you can use them for regular comments alongside mdat keywords without triggering warnings. This replaces 1.x parser configuration options like keywordPrefix.

Compound rule error handling

In 1.x, a failing sub-rule in a compound rule (array of rules) caused the entire expansion to fail. In 2.x, individual sub-rule failures are reported as warnings and skipped — the expansion only fails if every sub-rule fails.

New utility: mdatStrip

A new mdast-util-mdat-strip utility is available for removing all mdat comment nodes from a document while preserving the content between them. Code-style comments (//, #, /*) are left untouched. This is useful for producing a final Markdown document with no mdat scaffolding. See the Utilities section for details.

Removed export: deepMergeDefined

The deepMergeDefined utility has been moved to the mdat package. If you were importing it from remark-mdat, import it from mdat instead.

Implementation notes

This project was split from a monorepo containing both mdat and remark-mdat into separate repos in July 2024.

The API was redesigned and simplified for version 2 in March 2026.

Remark is not a peer dependency on account of this discussion: strip-markdown/issues/24

Maintainers

kitschpatrol

Acknowledgments

Thanks to the unified team for their superb ecosystem of AST tools.

Contributing

Issues are welcome and appreciated.

Please open an issue to discuss changes before submitting a pull request. Unsolicited PRs (especially AI-generated ones) are unlikely to be merged.

This repository uses @kitschpatrol/shared-config (via its ksc CLI) for linting and formatting, plus MDAT for readme placeholder expansion.

License

MIT © Eric Mika