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

@bhouston/markdown-content

v1.0.4

Published

Markdown parsing and schemas for structured marketing and blog content

Downloads

681

Readme

@bhouston/markdown-content

Pure TypeScript markdown parsing with YAML front matter and Zod schema validation. Zero Node.js dependencies — runs in any JavaScript runtime.

Install

npm install @bhouston/markdown-content

Quick Start

Use createMarkdownParser to build a typed parser from Zod schemas, then call parseMarkdownDocument on raw markdown strings.

Marketing pages

import { createMarkdownParser, builtInPageParserSchemas } from '@bhouston/markdown-content';

const parser = createMarkdownParser(builtInPageParserSchemas);
const page = parser.parseMarkdownDocument(rawMarkdown);
// page.title, page.description, page.keywords[], page.sections[]

Blog posts

import { createMarkdownParser, builtInBlogParserSchemas } from '@bhouston/markdown-content';

const parser = createMarkdownParser(builtInBlogParserSchemas);
const post = parser.parseMarkdownDocument(rawMarkdown);
// post.title, post.date, post.author, post.tags[], post.sections[]

Custom parser

import { createMarkdownParser } from '@bhouston/markdown-content';
import * as z from 'zod';

const parser = createMarkdownParser({
  pageFrontMatterSchema: z.object({ title: z.string(), description: z.string(), keywords: z.array(z.string()) }),
  sectionFrontMatterSchema: z.object({ layout: z.string() }),
  behavior: {
    partSeparator: '|||', // default
    implicitSectionFrontMatter: { layout: 'single' }, // default
  },
});

Document Format

Every file must open with a YAML front matter block. Additional --- blocks after the first define explicit sections. If no explicit section front matter follows, the remainder of the document becomes one implicit single section.

---
title: My Page
description: A page about things.
keywords: foo, bar
---

## Section One (implicit single section)

Regular markdown body.

With explicit sections:

---
title: My Page
description: A page about things.
keywords: foo, bar
---

---

layout: intro
title: Welcome
subTitle: A great introduction

---

Hero text here.

---

layout: single
title: Details

---

Body content.

Use ||| to split a section body into multiple parts (e.g. columns, grid cards, steps). ||| inside fenced code blocks is ignored.

---
layout: two-column
---

Left column content.

|||

Right column content.

Section Layouts

All sections share common optional fields: title, subTitle, description, bgColor (default | muted | dark). Strict validation rejects unknown keys.

intro

For hero or prominent opening sections. Supports only layout, title, subTitle, and bgColor — does not accept description.

Required: title, subTitle

---
layout: intro
title: The Programmable Asset Backend
subTitle: Stop bloating Git.
bgColor: default
---

<cta-button text="Get Started →" href="/signup"></cta-button>

single

A section with one markdown body.

---
layout: single
title: What Is This?
bgColor: muted
---

Body content here.

two-column

Split the body with one ||| into exactly 2 columns.

Optional fields: columnGap (positive number), verticalAlign (top | center, default top), horizontalAlign (left | center, default left)

---
layout: two-column
title: Load It Your Way
verticalAlign: center
---

### Direct URL

Load assets directly from the media endpoint.

|||

### SDK

Use the typed SDK for richer integrations.

three-column

Split the body with two ||| separators into exactly 3 columns.

Optional fields: columnGap (positive number), verticalAlign (top | center, default top), horizontalAlign (left | center, default left)

---
layout: three-column
title: Three Ways to Integrate
---

### Option A

First column.

|||

### Option B

Second column.

|||

### Option C

Third column.

feature-grid

Split the body with ||| into 2 or more markdown cards.

Optional fields: columns (2 | 3 | 4, default 3)

---
layout: feature-grid
title: Built for Developer Workflows
columns: 4
---

### Open Asset Library

Discover public assets.

|||

### Not Just Storage

Upload, preview, and convert.

steps

Split the body with ||| into 2 or more ordered steps.

Optional fields: variant (default | compact, default default)

---
layout: steps
title: Get Started
variant: default
---

### Create an Account

Sign up for free.

|||

### Upload Assets

Use the dashboard or CLI.

Built-in Page Front Matter

Required fields: title, description

Optional fields: keywords (comma-separated string or array, defaults to [])

---
title: My Page
description: A short description.
keywords: foo, bar, baz
---

Built-in Blog Front Matter

Required fields: title, description, date, author, and at least one tags entry.

Optional fields: shortTitle, subTitle, updatedDate, excerpt, imageUrl, audioUrl, keywords, tags, hide, canonical

Notes:

  • tags should be authored as a YAML array. Comma-separated strings are still accepted for compatibility; values are normalized to lowercase with spaces replaced by -.
  • keywords should be authored as a YAML array. Comma-separated strings are still accepted for compatibility.
  • hide accepts boolean or boolean-string ("true" / "false"), defaults to false.
  • Dates are coerced via new Date(value).
---
title: My Blog Post
description: What this post is about.
date: 2026-03-01
author: Jane Smith
tags:
  - release-notes
  - infrastructure
imageUrl: /images/hero.png
---

Post body here.

API Reference

createMarkdownParser(options)

Returns a parser object with two methods:

| Method | Description | | ---------------------------- | ----------------------------------------------------------------------------------------------------------------- | | parseMarkdownDocument(raw) | Parses the full document. Requires leading page front matter. Returns page front matter merged with sections[]. | | parseMarkdownSections(raw) | Parses only sections from the body (no page front matter). |

CreateMarkdownParserOptions

| Field | Type | Description | | ------------------------------------- | --------- | ----------------------------------------------------------------------- | | pageFrontMatterSchema | ZodType | Schema for the page-level front matter block. | | sectionFrontMatterSchema | ZodType | Schema for each section front matter block. | | labels.frontMatterLabel | string? | Label used in error messages. | | labels.missingFrontMatterMessage | string? | Message when page front matter is absent. | | behavior.partSeparator | string? | Part separator string (default \|\|\|). | | behavior.implicitSectionFrontMatter | object? | Front matter for the implicit section (default { layout: 'single' }). |

Error Handling

import { MarkdownContentError, zodIssuesToMarkdownContentIssues } from '@bhouston/markdown-content';

try {
  parser.parseMarkdownDocument(raw);
} catch (err) {
  if (err instanceof MarkdownContentError) {
    console.error(err.code); // e.g. 'INVALID_PAGE_FRONT_MATTER'
    console.error(err.issues); // MarkdownContentIssue[]
  }
}

Error codes

| Code | When thrown | | ------------------------------ | --------------------------------------------------------- | | MISSING_PAGE_FRONT_MATTER | File does not start with --- | | INVALID_FRONT_MATTER_BLOCK | YAML parse failure or malformed delimiters | | INVALID_PAGE_FRONT_MATTER | Page front matter fails schema validation | | INVALID_SECTION_FRONT_MATTER | A section front matter block fails schema validation | | INVALID_IMPLICIT_SECTION | The implicit section front matter fails schema validation |

Utility Functions (blog)

| Function | Description | | ----------------------------------------- | ------------------------------------------------------------------------------------------------- | | joinMarkdownSections(sections) | Joins all section markdown parts into a single string. | | getBlogDisplayTitle(page) | Returns shortTitle ?? title. | | getBlogDisplaySubtitle(page) | Returns subtitle for display, accounting for shortTitle / subTitle interplay. | | calculateReadingTimeInMinutes(markdown) | Estimates reading time (default 225 wpm, 5.7 chars/word). | | withPageMetadata(page, metadata) | Shallow-merges PageMetadata into a page object. | | withBlogPostMetadata(page, metadata) | Merges id, directory, and computed readTime into a blog post, returning HydratedBlogPost. |

Exports

| Module | Key exports | | --------------- | ---------------------------------------------------------------------------------------------------------------------- | | parser | createMarkdownParser, CreateMarkdownParserOptions, ParseMarkdownDocumentOptions | | frontmatter | parseFrontMatterBlock, ParsedFrontMatterBlock | | errors | MarkdownContentError, MarkdownContentErrorCode, MarkdownContentIssue, zodIssuesToMarkdownContentIssues | | core/schemas | All section schemas/types, MarkdownImageMetadataSchema, PageMetadataSchema, layout enums | | builtins/page | builtInPageParserSchemas, PageFrontMatterSchema, PageSchema, Page, PageFrontMatter | | builtins/blog | builtInBlogParserSchemas, BlogPostFrontMatterSchema, BlogPostPageSchema, HydratedBlogPost, BlogPost, helpers |

Requirements

  • TypeScript 5.x with moduleResolution: bundler or node16
  • No Node.js APIs; runs in browsers, edge runtimes, and servers

License

MIT