Help us understand the problem. What is going on with this article?

Netlify CMSのGatsbyJSテンプレートで個人ブロク作成(+TypeScript)

More than 1 year has passed since last update.

Netlify.png

Netlify CMSのGatsbyテンプレートをTypeScript化する時にやったことのメモです。

こう言うのをJAMstacと言うんですね。それぞれのツールは知っていても、それをまとめた概念と名前があるのは知りませんでした。

JAMstack | JavaScript, APIs, and Markup
JAMstack ベストプラクティス

登場人物

  • Netlify
    静的WebサイトホスティングサービスのSaaS

    • Functions

    実態はAWS Lambda。サーバーレスで関数を動かせる。
    ホスティングと連動し同一ドメイン上で動くので、CORS対策用のプロキシ用とかに使える。
    今回はとりあえず単独で動く所までを確認。

  • Netlify CMS
    オープンソースのコンテンツ管理システム。
    GitHubにプッシュすると自動でビルド・デプロイしてくれる。
    テンプレートからGitHubリポジトリ作成・ビルド・デブロイまで出来る
    チュートリアルがあるので、今回はこれを使用。

  • GatsbyJS
    Reactで作成できる静的サイトのジェネレータ
    Markdown→GraphQL→Reactで静的ファイルを作成

動機

React+GraphQLの練習にちょうどいい。
個人ブログ程度であれば、基本的に無料枠で収まる。
環境作成からデプロイまで至れり尽くせり。
GitHubのプライベートリポジトリでも運用できる。
TypeScript化するとVSCodeで補完が効く。

事前に必要なもの

  • GitHubアカウント
  • Node.js
    自分の環境ではv8.15.1を使用。
    他のバージョンでも大丈夫だとは思うけれど、functionsがv8.10で動くので
    とりあえずv8系を選択。

  • VSCode
    快適に開発できるようにするため、コレに合わせて設定を変えていきます。
    拡張機能はPrettier、TSLint,ESLintとかを入れている。

手順

大まかな流れはこんな感じ。

  1. テンプレートからデプロイ
  2. ローカル開発環境の作成
  3. パッケージ最新化
  4. TypeScript化

パッケージ類はバージョン差が開くとアップデートがしんどくなるので、初期構築時に出来る限り最新化しておく。
TypeScriptの型は完璧にやろうとするとしんどいので、出来る範囲で。
ビルド時のtslintや型チェックはひとまず置いておく。
Functionsはとりあえず動く所まで確認。

テンプレートからデプロイ

Start with a Template | Netlify CMS | Open-Source Content Management System

Gatsby Site Starterを選択。

githubにgatsby-starter-netlify-cmsというリポジトリが自動的に作られる。
作成時にリポジトリ名の変更も可能。
作成後に変更することも可能なので、ひとまずこのまま進める。

「Save & Deploy」を押すと、テンプレートをのビルド・デプロイが始まり、
「overview」(プロジェクトのダッシュボード)ページに自動的に移動する。

Production deploysProduction: master@HEADにステータスがPUBLISHEDになるまで待つ。
ここを開くと、コンソールのライブログを見ることが出来るので、ゴリゴリビルドしている様子が分かります。

ビルドが終わると、https://*******.netlify.com/にサンプルページ(なぜかKALDI)がデプロイされている。

CMS adminページ

デプロイされたページには、ブラウザからコンテンツを更新する出来るページ(admin)がある。
ビルド後、githubに登録しているメールアドレスにYou have been invitedというタイトルの招待メールが来るので、Accept the inviteにアクセスしてパスワードを登録。

CMSのページはhttps://*******.netlify.com/adminで、ページの更新やブログ記事の追加、画像の管理ができる。
更新後、「Publish」ボタンを押すと、更新した内容がgithubに自動的にプッシュされ、ビルドとデプロイが自動的に動く。

ローカル環境の作成

githubからリポジトリをダウンロードし、ローカル環境で動かしてみる。

$ git clone git@github.com:****/gatsby-starter-netlify-cms.git
$ cd gatsby-starter-netlify-cms
$ yarn install
$ yarn start

とりあえず動いているように見えるが、実行時になにやらエラーが出ている。

ERROR in Entry module not found: Error: Can't resolve 'babel-loader' 

functionsのビルドが出来ていない様子。
githubにビルド済みのファイルlambda/hello.jsがあるので、起動はできるけれど、src/lambda/hello.jsを更新しても反映されない。

なにやらyarn周りでなにか問題がありそう?
https://github.com/symfony/webpack-encore/issues/237

とりあえず、エラーが出るパッケージをインストールして対応。

$ yarn add -D \
  babel-loader \
  @babel/plugin-proposal-class-properties \
  @babel/plugin-transform-object-assign

ついでに、パッケージを諸々アップデート。

$ yarn upgrade

package.jsonのバージョンが古いと何か気持ち悪いので、npm-upgradeでアップデートされたパッケージのバージョンに書き換える。(これは必須ではないです。)

  bulma                              ^0.7.0   →     ^0.7.4 
  gatsby                            ^2.1.34   →    ^2.3.13 
  gatsby-image                      ^2.0.23   →    ^2.0.37 
  gatsby-plugin-netlify              ^2.0.6   →    ^2.0.13 
  gatsby-plugin-netlify-cms         ^3.0.15   →    ^3.0.17 
  gatsby-plugin-purgecss             ^3.1.0   →     ^3.1.1 
  gatsby-plugin-react-helmet         ^3.0.4   →    ^3.0.12 
  gatsby-plugin-sass                 ^2.0.7   →    ^2.0.11 
  gatsby-plugin-sharp               ^2.0.15   →    ^2.0.32 
  gatsby-remark-copy-linked-files    ^2.0.7   →    ^2.0.11 
  gatsby-remark-images               ^3.0.1   →    ^3.0.10 
  gatsby-remark-relative-images      ^0.2.1   →     ^0.2.2 
  gatsby-source-filesystem          ^2.0.26   →    ^2.0.28 
  gatsby-transformer-remark          ^2.3.4   →     ^2.3.8 
  gatsby-transformer-sharp           ^2.1.9   →    ^2.1.17 
  lodash                            ^4.17.5   →   ^4.17.11 
  lodash-webpack-plugin             ^0.11.4   →    ^0.11.5 
  netlify-cms                        ^2.6.1   →     ^2.9.0 
  parcel-bundler                     ^1.9.4   →    ^1.12.3 
  prop-types                        ^15.6.0   →    ^15.7.2 
  react                             ^16.8.4   →    ^16.8.6 
  react-dom                         ^16.8.4   →    ^16.8.6 
  uuid                               ^3.2.1   →     ^3.3.2 
  prettier                          ^1.15.3   →    ^1.16.4 
  rimraf                             ^2.6.2   →     ^2.6.3 

yarn startで起動出来ることを確認。
起動できたら、functionsがビルドされることを確認するため、内容を書き換えてみる。

src/hello.js
export function handler(event, context, callback) {
    console.log("queryStringParameters", event.queryStringParameters)
    callback(null, {
      // return null to show no errors
      statusCode: 200, // http status code
      body: JSON.stringify({
        msg: "はろーわーるど" + Math.round(Math.random() * 10),
      }),
    })
  }

更新後、functionsにアクセス。
http://localhost:8000/.netlify/functions/hello

うまくいくと、ホットリロードされ、ソースの内容が反映されたことが確認できる。

確認できたら、githubにプッシュ。自動デプロイされるので、
https://****.netlify.com/.netlify/functions/helloにアクセスして、デプロイ出来ることを確認。

TypeScript化(Gatsby編)

プラグインをインストール。

yarn add gatsby-plugin-typescript

Gatsbyの設定ファイルにプラグインの読み込みを追加。

gatsby-config.js
  plugins: [
    `gatsby-plugin-typescript`,
    "gatsby-plugin-react-helmet",
  ・・・

試しにサンプルソースの一つをTS化してみる。(src/templates/tags.js)

まず、GatsbyのGraphQLノードを作成するソースのところで指定しているコンポーネントの拡張子をjs→tsx化。

gatsby-node.js
      createPage({
        path: tagPath,
        component: path.resolve(`src/templates/tags.tsx`),
        context: {
          tag,
        },
      })

ソースの拡張子を変更。エラーが出た箇所を中心にTS化していく。
プロパティの型は、GraphQLのクエリとgatsby-node.jsのcreatePageで指定したcontextの内容から作成する。

ついでにコンポーネントがクラスなので、FunctionComponent化する。
クラスを使う理由がなければ、ReactHooksが使えるFunctionComponentの方が便利。

src/templates/tags.tsx
import React, { FC } from "react";
import Helmet from "react-helmet";
import { Link, graphql } from "gatsby";
import Layout from "../components/Layout";

type Props = {
  data: {
    allMarkdownRemark: {
      edges: Node[];
      totalCount: number;
    };
    site: {
      siteMetadata: {
        title: string;
      };
    };
  };
  pageContext: any;
};

type Node = {
  node: {
    fields: {
      slug: string;
    };
    frontmatter: {
      title: string;
    };
  };
};

const TagRoute: FC<Props> = props => {
  const posts = props.data.allMarkdownRemark.edges;
  const postLinks = posts.map(post => (
    <li key={post.node.fields.slug}>
      <Link to={post.node.fields.slug}>
        <h2 className="is-size-2">{post.node.frontmatter.title}</h2>
      </Link>
    </li>
  ));
  const tag = props.pageContext.tag;
  const title = props.data.site.siteMetadata.title;
  const totalCount = props.data.allMarkdownRemark.totalCount;
  const tagHeader = `${totalCount} post${
    totalCount === 1 ? "" : "s"
  } tagged with “${tag}”`;

  return (
    <Layout>
      <section className="section">
        <Helmet title={`${tag} | ${title}`} />
        <div className="container content">
          <div className="columns">
            <div
              className="column is-10 is-offset-1"
              style={{ marginBottom: "6rem" }}
            >
              <h3 className="title is-size-4 is-bold-light">{tagHeader}</h3>
              <ul className="taglist">{postLinks}</ul>
              <p>
                <Link to="/tags/">Browse all tags</Link>
              </p>
            </div>
          </div>
        </div>
      </section>
    </Layout>
  );
};

export default TagRoute;

export const tagPageQuery = graphql`
  query TagPage($tag: String) {
    site {
      siteMetadata {
        title
      }
    }
    allMarkdownRemark(
      limit: 1000
      sort: { fields: [frontmatter___date], order: DESC }
      filter: { frontmatter: { tags: { in: [$tag] } } }
    ) {
      totalCount
      edges {
        node {
          fields {
            slug
          }
          frontmatter {
            title
          }
        }
      }
    }
  }
`;

react-helmetの型定義がないと出るので型定義を追加。

$ yarn add -D @types/react-helmet

終わったら、「Ctrl+C」でサーバーを止め、yarn startで再起動する。
ファイル名を変えたりするとホットリロードが効かなくなるので、その都度再起動が必要。

TypeScript化(Functions編)

https://github.com/netlify/netlify-lambdaにTypeScript化の説明があるので、これを参考に進めていく。

まず、必要なパッケージを追加。

$ yarn add -D @babel/preset-typescript

.babelrcを追加する。配置するのはsrc/lambdaの下。
プロジェクトのルートだと、Gatsbyのビルドに影響が出る。Gatsbyはプラグインで完結しているので、babelのカスタマイズは不要。

src/lambda/.babelrc
{
  "presets": [
    "@babel/preset-typescript",
    "@babel/preset-env"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-object-assign",
    "@babel/plugin-proposal-object-rest-spread"
  ]
}

次に型定義を登録する。

yarn add -D @types/aws-lambda

次は、ソースの拡張子変更(js→ts)と型の追加。ついでに、アロー関数の形に変更。

src/lambda/hello.ts
import { Handler, APIGatewayEvent, APIGatewayProxyResult } from "aws-lambda";

export const handler: Handler<APIGatewayEvent, APIGatewayProxyResult> = (
  event,
  _contex,
  callback
) => {
  console.log("queryStringParameters", event.queryStringParameters);
  callback(null, {
    statusCode: 200,
    body: JSON.stringify({
      msg: "はろーわーるど TS " + Math.round(Math.random() * 10)
    })
  });
};

終わったら、yarn startで再起動。
http://localhost:8000/.netlify/functions/helloにアクセスして内容が変わったことを確認。

確認できたら、githubにプッシュしてデプロイ。
TS化しても動いていることを確認。

https://*****.netlify.com/tags/brewing/
https://*****.netlify.com/.netlify/functions/hello

TS化の続き

これでsrc下のファイルはTS化出来るようになったので、順番に書き換えていく。

他のファイルのTS化のときに引っかかった問題

svgファイルのインポートのエラー

src/components/FooterをTS化すると、VSCode上で次のエラーが出ます。

モジュール '../img/social/twitter.svg' が見つかりません。ts(2307)

まず、型定義を追加。
他にも型定義が提供されていないパッケージがある場合はこのフォルダに追加していく。

src/types/index.d.ts
declare module "*.svg" {
  const value: string;
  export default value;
}

次に、以下を参考に、tsconfig.jsonを追加。

gatsby/tsconfig.json at master · gatsbyjs/gatsby · GitHub

tsconfig.json
{
  "include": ["./src/**/*"],
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "lib": ["dom", "es2017"],
    // "allowJs": true,
    // "checkJs": true,
    "jsx": "react",
    "strict": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "noEmit": true,
    "skipLibCheck": true,
    "noImplicitAny": false,
  }
}

これで、VSCode上からはエラーが消えた。
あと、プロパティとかの型定義が済んでいないところは、any型になって
エラー表示されてしまうので、noImplicitAnyfalseを設定。

github リポジトリのリネームとprivate化

  1. githubの管理ページからrename
  2. netlifyのSettings→Build & deploy→Build settingsからリポジトリのアドレスを変更
  3. netlifyのSettings→Identity→Git Gatewayからアクセストークンを新規発行
  4. ローカルの作業ディレクトリで以下のコマンドを入力 git remote set-url origin git@github.com:*****/新リポジトリ名.git

とりあえず、ここまで。

ソースは以下にあります。(プライベート化はしてません)
GitHub - otanu/gatsby-starter-netlify-cms

otanu
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした