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

@ambicuity/matterforge

v1.0.0

Published

Parse front-matter from a string or file. Fast, reliable and easy to use. Parses YAML front matter by default, but also has support for YAML, JSON, TOML or Coffee Front-Matter, with options to set custom delimiters.

Readme

@ambicuity/matterforge

npm version downloads/month license types

Parse front-matter from a string or file. Fast, reliable, and easy to use. Parses YAML front matter by default, with built-in support for JSON and JavaScript (eval-gated). Extensible to TOML or any custom format via pluggable engines.

matterforge is for developers building content-heavy sites, docs platforms, static-site generators, MDX pipelines, and Edge-rendered publishing tools. Ships with TypeScript types, dual ESM/CJS exports, browser + edge-runtime support, and built-in schema validation.

Install

Requires Node.js ≥20. For older Node versions, use @ambicuity/[email protected].

npm install @ambicuity/matterforge

Upgrading from v1? See MIGRATION.md — the YAML engine swap and a couple of internals changed. Most consumers need no code changes.

Quick Start

import matter from '@ambicuity/matterforge';

const result = matter(`---
title: Hello
slug: home
---
Content here`);

console.log(result.data.title); // Hello
console.log(result.content);    // Content here

Overview

Converts a string with front-matter, like this:

---
title: Hello
slug: home
---
<h1>Hello world!</h1>

Into an object like this:

{
  content: '<h1>Hello world!</h1>',
  data: {
    title: 'Hello',
    slug: 'home'
  }
}

Why matterforge?

| Feature | matterforge | gray-matter | | --- | --- | --- | | Built-in schema validation (Zod, Valibot, ArkType) | ✅ | ❌ | | Error code frames & diagnostic CLI | ✅ | ❌ | | Deterministic stringify (sortKeys) | ✅ | ❌ | | ESM + CJS dual exports (via tshy) | ✅ | ❌ | | Edge-runtime support (Cloudflare/Vercel/Deno) | ✅ | ❌ | | Browser support (verified in headless Chromium) | ✅ | ❌ | | YAML 1.2 (via yaml) | ✅ | ❌ (YAML 1.1 via js-yaml) | | TypeScript generics (typed data field) | ✅ | ❌ | | Async file reading | ✅ | ❌ | | Property-based + fuzz tests | ✅ | ❌ | | Regex-free parsing | ✅ | ✅ | | Custom engines | ✅ | ✅ | | Tree-shakeable (ESM, sideEffects: false) | ✅ | ❌ | | Node ≥20 (modern toolchain) | ✅ | ❌ (Node ≥14) |

API Summary

| Method | Purpose | Runtime | |---|---|---| | matter(input, options?) | Parse front matter from a string | All | | matter.stringify(file, data, options?) | Convert data back into front matter | All | | matter.read(filepath, options?) | Read and parse a file synchronously | Node.js | | matter.readAsync(filepath, options?) | Read and parse a file asynchronously | Node.js | | matter.test(string, options?) | Check whether a string has front matter | All | | matter.clearCache() | Clear internal parse cache | All | | matter.language(string, options?) | Detect language after first delimiter | All | | matter.YAMLException | Deprecated. Alias for YAMLParseError from yaml. Removal planned in v3.0.0. | All | | matter.MatterforgeError | Enriched error class with code frames | All | | matter.MatterforgeValidationError | Validation error class with issues array | All |

Runtime Compatibility

| API | Node.js | Browser | Edge Runtime | |---|:---:|:---:|:---:| | matter() | ✅ | ✅ | ✅ | | matter.stringify() | ✅ | ✅ | ✅ | | matter.test() | ✅ | ✅ | ✅ | | matter.language() | ✅ | ✅ | ✅ | | matter.clearCache() | ✅ | ✅ | ✅ | | matter.read() | ✅ | ❌ | ❌ | | matter.readAsync() | ✅ | ❌ | ❌ |

Note: read() and readAsync() require the fs module and are only available in Node.js.

Usage

CommonJS (Node.js)

const matter = require('@ambicuity/matterforge');

ESM / ES Modules

import matter from '@ambicuity/matterforge';

// Named type + error class imports:
import { MatterforgeError, MatterforgeValidationError } from '@ambicuity/matterforge';
import type { MatterforgeFile, MatterforgeOptions, MatterEngine } from '@ambicuity/matterforge';

// All runtime methods are available on the default export:
matter.read('./file.md');
matter.stringify('content', { title: 'Hello' });
matter.test('---\ntitle: X\n---');

TypeScript

import matter from '@ambicuity/matterforge';
import type { MatterFile, MatterOptions, MatterEngine } from '@ambicuity/matterforge';

// Generic data typing:
interface FrontMatter {
  title: string;
  slug: string;
  tags?: string[];
}

const file = matter<FrontMatter>('---\ntitle: Hello\nslug: home\n---\nContent');
file.data.title; // string — fully typed!

Framework Examples

Next.js Blog Post

import matter from '@ambicuity/matterforge';
import fs from 'fs';

interface PostMeta {
  title: string;
  date: string;
  tags?: string[];
}

export async function getPost(slug: string) {
  const raw = await fs.promises.readFile(`./posts/${slug}.md`, 'utf8');
  const { data, content } = matter<PostMeta>(raw);
  return { metadata: data, body: content };
}

Astro Content Collection

import matter from '@ambicuity/matterforge';

export function parsePost(raw: string) {
  const { data, content } = matter<{
    title: string;
    date: string;
    tags?: string[];
  }>(raw);

  return { metadata: data, body: content };
}

Cloudflare Worker (Edge Runtime)

import matter from '@ambicuity/matterforge';

export default {
  async fetch(request) {
    const markdown = await getMarkdownFromKV(request);
    const { data, content } = matter(markdown);

    return new Response(JSON.stringify({ data, content }), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};

Schema Validation (Zod, Valibot, etc.)

matterforge includes a universal schema validation adapter protocol. You can pass any object with a .parse() or .safeParse() method (like a Zod schema) directly to options.schema.

import { z } from 'zod';
import matter from '@ambicuity/matterforge';

const PostSchema = z.object({
  title: z.string(),
  slug: z.string(),
  published: z.boolean().default(false),
  date: z.string().datetime()
});

const file = matter(raw, {
  schema: PostSchema,
  onValidationError: 'throw' // or 'return' to attach to file.errors
});

// file.data is fully validated and typed according to PostSchema!

CLI

matterforge ships with a built-in CLI to validate markdown content across your repository.

# Parse all markdown files, run validation (if a config exists), and report errors with code frames
npx matterforge check "content/**/*.md"

# Show aggregate statistics (languages used, common fields, error counts)
npx matterforge stats "content/**/*.md"

Create a matterforge.config.cjs to enable CLI schema validation:

// matterforge.config.cjs — CJS because matterforge v2 declares "type": "module"
const { z } = require('zod');

module.exports = {
  schema: z.object({
    title: z.string(),
    date: z.string().datetime()
  }),
  ignore: ['**/drafts/**'] // Optional ignore patterns
};

If your project does not use "type": "module", you can keep the .js extension.

Returned Object

matterforge returns a file object with the following properties.

Enumerable

  • file.data {Object}: the object created by parsing front-matter
  • file.content {String}: the input string, with matter stripped
  • file.excerpt {String}: an excerpt, if defined on the options
  • file.empty {String}: when the front-matter is "empty" (either all whitespace, nothing at all, or just comments and no data), the original string is set on this property.
  • file.isEmpty {Boolean}: true if front-matter is empty.
  • file.errors {Array}: an array of validation errors, if onValidationError: 'return' is used.

Non-enumerable

  • file.orig {Buffer|String}: the original input string (or buffer). String in Edge/browser runtimes.
  • file.language {String}: the front-matter language that was parsed. yaml is the default.
  • file.matter {String}: the raw, un-parsed front-matter string.
  • file.stringify {Function}: stringify the file by converting file.data to a string in the given language, wrapping it in delimiters and prepending it to file.content.

API

matter

Takes a string or object with content property, extracts and parses front-matter from the string, then returns an object with data, content and other useful properties.

Params

  • input {Object|String}: String, or object with content string
  • options {Object}
  • returns {Object}

Example

import matter from '@ambicuity/matterforge';
console.log(matter('---\ntitle: Home\n---\nOther stuff'));
//=> { data: { title: 'Home'}, content: 'Other stuff' }

.stringify

Stringify an object to YAML or the specified language, and append it to the given string. By default, only YAML and JSON can be stringified. See the engines section to learn how to stringify other languages.

Params

  • file {String|Object}: The content string to append to stringified front-matter, or a file object with file.content string.
  • data {Object}: Front matter to stringify.
  • options {Object}: Options to pass to matterforge and the underlying YAML engine (eemeli/yaml).
  • returns {String}: Returns a string created by wrapping stringified yaml with delimiters, and appending that to the given string.

Example

console.log(matter.stringify('foo bar baz', {title: 'Home'}, { sortKeys: true }));
// results in:
// ---
// title: Home
// ---
// foo bar baz

Deterministic Stringify Options

When stringifying YAML, you can pass options to enforce deterministic output:

  • sortKeys {Boolean|Function}: Sort object keys alphabetically. Can also be a custom compare function.
  • lineWidth {Number}: Max line width before wrapping (default: 80).
  • quotingType {String}: "'" or '"'.
  • forceQuotes {Boolean}: Force all strings to be quoted.
  • noRefs {Boolean}: Do not emit YAML aliases/anchors.

.read

Synchronously read a file from the file system and parse front matter. Returns the same object as the main function. Node.js only.

Params

  • filepath {String}: file path of the file to read.
  • options {Object}: Options to pass to matterforge.
  • returns {Object}: Returns an object with data and content

Example

const file = matter.read('./content/blog-post.md');

.readAsync

Asynchronously read a file from the file system and parse front matter. Returns a Promise. Node.js only.

Params

  • filepath {String}: file path of the file to read.
  • options {Object}: Options to pass to matterforge.
  • returns {Promise<Object>}: Returns a Promise resolving to an object with data and content

Example

const file = await matter.readAsync('./content/blog-post.md');

.test

Returns true if the given string has front matter.

Params

  • string {String}
  • options {Object}
  • returns {Boolean}: True if front matter exists.

.clearCache

Clears the internal parse cache. Useful when the same input string may have been modified or when you need to free memory.

Example

matter.clearCache();

.YAMLException (deprecated)

Alias for YAMLParseError from the yaml package, exposed for backward compatibility with v1.x. Will be removed in v3.0.0; new code should import YAMLParseError directly.

Example

try {
  matter('---\ninvalid: yaml: [\n---\ncontent');
} catch (err) {
  if (err instanceof matter.YAMLException) {
    console.error('YAML parse error:', err.message);
  }
}

Or, for new code:

import { YAMLParseError } from 'yaml';

try {
  matter('---\ninvalid: yaml: [\n---\ncontent');
} catch (err) {
  if (err instanceof YAMLParseError) {
    console.error('YAML parse error:', err.message);
  }
}

Options

options.excerpt

Type: Boolean|Function

Default: undefined

Extract an excerpt that directly follows front-matter, or is the first thing in the string if no front-matter exists.

If set to excerpt: true, it will look for the frontmatter delimiter, --- by default and grab everything leading up to it.

Example

const str = '---\nfoo: bar\n---\nThis is an excerpt.\n---\nThis is content';
const file = matter(str, { excerpt: true });

Results in:

{
  content: 'This is an excerpt.\n---\nThis is content',
  data: { foo: 'bar' },
  excerpt: 'This is an excerpt.\n'
}

You can also set excerpt to a function. This function uses the file and options that were initially passed to matterforge as parameters. The function can either mutate file.excerpt directly, or return a string:

Example (mutation pattern)

const file = matter(str, {
  excerpt: function(file, options) {
    file.excerpt = file.content.split('\n').slice(0, 4).join(' ');
  }
});

Example (return pattern)

const file = matter(str, {
  excerpt: function(file, options) {
    return file.content.split('\n').slice(0, 4).join(' ');
  }
});

options.excerpt_separator

Type: String

Default: undefined

Define a custom separator to use for excerpts.

console.log(matter(string, { excerpt_separator: '<!-- end -->' }));

Example

The following HTML string:

---
title: Blog
---
My awesome blog.
<!-- end -->
<h1>Hello world</h1>

Results in:

{
  data: { title: 'Blog'},
  excerpt: 'My awesome blog.',
  content: 'My awesome blog.\n<!-- end -->\n<h1>Hello world</h1>'
}

options.engines

Define custom engines for parsing and/or stringifying front-matter.

Type: Object Object of engines

Default: JSON, YAML and JavaScript are built-in. TOML and CoffeeScript can be added via custom engines (see examples below).

Engine format

Engines may either be an object with parse and (optionally) stringify methods, or a function that will be used for parsing only.

Examples

const toml = require('toml');

// As a function:
const file = matter(str, {
  engines: {
    toml: toml.parse.bind(toml),
  }
});

// As an object with parse and stringify:
const file = matter(str, {
  engines: {
    toml: {
      parse: toml.parse.bind(toml),
      stringify: function() {
        throw new Error('cannot stringify to TOML');
      }
    }
  }
});

options.language

Type: String

Default: yaml

Define the engine to use for parsing front-matter.

console.log(matter(string, { language: 'toml' }));

Dynamic language detection

Instead of defining the language on the options, matterforge will automatically detect the language defined after the first delimiter and select the correct engine to use for parsing.

---toml
title = "TOML"
description = "Front matter"
categories = "front matter toml"
---
This is content

options.delimiters

Type: String | [String, String]

Default: ---

Open and close delimiters can be passed as a string (same for open/close) or an array of two strings.

Example:

// Same delimiter for open and close:
matter.read('file.md', { delimiters: '~~~' });

// Different open/close delimiters:
matter.read('file.md', { delimiters: ['<<<', '>>>'] });

Would parse:

~~~
title: Home
~~~
This is the {{title}} page.

Custom Engines

matterforge ships with built-in engines for YAML, JSON, and JavaScript (eval-gated). Coffee, CSON, and TOML support are intentionally not bundled — register them as user-supplied engines so consumers who don't need them don't pay the dependency cost.

TOML

import matter from '@ambicuity/matterforge';
import toml from 'toml';
import * as tomlify from 'tomlify-j0.4';

matter.engines.toml = {
  parse: toml.parse.bind(toml),
  stringify: (data) => tomlify.toToml(data, { space: 2 })
};

const file = matter('+++\ntitle = "Hello"\n+++\ncontent', { delimiters: '+++' });

Custom format

Any object with a parse(str, options?) => object and optional stringify(data, options?) => string works:

matter.engines.myformat = {
  parse: (str) => ({ /* your parsed result */ }),
  stringify: (data) => '/* your serialized result */'
};

Error Handling

Parsing and validation errors are thrown by default. matterforge enriches these errors with diagnostic code frames pointing to the exact line and column where the error occurred.

try {
  matter('---\ninvalid: yaml: [\n---\ncontent');
} catch (err) {
  console.error(err.message);
  // Invalid front matter: missed comma between flow collection entries
  //
  //   1 | ---
  // > 2 | invalid: yaml: [
  //                    ^
  //   3 | ---
  //   4 | content
}

matterforge preserves the original error's prototype chain, meaning err instanceof matter.YAMLException will still work correctly for YAML syntax errors.

Error scenarios:

| Scenario | Error Class | |---|---| | Invalid YAML syntax | YAMLException (with code frames) | | Invalid JSON syntax | SyntaxError (with code frames) | | Schema validation failure | MatterforgeValidationError (with .issues array) | | Unknown engine | MatterforgeError | | Engine missing parse method | TypeError | | Non-string, non-object input | TypeError |

Deprecated Options

These continue to work but will be removed in a future major version:

| Deprecated | Use Instead | |---|---| | options.delims | options.delimiters | | options.lang | options.language | | options.parsers | options.engines |

TypeScript Types

matterforge exports convenience type aliases for framework and library authors:

import type {
  MatterFile,               // Alias for MatterforgeFile<string, D>
  MatterOptions,            // Alias for MatterforgeOption<string, any>
  MatterEngine,             // Engine function or { parse, stringify? } object
  MatterStringifyOptions,   // Stringify-specific options
  MatterforgeError,         // Enriched error class with code frames
  MatterforgeValidationError, // Validation error class with issues array
  SchemaAdapter             // Duck-typed schema adapter protocol
} from '@ambicuity/matterforge';

Security

matterforge parses YAML with eemeli/yaml — the modern YAML 1.2 reference implementation. Defaults are safe:

  • No prototype pollution__proto__, constructor, and prototype keys are stripped from parsed objects.
  • No code execution from YAML — unknown tags (!!js/function, custom tags) degrade to plain strings instead of executing.
  • Safe integer handling — values above Number.MAX_SAFE_INTEGER are surfaced as BigInt or strings rather than silently losing precision.

JavaScript front matter uses eval() and is disabled by default. You must opt in with matter(input, { eval: true }). Only enable for trusted content.

matterforge parses front matter safely, but does not sanitize HTML, validate metadata schemas without one, or prevent unsafe rendering in your application. Use a sanitizer (e.g. DOMPurify) when rendering untrusted HTML, and a schema validator (e.g. Zod, Valibot, ArkType) for type-safe data — pass any .parse()-shaped object as options.schema.

To report a security issue, see SECURITY.md.

Quality bar

matterforge is verified by a 5-layer test strategy (see docs/testing.md for the full breakdown):

| Layer | Coverage | Run with | | --- | --- | --- | | Unit + user-perspective (~210 tests) | Fixtures, real-world corpus, CLI E2E, dual-export, schema, large-input | npm test | | Property-based (8 invariants × ~300 cases) | Parse/stringify roundtrip, delimiters, BOM/CRLF, cache, options stability | npm run test:property | | Multi-runtime | Headless Chromium (@vitest/browser) + edge VM (@edge-runtime/vm) | npm run test:browser · npm run test:edge | | Fuzz | 100k random inputs against the parser, with crash-corpus replay | npm run test:fuzz | | Performance + smoke | Regression vs v2.0.0 baseline; npm pack + install + consumer smoke | npm run bench:regression · npm run test:published |

Covered edge cases include: front matter not at the beginning of a file; empty and comment-only front matter; custom open/close delimiters; nested --- inside content and YAML values; code blocks containing ---; BOM at the start of files; CRLF and mixed line endings; YAML/JSON/TOML front matter; invalid input error paths; large files (10 MB+); deeply nested YAML; special YAML types (null, boolean, arrays, nested objects, multiline strings); browser environments without Buffer; Vinyl file objects; cache correctness and bulk eviction.

Project

Author

Ritesh Rana[email protected]

License

Copyright © 2026, Ritesh Rana. Released under the MIT License.