Node.jsで開発環境をセットアップする(ESLint、Prettier、husky)

Node.jsの開発環境で、以下の機能をセットアップする方法について解説します。

  • ESLintによるlint
  • Prettierによるコードフォーマット
  • huskyによるpre-commit git hooks

開発環境を作成する

最初に構築対象となる開発環境を用意します。

$ npm init -y
Wrote to /Users/t0k0sh1/workspace/node_lint/package.json:

{
  "name": "node_lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

今回の説明とは関係ないため、デフォルトのままで進めます。

パッケージをインストールする

次に必要なパッケージをインストールします。

  • eslint – lintツール
  • prettier – formatterツール
  • eslint-config-prettier – eslintのフォーマットがprettierと競合する場合はeslint側をOFFにする
  • husky – pre-commit git hooksツール
$ npm i -D eslint eslint-config-prettier prettier husky

added 85 packages, and audited 86 packages in 4s

15 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

eslint-plugin-prettierは非推奨になっているため、eslint-config-prettierを使用しています。

インストール後のpackage.jsonは以下のようになっています。

// package.json
{
  "name": "node_lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "eslint": "^8.13.0",
    "eslint-config-prettier": "^8.5.0",
    "husky": "^7.0.4",
    "prettier": "^2.6.2"
  }
}

lintの設定を行う

まずはESLintの設定を行います。ここでは必要最低限の設定にしています。prettiereslint:recommendの後に書いてください。

// .eslintrc.json
{
  "extends": [
    "eslint:recommended",
    "prettier"
  ]
}

次にeslintでのlint(修正あり)とprettierでのフォーマットを行うスクリプトを追加します。

// package.json
{
  "name": "node_lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint --cache --fix . && prettier --write ."
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "eslint": "^8.13.0",
    "eslint-config-prettier": "^8.5.0",
    "husky": "^7.0.4",
    "prettier": "^2.6.2"
  }
}

コードを作成して動作確認を行う

実際のコードを作成して動作確認をしておきましょう。

// index.js
function hello(name) {
  return 'Hello, ' + name;
}

console.log(hello("John"));

簡単なプログラムを書きました。動作確認ができるようにlintでエラーになるコードになっています。

では動作確認します。

$ node index.js
Hello, John

エラーにならず動作していることを確認できました。

では、このコードに対してlintprettierを実行します。

$ npm run lint

> node_lint@1.0.0 lint
> eslint --cache --fix . && prettier --write .


/Users/t0k0sh1/workspace/node_lint/index.js
  6:1  error  'console' is not defined  no-undef

✖ 1 problem (1 error, 0 warnings)

consoleが未定義であるとエラーになりました。これは後で修正(除去)しますが、今はこのままにしておきます。

ここまでに作成したコードをコミットしておきます。

git initコマンドでgitリポジトリを初期化します。

$ git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint:   git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint:   git branch -m <name>
Initialized empty Git repository in /Users/t0k0sh1/workspace/node_lint/.git/

コミットするにあたり、node_modules.eslintcacheは対象から外したいので、.gitignoreを作成しておきます。

// .gitignore
node_modules/
.eslintcache

作成したファイルをステージングエリアに追加します。

$ git add .

ローカルリポジトリにコミットしておきます。

$ git commit -m 'first commit'
[master (root-commit) ec47452] first commit
 5 files changed, 1682 insertions(+)
 create mode 100644 .eslintrc.json
 create mode 100644 .gitignore
 create mode 100644 index.js
 create mode 100644 package-lock.json
 create mode 100644 package.json

pre-commit git hooksを設定する

huskyを使ってpre-commit git hooksを設定していきます。手順は公式の手順(https://github.com/typicode/husky)に従いますが、pre-commitに追加するコマンドがnpm run lintになっている点にだけ注意してください(公式の手順ではnpm test

$ npm set-script prepare "husky install"
$ npm run prepare

> node_lint@1.0.0 prepare
> husky install

husky - Git hooks installed
$ npx husky add .husky/pre-commit "npm run lint"
husky - created .husky/pre-commit
$ git add .husky/pre-commit

手順どおりに進めると、package.jsonがステージングエリアに追加されていないため、これを追加してローカルリポジトリにコミットします。

$ git add package.json
$ git commit -m 'setup husky'

> node_lint@1.0.0 lint
> prettier --write '**/*.js' && eslint .

index.js 25ms

/Users/t0k0sh1/workspace/node_lint/index.js
  6:1  error  'console' is not defined  no-undef

✖ 1 problem (1 error, 0 warnings)

husky - pre-commit hook exited with code 1 (error)

先ほどコマンド実行で確認した時と同じようにconsoleが未定義であるとエラーになりました。

pre-commitが機能していることを確認できましたが、念のためコミットされていないかを確認しておきます。

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   .husky/pre-commit
        modified:   package.json

コミットが行われておらず、pre-commitが正しく動作していることを確認できました。

コードを修正して再度コミットする

コードを修正して再度コミットしましょう。

ここでは単純にconsole.log関数を削除しておきます。

// index.js
function hello(name) {
  return "Hello, " + name;
}

hello("John");

念のため、コマンドで確認しておきます。

$ npm run lint

> node_lint@1.0.0 lint
> eslint --cache --fix . && prettier --write .

.eslintrc.json 20ms
index.js 7ms
package-lock.json 30ms
package.json 6ms

エラーにならないことを確認できましたので、ステージングエリアに追加します。

$ git add index.js

では、再度コミットしましょう。

$ git commit -m 'setup husky'
> node_lint@1.0.0 lint
> prettier --write '**/*.js' && eslint .

index.js 25ms
[master ed5dcae] setup husky
 3 files changed, 7 insertions(+), 2 deletions(-)
 create mode 100755 .husky/pre-commit

今度はエラーが表示されず、コミットが実行されました。

git statusgit logでもコミットされていることを確認します。

git status
On branch master
nothing to commit, working tree clean
$ git log
commit ed5dcae1e5e2487854e78e0d388a2f503324231d (HEAD -> master)
Author: Takashi Yamashina <takashi.yamashina@gmail.com>
Date:   Sat Apr 9 12:51:04 2022 +0900

    setup husky

commit 15186ea9211ed9d5ee4de9aa7dab7e2e04bac9de
Author: Takashi Yamashina <takashi.yamashina@gmail.com>
Date:   Sat Apr 9 12:37:45 2022 +0900

    first commit

問題なくコミットできていることが確認できました。

Prettierで特定のファイル、フォルダを対象から除外する(.prettierignore)

Prettierで特定のファイルやフォルダを対象から除外する方法について解説します。

.prettierignoreを作成する

.prettierignoreを作成し、そこに対象から除外したいファイルやフォルダを書きます。

config.js
test/

上記の例では、config.jsというファイルと、testフォルダを対象から除外しています。

実際に試してみる

実際にどう動くのか確認しましょう。

package.json

package.jsonは次のとおりです。prettierをインストールし、prettierコマンドを実行するスクリプトを追加しています。

{
  "name": "node_prettier",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "format": "prettier --write '**/*.js'"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "prettier": "^2.6.2"
  }
}

Prettierは設定ファイルがなくても実行可能なため、ここでは設定ファイルは作成せずに進めます。

プロジェクトの構造

プロジェクトの構造は以下のようになっており、用意した4つのJSファイルはすべてフォーマットされる書き方になっています。

$ tree
.
├── file1.js
├── file2.js
├── dir3
│   └── file3.js
├── dir4
│   └── file4.js
├── package-lock.json
└── package.json

各ファイルの中身は若干の違いはありますが、以下のような内容になっています。

function file1() {
console.log('file1');
}

関数内のインデントがない点と、文字列の囲み文字がダブルクォーテーションではなくシングルクォーテーションになっている点がフォーマットの対象となる箇所です。

Prettierを実行する

最初に.prettierignoreを用意せずに実行します。

$ npm run format

> node_prettier@1.0.0 format
> prettier --write '**/*.js'

dir3/file3.js 22ms
dir4/file4.js 2ms
file1.js 2ms
file2.js 1ms

プロジェクト直下の2ファイル、2つのフォルダ内のそれぞれのファイルが対象になっていることがわかります。

function file1() {
  console.log("file1");
}

実際にファイルを見てみると、インデントおよび文字列の囲み文字が修正されていることが確認できました。

次に.prettierignoreを作成し、プロジェクト直下の1ファイルと2つのファイルのうちの1つを除外してから再度実行してみましょう。

file2.js
dir4/

先ほどと同様に実行してみると、除外したファイルとフォルダ内のファイルが表示されなくなりました。

$ npm run format

> node_prettier@1.0.0 format
> prettier --write '**/*.js'

dir3/file3.js 23ms
file1.js 2ms

ファイルも確認してみると、

ファイル名が表示されたファイルは、先ほどと同様にフォーマットされていますが、

function file1() {
  console.log("file1");
}

ファイル名が表示されなかった(除外された)ファイルはフォーマットされていません。

function file2() {
console.log('file2');
}

node_modulesの除外は不要

実行してみるとわかりますが、node_modulesの除外は不要です。

そのため、対象としたいフォルダ内に除外したいファイルやフォルダがない場合は.prettierignoreを作成する必要はありません。

ESLintで特定のファイル、フォルダを対象から除外する(.eslintignore)

ESLintで特定のファイルやフォルダを対象から除外する方法について解説します。

.eslintignoreを作成する

.eslintignoreを作成し、そこに対象から除外したいファイルやフォルダを書きます。

config.js
test/

上記の例では、config.jsというファイルと、testフォルダを対象から除外しています。

実際に試してみる

実際にどう動くのか確認しましょう。

package.json

package.jsonは次のとおりです。eslintをインストールし、eslintコマンドを実行するスクリプトを追加しています。

{
  "name": "node_eslint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint ./"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "eslint": "^8.12.0"
  }
}

.eslintrc.json

.eslintrc.jsonは必要最小限にしています。

{
    "extends": [
      "eslint:recommended"
    ]
}

プロジェクトの構造

プロジェクトの構造は以下のようになっており、用意した4つのJSファイルはすべてLintでエラーが検出されます。

$ tree
.
├── file1.js
├── file2.js
├── dir3
│   └── file3.js
├── dir4
│   └── file4.js
├── .eslintrc.json
├── package-lock.json
└── package.json

ESLintを実行する

最初に.eslintignoreを用意せずに実行してみます。

$ npm run lint                                                                                                                     

> node_eslint@1.0.0 lint
> eslint ./


/Users/t0k0sh1/workspace/node_eslint/dir3/file3.js
  1:10  error  'file3' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

/Users/t0k0sh1/workspace/node_eslint/dir4/file4.js
  1:10  error  'file4' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

/Users/t0k0sh1/workspace/node_eslint/file1.js
  1:10  error  'file1' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

/Users/t0k0sh1/workspace/node_eslint/file2.js
  1:10  error  'file2' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

✖ 8 problems (8 errors, 0 warnings)

プロジェクト直下の2ファイルもフォルダ内の2ファイルもエラーになっていることがわかります。

次に.eslintignoreを作成し、プロジェクト直下の1ファイルと2つのファイルのうちの1つを除外してから再度実行してみましょう。

file2.js
dir4/

先ほどと同様に実行してみると、除外したファイルとフォルダ内のファイルがエラーに表示されなくなりました。

$ npm run lint

> node_eslint@1.0.0 lint
> eslint ./


/Users/t0k0sh1/workspace/node_eslint/dir3/file3.js
  1:10  error  'file3' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

/Users/t0k0sh1/workspace/node_eslint/file1.js
  1:10  error  'file1' is defined but never used  no-unused-vars
  2:1   error  'console' is not defined           no-undef

✖ 4 problems (4 errors, 0 warnings)

node_modulesの除外は不要

実行してみるとわかりますが、node_modulesの除外は不要です。

そのため、対象としたいフォルダ内に除外したいファイルやフォルダがない場合は.eslintignoreを作成する必要はありません。

モバイルバージョンを終了