🐱

ESLintを使ってモジュール間のimportを制限する

2022/12/11に公開

モジュラモノリス的な構成でプロジェクトを作成していると、別モジュールからの import を制限したくなります。

具体的には以下のような要件です。

  • modules/post からは modules/user のファイルを import できなくしたい
  • 同様に、modules/userからは modules/post のファイルを import できなくしたい
  • ただし、同一モジュール間では import の制限をしたくない
root/
  └ src/
      ├ share/
      │   └ components/**/* 共通コンポーネント
      └ modules/
          ├ user
          │   └ components/
          │       ├ UserProfile.tsx
          │       └ User.tsx
          └ post
              └ components/
                  └ Post.tsx
./src/modules/post/components/Post.tsx
// ./src/modules/user/components/Userからimport
import User from '@/modules/user/components/User' // エラーになってくれ!

const Post: React.FC = () => {
  return <div>post</div>;
};

export default Post;

eslint-plugin-importの設定

下記のライブラリをインストールします

yarn add -D eslint-plugin-import eslint-import-resolver-typescript

eslint で使用する設定を記載します。
ひとまず、./modules/postを他moduleからimportできないように制限します。
zonesに記載していきます。

eslintrc.js
module.exports = {
  extends: [
    "next/core-web-vitals", // NextJSのESLintの設定
    "plugin:import/recommended",
    "plugin:import/typescript",
  ],
  settings: {
    "import/resolver": {
      typescript: {},
    },
  },
  rules: {
    "import/no-restricted-paths": [
      "error",
      {
        zones: [
          {
            from: `./src/modules/post/!(public)/**/*`,
            target: `./src/modules/!(post)/**/*`
          }
        ]
      },
    ],
  }
}

fromは、制限するimport元、targetはimportの制限を適用するファイルです。

  • このパターンだと、「./src/modules/post」ディレクトリにあるファイルは、「./src/modules/post」以下のディレクトリからしかimportできなくなります
    (同一のモジュール間でしかimportできなくなります)
  • ただし、「./src/modules/post/public」にあるファイルは、他モジュールからimportをできるようにしています。

モジュールのパスを取得してルールを自動生成する

同様にuserのモジュールのimportも制限していきたいのですが、モジュールが増えるたびにeslintの設定を追加していくのは面倒です。

そこで、ディレクトリ一覧を取得してzoneに記載する項目を自動生成するようにします。

具体的には、下記のようにします

const fs = require('fs');
const modules = fs.readdirSync('./src/modules'); // ./src/modulesのディレクトリ一覧を取得
const zones = modules.map(module => ({
  from: `./src/modules/${module}/!(public)/**/*`,
  target: `./src/modules/!(${module})/**/*`
})); // 配列でディレクトリの名前が帰ってくるのでそれを元にeslintの設定を作成

あとはこちらをimport/no-restricted-pathsのzonesに渡せばOKです。

eslintrc.js
const fs = require('fs');
const modules = fs.readdirSync('./src/modules');
const zones = modules.map(module => ({
  from: `./src/modules/${module}/!(public)/**/*`,
  target: `./src/modules/!(${module})/**/*`
}));

module.exports = {
  extends: [
    "next/core-web-vitals",
    "plugin:import/recommended",
    "plugin:import/typescript",
  ],
  settings: {
    "import/resolver": {
      typescript: {},
    },
  },
  rules: {
    "import/no-restricted-paths": [
      "error",
      {
        zones,
      },
    ],
  }
}

これでモジュール間のimportを制限することができるようになりました。

以上!

参考記事

Discussion