mcpField
C
centralized-eslint-prettier

centralized-eslint-prettier

github.com/loxosceles-dev開発137 回インストール
リポジトリ

centralized-eslint-prettier について

centralized-eslint-prettierは、複数ワークスペースのTypeScriptプロジェクトで、Lintとフォーマットを統一するための設定パターンです。ルートに設定とスクリプトを集約し、ファイルパターンごとにルールを変えられます。

主な機能

  1. 01ルートにESLintフラット設定を配置
  2. 02ファイルパターンでワークスペース別ルールを適用
  3. 03Prettierのフォーマット設定を統一
  4. 04Huskyとlint-stagedでコミット前にチェック
  5. 05Lintとフォーマットのスクリプトをルートに集約

活用シーン

  1. 01フロントエンド、バックエンド、インフラのスタイル統一
  2. 02ワークスペースごとに異なるLintルールを設定
  3. 03コミット前に変更ファイルをフォーマット
  4. 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

関連パターン


段階的な改善

このスキルで防ぐべきだった動作を開発者が修正した場合は、今後同じ修正が必要にならないよう、このスキルへの具体的な修正を提案します。

コメント