mcpField
C
centralized-eslint-prettier

centralized-eslint-prettier

github.com/loxosceles-dev开发137 次安装
代码仓库

关于 centralized-eslint-prettier

centralized-eslint-prettier 是面向多工作区 TypeScript 项目的配置参考模式,适用于希望统一代码检查与格式化的团队。它以根目录配置集中规则和脚本,并按文件模式区分各工作区需求。

主要功能

  1. 01使用根目录 ESLint 扁平配置
  2. 02按文件模式应用工作区规则
  3. 03统一 Prettier 格式配置
  4. 04通过 Husky 与 lint-staged 运行提交前检查
  5. 05在根目录集中管理检查和格式化脚本

适用场景

  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 脚本。


为什么使用此模式?

优点:

  • 单一事实来源:只有一个配置文件,无重复内容
  • 工作区灵活性:通过文件模式为不同工作区设置不同规则
  • 维护更简单:只需在一处更新规则
  • 统一的提交前检查:所有工作区使用相同的钩子
  • 易于 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+),并基于文件模式设置规则
  • 工作区专属规则:通过 glob 模式为前端、后端和 CLI 设置不同规则
  • Prettier 集成:所有工作区共用单个 .prettierrc
  • Husky + lint-staged:提交前格式化已更改的文件
  • 集中式脚本:在根目录 package.json 中定义所有 lint/format 命令

实施

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 文件中不应包含 lint/format 脚本。

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'
  }
}

工作区专属模式

两层 CLI(简单项目)
{
  files: ['scripts/**/*.ts', 'tools/**/*.ts'],
  rules: { 'no-console': 'off' }
}
三层 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

相关模式


渐进式改进

如果开发者纠正了此技能本应避免的行为,请建议对此技能进行具体修订,以避免今后出现相同的纠正。

评论

支持 Markdown