RailsアプリケーションのためのフロントエンドLint環境の整備(JS編)
こんにちは。フロントエンドエンジニアの佐々木です。
まだ入社して2ヶ月ほどですが、最初の1ヶ月は主に Lint 環境の整備などを行ってきたのでその紹介をしてみます。
はじめに
Wondershake では Locari というサービスを運営しているのですが、2年以上運用されており様々な人達が関わって作られてきました。
コードベースは Ruby on Rails 4 の上に作られており、github-flow 形式で PR を作って開発しています。
レビューで致命的な問題は取り除かれているものの、多人数で作り上げられたコードは人によって書き方が違ったりと、どうしても細部のルールが揺れてしまっている状態でした。
そこでコーディング規約を機械的にチェックする Linter を入れ、ルールに適合しないコードは CI(CircleCI) で落とすようにしました。
これで最低限コーディング規約に従ったコードだけをレビューに出すことができ、一定の品質を保てるようになります。
この記事でやること
- CoffeeLintの導入
- ESLintの導入
※ nodejs が既に入っていることを前提として書いています。
CoffeeLintの導入
CoffeeLint は特に悩むこと無く導入することが出来ると思います。
$ npm i -D coffeelint # devDependenciesに追加
CoffeeLint は CLI で実行できるようなので、試しに動かしてみます。
./node_modules/.bin/coffeelint app/assets/javascripts ✗ app/assets/javascripts/admin_post_form/form/dropdown.coffee ✗ #7: Line exceeds maximum allowed length. Length is 82, max is 80. ✓ app/assets/javascripts/admin_post_form/form/edit_post_cover_picture.coffee # 途中省略 ✗ Lint! » 33 errors and 0 warnings in 52 files
ほとんどのエラーが mux_line_length
のエラーでした。
※ 各ルールは公式ドキュメントでご確認ください。
CoffeeLint - Lint your CoffeeScript
デフォルトで有効なルールには適合するようにコードを修正したいところですが、CoffeeScript で一行 80 文字制限は少々厳しいので、このルールを無視して実行するようにしたいと思います。
プロジェクトルートに下記の内容で .coffeelint.json
を作成します。
(ファイル名はなんでも良いですが、.json
で作成するのをおすすめします。)
{ "max_line_length" : { "value": 80, "level": "ignore" } }
"level": "ignore"
にすることでルールを無視することができます。
"level": "warn"
だと警告を出力し、 "level": "error"
だとエラーを出力します。
もし一行の文字数の上限を上げたい場合は "value": 120, "level": "error"
などにすると良いです。
この設定ファイルを読み込むように指定し、もう一度 CoffeeLint を実行してみます。
./node_modules/.bin/coffeelint -f .coffeelint.json app/assets/javascripts ✓ app/assets/javascripts/admin_post_form/form/dropdown.coffee # 途中省略 Ok! » 0 errors and 0 warnings in 52 files
エラーが表示されなくなりました。
最後に、CoffeeLint を動かすコマンドが長いので npm scripts に登録しておきます。
package.json の scripts の項に test:coffeelint
として登録します。
"scripts": { "test:coffeelint": "coffeelint -f .coffeelint.json app/assets/javascripts" }
これで $ npm run test:coffeelint
だけで CoffeeLint を走らせることが出来るようになりました。
ESLintの導入
管理画面側の一部で browserify-rails + babelify を使って書かれた ES2015+ なコードがあります。
これらを ESLint を使って構文チェックさせたいと思います。
ESLint には他の人が作った設定をインポートできる仕組みがあるので、定番の airbnb の設定を使います。
下記コマンドで一連のモジュールをインストールします。
npm i -D eslint-config-airbnb eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y eslint babel-eslint
それぞれのモジュールの役割は以下の通りです。
eslint-config-airbnb
- airbnb の eslint 設定
- 今回はこれを使いまわしたい
eslint-plugin-import
- eslint の設定をインポートするために必要
eslint-plugin-react
- eslint で react を扱うために必要
eslint-plugin-jsx-a11y
- eslint で jsx を扱うために必要
eslint
- eslint 本体
babel-eslint
- babel 側で読み込んだ拡張構文を eslint 側に対応させるために必要
- ES6/ES7 の範囲で書かれていれば不要
- React Components で static property を使っているため今回は必要
モジュールをインストールできたらプロジェクトルートに .eslintrc
を作成します。
{ "parser": "babel-eslint", "extends": "airbnb", "env": { "browser": true, "node": true }, "globals": { "ga": true, "$": true, "I18n": true }, "rules": { // 必要ならここに airbnb の eslint ルールを上書きしていく } }
"extends": "airbnb"
で eslint-config-airbnb
の設定を読み込んでいます。
"globals"
には window
直下に作られているグローバル変数を記述しておきます。
airbnb のルールは結構厳しめで作られているので、不都合ある場合は "rules"
に追記してルールを上書きしていきます。
この時点で一度 ESLint を実行してみます。
./node_modules/.bin/eslint --ext .js,.jsx app/assets/javascripts # 中略 5:398 error Missing semicolon semi 5:401 error 'jQuery' is not defined no-undef ✖ 53669 problems (52666 errors, 1003 warnings)
出力が完了するまで20秒くらいかかりました。
これだけ時間がかかった原因は app/assets/javascripts/lib/*
以下の js ファイルも ESLint 対象になっているためのようです。
外部ライブラリは ESLint 対象にする必要がありませんので、プロジェクトルートに下記の内容で .eslintignore
を作成し、ESlint の対象にしたくないファイルを追記していきます。
app/assets/javascripts/lib/*
もう一度実行してみます。
./node_modules/.bin/eslint --ext .js,.jsx app/assets/javascripts /path/to/hoge.js 6:3 warning Unexpected constant condition no-constant-condition /path/to/fuga.js 8:3 warning Unexpected constant condition no-constant-condition 26:3 warning Unexpected constant condition no-constant-condition /path/to/piyo.js 6:3 warning Unexpected constant condition no-constant-condition ✖ 4 problems (0 errors, 4 warnings)
出力が減って見やすくなりました。
no-constant-condition
の警告が出力されています。
当該箇所のコードは while (true) {}
で無限ループを作っている箇所でした。
これはどうしてもコード側を修正することができないので、警告を表示しないようにルールを追加します。
{ "parser": "babel-eslint", "extends": "airbnb", "env": { "browser": true, "node": true }, "globals": { "ga": true, "$": true, "I18n": true }, "rules": { // 追記 "no-constant-condition": ["error", { "checkLoops": false }] } }
もう一度実行してみます。
./node_modules/.bin/eslint --ext .js,.jsx app/assets/javascripts
出力無しなので正常終了したようです。
こちらもコマンドが長いので package.json
の scripts に登録しておきます。
"scripts": { "test": "npm run test:lint", "test:lint": "npm run test:coffeelint && npm run test:eslint", "test:coffeelint": "coffeelint -f .coffeelint.json app/assets/javascripts", "test:eslint": "eslint --ext .js,.jsx app/assets/javascripts" }
これで $ npm t
を実行するだけで npm run test:coffeelint
と npm run test:eslint
が実行されるようになりました。
(あとで "test:unit"
を作ろうと思っているので "test"
と "test:lint"
を分けています)
まとめ
今回は CoffeeLint と ESLint の導入方法をご紹介しました。
ブランチをプッシュしたら CI で npm t
を実行するようにすればコーディング規約を逸脱したコードがマージされることを防ぐことができます。
今回は長くなったので JS 編としました。
似たような感じで scss-lint の導入についても後日ご紹介できればと思います。
Wondershake では サーバーサイドエンジニアや iOS、Android ディベロッパーを募集しています。
興味が湧いた方は是非こちらからご応募下さい!