マークダウンのmetadata(yaml header)をNode.jsでパースする

マークダウンのmetadata(yaml header)をNode.jsでパースする

Clock Icon2023.02.27

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 Delivery部の若槻です。

マークダウン(Markdown)文書では、次のようにファイルの冒頭で---を使ってmetadata(yaml header)を定義することができます。

---
title: This is a test
description: Once upon a time...
---
# Title of my great post
Lorem ipsum dolor...

レンダリング側がサポートしているかに依りますが、VS Codeのマークダウンプレビュー機能であれば、ソース側で記述されていたmetadataをプレビュー側ではちゃんと消してくれます。

逆にGitHubではテーブル形式で表示してくれます。

今回は、このマークダウンのmetadataをNode.jsでパースする方法を確認してみました。

方法

npmパッケージのparse-mdが良さそうだったので使ってみます。

インストール

npm i parse-md

コード

import fs from 'fs'
import parseMD from 'parse-md'

const fileContents = fs.readFileSync('posts/first.md', 'utf8');
const { metadata, content } = parseMD(fileContents);

console.log(metadata);
console.log(content);

実行するとmetadataおよびcontentをパースできました。

$ node parse.js
{ title: 'This is a test', description: 'Once upon a time...' }
# Title of my great post
Lorem ipsum dolor...

実行がnode:65885エラーとなる場合

$ node parse.js
(node:65885) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/parse.js:1
import fs from 'fs'
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1033:15)
    at Module._compile (node:internal/modules/cjs/loader:1069:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at node:internal/main/run_main_module:17:47

package.jsonに次の記述を追加して、パッケージ内の全ての.jsファイルがcommonjsではなくESモジュールとして扱われるようにします。

{
  "type": "module"
}

TypeScriptで書くなら

TypeScriptで書く場合は次のようになりました。

import * as fs from 'fs';
import parseMD from './node_modules/parse-md/dist/index.js';

const fileContents = fs.readFileSync('posts/first.md', 'utf8');
const { metadata, content } = parseMD(fileContents);

console.log(metadata);
console.log(content);

ちなみにインポートをimport parseMD from 'parse-md'としたら、parse-mdの定義ファイルが無いよと怒られます。

Could not find a declaration file for module 'parse-md'. '/Users/wakatsuki.ryuta/projects/cm-rwakatsuki/cdk_sample_app/parse-md.js' implicitly has an 'any' type.

JavaScriptの時と同じですが、package.jsonに次の記述を追加して、パッケージ内の全ての.jsファイルがcommonjsではなくESモジュールとして扱われるようにします。

{
  "type": "module"
}

tsconfig.jsonの記述を次のようにします。compilerOptions.moduleesnextまたはes2020を指定してモジュールをESモジュールとして読み込むようにします。また"ts-node": {"esm": true}を追加してERR_UNKNOWN_FILE_EXTENSIONを回避します。(ts-node-esmまたはts-node --esmでも回避できます。)

{
  "compilerOptions": {
    "module": "esnext",
  },
  "ts-node": {
      "esm": true
  }
}

実行できました。

$ npx ts-node parse.ts
{ title: 'This is a test', description: 'Once upon a time...' }
# Title of my great post
Lorem ipsum dolor...

参考

以上

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.