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からSublimeLinterとSublimeLinter-contrib-eslintを入れています。
JSHintなどの他のLinterを無効化したい場合は、
Tools → SublimeLinter → Toggle Linter
で、使用可能なLinterのenabled/disabledの切替をしましょう。
ルールの設定
プロジェクト用に.eslintrcを作成、設定します。
内容は後述。
ES6のルール・警告
ES6用のルールは以下にまとまってます。
とりあえず一通りのルールを設定してみて、自分のコードで警告が出たものについては、以下のように対応をしてみました。
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関連のルールは以下にまとまってます。
とりあえず一通りのルールを設定してみて、自分のコードで警告が出たものについては、以下のように対応をしてみました。
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>
);
}
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
現状こんな感じ。
{
"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書きます。
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追加して
{
...中略
"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