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

mdat

v2.3.2

Published

CLI tool and TypeScript library implementing the Markdown Autophagic Template (MDAT) system. MDAT lets you use comments as dynamic content templates in Markdown files, making it easy to generate and update readme boilerplate.

Downloads

2,260

Readme

mdat

NPM Package mdat License: MIT CI

CLI tool and TypeScript library implementing the Markdown Autophagic Template (MDAT) system. MDAT lets you use comments as dynamic content templates in Markdown files, making it easy to generate and update readme boilerplate.

Table of contents

Overview

MDAT is a CLI tool and library that uses HTML comments in Markdown files as placeholders for dynamic content. Write a comment like <!-- title -->, run mdat, and it expands into real content pulled from your project metadata. Bundled rules handle common readme sections automatically, and custom rules let you easily extend it to output almost anything.

A trivial example...

Given placeholder comments in a Markdown file like this:

some-file.md

<!-- title -->

Run your file through the tool:

mdat some-file.md

To get:

some-file.md

<!-- title -->

# mdat

<!-- /title -->

The <!-- title --> comment was expanded with content derived from your project's metadata. The rule system behind these expansions is simple to define and readily extensible.

Getting started

Dependencies

Node 22+ (specifically >=22.17.0). Written in TypeScript with bundled type definitions.

Installation

Install locally to access the CLI and API in a single project:

pnpm install mdat

Or install globally:

pnpm install --global mdat

Features

  1. Minimalist syntax

    No screaming caps or wordy opening and closing tag keywords:

    <!-- title -->
    
    # mdat
    
    <!-- /title -->
  2. Single-comment placeholders

    Drop in a single opening comment and mdat adds the closing tag on expansion:

    <!-- title -->
  3. JSON arguments

    Pass extra data or configuration into a comment template with JSON:

    <!-- title({ prefix: "🙃" }) -->

    Arguments are parsed with JSON5, so quoting keys is optional. The bundled readme rules use Zod to validate arguments.

  4. Flexible rule system

    Rules range from a single key-value pair:

    export default { keyword: 'content' }

    To async functions:

    export default { date: () => `${new Date().toISOString()}` }

    To full rule objects with metadata:

    export default {
      date: {
        content: () => `${new Date().toISOString()}`,
        order: 1,
      },
    }

    See the bundled rules for more complex examples.

    JSON files can also be used as rule sets. MDAT flattens them so any dot-notated key path becomes a comment keyword.

  5. TypeScript native

    Rule types are exported, and configuration files can be written in TypeScript.

  6. Validation

    The mdat check command dry-runs an expansion and exits with code 1 if the file on disk has stale content.

  7. Compound rules

    Compound rules combine several individual rules into a single comment keyword.

    See <!-- header --> for an example.

  8. Polyglot metadata

    Bundled rules pull normalized metadata from almost any project type via metascope, not just package.json.

Usage

[!WARNING]

The MDAT CLI tool directly manipulates the contents of Markdown files.

Make sure any text you care about is committed before running mdat, and never directly modify content inside comment expansion blocks.

CLI

Command: mdat

Work with MDAT placeholder comments in Markdown files.

This section lists top-level commands for mdat.

If no command is provided, mdat expand is run by default.

Usage:

mdat [command] [files..] [options]

| Command | Argument | Description | | ---------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------- | | expand | [files..] [options] | Expand MDAT placeholder comments. If no files are provided, the closest readme.md is expanded. (Default command.) | | collapse | [files..] [options] | Collapse MDAT placeholder comments. If no files are provided, the closest readme.md is collapsed. | | strip | [files..] [options] | Strip MDAT comments while preserving expanded content. If no files are provided, the closest readme.md is stripped. | | check | [files..] [options] | Check if MDAT placeholder comments are up to date. Exits with code 1 if any files have stale or unexpanded content. | | create | [options] | Create a new Markdown file from a template. |

See the sections below for more information on each subcommand.

Subcommand: mdat expand

Expand MDAT placeholder comments. If no files are provided, the closest readme.md is expanded.

Usage:

mdat expand [files..] [options]

| Positional Argument | Description | Type | | ------------------- | ----------------------------------------------------------------------------------------------------- | -------- | | files | Markdown file(s) with MDAT placeholder comments. If not provided, the closest readme.md file is used. | string |

| Option | Description | Type | Default | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------- | | --verbose | Enable verbose logging. All verbose logs are prefixed with their log level and are printed to stderr for ease of redirection. | boolean | | | --config-c | Path(s) to additional mdat configuration files. | array | | | --output-o | Output file directory. | string | Same directory as input file. | | --name-n | Output file name. | string | Same name as input file. Overwrites the input file. | | --print | Print the expanded Markdown to stdout instead of saving to a file. Ignores --output and --name options. | boolean | | | --format-f | Format the output with Prettier. Discovers Prettier config from the file path. Requires prettier as a peer dependency. | boolean | | | --help-h | Show help | boolean | | | --version-v | Show version number | boolean | |

Subcommand: mdat collapse

Collapse MDAT placeholder comments. If no files are provided, the closest readme.md is collapsed.

Usage:

mdat collapse [files..] [options]

| Positional Argument | Description | Type | | ------------------- | ----------------------------------------------------------------------------------------------------- | -------- | | files | Markdown file(s) with MDAT placeholder comments. If not provided, the closest readme.md file is used. | string |

| Option | Description | Type | Default | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------- | | --verbose | Enable verbose logging. All verbose logs are prefixed with their log level and are printed to stderr for ease of redirection. | boolean | | | --output-o | Output file directory. | string | Same directory as input file. | | --name-n | Output file name. | string | Same name as input file. Overwrites the input file. | | --print | Print the expanded Markdown to stdout instead of saving to a file. Ignores --output and --name options. | boolean | | | --format-f | Format the output with Prettier. Discovers Prettier config from the file path. Requires prettier as a peer dependency. | boolean | | | --help-h | Show help | boolean | | | --version-v | Show version number | boolean | |

Subcommand: mdat strip

Strip MDAT comments while preserving expanded content. If no files are provided, the closest readme.md is stripped.

Usage:

mdat strip [files..] [options]

| Positional Argument | Description | Type | | ------------------- | ----------------------------------------------------------------------------------------------------- | -------- | | files | Markdown file(s) with MDAT placeholder comments. If not provided, the closest readme.md file is used. | string |

| Option | Description | Type | Default | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------- | | --verbose | Enable verbose logging. All verbose logs are prefixed with their log level and are printed to stderr for ease of redirection. | boolean | | | --output-o | Output file directory. | string | Same directory as input file. | | --name-n | Output file name. | string | Same name as input file. Overwrites the input file. | | --print | Print the expanded Markdown to stdout instead of saving to a file. Ignores --output and --name options. | boolean | | | --format-f | Format the output with Prettier. Discovers Prettier config from the file path. Requires prettier as a peer dependency. | boolean | | | --help-h | Show help | boolean | | | --version-v | Show version number | boolean | |

Subcommand: mdat check

Check if MDAT placeholder comments are up to date. Exits with code 1 if any files have stale or unexpanded content.

Usage:

mdat check [files..] [options]

| Positional Argument | Description | Type | | ------------------- | ----------------------------------------------------------------------------------------------------- | -------- | | files | Markdown file(s) with MDAT placeholder comments. If not provided, the closest readme.md file is used. | string |

| Option | Description | Type | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------- | | --verbose | Enable verbose logging. All verbose logs are prefixed with their log level and are printed to stderr for ease of redirection. | boolean | | --config-c | Path(s) to additional mdat configuration files. | array | | --format-f | Format the output with Prettier. Discovers Prettier config from the file path. Requires prettier as a peer dependency. | boolean | | --help-h | Show help | boolean | | --version-v | Show version number | boolean |

Subcommand: mdat create

Create a new Markdown file from a template.

Usage:

mdat create [options]

| Option | Description | Type | Default | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------- | | --verbose | Enable verbose logging. All verbose logs are prefixed with their log level and are printed to stderr for ease of redirection. | boolean | | | --interactive-i | Run the guided interactive create process. Set explicitly to false to use default values and skip the prompt. | boolean | true | | --overwrite | Replace an existing readme file if one is found. | boolean | false, if an existing readme is found, don't touch it. | | --output-o | Output file directory. | string | Same directory as input file. | | --expand-e | Automatically run mdat immediately after creating the readme template. | boolean | true | | --template-t | Specify a template to use for the new readme. | "MDAT Readme" "Standard Readme Basic" "Standard Readme Full" | "MDAT Readme" | | --compound | Use compound comment version of the template to replace several individual comment placeholders where possible. This combines things like <!-- title -->, <!-- badges -->, etc. in a single <!-- header --> comment. It's less clutter when you're editing, but it's also less explicit. The final readme.md output is identical. | boolean | true | | --help-h | Show help | boolean | | | --version-v | Show version number | boolean | |

Examples

Expand the nearest readme
mdat
Expand a specific file
mdat your-file.md
Expand multiple files
mdat *.md
Additional config
mdat --config rules.ts more-rules.js yet-more-rules.json
Check if a file is up to date
mdat check
Collapse expanded content
mdat collapse
Strip MDAT comments from expanded content
mdat strip
Expand and format with Prettier
mdat --format
Create a starter readme from scratch
mdat create

API

mdat exports functions for expanding, collapsing, checking, and creating Markdown files programmatically.

expand / expandString

Expands MDAT comments in one or more files. If no files are provided, auto-finds the closest readme. Writing is the caller's responsibility. Call .toString() on the returned VFile to get the result.

import { expand } from 'mdat'
import { write } from 'to-vfile'

const [file] = await expand('readme.md')
await write(file)
function expandString(
  markdown: string,
  config?: ConfigToLoad,
  options?: { format?: boolean },
): Promise<VFile>

collapse / collapseString

Removes expanded content, leaving only the opening comment placeholders. Same signatures as expand / expandString.

strip / stripString

Strips all MDAT comment tags (both opening and closing) while preserving expanded content between them. Same signatures as expand / expandString (without the config parameter, since rules are not needed).

This is useful for producing a "clean" Markdown file that no longer depends on MDAT for future updates.

check / checkString

Dry-run expand and compare with the file on disk. Returns inSync: false if the file would change. When stale, per-tag diagnostic messages are added to the result VFile identifying which specific tags are stale or unexpanded. Use reporterMdat from remark-mdat to display these messages.

create / createInteractive

function create(options?: {
  compound?: boolean
  expand?: boolean
  output?: string
  overwrite?: boolean
  template?: string
}): Promise<string>

Creates a new readme from a bundled template. createInteractive runs the same flow with interactive prompts.

loadConfig

function loadConfig(options?: {
  additionalConfig?: ConfigToLoad
  defaults?: Config
  searchFrom?: string
}): Promise<Config>

Discovers and loads configuration via cosmiconfig, merges with defaults and additional config, and returns a validated Config object. Useful for advanced use cases or passing into remark-mdat directly.

Configuration

Configuration is defined in config files discovered automatically by cosmiconfig, or provided explicitly via --config.

TypeScript or JavaScript configuration files are recommended. The file default-exports a Config record:

// Your mdat.config.ts
import { defineConfig } from 'mdat'

export default defineConfig({
  date: {
    content: () => new Date().toISOString(),
    order: 1,
  },
  greeting: 'Hello, world!',
})

The configuration file may be located in any cosmiconfig search location. mdat.config.ts in the project root is the most common choice.

Configuration in package.json

Shared configurations can be specified in package.json by passing a string that resolves to a module with a default Config export:

{
  "mdat": "@kitschpatrol/mdat-config"
}

Rules can also be defined directly in package.json:

{
  "mdat": {
    "what": "hath god wrought"
  }
}

[!NOTE]

mdat also searches for and merges any ambient Remark .remarkrc configuration files. This is unrelated to mdat rules, but it can affect how Markdown is rendered.

Config format

A config is a record whose keys become comment keywords:

type Config = Record<string, Rule>

type Rule =
  | ((options: JsonValue, context: RuleContext) => Promise<string> | string)
  | Rule[]
  | string
  | {
      content:
        | ((options: JsonValue, context: RuleContext) => Promise<string> | string)
        | Rule[]
        | string
      order?: number
    }

type RuleContext = {
  filePath: string | undefined
  frontmatter: Record<string, unknown> | undefined
  tree: Root
}

Simple rules are strings or functions on the key. For metadata like processing order, use the object form with a content key. The content value can be an array of Rule objects for compound rules.

When multiple config files are loaded, they are merged. CLI --config takes precedence over ambient configuration, and the last rule for a given key wins.

Creating custom rules

See the Examples section of the remark-mdat readme, or look at the bundled rules for complex examples.

Bundled rules

Stand-alone

  • <!-- title -->

    The project name, derived from project metadata.

  • <!-- banner -->

    Looks for an image in the project directory for use as a banner. Searches for typical names and formats.

  • <!-- badges -->

    Generates badges based on project metadata. Supports NPM version, license, and CI status badges.

  • <!-- description -->

    The project description. Also aliased as <!-- short-description --> for standard-readme compatibility.

  • <!-- install -->

    Ecosystem-aware install instructions derived from project metadata. Emits pnpm add / npm install for Node packages (plus a pnpx / npx hint when the project exposes a binary), and falls back to pip, cargo, gem, or go install for Python, Rust, Ruby, and Go projects.

  • <!-- dependencies -->

    Documents platform requirements and peer dependencies. Lists runtime platforms (Node, Python, Rust, Go, Ruby, etc.) with version constraints from engines or equivalent metadata, supported operating systems, and peer dependencies with links to npm.

  • <!-- table-of-contents -->

    Auto-generated via mdast-util-toc. Also aliased as <!-- toc -->.

  • <!-- contributing -->

    Invites issues and pull requests with links derived from project metadata.

  • <!-- license -->

    Documents the project license.

  • <!-- code({ file: "./file.ts" }) -->

    Embeds a code block from elsewhere in your repository.

  • <!-- size({ file: "./package.json" }) -->

    Embeds a file's size, with optional Brotli or Gzip compressed size.

  • <!-- size-table({ files: [".gitignore", "license.txt"] }) -->

    A table of files and their compressed sizes:

    | File | Original | Gzip | Brotli | | ----------- | -------- | ----- | ------ | | .gitignore | 318 B | 252 B | 237 B | | license.txt | 1 kB | 659 B | 468 B |

Compound

Compound rules combine several stand-alone rules under a single keyword.

  • <!-- header -->

    Combines rules commonly applied at the top of a readme:

    <!-- title -->
    <!-- banner -->
    <!-- badges -->
    <!-- shortDescription -->
  • <!-- footer -->

    Combines rules commonly applied at the end:

    <!-- contributing -->
    <!-- license -->

Bundled templates

The create command provides starter readme templates:

  • MDAT Readme — An expansive starting point. The readme in this repo was started from this template.
  • Standard Readme basic — Only the "required" sections from the Standard Readme spec.
  • Standard Readme full — All sections from the Standard Readme spec.

Plugins

Rule plugins are packages for sharing mdat expansion rules across projects.

Installing a rule plugin

pnpm install mdat-plugin-example

Spread the plugin into your configuration:

// Your mdat.config.ts
import { defineConfig } from 'mdat'
import example from 'mdat-plugin-example'

export default defineConfig({
  ...example,
})

Then use the rule in Markdown:

<!-- example -->

And expand:

mdat

Creating a rule plugin

A rule plugin is an ESM module with a default export of Config. By convention, use the mdat-plugin- name prefix.

import type { Config } from 'mdat'

export default {
  hello: {
    content() {
      return 'Hello from an mdat plugin!'
    },
  },
} satisfies Config

See mdat-plugin-example for a complete example.

Available rule plugins

mdat-plugin-tldraw

Embed tldraw files in your readme.

Example: <!-- tldraw({ src: "./sketch.tldr" }) -->

mdat-plugin-cli-help

Transform a CLI command's --help output into Markdown tables. Recursively calls --help on subcommands. Currently parses Yargs and Meow output formats, falling back to a plain text code block as necessary.

Example: <!-- cli-help -->

Migrating from 1.x to 2.x

The 2.0 version introduces significant breaking changes in the interest of simplicity and a somewhat narrowed scope of concerns.

Details of the changes and migration strategies are enumerated below.

Flat CLI commands

The mdat readme subcommand is gone. All commands are now top-level:

| v1 | v2 | | ------------------- | --------------- | | mdat readme | mdat | | mdat readme init | mdat create | | mdat readme check | mdat check | | mdat expand | mdat expand | | mdat collapse | mdat collapse |

Running mdat with no arguments expands the closest readme, matching the behavior of the 1.x mdat readme command.

Polyglot metadata

Readme rules no longer read from package.json directly. Instead, mdat uses metascope to aggregate metadata across ecosystems (Node, Python, Rust, Go, Ruby, etc.). Rules like <!-- title -->, <!-- description -->, and <!-- license --> now work in non-Node projects.

Simplified configuration

The v1 Config type (which had fields like addMetaComment, assetsPath, closingPrefix, etc.) is gone. Configuration files now export a flat Config record of rules directly:

// Mdat.config.ts (v2)
import { defineConfig } from 'mdat'

export default defineConfig({
  hello: 'world',
})

The --assets, --package, --meta, --prefix, and --rules CLI options have been removed. Use --config (-c) to provide additional config files.

Stricter argument syntax

In 2.x, arguments must use function-call syntax with parentheses

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

New functionality

  • The new --format flag runs expanded output through Prettier with local configuration before writing.
  • The badges rule now detects GitHub Actions CI workflows and includes a CI status badge automatically.
  • Check command reimplemented as a simplified dry-run expand and diff and reporting if content is unexpanded or out of date. Respects the --format flag.

Rule shape changes

See the remark-mdat project readme for details on lower-level changes to how rules are defined and structured.

Background

Motivation

A package definition file like package.json is the canonical source of truth for a project's metadata, yet fragments of it end up duplicated in the readme. Keeping them in sync is tedious.

MDAT solves this by turning HTML comments in Markdown into placeholders for dynamic content. Run mdat and the comments expand with content pulled from your project metadata. The file is updated in place.

Similar projects

There's quite a bit of prior art and similar explorations of this problem space:

  • Benjamin Lupton's projectz
    Goes way back.

  • David Wells' Markdown Magic
    I somehow missed the existence of this one until after building out MDAT. It's very similar conceptually, and has a nice ecosystem of plugins.

  • Titus Wormer's mdast-zone
    Allows comments to be used as ranges or markers in Markdown files. Similar tree parsing and walking strategy to MDAT. Mdast-zone uses different syntax for arguments, and requires both opening and closing tags to be present for expansion to occur.

  • Jason Dent's inject-markdown

  • lillallol's md-in-place

  • AutoMD
    Extremely similar functionality to mdat. The project was initiated around the same time as MDAT, but I didn't find the project until a few years later. Ships in the night.

  • Franck Abgrall's readme-md-generator

  • Anders Pitman's tuplates

  • VitePress' Markdown file inclusion

  • Hiroki Osame's comment-mark

  • Hiroki Osame's mdeval

Implementation notes

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

Maintainers

kitschpatrol

Acknowledgments

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