centralized-eslint-prettier について
centralized-eslint-prettierは、複数ワークスペースのTypeScriptプロジェクトで、Lintとフォーマットを統一するための設定パターンです。ルートに設定とスクリプトを集約し、ファイルパターンごとにルールを変えられます。
主な機能
- 01ルートにESLintフラット設定を配置
- 02ファイルパターンでワークスペース別ルールを適用
- 03Prettierのフォーマット設定を統一
- 04Huskyとlint-stagedでコミット前にチェック
- 05Lintとフォーマットのスクリプトをルートに集約
活用シーン
- 01フロントエンド、バックエンド、インフラのスタイル統一
- 02ワークスペースごとに異なるLintルールを設定
- 03コミット前に変更ファイルをフォーマット
- 04単一コマンドでリポジトリ全体を検査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ loxosceles-dev/ dev-skills --skill centralized-eslint-prettierインストール後、Claude Code で/centralized-eslint-prettierを入力して読み込まれたか確認します。
詳細
ESLint と Prettier の一元管理設定
これはリファレンスパターンです。 このアプローチから学び、状況に合わせて適用してください。そのままコピーしないでください。
課題:複数のワークスペースを持つ TypeScript プロジェクト(フロントエンド、バックエンド、インフラストラクチャ)では、重複を避けながら、一貫したリンティングとフォーマットが必要です。また、コミット前フックとリポジトリ全体のフォーマットにも対応する必要があります。
解決策:ルートレベルに単一の eslint.config.mjs を配置し、ファイルパターンでワークスペース固有のルールを適用します。Prettier 設定を統一し、Husky と連携する npm スクリプトを一元管理します。
このパターンを使う理由
メリット:
- 信頼できる唯一の情報源:設定ファイルは 1 つだけで、重複しません
- ワークスペースの柔軟性:ファイルパターンを使って、ワークスペースごとに異なるルールを適用できます
- メンテナンスの簡素化:ルールを 1 か所で更新できます
- 一貫したコミット前処理:すべてのワークスペースで同じフックを使用できます
- 簡単な CI/CD:単一のコマンドでコードベース全体をリンティングできます
- プロジェクトを選ばない:あらゆる TypeScript モノレポ構成で機能します
ユースケース:
- 複数のワークスペースを持つ TypeScript プロジェクト(Next.js + CDK、Nuxt + Node.js など)
- ワークスペースごとに異なるリンティング要件があるプロジェクト(フロントエンドと CLI、Lambda など)
- ワークスペースごとの設定を設けず、コードスタイルを統一したいチーム
- コミット前フックとリポジトリ全体のフォーマットの両方が必要なプロジェクト
パターン
アーキテクチャ:
project-root/
├── eslint.config.mjs # Single source of truth
├── .prettierrc # Unified formatting rules
├── package.json # Root scripts only
├── .husky/pre-commit # Git hooks
├── pnpm-workspace.yaml
├── frontend/
│ ├── package.json # NO lint scripts
│ └── tsconfig.json
└── infrastructure/
├── package.json # NO lint scripts
└── tsconfig.json
主なコンポーネント:
- ルート ESLint 設定:ファイルパターンベースのルールを使うフラット設定(ESLint 9+)
- ワークスペース固有のルール:グロブパターンを使って、フロントエンド、バックエンド、CLI に異なるルールを適用します
- Prettier との連携:すべてのワークスペースで単一の
.prettierrcを使用します - Husky + lint-staged:変更されたファイルをコミット前にフォーマットします
- スクリプトの一元管理:すべてのリンティング/フォーマット用コマンドをルートの
package.jsonに配置します
実装
1. ルート ESLint 設定(eslint.config.mjs)
import js from '@eslint/js';
import typescriptEslint from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';
import globals from 'globals';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const IGNORE_PATTERNS = [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'**/.next/**',
'**/cdk.out/**',
'**/*.d.ts',
'**/*.config.js'
];
const SHARED_RULES = {
'eol-last': ['error', 'always'],
'no-console': ['warn', { allow: ['error', 'warn'] }],
'no-unused-vars': 'off'
};
export default [
// Global ignores
{ ignores: IGNORE_PATTERNS },
// Frontend TypeScript
{
files: ['frontend/**/*.{ts,tsx}'],
languageOptions: {
parser: typescriptParser,
parserOptions: {
project: path.join(__dirname, 'frontend/tsconfig.json'),
ecmaVersion: 'latest',
sourceType: 'module'
}
},
plugins: { '@typescript-eslint': typescriptEslint },
rules: {
...SHARED_RULES,
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-explicit-any': 'error'
}
},
// Infrastructure TypeScript
{
files: ['infrastructure/**/*.ts'],
languageOptions: {
parser: typescriptParser,
parserOptions: {
project: path.join(__dirname, 'infrastructure/tsconfig.json'),
ecmaVersion: 'latest',
sourceType: 'module'
}
},
plugins: { '@typescript-eslint': typescriptEslint },
rules: {
...SHARED_RULES,
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-explicit-any': 'error'
}
},
// CLI scripts - allow console output
{
files: [
'infrastructure/lib/cli/**/*.ts',
'scripts/**/*.ts'
],
rules: { 'no-console': 'off' }
}
];
2. Prettier 設定(.prettierrc)
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"endOfLine": "auto"
}
3. ルートの Package.json スクリプト
{
"scripts": {
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
"lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
"prepare": "husky"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write --end-of-line auto"
],
"*.{json,md,yml}": [
"prettier --write --end-of-line auto"
]
}
}
重要:ワークスペースの package.json ファイルには、リンティング/フォーマット用スクリプトを含めないでください。
4. Husky のコミット前フック
# .husky/pre-commit
pnpm exec lint-staged
5. 依存関係
pnpm add -D -w eslint \
@typescript-eslint/parser \
@typescript-eslint/eslint-plugin \
typescript-eslint \
@eslint/js \
globals \
prettier \
eslint-config-prettier \
husky \
lint-staged
フレームワーク固有のバリエーション
Next.js フロントエンド
import nextPlugin from '@next/eslint-plugin-next';
{
files: ['frontend/**/*.{ts,tsx}'],
plugins: {
'@next/next': nextPlugin,
'@typescript-eslint': typescriptEslint
},
settings: {
next: { rootDir: path.join(__dirname, 'frontend') }
}
}
Nuxt.js フロントエンド(自動生成される設定)
Nuxt は .nuxt/eslint.config.mjs を自動生成します。これを保持して拡張してください。
// frontend/eslint.config.mjs
import withNuxt from './.nuxt/eslint.config.mjs';
import { SHARED_RULES } from '../eslint.shared.mjs';
export default withNuxt({
rules: { ...SHARED_RULES }
});
注:Nuxt では設定が自動生成されるため、ワークスペースレベルの設定を保持してください。ルートに eslint.shared.mjs を作成して、ルールを共有します。
Lambda 関数
{
files: ['infrastructure/lib/lambdas/**/*.ts'],
rules: {
'no-console': 'off', // CloudWatch logs
'@typescript-eslint/no-explicit-any': 'error'
}
}
ワークスペース固有のパターン
2 層 CLI(シンプルなプロジェクト)
{
files: ['scripts/**/*.ts', 'tools/**/*.ts'],
rules: { 'no-console': 'off' }
}
3 層 CLI(インフラストラクチャプロジェクト)
// Tier 1: CLI Binaries
{
files: ['infrastructure/lib/cli/bin/**/*.ts'],
rules: { 'no-console': 'off' }
},
// Tier 2: Commands
{
files: ['infrastructure/lib/cli/commands/**/*.ts'],
rules: { 'no-console': 'off' }
},
// Tier 3: Domain Logic
{
files: ['infrastructure/core/**/*.ts'],
rules: { 'no-console': 'warn' }
}
使用方法
コミット前(自動)
git add .
git commit -m "feat: add feature"
# Automatically runs lint-staged on changed files
リポジトリ全体のフォーマット
# Check formatting
pnpm format:check
# Fix all files
pnpm format
# Lint entire codebase
pnpm lint
# Auto-fix linting issues
pnpm lint:fix
CI/CD との連携
# GitHub Actions
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm format:check
トレードオフ
ESLint 9+ のフラット設定が必須
制約:このパターンでは、ESLint 9+ のフラット設定形式(.mjs ファイル)を使用します。
理由:フラット設定は ESLint の将来を担う形式であり、TypeScript のサポートがより優れています。
移行:古い .eslintrc.js 設定は変換が必要です。ESLint 移行ガイドを参照してください。
ファイルパターンの順序が重要
制約:より具体的なパターンは、一般的なパターンより後に指定する必要があります。
例:
// ✅ Correct order
{ files: ['**/*.ts'], rules: {...} },
{ files: ['frontend/**/*.ts'], rules: {...} },
{ files: ['frontend/lib/cli/**/*.ts'], rules: {...} }
// ❌ Wrong order - specific rules won't apply
{ files: ['frontend/lib/cli/**/*.ts'], rules: {...} },
{ files: ['**/*.ts'], rules: {...} }
Nuxt.js の例外
制約:Nuxt は ESLint 設定を自動生成するため、ワークスペースレベルの設定が必要です。
解決策:frontend/eslint.config.mjs を保持し、ルートの eslint.shared.mjs から共有ルールをインポートします。
使用しない方がよいケース
- 単一ワークスペースのプロジェクト:ワークスペースレベルの設定を使用する方がシンプルです
- TypeScript 以外のプロジェクト:このパターンは TypeScript に重点を置いています(ただし、適応は可能です)
- 古い ESLint バージョン:フラット設定には ESLint 9+ が必要です
- ワークスペースごとの要件が大きく異なる場合:ワークスペースでまったく異なるツールが必要であれば、設定を分けた方が明確な場合があります
検証チェックリスト
セットアップ後:
# 1. Full codebase linting works
pnpm lint
# 2. Auto-fix works
pnpm lint:fix
# 3. Formatting works
pnpm format
# 4. Pre-commit hooks work
git add . && git commit -m "test"
# 5. No duplicate scripts in workspaces
grep -r '"lint":' */package.json
# Should ONLY show root package.json
関連パターン
- CLI アーキテクチャ - console.log のルールに関する CLI の階層を理解します
- 環境の検証 - フェイルファスト検証のパターン
段階的な改善
このスキルで防ぐべきだった動作を開発者が修正した場合は、今後同じ修正が必要にならないよう、このスキルへの具体的な修正を提案します。