Monorepo架构下ESLint配置最佳实践

43 阅读3分钟

核心原则

在Monorepo大仓架构中,ESLint配置应遵循"分层治理"原则:

  • 根目录:提供通用的基础配置
  • 包级别:针对不同包的特殊需求进行定制
  • 应用级别:为具体应用提供专用配置

推荐配置方案

1. 文件结构示例

monorepo-project/
├── .eslintrc.json                 # 根配置(基础规则)
├── packages/
│   ├── ui-components/
│   │   ├── .eslintrc.js          # UI组件包专用配置
│   │   └── tsconfig.json
│   ├── utils/
│   │   ├── .eslintrc.js          # 工具包专用配置
│   │   └── tsconfig.json
│   └── shared-types/
│       ├── .eslintrc.js          # 类型包专用配置
│       └── tsconfig.json
├── apps/
│   ├── web-app-1/
│   │   ├── .eslintrc.js          # Web应用专用配置
│   │   └── tsconfig.json
│   ├── web-app-2/
│   │   ├── .eslintrc.js          # 另一个Web应用配置
│   │   └── tsconfig.json
│   └── mobile-app/
│       ├── .eslintrc.js          # 移动应用配置
│       └── tsconfig.json
└── tsconfig.json                  # 根TypeScript配置

2. 根配置(.eslintrc.json

{
  "extends": ["@company/eslint-config"],
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "ignorePatterns": [
    "dist/",
    "build/",
    "node_modules/",
    "coverage/"
  ]
}

3. 包级别配置示例

Utils包配置(packages/utils/.eslintrc.js

module.exports = {
  extends: ['@company/eslint-config'],
  env: {
    browser: true,
    es6: true,
    node: true,
  },
  parserOptions: {
    // 启用类型感知功能
    project: require('path').resolve(__dirname, './tsconfig.json'),
  },
  rules: {
    // 工具包特有规则
    'no-console': 'warn',
    '@typescript-eslint/explicit-function-return-type': 'error',
  },
};

UI组件包配置(packages/ui-components/.eslintrc.js

module.exports = {
  extends: [
    '@company/eslint-config',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended'
  ],
  env: {
    browser: true,
    es6: true,
  },
  parserOptions: {
    project: require('path').resolve(__dirname, './tsconfig.json'),
  },
  rules: {
    // React组件特有规则
    'react/prop-types': 'off',
    'react/react-in-jsx-scope': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'error',
  },
  settings: {
    react: {
      version: 'detect',
    },
  },
};

4. 应用级配置示例

Web应用配置(apps/web-app/.eslintrc.js

module.exports = {
  extends: [
    '@company/eslint-config',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended'
  ],
  env: {
    browser: true,
    es6: true,
  },
  parserOptions: {
    project: require('path').resolve(__dirname, './tsconfig.json'),
  },
  rules: {
    // 应用级特殊规则
    'no-console': 'warn',
    'prefer-const': 'error',
  },
  globals: {
    // 应用特有的全局变量
    '__APP_VERSION__': 'readonly',
    '__ENV__': 'readonly',
  },
};

关键配置要点

1. TypeScript类型感知配置

parserOptions: {
  // 必须配置project才能启用类型感知
  project: require('path').resolve(__dirname, './tsconfig.json'),
}

重要说明

  • ESLint默认不会读取tsconfig.json
  • 需要显式配置parserOptions.project来启用类型感知功能
  • 类型感知模式性能较慢,按需启用

2. 全局类型声明

对于需要全局类型的情况,在TypeScript声明文件中使用正确语法:

// global.d.ts
declare global {
  interface Window {
    customProperty: any;
  }
  
  type CustomHandler = (event: Event) => void;
}

export {}; // 使文件成为模块

3. 配置继承优先级

ESLint配置按以下优先级生效:

  1. 文件所在目录的配置
  2. 父目录配置(向上查找)
  3. 根目录配置

最佳实践建议

1. 配置策略选择

场景推荐方案理由
小型Monorepo(<10个包)统一根配置维护简单
中型Monorepo(10-50个包)混合配置平衡灵活性与维护成本
大型Monorepo(>50个包)分层配置支持复杂需求

2. 性能优化

// 仅在需要类型检查的包中启用类型感知
parserOptions: {
  project: process.env.LINT_TYPE_CHECK === 'true' 
    ? require('path').resolve(__dirname, './tsconfig.json')
    : undefined,
}

3. CI/CD集成

{
  "scripts": {
    "lint": "eslint . --ext .ts,.tsx,.js,.jsx",
    "lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
    "lint:packages": "lerna run lint --parallel",
    "lint:type-check": "LINT_TYPE_CHECK=true npm run lint"
  }
}

4. 忽略文件配置

在根目录创建.eslintignore

# 构建产物
dist/
build/
lib/

# 依赖
node_modules/

# 测试覆盖率
coverage/

# 临时文件
*.tmp
*.log

# 特定包的忽略
packages/legacy-package/

常见问题解决

1. 类型未定义错误

问题:ESLint报告'SomeType' is not defined

解决

  • 检查是否配置了parserOptions.project
  • 确认全局类型声明语法正确
  • 添加必要的类型导入

2. 配置冲突

问题:不同层级配置产生冲突

解决

  • 使用overrides字段针对特定文件
  • 合理设计配置继承关系
  • 定期审查配置一致性

3. 性能问题

问题:ESLint运行缓慢

解决

  • 仅在必要时启用类型感知
  • 使用合适的忽略模式
  • 考虑并行化处理

总结

Monorepo架构下的ESLint配置关键在于平衡统一性与灵活性。通过分层配置策略,既能保证代码质量的一致性,又能满足不同包的特殊需求。核心是要建立清晰的配置层级,合理使用TypeScript类型感知功能,并保持良好的性能表现。