Wondershake 開発者ブログ

LOCARI(ロカリ)の運営会社の開発者ブログです。

RailsアプリケーションのためのフロントエンドLint環境の整備(JS編)

こんにちは。フロントエンドエンジニアの佐々木です。

まだ入社して2ヶ月ほどですが、最初の1ヶ月は主に Lint 環境の整備などを行ってきたのでその紹介をしてみます。

はじめに

Wondershake では Locari というサービスを運営しているのですが、2年以上運用されており様々な人達が関わって作られてきました。

コードベースは Ruby on Rails 4 の上に作られており、github-flow 形式で PR を作って開発しています。

レビューで致命的な問題は取り除かれているものの、多人数で作り上げられたコードは人によって書き方が違ったりと、どうしても細部のルールが揺れてしまっている状態でした。

そこでコーディング規約を機械的にチェックする Linter を入れ、ルールに適合しないコードは CI(CircleCI) で落とすようにしました。
これで最低限コーディング規約に従ったコードだけをレビューに出すことができ、一定の品質を保てるようになります。

この記事でやること

  • CoffeeLintの導入
  • ESLintの導入

※ nodejs が既に入っていることを前提として書いています。

CoffeeLintの導入

f:id:sskyu:20160627204133p:plain

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の導入

f:id:sskyu:20160627203914p:plain

管理画面側の一部で browserify-rails + babelify を使って書かれた ES2015+ なコードがあります。
これらを ESLint を使って構文チェックさせたいと思います。

ESLint には他の人が作った設定をインポートできる仕組みがあるので、定番の airbnb の設定を使います。

eslint-config-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:coffeelintnpm run test:eslint が実行されるようになりました。
(あとで "test:unit" を作ろうと思っているので "test""test:lint" を分けています)

まとめ

今回は CoffeeLint と ESLint の導入方法をご紹介しました。
ブランチをプッシュしたら CI で npm t を実行するようにすればコーディング規約を逸脱したコードがマージされることを防ぐことができます。

今回は長くなったので JS 編としました。
似たような感じで scss-lint の導入についても後日ご紹介できればと思います。

Wondershake では サーバーサイドエンジニアや iOSAndroid ディベロッパーを募集しています。
興味が湧いた方は是非こちらからご応募下さい!

www.wantedly.com