Next.jsベースの開発環境構築 及び GitHub ActionsによるStorybookの自動デプロイ
はじめに
個人開発だけでなく、チーム開発でも使えるように書きました。また、Webアクセシビリティを意識した開発をするためのStorybookアドオンの紹介や開発者だけに閉じがちな情報をオープンにするためのStorybook自動デプロイの方法なども書いてあります。
セットアップ内容
- Next.js (記事作成時 v10.0.8)
- TypeScript
- SCSS
- Jest
- ESLint
- Prettier
- lint-staged
- husky
- Storybook
- GitHub Actions
動作環境
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.7
BuildVersion: 19H2
$ node -v
v14.15.0
$ yarn -v
1.22.4
Next.jsプロジェクトの作成
$ npx create-next-app --example with-typescript-eslint-jest next-app
next.js/examples用意されているこちらのボイラープレートを使わせていただきます。
このコマンドを実行するだけで、ESLint, Prettier, Jest, lint-staged, huskyが導入されたNext.jsプロジェクトを作成することができます。コマンド最後のnext-app
がプロジェクトの名前(ディレクトリ名)になります。任意の名前で置き換えてください。
なお、上記コマンドで作成したプロジェクトはデフォルトでyarn.lock
を作成します。package-lock.json
を使用したい場合は、上記コマンドの最後に--use-npm
をつけてください。もし、意図したlockファイルではない方が作成されていた場合はそのlockファイルとnode_modulesを削除して、使いたい方のコマンドでyarn install
or npm install
してください。
また、初回作成時はインストールした各パッケージのバージョンが固定されていないので、必要に応じて固定しましょう。
husky + lint-staged について
husky: commitやpushを行う直前に任意のコマンドを実行することができるツールです。
lint-staged: gitのステージに上がっているファイルのみをターゲットできるツールです。
これらを組み合わせることで、例えば、commitコマンドを実行した際に、いきなりcommitせずに、Lintエラーのチェックとフォーマットを実行し、これらが正常に終了した場合にcommitさせる、ということが可能になります。
起動してみる
$ cd next-app
$ yarn dev
上記コマンドを実行したら、 http://localhost:3000 にアクセスしてみてください。以下の画像の通りに表示されていれば問題なく作成できています。
カスタマイズする
ここは必要性やお好みに合わせて設定してみてください。
- 起動するポートを変更する
...
"scripts": {
- "dev": "next dev",
+ "dev": "next dev -p 3001",
...
- 末尾コロンつけないようにする
設定変更したらyarn format
を実行してフォーマットしておきます。
{
"semi": false,
"singleQuote": true,
+ "trailingComma": "none"
}
- .eslintrc.jsonの設定の表記を数字から英語にする
数字だと設定がわかりにくいです。
0 --> "off"
1 --> "warn"
2 --> "error"
-
npm install
コマンドの実行を禁止する
yarn.lock
が作成された状態でnpm install
するとpackage-lock.json
が作成されてlockファイルが2種類になってしまいます。
プロジェクトのルートディレクトリに.npmrc
を作成
engine-strict=true
...
+ "engines": {
+ "npm": "npmではなくyarnを使ってください"
+ },
"dependencies": {
...
SCSS導入
$ yarn add sass
sassパッケージをインストールするだけで終わりです。Next.jsでは大変ありがたいことにこれだけでScssを使用することが可能になります。また、Next.jsでは{任意の名前}.module.scss
というファイル名にすることでSCSS Modulesとしてscopedにスタイルを当てることができます。
なぜ、Tailwind CSSではなく、CSS Modulesの形式を採用したのかといいますと、デザイナーの開発への参加しやすさを考慮しています。CSS(SCSS)ファイルとしてTSXファイルから分離されていることで、スタイルのコーディングのハードルを下げています。また、TSX自体もスタイルの定義が減って読みやすくなります。ただ、気持ち的な理由ではあるので、どちらを採用するかはお好みで選べば良いと思っています。
Storybook導入
$ npx sb init
上記のコマンドを実行するだけでStorybookが起動できるようになります。このコマンドは多くの人が使うであろうアドオンも一緒にセットアップしてくれます。詳しくはこちら
$ yarn storybook
上記のコマンドを実行することでStorybookが起動します。コマンドを実行するだけで自動的にStorybookのページをブラウザで表示してくれるはずです。自動的に表示されない場合は、 http://localhost:6006 にアクセスしてください。以下の画像のように表示されていればOKです。
しかし、このままだとESLintの設定に引っかかるので修正していきます。
まずは、フォーマットしましょう。
$ yarn format
自動整形されなかったところは手動で直してESLintに引っかからないようにしましょう。exampleが不要であれば、storiesディレクトリごと削除してしまっても良いです。その場合は、.storybook/main.js
でstories
に設定しているパスを削除しておきましょう。逆に、componentsディレクトリを作成してそこにストーリーファイルを置きたいという場合はstories
にパスを設定する必要があります。
以下は修正の例です。良しなに対応してもらえればと思います。
...
export interface PageProps {
- user?: {}
+ user?: Record<string, unknown>
...
- such data from the "args" of child component stories
+ such data from the {'"args"'} of child component stories
...
...
export interface HeaderProps {
- user?: {}
+ user?: Record<string, unknown>
...
StorybookでScssを読み込めるようにする
$ yarn add -D css-loader sass-loader style-loader
必要なローダーをインストールします。そして、インストールしたローダーをStorybookで使うように設定します。
+const path = require('path')
...
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
+ webpackFinal: async (config) => {
+ config.module.rules.push({
+ test: /\.scss$/,
+ use: ['style-loader', 'css-loader', 'sass-loader'],
+ include: [
+ path.resolve(__dirname, '../styles'),
+ path.resolve(__dirname, '../components'),
+ path.resolve(__dirname, '../node_modules')
+ ]
+ })
+
+ return config
+ }
}
next/imageがStorybookで使えないので置き換える設定を追加
+import * as nextImage from 'next/image'
...
+Object.defineProperty(nextImage, 'default', {
+ configurable: true,
+ value: (props) => {
+ const { width, height } = props
+ const ratio = (height / width) * 100
+ return (
+ <div
+ style={{
+ paddingBottom: `${ratio}%`,
+ position: 'relative'
+ }}
+ >
+ <img
+ style={{
+ objectFit: 'cover',
+ position: 'absolute',
+ width: '100%',
+ height: '100%'
+ }}
+ {...props}
+ />
+ </div>
+ )
+ }
+})
カスタマイズする
- Storybook上で任意の画面サイズのプレビューを見れるようにする
export const parameters = {
- actions: { argTypesRegex: '^on[A-Z].*' }
+ actions: { argTypesRegex: '^on[A-Z].*' },
+ viewport: {
+ viewports: {
+ iphone5S: {
+ name: 'iPhone 5S・SE1',
+ styles: {
+ width: '320px',
+ height: '568px'
+ }
+ },
+ iphone8: {
+ name: 'iPhone 8・SE2',
+ styles: {
+ width: '375px',
+ height: '667px'
+ }
+ }
+ }
+ }
}
- Storybookででwebアクセシビリティの確認をできるようにする
$ yarn add -D @storybook/addon-a11y
アドオンのパッケージをインストールします。インストールしたアドオンをStorybookに適用します。
...
- addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
+ addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-a11y'],
...
@storybook/addon-a11yはコンポーネントがWebアクセシビリティを準拠しているか確認することができます。昨今、提供するプロダクトがWebアクセシビリティを準拠していることを求められるようになりつつあるので、このアドオンを使ってコンポーネントに警告が出たら、都度対応してみることから始めてみると良いのかもしれません。
GitHub Actionsを用いたStorybookの自動デプロイ設定
これを行うためには、GitHubにリポジトリを作成して、ここまでで作成したコードをpushする必要があります。GitHub Actionsを用いてmainブランチにマージされたタイミングでGitHub PagesにStorybookを自動デプロイさせます。
Storybookの自動デプロイの設定によって、Storybookが埃を被りにくくなり、開発環境をセットアップしていない人にも、すぐに開発状況を視覚的に共有することが可能です。
つまり、効率的な開発を持続するメリットと開発者に閉じがちな情報をオープンにしてコミュニケーションを円滑にするメリットがあります。
Storybookのビルドコマンドを設定
...
- "build-storybook": "build-storybook"
+ "build-storybook": "build-storybook -o build -s ./stories/assets"
...
-o
はビルドするディレクトリを指定していて、-s
は静的なファイルをビルドに含めるためにパスの指定をしています。
GitHub Actionsを用いたCIによるStorybookの自動デプロイ設定
.github/workflows/deploy.yml
というファイルを作成してください。
name: deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: install packages
run: yarn install
- name: build storybook
run: yarn build-storybook
- name: deploy storybook
uses: JamesIves/github-pages-deploy-action@4.1.0
with:
branch: gh-pages
folder: build
GitHub Actionsによるデプロイを行うためにJamesIves/github-pages-deploy-actionを使用しています。
重要なポイントとして、build-storybookする時のディレクトリ指定とGitHub Actionsでのdeploy storybookのフォルダー指定は同じである必要があります。今回は両方ともbuild
ディレクトリを指定しています。
on:
push:
branches:
- main
上記の設定はmainブランチにpush(merge)した時のみGitHub Actionsのjobが実行される設定です。この設定をしておかないとPRを作成するため、もしくはPRにcommitを積むためのpushを行うたびにbuildが実行されて非常に冗長な処理が発生します。
GitHubリポジトリのGitHub Pagesの設定
リポジトリのSettingsタブを押して最初に表示されているOptionsのページをスクロールすると以下の画像のようにGitHub Pagesという項目があるので画像のようにBranchをgh-pages
、パスは/(root)
を選択してください。
一度mainブランチにPRをマージしてgh-pagesブランチを生成した後でないとBranchにgh-pages
を選択できないかもしれません。
GitHub Actions設定のyamlファイルでbranch: gh-pages
を指定したので、gh-pagesブランチでビルドされます。これによって、mainブランチにはビルドされたファイルが置かれないので、ローカルにpullされることもありません。
GitHub Pagesの項目の設定をするとすぐ上にURLが表示されるので、リポジトリのホーム画面にこのURLを設定しましょう。
画面右上のAboutの歯車アイコンをクリックしてURLを設定してください。これでリポジトリのホーム画面にStorybookをデプロイしたURLが表示されて、1クリックで最新のStorybookを見ることが可能になりました。
今回実装したGitHubのリポジトリ
Discussion