gray-matter

解析 Markdown 文件顶部 Front Matter 元数据的 Node.js 库,把结构化元数据和正文分离,是内容工程管线的基础层。

#type / concept #status / evergreen #tech / javascript #package / gray-matter

[!info] related notes

gray-matter

一句话定义

gray-matter 是一个 Node.js 库,专门从 Markdown / 文本文件中解析顶部的 YAML(或 JSON / TOML)Front Matter,把”给机器用的结构化元数据”和”给人读的正文”拆开。

Front Matter 是什么

Front Matter 是 Markdown 文件最顶部的一段结构化配置,用 --- 分隔符包起来:

---
title: SSE 数据传输与 Markdown 渲染
slug: sse-markdown-rendering
tags:
  - ai-agent
  - frontend
status: draft
created: "2026-06-28"
---

# 正文内容

这里是给人读的 Markdown 正文……

可以把它理解为两个区域:

┌──────────────────────────────┐
│ Front Matter 元数据           │  给程序读:标题、日期、标签、状态、封面、slug
├──────────────────────────────┤
│ Markdown 正文                 │  给人读:文章内容、笔记内容、教程内容
└──────────────────────────────┘

Front Matter 常见于:Astro、VitePress、Docusaurus、Gatsby、Next.js MDX、Obsidian 知识库、静态博客、文档站、内容管理系统。

它在解决什么

没有 gray-matter 时,你可能会自己写正则:

const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)

但实际项目里会遇到很多边界情况:

  • 正文里有代码块,代码块里包含 --- 分隔符
  • 正文里有 YAML 示例,看起来像 front matter
  • 空 front matter(---\n---
  • 非标准分隔符(~~~ 而不是 ---
  • JSON / TOML 格式的 front matter

gray-matter 的核心价值就是:正确处理这些边界情况,不需要你自己维护一套脆弱的正则。它不依赖正则做解析,能区分正文里的 --- 和真正的 front matter 边界。

核心心智模型

gray-matter 做且只做一件事:

输入:一段完整的 Markdown 字符串

gray-matter

输出:{ data: {...}, content: '...' }
  • data — 解析后的 front matter 对象(JS 对象)
  • content — 去掉 front matter 之后的正文(字符串)

不负责

  • 校验 front matter 结构是否正确(交给 Zod / Valibot)
  • 渲染 Markdown 正文(交给 remark / rehype / MDX)
  • 建立搜索索引(交给 lunr / flexsearch)
  • 生成 slug(交给 slugify)

负责

  • 识别 front matter 的起止边界
  • 解析 YAML / JSON / TOML 为 JS 对象
  • 提取纯正文部分
  • 处理各种边界情况(空 front matter、代码块里的假分隔符等)

返回对象详解

matter(raw) 返回的不是单纯的 { data, content },而是一个 file object,包含多个字段:

枚举字段(enumerable)

const file = matter(raw)

file.data       // 解析后的 front matter 对象
file.content    // 去掉 front matter 之后的正文
file.excerpt    // 摘要,只有配置 excerpt 选项时才有
file.empty      // front matter 为空时的原始内容
file.isEmpty    // front matter 是否为空(boolean)

调试字段(non-enumerable)

file.orig       // 原始输入字符串
file.language   // 检测到的 front matter 语言(yaml / json / toml)
file.matter     // 原始 front matter 字符串(未经解析)
file.stringify  // 序列化函数,可自定义输出格式

其中 file.matterfile.data 的区别很重要:

  • file.matter 是原始 YAML 字符串:"title: AI Agent\ntags:\n - langgraph"
  • file.data 是解析后的 JS 对象:{ title: 'AI Agent', tags: ['langgraph'] }

支持的格式

gray-matter 默认解析 YAML,也支持其他格式:

YAML(默认)

---
title: Hello
tags:
  - ai
  - frontend
draft: false
---

JSON

---json
{
  "title": "Hello",
  "tags": ["ai", "frontend"],
  "draft": false
}
---

TOML

---toml
title = "Hello"
draft = false
---

自定义分隔符

const file = matter(raw, {
  delimiters: '~~~',
})

也支持 open / close 分隔符数组:

const file = matter(raw, {
  delimiters: ['~~~', '~~~'],
})

语言自动检测

分隔符后面可以加语言标识来指定解析器:

---json
{ "title": "Hello" }
---
---toml
title = "Hello"
---

不加标识时默认走 YAML 解析器。

工程建议:实际项目里统一用 YAML。原因是 Markdown 生态最通用,Astro / VitePress / Obsidian 都更熟悉 YAML front matter,人工维护成本最低。

边界与常见误解

它不是 Markdown 渲染器

错误理解:“gray-matter 可以把 Markdown 渲染成 HTML”

不对。它只做 Markdown 原文 → frontmatter data + content。Markdown 渲染要交给 remark / rehype / markdown-it / MDX / Astro content pipeline。

它不是 schema 校验器

file.data 是 YAML 解析器的原始输出,不做结构校验。tags: frontend(字符串)和 tags: [frontend](数组)都会被原样返回。校验要交给 Zod / Valibot。

它不是文件读取器

matter() 接收的是字符串,不负责读文件。matter.read(filepath) 存在但是同步的,现代项目建议自己用 fs/promises 读文件再交给 matter()

常见坑

YAML 类型自动转换

YAML 解析器会自动转换某些值的类型:

draft: false       # → boolean
count: 123         # → number
date: 2026-06-28   # → 可能变成 Date 或 string,取决于解析器行为

工程建议:

  • 日期统一写字符串:created: "2026-06-28"
  • 状态统一用 enum:status: draft
  • 标签统一用数组:tags: [ai, frontend]
  • 不要在 front matter 里塞复杂业务对象

正文开头空行

解析后 content 可能包含开头换行:

const file = matter(`---
title: Test
---

# Hello`)
console.log(JSON.stringify(file.content))
// "\n# Hello"

如果要做摘要、首段提取、embedding,最好 content.trimStart()。但如果要保持原文格式,就不要随便 trim。

stringify 会改变 YAML 格式

matter.stringify() 很方便,但它可能会重排 YAML 格式、改变引号、改变数组风格:

# 原来
tags: [ai, frontend]

# stringify 后可能变成
tags:
  - ai
  - frontend

批量 fix 前建议:先 git diff,小批量处理,保留人工写作习惯,只对规范化字段做自动修复。

空 front matter

当文件没有 front matter 时:

const file = matter('# Just content\nNo front matter here.')
file.isEmpty  // true
file.data     // {}
file.content  // '# Just content\nNo front matter here.'

当 front matter 为空时:

const file = matter('---\n---\nContent')
file.isEmpty  // true
file.data     // {}

两种情况都需要在后续处理中考虑。

创建于 2026/6/28 更新于 2026/7/15