every Tech Blog

株式会社エブリーのTech Blogです。

monorepo環境でeslint flat configを導入してみた

はじめに

この記事は、every Tech Blog Advent Calendar 2023 の21日目の記事です!

男梅シート、あのクセになるしょっぱさと噛めば噛むほど溢れ出てくる旨さは悪魔的ですよね。僕の推しです。

初めまして!エブリーで内定者インターンをしている @きょー です! インターンでは業務でサーバーやフロントをタスクベースで開発しています。

現在フロントのコードをリプレイスしていて、Eslintの設定ファイルを見直す機会をもらったのでその際に得た知見を共有していきたいと思います!

導入に至った背景

We expect the first alpha release of ESLint v9.0.0 to be released in December or January, depending on the progress we make on our tasks.

ESLint v9.0.0の最初のアルファリリースは、タスクの進捗にもよりますが、12月か1月にリリースされる予定です。

Eslint v9.0.0 からFlat Configという新しい設定ファイルがデフォルトになります。それに伴いこれまで使っていたeslintrc形式は非推奨になり、v10.0.0(公式:予定では2024年末〜2025年初頭) では完全に削除されてしまいます。現在のプロジェクトはeslintrc形式で記述してあったので対応する必要がありました。

いざ導入!

ゴール

「monorepoの各projectでflat configを導入すること」をゴールとします!現プロジェクトではmonorepoで開発しているためです。monorepoの説明は省きますが、詳しく知りたい方は circleci blog を見てみてください。

書いていく!

root配下に一つだけeslint.config.jsを置くところから移行は始めました。これからその過程を書いていこうと思います。また、turborepoを導入しているのでcacheをうまく使えるようにするのを意識しながら書いていきました。 最初は公式とuhyoさんの記事 を参考にさせていただきました。uhyoさん、丁寧でわかりやすい記事をありがとうございます!!! - https://eslint.org/docs/latest/use/configure/migration-guide - https://eslint.org/docs/latest/use/configure/configuration-files-new

Step 1:

一番最初は↓のような構成でした。root配下の設定ファイルの中で各projectのパスと設定したいrulesやその他tsconfig.jsonなどの設定ファイルをprojectごとに書いていきました。

|---/projectA
|---/projectB
|---/projectC
|---eslint.config.js
eslint.config.js
module.exports = [
  {
    files: ['/projectA/**/*.ts'],
    // その他設定
  },

  {
    files: ['/projectB/**/*.ts'],
    // その他設定
  },

  {
    files: ['/projectC/**/*.ts'],
    // その他設定
  },
];

しかしこの構成だとあるプロジェクト固有のlint設定を追加した時や、一部のlintを修正しただけで全てのlintのcacheが効かなくなってしまいます。

Step 2:

そこでその課題を解決するために各project配下に設定ファイルを置き、それぞれの設定ファイルから共通でlintさせたいrulesを読み込み適用させる必要がありました。最終的に↓のような構造になります。この状態だとそれぞれのprojectごとでcacheが残るようになります。

|---/projectA
  |---eslint.config.js
|---/projectB
  |---eslint.config.js
|---/projectC
  |---eslint.config.js
|---/eslint-config-custom
  |---index.js
eslint-config-custom/index.js

全projectで共通にしたいlintのruleやparserの設定が書かれています。

const globals = require('globals');
const tsEsLintParser = require('@typescript-eslint/parser');
const { FlatCompat } = require('@eslint/eslintrc');

const compat = new FlatCompat();

const jsRules = {
  // jsに適用するルール
};

const tsRules = {
  // js, tsに適用するルール
};

module.exports = [
  ...compat.extends('eslint-config-airbnb-base'),

  {
    rules: {
      ...jsRules,
    },
  },

  {
    files: ['/**/*.ts', '/**/*.tsx'],
    languageOptions: {
      parser: tsEsLintParser,
      parserOptions: {
        globals: {
          ...globals.browser,
        },
        sourceType: 'module',
        project: './tsconfig.json',
      },
    },
    rules: {
      ...jsRules,
      ...tsRules,
    },
  },
];
project~/eslint.config.js

index.jsの設定を引き継いだproject~/の設定ファイルです。ここではindex.jsでexportsされた設定を展開させ、projectごとで適応させたい設定(例えばtsconfig.jsonなど)を設定しています。

const custom = require('../eslint-config-custom/index.js');

module.exports = [
  ...custom,
  {
    // projectで設定したいsetting
  },
];

導入で躓いたところ

エディター上でlintが効かなくなる

設定ファイルを編集している時にエディター上でlintが効かなくなることがありました。原因としては適切にeslintのrulesの設定がされていなかったり、exportsされたeslintの設定ファイルが配列ではなくオブジェクトになっていたためでした。 import / exportsしているlintの設定ファイルをconsole.logで出力して確認したり、cliでlintをチェックして確認しましょう。

exportsされたeslintの設定は↓のようになっていれば適用されるはずです。

[
  {
    files: [ '/**/*.ts', '/**/*.tsx' ],
    rules: { 'no-unused-vars': 'off' }
  },
  {
    files: [ '/**/*.js' ],
    rules: { 'no-undef': 'off' }
  }
]

ワークスペースが認識されない

今回のような複数のprojectで作業するときにエディター上でeslintが効いていないように見えることがあります。cliでeslintをチェックしてみて期待しているエラーが出ている場合はvscode/settings.jsonに明示的にワークスペースを登録する必要があるかも知れません。自分はこれで解決しました!

例)

"eslint.workingDirectories": [
    "./projectA",
    "./projectB"
]

monorepo環境でflat configを導入してみての感想

monorepo環境下でも無事に導入できました!基本的にproject配下の設定ファイルではindex.jsをimportしてくるだけで共通の設定を適用できるので、projectが増えても簡単に拡張しやすい構成になっていると思っています!また依存性の管理も/eslint-config-customの中を見れば大体書いてあるので把握しやすくなっています。

eslint自体の知識が浅かったのですが、どのようなルールでlintingされていてコードの可読性が保たれているのかを知れる機会になったので勉強になりました。綺麗なコードはlintから、、、!!

終わりに

monorepo環境下でのeslint flat config導入で得た知見を紹介しました!書いた記事が皆さんに役に立てたら嬉しいです。

もし何かありましたらtwitterなどで聞きにきてください!

明日も記事が出ると思います!お楽しみに!!

ps. ホロパレード楽しい、、!!