ESLint 9.x Flat ConfigとStylelintをVitePressに導入する完全ガイド
VitePressで構築した静的サイトにESLint 9.xとStylelintを導入しました。
ESLint 9.xから採用された新しいFlat Config形式を採用し、TypeScript/JavaScript/CSSのLint環境を整備しています。
また、GitHub ActionsでCI/CD自動Lintを実現し、コード品質の向上と一貫性のあるコードスタイルの維持を目指しました。
本記事では、導入から設定、つまづきポイントまで、実際のコードを交えて詳しく解説します。
こんな人におすすめ
この記事は、以下のような方におすすめです。
- VitePressやVue.jsプロジェクトにLint環境を導入したい方
- ESLint 9.xの新しいFlat Config形式を知りたい方
- TypeScriptとJavaScriptが混在するプロジェクトでLint設定に悩んでいる方
- GitHub Actionsで自動Lintを構築したい方
- StylelintでCSSの品質管理を始めたい方
目次
- ESLint設定の全体像
- ESLint 9.x Flat Configとは
- TypeScript/JavaScript混在環境の設定
- Stylelintの設定(VitePress対応)
- GitHub Actions CI/CDパイプライン
- つまづきポイントと解決策
- 得られた効果
- よくある質問(FAQ)
- まとめ
ESLint設定の全体像
今回構築したLint環境の構成を図で示します。
flowchart TD
subgraph ESLint
A[eslint.config.js] --> B[JavaScript Files]
A --> C[TypeScript Files]
A --> D[Cloudflare Functions]
B --> E[js.configs.recommended]
C --> F[tseslint.configs.recommended]
D --> G[Workers Globals]
end
subgraph Stylelint
H[.stylelintrc.js] --> I[CSS Files]
H --> J[Vue SFC]
end
subgraph CI/CD
K[GitHub Actions] --> L[ESLint]
K --> M[Stylelint]
K --> N[Tests]
end
ESLint 9.x Flat Configとは
ESLint 9.xから導入されたFlat Config形式(eslint.config.js)を使用しました。
従来の.eslintrc.js形式と比較して、以下のメリットがあります。
| 特徴 | 従来形式(.eslintrc.js) | Flat Config(eslint.config.js) |
|---|---|---|
| 記述形式 | JSON/YAML/JS | JavaScript(ESM)のみ |
| ファイルタイプ別設定 | overridesで記述 | files配列で簡潔に |
| 共有設定 | extendsで継承 | スプレッド構文で合成 |
| TypeScript対応 | 別途設定が必要 | ネイティブサポート向上 |
特に、filesプロパティでファイルタイプごとに異なる設定を適用できるため、TypeScriptとJavaScriptで別々のパーサーを使用する構成が実現しやすくなっています。
2026年2月時点の補足: ESLint v10.0.0(2026-02-06リリース)で、旧来の
.eslintrc.*形式(eslintrc)のサポートが完全に廃止されました。ESLint 9.x系は2026-08-06にEOL(サポート終了)を迎えているため、本記事で紹介しているFlat Configへの移行は遅かれ早かれ必須の作業になります。
v10ではNode.jsの対応バージョンも
^20.19.0 || ^22.13.0 || >=24に引き上げられているため、アップグレード時はランタイムのバージョンも合わせて確認してください。
eslint.config.js(新規)
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import vue from 'eslint-plugin-vue';
import globals from 'globals';
export default [
// Global ignores
{
ignores: [
'node_modules/**',
'docs/.vitepress/dist/**',
'docs/.vitepress/cache/**',
'dist/**',
'coverage/**',
'.wrangler/**',
'build/**'
]
},
// Base JavaScript rules
js.configs.recommended,
// JavaScript files (.mjs, .js)
{
files: ['**/*.mjs', '**/*.js'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: { ...globals.node }
},
rules: {
'spaced-comment': ['error', 'always', { markers: ['/'] }],
'no-console': 'off',
'no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
'no-prototype-builtins': 'off'
}
},
// TypeScript configuration
...tseslint.configs.recommended.map(config => ({
...config,
files: ['**/*.ts', '**/*.mts']
})),
// TypeScript specific overrides
{
files: ['**/*.ts', '**/*.mts'],
languageOptions: {
parser: tseslint.parser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: null
},
globals: {
...globals.node,
...globals.browser
}
},
rules: {
'spaced-comment': ['error', 'always', { markers: ['/'] }],
'no-console': 'off',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/require-await': 'off',
'no-prototype-builtins': 'off'
}
},
// Cloudflare Pages Functions
{
files: ['functions/**/*.ts'],
languageOptions: {
globals: {
...globals.node,
fetch: 'readonly',
Request: 'readonly',
Response: 'readonly',
Headers: 'readonly'
}
},
rules: { '@typescript-eslint/no-explicit-any': 'off' }
}
];
TypeScript/JavaScript混在環境の設定
このプロジェクトでは、TypeScript(.ts, .mts)とJavaScript(.js, .mjs)が混在しています。
つまづいた点
最初はtseslint.configs.recommendedをグローバルに適用していましたが、これにより.jsファイルでも@typescript-eslintルールが適用されてエラーが発生しました。
解決策
filesプロパティで.ts/.mtsファイルのみにTypeScript設定を適用するように修正しました。
flowchart LR
A[ファイル検出] --> B{拡張子判定}
B -->|.ts / .mts| C[TypeScript Parser]
B -->|.js / .mjs| D[JavaScript Parser]
C --> E[tseslint.rules]
D --> F[js.configs.rules]
日本語コメントの保護
プロジェクトで日本語コメントを使用しているため、spaced-commentルールにmarkers: ['/']オプションを指定して、日本語コメントを許可しています。
これにより、// コメント形式の日本語コメントがエラーにならず、コードの可読性を維持できます。
Cloudflare Pages Functionsの対応
Cloudflare Workers環境では、fetch, Request, Response, Headersなどのグローバル変数が利用可能です。
これらをreadonlyとして設定することで、未使用変数警告を回避しています。
Stylelintの設定(VitePress対応)
StylelintでCSSのLint設定を作成しました。
VitePressのカスタムプロパティや疑似クラス/疑似要素に対応するため、一部のルールを無効化しています。
.stylelintrc.js(新規)
export default {
extends: [
'stylelint-config-standard',
'stylelint-config-standard-vue'
],
rules: {
// VitePress対応
'custom-property-pattern': null,
'alpha-value-notation': null,
'color-function-notation': null,
'selector-class-pattern': null,
'selector-id-pattern': null,
'declaration-block-no-shorthand-property-overrides': null,
'selector-pseudo-class-no-unknown': [true, { ignorePseudoClasses: ['global', 'deep', 'slotted'] }],
'selector-pseudo-element-no-unknown': [true, { ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted'] }],
'font-family-name-quotes': null,
'keyframes-name-pattern': null,
'unit-allowed-list': null,
// 日本語コメント対応
'comment-empty-line-before': null,
'comment-whitespace-inside': null,
'declaration-empty-line-before': null,
// VitePressオーバーライド用
'declaration-no-important': null,
'no-duplicate-selectors': null,
'max-empty-lines': 2
},
ignoreFiles: [
'node_modules/**',
'docs/.vitepress/dist/**',
'coverage/**',
'**/*.min.css'
]
};
GitHub Actions CI/CDパイプライン
GitHub ActionsでCI/CDパイプラインを作成しました。
プッシュ時とPR時に自動的にLintが実行されます。
CI/CDフロー
flowchart LR
A[Push / PR] --> B[Checkout]
B --> C[Setup Node.js]
C --> D[npm ci]
D --> E[ESLint]
D --> F[Stylelint]
D --> G[Tests]
E --> H{全てPass?}
F --> H
G --> H
H -->|Yes| I[✅ Success]
H -->|No| J[❌ Fail]
.github/workflows/lint.yml(新規)
name: Lint
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint:js
- name: Run Stylelint
run: npm run lint:css
- name: Run tests
run: npm test
package.json(変更)
Lintスクリプトと依存パッケージを追加しました。
{
"scripts": {
+ "lint": "npm run lint:js && npm run lint:css",
+ "lint:fix": "npm run lint:js:fix && npm run lint:css:fix",
+ "lint:js": "eslint .",
+ "lint:js:fix": "eslint . --fix",
+ "lint:css": "stylelint \"**/*.css\"",
+ "lint:css:fix": "stylelint \"**/*.css\" --fix"
},
"devDependencies": {
+ "@eslint/js": "^9.39.2",
+ "eslint": "^9.39.2",
+ "eslint-plugin-vue": "^10.6.2",
+ "globals": "^17.0.0",
+ "postcss": "^8.5.6",
+ "postcss-html": "^1.8.0",
+ "stylelint": "^16.26.1",
+ "stylelint-config-standard": "^39.0.1",
+ "stylelint-config-standard-vue": "^1.0.0",
+ "typescript-eslint": "^8.52.0"
}
}
package-lock.jsonの重要性
GitHub Actionsのnpm cache機能を使用するには、package-lock.jsonがリポジトリに含まれている必要があります。
.gitignoreから除外すると、npm ci実行時に「Dependencies lock file is not found」エラーが発生します。
ロックファイルは依存関係の再現性を保証するため、バージョン管理に含めることが推奨されます。
.gitignore(変更)
# 依存関係
node_modules/
-package-lock.json
yarn.lock
pnpm-lock.yaml
つまづきポイントと解決策
Lint警告の修正(複数ファイル)
未使用のインポートと変数を修正しました。
docs/.vitepress/config.mts:
- import { loadEnv } from 'vite'
docs/.vitepress/theme/index.ts:
- import { useRouter } from 'vitepress'
docs/.vitepress/theme/language-persistence.ts:
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((window as any).__VITEPRESS__?.router) {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (window as any).__VITEPRESS__.router.onAfterRouteChange?.((to: any) => {
+ (window as any).__VITEPRESS__.router.onAfterRouteChange?.((_to: any) => {
updateLanguageCookie();
});
}
functions/tests/middleware.test.ts:
- import { describe, it, expect, beforeEach, vi } from 'vitest';
+ import { describe, it, expect } from 'vitest';
- const acceptLanguage = 'en-US';
- const country = 'JP';
+ const _acceptLanguage = 'en-US';
+ const _country = 'JP';
scripts/generate-roadmap.js:
- for (const [key, status] of Object.entries(statuses)) {
+ for (const [_key, status] of Object.entries(statuses)) {
得られた効果
今回のLint導入によって、以下の効果が得られました。
定量的な効果
| 項目 | 導入前 | 導入後 |
|---|---|---|
| 未使用変数の検出 | 手動のみ | 自動検出(12件修正) |
| CIでの品質チェック | なし | プッシュ時自動実行 |
| コードスタイルの一貫性 | 個人依存 | ルールベースで統一 |
定性的な効果
- レビュー負荷の軽減: スタイル指摘が自動化され、本質的なコードレビューに集中できるようになりました
- 新規参加者のオンボーディング: ルールが明文化されることで、チームメンバーがコードスタイルをすぐに理解できるようになりました
- 心理的安全性の向上: CIで自動チェックされるため、スタイル指摘が個人的なものではなくなりました
よくある質問(FAQ)
ESLint 9.x Flat Configで、従来のextendsは使えますか?
いいえ、Flat Configではextendsは使用できません。
代わりに、設定オブジェクトをスプレッド構文(...)で合成します。
// 従来の書き方(Flat Configでは使えない)
module.exports = {
extends: ['eslint:recommended']
};
// Flat Configでの書き方
import js from '@eslint/js';
export default [
...js.configs.recommended
];
2026年の更新: 2025年3月のアップデート以降、eslint/configが提供するdefineConfig()ヘルパーを使うと、Flat Configでもextends配列を書けるようになりました(内部実装は従来のeslintrcのextendsとは異なります)。
import { defineConfig } from 'eslint/config';
import js from '@eslint/js';
export default defineConfig([
{
extends: [js.configs.recommended],
},
]);
ただし、本記事のようにスプレッド構文(...)で合成する書き方も引き続き有効です。どちらもサポートされているので、プロジェクトの好みに応じて選択してください。
TypeScriptとJavaScriptで異なるパーサーを指定するには?
filesプロパティで対象ファイルを絞り込み、それぞれの設定でlanguageOptions.parserを指定します。
export default [
// JavaScript用
{
files: ['**/*.js'],
languageOptions: { ecmaVersion: 'latest' }
},
// TypeScript用
{
files: ['**/*.ts'],
languageOptions: { parser: tseslint.parser }
}
];
日本語コメントがエラーになるのはなぜ?
spaced-commentルールが// 日本語のような形式を許可しないためです。
以下のオプションで解決できます。
'spaced-comment': ['error', 'always', { markers: ['/'] }]
GitHub Actionsでnpm ciが失敗するのはなぜ?
package-lock.jsonがリポジトリに含まれていない可能性があります。
.gitignoreからpackage-lock.jsonを削除し、コミットしてください。
StylelintでVueのSFCスタイルをLintするには?
stylelint-config-standard-vueをextendsに追加し、postcss-htmlをインストールします。
export default {
extends: ['stylelint-config-standard', 'stylelint-config-standard-vue']
};
ESLintとStylelintは何が違うのですか?使い分けはどうすればいいですか?
ESLintはJavaScript/TypeScriptのコード品質・バグ検出を担当するLintツールです。未使用変数や潜在的なバグパターンなどを検出します。
StylelintはCSS(本記事のようにVueのSFCスタイルブロックも含む)の記法・品質をチェックするLintツールです。プロパティの命名規則や非推奨記法などを検出します。
対象言語が異なるため競合しません。両方を導入し、対象拡張子ごとに実行を分けるのが基本の使い分けです。
本記事のpackage.jsonのようにlint:js(ESLint)とlint:css(Stylelint)をスクリプトとして分け、CIでは両方を実行するのが実践的です。
まとめ
VitePressサイトへのESLint 9.x Flat ConfigとStylelint導入について解説しました。
Flat Config形式を活用することで、TypeScript/JavaScript混在環境でも柔軟な設定が可能です。
また、GitHub Actionsと組み合わせることで、コード品質を自動的に維持できる仕組みを構築できました。
特に印象的だったのは、TypeScriptとJavaScriptで別々のパーサーを適用する設定が、Flat Config形式のおかげでシンプルに記述できた点です。
これからLint環境を構築される方は、ぜひESLint 9.x Flat Config形式を試してみてください。


