【環境構築】Next × App Router × Prettier × ESLint × Husky × Shadcn/ui の環境構築をしてみる

# はじめに
Next.jsのプロジェクトの環境を統一しようと思って記事にしました。
間違いがあれば指摘していただけると嬉しいです。

# プロジェクトの作成

`typescript`を含めてプロジェクトを作ります。
```
 npx create-next-app your-project --typescript
```
色々聞かれると思いますので今回は以下の構成にしました。
```
√ Would you like to use ESLint with this project? ... Yes
√ Would you like to use Tailwind CSS with this project? ... Yes 
√ Would you like to use `src/` directory with this project? ... Yes
√ Use App Router (recommended)? ... Yes
√ Would you like to customize the default import alias? ... Yes
√ What import alias would you like configured? ... @/*
```
All yes. 気遣いはありがたく受けます。これで作成完了です。

# ESLint,Prettierの導入
`prettier(プリティア）`をプリッターと呼んでいた苦い過去があります。

`ESLint`は先程入れています！足りないパッケージを入れていきます。

```
npm install --save-dev \
    @typescript-eslint/parser \
    @typescript-eslint/eslint-plugin \
    eslint-plugin-react \
    eslint-plugin-react-hooks \
    eslint-plugin-jsx-a11y \
    eslint-plugin-import\
    eslint-import-resolver-typescript
```
```
 npm install -D prettier eslint-plugin-prettier eslint-config-prettier
```
ふと`npm install` と`npm install -D`の違いってなんだっけと調べてみたら開発環境で使うか本番環境で使うかの違いでした。

`eslint`の設定ファイルを書いていきます。
```.eslintrc.json
{
  "root": true, 
  "env": {
    "browser": true,     
    "es2020": true 
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:jsx-a11y/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
    "plugin:prettier/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser", 
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true     
},
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "plugins": ["@typescript-eslint", "react", "import"],
  "settings": {
      "import/resolver": {
      "node": {
        "extensions": [".js", ".jsx", ".ts", ".tsx", ".json"] 
      },

      "typescript": {
        "config": "tsconfig.json", 
        "alwaysTryTypes": true       
      }
    },
    "react": {
      "version": "17.0.0"
    }
  },
  "ignorePatterns": ["/*.js"],
  "rules": {
    "@typescript-eslint/ban-types": [
      "error",
      {
        "types": {
          "{}": false
        }
      }
    ],
    "react/prop-types": ["off"],
    "react/react-in-jsx-scope": "off",
    "react/jsx-filename-extension": [
      "error",
      { "extensions": [".jsx", ".tsx"] }
    ],
    "import/order": ["error"],
    "prettier/prettier": [
      "error",
      {
        "trailingComma": "all",
        "endOfLine": "lf",
        "semi": false,
        "singleQuote": true,
        "printWidth": 80,
        "tabWidth": 2
      }
    ]
  }
}
```

エイリアスはプロジェクト作成時に入ってます！

`npm run lint`で実行できるようにしておきます。

```package.json
{
  ...
  "scripts": {
    ...
    "lint": "eslint --ext .js,.jsx,.ts,.tsx .",
    "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx --fix ."
  },
  ...
}
```
続いて`prettier`です。`eslint`の設定と合わせておきましょう。
```.prettierrc
{
  "trailingComma": "all",
  "endOfLine": "lf",
  "semi": false,
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2
}
```
### huskey
`husky`を入れて`commit時`に`lint`が走るようにします。

```
 npx husky install
```
```
 npm install -D  lint-staged
```
```
 npx husky add .husky/pre-commit "npx lint-staged"
```
`package.json`直下に以下のコードを追加します。
```package.json
{
    ...
    "scripts":{
        "prepare": "husky install"
    }
    ...
    "husky": {
        "hooks": {
          "pre-commit": "lint-staged"
        }
      },
      "lint-staged": {
        "*.{js,jsx,ts,tsx}": [
          "eslint --fix"
        ]
      }
    ...
}
```

これで、`ESLint`, `Prettier`の導入は完了です！

# Jestの導入
`Jest`は`JavaScript`のテストフレームワークです。

今回は`React Testing Library`と併用をします。

では必要な`package`を追加します。

```
 npm install  -D \
    jest  \
    @testing-library/react \
    @types/jest \
    @testing-library/jest-dom \
    babel-jest \
    @testing-library/user-event \
    jest-css-modules 
    jest-environment-jsdom
```


次にjest.config.jsを作って中身を書いていきます。
```jest.config.js
const nextJest = require("next/jest");

const createJestConfig = nextJest({
  // next.config.jsとテスト環境用の.envファイルが配置されたディレクトリをセット。基本は"./"で良い。
  dir: "./",
});

const customJestConfig = {
  moduleNameMapper: {
    "^@/components/(.*)$": "<rootDir>/components/$1",
    "^@/pages/(.*)$": "<rootDir>/pages/$1",
  },
  testEnvironment: "jest-environment-jsdom",
};

module.exports = createJestConfig(customJestConfig);
```
`npm run test`で実行できるようにしておきます。
```package.json
{
  ...
  "scripts": {
    ...
    "lint": "eslint --ext .js,.jsx,.ts,.tsx .",
    "lint:fix": "eslint --ext .js,.jsx,.ts,.tsx --fix ."
    "test": "jest --env=jsdom --verbose", 
  },
  ...
}
```
`--env=jsdom --verbose`のオプションを付与することでテストファイル1個1個に対してテストが通ったかを確認することができます。

# shadcn/ui
`shadcn/ui`とは`tailwind`で書かれた、コピーペーストでアプリに組み込める美しく設計されたコンポーネント集です。

`tailwind`はプロジェクト作成時に選択しましたので、`shadcn-ui`のプロジェクトをセットアップします。

```
npx shadcn-ui@latest init
```
すると大量に質問されるので、
```
√ Which style would you like to use? » New York
√ Which color would you like to use as base color? » Neutral
√ Where is your global CSS file? ... src/styles/globals.css
√ Do you want to use CSS variables for colors? ... yes
√ Where is your tailwind.config.js located? ... tailwind.config.js
√ Configure the import alias for components: ... @/components
√ Configure the import alias for utils: ... @/lib/utils
√ Are you using React Server Components? ... yes
√ Write configuration to components.json. Proceed? ... yes
```
と答えました。

さらに、`shadcn/ui`の公式ドキュメントに以下のようなディレクトリ構成の提案があったのでこの構成に変更しました

```
.
├── app
│   ├── layout.tsx
│   └── page.tsx
├── components
│   ├── ui
│   │   ├── alert-dialog.tsx
│   │   ├── button.tsx
│   │   ├── dropdown-menu.tsx
│   │   └── ...
│   ├── main-nav.tsx
│   ├── page-header.tsx
│   └── ...
├── lib
│   └── utils.ts
├── styles
│   └── globals.css
├── next.config.js
├── package.json
├── postcss.config.js
├── tailwind.config.js
└── tsconfig.json

```

以上で環境構築完了です。ありがとうございました！

# 参考

https://qiita.com/282Haniwa/items/dcce1ba6bb6ae541893e

https://qiita.com/KNR109/items/7cf6b24bed318dab5715#react-testing--library%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%A8%E8%A8%AD%E5%AE%9A

https://zenn.dev/miruoon_892/articles/e42e64fbb55137