Búsqueda de sitios web

typescript-eslint/pre-commit hook (monorepo): Error de análisis: se ha establecido "parserOptions.project" para @typescript-eslint/parser


Recientemente nos mudamos a un monorepo y todavía estamos tratando de configurar todo. Usamos TypeScript y ESLint con Prettier. La estructura es la siguiente:

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

La carpeta common contiene código que se utiliza en varios proyectos y no se considera un proyecto por sí mismo (no tiene tsconfig.json). Todos los subproyectos que contienen su propio tsconfig.json y archivo .eslintrc.json, están extendiendo los archivos base, como se puede ver a continuación:

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 (reglas omitidas por brevedad)

{
    "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 (tomado del ejemplo en 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 (reglas omitidas por brevedad)

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

Empaquetar project_a con webpack, usando ts-loader, eslint-webpack-plugin y project_a/tsconfig.json funciona perfectamente bien. No se reportan errores de linting. Lo mismo para VSCode, la extensión vscode-eslint no reporta nada.

También ejecutar lo siguiente dentro de project_a tiene éxito sin ningún error de linting.

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

Sin embargo, al cambiar un archivo en project_a y tratar de preparar y confirmar, se produce el siguiente error:

/home/web/project_a/components/component_a/Component.tsx

0:0 error Error de análisis: "parserOptions.project" se ha establecido para @typescript-eslint/parser. El archivo no coincide con la configuración de tu proyecto: project_a/components/component_a/Component.tsx.

El archivo debe estar incluido en al menos uno de los proyectos proporcionados

No tiene sentido para mí por qué este error solo se produciría en ese caso de uso específico, cuando se ejecuta el linting a través del hook de pre-commit. Además, es muy peculiar, ya que project_a/tsconfig.json include todos los archivos en esa carpeta (excepto node_modules y los artefactos de build).

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

Me parece que podría estar relacionado con que hayamos agregado

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

en ./tsconfig.json. Eso se añadió (según el comentario), porque tener un archivo de common abierto en VSCode y tratar de guardar algunos cambios provocaría que eslint se bloquee, ya que, según varias fuentes, el @typescript-eslint/parser incluiría todos los archivos en memoria para recopilar información de tipos para el linting.

Todos los problemas relacionados en Github solo mencionan que el archivo en cuestión simplemente no está incluido, según el propio error, pero no veo cómo esto está sucediendo en mi caso.

Respuesta #1

Resulta que @typescript-eslint/parser resuelve el tsconfig.json proporcionado en relación con el directorio de trabajo actual, lo cual pasé por alto al leer la documentación. Por lo tanto, dado que el hook de pre-commit siempre se ejecuta desde el directorio raíz, estaba usando tsconfig.base.json en lugar del que está en project_a.

Para solucionar esto, se deben seguir los siguientes pasos (según https://github.com/typescript-eslint/typescript-eslint/issues/251#issuecomment-567365174):

  • renombrar project_a/.eslintrc.json a project_a/.eslintrc.js
  • agrega parserOptions.tsconfigRootDir como lo siguiente:
  • module.exports = {
      parserOptions: {
        project: './tsconfig.json'
        tsconfigRootDir: __dirname // important option
      }
    }
    

Respuesta #2

tu configuración:

- 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

usa language: node, que es un entorno gestionado (pre-commit creará su propio entorno de node aislado y separado y lo usará -- y no tendrá acceso a tus dependencias)

probablemente quieras language: system ya que estás gestionando el entorno externo a pre-commit


descargo de responsabilidad: creé pre-commit