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
※バージョン指定はお好みで!
@nuxt/eslint-config
を導入
2. 今回のアップデートで @nuxt/eslint-config
を新たに追加しました。flat config に対応したバージョンを導入します。
npm install -D @nuxt/eslint-config@1.3.0
※バージョン指定はお好みで!
.eslintrc
を削除 or 無効化
3. 旧 ESLint v9 では .eslintrc
が無効になって不要になりますが、移行が完全に完了するまでは参照用として置いておきます。
eslint.config.mjs
を新規作成
4. // 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
},
})
ハマりポイントと対処法
eslint
コマンドが通らない
✅ 1.
--ignore-path
オプションが eslint.config.mjs に対応していない
課題1: - 公式ドキュメントによると、
—-ignore-path
のオプションはeslint.config.mjsには対応していない(eslintrcのみ対応)ので、このオプションをつけるとエラーになります
eslint-config-flat-gitignore
を使う
対策1: 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
},
})
.nuxt
ディレクトリに eslint.config.mjs
が生成されておらず、ESLint がルールを読み込めない
課題2:
npm run dev
などを一度実行して .nuxt
を生成しておく
対策2: -
.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