Nuxt.js で Markdown ベースのブログを構築する(Markdown 編)

この記事は「Nuxt.js で Markdown ベースのブログを構築する」シリーズの一部です。


本ブログは、

  • Markdown で記事を書き、
  • Nuxt.js の静的ファイル生成機能を使って静的ファイルを書き出し、
  • Netlify を使ってホスティングする

というシステム構成になっている。具体的にどのようにしているか簡単に紹介する。

processmd を使った Markdown ファイルの変換処理

それぞれのブログ記事は YYYY-MM-DD-xxxxx.md という Markdown ファイルに書いている。ファイルの先頭に YAML front matter でメタ情報を記述するという、ブログ記事によく見られるフォーマットになっている。

---
title: 記事のタイトル
created_at: 2018-01-01
---

## 見出し 2

本文です。

```javascript
const f = () => {};
```

この Markdown ファイルは、Nuxt.js の中で変換処理を行うと複雑になるので、あらかじめ Nuxt.js(というか JS)で扱いやすいように、JSON ファイルに変換している。

この処理に使っているのが processmd という CLI ツールで、これがとても便利だった。

例えば、上にあげたような Markdown ファイルを processmd コマンドに渡すと

{
  "title": "記事のタイトル",
  "created_at": "2018-01-01T00:00:00.000Z",
  "bodyContent": "## 見出し 2\n\n本文です。\n\n```javascript\nconst f = () => {};\n```",
  "bodyHtml":
    "<h2>見出し 2</h2>\n<p>本文です。</p>\n<pre><code class=\"hljs\"><span class=\"hljs-keyword\">const</span> f = <span class=\"hljs-function\"><span class=\"hljs-params\">()</span> =&gt;</span> {};</code></pre>",
  "dir": "content/posts",
  "base": "2018-01-01-test.json",
  "ext": ".json",
  "sourceBase": "2018-01-01-test.md",
  "sourceExt": ".md"
}

といった JSON ファイルに変換してくれる。Front matter で書いたメタ情報はそれぞれフィールドとなり、bodyContent には元の Markdown が、 bodyHTML にはそれを markdown-it で HTML に変換した値が含まれる。

よく見てもらえばわかるように、コードブロックは highlight.js が適用された状態の HTML で書き出されるので、あとは highlight.js 用のテーマ CSS を読み込むだけで、簡単にシンタックスハイライトが導入できる。

また、--stdout というオプションをつけると、同時にサマリーファイルも書き出すことができる。

$ processmd content/posts/**/*.md --stdout --outputDir content/posts/json > summary.json

summary.json はこんな感じ。

{
  "fileMap": {
    "content/posts/json/2007-09-25-link-visited-hover-active.json": {
      "title": ":link、:visited、:hover、:active の記述順序とその覚え方",
      "created_at": "2007-09-27T00:00:00.000Z",
      "dir": "content/posts/json",
      "base": "2007-09-25-link-visited-hover-active.json",
      "ext": ".json",
      "sourceBase": "2007-09-25-link-visited-hover-active.md",
      "sourceExt": ".md"
    },
    "content/posts/json/2012-12-05-sass-if-function.json": {
      "title": "Sass の if 関数",
      "created_at": "2012-12-05T00:00:00.000Z",
      "dir": "content/posts/json",
      "base": "2012-12-05-sass-if-function.json",
      "ext": ".json",
      "sourceBase": "2012-12-05-sass-if-function.md",
      "sourceExt": ".md"
    },
    "content/posts/json/2011-03-21-innerHTML-returns-serialized-html.json": {
      "title": "innerHTML や jQuery.html() は HTMLをそのまま取得できるわけではない",
      "created_at": "2011-03-21T00:00:00.000Z",
      "dir": "content/posts/json",
      "base": "2011-03-21-innerHTML-returns-serialized-html.json",
      "ext": ".json",
      "sourceBase": "2011-03-21-innerHTML-returns-serialized-html.md",
      "sourceExt": ".md"
    }
  },
  "sourceFileArray": [
    "content/posts/2007-09-25-link-visited-hover-active.md",
    "content/posts/2011-03-21-innerHTML-returns-serialized-html.md",
    "content/posts/2012-12-05-sass-if-function.md"
  ]
}

Markdown ファイル名に日付(YYYY-MM-DD)を使っておくと sourceFileArray は日付順に並ぶので、記事一覧リストの実装にそのまま利用することができた。

また、本ブログでは使っていないが --preview オプションを使うと、指定した文字分だけコンテンツの先頭をテキストで書き出してくれる。「記事一覧リストに本文の概要を載せたい」といった場合に便利だと思う。

{
  "title": "記事のタイトル",
  "created_at": "2018-01-01T00:00:00.000Z",
  "bodyContent": "## 見出し 2\n\n新しいブログ記事です。\n\n```javascript\nconst f = () => {};\n```",
  "bodyHtml":
    "<h2>見出し 2</h2>\n<p>新しいブログ記事です。</p>\n<pre><code class=\"hljs\"><span class=\"hljs-keyword\">const</span> f = <span class=\"hljs-function\"><span class=\"hljs-params\">()</span> =&gt;</span> {};</code></pre>",
  "preview": "見出し 2\n\n新しいブログ記事です。\n\nconst f = () = {};\n`"
}

Nuxt.js 編 に続く。