Next.js + TypeScriptの環境構築（ツール導入編）

## はじめに

Next.js で環境構築する機会があったのでメモとしてまとめます。
なるべく一般的なライブラリ選定やその設定などにして汎用性を持たせることを意識しました。
その際、エラーが出ることもあったので解消方法も書きました。
一部主観で選定している部分もあるので、一つの参考としてご覧いただければと幸いです。

## 前提
### JavaScript のコードフォマットについて
今回、JavaScript のコードフォーマットは Prettier は使用せず JavaScript Standard Style を使用します。
一般的なスタイルを使用したほうが良いと考えているのとツールを増やしたくないためです。

### CSS
スタイルは今回 CSS Modules を使用します。
Next.js で標準サポートされているから、というのは大義で、個人的に CSS in JS を使用するよりも CSS は CSS（Sass） として書いたほうがメリットを感じているためです。

## 環境
以下は導入時点での各パッケージのバージョンです。
パッケージのメジャーアップデートが発生している場合は破壊的な変更がないか各パッケージの Release note をよくご確認ください。

- Node.js v16.14.0
- npm v8.3.1
- yarn v1.22.4
- Next.js v12.1.0
- TypeScript v4.5.5
- sass v1.49.8
- eslint v8.9.0
- stylelint v14.5.3
- jest v27.5.1
- husky v7.0.4
- lint-staged v12.3.5

今回は CLI のコマンドは yarn を使って実行していきます。
npm コマンドを使用したい方は適宜読み替えてください。

## Next.js のインストール

公式サイト：[Basic Features: TypeScript | Next.js](https://nextjs.org/docs/basic-features/typescript)
今回は TypeScript 版でインストールします。
まずはインストールしたいディレクトリへ移動します。

```bash
cd [インストールしたいディレクトリ]
```

次に Next.js（TypeScript 版）をインストールします。

```bash:bash
npx create-next-app@latest --ts
# or
yarn create next-app --typescript
```

CLI 上でプロジェクト名を質問されるので任意の名前を入力して Enter を押します。
※ 入力したものがディレクトリ名になってその配下に Next.js のディレクトリ・ファイル群が作成されます。

```bash
What is your project named? › my-app
```

しばらくすると Next.js のインストールが完了します。
インストール完了後、試しに起動してみます。

```bash:bash
yarn dev
```

以下がCLIで表示されたら実際にブラウザのアドレスバーに `http://localhost:3000を` 入力して Enter を押して確認します。

```bash
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
```

「 Welcome to Next.js! 」と表示されたページが開かれれば成功です。

## Sassのインストール

公式サイト：[Basic Features: Built-in CSS Support | Next.js](https://nextjs.org/docs/basic-features/built-in-css-support)

次に Sass をインストールします。

```bash:bash
yarn add -D sass
```

インストール後、Sass が動作するか試しに変えてみます。
`Home.module.css` を scss ファイルに変更します。

```bash:bash
mv styles/Home.module.css styles/Home.module.scss
```

`pages/index.tsx` のインポートを css ファイルから scss ファイルに変更します。

```jsx:pages/index.tsx
- import styles from '../styles/Home.module.css'
+ import styles from '../styles/Home.module.scss'
```

`styles/Home.module.scss` の内容を Sass 独自の書き方に変更します。

変更前

```scss:styles/Home.module.scss
.title a {
  color: #0070f3;
  text-decoration: none;
}

.title a:hover,
.title a:focus,
.title a:active {
  text-decoration: underline;
}
```

変更後

```scss:styles/Home.module.scss
.title a {
  color: #0070f3;
  text-decoration: none;

  &:hover,
  &:focus,
  &:active {
    text-decoration: underline;
  }
}
```

エラーが出ず title のリンクをホバー時に下線が表示されれば正常に動作しています。

## ESLintの設定

公式サイト：[Basic Features: ESLint | Next.js](https://nextjs.org/docs/basic-features/eslint)

ESLint は Next.js v11.0.0 以降では標準実装されています。
以下を実行して動作することを確認します。

```bash:bash
yarn lint
```

実際には `next lint` コマンドを実行しています。
Next.js では `eslint` コマンドを実行するとエラーになってしますようなので、今回は `next lint` を ESLint の実行コマンドとして使用します。
Next.js として `.eslintrc.json` に書かれている ESLint の設定は以下です。

```bash:.eslintrc.json
{
  "extends": "next/core-web-vitals"
}
```

React としての設定をするため、 ESLint を改めて初期化します。

```bash:bash
yarn create @eslint/config
```

CLI 上でいくつか質問されるので答えていきます。

```bash
? How would you like to use ESLint? … 
  To check syntax only
  To check syntax and find problems
❯ To check syntax, find problems, and enforce code style
```

「構文チェック」「問題の発見」「スタイル強制」をすべておこないたいので、カーソルキーで一番下の項目を選んで `Enter` キーを押します。

```bash
? What type of modules does your project use? … 
❯ JavaScript modules (import/export)
  CommonJS (require/exports)
  None of these
```

モジュールのインポート形式には `import/export` を選択します。

```bash
? Which framework does your project use? … 
❯ React
  Vue.js
  None of these
```

今回は React を選択します。

```bash
? Does your project use TypeScript? › No / Yes
```

TypeScript を使用するので Yes を選択します。

```bash
? Where does your code run? …  (Press <space> to select, <a> to toggle all, <i> to invert selection)
✔ Browser
  Node
```

Browser を選択します。

```bash
? How would you like to define a style for your project? … 
❯ Use a popular style guide
  Answer questions about your style
```

ポピュラーなスタイルガイドを選択します。

```bash
? Which style guide do you want to follow? … 
  Airbnb: https://github.com/airbnb/javascript
❯ Standard: https://github.com/standard/standard
  Google: https://github.com/google/eslint-config-google
  XO: https://github.com/xojs/eslint-config-xo
```

Standard を選択します。

```bash
? What format do you want your config file to be in? … 
❯ JavaScript
  YAML
  JSON
```

JavaScript を選択します。

```bash
Checking peerDependencies of eslint-config-standard@latest
? The style guide "standard" requires eslint@^7.12.1. You are currently using eslint@8.9.0.
  Do you want to downgrade? › No / Yes
```

パッケージの依存関係の都合上、 ESLint をダウングレードする必要がある場合に上記の表示が出ます。 Yes を選択します。

```bash
The config that you've selected requires the following dependencies:

eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest eslint-config-standard@latest eslint@^7.12.1 eslint-plugin-import@^2.22.1 eslint-plugin-node@^11.1.0 eslint-plugin-promise@^4.2.1 || ^5.0.0 @typescript-eslint/parser@latest
? Would you like to install them now with npm? › No / Yes
```

必要なパッケージの一覧が表示されインストールしたいか聞かれるので Yes を選択します。
インストールが完了したら以下のファイルが作成されます。

```js:.eslintrc.json
module.exports = {
  env: {
    browser: true,
    es2021: true
  },
  extends: [
    'plugin:react/recommended',
    'standard'
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true
    },
    ecmaVersion: 'latest',
    sourceType: 'module'
  },
  plugins: [
    'react',
    '@typescript-eslint'
  ],
  rules: {
  }
}
```

ここで改めて `yarn lint` を実行します。

```bash:bash
yarn lint
```

すると以下のエラーが出ます。

```bash
./pages/_app.tsx
4:15  Error: Missing space before function parentheses.  space-before-function-paren
5:10  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope

./pages/api/hello.ts
8:32  Error: Missing space before function parentheses.  space-before-function-paren

./pages/index.tsx
8:5  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
9:7  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
10:9  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
11:9  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
12:9  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
15:7  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
16:9  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
17:22  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
20:9  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
22:11  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
25:9  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
26:11  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
27:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
28:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
31:11  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
32:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
33:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
36:11  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
40:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
41:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
44:11  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
48:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
49:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
56:7  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
57:9  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
63:11  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope
64:13  Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope

info  - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/basic-features/eslint#disabling-rules
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
```

`Error: 'React' must be in scope when using JSX  react/react-in-jsx-scope` というエラーは、 React v17 からは JSX を使っているファイルに `import React from 'react'` を書かなくても OK になったようです。
しかし ESLint で `plugin:react/recommended` を指定していると JSX で `import React from 'react'` が無いとエラーになってしまってしまうようです。
ESLint のこのエラーは検知しなくても良さそうです。
`.eslintrc.js` に`'react/react-in-jsx-scope': 'off',` を追加して無効にします。

```js:.eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true
  },
  extends: [
    'plugin:react/recommended',
    'standard'
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      jsx: true
    },
    ecmaVersion: 'latest',
    sourceType: 'module'
  },
  plugins: [
    'react',
    '@typescript-eslint'
  ],
  rules: {
    'react/react-in-jsx-scope': 'off', // 追加
  }
}
```

参考：[React v17 create-react-app で作ったアプリで ESLint に怒られまくった](https://chaika.hatenablog.com/entry/2020/12/04/083000)

`Error: Missing space before function parentheses.` は関数括弧の前にスペースがないためなので、スペースを追加します。

pages/_app.tsx（ `MyApp` の後にスペースを追加）

```ts:pages/_app.tsx
function MyApp ({ Component, pageProps }: AppProps) {
```

pages/api/hello.ts（ `handler` の後にスペースを追加）

```ts:pages/api/hello.ts
export default function handler (
```

改めて `yarn lint` を実行します。

```bash:bash
yarn lint
```

エラーがすべて出なくなっていれば OK です。
VSCode で自動でフォーマットするようにしたい場合、拡張機能の追加と `.vscode/settings.json` ファイルを作成します。

拡張機能

- ****[StandardJS - JavaScript Standard Style](https://marketplace.visualstudio.com/items?itemName=standard.vscode-standard)****
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)

`.vscode/settings.json` ファイルを作成します。

```bash:bash
mkdir .vscode
touch settings.json
```

`.vscode/settings.json` ファイルには以下を書きます。

```js:.vscode/settings.json
{
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "editor.defaultFormatter": "standard.vscode-standard",
  "eslint.format.enable": false,
  "editor.formatOnSave": true,
  "editor.lineNumbers": "on",
  "editor.rulers": [80],
  "editor.wordWrap": "on",
  "eslint.packageManager": "yarn",
  "files.insertFinalNewline": true,
  "files.trimTrailingWhitespace": true,
  "npm.packageManager": "yarn",
  "typescript.enablePromptUseWorkspaceTsdk": true
}
```

上記を保存すると ESLint でエラーになる部分に赤の下線が表示され、保存するとフォーマットを調整できるものは自動で調整してくれます。

参考：

- ****[prettier,eslintを導入する際にハマったこと2021新年](https://zenn.dev/ryusou/articles/nodejs-prettier-eslint2021)****
- [TypeScrip + ESLint + Prettier + Jest のプロジェクト設定にハマる](https://chaika.hatenablog.com/entry/2021/07/10/083000)

`next.config.js` と `.eslintrc.js` を開くと1行目に赤い下線が表示され以下のようなエラーが出るかと思います。

```bash
Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser.
The file does not match your project config: next.config.js.
The file must be included in at least one of the projects provided.
```

これは主にファイルがどこからも import されていない場合に表示されるエラーです。
`.eslintignore` ファイルを作成して対象ファイルを記述します。
まずは `.eslintignore` ファイルの作成します。

```bash:bash
touch .eslintignore
```

`.eslintignore` ファイルには以下を記述します。

```bash:.eslintignore
/.eslintrc.js
/next.config.js
```

これで赤い下線が出なければOKです。

## stylelintの設定

公式サイト：[Getting started | Stylelint](https://stylelint.io/user-guide/get-started)

今回は scss ファイルを対象にするので以下をインストールします。

```bash:bash
yarn add -D stylelint stylelint-config-standard-scss
```

次に `.stylelintrc` ファイルを作成します。

```bash:bash
touch .stylelintrc
```

`.stylelintrc` には以下を記述します。

```bash:.stylelintrc
{
  "extends": "stylelint-config-standard-scss"
}
```

`package.json` に以下を追加します。

```js:package.json
"scripts": {
    // 省略
    "stylelint": "stylelint '**/*.scss'"
  },
```

実際にコマンドを実行してみます。

```bash:bash
yarn stylelint
```

すると Next.js の作成時に自動で作成されたファイルでエラーが出るのでエラーを確認しながらエラーを解消していきます。

```bash
styles/Home.module.scss
 64:14  ✖  Expected newline after ":" with a            declaration-colon-newline-after       
           multi-line declaration                                                             
 64:21  ✖  Expected newline after "," in a multi-line   value-list-comma-newline-after        
           list                                                                               
 64:29  ✖  Expected newline after "," in a multi-line   value-list-comma-newline-after        
           list                                                                               
 64:31  ✖  Expected quotes around "Lucida Console"      font-family-name-quotes               
 64:45  ✖  Expected newline after "," in a multi-line   value-list-comma-newline-after        
           list                                                                               
 64:47  ✖  Expected quotes around "Liberation Mono"     font-family-name-quotes               
 64:62  ✖  Expected newline after "," in a multi-line   value-list-comma-newline-after        
           list                                                                               
 64:64  ✖  Expected quotes around "DejaVu Sans Mono"    font-family-name-quotes               
 65:5   ✖  Expected quotes around "Bitstream Vera Sans  font-family-name-quotes               
           Mono"                                                                              
 65:29  ✖  Expected newline after "," in a multi-line   value-list-comma-newline-after        
           list                                                                               
 65:31  ✖  Expected quotes around "Courier New"         font-family-name-quotes               
 65:42  ✖  Expected newline after "," in a multi-line   value-list-comma-newline-after        
           list                                                                               
 96:3   ✖  Unexpected longhand value '0 0 1rem 0'       shorthand-property-no-redundant-values
           instead of '0:0 1rem'
```

エラー部分に赤い下線を表示させたいのと自動修正してほしいので拡張機能の追加と.vscode/settings.jsonに設定していきます。

拡張機能：[stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint)

`.vscode/settings.json` に以下を追加します。

```js:.vscode/settings.json
{
  "editor.codeActionsOnSave": {
    // 省略
    "source.fixAll.stylelint": true
  },
  // 省略
  "stylelint.validate": ["css", "scss"],
  "scss.validate": false,
  "css.validate": false,
}
```

設定できたら `styles/Home.module.scss` を開いて保存します。
おそらくすべてのエラーが解消したかと思います。もしエラーがまだ残っているようなら手動で修正していきます。

参考：[Stylelint(14系)をSCSSとVSCodeに導入する2022](https://zenn.dev/knknk98/articles/422034ea94dc78)

プロパティも自動で並び替えしてほしいので、ファイル保存時に自動でプロパティを並び替えてくれるライブラリをインストールします。
並び替えのライブラリはいくつかありますが、今回は stylelint-config-property-sort-order-smacss というライブラリにします。

```bash:bash
yarn add -D stylelint-order stylelint-config-property-sort-order-smacss
```

`.stylelintrc` を以下に書き換えます。

```bash:.stylelintrc
{
  "extends": [
    "stylelint-config-standard-scss",
    "stylelint-config-property-sort-order-smacss"
	]
}
```

参考：[GitHub - hudochenkov/stylelint-order: A plugin pack of order related linting rules for Stylelint.](https://github.com/hudochenkov/stylelint-order)

改めて `stylelint` のコマンドを実行します。

```bash:bash
yarn stylelint
```

すると順番についてのエラーが出ます。

```bash
styles/Home.module.scss
   8:3  ✖  Expected "flex" to come before "padding" in group "box"                    order/properties-order
   9:3  ✖  Expected "display" to come before "flex" in group "box"                    order/properties-order
  12:3  ✖  Expected "align-items" to come before "justify-content" in group "box"     order/properties-order
  20:3  ✖  Expected "justify-content" to come before "border-top" in group "box"      order/properties-order
  21:3  ✖  Expected "align-items" to come before "justify-content" in group "box"     order/properties-order
  27:3  ✖  Expected "align-items" to come before "justify-content" in group "box"     order/properties-order
  28:3  ✖  Expected "flex-grow" to come before "align-items" in group "box"           order/properties-order
  45:3  ✖  Expected "font-size" to come before "line-height" in group "text"          order/properties-order
  56:3  ✖  Expected "font-size" to come before "line-height" in group "text"          order/properties-order
  61:3  ✖  Expected "border-radius" to come before "background" in group "border"     order/properties-order
  62:3  ✖  Expected "padding" to come before "border-radius" in group "box"           order/properties-order
  64:3  ✖  Expected "font-family" to come before "font-size" in group "text"          order/properties-order
  79:3  ✖  Expected "flex-wrap" to come before "justify-content" in group "box"       order/properties-order
  87:3  ✖  Expected "color" to come before "text-align" in group "text"               order/properties-order
  89:3  ✖  Expected "border" to come before "text-decoration" in group "border"       order/properties-order
  91:3  ✖  Expected "transition" to come before "border-radius" in group "animation"  order/properties-order
  92:3  ✖  Expected "max-width" to come before "transition" in group "box"            order/properties-order
  99:3  ✖  Expected "border-color" to come before "color" in group "border"           order/properties-order
 121:5  ✖  Expected "flex-direction" to come before "width" in group "box"
```

`styles/Home.modules.scss` ファイルを開いて保存すると自動で順番が並び変わります。
改めて stylelint のコマンドを実行します。

```bash:bash
yarn stylelint
```

エラーは出なくなったかと思います。
おっと、 `globals.css` が scss ファイルになっていませんでした。
scss ファイルへ変換して `stylelint` のコマンドを実行しましょう。

scss ファイルへ変換します。

```bash:bash
mv styles/globals.css styles/globals.scss
```

`pages/_app.tsx` で読み込んでいるのでインポート元のパスを変更します。

```jsx:pages/_app.tsx
import '../styles/globals.scss' // cssをscssに修正
```

stylelint のコマンドを実行します。

```bash:bash
yarn stylelint
```

ファイルの中身をまだ変更していなければ以下のエラーが出ます。

```bash
styles/globals.scss
 4:3   ✖  Expected "margin" to come before "padding" in group "box"  order/properties-order         
 5:14  ✖  Expected newline after ":" with a multi-line declaration   declaration-colon-newline-after
 5:29  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after 
 5:49  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after 
 5:51  ✖  Expected quotes around "Segoe UI"                          font-family-name-quotes        
 5:59  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after 
 5:67  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after 
 6:11  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after 
 6:22  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after 
 6:24  ✖  Expected quotes around "Fira Sans"                         font-family-name-quotes        
 6:33  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after 
 6:35  ✖  Expected quotes around "Droid Sans"                        font-family-name-quotes        
 6:45  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after 
 6:47  ✖  Expected quotes around "Helvetica Neue"                    font-family-name-quotes        
 6:61  ✖  Expected newline after "," in a multi-line list            value-list-comma-newline-after

error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
```

`styles/globals.scss` を開いて保存し、改めて stylelint のコマンドを実行します。

```bash:bash
yarn stylelint
```

エラーが出なくなれば OK です。

## Jest の設定

公式サイト：****[Setting up Jest (with the Rust Compiler)](https://nextjs.org/docs/testing#setting-up-jest-with-the-rust-compiler)****

Jest を使用するのに必要なパッケージをインストールします。

```bash:bash
yarn add --dev jest @testing-library/react @testing-library/jest-dom
```

`jest.config.js` を作成します。

```bash:bash
touch jest.config.js
```

`jest.config.js` に以下を記述します。

```js:jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  dir: './',
})

const customJestConfig = {
  moduleDirectories: ['node_modules', '<rootDir>/'],
  testEnvironment: 'jest-environment-jsdom',
}

module.exports = createJestConfig(customJestConfig)
```

`jest.config.js` にエラーが出ているので `.eslintignore` に以下を追加します。

```bash:.eslintignore
/jest.config.js
```

typescript-eslint の GitHub で「 TypeScript プロジェクトでは no-undeflint ルールを使用しないことを強くお勧めします。」とあり、実際にこの後のテストコードで書く `test()` や `expect()` がエラーになるので回避する設定をおこないます。

`.eslintrc.js` に以下を追加します。

```js:.eslintrc.js
rules: {
    // 省略
    "no-undef": "off"
  }
```

参考：[I get errors from the `no-undef` rule about global variables not being defined, even though there are no TypeScript errors](https://github.com/typescript-eslint/typescript-eslint/blob/main/docs/linting/TROUBLESHOOTING.md)

`package.json` に Jest の実行コマンドを記述します。

```js:package.json
"scripts": {
    // 省略
    "test": "jest"
  },
```

これで設定が整いました。
次にテストするファイルとテストコードを作成します。

```ts:sum.ts
export default function sum (a: number, b: number) {
  return a + b
}
module.exports = sum
```

```ts:sum.test.ts
import sum from './sum'

test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3)
})
```

Jest のコマンドを実行します。

```bash:bash
yarn test
```

問題なく動作してテストが通っていれば OK です。

## Storybook の設定

公式サイト：[React 向け Storybook のチュートリアル | Storybook Tutorials](https://storybook.js.org/tutorials/intro-to-storybook/react/ja/get-started/)

Storybook のパッケージを追加します。

```bash:bash
npx -p @storybook/cli sb init
```

しばらくすると ESLint プラグインをインストールするか聞かれるので `y` を押します。

```bash
We've detected you are not using our eslint-plugin.

In order to have the best experience with Storybook and follow best practices, we advise you to install
eslint-plugin-storybook.

More info: https://github.com/storybookjs/eslint-plugin-storybook#readme

? Do you want to run the 'eslintPlugin' fix on your project?
```

プロジェクトのルートフォルダーに `.env` という名前で、以下の内容のファイルを作成します。

```bash:.env
SKIP_PREFLIGHT_CHECK=true
```

Storybook を以下のコマンドで起動します。

```bash:bash
yarn storybook
```

[`http://localhost:6006/`](http://localhost:6006/)  で Storybook を起動できたら成功です。

試しにESLint, stylelint を実行して問題ないか確認します。

```bash:bash
yarn lint
yarn stylelint
```

stylelint で以下のエラーが出る場合があります。

```bash
TypeError: Class extends value undefined is not a constructor or null
```

このエラーが出た場合、postcss を明示的にインストールしておく必要があります。
執筆時点での最新版として postcss v8.4.7 をインストールします。

```bash:bash
yarn add -D postcss@8.4.7
```

改めて `yarn stylelint` を実行します。

```bash:bash
yarn stylelint
```

エラーが出なければ OK です。
スナップショットテスト機能を実装します。
必要なパッケージをインストールします。

```bash:bash
yarn add -D @storybook/addon-storyshots react-test-renderer
```

`src/storybook.test.js` ファイルを以下の内容で作成します

```js:src/storybook.test.js
import initStoryshots from '@storybook/addon-storyshots'
initStoryshots()
```

テストしてみます。

```bash:bash
yarn test
```

```bash
console.error
      Unexpected error while loading ./Introduction.stories.mdx: Cannot use import statement outside a module
       Jest encountered an unexpected token
      
      Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
      
      Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
      
      By default "node_modules" folder is ignored by transformers.
      
      Here's what you can do:
       • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
       • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
       • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
       • If you need a custom transformation specify a "transform" option in your config.
       • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
      
      You'll find more details and examples of these config options in the docs:
      https://jestjs.io/docs/configuration
      For information about custom transformations, see:
      https://jestjs.io/docs/code-transformation
      
      Details:
      
      /Users/user/Documents/web/nextjs-example/stories/Introduction.stories.mdx:1
      ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import { Meta } from '@storybook/addon-docs';
                                                                                        ^^^^^^
      
      SyntaxError: Cannot use import statement outside a module
          at new Script (node:vm:100:7)
          at Runtime.createScriptFromCode (/Users/user/Documents/web/nextjs-example/node_modules/jest-runtime/build/index.js:1728:14)
          at Runtime._execModule (/Users/user/Documents/web/nextjs-example/node_modules/jest-runtime/build/index.js:1596:25)
          at Runtime._loadModule (/Users/user/Documents/web/nextjs-example/node_modules/jest-runtime/build/index.js:1185:12)
          at Runtime.requireModule (/Users/user/Documents/web/nextjs-example/node_modules/jest-runtime/build/index.js:1009:12)
          at Runtime.requireModuleOrMock (/Users/user/Documents/web/nextjs-example/node_modules/jest-runtime/build/index.js:1210:21)
          at requireContext (/Users/user/Documents/web/nextjs-example/node_modules/@storybook/babel-plugin-require-context-hook/register.js:29:12)
          at /Users/user/Documents/web/nextjs-example/node_modules/@storybook/core-client/dist/cjs/preview/executeLoadable.js:77:29
          at Array.forEach (<anonymous>)
          at /Users/user/Documents/web/nextjs-example/node_modules/@storybook/core-client/dist/cjs/preview/executeLoadable.js:75:18
```

`Introduction.stories.mdx` でエラーが出たようです。
mdx ファイルはテスト不要なので無視する設定をおこないます。
以下のファイルを作成します。

```bash:bash
mkdir -p .jest/__mocks__
touch .jest/__mock__/mdx.ts
```

.jest/**mocks**/mdx.ts に以下を追加します。

```ts:.jest/**mocks**/mdx.ts
export default ''
```

`jest.config.js` に以下を追加します。

```js:jest.config.js
const customJestConfig = {
  // 省略
  moduleNameMapper: {
  "\\.(mdx)$": "<rootDir>/.jest/__mocks__/mdx.ts"
  },
}"
```

参考：[StorybookのStoryshots入れるだけでやたら手こずった](https://zenn.dev/ucwork/articles/c331de8917ea5b)

もう一度テストコマンドを実行します。

```bash:bash
yarn test
```

エラーが出なくなったらOKです。

## husky/lint-staged の設定

公式サイト：[GitHub - okonet/lint-staged: 🚫💩 — Run linters on git staged files](https://github.com/okonet/lint-staged)

husky と lint-staged をインストールします。

```bash:bash
npx mrm@2 lint-staged
```

`package.json` に以下を追加します。

```js:package.json
{
  "scripts": {
    //省略
    "lint-staged": "lint-staged"
  },
  "lint-staged": {
    "*.{ts,tsx}": "eslint --cache --fix",
    "*.scss": "stylelint --syntax=scss --fix"
  },
}
```

ついでに npm scripts を整理しておきます。

npm-run-all をインストールします。

```bash:bash
yarn add -D npm-run-all
```

lint のコマンドを整理します。

```js:package.json
{
  "scripts": {
    // 省略
    "lint": "run-p lint:*",
    "lint:eslint": "next lint",
    "lint:stylelint": "stylelint '**/*.scss'",
  }
}
```

参考：[GitHub - mysticatea/npm-run-all: A CLI tool to run multiple npm-scripts in parallel or sequential.](https://github.com/mysticatea/npm-run-all)

## さいごに
以上で Next.js * TypeScript の環境でツールを導入する方法を終わります。
ディレクトリ構成等、他にも環境構築に必要なことはありますが、別の機会で記事にしたいと思います。