Node.js - 如何读写Markdown文件并更改其前置元数据?

9

我有几百个Markdown文件,我想循环遍历它们并向它们的Front Matter元数据中添加新数据。

例如,这是我的文件当前的外观:

---
title: My title here
---


Markdown content here

我想给它们添加一个id属性:

---
title: My title here
id: 1982n1x23981u1
---


Markdown content

在Node中,最干净的方式是什么?我找到了一些处理Markdown的包,但它们都旨在将其转换为JSON。

提前感谢!


你正在谈论Markdown/CommonMark...但你向我们展示了YAML并谈论将其转换为JSON。 - Nicholas Carey
嗨,尼古拉斯!感谢您的留言。我不确定我理解您的意思,但我已经编辑了我的问题以便更加清晰。在使用JAMStack时,在Markdown文件中使用YAML前置数据是很常见的。 - Jonathan
3个回答

13

这是我用来更新Markdown脚本中所有笔记的元数据的基本脚本:

// @ts-check
const { readdir, readFile, writeFile } = require("fs/promises");
const matter = require("gray-matter");
const { stringify } = require("yaml");

const directory = "<YOUR-DIRECTORY>";

async function updateFrontMatter(filename) {
  const filepath = `${directory}/${filename}`;

  const { data: frontMatter, content } = matter(await readFile(filepath));

  // remove desc attribute
  if (frontMatter.desc === "") {
    delete frontMatter["desc"];
  }

  // parse created date attribute and convert it as timestamp
  if (typeof frontMatter.created === "string") {
    frontMatter.created = new Date(frontMatter.created).getTime();
  }

  const newContent = `---\n${stringify(frontMatter)}---\n${content}`;

  await writeFile(filepath, newContent);

  console.log(`- [x] ${filepath}`);
}

async function main() {
  const filenames = await readdir(directory);
  const markdownFilenames = filenames.filter((f) => f.endsWith(".md"));

  await Promise.all(markdownFilenames.map(updateFrontMatter));
}

main().catch(console.error);

1
假设您实际上是在谈论 YAML 而不是 Markdown/CommonMark,其中 YAML 是数据标记语言,类似于 XML 或 JSON;Markdown/CommonMark 则是用于文本格式化的标记语言... 您需要使用以下任意一个 NPM 包之一:
  • js-yaml @ 1500万次每周下载量
  • YAML @ 490万次每周下载量
如果您实际上拥有 Markdown/CommonMark,则可以使用像 NPM 的 commonmark 这样的工具来解析文档并将其加载到可以操作和重写为 Markdown 的 抽象语法树 (AST) 中。
请注意,NPM 中还有许多其他的 Markdown 处理器,包括这个。它们是否与您使用的 Markdown 兼容... 您需要自己弄清楚。CommonMark 的存在是因为许多 Markdown 实现已经分歧。
另一个选择是使用 pandoc 并编写一个过滤器来处理它生成的 AST。

0

我遇到了类似的挑战,并想出了一个通用的解决方案:

import matter from "gray-matter";

function processFrontmatter(md, options) {
  const frontmatter = matter(md).data;
  Object.entries(options).forEach(function ([fieldName, value]) {
    if (value === "del" || value === "-") {
      delete frontmatter[fieldName];
    } else {
      frontmatter[fieldName] = value;
    }
  });

  return matter.stringify(matter(md));
}

此函数接受Markdown内容(以字符串格式),解析frontmatter并根据options字典参数进行更改:

// ---
// field: example value
// another_field: some value
// ---
// Markdown content
const md = '---\nfield: example value\nanother_field: some value\n---\nMarkdown content'

const options = {
    field: 'new example value',
    another_field: 'del',
    new_field: 'a new field!'
}

console.log(processFrontmatter(md, options))

这将产生:

---
field: new example value
new_field: a new field!
---
Markdown content

我将这个解决方案发布为一个节点模块,可以在这里查看。

如果要从文件中读取Markdown内容并写入它,我会参考fs模块。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接