Next.js(v11.1)+TypeScript+Tailwind+SASS+Storybook+Jestのボイラープレート
先日、Next.js 11.1がリリースされました。
Storybookの最新版インストールを試してみたのですが、中々うまくいかなかったりしたので、環境の整理ついでにボイラープレートとしてまとめてみました。
構成は以下のようになっています。
- yarn
- Next.js v11.1.0
- ESLint v7.32.0(Next.js v11.1から含まれるように)
- TypeScript v4.4.2
- Tailwind v2.2.8
- Storybook v6.3
- Prettier v2.3.2
- Jest v27.1.0
コードの全体はこちらです。
Next.js インストール
npx create-next-app --ts nextjs-ts-storybook-tailwind
rm package-lock.json
yarn install
yarn dev
Tailwindの設定
Tailwindのドキュメントに沿って入れていきます。
https://tailwindcss.com/docs/guides/nextjs
yarn add -D tailwindcss@latest postcss@latest autoprefixer@latest
npx tailwindcss init -p
以下のようにファイルが作られます。
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
module.exports = {
purge: ["./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}"],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
Next.jsのグローバルのCSSの設定に、Tailwindの設定を入れます。
元にあった値は使わないので、まるごと入れ替えです。
@tailwind base;
@tailwind components;
@tailwind utilities;
サンプルページのスタイルの設定はガラリと変わるので、Next.jsのTailwindのサンプルから拝借。
https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss
(https://github.com/vercel/next.js/blob/canary/examples/with-tailwindcss/pages/index.js)
画像の部分はESLintのエラーが出るので、気になる場合は以下のように差し替え。
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
Home.modules.css
は不要になるので削除します。
Sassの設定
SCSSを使うため、Next.jsのドキュメントに沿ってSASSを入れて行きます。
https://nextjs.org/docs/basic-features/built-in-css-support#sass-support
yarn add sass
/** @type {import('next').NextConfig} */
const path = require("path");
module.exports = {
reactStrictMode: true,
sassOptions: {
includePaths: [path.join(__dirname, "styles")],
},
};
styles/globals.css
→ styles/globals.scss
に変更。
pages/_app.tsx
のように、CSSをimportしている箇所も合わせて修正。
Storybookの設定
初期設定やサンプルのインストールは以下のコマンドで出来ます。
npx sb init
インストール完了後、yarn storybook
のように起動コマンドの例が表示されますが、Tailwind(PostCSS)やSASSの設定をしないと起動出来ないので先に設定を行います。
Error: PostCSS plugin tailwindcss requires PostCSS 8.
おそらく、Next.jsの内部でWebpackが色々やっていることを、Storybook側でもやらないといけないようです。
この手順では、以下の設定を追加していきます。
- StorybookをWebpack5 で起動出来るようにする
- PostCSS/SASSのloaderを追加する
@storybook/addon-postcss
などStorybook用のプラグインはありますが、パッケージのバージョンの問題が色々発生するので、この方法を選びました。
yarn add -D webpack @storybook/builder-webpack5 @storybook/manager-webpack5
yarn add -D style-loader css-loader postcss-loader sass-loader
module.exports = {
stories: [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
core: {
builder: "webpack5",
},
webpackFinal: (config) => {
config.module.rules.push({
test: /\.scss$/,
sideEffects: true,
use: [
"style-loader",
{
loader: "css-loader",
options: {
importLoaders: 2,
},
},
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [require("tailwindcss"), require("autoprefixer")],
},
},
},
{
loader: "sass-loader",
options: {
sourceMap: true,
},
},
],
});
return config;
},
};
import "../styles/globals.scss"
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}
StorybookのサンプルファイルをSCSSに変更します。
stories/button.css ⇒ stories/button.scss
stories/Button.tsx のimportも直します。
Tailwindが効いていることを確認するため、スタイルを入れ替えてみます。
.storybook-button--primary {
color: white;
background-color: #1ea7fd;
}
.storybook-button--primary {
@apply text-white bg-blue-600
}
設定に問題なければ、yarn storybook
で以下のように表示されます。
Prettierの設定
https://prettier.io/docs/en/install.html
yarn add -D prettier eslint-config-prettier
echo {}> .prettierrc.json
touch .prettierignore
Prettierの対象にしないディレクトリ・ファイルをここで指定。
.next
node_modules
Prettierの設定を入れます。内容はお好みで。
{
"endOfLine": "lf",
"semi": false,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2
}
Eslintと干渉しないように設定を追加。
{
"extends": ["next/core-web-vitals", "prettier"]
}
yarn prettier --check .
で対象のファイルを確認。対象にしたくないファイルが含まれていたら、.prettierignore
に追加します。
yarn prettier
で実行できるように、package.json
のscriptsに以下の設定を追加します。
"prettier": "prettier --write .",
Jestの設定
単純な関数のテストが出来る所までの設定です。
ReactやHooksのテストや、Next.jsのCypressを用いたEnd-toEndのテスト などは追加で設定が必要です。
yarn add -D jest @types/jest ts-jest ts-node
Jestの起動コマンドをpackage.json
のscripts
に追加。
"test": "jest"
Jestの設定を入れます。TypeScriptでも書けるようです。
import type { Config } from '@jest/types'
const config: Config.InitialOptions = {
roots: ['<rootDir>/'],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
},
}
export default config
試しに、以下のようにサンプルコードとテストを追加してみます。
export const add = (a: number, b: number) => a + b
import { add } from './sum'
test('add', () => {
expect(add(1, 1)).toEqual(2)
})
yarn test
でテストを実行できます。
yarn test
> yarn run v1.22.11
> $ jest
> PASS lib/sum.test.ts
> ✓ add (3 ms)
>
> Test Suites: 1 passed, 1 total
> Tests: 1 passed, 1 total
> Snapshots: 0 total
> Time: 1.995 s, estimated 3 s
> Ran all test suites.
> ✨ Done in 6.22s.
Importのエイリアスの設定
エイリアスの設定でコンポーネントのimportの相対パスを以下のように省略出来ます。
import '../styles/globals.scss'
import '@/styles/globals.scss'
tsconfig.json、Storybook、Jestのそれぞれで設定する必要があります。
"compilerOptions": {
...
"baseUrl": ".",
"paths": {
"@/*": ["*"]
}
},
const path = require('path')
module.exports = {
~~省略~~
webpackFinal: (config) => {
config.resolve.alias = {
...config.resolve.alias,
'@': path.resolve(__dirname + '/..'),
}
~~省略~~
import type { Config } from '@jest/types'
const config: Config.InitialOptions = {
roots: ['<rootDir>/'],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
},
moduleNameMapper: {
'@/(.+)': '<rootDir>/$1',
},
}
export default config
Discussion