ESLint プラグイン紹介: eslint-plugin-eslint-comments

[eslint-plugin-eslint-comments] は、[ESLint] ディレクティブ コメント (`/*eslint-disable*/`のようなやつ) を使うときのベストプラクティスをまとめた [ESLint] の追加ルール集です。

[ESLint] ディレクティブ コメントを使うときは、意図せず有用な警告が抑制されてしまうことを防ぐため、可能な限り影響範囲が狭いように記述すべきです。
このプラグインのルールは、そのための手助けをしてくれます。

# :cd: インストール

[ESLint] プラグインですので、[npm] を利用してインストールします。
[ESLint] をグローバル (`--global`) にインストールしている場合は、同じくグローバルへ。[ESLint] をローカル (`--save-dev`) にインストールしている場合は、同じくローカルへインストールしてください。

```bash:Shell
$ npm install --save-dev eslint eslint-plugin-node
```

- Node.js 4.0.0 以降が必要です。
- ESLint 3.1.0 以降が必要です。

なお、[推奨はローカル インストール](http://qiita.com/mysticatea/items/6bd56ff691d3a1577321)です。

# :book: 使い方

設定ファイル (`.eslintrc.json`等) にプラグインとルールの設定を追加します。

```json:例
{
    "extends": ["eslint:recommended"],
    "plugins": ["eslint-comments"],
    "rules": {
        "eslint-comments/disable-enable-pair": "error",
        "eslint-comments/no-duplicate-disable": "error",
        "eslint-comments/no-unlimited-disable": "error",
        "eslint-comments/no-unused-disable": "error",
        "eslint-comments/no-unused-enable": "error",
        "eslint-comments/no-use": "off"
    }
}
```

6つのルールがあります。

## [eslint-comments/disable-enable-pair]

このルールは、

- ルールを一時的に無効にする `/*eslint-disable */`
- 一時的に無効化されたルールを再度有効にする `/*eslint-enable */`

を必ずペアで利用しなさい、というルールです。
ペアとなる `/*eslint-enable */` コメントが存在しなかった `/*eslint-disable */` コメントを警告します。

:-1: Examples of **incorrect** code for this rule:

```js
/*eslint-disable no-console */
"※ERROR: Requires 'eslint-enable' directive for 'no-console'."

doSomething().catch(err => {
    console.error(err.stack)
    process.exitCode = 1
})
```

:+1: Examples of **correct** code for this rule:

```js
doSomething().catch(err => {
    /*eslint-disable no-console */
    console.error(err.stack)
    process.exitCode = 1
    /*eslint-enable no-console */
})
```

## [eslint-comments/no-duplicate-disable]

このルールは、既に一時的に無効化されているルールを、重ねて無効化しようとした時に警告します。
コメントを削除して無効化解除したつもりが、別の場所にも同じ無効化コメントがあったために警告が復活しなかった... というようなミスを防げます。

:-1: Examples of **incorrect** code for this rule:

```js
/*eslint-disable no-console */

doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-console
                             "※ERROR: 'no-console' rule has been disabled already."
    process.exitCode = 1
})
```

:+1: Examples of **correct** code for this rule:

```js
doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-console
    process.exitCode = 1
})
```

## [eslint-comments/no-unlimited-disable]

このルールは、無効化コメントがあらゆるルールを無効化してしまう時に警告します。
ルール名を指定せずに `/*eslint-disable */` 等の無効化コメントを書くと、全ての警告を抑制してしまいます。
ルール名を指定して、邪魔なルールだけを抑制しましょう。

:-1: Examples of **incorrect** code for this rule:

```js
/*eslint-disable no-console */

doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line
                             "※ERROR: Unexpected unlimited 'eslint-disable-line' comment. Specify some rule names to disable."
    process.exitCode = 1
})
```

:+1: Examples of **correct** code for this rule:

```js
doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-console
    process.exitCode = 1
})
```

## [eslint-comments/no-unused-disable]

このルールは、一時的に無効化したルールが一切報告されなかった場合に警告します。
そのようなコメントは単純にミスである可能性が高いです。
放置すると、将来 有用な警告を見逃してしまうかもしれません。

:-1: Examples of **incorrect** code for this rule:

```js
/*eslint-disable no-console */

doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-process-exit
                             "※ERROR: 'no-process-exit' rule is disabled but never reported."
    process.exitCode = 1
})
```

タイポも報告されます。

```js
/*eslint-disable no-console */

doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-consola
                             "※ERROR: 'no-consola' rule is disabled but never reported."
    process.exitCode = 1
})
```

:+1: Examples of **correct** code for this rule:

```js
doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-console
    process.exitCode = 1
})
```

## [eslint-comments/no-unused-enable]

このルールは、一時的に無効化されていないルールを、再度有効化しようとした場合に警告します。
そのような有効化コメントはミスである可能性が高いです。

:-1: Examples of **incorrect** code for this rule:

```js
/*eslint-disable no-console */

doSomething().catch(err => {
    console.error(err.stack)
    process.exitCode = 1
})

/*eslint-enable no-consoll */
"※ERROR: 'no-consoll' rule is re-enabled but it has not been disabled."
```

:+1: Examples of **correct** code for this rule:

```js
/*eslint-disable no-console */

doSomething().catch(err => {
    console.error(err.stack)
    process.exitCode = 1
})

/*eslint-enable no-console */
```

## [eslint-comments/no-use]

このルールは、そもそもディレクティブ コメントを禁止するルールです。
すべてのディレクティブ コメントを警告します。

:-1: Examples of **incorrect** code for this rule:

```js
/*eslint-disable no-console */
"※ERROR: Unexpected ESLint directive comment."

doSomething().catch(err => {
    console.error(err.stack)
    process.exitCode = 1
})

/*eslint-enable no-console */
"※ERROR: Unexpected ESLint directive comment."
```

```js
doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-console
                             "※ERROR: Unexpected ESLint directive comment."
    process.exitCode = 1
})
```

:+1: Examples of **correct** code for this rule:

```js
doSomething().catch(err => {
    console.error(err.stack)
    process.exitCode = 1
})
```

:wrench: `allow`オプションによって、特定のディレクティブ コメントだけを許可することもできます。

:-1: Examples of **incorrect** code for the `{"allow": ["eslint-disable-line"]}` option:

```js
/*eslint-disable no-console */
"※ERROR: Unexpected ESLint directive comment."

doSomething().catch(err => {
    console.error(err.stack)
    process.exitCode = 1
})

/*eslint-enable no-console */
"※ERROR: Unexpected ESLint directive comment."
```

:+1: Examples of **correct** code for the `{"allow": ["eslint-disable-line"]}` option:

```js
doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-console
    process.exitCode = 1
})
```

# :warning: 既知の制限事項

これらは ESLint ルールとして提供されているため、無効化することが可能です (えー)。

```js
/*eslint eslint-comments/no-use: off */

doSomething().catch(err => {
    console.error(err.stack) //eslint-disable-line no-console
    process.exitCode = 1
})
```

[ESLint]: http://eslint.org/
[eslint-plugin-eslint-comments]: https://github.com/mysticatea/eslint-plugin-eslint-comments#eslint-plugin-eslint-comments
[eslint-comments/disable-enable-pair]: https://github.com/mysticatea/eslint-plugin-eslint-comments/blob/master/docs/rules/disable-enable-pair.md
[eslint-comments/no-duplicate-disable]: https://github.com/mysticatea/eslint-plugin-eslint-comments/blob/master/docs/rules/no-duplicate-disable.md
[eslint-comments/no-unlimited-disable]: https://github.com/mysticatea/eslint-plugin-eslint-comments/blob/master/docs/rules/no-unlimited-disable.md
[eslint-comments/no-unused-disable]: https://github.com/mysticatea/eslint-plugin-eslint-comments/blob/master/docs/rules/no-unused-disable.md
[eslint-comments/no-unused-enable]: https://github.com/mysticatea/eslint-plugin-eslint-comments/blob/master/docs/rules/no-unused-enable.md
[eslint-comments/no-use]: https://github.com/mysticatea/eslint-plugin-eslint-comments/blob/master/docs/rules/no-use.md
[npm]: https://www.npmjs.com/