2

We recently migrated to a monorepo and we are still trying to set everything up. We use typescript and eslint with prettier. The structure is the following:

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

The common folder contains code that is used across multiple projects and isn't considered a project of its own (no tsconfig.json). All sub-projects that contain their own tsconfig.json and .eslintrc.json file, are extending the base ones, as you can see below:

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 (rules omitted for brevity)

{
    "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 (taken from the example in https://github.com/pre-commit/pre-commit/issues/466#issuecomment-274282684)

- 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 (rules omitted for brevity)

{
    "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": {}
}

Bundling project_a with webpack, using ts-loader, eslint-webpack-plugin and project_a/tsconfig.json works perfectly fine. No linting errors reported. Same for VSCode, the vscode-eslint extension doesn't report anything.

Also running the following inside project_a is successful without any linting errors.

../node_modules/.bin/eslint . --ext .ts,.tsx --fix -c .eslintrc.json

However, when changing a file in project_a and trying to stage and commit, the following error is thrown:

/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

It makes no sense to me, why this error would only be thrown in that specific use-case, when linting via the pre-commit hook. Also, it's very peculiar, since project_a/tsconfig.json includes all files in that folder (except node_modules and build artifacts).

"include": ["**/*"]

It seems to me it might be related to us having added

"include": ["common/**/*"]

in ./tsconfig.json. That was added (as per the comment), because having a file from common open in VSCode and trying to save some changes, would make eslint crash, since according to various sources, the @typescript-eslint/parser would include all files in memory to gather type information for linting.

All related Github issues just mention that the file in question is just not included, as per the error itslef, but I don't see how this is happening in my scenario.

2 Answers 2

4

It turns out that @typescript-eslint/parser resolves the provided tsconfig.json relative to the current working directory, which I missed while reading the docs. Thus, since the pre-commit hook is always run from the root directory, it was using tsconfig.base.json instead of the one in project_a.

In order to fix this, the following steps need to be taken (according to https://github.com/typescript-eslint/typescript-eslint/issues/251#issuecomment-567365174):

  • rename project_a/.eslintrc.json to project_a/.eslintrc.js
  • add parserOptions.tsconfigRootDir like the following:
  • module.exports = {
      parserOptions: {
        project: './tsconfig.json'
        tsconfigRootDir: __dirname // important option
      }
    }
    
Sign up to request clarification or add additional context in comments.

Comments

1

your configuration:

- 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

uses language: node which is a managed envinronment (pre-commit will create its own separate isolated node environment and use that -- and won't have access to your dependencies)

you probably want language: system since you are managing the environment external to pre-commit


disclaimer: I created pre-commit

7 Comments

Thanks for the quick reply. Same error still occurs unfortunately
hooks will also be run from the root of the repository, so the equivalent entry should succeed from there
I'm not sure I understand what you mean with so the equivalent entry should succeed from there
cd to the root of the repository and run the entry with at least one filename after it and that's equivalent to what's happening here (independent of pre-commit) -- iterate on that until it succeeds
I now get the same error, thanks for the explanation. But it still doesn't explain the inconsistencies of the lint result, when ran from the root and from the project folder
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.