ARTICLES

modern site generator で Markdown 文書の一覧を作成するには

2019-04-04

更に MDX 話の続きです。前回までの Articles はこちら。

とりあえず、MDX は React コンポーネントへ変換できました、Front Matter もコンポーネントに渡す変数として読み込むことが出来ました、ということになったとして、その後もう一つ壁が立ちはだかります。

記事の一覧ページをどうしよう」という問題です。

React ベースの静的サイトジェネレーター、ここからは Gatsby.js のサイトにも掲載されている「modern site generator」という言葉を借りて、従来の静的サイトジェネレーター である Static Site Generator (SSG) に対して MSG という略語で話を進めていきます。MSG は、Markdown ベースのコンテンツの一覧ページとの相性があまりよくありません。React-Static や Gatsby.js 、そして、Next.js と様々な MSG を使ってサイトを構築していますが、いずれも一覧ページについて強力なサポートがない状態です。

一覧ページに関する一つの方向性として、headless CMS を使うという方針があります。headless CMS というのは、これまでの CMS と違って HTML を返すのではなく、JSON を返す仕組みであり、管理画面つきの API サーバーというのが理解しやすい構成と言えるでしょう。 headless CMS であれば、一覧を返す API があるので、それを GET リクエストで取得することで、一覧ページ生成用のデータが手に入ります。

それ以外の方法をとる場合、ローカルの Markdown ファイルを読み込み、それを一覧ページとして生成するもしくは、静的な JSON ファイルとして書き出しておくという方法が考えられます。ただ、この場合も数が増えた場合にどうやってページングするかなど、問題があります。

その点、SSG であれば、カテゴリーやタグなどの機能が用意されており、一覧ページを作成する手間がありません。例えば Hugo では、以下のように、Front Matter を記述すると、categories と tags それぞれに属するページも生成されます。これくらいスムーズに出来るとありがたいですね。詳しくはこちら Taxonomies | Hugo

+++
categories = ["Development", "VIM"]
date = "2012-04-06"
tags = [".vimrc", "plugins", "spf13-vim", "vim"]
title = "spf13-vim 3.0 release and new website"
+++

React-Static における一覧ページ

React Static を例にちょっと解説してみます。今回は MDX ではなく、Markdown をベースに話します。React Static には、react-static-plugin-markdown というプラグインがあります。これを使うと、React Static で Markdown を扱うことができます。

インストールは以下のようにして行い、

$ npm i -D react-static-plugin-markdown

Markdown 用のレイアウトコンポーネントを例えば ContentPage.js という名前で src/containers/ に作成します。

import React from 'react'
import { withRouteData } from 'react-static';
import { ContentPage } from 'react-static-plugin-markdown';

export default withRouteData((props) => (
  <article>
    <h1>{props.markdown.data.title}</h1>
    <ContentPage {...props}/>
  </article>)
);

あとは、static.config.js にプラグインの設定をするだけです。renderComponent には前述のレイアウト用コンポーネントを指定します。

  plugins: [
            [ 
                'react-static-plugin-markdown',
                { renderComponent: 'src/containers/ContentPage' }
            ]
           ]

さて、以上で、React-Static においても Markdown によるコンテンツ記述が出来るようになりました。ただ、これだけだと、個別ページが出来たのみです。 ちなみに、react-static-plugin-markdown が出てくる前は、自前で以下のように static.config.js に書いて変換を行なっていました。

    const files = await glob.promise("contents/**/*.md", {})
    const file_contents = await asyncMap(files, async file => {
      const file_content = fm(await fs.readFile(file, "utf8"))
      const contentPath = filterPath(file)
      return {
        path: contentPath,
        component: 'src/containers/Post',
        attributes: file_content.attributes,
        name: file_content.attributes.title,
        tags: file_content.attributes.tags,
        getData: () => ({
          post: {
            ...file_content.attributes,
            contents: marked(file_content.body)}})
      }
    })

一覧ページはどうすればいいのでしょうか。Markdown に含まれているタグなどの一覧を取得する API などはないため、地道にやる場合は自作することになります。 例えばタグ一覧を取得する場合、以下のように、static.config.jsgetRoutes に記述することで取得することができます。やっていることは、前述の file_contents に対してタグごとにリストを作成して、それらを Routes 形式のオブジェクトとして生成するという方式をとっています。これをプラグイン形式とかにしておくといいのかもしれませんが、やっていません。

    const tags = file_contents.reduce((prev, current) => { return _.union(prev, current.attributes.tags) }, [])
    const tags_pages = tags.map((tag) => {
      return {
        path: `/tags/${slug(tag)}`,
        component: 'src/containers/Tag',
        name: tag,
        getData: () => ({
          tag,
          page_list: file_contents.reduce((prev, current) => {
            return _.indexOf(current.attributes.tags, tag) !== -1 ? _.concat(prev, current) : prev
          }, [])})
      }
    })

上記方法は、実際に自分の別サイト Siy & Diy にて使っています。こちらは、React-Static ベースです。

MDX でも Markdown でも Front Matter 部分は同じなので、同様の方法で MDX の一覧ページを作成できそうです。 ただ、Next.js で実現するには、もう 1 ハードルあります。続きます。

参照


ARTICLES


    AUTHOR

    原 一浩 の顔写真

    (はら) 一浩(かずひろ)

    カンソクインダストリーズ代表 / グレーティブ合同会社代表

    1998年に独立し、同年、ウェブデザイン専門のメールメディア DesignWedgeの発行を開始。Webデザイン業の傍ら、海外のWebデザインに関する情報発信を行う。
    雑誌への寄稿多数。主な著書に『はじめてのフロントエンド開発』『プロセスオブウェブデザイン』、『Play framework徹底入門』、『ウェブデザインコーディネートカタログ』など。自社製のWebデザインのクロール&キャプチャシステムvaqumをベースに、様々なリサーチを行っている。Web 検定プロジェクトメンバー

    著作一覧はこちら