Gatsbyで作ったポートフォリオをv2からv3へアップデートした

## これは何

- [自分のポートフォリオサイト](https://www.keisukewatanuki.work/)をGatsbyで作っていて、v2からv3へアップデートした際に行ったことをまとめた記事です
    - あくまで個人の事例なので[詳しいアップデート手順は公式のドキュメントより](https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v2-to-v3/)どうぞ
- [フロントエンド強化月間 - 開発する上で知っておくべき知見を共有しよう](https://qiita.com/official-events/b9ad63394fa2635dfca9)イベントへの投稿記事でもあります

https://qiita.com/official-events/b9ad63394fa2635dfca9

## Gatsby v3とは？

ざっと特徴をまとめると以下のように変化しました。

- Incremental Builds in OSS
    - 必要なときだけHTMLを再生成することで、ビルドの高速化を実現
- Fast Refresh
    - 新しいホットリローディングエンジン、エラーリカバリー、より良いDX
- `gatsby-image`から`gatsby-plugin-image`に
- `gatsby-source-wordpress`がv5に
- `gatsby-source-contentful`がv5に

これ以外にも、プラグインの各種変更があったり、依存関係がアップデートされたりしています。
詳しくはリリースノートに載っていました。

https://www.gatsbyjs.com/docs/reference/release-notes/v3.0/

## v2からv3へ

全体の流れとしては、まずはマイグレーション用のドキュメントに従い

https://www.gatsbyjs.com/docs/reference/release-notes/migrating-from-v2-to-v3/

その後に`gatsby-image`を`gatsby-plugin-image`へ変更しました[^1]

https://www.gatsbyjs.com/docs/reference/release-notes/image-migration-guide/

[^1]: `gatsby-plugin-image`自体はGatsbyが`2.24.78`以上であれば動きます。検証はできていませんが、先に`gatsby-plugin-image`を導入してあとからv3へアップデートしても多分大丈夫です。

※自分はyarnを使っているので、以下の説明は全てyarnでの内容です。npmを使っている方は適宜置き換えて読んでください。

### package.jsonでのGatsbyのバージョンを上げる

```shell
$ yarn add gatsby@latest
```

### `gatsby-`から始まるパッケージをアップデートする

```shell
yarn upgrade-interactive --latest
```

本体のアップデートにあわせて、関連パッケージをアップデートする必要があります。
ただし、あくまで`gatsbyjs/gatsby`のリポジトリで管理されているパッケージ（＝公式のもの）だけが対象です。

### Node.jsのバージョン変更

今回のアップデートで、`12.13.0`以上が必須になったようです。
ただし私の場合は逆に`16.0.0`を使っていた結果、以下のようなエラーが出てしまいました。

```
(node:32396) [DEP0148] DeprecationWarning: Use of deprecated folder mapping "./" in the "exports" field module resolution of the package at
/Path/To/Repository/node_modules/gatsby/node_modules/postcss/package.json.
Update this package.json to use a subpath pattern like "./*".
```

PostCSSの問題で、2021年4月30日現在は直っているようなのですが、Gatsbyの中で使っているバージョンがまだ古いようです。

https://github.com/postcss/postcss/issues/1561

記憶がおぼろげですが、何かを試してみたくてNode.jsのバージョンを16.0.0にしていただけだったはずなので、おとなしく推奨版である14.16.1に変えました。

### 変更する案内が出ているけど、自分の環境では問題なかったor使っていなかったものたち

作りとしてはかなり簡単なサイトなので、大半は変更なしで大丈夫でした。
単に列挙するだけになってしまいますが、こういったことが書いてありましたと紹介します。

- Webpackを4から5へバージョンアップ
- ESLintを6から7へバージョンアップ
- `gatsby-link`での`navigateTo`, `push`, `replace`を`navigate`に変更
- `__experimentalThemes`を削除
- `pathContext`を`pageContext `に変更
- `boundActionCreators`を`actions`に変更
- `deleteNodes`の代わりに`nodes`のループ処理で`deleteNode`を使用
- `createNodeField`での`fieldName`と`fieldValue`をそれぞれ`name`と`value`へ変更
- `hasNodeChanged`の削除
- `gatsby-image`での`sizes`と`resolutions`をそれぞれ`fluid`と`fixed`へ変更
- `touchNode`で`node.id`ではなく`node`を渡す
- `deleteNode`で`node.id`ではなく`node`を渡す
- `gatsby-browser`APIのうちいくつかの名前が変更
- `import { graphql } from "gatsby"`が必須に
- CSS Modulesのimportの仕方が変更に
- アセットがES Modulesとしてimport可能に
- GraphQLで正規表現のエスケープが変更に
- GraphQLで`__typename`フィールドを明示的に追加する必要がある
- スキーマのカスタマイズでの変更が色々

### 匿名のアロー関数をなくす

チュートリアルやサンプルでは使われていますが、

```jsx
export default () => {}
```

匿名のアロー関数だと今回の大きな変更であるFast Refreshが上手く動かない原因になるそうです。

```jsx
const Named = () => {}
export default Named
```

これでOK。

## `gatsby-image`を`gatsby-plugin-image`へ変更

今までの`gatsby-image`は便利は便利だったんですがクセも強い印象でした。
それが、`StaticImage`コンポーネントを使うと、かなり軽減されると思います。

マイグレーションのためのドキュメントはこちらで、

https://www.gatsbyjs.com/docs/reference/release-notes/image-migration-guide/

`gatsby-plugin-image`自体の説明はこちらです。

https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-plugin-image/

### パッケージの追加

```shell
yarn add gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp
```

※`gatsby-plugin-sharp`と`gatsby-transformer-sharp`は普通にGatsbyを使っていればインストールしていると思いますが、一応`gatsby-plugin-image`を使うのには必須なのであえて記載しています。

### `gatsby-config.js`のアップデート

```javascript
module.exports = {
  plugins: [
    `gatsby-plugin-image`,
    `gatsby-plugin-sharp`,
    `gatsby-transformer-sharp`,
  ],
}
```

### コマンドを叩く

既存の`gatsby-image`の書式で書かれたコードを`gatsby-plugin-image`用に変換するコマンドを用意してくれています。

```shell
npx gatsby-codemods gatsby-plugin-image
```

ただし、全部が全部このコマンドで対応できるわけでもありません。
上手く変換できなかった分はターミナル上で教えてくれるので、手動で変更します。

私の場合は今までこのようなイメージのクエリを書いていました。

```graphql
query MyQuery($id: String) {
  mdx(id: { eq: $id }) {
    frontmatter {
      featuredImage {
        childImageSharp {
          fixed {
            src
          }
        }
      }
    }
  }
}
```

`src`の取り方が変わったので、クエリを書き直し

```graphql
query MyQuery($id: String) {
  mdx(id: { eq: $id }) {
    frontmatter {
      featuredImage {
        childImageSharp {
          gatsbyImageData
        }
      }
    }
  }
}
```

パスの渡し方を変えました。

```diff_jsx
const WorkTemplatePage = ({ data: { mdx } }) => {
- const featuredImgPath = mdx.frontmatter.featuredImage.childImageSharp.fixed.src
+ const featuredImgPath = getSrc(mdx.frontmatter.featuredImage.childImageSharp.gatsbyImageData)
  return (
    <>
      <Seo title={mdx.frontmatter.title} description={mdx.frontmatter.description} ogImagePath={featuredImgPath} />
      {/* 省略 */}
    </>
  )
}
```

余談ですが、`gatsby-image`は読み込み中はMeduimのように粗い画像をぼかして表示するのがデフォルトだったのが、`gatsby-plugin-image`ではTwitterのようにベースになる1色で塗りつぶすようになっているみたいです。