🍴

マニュアルでReactのフロント環境構築(後編)

13 min read

前編の続き

6.リンターとフォーマッターを設定する

リンターにはESLintを、フォーマッターにはPrettierを使用します。
具体的な役割は以下のようになります。注意する点は、ESLintの対象はあくまでJavaScriptだということです。Prettierはすべての形式のファイルが対象です。

  • eslint

    • Javascriptファイルの文法チェック
    • Javascriptファイルフォーマットチェック、自動修正
      今回はreactとTypeScriptを使うのでそれらの文法チェックができるようにもします。
  • prettier

    • すべてのファイルのフォーマット(今回はsrc以下を対象とする)

※ESLintとPrettierは併用されることが多く、そのためのプラグインとしてeslint-plugin-prettierが有名ですが、公式が非推奨としています。なのでここでは使用しません。

6-1.ESLint

関連モジュール
eslint: ESLint本体
eslint-config-prettier: Prettierと重複するルールをoffにする
eslint-plugin-react: reactのjsx記法に対応する
@typescript-eslint/eslint-plugin:
@typescript-eslint/parser:

使用するファイル
.eslintrc.js: ESLint設定ファイル

module.exports = {
  root: true,
  env: {
    es6: true,
    node: true,
    browser: true,
  },
  parser: '@typescript-eslint/parser',
  parserOptions: {
    sourceType: 'module',
    ecmaVersion: 6,
    tsconfigRootDir: __dirname,
    project: './tsconfig.eslint.json',
  },
  // extendsから利用するためコメントアウト
  // plugins: ['react', '@typescript-eslint'],
  settings: {
    react: {
      version: 'detect',
    },
  },
  extends: [
    'eslint:recommended',
    // 'plugin:@typescript-eslint/eslint-recommended',
    // eslint:recommendedに含まれるルールを型チェックでカバーできるものは無効化とあったが公式に言及見当たらず
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
    'plugin:react/recommended',
    'prettier',
    'prettier/@typescript-eslint',
  ],
  ignorePatterns: [
    '.eslintrc.js', 'webpack.common.js', 'webpack.prod.js', 'webpack.dev.js', 'babel.config.js',
  ],
};
  • root: このファイルがプロジェクトのルートディレクトリにあることを示します。
    記載がなければより上位の階層に設定ファイルがないか探しにいきます。

  • env: 環境によって定義されたグローバル変数があります。ここに実行されうる環境を記載しておけば、その環境特有の変数をESlintはスルーしてくれます。書かないと違反として検出します。

  • browserはブラウザで実行することを想定して、es6はes6のスタイルでコーディングするので記載しておきます。nodeについては、開発はnode.js上でサーバーを実行しますが、ssrをしないのなら本番環境では必要ありません。今のところその辺の判断もわからないのでとりあえず書いておきます。
    これで今のところ不用意にエラー判定されることはないと思いますが、必要に応じて都度追記すればいいでしょう。
    (ESlint module.exports が no-undef なエラーになるとき)

  • parser: ESLintにTypeScriptを使用していることを知らせ、かつ構文も解釈できるようにします。
    TypeScriptとESLintについては、公式GitHubに説明があります。

  • parserOptions: ESLintはデフォルトでES5のみをサポートしています。ここでは、サポートするJSの細かな設定ができます。公式

  • sourceType:には、ESModuleでコーディングするので'module'、ecmaVersion:には6を記載します。

  • tsconfigRootDir: TSの設定ファイルであるtsconfig.jsがあるディレクトリを記載します。この指定は、型情報を使うルールが必要な場合に設定を要求されます。公式GitHub実際にそのようなルールを使うかわかりませんが、とりあえず指定しておきます。

  • project: リントの対象となるディレクトリを記したファイルが記載されています。最初はここを書かずにlintを実行したらエラーが出たので以下を参考に対処しました。
    typescript-eslint v2.x.xでパースに失敗する場合の対処法
    "parserOptions.project" has been set for @typescript-eslint/parser をなんとかする

  • settings: reactのバージョン指定をしています。
    こう記載することで自動的にpackage.jsonから検出してくれます。指定がないとリント実行時に警告がでます。
    (Warning: React version not specified in eslint-plugin-react settings.」を解決する)

  • plugins: 様々な拡張設定が行えます。npmモジュールとしてインストールして利用します。(モジュール名は、eslint-plugin-モジュールの固有名といった命名規則に則ります。)
    具体的に何が行われるのかは、公式:Configuring PluginsのNotes:1に記載があります。pluginsに設定することによってrequire('モジュール名')が実行され、envやrules、Processors に対して設定を追加したりできるようです。
    今回はreactとTypeScriptのpluginを設定してあります。configには、命名規則にあるプレフィックスの'eslint-plugin-'以降の文字列を記載します。
    一つ目にreactと記載があるので、これは'eslint-plugin-react'です。
    二つ目の@typescript-eslintは、@typescript-eslint/eslint-pluginです。先ほどの命名規則と違いますが、これはscoped packageと呼ばれるものに当たります。(scopeとは)
    scoped packageとは、関連するパッケージをグループ化するための仕組みです。命名規則は、@ユーザー名/パッケージ名です。このように同一のパッケージ名でコンフリクトを起こさずに複数のパッケージを登録できます。
    今回の場合だと、ESLintのプラグインはscoped package登録の際に、@scope名/eslint-plugin-plugin名@scope名/eslint-pluginといった命名規則を課しています。つまり、'eslint-plugin'というパッケージのグループに対して、typescript-eslintにより作成されたscoped packageということになります。configファイル中では、plugins:[@scope名, ...]といったように記述します。
    ここまで書いておきながら、実はpluginはextendsという項目からも使用することができます。なので本来の設定項目であるpluginsは丸ごとコメントアウトしてあります。

  • extends: これもルールの拡張設定をする項目です。
    まず、'eslint:recommended'と記載がありますが、これはESlintの推奨するルール集の中で推奨のものだけをonにするものです。
    その下にpluginを使用する記述が続きます。書き方はplugin:(プラグイン、スコープ名)/(オプション)です。(Using a configuration from a plugin)
    なので、plugin:@typescript-eslint/recommendedは@typescript-eslint/eslint-pluginの、plugin:react/recommendedはeslint-plugin-reactの推奨設定をonにしています。
    次のprettierですが、shareable configと呼ばれるnpmパッケージです。パッケージ名にはeslint-config-をプレフィックスにする規則があるので、これはeslint-config-prettierということです。
    shareable configとは、単純に設定情報のオブジェクトをexportするモジュールです。このeslintrc.jsと同じ項目を記載できるので、shareable configを適用することは、ルールなどを追加/オーバライドすることと同義です。
    改めて、eslint-config-prettierの機能ですが、prettierと競合するESLintの設定をoffにしてくれます。注意点は、extendsは記載順に適応されるため、最後にeslint-config-prettierを設定することです。そうすれば、それ以前の余計な設定をオーバライドしてくれます。


余談
eslint-plugin-reactnのドキュメントを見ても、extendsから推奨設定を行えばそれで最低限の機能は果たすように読めるのですが、pluginsのセクションでもplugins:[react]って書いてるのをよく見かけます。今回pluginsをコメントしても挙動に影響はなかったので、extendsから設定してあればそれで十分とおもいます。
また、pluginとshareable configについて機能に明確な違いがあるようには見えませんでした。性質的にはpluginは特定の言語やフレームワークに特化したルールを、shareable configはあくまでESLintのルールを追加しているという違いはありそうです。公式ドキュメント、情報が散りぢりで読みにくい……。どっかに書いてたりするんでしょうか。


rules: ルールには個別の検証項目を記載します。
(検証項目):[オプション]といった形式で記載します。
また、ルールは三段階で制御できます。

  • off オフにする。plugin(後述)などに含まれ一括で設定されているルールのなかから、個別で強制的にoffにするような使い方もできます。
  • warn 検知した違反を警告として扱う。(エラーでなければESlintによる自動修正に影響を受けない)
  • error 検知した違反をエラーとして扱う。修正の対象になる。
    以上から、例のような書き方になります。
rule-example.js
'indent': [ // インデントはスペース4つとし、違反をエラーとして報告
    'error',
    4
],
'linebreak-style': [ // 改行スタイルはUNIX(LF)とし、違反を警告として報告
    'warn',
    'unix'
]

ignorePatterns: リントの対象から除外するパターンを記載します。別ファイルに分けることもできますが、そんなにボリュームもないので今回はconfigファイルにまとめておきます。

6-2.prerttier

関連モジュール
prerttier: Prettier本体

使用するファイル
.prettierrc.js: Prettier設定ファイル

.prettierrc.js
  module.exports = {
    singleQuote: true,
    trailingComma: 'all',
    printWidth: 100,
  };

フォーマットの設定は少ないです。オプションを書いているだけなので説明は省きます。
公式:Options


ESLintとPrettierの併用
両社の併用についてですが、Prettierが公式で触れています。
Integrating with Linters
これによると、eslint-plugin-prettierなどを用いて、ESLintにPrettierを統合する方法もあるが、基本的には非推奨とのことです。なので、それぞれの導入はあくまで個別に行います。両方を実行するスクリプトも、単純にそれぞれのコマンドをつなげただけのものでモジュールとしては別々に機能しています。スクリプトについては次の見出しです。

また、以下のVSCodeの拡張機能も入れておきましょう。

  • ESLint
  • Prettier - Code formatter
    ESLintについては、VSCodeの画面下に青いメニューバーがあり、"ESLINT"の表記が出るはずです。赤くなっていたら適用が許可されていないので、クリックして有効にしておきましょう。

参考

7.リンターとフォーマッターのスクリプト

ESLintとPrettierで、違反を検出するだけのスクリプトと、修正を行うスクリプトをそれぞれ用意します。

package.json
...  
  "scripts":{
    "lintcheck": "eslint --ext ts,tsx src", // ESLintでチェック
    "lintfix": "eslint --fix --ext .tsx,.ts src", // ESLintで修正
    "formatcheck": "prettier --check src", // Prettierでチェック
    "formatfix": "prettier --write src" // Prettierで修正
  }
...

コマンドのオプション参考
Prettier CLI
ESLint Command Line Interface

8.GitHubで管理する

コードはGitHubで管理します。
リポジトリの作成など基本的なことは割愛しコードがGitHubの管理下にある前提で、今回採用した、必須でない便利な機能のみ紹介します。

8-1.Git Hooksを使って、ステージング時にリント/フォーマットする

ここまでの設定では、フォーマット修正はあくまでコマンドが作業者により実行された場合にのみ行われます。VSCodeの拡張機能を導入していれば保存時に自動で修正されますがコピペした時など事故が起きることがあったり、複数人で作業する場合は人によって拡張機能を入れていなかったりするかもしれません。
そういった不確定要素をカバーするために以下のツールを使います。

  • huskey
    Git Hooksの設定をpackage.jsonからできるようにするものです。
    Git Hooksとは、特定のGit操作が行われた際に決められたスクリプトを実行するといった機能です。今回やろうとしている動作に当てはめると、git addしたときにESLint、Prettierのスクリプトを実行する、といった感じです。

  • lint-staged
    ステージングされたファイルに対しリントをかけ、コードによからぬものが混ざりこむのを防ぎます。

下記のように、huskyからhooknの機能を呼び出し、lint-stagedを実行するように書きます。
lint-stagedでは、リンターとフォーマッターのコマンドを指定しています。

package.json
"husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "src/**/*.{ts,tsx}": [
      "eslint --fix --ext .tsx, .ts src"
    ],
    "./src/**": [
      "prettier --write src"
    ]
  }

参考

8-2.GitHubの管理対象から外す

.gitignoreというファイルを特定のディレクトリに作成することで、その階層以下のディレクトリを、コードの管理対象から外すよう指定することができます。今回はルートディレクトリに作成します。

.gitignore
node_modules/

上記ではnode_modulesを指定しています。
基本的に、package.jsonの存在があるためパッケージの本体が保存されているこのディレクトリは共有の必要がありません。リポジトリのREADME.mdでも案内しているように、プロジェクトをチェックアウトした直後は、最初にパッケージのインストールを行います。
また、yarn.lockはignoreに入れてはいけません。ここにはパッケージのバージョン情報が記録されているので、これがないとyarn installを行う時期によってパッケージのバージョンにユーザー間でばらつきがでてしまいます。


その他参考
node.jsドキュメント
how to exclude css files from eslint parser in React
【npm】パッケージとモジュールの違いって何?
About packages and modules

おわり

これで完了です。

感想

とにかく整合のとれる情報を集めるのが大変でした。なるべく一次情報をもとにしつつ、足りない部分は情報の公開時期を絞って調べながらやったのですが、フロントエンドは流行り廃りのサイクルが早いといわれるのが実感できました。
個人開発においてはじめての記事の内容がこれで、正直千回くらい挫折しそうになりましたが地道にやっていきたいです。

Discussion

ログインするとコメントできます