Nx: Migration to ESlint

Created on 16 Dec 2020  路  20Comments  路  Source: nrwl/nx


I have a nx workspace already configured with TSlint and I've migrated to nx 11 recently.
How do I migrate to ESLint? angular-eslint instruct us to follow nx path in order to migrate properly. Which path would it be? I couldn't find it anywhere.

Environment

``` Node : 12.14.0
OS : linux x64
npm : 6.13.4

nx : Not Found
@nrwl/angular : 11.0.4
@nrwl/cli : 11.0.4
@nrwl/cypress : 11.0.4
@nrwl/devkit : 11.0.4
@nrwl/eslint-plugin-nx : Not Found
@nrwl/express : Not Found
@nrwl/jest : 11.0.4
@nrwl/linter : 11.0.4
@nrwl/nest : Not Found
@nrwl/next : Not Found
@nrwl/node : Not Found
@nrwl/react : Not Found
@nrwl/schematics : Not Found
@nrwl/tao : 11.0.4
@nrwl/web : Not Found
@nrwl/workspace : 11.0.4
typescript : 4.0.5```

linter question / discussion

Most helpful comment

@JamesHenry (the author of angular-eslint) is currently working on a migration from TSLint to ESLint+angular-eslint in Nx workspaces. A few days ago, he said that he estimated that he was about 85% done, so follow the semi-automated migration instructions in my guide mentioned by @KingDarBoja or wait until the migration is ready 馃檪

All 20 comments

There is a good article recently published by @LayZeeDK for migrating to ESLint in the meantime: The ultimate migration guide to angular-eslint, ESLint and Nx 11

@JamesHenry (the author of angular-eslint) is currently working on a migration from TSLint to ESLint+angular-eslint in Nx workspaces. A few days ago, he said that he estimated that he was about 85% done, so follow the semi-automated migration instructions in my guide mentioned by @KingDarBoja or wait until the migration is ready 馃檪

Hey @LayZeeDK
I was following your post but, at the point of running the batch script, I started running in to:

Unable to resolve @angular-eslint/schematics:convert-tslint-to-eslint.
Cannot find module '@angular-devkit/core'

Any ideas on how I can resolve this?

@sameera, based on the error message, I guess you need to npm install --save-dev @angular-devkit/core even though I've never had to add that package explicitly. Which version of Nx are you using?

Thanks @LayZeeDK
I'm on NX 11.0.12

However, the problem had been that I had mistakenly uninstalled codelyzer prior to running the batch script. This was resolved by installing it. Sorry for the noise.

I did get this working thanks to @LayZeeDK 's post.
However, I needed to do few more things:

  1. Add plugin:import/errors, plugin:import/typescript to extends in order to get imports/order working.
  2. Add "import/no-unresolved": "off", (as nothing here worked)

However, I had to add the following to each project's .eslintrc.json file to get rid of
Strings must use singlequote error.

    "quotes": [
        "error",
        "double",
        { "avoidEscape": true, "allowTemplateLiterals": true }
    ]

Is this expected? Why isn't adding it in the .eslintrc.json at the repo's root work?

Is this expected? Why isn't adding it in the .eslintrc.json at the repo's root work?

I realized this is because in each of the derived .eslintrc.json we have this part:

"extends": [
        "plugin:@nrwl/nx/angular",
        "plugin:@angular-eslint/template/process-inline-templates"
      ]

with a files:["*.ts"] filter.

I have resolved this by moving these extends to the root. This allows me to clean up all of the derived configs keeping only the @angular-eslint/component-selector and @angular-eslint/directive-selector rules in libs where there are actual Angular components.

@sameera There is a very good reason why we use overrides, so I would not recommend what you have done.

I would recommend reading my write up on ESLint configs for Angular projects in the angular-eslint project here: https://github.com/angular-eslint/angular-eslint#notes-on-eslint-configuration

Not everything in that README is 100% applicable to Nx workspaces, but the fundamentals of ESLint config structure are the same.

Adding rules to the root .eslintrc.json works just fine, you just need to add it into an appropriate override block for the files you want it to apply to

PR #4328 is open which adds the automated migration of TSLint projects to ESLint, it should be merged soon so IMO it is worth holding off on manual efforts for now

@JamesHenry very sorry for the confusion: By root, I meant the eslintrc.json at the root of the Nx workspace. Not the root of the json object :)

I am using overrides in the root file.

Ah ok cool, that's definitely better than what I initially understood 馃憤

Nevertheless, there is an important thing to note about applying things in the root config at all as well (and it's the reason we apply angular preset configs on a project level) - Nx by it's nature is multi-framework (and even multi-environment when you consider its OOTB node support etc).

Therefore it is very common for an Nx workspace to contain an Angular project, and a Node project, for example. Even if this is not something you have now, Nx would allow you to trivially add it in future.

However, if you make your root configs more Angular specific, then you will lose some of that flexibility because linting Angular projects is pretty different to React/Node/Express projects, for example.

Hope that makes sense

Yup @JamesHenry
Totally makes sense.

So, I guess the right thing to do, would be to write a custom config deriving @nrwl/nx/angular and extend that on a per-project level so that we need not repeat the same rules in each Angular project.

I upgraded to Angular 11 and followed this guide (see below) to upgrade tslint to eslint all seem to upgrade OK, except when I try and run nx run-many --target=lint --all I get the following error:

SyntaxError: Unexpected token } in JSON at position 681
at JSON.parse ()
at Object.parseJsonWithComments (/home/duncan.faulkner/development/integra-frontend/node_modules/@nrwl/workspace/src/utils/fileutils.js:44:17)
at new TargetProjectLocator (/home/duncan.faulkner/development/integra-frontend/node_modules/@nrwl/workspace/src/core/target-project-locator.js:22:46)
at buildExplicitTypeScriptDependencies (/home/duncan.faulkner/development/integra-frontend/node_modules/@nrwl/workspace/src/core/project-graph/build-dependencies/explicit-project-dependencies.js:8:34)
at /home/duncan.faulkner/development/integra-frontend/node_modules/@nrwl/workspace/src/core/project-graph/project-graph.js:60:41
at Array.forEach ()
at buildProjectGraph (/home/duncan.faulkner/development/integra-frontend/node_modules/@nrwl/workspace/src/core/project-graph/project-graph.js:60:26)
at Object.createProjectGraph (/home/duncan.faulkner/development/integra-frontend/node_modules/@nrwl/workspace/src/core/project-graph/project-graph.js:40:30)
at Object. (/home/duncan.faulkner/development/integra-frontend/node_modules/@nrwl/workspace/src/command-line/run-many.js:17:46)
at Generator.next ()

Guide:
https://dev.to/this-is-angular/the-ultimate-migration-guide-to-angular-eslint-eslint-and-nx-11-1eh2#migrating-an-existing-nx-10-angular-workspace-using-tslint

Hi @DuncanFaulkner,

Can you run nx report and paste your workspace.json or angular.json as well as your root .eslintrc.json?

Error indicates a syntax error in either your workspace configuration or one of your .eslintrc.json configuration files.

@LayZeeDK

NX Report complete - copy this into the issue template

Node : 14.15.3
OS : linux x64
npm : 6.14.9

nx : Not Found
@nrwl/angular : 11.0.18
@nrwl/cli : 11.0.18
@nrwl/cypress : 11.0.18
@nrwl/devkit : 11.0.18
@nrwl/eslint-plugin-nx : 11.0.18
@nrwl/express : Not Found
@nrwl/jest : 11.0.18
@nrwl/linter : 11.0.18
@nrwl/nest : Not Found
@nrwl/next : Not Found
@nrwl/node : Not Found
@nrwl/react : Not Found
@nrwl/schematics : Not Found
@nrwl/tao : 11.0.18
@nrwl/web : Not Found
@nrwl/workspace : 11.0.18
typescript : 4.0.5

angular json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "",
  "projects": {
    "integra-frontend": {
      "projectType": "application",
      "schematics": {
        "@nrwl/workspace:component": {
          "style": "scss"
        }
      },
      "root": "apps/integra-frontend",
      "sourceRoot": "apps/integra-frontend/src",
      "prefix": "integra-frontend",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/apps/integra-frontend",
            "index": "apps/integra-frontend/src/index.html",
            "main": "apps/integra-frontend/src/main.ts",
            "polyfills": "apps/integra-frontend/src/polyfills.ts",
            "tsConfig": "apps/integra-frontend/tsconfig.app.json",
            "aot": true,
            "assets": [
              "apps/integra-frontend/src/favicon.ico",
              "apps/integra-frontend/src/assets"
            ],
            "styles": [
              "node_modules/@angular/material/prebuilt-themes/purple-green.css",
              "apps/integra-frontend/src/styles/main.scss"
            ],
            "stylePreprocessorOptions": {
              "includePaths": [
                "apps/integra-frontend/src/styles/"
              ]
            }
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "apps/integra-frontend/src/environments/environment.ts",
                  "with": "apps/integra-frontend/src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "3mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "20kb"
                }
              ]
            }
          },
          "outputs": [
            "{options.outputPath}"
          ]
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "integra-frontend:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "integra-frontend:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "integra-frontend:build"
          }
        },
        "lint": {
          "builder": "@nrwl/linter:eslint",
          "options": {
            "lintFilePatterns": [
              "apps/integra-frontend/**/*.ts",
              "apps/integra-frontend/**/*.html"
            ]
          }
        },
        "test": {
          "builder": "@nrwl/jest:jest",
          "options": {
            "jestConfig": "apps/integra-frontend/jest.config.js",
            "passWithNoTests": true
          },
          "outputs": [
            "coverage/apps/integra-frontend"
          ]
        }
      }
    },
    "integra-frontend-e2e": {
      "root": "apps/integra-frontend-e2e",
      "sourceRoot": "apps/integra-frontend-e2e/src",
      "projectType": "application",
      "architect": {
        "e2e": {
          "builder": "@nrwl/cypress:cypress",
          "options": {
            "cypressConfig": "apps/integra-frontend-e2e/cypress.json",
            "tsConfig": "apps/integra-frontend-e2e/tsconfig.e2e.json",
            "devServerTarget": "integra-frontend:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "integra-frontend:serve:production"
            }
          }
        },
        "lint": {
          "builder": "@nrwl/linter:eslint",
          "options": {
            "lintFilePatterns": [
              "apps/integra-frontend-e2e/**/*.{js,ts}"
            ]
          }
        }
      }
    }
  },
  "cli": {
    "warnings": {
      "typescriptMismatch": false,
      "versionMismatch": false
    },
    "defaultCollection": "@nrwl/angular"
  },
  "schematics": {
    "@nrwl/angular:application": {
      "unitTestRunner": "jest",
      "e2eTestRunner": "cypress"
    },
    "@nrwl/angular:library": {
      "unitTestRunner": "jest"
    }
  },
  "defaultProject": "integra-frontend"
}

.eslinterc

{
  "root": true,
  "ignorePatterns": [
    "**/*"
  ],
  "overrides": [
    {
      "files": [
        "*.ts",
        "*.tsx",
        "*.js",
        "*.jsx"
      ],
      "rules": {
        "@nrwl/nx/enforce-module-boundaries": [
          "error",
          {
            "enforceBuildableLibDependency": true,
            "allow": [],
            "depConstraints": [
              {
                "sourceTag": "*",
                "onlyDependOnLibsWithTags": [
                  "*"
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "files": [
        "*.ts"
      ],
      "parserOptions": {
        "project": "./tsconfig.*?.json"
      },
      "extends": [
        "plugin:@nrwl/nx/typescript"
      ],
      "rules": {
        "@typescript-eslint/consistent-type-definitions": "error",
        "@typescript-eslint/dot-notation": "off",
        "@typescript-eslint/explicit-member-accessibility": [
          "off",
          {
            "accessibility": "explicit"
          }
        ],
        "@typescript-eslint/no-inferrable-types": [
          "off",
          {
            "ignoreParameters": true
          }
        ],
        "id-blacklist": "off",
        "id-match": "off",
        "no-underscore-dangle": "off"
      }
    }
  ],
  "plugins": [
    "@nrwl/nx"
  ]
}

@DuncanFaulkner Try Node.js 12.x, delete and reinstall node_modules and lockfile.

OK will check this I upgraded another project today and that all works, but I might not have upgraded node for that project. I will take a look at this after Christmas.
Is 14.15.3 of Node not supported?

Node.js 14 is not officially supported by Angular or Nx yet as far as I'm aware. I've seen it break some tools.

It would appear that my NX project isn't properly configured for NX workspaces, I also rolled back to 12.20.0 just to be on the safe side

Was this page helpful?
0 / 5 - 0 ratings