ESLintをv8系→v9系にバージョンアップした
ESLint v9 にバージョンアップした話(Nuxt)
はじめに
この記事では、Nuxt プロジェクトで ESLint を v8 系から v9 系にバージョンアップし、同時に @nuxt/eslint-config を新たに導入した際のポイント、注意点、導入手順についてまとめます。
ESLint v9 では設定形式が変わったことや、eslint.config.js への移行が求められるため、既存プロジェクトとの互換性に配慮した対応が必要です。加えて、今回のアップデートでは @nuxt/eslint-config を組み込むことで Nuxt 向けの推奨設定を効率的に取り込むことができました。
変更の背景
- Nuxt 3 の開発環境でも最新 ESLint を使いたかった
 - 
@nuxt/eslint-configを新たに導入して、公式推奨の lint 設定を活用したかった 
アップグレード手順(概要)
1. ESLint v9 にアップグレード
npm install -D eslint@9.24.0
※バージョン指定はお好みで!
 2. @nuxt/eslint-config を導入
今回のアップデートで @nuxt/eslint-config を新たに追加しました。flat config に対応したバージョンを導入します。
npm install -D @nuxt/eslint-config@1.3.0
※バージョン指定はお好みで!
 3. 旧 .eslintrc を削除 or 無効化
ESLint v9 では .eslintrc が無効になって不要になりますが、移行が完全に完了するまでは参照用として置いておきます。
 4. eslint.config.mjs を新規作成
// eslint.config.mjs
import withNuxt from './.nuxt/eslint.config.mjs'
export default withNuxt(  
  {
    files: ["**/*.ts", "**/*.vue"],
    rules: {
      "no-console": "warn",
      ...etc
  },
)
  .override('nuxt/typescript/rules', {
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      ...etc
    },
  })
ハマりポイントと対処法
 ✅ 1. eslint コマンドが通らない
 課題1: --ignore-pathオプションが eslint.config.mjs に対応していない
- 公式ドキュメントによると、
—-ignore-pathのオプションはeslint.config.mjsには対応していない(eslintrcのみ対応)ので、このオプションをつけるとエラーになります 
 対策1: eslint-config-flat-gitignore を使う
npm install -D eslint-config-flat-gitignore
- 
eslint.config.mjsで .gitignore を読み込む設定を追加します: 
// eslint.config.mjs
import withNuxt from './.nuxt/eslint.config.mjs'
// 追記
import gitignore from "eslint-config-flat-gitignore";
export default withNuxt(
  // 追記
  gitignore(),
  {
    files: ["**/*.ts", "**/*.vue"],
    rules: {
      "no-console": "warn",
      ...etc
  },
)
  .override('nuxt/typescript/rules', {
    rules: {
      '@typescript-eslint/no-explicit-any': 'off',
      ...etc
    },
  })
 課題2: .nuxt ディレクトリに eslint.config.mjs が生成されておらず、ESLint がルールを読み込めない
 対策2: npm run dev などを一度実行して .nuxt を生成しておく
- 
.nuxt内部にeslint.config.mjsが生成されていないとエラーになるので、.nuxtファイルをbuildするコマンドを入れることで回避できました 
✅ 2. Flat config への移行ツールが活用しきれなかった
ESLint v9 では flat config 形式になったことで設定ファイルの書き方が刷新されたため、これまで使っていた .eslintrc.js や .eslintrc.json の内容をそのまま eslint.config.js に移植することができません。
eslint公式で移行手順は記載( https://eslint.org/docs/latest/use/configure/migration-guide )されており、npx@eslint/migrate-config .eslintrc といったコマンドで従来の .eslintrc を eslint.config.js(または .mjs)形式に変換することができます。しかし、自分たちのプロジェクトでは @nuxt/eslint-config の withNuxt を使用して設定をラップする方針で移行を進めているため、自動変換後の内容をそのまま適用することは難しく、各ルールを 1 つずつ確認・整理しながら移行を進める方針を取りました。
 対策:rules の中身をひとつずつコピペ&確認
- 
.eslintrcに記載していたルールをそのままeslint.config.mjsのrulesに転記 - プロジェクトのコードに対して一つずつ適用し、エラーが出るか確認しながら微調整
 
ファイル移行before/after
- 
before(
.eslintrc){ "extends": [ "@nuxtjs/eslint-config-typescript", "plugin:storybook/recommended" ], "rules": { "arrow-parens": 0, "comma-dangle": 0, "space-before-function-paren": 0, "vue/max-attributes-per-line": 0, "vue/multi-word-component-names": 0, "vue/singleline-html-element-content-newline": 0, "vue/require-default-prop": "error", "storybook/prefer-pascal-case": 0, "@typescript-eslint/no-unused-vars": [ "error", { "argsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_", "varsIgnorePattern": "^_" } ], "import/order": [ "error", { "groups": ["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"], "newlines-between": "always" } ], "@typescript-eslint/consistent-type-imports": "error", "@typescript-eslint/naming-convention": [ "error", { "selector": "typeAlias", "format": ["PascalCase"], "filter": { "regex": "^_[A-Za-z0-9]+$", "match": false } }, { "selector": "interface", "format": ["PascalCase"] } ] } } - 
after(
eslint.config.mjs)import withNuxt from './.nuxt/eslint.config.mjs' import { globalIgnores } from "eslint/config"; import storybook from 'eslint-plugin-storybook' import gitignore from 'eslint-config-flat-gitignore' export default withNuxt( ...storybook.configs['flat/recommended'], globalIgnores( [ '.storybook/**', ] ), gitignore(), { files: ['**/*.ts', '**/*.tsx'], rules: { 'no-console': 'off', '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/no-invalid-void-type': 'off', '@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-import-type-side-effects': 'off', '@typescript-eslint/no-wrapper-object-types': 'off', '@typescript-eslint/no-unused-expressions': 'off', '@typescript-eslint/no-dynamic-delete': 'off', '@typescript-eslint/unified-signatures': 'off', 'vue/no-ref-as-operand': 'off', 'no-unsafe-optional-chaining': 'off', "@typescript-eslint/naming-convention": [ "error", { "selector": "typeAlias", "format": ["PascalCase"], "filter": { "regex": "^_[A-Za-z0-9]+$", "match": false } }, { "selector": "interface", "format": ["PascalCase"] } ], "import/order": [ "error", { "groups": ["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"], "newlines-between": "always" } ], "@typescript-eslint/no-unused-vars": [ "error", { "argsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_", "varsIgnorePattern": "^_" } ], } }, ) .override('nuxt/typescript/rules', { rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/no-unused-expressions': 'off', '@typescript-eslint/unified-signatures': 'off', }, }) .override('nuxt/vue/rules', { rules: { 'vue/no-ref-as-operand': 'off', 'no-unsafe-optional-chaining': 'off', 'vue/require-default-prop': 'error', }, }) 
完全とまでにはいきませんが、新たに出たエラーをある程度直して、eslintで移行前と同じような使用感で利用できるようにはなりました。warningがかなり出るようになったので、まずはエラーのみを見やすくして解消するために—-quietオプションを付けて実行と調整をしました。
所感
ESLint v9 への移行は一見シンプルに見えますが、設定ファイル形式が大きく変わったことで、既存プロジェクトでは思った以上に手間がかかりました。
今回は @nuxt/eslint-config を新たに導入したこともあり、Nuxt プロジェクトに最適化された lint 設定を効率的に取り込めました。公式の flat config 対応状況を確認しつつ慎重に進める必要はありますが、一度移行してしまえば設定がシンプルかつ直感的になり、ルールの管理もしやすくなります。
まとめ
- ESLint v9 は 
eslint.config.jsへの移行が必須 - Nuxt 向けには 
@nuxt/eslint-configを導入することで対応が楽に - 
.vueファイル対応にはeslint-plugin-vueの明示的導入が必要 - 
-ignore-pathは使えないのでeslint-config-flat-gitignoreを使用する - 
.nuxtがないと ESLint が通らない場合はビルドを事前に行う 
ESLint v9 への移行と Nuxt 向け設定の導入を検討している方の参考になれば幸いです。
Discussion