現在開発しているアプリのフロント実装をNext.js × TypeScript・Tailwind CSSでやっています。
今後もしばらくこの構成が鉄板になりそうなので、関連ライブラリのセットアップも含めてボイラープレート化することにしました。
環境
- React 17.0.2
- yarn 1.22.11
- Next.js 11.1.0
- TypeScript 4.4.2
- Tailwind 2.2.9
- ESLint 7.32.0
- Prettier 2.3.2
- Jest 27.1.0
##1. Next.jsとTypeScriptでプロジェクトの開始
Next.jsとTailwind CSSのセットでインストールする方法もありますが、TypeScriptとのセットでやった方がTypeScript周りの設定が楽になるのでこちらでいきます。(Tailwind CSSについては後ほど設定していきます。)
npx create-next-app --ts my-app[アプリ名]
#or
yarn create next-app --ts my-app[アプリ名]
参考:
※ちなみに、Tailwind CSSの公式にあるやり方も踏まえ yarn create next-app -e with-tailwindcss —-ts
みたいに同時にインストールしてみようとしましたが、最初に記述した方しか適用されておらず同時インストールは無理でした。
ついでにsrcフォルダを作成し配下にファイルを移動
mkdir src
mv pages src
mv components src
mkdir src/lib
##2. 絶対パス/パスエイリアスの指定
ts.configの設定
※ tsconfig.jsonの変更後はdev serverをリフレッシュしないと反映されないので注意
{
"compilerOptions": {
{...}
"baseUrl": ".",
"paths": {
"@components/*": ["components/*"],
"@styles/*": ["styles/*"],
"@pages/*": ["pages/*"],
"@hooks/*": ["hooks/*"]
}
{...}
}
こんな感じ
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"baseUrl": ".",
"paths": {
"@components/*": ["components/*"],
"@styles/*": ["styles/*"],
"@pages/*": ["pages/*"],
"@hooks/*": ["hooks/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
参考:
この中身についてはこちらの記事が役に立つ
適宜targetやlibなど設定しておく(本題ではないので飛ばします。興味がある方はご自身で調べてみてください。)
##3. Tailwind CSS と JIT Modeの設定
※注) 1月24日(月)現在はv3のため設定が異なります。
やり方は3つあり、それぞれ簡略化されて設定が楽になっています。
↓詳しくはこちらをご覧ください。
https://tailwindcss.com/docs/installation
これは色々と設定が必要になります。
$ npm install -D tailwindcss@latest postcss@latest autoprefixer@latest postcss-cli
#or
$ yarn add -D tailwindcss@latest postcss@latest autoprefixer@latest postcss-cli
https://tailwindcss.com/docs/guides/nextjs#install-tailwind-via-npm
tailwind.config.tsとpostcss.config.jsファイルの作成をしていきます。
npx tailwindcss init -p
postcss.config.js
ファイルは基本的にそのままで大丈夫です。
簡単に中身だけ説明しておくと
module.exports = {
plugins: {
tailwindcss: {}, //PostCSSのプラグインとしてTailwind CSSを使用する設定
autoprefixer: {}, //-webkitなどベンダープレフィックスを自動でつけてくれる(様々なブラウザ対応のため)
},
};
tailwind.config.ts
の中身を設定
JITモードに設定し、不要なCSSを削除するためのpurge設定をする。
purgeとは、ここに設定されているファイル内で使用されているCSSのみをbuild時に使用する最適化手法です。
初期設定は18万行のコードを全て読み込むことになっているため、ファイルサイズが大きくなってしまうのです。
※パス指定はご自身のファイル構造に従ってください。ここが違うとCSSが正しく読み込まれません。
module.exports = {
mode: 'jit',
purge: ['./src/pages/**/*.{js,ts,jsx,tsx}', './src/components/**/*.{js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
Tailwind CSSで生成されたCSSを読み込むために styes/Home.module.cssファイルを削除しておきます。
また、styles/global.module.cssはbuild時にtailwind.cssを読み込む際に、動的に反映するファイルとして残しておきましょう。(詳細は後ほど説明します)
そして、styes/tailwind.cssファイルを作成して中身に以下を記述します。
@tailwind base;
@tailwind components;
@tailwind utilities;
もしも@tailwindのルールがないってエラーが出たら
以下の方法で解決
PostCSSのプラグインを導入(VS Codeのみ)
PostCSS Language Support - Visual Studio Marketplace
How to add a @tailwind CSS rule to css checker
詳しくは記事化しました↓
ついでにTailwind CSSの入力補完をしてくれる、Tailwind CSS IntelliSenseも導入しておきましょう。
このままではTailwind CSSの記述を変更してもJITモードでリアルタイムに変更されないので、そのための設定をする必要があります。
package.jsonの設定を加える
{
...
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"css:dev": "TAILWIND_MODE=watch postcss src/styles/tailwind.css -o src/styles/globals.css --watch",
"css:build": "postcss src/styles/tailwind.css -o src/styles/globals.css",
},
...
ちょっとわかりづらいかもしれませんが中身はこうなっています。
TAILWIND_MODE=watch(watchモードにする)
postcss [対象となるファイル] -o(outputの略)[アウトプット先のファイル]
The css:dev script will run PostCSS in watch mode, and Tailwind will listen for changes in our component classes to build new utilities classes on the go. Once everything is done, we can build the final version of the CSS by running our css:build script.
watchモードとは
ファイルを監視して変更があったらビルドを再実行する機能のこと。watchモードでは基本的にキャッシュが有効になるため、ビルド時間が短くなる。
例えば、pages/index.tsx内のTailwind CSSを監視して、そこに変更があったときにbuildを改めて走らせてくれる様になります。
では、npm run dev
もしくはyarn dev
してlocallhostを立ち上げてください。
Tailwind CSSの記述をしたときにglobals.css(postCSSのファイル)に動的に追記されればOKです。
ブラウザでも動的に変わるか確認しましょう。
変わっていなければ上記の設定の記述に誤りがないか、パスの指定はあっているかなど見直してみてださい。
・左側のファイル: src/pages/index.tsx
・右側のファイル:globals.css
尚、追加されたCSSの記述はそのまま残ってしまいますが、build時に使わないファイルは消去してくれるので yarn dev
し直すと不要な記述が消えていることが確認できます。
##4. ESLintとPrettierの導入
ESLintの導入
Next.js のバージョン 11 に対応
Next.js では、バージョン 11 から、ESLint がデフォルトで搭載されています。ESLint に関連するインストール済みのパッケージは以下 2 つです。
"devDependencies": {
"eslint": "7.32.0",
"eslint-config-next": "11.1.0",
...
}
Next.js の新規プロジェクト作成時に生成された .eslintrc.json
は ESLint の設定ファイルを意味しており、デフォルトで eslint-config-next の設定が適用されています(ルールの詳細は後述)
// .eslintrc
{
"extends": ["next", "next/core-web-vitals"]
}
次に拡張機能としてESLintを導入します。
VS Code 上でリアルタイムに ESLint で構文解析でき、コードの保守性が高められる様になります。
しかし、ファイル数が多いとき、全てのファイルを VS Code で確認するのは大変なため、対象ファイルを一括で ESLint による構文解析したい場合は、lint
コマンドを使用する。
npm run lint
#or
yarn lint
また Next.js にデフォルトで搭載された ESLint では、lint コマンドの対象ディレクトリが 以下 3 つに限定されているそうです。
pages
lib
components
そのためおすすめはsrcディレクトリにpagesを含むソースコードを管理して、lintコマンドオプション —-dir src
でsrcディレクトリ配下のファイルが対象になる。
#例)
src
├── pages
│ ├── _app.tsx
│ ├── api
│ └── index.tsx
├── components
│ └── hoge.tsx #任意のディレクトリ・ファイル構成
└── styles
├── Home.module.css
└── globals.css
次に、package.json
の lint
コマンドに対し、--dir src
を記述してください。
"scripts": {
...
"lint": "next lint --dir src"
...
},
Prettierの導入
チーム開発する際に、コード整形のルールをコードベースで設定することでばらつきを防ぐことができる。
Prettier におけるコード整形のルールを記述する主な方法は、以下 2 つ
-
.prettierrc
ファイルを作成し、ルールを記述 -
package.json
にルールを記述
省ファイルを意識してpackage.jsonに記述するのがおすすめ
"prettier": {
"trailingComma": "all",// 末尾のカンマあり
"tabWidth": 2,// tab の長さは半角スペース 2 つ
"semi": true,// セミコロンあり
"singleQuote": true,// シングルクォーテーションに統一
"jsxSingleQuote": true,//jsx もシングルクォーテーションに統一
"printWidth": 100 // 1 行の最大文字数 100
}
次に、
以下 2 つのパッケージをインストールしましょう。
-
prettier
各ファイルをコマンドで、フォーマットできるようにするため -
eslint-config-prettier
ESLint と Prettier のコード整形がバッティングしないようするため
npm install -D prettier eslint-config-prettier
#or
yarn add -D prettier eslint-config-prettier
さらに.eslintrc.jsonに下記のように設定することでESLintとPrettierのコード整形がバッティングをしない様にできる
{
"extends": ["next", "next/core-web-vitals", "prettier"]
}
最後に、Prettier でフォーマットするコマンドを package.json
に記述しましょう。
補足
--write
: フォーマット整形
--ignore-path .gitignore
: .gitignore に含まれているファイルはコード整形の対象外
'./**/*.{js,jsx,ts,tsx,json,css}'"
:対象ファイルの拡張子を指定
"scripts": {
"lint": "next lint --dir src",
"lint:fix": "eslint src --ext .js,jsx,.ts,.tsx --fix",
"format": "prettier --write --ignore-path .gitignore './**/*.{js,jsx,ts,tsx,json,css}'"
}
prettierがCLIで効くか
確認する
npm run format
#or
yarn format
##5. JestとReact Testing Libraryのセットアップ
yarn add -D @testing-library/dom @testing-library/jest-dom @testing-library/react @testing-library/user-event babel-jest jest jest-dom node-mocks-http ts-jest ts-loader
Project folder 直下に".babelrc"ファイルを作成して下記設定を追加
$ touch .babelrc
{
"presets": ["next/babel"]
}
package.json に jest の設定を追記
"jest": {
"testPathIgnorePatterns": [
"<rootDir>/.next/",
"<rootDir>/node_modules/"
],
"moduleNameMapper": {
"\\.(css)$": "<rootDir>/node_modules/jest-css-modules"
}
}
package.jsonに test scriptを追記
"scripts": {
...
"test": "jest --env=jsdom --verbose"
},
prettierの設定 : .prettierrcを作成しsettingsでRequire Config + Format On Saveにチェック
$ touch .prettierrc
.prettierrc
{
"singleQuote": true,
"semi": false
}
Test動作確認
__tests__
フォルダとHome.test.tsx
ファイルの作成
/**
* @jest-environment jsdom
*/
import { render, screen } from '@testing-library/react'
import '@testing-library/jest-dom'
import Home from '../pages/index'
it('Should render title text', () => {
render(<Home />)
expect(screen.getByText('Next.js!')).toBeInTheDocument()
})
yarn test -> テストがPASSするか確認
PASS __tests__/Home.test.tsx
✓ Should render hello text (20 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.728 s, estimated 2 s
次の様なエラーが出たら、
Hello
などtextのタグにスペースを開けずにテストしたいテキストのみを記述する。TestingLibraryElementError: Unable to find an element with the text: TailwindCSS.
This could be because the text is broken up by multiple elements.
In this case, you can provide a function for your text matcher to make your matcher more flexible.
これで先程のコマンドをもう一度実行して、パスしていれば完了です。
※とりあえず仮のテストなのでテスト内容によって書き換えが必要です
##最後に
これでようやくできました。色々設定が必要ですが、こうしてまとめておくと今後はプロジェクトのセットアップを省力化できるはずです。
また今回の設定はフロントエンド諸学者である私一個人が調べて導入したものであって、全ての人に当てはまるベストプラクティスではないことを念の為お伝えしておきます。
必要に応じてカスタマイズをしてみてください。
自分も、storybookなどまだ試したことはないけれど入れておきたいライブラリがあるので、確認できたら随時更新していきたいと思います。
また上記の設定で、よりベターな選択肢があれば積極的に情報共有してもらえると嬉しいです。
不明点や誤りがある場合も遠慮なく言っていただければ幸いです。
##参考資料