Gatsbyで1時間でブログを作るチュートリアル (Qiitaの記事を移行する)


Gatsbyを使ってブログを作ります。
基本的なお作法は[こちら](https://qiita.com/abouch/items/ab7694e64f011ee2b265)にまとめたので良かったら参照ください。
今回はstarterは使わずにやっていきます。

最終的にはQiitaに書いた記事を[作ったサイト](https://churabou.github.io/gatsby-blog-tutorial/)にのっけて公開しました。
(画像はS3を参照しているので完全な移行ではないですが)


<img width="1680" alt="スクリーンショット 2020-04-26 16.25.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166241/20305b41-04bd-9cca-8747-bb204fe524ef.png">





## 前提


記事は下記のようなメタ情報を含むマークダウンで書かれている前提です。

```
---
title: Gatsbyでブログを作るチュートリアル
date: 2020-04-11
tags: ["Gatsby"]
---

# Step1 Gatsbyをインストール&Hello Gatsby!

...

```


## Step1 Gatsbyをインストール&Hello Gatsby!

プロジェクトを作成して以下の手順ですすめます。

```
echo "{}" > package.json
yarn add -D react react-dom gatsby
mkdir -p src/pages
```

``` src/pages/index.js
import React from "react"
export default () => <h1>Hello Gatsby!</h1>
```

```
gatsby develop
```

http://localhost:8000/ にアクセスして表示されることを確認します。

[setting-up-gatsby-without-gatsby-new](https://www.gatsbyjs.org/docs/setting-up-gatsby-without-gatsby-new/)

## Step2 Graphqlを使ってブログのの内容を取得する。

`contents`ディレクトリに記事を作成します。

```
mkdir contents
```

``` contents/hello.md
---
title: Gatsbyでブログを作るチュートリアル
date: 2020-04-11
tags: ["Gatsby"]
---

# Step1 Gatsbyをインストール&Hello Gatsby!

...
```

```
yarn add -D gatsby-source-filesystem gatsby-transformer-remark
```

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

上記のプラグインを導入して先程作成したcontentsディレクトリを読むようにプラグインの設定を追加します。

http://localhost:8000/___graphql にアクセスすると以下のクエリで記事の一覧が取得できると思います。

```
{
  allMarkdownRemark {
    edges {
      node {
        frontmatter {
          title
          tags
          date(formatString: "YYYY年MM月DD日")
        }
      }
    }
  }
}
```

``` json
{
  "data": {
    "allMarkdownRemark": {
      "edges": [
        {
          "node": {
            "frontmatter": {
              "title": "Hello Gatsby",
              "tags": [
                "Gatsby"
              ],
              "date": "2020年04月11日"
            }
          }
        }
      ]
    }
  }
}
```

これで`contents`に配置したmdファイルの内容を取得できます。


## Step3 記事一覧ページを作る

先程のクエリをつかって、記事一覧を表示します。


``` src/pages/index.js
import React from "react"
import { graphql, Link } from "gatsby"

export const query = graphql`
  query {
    allMarkdownRemark(sort: { order: DESC, fields: frontmatter___date }) {
      edges {
        node {
          id
          frontmatter {
            title
            tags
            date(formatString: "YYYY年MM月DD日")
          }
        }
      }
    }
  }
`

export default ({ data }) => {
  const edges = data.allMarkdownRemark.edges
  return (
    <div>
      <h1>全ての記事</h1>
      <ul>
        {edges.map((edge) => (
          <li key={edge.node.id}>
            <Link to={edge.node.id}>
              <div>
                {edge.node.frontmatter.tags.map((tag) => (
                  <span key={tag}>{tag}</span>
                ))}
              </div>
              <div>{edge.node.frontmatter.title}</div>
              <div>{edge.node.frontmatter.date}</div>
            </Link>
          </li>
        ))}
      </ul>
    </div>
  )
}

```



## Step4 テンプレートを使って記事を表示する

一覧ページから遷移する記事のページを作成します。

```
mkdir -p src/templates
```


テンプレートを作成します。

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

export const pageQuery = graphql`
  query markdown($id: String!) {
    markdownRemark(id: { eq: $id }) {
      html
      frontmatter {
        title
        tags
        date(formatString: "YYYY年MM月DD日")
      }
    }
  }
`

export default ({ data }) => {
  const html = data.markdownRemark.html
  const { title, tags, date } = data.markdownRemark.frontmatter
  return (
    <div>
      <article>
        <h1>{title}</h1>
        <div>
          {tags.map((tag) => (
            <span key={tag}>{tag}</span>
          ))}
        </div>
        <div>{date}</div>
        <div dangerouslySetInnerHTML={{ __html: html }} />
      </article>
    </div>
  )
}
```


作成したテンプレートと`createPage` APIを使って動的にページを生成します。

``` gatsby-node.js
const path = require("path");

exports.createPages = async ({ graphql, actions }) => {
  const { createPage } = actions;

  const result = await graphql(`
      query {
        allMarkdownRemark {
          edges {
            node {
              id
              html
            }
          }
        }
      }
  `);

  if (result.errors) {
    throw result.errors;
  }

  const posts = result.data.allMarkdownRemark.edges;

  posts.forEach((post) => {
    createPage({
      path: post.node.id,
      component: path.resolve(`./src/templates/post.js`),
      context: {
        id: post.node.id
      },
    });
  });
};
```

これで記事一覧ページと記事ページへの遷移が完成です。


## Step5 見た目を整える

好みのCSSフレームワークを使って見た目を調整していきます。今回はstyled-componentを使います。

```
yarn add -D gatsby-plugin-styled-components styled-components babel-plugin-styled-components
```


https://www.gatsbyjs.org/docs/styled-components/


``` gatsby-config.js
module.exports = {
  plugins: [
    `gatsby-plugin-styled-components`,
     ...
  ],
}
```

CSSは`gatsby-browser.js`で読み込みます。

``` idnex.css
body {
  margin: 0px;
  --theme-color: hsl(209, 47%, 35%);
}
```

``` gatsby-browser.js
require("./index.css")
```


### 5-1 一覧ページ

コンポネントを作成しつつ、スタイルをあてていきます。

``` src/components/Header.js
  
import React from "react";
import { Link } from "gatsby";
import styled from "styled-components";

const Wrapper = styled.header`
  background: var(--theme-color);
  margin: 0px;
  padding: 0.5em 1em;
  color: white;
  a {
    text-decoration: none;
    color: white;
  }
`;

export default () => (
  <Wrapper>
    <h1>
      <Link to="/">Qiitaに書いたやつ</Link>
    </h1>
  </Wrapper>
);
```

``` src/components/Footer.js
import React from "react"
import styled from "styled-components"

const Wrapper = styled.footer`
  background: #999;
  padding: 1em;
  color: white;
`
export default () => <Wrapper>2020 churabou</Wrapper>
```

``` src/pages/index.js
import React from "react"
import styled from "styled-components"
import { graphql, Link } from "gatsby"
import Header from "../components/Header"
import Footer from "../components/Footer"

export const query = graphql`
  query {
    allMarkdownRemark(sort: { order: DESC, fields: frontmatter___date }) {
      edges {
        node {
          id
          frontmatter {
            title
            tags
            date(formatString: "YYYY年MM月DD日")
          }
        }
      }
    }
  }
`

const Body = styled.div`
  padding: 2em;
  ul {
    list-style: none;
    padding: 0px;
  }
  li {
    padding: 1em;
    margin-bottom: 1em;
    box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.3);
    div {
      padding: 0.25em;
    }
    span {
      display: inline-block;
      padding: 0.25em;
      background: var(--theme-color);
      color: white;
      margin: 0 0.25em 0.25em 0;
    }
    a {
      text-decoration: none;
      color: black;
    }
  }
`

export default ({ data }) => {
  const edges = data.allMarkdownRemark.edges
  return (
    <React.Fragment>
      <Header />
      <Body>
        <h1>全ての記事</h1>
        <ul>
          {edges.map((edge) => (
            <li key={edge.node.id}>
              <Link to={edge.node.id}>
                <div>
                  {edge.node.frontmatter.tags.map((tag) => (
                    <span key={tag}>{tag}</span>
                  ))}
                </div>
                <div>{edge.node.frontmatter.title}</div>
                <div>{edge.node.frontmatter.date}</div>
              </Link>
            </li>
          ))}
        </ul>
      </Body>
      <Footer />
    </React.Fragment>
  )
}
```

### 5-2 記事一覧ページ

以下のプラグインを追加してsyntax hilightをします。
GatsbyのPluginのgatsby-transformer-remarkのプラグインです。

```
yarn add -D gatsby-remark-prismjs prismjs
```

``` gatsby-config.js
module.exports = {
  plugins: [
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          {
            resolve: `gatsby-remark-prismjs`,
          }
        ]
      }
    },
  ]
}
```

使いたいテーマのCSSを読み込みます。

``` gatsby-browser.js
require("prismjs/themes/prism-okaidia.css");
```

https://littlebylittle.work/2020/01/gatsby-syntax-highlighting/

少しスタイル当てると一気にブログっぽくなりますね。

https://github.com/churabou/gatsby-blog-tutorial/commits/master


## Step6 公開する

### GitHub Pages

GitHub Pagesで公開します。~~Gatsby関連は基本的にドキュメントを見るのが早いです。~~

https://www.gatsbyjs.org/docs/how-gatsby-works-with-github-pages/

```
yarn add -D gh-pages
```

``` gatsby-config.js
module.exports = {
  pathPrefix: "/repository_name",
  ...
}
```


``` package.json
{
  "scripts": {
    "deploy": "gatsby build --prefix-paths && gh-pages -d public"
  }
}
```

```
yarn run deploy
```

~~リポジトリの設定でgh-pagesブランチを選んで公開完了です。~~


### Netlifyで公開する


こちらが参考になりました

https://qiita.com/k-penguin-sato/items/7554e5e7e90aa10ae225

--- 

## その他

#### Qiitaの記事ダウンロードする


``` download.js
const user = "Qiitaのユーザー名";
const https = require("https");
const fs = require("fs");
const URL = `https://qiita.com/api/v2/users/${user}/items`;

const fetchItems = () =>
  new Promise((resolve, reject) => {
    https
      .get(URL, (res) => {
        let rawData = "";
        res.setEncoding("utf8");
        res.on("data", (chunk) => (rawData += chunk));
        res.on("end", () => {
          try {
            resolve(JSON.parse(rawData));
          } catch {
            reject(e);
          }
        });
      })
      .on("error", (e) => reject(e));
  });

const main = async () => {
  const items = await fetchItems();
  items.forEach((item) => {
    const { title, tags, body, created_at } = item;
    const content = [
      "---",
      `title: ${title}`,
      `date: ${created_at.split("T")[0]}`,
      `tags: [${tags.map((tag) => `"${tag.name}"`).join(",")}]`,
      "---",
      "",
      body,
    ].join("\n");

    fs.writeFile(
      `./contents/${created_at}.md`,
      content,
      "utf8",
      (date, err) => {}
    );
  });
};

main();
```

```
node download.js
```

上記のスクリプトを実行して投稿した記事のmarkdownを落としつつ、メタ情報を追加します。



#### 関連タグを表示する


以下のクエリで同じタグかつ自分以外の記事を日付順に5件まで取得しました。

```
    sameTagPosts: allMarkdownRemark(
      limit: 5
      sort: { order: DESC, fields: frontmatter___date }
      filter: { frontmatter: { tags: { in: $tags } }, id: { ne: $id } }
    ) 
```

https://github.com/churabou/gatsby-blog-tutorial/commit/660e6089f44b5d2d1857b7914b4d545c360353d5

#### コードタイトルを表示する

`gatsby-remark-code-titles`というpluginが人気らしいのです。

https://littlebylittle.work/2020/01/gatsby-syntax-highlighting/

[#コードブロックにタイトルをつける](https://qiita.com/muuuuminn/items/ec9b620f057da3e66351#%E3%82%B3%E3%83%BC%E3%83%89%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AB%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%82%92%E3%81%A4%E3%81%91%E3%82%8B)

ただ自分には`js:title=filename`見たいな書き方に抵抗があるので、qiitaのようにそのままファイル名を書いて使えるようにローカルにpluginを作ります。


```
mkdir -p plugins/gatsby-remark-code-title
```

package.jsonを作成して`gatsby-config.js`で`gatsby-markdown-remark`のpluginに指定します。


https://github.com/churabou/gatsby-blog-tutorial/commit/6b3a888702946533764f92e1c64ad46184271c67



#### SEO対策をする

ライブラリやプラグインを使うのが良さそうですね。

https://www.gatsbyjs.org/docs/add-seo-component/


#### ぺージング、検索機能、などなど・・・

時間があればやってきたいですね