フロントエンドにESLint、Prettier、stylelintを導入して快適な開発環境を整えました

こんにちは、Webチームの柴山(@shikeapp0909)です。

私たちのプロダクト「ままのて」のフロントエンドはReact + SCSSで開発しています。コード規約は存在するもののインデントやセミコロンのつけ忘れなどをいちいちコーディングの際に気をつけながら開発するのも、コードレビューでチェックして防ぐのも余計な労力で効率が悪いため、リントとコードフォーマッターを入れることにしました。

今回はその導入内容を手順と共にご紹介します。

JSのリントとフォーマッター

JSの方はESLintPrettierでリントとフォーマットを行うようにしました。

JavaScript Standard Style

基本的なJSのコード規約はJavaScript Standard Styleに準拠することとしました。

公式から簡単にルールを抜粋すると、以下のようになっています。

  • インデントは2スペース
  • 文字列はシングルクオート
  • 未使用変数は禁止
  • 文末セミコロンは禁止
  • キーワードの後にスペースを入れる
  • 関数名の後にはスペースを入れる
  • 値の比較には==ではなく===を使う

こちらの記事が詳しく解説してくださっていますので、読んでみてください。

JavaScript Standard Styleのススメ - Qiita

これをESLintのルールとして適用するようにしています。そのためのプラグインが用意されているので、ESLint本体とそれらをインストールします。

$ yarn add -D eslint eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node

※yarnは適宜npmと読み替えてください。

そしたらpackage.jsonと同じディレクトリに.eslintrc.ymlファイルを作成し、以下のように記述します。

extends:
  - standard

これだけでESLintにJavaScript Standard Styleの設定は完了です。

React用のESLint

次にReact用の設定です。こちらもESLint用のプラグインが用意されているので、それをインストールします。

$ yarn add -D eslint-plugin-react eslint-plugin-jsx-a11y

そしたら先ほどの.eslintrc.ymlに追記します。

extends:
  - standard
# ↓追加
  - plugin:react/recommended
plugins:
  - react
settings:
  react:
    version: 16.6 # お使いのReactのバージョン
# ↑追加

これで、Reactのバージョンに応じてDeprecatedになったAPIをしようしていないかなどをチェックしてくれます。

このルールではPropTypesの定義が必須なのですが、既存のコードではPropTypesをまったく記述していなかったので、今回はこのルールをいったん除外することにしました。そのためには.eslintrc.ymlrulesに追加します。

extends:
  - standard
  - plugin:react/recommended
plugins:
  - react
# ↓追加
rules:
  react/prop-types:
    - 0
# ↑追加
settings:
  react:
    version: 16.6 # お使いのReactのバージョン

これでPropTypesを定義していなくても怒られなくなりました。

Prettier

最後にPrettierの設定です。PrettierはJSだけでなくCSSなどさまざまなファイルをフォーマットしてくれる便利なプラグインです。設定できるルールについては、公式ドキュメントで確認してください。

Prettierのデフォルトのルールは先ほどのJavaScript Standard Styleと重複していて相反するものがいくつかあるので、カスタマイズして使用するため、.prettierrc.ymlファイルを.eslintrc.ymlファイルと同じディレクトリに作成し、以下のように記述します。

printWidth: 120
semi: false
singleQuote: true
trailingComma: es5

デフォルトだと80文字を越えると改行されますが、それだと少し短いと感じたので、とりあえず120文字と設定し、配列とobjectには最後のプロパティの後ろにカンマを入れるように設定しました。

これでPrettierの設定はできましたが、こちらに記載されているように、Prettierの設定をESLintに取り込んで、ESLintのルールとして実行することが可能です。そのためのプラグインをインストールします。

$ yarn add -D eslint-plugin-prettier eslint-config-prettier

そしたら.eslintrc.ymlファイルに以下を追加します。

extends:
  - standard
  - plugin:react/recommended
  - prettier # ←追加
plugins:
  - react
  - prettier # ←追加
rules:
  react/prop-types:
    - 0
  # ↓追加
  prettier/prettier:
    - error
  # ↑追加
settings:
  react:
    version: 16.6 # お使いのReactのバージョン

eslint-plugin-prettierの方はESLintのルールとしてPrettierのルールを読み込んで処理するプラグインで、eslint-config-prettierはESLintで設定されているルールとPrettierのルールが重複しないようにするためのプラグインです。

これでESLintとPrettierの設定の統合までできました。

グローバル変数などの対応

このままだと、windowjQuery$などのグローバル変数などが未定義の変数として怒られてしまうので、それらを除外するための設定を.eslintrc.ymlに追加します。

extends:
  - standard
  - plugin:react/recommended
  - prettier
plugins:
  - react
  - prettier
# ↓追加
env:
  browser: true
  jquery: true
globals:
  googletag: true
  ga: true
# ↑追加
rules:
  react/prop-types:
    - 0
  prettier/prettier:
    - error
settings:
  react:
    version: 16.6

envbrowser: truewindowなどが、jquery: truejQuery$が怒られなくなります。 globalsにはその他に除外したいグローバル変数を追加すると怒られなくなります。今回はGoogle AnalyticsGoogle Adsenseなどのグローバル変数が定義されているので、それらを除外しました。

これでESLintはすべての設定が終わりました。

npm scriptを設定する

後はnpm scriptでリントを実行できるようにします。

package.jsonscriptsに以下のように記述します。

"scripts": {
  "eslint": "eslint -c .eslintrc.yml --color './src/**/*.{js,jsx}'",
  "eslint:fix": "eslint -c .eslintrc.yml --color './src/**/*.{js,jsx}' --fix"
}

これでyarn eslintでESLint実行、yarn eslint:fixでESLint実行と自動フォーマットができるようになりました。

webpackビルド時にESLintを実行するようにする

webpackを使っている場合は、ビルド時にESLintを実行させることも可能です。そのためにはeslint-loaderをインストールします。

$ yarn add -D eslint-loader

そしたらwebpackの設定ファイルに追加します。

module.export = {

  ...

  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {
          fix: true,
          configFile: '.eslintrc.yml',
        },
      },
  ...
}

上記のように、loaderの設定を追加するだけで、ビルド時にESLintを実行してくれます。enforce: 'pre'を指定することで、webpackビルド前に実行してくれるようになります。optionsfix: trueを指定して、自動フォーマットまでするようにしたかったのですが、うまく機能してくれてません。。。公式を見る限りではこれだけでやってくれそうな感じなのですが。。。原因がわかる方いらっしゃったら教えていただきたいです。

エディターの設定

後はお使いのエディターのプラグインに、ESLint用のものがおそらくあるかと思いますので、それをインストールすれば、自動で設定ファイルを読み込んでコーディング時にリアルタイムでリントしてくれるようになると思います。ファイル保存時にフォーマットを実行するオプションを使えば、さらに快適にコーディングできると思います。ちなみに私はVS Codeを使用しています。

SCSSのリント

SCSSのリントには、stylelintを選びました。FacebookGitHubWordPressでも使用されており、豊富なルールが用意されていて、導入事例も増えているように思います。何よりもESLint同様自動フォーマットオプションがついているのが大変ありがたいです。

導入

stylelint本体と、公式でも取り上げられているプリセットルール、そしてSCSS用のプラグインを入れます。

$ yarn add -D stylelint stylelint-config-standard stylelint-scss

stylelint-config-standardの他にstylelint-config-recommendedもありますが、すごくざっくり言うと後者は最小限のルールのみで、前者がstylelint-config-recommendedを拡張したもので、よりかっちりしたルールといった感じでしょうか。今回は詳細なルールを決めてより統一感のあるコードにしたかったので、stylelint-config-standardを選択しました。

インストールしたら、ESLint同様、.stylelintrc.ymlファイルを作成し、以下のように記述します。

plugins:
  - stylelint-scss
extends:
  - stylelint-config-standard
syntax:
  - scss
rules:
  at-rule-no-unknown: null
  scss/at-rule-no-unknown: true

SCSSの@extendなどの機能を使う場合、at-rule-no-unknownは対応しておらず怒られてしまうため無効にしないといけません。代わりにscss/at-rule-no-unkwonを有効にすることで対応できます。

次に、プロパティのソート順をリントしてくれるプラグインを入れます。今回はstylelint-config-recess-orderを入れました。これはstylelint-orderをベースに、機能単位でのソート順が定義されています。

$ yarn add -D stylelint-config-recess-order

インストールしたら、.stylelintrc.ymlに追加します。

plugins:
  - stylelint-scss
extends:
  - stylelint-config-standard
  - stylelint-config-recess-order # ←追加
syntax:
  - scss
rules:
  at-rule-no-unknown: null
  scss/at-rule-no-unknown: true

また、ままのてでは上記の他にもまだ定義できるルールがいくつかあるので、そちらも定義しています。

plugins:
  - stylelint-scss
extends:
  - stylelint-config-standard
  - stylelint-config-recess-order
syntax:
  - scss
rules:
  at-rule-no-unknown: null
  scss/at-rule-no-unknown: true
  at-rule-no-vendor-prefix: true # @ルールのベンタープリフィックス禁止
  font-family-name-quotes: always-where-recommended # 'font-family'はスペースで区切られたフォント名の場合クオートで囲む
  font-weight-notation: named-where-possible # 'font-weight'はnormalなどのキーワードが使える場合はそちらを使う(400はNG)
  function-url-quotes: always # 'url()'の引数はクオートで囲む
  media-feature-name-no-vendor-prefix: true # '@media'内のベンダープリフィックス禁止
  no-descending-specificity: null # 詳細度の高いセレクタより後に詳細度の低いセレクタを定義するのを許容
  property-no-vendor-prefix: true # プロパティのベンダープリフィックス禁止
  selector-attribute-quotes: never # '[type=text]'などのセレクタの属性はクオートで囲まない
  selector-no-vendor-prefix: true # セレクタのベンダープリフィックス禁止
  string-quotes: single # 文字列はシングルクオートで囲む
  value-no-vendor-prefix: true # 値のベンダープリフィックス禁止

no-descending-specificityについては、オンにしている場合にSCSSで開発していると、たとえば以下のような場合このルールに引っかかります。

textarea {
  width: 100%;

  &:focus {
    outline: none;
  }
}

どうやらこれはtextarea:focusの方が先に評価されるらしく、このルールに引っかかるようです。そのため、このルールはオフにしています。

ベンダープリフィックス系はautoprefixerを使っているのですべて禁止するようにしています。

ここまでできたら、npm scriptでリント実行できるようにします。package.jsonに以下のように記述します。

"scripts": {
  "stylelint": "stylelint --config .stylelintrc.yml './src/**/*.scss'",
  "stylelint:fix": "stylelint --config .stylelintrc.yml './src/**/*.scss' --fix"
}

これでyarn stylelintでリント実行、yarn stylelint:fixでリント実行と自動フォーマットができるようになります。

webpackビルド時にstylelintを実行するようにする

ESLint同様、stylelintもwebpackビルド時に実行するためのプラグインがあるので、そちらをインストールします。

$ yarn add -D stylelint-webpack-plugin

インストールしたら、webpackの設定ファイルのpluginsに以下のように追加します。

const StyleLintPlugin = require('stylelint-webpack-plugin')

module.export = {

  ...

  plugins: [
    ...

    new StyleLintPlugin({
      configFile: '.stylelintrc.yml',
    }),
  ],

  ...
}

これでwebpackビルド時にstylelintも実行されるようになりました。

エディターの設定

こちらもESLint同様、エディター用のプラグインが用意されているかと思いますので、そちらをインストールすればコーディング時にリアルタイムでリントしてくれるようになります。

まとめ

ESLintとstylelintをまた導入したばかりですが、確実に快適に開発できるようになって、生産性も上がっていると思います。万が一リントのエラーを見逃してコミットしても、ままのてではCIでリントを実行するように設定してあるので、そちらで拾ってくれるようになっています。実装者の負担も減るし、誰が書いてもスタイルの差が生じにくくなったと思います。

やはり快適に気持ちよく開発できる環境は大事だと改めて実感しました!

We're hiring!!

弊社ではWeb / ネイティブアプリエンジニアを募集しております。 ご興味がありましたらお気軽にご連絡下さいませ! エンジニアの方、ぜひ情報交換しましょう!

サーバーサイド

www.wantedly.com

ネイティブアプリ

www.wantedly.com