ESLintの導入と警告対応のメモ

  • 6
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

React+ES6な環境で開発をしています。
その関係でES6・JSXに対応したLinterが必要と思いまして、ESLintを入れてみました。
その作業時のメモです。

環境

Mac OS X 10.10.5(Yosemite)
npm 2.7.5
eslint 2.1.0

インストール

eslint本体

npm install -g eslint

今回はJSXも解析対象としたいので、Reactプラグイン追加します。

npm install -g eslint-plugin-react

SublimeTextの設定

SublimeText使っているので、こちらでLinterを有効にすべく
Package ControlからSublimeLinterSublimeLinter-contrib-eslintを入れています。
JSHintなどの他のLinterを無効化したい場合は、

Tools → SublimeLinter → Toggle Linter

で、使用可能なLinterのenabled/disabledの切替をしましょう。

ルールの設定

プロジェクト用に.eslintrcを作成、設定します。
内容は後述

ES6のルール・警告

ES6用のルールは以下にまとまってます。

http://eslint.org/docs/rules/#ecmascript-6

とりあえず一通りのルールを設定してみて、自分のコードで警告が出たものについては、以下のように対応をしてみました。

prefer-allow-callback

警告箇所の例
const sortedList = this.state.list.concat().sort(function (s1, s2) {
...中略
});

http://eslint.org/docs/rules/prefer-arrow-callback
this束縛もあるので、callback関数はArrow functionsで書いた方がいいですよ、とのこと。

修正後
const sortedList = this.state.list.concat().sort((s1, s2) => {
...中略
});

prefer-template

警告箇所の例
...中略
{
    url: '/api/sample/' + this.state.hoge
},
...中略

http://eslint.org/docs/rules/prefer-template
文字列結合にはTemplate strings使いましょう、とのこと。

修正後
...中略
{
    url: `/api/sample/${this.state.hoge}`
},
...中略

object-shotrhand

警告箇所の例
const hoge = 'hoge';
this.setState({
    hoge : hoge
});

http://eslint.org/docs/rules/object-shorthand
methodsとpropertiesがあり、今回はpropertiesの方に引っかかっています。
プロパティ名と設定する変数名が同じ場合は、プロパティ名を省略できると。
こだわりは無かったので省略してみました。

修正後
const hoge = 'hoge';
this.setState({
    hoge
});

Reactプラグインのルール・警告

React関連のルールは以下にまとまってます。

https://github.com/yannickcr/eslint-plugin-react

とりあえず一通りのルールを設定してみて、自分のコードで警告が出たものについては、以下のように対応をしてみました。

display-name

警告箇所の例
export default class App extends React.Component {

classの所に警告が付きます。
https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md

displayNameの有無チェックなので外してもいいかと思いましたが、オプションで
acceptTranspilerNameを設定すれば、Class記法に合わせたチェックが可能になるようだったので、以下の設定で運用することにしました。

    "rules": {
...中略
        "react/display-name": [1, {"acceptTranspilerName": true}],
...中略

no-set-state

警告箇所の例
this.setState{ hoge : 'fuga'};

みたいな箇所が警告対象となりました。
https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md
Fluxしてるのでthis.setStateを呼び出さないようにしたい、というような場合に有効化するものらしく。
今回はFlux使わない/setState呼び出し多数なアプリを対象とした設定なので、ルールを無効化しました。

prop-types

https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md
そのままですが、propsの値に対してPropTypesを指定していない場合に警告となります。
今回はアプリ固有のComponentsしか作成せず、PropTypesを使うメリットがそれほどなかったので(本当は使った方がいいんでしょうが)、ルールを無効化しました。

jsx-boolean-value

警告箇所の例
render() {
    return (
        <div>
            <span aria-hidden={true}>x</span>
        </div>
    );
}

https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md

booleanな属性、かつtrueを設定する場合にはvalueの指定を省略できるよ、とのこと。

修正後
render() {
    return (
        <div>
            <span aria-hidden>x</span>
        </div>
    );
}

jsx-no-literals

警告箇所の例
render() {
    return (
        <div>hoge</div>
    );
} 

「hoge」のテキスト部分が警告となります。
https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md
リンクの内容そのままなんですが、、、このリテラルがイケてるのかどうかよく分からず違和感あったので、ルールを無効化しました。

jsx-max-props-per-line

警告箇所の例
render() {
    return (
        <div className="topBox" onClick={this.handleClick}>hoge</div>
    );
}

onClickの辺りが警告となります。
https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md
JSX上の1行に指定できるprops数を超えた、ということみたいですね。
デフォルトは1とのことですが、このままだと縦長なコードになりそうで微妙だと思ったので、1行あたり2propsまでを許容するようにして一先ず運用することにしました。

    "rules": {
...中略
        "react/jsx-max-props-per-line": [1, {"maximum": 2}],
...中略

jsx-sort-props

警告箇所の例
render() {
    return (
        <img src={imageUrl} alt={this.props.unit.commodityName} />
    );
}

propsがアルファベット順に指定されていない、ということらしく。
(例の場合はsがaより前に来ている。)
propsの並び順、規約的に決めた方がいいのかもしれないとは思いましたが、現状の開発ではメリット感じられなかったので無効化。

.eslintrc

現状こんな感じ。

.eslintrc
{
    "extends": "eslint:recommended",
    "plugins": ["react"],
    "env": {
        "browser": true,
        "es6": true
    },
    parserOptions: {
        "ecmaVersion": 6,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "rules": {
        "semi": 2,
        "no-empty-pattern": 2,
        "arrow-parens": 2,
        "arrow-spacing": 2,
        "constructor-super": 2,
        "generator-star-spacing": 2,
        "no-class-assign": 2,
        "no-const-assign": 2,
        "no-dupe-class-members": 2,
        "no-this-before-super": 2,
        "no-var": 2,
        "object-shorthand": 2,
        "prefer-arrow-callback": 2,
        "prefer-const": 2,
        "prefer-reflect": 2,
        "prefer-spread": 2,
        "prefer-template": 2,
        "require-yield": 2,
        "react/display-name": [1, {"acceptTranspilerName": true}],
        "react/forbid-prop-types": 1,
        "react/jsx-boolean-value": 1,
        "react/jsx-closing-bracket-location": 0,
        "react/jsx-curly-spacing": 1,
        "react/jsx-indent-props": 1,
        "react/jsx-max-props-per-line": [1, {"maximum": 2}],
        "react/jsx-no-duplicate-props": 1,
        "react/jsx-no-literals": 0,
        "react/jsx-no-undef": 1,
        "react/jsx-sort-prop-types": 1,
        "react/jsx-sort-props": 0,
        "react/jsx-uses-react": 1,
        "react/jsx-uses-vars": 1,
        "react/no-danger": 1,
        "react/no-did-mount-set-state": 1,
        "react/no-did-update-set-state": 1,
        "react/no-direct-mutation-state": 1,
        "react/no-multi-comp": 1,
        "react/no-set-state": 0,
        "react/no-unknown-property": 1,
        "react/prefer-es6-class": 1,
        "react/prop-types": 0,
        "react/react-in-jsx-scope": 1,
        "react/require-extension": 1,
        "react/self-closing-comp": 1,
        "react/sort-comp": 1,
        "react/wrap-multilines": 1        
    }
}

現状これで事足りてるので、後は運用しながら適宜調整しようと思います。

checkstyle形式で出力

解析結果のフォーマットについては、コマンドラインの実行オプション「-f」で指定できます。
http://eslint.org/docs/user-guide/command-line-interface#f-format

% eslint /project/**/*.js -f checkstyle

現状CIにはJenkinsを使っていますが、この結果をcheckstyleプラグインに噛ませれば、CIでlintを回せそうです。

gulpから使う

現状gulpを使っているので、そちらからも使えるように設定してみました。

必要なmoduleをインストール。

npm install gulp-eslint
npm install fs 

fsは結果をファイル出力する際に必要になります。

gulpfile書きます。

gulpfile.js
var gulp = require('gulp');
var fs = require('fs');
var eslint = require('gulp-eslint');

gulp.task('lint', function () {
    return gulp.src(['**/*.js', '!gulpfile.js', '!node/**', '!node_modules/**'])
        .pipe(eslint('.eslintrc'))
        .pipe(eslint.format()) // 標準出力
        .pipe(eslint.format('checkstyle', fs.createWriteStream('checkstyle-result.xml'))); // checkstyle形式のファイル出力
});

package.jsonにscripts追加して

package.json
{
...中略
  "scripts": {
    "lint": "gulp lint"
  },
...中略
}

npm run lint

で実行。

参考ページ

以下を参考にさせていただきました。
ありがとうございました。

http://qiita.com/mysticatea/items/f523dab04a25f617c87d
https://www.bountysource.com/issues/29193844-how-to-output-formatter-to-file-using-gulp