I have a few hundred markdown files and I would like to loop through them and add a new data to their front matter metadata.
For example, this is what my files look like now:
---
title: My title here
---
Markdown content here
And I want to add an id property to them:
---
title: My title here
id: 1982n1x23981u1
---
Markdown content
What would be the cleanest way to do that in Node? I've found a few packages to handle markdown, but all of them aims to convert it to JSON instead.
Thanks in advance!
I have a few hundred markdown files and I would like to loop through them and add a new data to their front matter metadata.
For example, this is what my files look like now:
---
title: My title here
---
Markdown content here
And I want to add an id property to them:
---
title: My title here
id: 1982n1x23981u1
---
Markdown content
What would be the cleanest way to do that in Node? I've found a few packages to handle markdown, but all of them aims to convert it to JSON instead.
Thanks in advance!
Share Improve this question edited Jun 26, 2020 at 0:34 Jonathan asked Jun 25, 2020 at 23:55 JonathanJonathan 1632 silver badges15 bronze badges 2- You're talking about Markdown/CommonMark... but you're showing us YAML and talking about conversion to JSON. – Nicholas Carey Commented Jun 25, 2020 at 23:59
- Hi Nicholas! Thanks for your message. I'm not sure I understand what you mean, but I've edited my question for clarity. It's mon to have YAML front matter data in markdown files when working with the JAMStack – Jonathan Commented Jun 26, 2020 at 0:06
3 Answers
Reset to default 13Here a basic script I used to update frontmatter of all my notes in Markdown script:
// @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);
Assuming you are actually talking about YAML and not Markdown/CommonMark, YAML being data markup like XML or JSON; Markdown/CommonMark being markup for text formatting...
You'll be wanting to user either of these NPM packages:
- js-yaml @ 15.6 million downloads/week
- YAML @ 4.9 million downloads/week
If you actually have Markdown/CommonMark, you can use a tool like NPM's monmark which parses the document and loads it into an abstract syntax tree (AST) that you can manipulate and rewrite as Markdown.
Note that there are [many] other Markdown processors in NPM, including this one. Where they are patible with the Markdown you are using... you'll have to figure that out yourself. CommonMark exists because of how the many Markdown implementations have diverged.
Another option would be to use pandoc and write a filter to process the AST that it produces.
I run into a similar challenge and came up with a generalised solution:
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));
}
This function accepts markdown content (in string format), parses the frontmatter and changes it according to the options
dictionary parameter:
// ---
// 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))
Which will yield:
---
field: new example value
new_field: a new field!
---
Markdown content
I published this solution as a node module, see here.
For reading the Markdown content from a file and writing it, I'd refer to the fs module.