GatsbyJS トランスフォーマープラグインを使用してマークダウンの内容を表示する

2019-06-20

プロジェクトディレクトリ内のマークダウンをGraphQLで照会し、一覧画面と詳細画面を作成します。

目次

準備:プラグインのインストールとマークダウンの作成

gatsby-transformer-remarkのインストール

gatsby-source-filesystemも必要となるため、一緒にインストールします。

gatsby new gatsby-hello-world https://github.com/gatsbyjs/gatsby-starter-hello-world
npm install --save gatsby-source-filesystem gatsby-transformer-remark

次に、gatsby-config.jsのplugins配列に設定内容を記述します。
今回はプロジェクトディレクトリの下にdataディレクトリを作成し、ここにマークダウンファイルを配置するようにします。

gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `data`,
        path: `${__dirname}/data/`,
      },
    },
    `gatsby-transformer-remark`,
  ],
}

GraphiQL(http://localhost:8000/___graphq) にアクセスし、allMarkdownRemarkとmarkdownRemarkが表示されていることを確認します。

gatsby_markdown_1.png

マークダウンファイルの作成

dataディレクトリにfile1.md、file2.mdを作成します。
ファイルの最初に「-」に囲まれたスペースがあります。これは「frontmatter」と呼ばれGraphQLから参照できます。

data/file1.md
---
path: "/file1"
date: "2019-06-16"
title: "マークダウンファイル その1"
---

## マークダウンファイルの内容
data/file2.md
---
path: "/file2"
date: "2019-06-19"
title: "マークダウンファイル その2"
---

+ 箇条書き
+ 箇条書き
+ 箇条書き

GraphQLでマークダウンファイルの内容を取得する

開発サーバーを再起動し、GraphiQLでマークダウンファイルの内容を取得します。
以下の内容のGraphQLを作成します。

  • frontmatterのdateの昇順
  • frontmatterの内容を出力
  • マークダウンを変換したhtml
  • マークダウンファイル数(totalCountで取得する)
  • マークダウン内容の文字列部分の取得(excerptで取得する)
query MyQuery {
  allMarkdownRemark(sort: 
    	{
        fields: frontmatter___date, 
        order: ASC
      }) {
    totalCount
    edges {
      node {
        frontmatter {
          title
          path
          date
        }
        html
        excerpt(format: PLAIN)
      }
    }
  }
}

実行結果にうまく表示されることを確認します。
html部分の出力結果をみるとマークダウン内の「##」が「h2」、「+」が「li」に変換されています。
excerptで指定している「format: PLAIN」はhtml要素を抜いた文字列のみ取得できます(デフォルト)。「format: HTML」でhtml要素つき、「format: MARKDOWN」でマークダウンの記述で取得できます。またデフォルトでは140文字数(html要素含まない)ですが「pruneLength」を指定すると取得文字数を増減できます。
マークダウンのファイルはdataディレクトリ直下でなくても検索できるので、日付ディレクトリやカテゴリディレクトリを作成して、その配下にマークダウンを作成しても表示できます。

gatsby_markdown_2.png

GraphQLで取得した内容を画面に表示する

index.jsにマークダウンの内容を一覧で表示します。マークダウンファイルの件数とfrontmatter(date、title)、抜粋を表示します。

emotionでCSSを記述するのでインストールします。

npm install --save gatsby-plugin-emotion @emotion/core @emotion/styled
src/pages/index.js
// index.js
import React from "react"

// 1. graphqlのインポート
import { graphql } from "gatsby"
/** @jsx jsx */
import { jsx  } from '@emotion/core'

//emotionでそれっぽいスタイルを指定
const title = {
    fontSize: '24px'
}
const date = {
    marginRight: '16px',
    fontSize: '16px'
}
const excerpt = {
    marginTop: '8px',
    fontSize: '16px'
}
// 3. 2の結果がdataに自動的に渡されてくる
export default ({data}) => {
    //dataの内容を表示してみる
    console.log(data);
    return(
        <div>
        マークダウン数:{data.allMarkdownRemark.totalCount}{data.allMarkdownRemark.edges.map(({ node }, index) => (
        <div key={index}>
            <span css={date}>{node.frontmatter.date}</span>
            <span css={title}>{node.frontmatter.title}</span>
            <p css={excerpt}>{node.excerpt}</p>
        </div>
        ))}
        </div>
    )
}

// 2.マークダウン件数(totalCount)、frontmatter、内容の抜粋(excerpt)を取得する
export const query = graphql`
query MyQuery {
    allMarkdownRemark(sort: {fields: frontmatter___date, order: ASC}) {
      totalCount
      edges {
        node {
          frontmatter {
            title
            path
            date
          }
          excerpt
        }
      }
    }
  }  
`

開発サーバーを再起動して、ブラウザで確認できます。

gatsby_markdown_3.png

マークダウンからページを生成する

次に、サーバー再起動時にマークダウンごとのページを作成します。

処理としては、 ページの元となるテンプレートに サーバ起動時gatsby-node.jsの処理で マークダウンファイルの内容を埋め込む感じとなります。

テンプレートの作成

マークダウンの内容を表示するテンプレートを「src/templates/blog.js」に作成します。
内容はGraphQLでtitleとhtmlを取得し表示するページとなりますが、どのマークダウンか特定するためにfieldsのslugが「$slug」と一致するという条件を指定します($slugはgatsby-node.jsで作成)。

src/templates/blog.js
// blog.js
import React from "react"
import { graphql } from "gatsby"


export default ({ data }) => {  
    const post = data.markdownRemark
    return (
        <div>
            <h1>{post.frontmatter.title}</h1>
            <div dangerouslySetInnerHTML={{ __html: post.html }} />
        </div>
        )
    }
// $slugに一致するマークダウンを取得
    export const query = graphql`
    query($slug: String!) {
        markdownRemark(fields: { slug: { eq: $slug } }) {
            html
            frontmatter {
                title
            }
        }
    }
`

gatsby-node.jsで「slug」を設定する

さきほどテンプレートで記述した「slug」をGraphQLで使用できるようにします。
それには、ページを作成にはgatsby-node.jsを使用します。プロジェクトディレクトリ直下になければ作成し、onCreateNode関数を記述します。
onCreateNodeはノードが作成(または更新)されるタイミングで実行されます。
そのため、マークダウン以外のノードでも実行されてしまうので、マークダウンの時のみ処理できるようにします。

// gatsby-node.js
exports.onCreateNode = ({ node }) => {
    
    if (node.internal.type === `MarkdownRemark`) {
        //MarkdownRemarkのノードの時に表示
        console.log(node)
    }
}

開発サーバーの再起動を行うとコンソールに、マークダウンのnodeの内容が出力されます。

gatsby_markdown_4.png

今回は、マークダウンのfrontmatterのpathをURLに利用します。
pathの内容を「slug」という名前でノードに追加します。

// gatsby-node.js
exports.onCreateNode = ({ node,actions }) => {
    if (node.internal.type === `MarkdownRemark`) {
        const { createNodeField } = actions
        // ノードに追加 fields配下に追加される
        createNodeField({
            node,//このノードに
            name: `slug`,//slugという名前で
            value: node.frontmatter.path//pathを設定
        })

    }
}

開発サーバーを再起動すると、GraphQLで「slug」が参照できます。

query MyQuery {
  allMarkdownRemark {
    edges {
      node {
        fields {
          slug
        }
      }
    }
  }
}

開発サーバーを再起動すると、新しくfieldsが追加され、createNodeFieldで追加した「slug」が表示されます。
この「slug」がさきほどテンプレートを作成した際に記述した「slug」になります。

gatsby_markdown_5.png

gatsby-node.jsでページの生成

次に、ページの生成をします。
gatsby-node.jsにonCreateNodeを追加します、onCreateNodeはノードの作成が完了した後に実行されます。

onCreateNodeでは以下の内容実装します。

  • テンプレートの絶対パスを取得

    • path.resolveで絶対パスの取得ができます(事前にrequire(path)でpathを読み込んでおく)。
  • マークダウンのslugを取得
  • createPageでページの生成を行う

    • 引数に対応するパス、テンプレートの絶対パス、テンプレートに渡す引数を指定する
// gatsby-node.js
exports.onCreateNode = ({ node,actions }) => {
    if (node.internal.type === `MarkdownRemark`) {
        const { createNodeField } = actions
        // ノードに追加 fields配下に追加される
        createNodeField({
            node,//このノードに
            name: `slug`,//slugという名前で
            value: node.frontmatter.path//pathを設定
        })
    }
}

//ページ生成
const path = require(`path`)
exports.createPages = ({ graphql, actions }) => {
    //テンプレートの絶対パスを取得
    const blogTemplate = path.resolve(`src/templates/blog.js`)
    const { createPage } = actions

    // マークダウンファイルのslugを取得
    return graphql(`    
        {
            allMarkdownRemark {
                edges {
                    node {
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `).then(
        result => {
            // 取得したslugの分ページを作成
            result.data.allMarkdownRemark.edges.forEach(edge  => {
                createPage({
                    path: edge.node.fields.slug,//このURLのとき
                    component: blogTemplate,//このテンプレートでページ生成
                    context: {
                        slug: edge.node.fields.slug,//slugという引数でedge.node.fields.slugを渡す
                    },
                })
            })
        }
    )
}

開発サーバーの再起動後、http://localhost:8001/file2にアクセスすると マークダウンの内容が確認できます。

gatsby_markdown_6.png

一覧にリンクを設定

index.jsの一覧表示から各ページへ遷移できるようにします。

  • GraphQLでfields.slugを取得
  • Linkコンポーネントでリンクを作成し、遷移先にfields.slugを指定する
index.js
// index.js
import React from "react"

// 1. graphqlのインポート
import { Link,graphql } from "gatsby"
/** @jsx jsx */
import { jsx  } from '@emotion/core'

//emotionでそれっぽいスタイルを指定
const title = {
    fontSize: '24px'
}
const date = {
    marginRight: '16px',
    fontSize: '16px'
}
const excerpt = {
    marginTop: '8px',
    fontSize: '16px'
}
// 3. 2の結果がdataに自動的に渡されてくる
export default ({data}) => {
    //dataの内容を表示してみる
    console.log(data);
    return(
        <div>
        マークダウン数:{data.allMarkdownRemark.totalCount}{data.allMarkdownRemark.edges.map(({ node }, index) => (
        <div key={index}>
            <span css={date}>{node.frontmatter.date}</span>
            <Link to={node.fields.slug} css={title}>{node.frontmatter.title}</Link>
            <p css={excerpt}>{node.excerpt}</p>
        </div>
        ))}
        </div>
    )
}

// 2.マークダウン件数(totalCount)、frontmatter、内容の抜粋(excerpt)、fieldsのslugを取得する
export const query = graphql`
query MyQuery {
    allMarkdownRemark(sort: {fields: frontmatter___date, order: ASC}) {
      totalCount
      edges {
        node {
          frontmatter {
            title
            path
            date
          }
          excerpt
          fields {
            slug
          }
        }
      }
    }
  }  
`
■同じタグの記事(最新5件)
GatsbyJS FaunaDBからデータを取得する
GatsbyJS 検索機能を実装する(JsSearchを利用)
GatsbyJS PostgreSQLの内容を取得する
GatsbyJS rehypeReactでマークダウンの内容を変更する
GatsbyJS マークダウンにコンポーネントを表示する
■同じタグの記事を見る