【コピペだけで実装】Next.jsにPrettierとESLintを搭載したHuskyを導入する

皆さんPrettierやESLintってどのタイミングで適用させてますか？

僕は今まで手動で行っていたのですが、だんだん面倒になり、そういえばHuskyとかいうパッケージでGitのコミット時にフォーマットを整えてくれるやつあったな〜

と思い、Huskyを導入しました。

Huskyはバージョンごとに扱いが少し異なるそうなので、中々手こずりました.

もう二度とHuskyで時間は食われたくないので、そんな意味も込めて、Huskyの導入方法を備忘録として書いていきます。

特に詳しい説明は致しませんので、気になる点がございましたらコメントにて質問していただくか、ご自身で調べてください。

また、Next.jsはTypeScriptを前提として設定を行いますのでご了承下さい。

それでは説明していきます！

# パッケージのインストール

```zsh
// npm
npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint  eslint-config-prettier eslint-plugin-react husky@6 lint-staged eslint-plugin-import eslint-plugin-prettier eslint-plugin-react-hooks

// yarn
yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint  eslint-config-prettier eslint-plugin-react husky@6 lint-staged eslint-plugin-import eslint-plugin-prettier eslint-plugin-react-hooks
```

``husky``のバージョンは６でインストールして下さい。

# package.json

以下の内容を追加

```package.json
 "scripts": {
    "check-types": "tsc --noEmit",
    "lint": "eslint **/**.ts **/**.tsx",
    "lint:fix": "eslint **/**.ts **/**.tsx --fix",
    "format": "prettier --write **/**.ts **/**.tsx",
    "test-all": "npx yarn-run-all lint check-types lint:fix",
    "prepare": "npx husky install",
    "lint-staged": "lint-staged"
  },
  "lint-staged": {
    "*.@(css)": [
      "prettier --write --parser css"
    ],
    "*.@(ts|tsx)": [
      "yarn lint",
      "yarn format"
    ]
  },
```



# tsconfig.json

以下をコピペ

```tsconfig.json
{
  "compilerOptions": {
    "rootDir": "src", // srcディレクトリ配下に置いている場合はこれを追加
    "typeRoots": ["node_modules/@types"],
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve"
  },
  "include": ["next-env.d.ts", "src"],
  "exclude": ["node_modules", ".next", ".out"]
}
```

# tsconfig.eslint.json

```tsconfig.eslint.json
{
  "extends": "./tsconfig.json",
  "include": ["next-env.d.ts", "src"],
  "exclude": ["node_modules", ".next", "out"]
}
```

# .eslintrc.json

```.eslintrc.json
{
  "root": true,
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "extends": [
    "plugin:react/recommended",
    "plugin:import/errors",
    "plugin:import/warnings",
    "plugin:import/typescript",
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 2020,
    "project": "./tsconfig.eslint.json",
    "sourceType": "module"
  },
  "plugins": [
    "@typescript-eslint",
    "import",
    "react",
    "react-hooks",
    "prettier"
  ],
  "rules": {
    "react/react-in-jsx-scope": "off",
    "no-use-before-define": "off",
    "prefer-const": "warn",
    "@typescript-eslint/ban-types": "off",
    "@typescript-eslint/explicit-module-boundary-types": "off",
    "@typescript-eslint/no-use-before-define": [
      "error"
    ],
    "no-void": [
      "error",
      {
        "allowAsStatement": true
      }
    ],
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        "vars": "all",
        "args": "after-used",
        "argsIgnorePattern": "_",
        "ignoreRestSiblings": false,
        "varsIgnorePattern": "_"
      }
    ],
    "import/extensions": [
      "error",
      "ignorePackages",
      {
        "js": "never",
        "jsx": "never",
        "ts": "never",
        "tsx": "never"
      }
    ],
    "react/jsx-filename-extension": [
      "error",
      {
        "extensions": [
          ".jsx",
          ".tsx"
        ]
      }
    ]
  },
  "overrides": [
    {
      "files": [
        "*.tsx"
      ],
      "rules": {
        "react/prop-types": "off"
      }
    }
  ],
  "settings": {
    "react": {
      "version": "detect"
    },
    "import/resolver": {
      "node": {
        "paths": [
          "src"
        ]
      }
    }
  }
}
```

# .prettierrc

```.prettierrc
{
  "printWidth": 140,
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all"
}
```

ご自身の好きなフォーマットに変えて下さい。

ここまで来たら、一旦、PrettierやESLintを走らせてみて下さい。

```zsh
// prettier
npm run format
yarn format

// ESLint
npm run lint
yarn lint

// 型チェック
npm run check-types
yarn check-types
```

# Huskyの設定

さて、これでPrettierとESLintの設定は完了です。

あとはHuskyにこれらのコマンドを走らせるだけです！

## Huskyの初期化

```zsh
// npm
npm run prepare

// yarn
yarn prepare
```

するとプロジェクト直下に``.husky``ディレクトリが作成されます。

そしたら以下のコマンドでファイルを作成。

特に理由がない限り、``pre-commit``を作成したほうが良いと思います。

```zsh
// コミット時にHuskyを走らせたい場合（おすすめ）
touch .husky/pre-commit

// プッシュ時にHuskyを走らせたい場合
touch .husky/pre-push
```

``pre-commit``もしくは``pre-push``に以下をコピペ

```
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

// npm
npm run lint-staged
git add .

// yarn
yarn lint-staged
git add .
```

ファイルの読み取り権限追加

```terminal
// commitの場合
chmod a+x .husky/pre-commit

// pushの場合
chmod a+x .husky/pre-push
```

これでHuskyの設定は完了です！

実際にHuskyが走るかチェックしてみましょう！

```zsh
git add .
git commit -m 'apply husky'
yarn run v1.22.11
$ lint-staged
✔ Preparing...
✔ Running tasks...
✔ Applying modifications...
✔ Cleaning up...
✨  Done in 11.36s.

git push
```

ESLintの設定に引っかかる場合はその箇所を直してからまたコミットして下さい。

また、プッシュ時に実装している方もやり方は同じです。

ただ、Huskyが走るタイミングが``git commit``か``git push``だけの違いです。

いかがだったでしょうか？

冒頭にも述べましたが、Huskyはバージョンによって扱い方が少し変わるようなので厄介ですね。

今回書いたこの記事が、自分的には一番しっくり来ました。

なので、皆さんがこの記事をコピペするだけで実装できることを祈ってます（笑）。

以上、「【コピペだけで実装】Next.jsにPrettierとESLintを搭載したHuskyを導入する」でした！

Thank you for reading

# 参考記事

この記事を書くにあたって、参考にさせていただいた記事となります。

・[husky v6 のインストール方法と使い方。lint-staged も導入して、品質を保とう](https://fwywd.com/tech/husky-setup)
・[Next.js (TypeScript) に ESLint と Prettier を導入し、コードを綺麗に保とう](https://fwywd.com/tech/next-eslint-prettier)

ご協力頂き誠にありがとうございます！