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とかを入れている。
手順
大まかな流れはこんな感じ。
- テンプレートからデプロイ
- ローカル開発環境の作成
- パッケージ最新化
- 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 deploys
のProduction: 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
- http://localhost:8000/
- http://localhost:8000/___graphql
-
http://localhost:8000/.netlify/functions/hello
サーバーサイド
とりあえず動いているように見えるが、実行時になにやらエラーが出ている。
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がビルドされることを確認するため、内容を書き換えてみる。
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の設定ファイルにプラグインの読み込みを追加。
plugins: [
`gatsby-plugin-typescript`,
"gatsby-plugin-react-helmet",
・・・
試しにサンプルソースの一つをTS化してみる。(src/templates/tags.js)
まず、GatsbyのGraphQLノードを作成するソースのところで指定しているコンポーネントの拡張子をjs→tsx化。
createPage({
path: tagPath,
component: path.resolve(`src/templates/tags.tsx`),
context: {
tag,
},
})
ソースの拡張子を変更。エラーが出た箇所を中心にTS化していく。
プロパティの型は、GraphQLのクエリとgatsby-node.js
のcreatePageで指定したcontextの内容から作成する。
ついでにコンポーネントがクラスなので、FunctionComponent化する。
クラスを使う理由がなければ、ReactHooksが使えるFunctionComponentの方が便利。
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のカスタマイズは不要。
{
"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)と型の追加。ついでに、アロー関数の形に変更。
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)
まず、型定義を追加。
他にも型定義が提供されていないパッケージがある場合はこのフォルダに追加していく。
declare module "*.svg" {
const value: string;
export default value;
}
次に、以下を参考に、tsconfig.json
を追加。
gatsby/tsconfig.json at master · gatsbyjs/gatsby · GitHub
{
"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型になって
エラー表示されてしまうので、noImplicitAny
にfalse
を設定。
github リポジトリのリネームとprivate化
- githubの管理ページからrename
- netlifyのSettings→Build & deploy→Build settingsからリポジトリのアドレスを変更
- netlifyのSettings→Identity→Git Gatewayからアクセストークンを新規発行
- ローカルの作業ディレクトリで以下のコマンドを入力
git remote set-url origin git@github.com:*****/新リポジトリ名.git
とりあえず、ここまで。
ソースは以下にあります。(プライベート化はしてません)
GitHub - otanu/gatsby-starter-netlify-cms