Monorepo环境pre-commit执行typescript-eslint校验报parserOptions.project错误
我们近期完成了monorepo架构迁移,目前仍在完善配套配置,技术栈采用TypeScript + ESLint + Prettier。
项目目录结构
root - common - folder_a - file_a.ts - build_scripts - script_a.js - project_a - components - component_a - Component.tsx - tsconfig.json - .eslintrc.json - node_modules - package.json - .prettierignore - .prettierrc.yml - .eslintignore - .eslintrc.base.json - tsconfig.base.json - node_modules - package.json - Makefile - webpack.config.js - .pre-commit-config.yaml
说明:common目录存放多项目复用的公共代码,本身不是独立项目(无自有tsconfig.json)。所有带自有tsconfig.json和.eslintrc.json的子项目都继承根目录的基础配置。
核心配置详情
tsconfig.base.json
{ "compilerOptions": { "lib": ["ES2017", "DOM"], "target": "ES6", "module": "commonjs", "baseUrl": ".", "sourceMap": true, "alwaysStrict": true, "noUnusedParameters": true, "noUnusedLocals": true, "noEmitOnError": true, "paths": { "@common/*": ["./common/*"] } }, // include all projects that don't have their own // tsconfig.json file AND need to be compiled/linted // => very important because typescript-eslint will // otherwise load ALL files in memory for type-checking // and consequently eslint will crash with an OOM error "include": ["common/**/*"] }
.eslintrc.base.json(规则部分省略)
{ "env": { "es2020": true, "node": true }, "extends": [ "plugin:destructuring/recommended", "airbnb-base", "prettier", "plugin:prettier/recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { "project": "./tsconfig.base.json", "ecmaVersion": 11, "sourceType": "module" }, "plugins": [ "prettier", "@typescript-eslint", "destructuring" ], "rules": {} }
.eslintignore
*/**/build Makefile **/*.js **/*.json
.pre-commit-config.yaml
- repo: local hooks: - id: lint_fix name: lint_fix_project_a entry: node_modules/.bin/eslint --report-unused-disable-directives --fix -c project_a/.eslintrc.json --ext .ts,.tsx files: ^project_a/ types: [file] language: node
根目录package.json
{ "name": "web", "version": "1.0.0", "private": true, "dependencies": {}, "devDependencies": { "@babel/core": "7.2.2", "@typescript-eslint/eslint-plugin": "4.15.1", "@typescript-eslint/parser": "4.15.1", "eslint": "7.20.0", "eslint-config-airbnb": "18.2.1", "eslint-config-airbnb-base": "14.2.0", "eslint-config-prettier": "6.10.1", "eslint-config-standard": "14.1.1", "eslint-import-resolver-typescript": "2.0.0", "eslint-plugin-destructuring": "2.2.0", "eslint-plugin-import": "2.22.1", "eslint-plugin-node": "11.1.0", "eslint-plugin-prettier": "3.1.3", "eslint-plugin-promise": "4.2.1", "eslint-plugin-standard": "4.0.1", "eslint-webpack-plugin": "2.5.4", "eslint-plugin-jsx-a11y": "6.4.1", "eslint-plugin-react": "7.22.0", "eslint-plugin-react-hooks": "4.2.0", "express": "4.0.0", "prettier": "2.1.2", "typescript": "3.9.7" } }
project_a/tsconfig.json
{ "extends": "../tsconfig.base.json", "compilerOptions": { "lib": ["ES2017", "DOM", "DOM.Iterable"], "jsx": "react", "baseUrl": ".", "paths": { "@common/*": ["../common/*"], "@components/*": ["./components/*"] } }, // overwrite the base "includes" "include": ["**/*"], "exclude": [ "**/node_modules/*", "**/build/*" ] }
project_a/.eslintrc.json(规则部分省略)
{ "extends": [ "plugin:react/recommended", "plugin:destructuring/recommended", "airbnb", "airbnb/hooks", "prettier", "plugin:prettier/recommended", "prettier/react", "../.eslintrc.base.json" ], "env": { "browser": true, "commonjs": true, "node": false }, "parserOptions": { "project": "./tsconfig.json", "ecmaFeatures": { "jsx": true } }, "plugins": [ "prettier", "react", "@typescript-eslint", "destructuring" ], "rules": {} }
问题现象
以下场景执行均无异常:
- 使用webpack配合ts-loader、eslint-webpack-plugin基于
project_a/tsconfig.json打包project_a项目,无任何lint报错 - VSCode的vscode-eslint插件无异常提示
- 在
project_a目录下执行../node_modules/.bin/eslint . --ext .ts,.tsx --fix -c .eslintrc.json正常通过,无lint错误
但修改project_a内的文件后执行git暂存提交时,pre-commit钩子抛出如下错误:
/home/web/project_a/components/component_a/Component.tsx
0:0 error Parsing error: "parserOptions.project" has been set for @typescript-eslint/parser. The file does not match your project config: project_a/components/component_a/Component.tsx.
The file must be included in at least one of the projects provided
问题根因
pre-commit钩子默认在仓库根目录执行,而project_a/.eslintrc.json中parserOptions.project配置的是相对路径./tsconfig.json,从根目录执行时,该路径会被解析为根目录下的tsconfig.base.json,而tsconfig.base.json的include仅覆盖了common目录的文件,project_a下的文件不在其包含范围内,因此触发报错。
解决方案
可选方案如下,推荐组合使用方案1+方案2保证稳定性:
方案1:指定pre-commit钩子的执行目录
修改.pre-commit-config.yaml,给对应钩子添加working_directory参数指定执行目录为project_a,同时调整entry的路径适配:
- repo: local hooks: - id: lint_fix name: lint_fix_project_a entry: ../node_modules/.bin/eslint --report-unused-disable-directives --fix -c .eslintrc.json --ext .ts,.tsx files: ^project_a/ types: [file] language: node working_directory: project_a # 新增这一行
方案2:固定tsconfig的解析基准路径
将project_a/.eslintrc.json改为.eslintrc.js,添加tsconfigRootDir配置,保证无论从哪个目录执行eslint,都能正确找到对应tsconfig:
module.exports = { // 原有配置不变 "parserOptions": { "project": "./tsconfig.json", "tsconfigRootDir": __dirname, // 新增这一行 "ecmaFeatures": { "jsx": true } }, // 原有配置不变 }
方案3:显式指定tsconfig路径
也可以直接在pre-commit的entry中指定tsconfig的相对根目录的路径:
entry: node_modules/.bin/eslint --report-unused-disable-directives --fix -c project_a/.eslintrc.json --parser-options project:./project_a/tsconfig.json --ext .ts,.tsx
内容的提问来源于stack exchange,提问作者XeniaSis

