eslintをdockerを使って試してみたメモ

## 概要

eslintをdockerでインストールして動かしてみる。([tslintでもやってみた](http://qiita.com/hibohiboo/items/d3d5976f38e05110e2e7))
以下の2点をそれぞれ確認できるようにする。
今回はReactを対象にしているのでjsxもチェックできるようにする。

* 最低限の文法チェック
* エラーが出た部分の自動修正

## ディレクトリ構成

```bash
project
  + src # ソースの入ったディレクトリ
  + webpack # react,es6トランスパイル用のビルドツールの入ったディレクトリ
  - bin
    - eslint.sh
    - eslint_fix.sh
  - eslint 
    - Dockerfile
    - .eslintrc.json
    - package.json
  - docker-compose.yml
```

## 各ファイルの内容

```docker:Dockerfile
# docker-hubからnode入りコンテナを取得
# https://hub.docker.com/_/node/
FROM node:7.5.0

# コンテナ上の作業ディレクトリ作成
WORKDIR /my_lint

# 後で確認出来るようにpackage.jsonを作成
RUN npm init -y

RUN npm i -D eslint
RUN npm i -D eslint-plugin-react
RUN npm i -D babel-eslint
```

eslintを起動するときに、拡張子を.js,.jsxを対象とするように以下で設定している。
チェック対象にはsrcディレクトリを指定している。

```json:package.json
{
  "name": "my_lint",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "eslint": "eslint --ext .js --ext .jsx src",
    "eslint-fix": "eslint --fix --ext .js --ext .jsx src"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-eslint": "^7.1.1",
    "eslint": "^3.15.0",
    "eslint-plugin-react": "^6.9.0"
  }
}
```

以下のルールは動作確認用に最小限となっている。
実際に使用するときはair-bnbなどのルールを使うのがよいかも。

```json:.eslintrc.json
{
    "parser": "babel-eslint",
    "env": {
        "browser": true,
        "commonjs": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaFeatures": {
            "experimentalObjectRestSpread": true,
            "jsx": true
        },
        "sourceType": "module"
    },
    "plugins": [
        "react"
    ],
    "rules": {
        "no-unused-vars": ["off"],
        "indent": [
            "error",
            2
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single",
            {
                "allowTemplateLiterals": false
            }
        ],
        "semi": [
            "error",
            "always"
        ]
    }
}
```


```yaml:docker-compose.yml

lesson_eslint_tool:
  build: ./eslint
  volumes:
    # ビルドするソースファイル
    - ./src:/my_lint/src
    # eslint設定ファイル
    - ./eslint/.eslintrc.json:/my_lint/.eslintrc.json
    # コンテナ上のpackage.jsonを上書き
    - ./eslint/package.json:/my_lint/package.json
```

eslintを起動するdocker-composeのコマンドを毎回書くのは面倒なのでシェルスクリプトで実行する。

```
#!/bin/bash

# このシェルスクリプトのディレクトリの絶対パスを取得。
bin_dir=$(cd $(dirname $0) && pwd)

# docker-composeの起動。 コンテナ内に入る
cd $bin_dir/../ && docker-compose run lesson_eslint_tool npm run -s eslint
```

`-s`オプションで、npmのエラーを消している。

```bash:eslint_fix.sh
#!/bin/bash

# このシェルスクリプトのディレクトリの絶対パスを取得。
bin_dir=$(cd $(dirname $0) && pwd)

# docker-composeの起動。 コンテナ内に入る
cd $bin_dir/../ && docker-compose run lesson_eslint_tool npm run -s eslint-fix
```

### 補足

`.eslintrc.json`ファイルで`"parser": "babel-eslint",`を使わないと、クラスのプロパティとしてラムダ式を使った場合にパースエラーが出てしまう。そのような書き方がなければ不要かも。

```
/my_lint/src/toggle.jsx
  9:15  error  Parsing error: Unexpected token =

✖ 1 problem (1 error, 0 warnings)
```

パースエラーがでているのは以下のソースの`handleClick = () => {`の行

```js
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
  }

  handleClick = () => {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}
```


## 参考

[eslint最初の一歩][*5]
[eslint rules][*6]
[npm ERR 非表示][*7]
[ESLintでReactとES2015の構文チェック(eslint-config-airbnb)][*8]
[babel-eslintでチェックする][*9]
[mochaのitとifを間違えるのでESLintのrule pluginを自作した][*10]
[Class Propertiesを使ったReactコンポーネントをESLintで静的検証][*3]
[ESLintでReactをチェックする][*1]
[TypeScript を ESLint を使ってチェックする][*2]
[tslint-react][*3]
[typescript-eslint-parser][*4]


[*1]:http://qiita.com/trapple/items/b2304ca3cd8c09c16705
[*2]:http://smart.ataglance.jp/2016-01-21-check-typescript-with-eslint/
[*3]:https://github.com/palantir/tslint-react
[*4]:https://github.com/eslint/typescript-eslint-parser
[*5]:http://qiita.com/mysticatea/items/f523dab04a25f617c87d
[*6]:http://eslint.org/docs/rules/
[*7]:http://sota1235.hatenablog.com/entry/2016/08/06/210659
[*8]:http://dackdive.hateblo.jp/entry/2016/05/13/094000
[*9]:https://havelog.ayumusato.com/develop/javascript/e660-eslint_with_babel.html
[*10]:http://shokai.org/blog/archives/tag/javascript
[*11]:http://qiita.com/tkrkt/items/b3b624ef32e05bd6bf55