核心原则
在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. 配置策略选择
场景 | 推荐方案 | 理由 |
---|---|---|
小型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类型感知功能,并保持良好的性能表现。