ESLint v9をモノレポで利用するときの考え方

2025/10/26

累計閲覧数 5 PV

ディレクトリ構成

pnpm-workspace.yamlは以下の通り。

packages:
  - packages/*
  - devtools/*

モノレポ向けのESLintの設定

とりあえず、体に良いものをありったけ入れる。 コードの後も文章は続く。

import pluginJs from "@eslint/js";
import { defineConfig, globalIgnores } from "eslint/config";
import importPlugin from "eslint-plugin-import";
import { configs as pnpmConfigs } from "eslint-plugin-pnpm";
import react from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import storybook from "eslint-plugin-storybook";
import pluginUnusedImports from "eslint-plugin-unused-imports";
import globals from "globals";
import tseslint from "typescript-eslint";

export default defineConfig([
  globalIgnores([
    "**/package.json",
    "**/pnpm-lock.yaml",
    "**/next-env.d.ts",
    "lib",
    "dist",
    "*.md",
    "*.d.ts",
    "**/out/",
    "**/.next/",
    "**/.storybook",
    "**/vitest.setup.ts",
  ]),
  { languageOptions: { globals: globals.browser } },
  {
    rules: {
      ...pluginJs.configs.recommended.rules,
    },
  },
  tseslint.configs.strict,
  {
    extends: tseslint.configs.stylistic,
    rules: {
      "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
    },
  },
  {
    extends: [importPlugin.flatConfigs.recommended],
    settings: {
      "import/internal-regex": "^@himenon/",
      "import/resolver": {
        typescript: true,
      },
    },
  },
  {
    ...react.configs.flat.recommended,
    settings: {
      react: {
        version: "19.2.0",
      },
    },
    rules: {
      ...react.configs.flat.recommended.rules,
      "react/prop-types": "off",
      "react/jsx-uses-react": "error",
      "react/jsx-tag-spacing": "error",
    },
  },
  reactHooks.configs.flat["recommended-latest"],
  {
    plugins: {
      "unused-imports": pluginUnusedImports,
    },
    rules: {
      "react/react-in-jsx-scope": "off",
      "react/no-children-prop": "off",
      "@typescript-eslint/no-unused-vars": "off",
      "unused-imports/no-unused-imports": "error",
      "unused-imports/no-unused-vars": [
        "warn",
        {
          vars: "all",
          varsIgnorePattern: "^_",
          args: "after-used",
          argsIgnorePattern: "^_",
        },
      ],
    },
  },
  ...storybook.configs["flat/recommended"],
  {
    files: ["**/*.ts", "**/*.tsx"],
    languageOptions: {
      parserOptions: {
        projectService: false,
        project: ["./devtools/*/tsconfig.json", "./packages/*/tsconfig.json"],
      },
    },
  },
  ...pnpmConfigs.json,
  ...pnpmConfigs.yaml,
]);

特にモノレポ内のtsconfig.jsonを読み取っておいたほうが、仮にaliasを設定した場合でも解決してくれる。

{
  project: ["./devtools/*/tsconfig.json", "./packages/*/tsconfig.json"],
}

個人プロダクトではaliasは使わないが、実プロダクトでは最初から入っているケースもあるので入れておくと良い。

globalIgnoresに色々と書いているが、.gitignore読んだほうが早いんじゃないか?とは思う。 ドキュメント(https://eslint.org/docs/latest/use/configure/ignore#including-gitignore-files)を読むと、 gitignoreを読み込む方法はあるらしい。

import { includeIgnoreFile } from "@eslint/compat";

動作確認

VSCodeを利用している場合は

ESLint: Show Output Channel

をコマンドパレットに入力してログを確認すると良い。そこにエラーがなければ動作しているはずだ。