DEV Community

Cover image for The ultimate migration guide to angular-eslint, ESLint and Nx 11

The ultimate migration guide to angular-eslint, ESLint and Nx 11

Lars Gyrup Brink Nielsen on December 13, 2020

Cover photo by Anastasia Taioglou on Unsplash. Updated to Nx version 11.0.18. Nx version 11 has built-in support for Angular version 11 and ESLin...
Collapse
 
kylecannon profile image
Kyle Cannon

I took this a step further and I think I have successfully bashified it..

#!/bin/bash
#set -euxo pipefail
set -o pipefail
echo "Installing json globally for the time being";
npm i -g json

mv tsconfig.base.json tsconfig.json

yarn nx add @angular-eslint/schematics
yarn add eslint@7.10.0 -D

for project in $(cat angular.json | jq -c '.projects | to_entries | map_values(.key) | .[]' | xargs echo); do
    nx generate @angular-eslint/schematics:convert-tslint-to-eslint $project;
done

mv tsconfig.json tsconfig.base.json
yarn remove @angular-eslint/builder @angular-eslint/schematics
yarn add --dev @nrwl/eslint-plugin-nx eslint-config-prettier eslint-plugin-cypress

# Ignore all files not matched in overrides
npx json -I -f .eslintrc.json -e "this.ignorePatterns = ['**/*'];"

# Support ESLint plugins from `@nrwl/eslint-plugin-nx`
npx json -I -f .eslintrc.json -e "this.plugins = ['@nrwl/nx'];"

# Include tsx files
# Can be left out from an Angular-only workspace
npx json -I -f .eslintrc.json -e "this.overrides[0].files = ['*.ts', '*.tsx'];"

# Match all TypeScript project configuration files
npx json -I -f .eslintrc.json -e "this.overrides[0].parserOptions.project = './tsconfig.*?.json';"

# This setting is not used by the Nrwl Linter
npx json -I -f .eslintrc.json -e "delete this.overrides[0].parserOptions.createDefaultProgram;"

# Replace angular-eslint plugins with the Nx TypeScript ESLint plugin as it uses them internally
npx json -I -f .eslintrc.json -e "this.overrides[0].extends = ['plugin:@nrwl/nx/typescript'];"

# Remove component template rule as this is defined in project-specific ESLint configurations
npx json -I -f .eslintrc.json -e "this.overrides = this.overrides.slice(0, 1);"

# Use Nx JavaScript ESLint plugin for js and jsx files
# Can be left out from an Angular-only workspace
npx json -I -f .eslintrc.json -e "this.overrides = [...this.overrides, { files: ['*.js', '*.jsx'], extends: ['plugin:@nrwl/nx/javascript'], rules: {} }];"

# Remove angular-eslint rules that are added to project-specific ESLint configurations
npx json -I -f .eslintrc.json -e "delete this.overrides[0].rules['@angular-eslint/component-selector'];"
npx json -I -f .eslintrc.json -e "delete this.overrides[0].rules['@angular-eslint/directive-selector'];"
npx json -I -f .eslintrc.json -e "this.overrides = [{ files: ['*.ts', '*.tsx', '*.js', '*.jsx'], rules: { '@nrwl/nx/enforce-module-boundaries': ['error', { enforceBuildableLibDependency: true, allow: [], depConstraints: [{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] }] }] } }, ...this.overrides];"

# For non e2e projects
for row in $(cat angular.json | jq -c '.projects | to_entries | .[] | select(.value.architect.e2e == null) | @base64' | xargs echo); do
    _jq() {
     echo ${row} | base64 --decode | jq -r ${1}
    }

   PROJECT_KEY=$(_jq '.key');
   PROJECT_ROOT=$(_jq '.value.root')
   PROJECT_DEPTH=$(echo ${PROJECT_ROOT} | tr -cd '/' | wc -c)
   PROJECT_TYPE=$(_jq '.value.projectType')
   echo "Migrating project ${PROJECT_KEY}";
   ESLINT_ROOT_PATH=".eslintrc.json"
    for i in `seq 0 ${PROJECT_DEPTH}`; do
        ESLINT_ROOT_PATH="../${ESLINT_ROOT_PATH}"
    done

    # Correct path to root ESLint configuration
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.extends = '${ESLINT_ROOT_PATH}'"

    # Add Nx Angular ESLint plugin and the ESLint inline component template processor
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.overrides[0].extends = ['plugin:@nrwl/nx/angular', 'plugin:@angular-eslint/template/process-inline-templates'];";

    # Match all TypeScript project configuration files
    if [ $PROJECT_TYPE  == "application" ]; then
        npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.overrides[0].parserOptions.project = [this.overrides[0].parserOptions.project[0].replace('/tsconfig.app.json', '/tsconfig.*?.json')];";
    fi

    if [ $PROJECT_TYPE  == "library" ]; then
        npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.overrides[0].parserOptions.project = [this.overrides[0].parserOptions.project[0].replace('/tsconfig.lib.json', '/tsconfig.*?.json')];";
    fi

    # This setting is not used by the Nrwl Linter
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "delete this.overrides[0].parserOptions.createDefaultProgram;";
    # Use the ESLint component template processor and recommended component template rules from angular-eslint
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.overrides[1].extends = ['plugin:@nrwl/nx/angular-template', 'plugin:@angular-eslint/template/recommended'];";

    npx json -I -f angular.json -e "this.projects['${PROJECT_KEY}'].architect.lint.builder = '@nrwl/linter:eslint';"
done

# For e2e projects
for row in $(cat angular.json | jq -c '.projects | to_entries | .[] | select(.value.architect.e2e != null) | @base64' | xargs echo); do
    _jq() {
     echo ${row} | base64 --decode | jq -r ${1}
    }

   PROJECT_KEY=$(_jq '.key');
   PROJECT_ROOT=$(_jq '.value.root')
   PROJECT_DEPTH=$(echo ${PROJECT_ROOT} | tr -cd '/' | wc -c)
   echo "Migrating project ${PROJECT_KEY}";

    # Use rules recommended by Cypress
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.extends = ['plugin:cypress/recommended', this.extends];"

    # Delete rule for component templates
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.overrides = this.overrides.slice(0, 1);"

    # Add rules specifically for the Cypress plugin loader
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.overrides = [{ files: ['src/plugins/index.js'], rules: { '@typescript-eslint/no-var-requires': 'off', 'no-undef': 'off' } }, ...this.overrides];"

    # Match all TypeScript project configuration files
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "this.overrides[1].parserOptions.project = [this.overrides[1].parserOptions.project[0].replace('/tsconfig.app.json', '/tsconfig.*?.json')];"

    # This setting is not used by the Nrwl Linter
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "delete this.overrides[1].parserOptions.createDefaultProgram;"

    # Remove Angular declarable rules
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "delete this.overrides[1].rules['@angular-eslint/component-selector'];"
    npx json -I -f ${PROJECT_ROOT}/.eslintrc.json -e "delete this.overrides[1].rules['@angular-eslint/directive-selector'];"


    npx json -I -f angular.json -e "this.projects['${PROJECT_KEY}'].architect.lint.builder = '@nrwl/linter:eslint';"

    # Only lint js and ts files in the end-to-end test project
    npx json -I -f angular.json -e "this.projects['${PROJECT_KEY}'].architect.lint.options.lintFilePatterns = [this.projects['${PROJECT_KEY}'].architect.lint.options.lintFilePatterns[0].replace('*.ts', '*.{js,ts}')];"
done
yarn remove codelyzer tslint
rm tslint.json

echo "Uninstalling json globally since we're done";
npm uninstall -g json

Enter fullscreen mode Exit fullscreen mode
Collapse
 
warrendugan profile image
Warren Dugan

You're a legend! Thank you so much for this.

Note: if anyone is struggling to get this work due to zsh errors, you'll want to fix the conditionals by escaping the square brackets since zsh tries to do it's own own thing with those

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

That's great 😊 I just realized that we might need to treat non-Angular projects differently, somewhat similar to the e2e project, but without the Cypress rules. Maybe we should filter on builders or something like that.

Collapse
 
kylecannon profile image
Kyle Cannon

Ah yeah.. you're right. Totally doable though.

Collapse
 
petterhoel profile image
Petter

Thank you so much for this guide 😊 the json package stopped supporting lookups in v10 (github.com/trentm/json/blob/master...). Any ideas as to how to migrate all those json editing commands?

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

Hi,

I'm using json version 10.0. It complains sometimes. I have it installed globally which usually works. Sometimes you have to install it as a development dependency in the project and use npx or pnpx to run it. If everything fails, installing it as a development dependency and running it through a package.json script usually works.

Thread Thread
 
petterhoel profile image
Petter

Ah!

I was skimming the json changelog and assumed it was the version, as I have successfully done this in the past. But that was on a different machine/environment, so it was probably something else.

Global install and omitting npx solved it for me. Thanks ☺️

Collapse
 
cogoo profile image
C.OG

THANK YOU! Caps intended .. this was so so useful

Collapse
 
villan3ll3 profile image
VILLAN3LL3

After converting my Angular 11 project to eslint according to this tutorial, it takes 20 minutes to do the linting, as opposed to a few seconds before with TSLint. Is there anyone here who has observed the same behavior? Where is the mistake?

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

Which command do you usually use for linting?

Did you try something like

nx workspace-lint && nx run-many --target=lint --all --parallel --max-parallel=4
Enter fullscreen mode Exit fullscreen mode

or

nx workspace-lint && nx affected --target=lint --parallel --max-parallel=4
Enter fullscreen mode Exit fullscreen mode
Collapse
 
villan3ll3 profile image
VILLAN3LL3

I've already tried both. Likewise ng lint and nx lint. It always takes so long. Isn't that the case with you?

Thread Thread
 
layzee profile image
Lars Gyrup Brink Nielsen

I don't currently have access to a large real world repo, but I just set this one up github.com/LayZeeDK/nx-nrwl-airlin...

Collapse
 
davidshortman profile image
David • Edited

There definitely is a mistake because I'm noticing the opposite. I'm getting about 2x speed improvements in my lint builds which at longest took over 20 minutes and now takes about 10 minutes.

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

It could have to do with operating system. Which operating system are you all using? I'm on Windows 10.

Thread Thread
 
villan3ll3 profile image
VILLAN3LL3

Win 10

Collapse
 
villan3ll3 profile image
VILLAN3LL3

Thank you for sharing your experience, David! Is your repository public so I can compare the eslint implementation?

Thread Thread
 
Sloan, the sloth mascot
Comment deleted
 
villan3ll3 profile image
VILLAN3LL3

This would be really great, thank you! Can you add the tsconfig, too?

Collapse
 
kylecannon profile image
Kyle Cannon • Edited

For people wanting to not do the nx generate @angular-eslint/schematics:convert-tslint-to-eslint for a lot of projects... install jq and then run the following below:

for project in $(cat angular.json | jq -c '.projects | to_entries | map_values(.key) | .[]' | xargs echo); do nx generate @angular-eslint/schematics:convert-tslint-to-eslint $project; done
Enter fullscreen mode Exit fullscreen mode
Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen • Edited

Thanks for this nice suggestion. For some reason, installing jq fails on my machine both under Node 10 and Node 12, so I added a PowerShell script and a Bash script using the json package to do the same as what you suggest here.

Collapse
 
fredericojesus profile image
Frederico Jesus

Hi. Just ran into a different problem after following this guide.
I'm getting this error: 'cannot read config file ........@typescript-eslint.js'.

The error also says that something on eslint-config-prettier version 8.0.0 and to go here github.com/prettier/eslint-config-... to follow instructions.

I followed their instructions but just can't remove that error, can someone help?

For now I reverted eslint-config-prettier to version 7.2.0 and everything works fine, but would be great if someone could help solving the issue for versions 8+.

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

Hi, which version of eslint-config-prettier gets installed with the latest Nx version?

Collapse
 
fredericojesus profile image
Frederico Jesus

Hi Lars, just made the test, version 6.0.0 gets installed. I think this answers the question, thanks a lot.
So maybe they will address this issue when they start using version 8 of eslint-config-prettier.

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

There's currently an issue with some @nrwl/* packages being released at version 11.0.5 while others are still at 11.0.4. I assume this will stabilize soon.

Collapse
 
villan3ll3 profile image
VILLAN3LL3 • Edited

Is there a github repo with the final versions for comparing purposes? Thx!

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen • Edited

Not right now, but that's a great idea 👍

Here's a more realistic sample project generated using the Nx 11 Angular workspace preset github.com/LayZeeDK/nx-nrwl-airlin...

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

Happy to let you all know that Nx 11.6 comes with an official TSLint to ESLint (and angular-eslint) migration.

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

The @nrwl/angular:convert-tslint-to-eslint generator was released as part of Nx 11.6.0, but it seems to only migrate one project at a time. If you're looking to bulk convert a workspace, you have to come up with a solution similar to this suggestion.

Collapse
 
shravansofts profile image
Sravan

How to generate angular v10 based application?
please help me

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

Hi, this is already covered in this guide. Look for migrating from existing workspace. These sections start by generating an Nx 10 workspace.