DEV Community

Migrating and configuring Eslint with Angular 11

Giovanni Sarciotto on November 30, 2020

In this post we will walk through migrating and configuring an Angular 11 project to utilize ESLint and as a bonus add the Prettier formatter. [21...
Collapse
 
ben_howl profile image
Ben

Hello Giovanni ! Thank you for this complete, up-to-date and beginner-friendly tutorial !

I followed the whole tutorial, but here :

"Test the configuration
Check to see if everything is working. For example, in your configuration above, the no unused variables is enabled, so open a Typescript file and create a new variable and check if the linting works."

I installed ESLint VSCode extension wrote an unused var in order to test my config (same as yours), and it didn't get red, so I couldn't check if the linting works . Maybe you could know why it didn't work for me ?

Thanks a lot,

Ben

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

Hi Ben, thanks for the compliments!

Hmm, thats odd, try running the ng lint command. If it works then there is some setting in the extension that you need to see (probably the lint on save one). If it doesn't then reply and I will be glad to help

Collapse
 
ben_howl profile image
Ben

Hi Giovanni, thank you for your quick reply !
When I run ng lint, I do have errors and warnings, and unused vars belong to the warning category - that might be the reason why it doesn't get red when I write an unused var. That's weird anyway, because I just followed your tuto.

Thread Thread
 
gsarciotto profile image
Giovanni Sarciotto

Yeah, in the config above unused vars is a warning. You can make it an error in the rules section, see this. I don't think you will need to disable the core ESLint rule since @typescript-eslint/recommended already does this, so just add the "@typescript-eslint/no-unused-vars": ["error"] line

Collapse
 
fabio984 profile image
fabio984

Hi, after eslint setup should we delete the lint
@angular-eslint/schematics": "1.0.0",
from package.json? Otherwise when npm install it warns that TSLINT is deprecated.

Finding which package depends on it:

npm ls tslint
-- @angular-eslint/schematics@1.0.0
-- tslint-to-eslint-config@2.0.1
`-- tslint@6.1.3

tkx

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

I don't think it would cause any problems to uninstall the schematics after the transition.

Collapse
 
pmo1948 profile image
pmo1948

Hi,
Once everything has been configured, is there a way to run it, or add it to a github hook?
Before, I could run npm lint, where that would run ng lint and I could use that as needed. I tried running eslint from the terminal, but it did not give me any errors. (I know I have errors though, bc Webstorm is letting me know)

Collapse
 
pmo1948 profile image
pmo1948

solution - in angular.json, change "builder" : "@angular-devkit/build-angular:tslint" to "builder": "@angular-eslint/builder:lint"

Collapse
 
denernun profile image
Dener Rocha

Show! but SLint couldn't find the config "prettier/@typescript-eslint" to extend from. Please check that the name of the config is correct. How solve this issue ?

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

Did you install the dependencies npm install -D prettier eslint-config-prettier eslint-plugin-prettier?

Collapse
 
denernun profile image
Dener Rocha

Hi, yes, i did...

eslintrc.json

"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates",
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"prettier",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],

command

$ eslint --color -c .eslintrc.json --ext .ts ./src/

result

Oops! Something went wrong! :(
ESLint: 7.20.0
ESLint couldn't find the config "prettier/@typescript-eslint" to extend from. Please check that the name of the config is correct.
The config "prettier/@typescript-eslint" was referenced from the config file in ".eslintrc.json".
If you still have problems, please stop by eslint.org/chat/help to chat with the team.

log

0 info it worked if it ends with ok
1 verbose cli [
1 verbose cli 'C:\Program Files\nodejs\node.exe',
1 verbose cli 'C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js',
1 verbose cli 'run',
1 verbose cli 'eslint'
1 verbose cli ]
2 info using npm@6.14.11
3 info using node@v14.15.5
4 verbose run-script [ 'preeslint', 'eslint', 'posteslint' ]
5 info lifecycle app@1.0.0~preeslint: app@1.0.0
6 info lifecycle app@1.0.0~eslint: app@1.0.0
7 verbose lifecycle app@1.0.0~eslint: unsafe-perm in lifecycle true
8 verbose lifecycle app@1.0.0~eslint: PATH: C:\Program Files\nodejs\node_modules\npm\node_modules\npm-lifecycle\node-gyp-bin;D:\SISTEMAS\CLOUDCLASS\cloudclass-app\node_modules.bin;C:\Program Files\PowerShell\7;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\intel64\compiler;C:\Program Files\Python27\;C:\Program Files\Python27\Scripts;C:\Windows;C:\Windows\system32;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\ProgramData\chocolatey\bin;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files (x86)\Firebird\Firebird_2_5\bin;D:\SSL\BIN;D:\SSH;D:\SSH\tools;D:\SVN;D:\UTIL;D:\DLL;C:\Program Files\dotnet\;C:\Program Files\Git LFS;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files\Common Files\Tom Sawyer Software\10.0.0;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files\PowerShell\7-preview\preview;C:\Program Files\Java\bin;C:\Program Files\Docker\Docker\resources\bin;C:\ProgramData\DockerDesktop\version-bin;C:/Program Files (x86)/CS DEVICES/SatCare/Biblioteca de funções;C:\Program Files\PowerShell\7\;C:\Program Files\nodejs\;C:\Program Files\Git\cmd;C:\Users\dener\AppData\Local\Microsoft\WindowsApps;C:\Users\dener\AppData\Local\Programs\Microsoft VS Code Insiders\bin;C:\Program Files\kdiff3;C:\Users\dener.dotnet\tools;C:\Users\dener\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\dener\AppData\Local\Microsoft\WindowsApps;C:\Users\dener\AppData\Roaming\DBeaverData\drivers\clients\postgresql\win\12;C:\Users\dener.dotnet\tools;d:\harbour\bin;d:\bcc\bin;;C:\Program Files (x86)\CS DEVICES\SatCare\Biblioteca de funções;C:\Users\dener\AppData\Roaming\npm
9 verbose lifecycle app@1.0.0~eslint: CWD: D:\SISTEMAS\CLOUDCLASS\cloudclass-app
10 silly lifecycle app@1.0.0~eslint: Args: [ '/d /s /c', 'eslint --color -c .eslintrc.json --ext .ts ./src/' ]
11 silly lifecycle app@1.0.0~eslint: Returned: code: 2 signal: null
12 info lifecycle app@1.0.0~eslint: Failed to exec eslint script
13 verbose stack Error: app@1.0.0 eslint: eslint --color -c .eslintrc.json --ext .ts ./src/
13 verbose stack Exit status 2
13 verbose stack at EventEmitter. (C:\Program Files\nodejs\node_modules\npm\node_modules\npm-lifecycle\index.js:332:16)
13 verbose stack at EventEmitter.emit (events.js:315:20)
13 verbose stack at ChildProcess. (C:\Program Files\nodejs\node_modules\npm\node_modules\npm-lifecycle\lib\spawn.js:55:14)
13 verbose stack at ChildProcess.emit (events.js:315:20)
13 verbose stack at maybeClose (internal/child_process.js:1048:16)
13 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:288:5)
14 verbose pkgid app@1.0.0
15 verbose cwd D:\SISTEMAS\CLOUDCLASS\cloudclass-app
16 verbose Windows_NT 10.0.21318
17 verbose argv "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" "run" "eslint"
18 verbose node v14.15.5
19 verbose npm v6.14.11
20 error code ELIFECYCLE
21 error errno 2
22 error app@1.0.0 eslint: eslint --color -c .eslintrc.json --ext .ts ./src/
22 error Exit status 2
23 error Failed at the app@1.0.0 eslint script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 2, true ]

if comment "prettier/@typescript-eslint", works fine

Collapse
 
stativka profile image
Eugene Stativka

I have the same problem and I install these dependendencies

Thread Thread
 
gsarciotto profile image
Giovanni Sarciotto

@stativka @denernun

I discovered the problem, eslint-config-prettier was updated yesterday and they changed somethings. Now you only need to extend plugin:prettier/recommended. So just remove both entries of prettier/@typescript-eslint from the config file and it should work.

Thread Thread
 
stativka profile image
Eugene Stativka

Thank you for your prompt reply and for this article!

Collapse
 
d4lj1t profile image
d4lj1t

this did it for me , i must have missed the other dependencies

Collapse
 
davidbronfen profile image
David Bronfen • Edited

Hi, thanks for the informative and very helpful post :)
when I run the ng lint command I get errors that relate to type any of the angular form's value
Thrown error: Unsafe member access [curr] on an any value - @typescript-eslint/no-unsafe-member-access
Do I need to change any eslint - typescript configuration in order to avoid this type of errors?

error log

  155:6 error Unsafe member access [curr] on an any value  @typescript-eslint/no-unsafe-member-access
  160:6 error Unsafe member access [curr] on an any value @typescript-eslint/no-unsafe-member-access
  162:6 error Unsafe assignment of an any value @typescript-eslint/no-unsafe-assignment
  162:18 error Unsafe member access [curr] on an any value @typescript-eslint/no-unsafe-member-access
Enter fullscreen mode Exit fullscreen mode

code snippet

getDataToUpdate(): Partial<IUserModelForAdmin> {
        return Object.keys(this.userForm.value).reduce(
            (acc: Partial<IUserModelForAdmin>, curr: string) => {
                if (
                    curr === "company" &&
                    this.user[curr] &&
                    // Line 155
                    this.userForm.value[curr] !== this.user[curr]["_id"]
                )
                    acc[curr]["_id"] = this.user[curr]["_id"];
                if (
                    curr !== "company" &&
                    // Line 160
                    this.userForm.value[curr] !== this.user[curr]
                )
                // line 162 
                    acc[curr] = this.userForm.value[curr]; 
                return acc;
            },
            { _id: this.user._id }
        );
    }
Enter fullscreen mode Exit fullscreen mode

eslintrc.json

{
  "root": true,
  "ignorePatterns": [
    "projects/**/*",
    "src/app/verify/**/*"
  ],
  "overrides": [
    {
      "files": [
        "*.ts"
      ],
      "parserOptions": {
        "project": [
          "tsconfig.json",
          "tsconfig.spec.json",
          "e2e/tsconfig.json"
        ],
        "createDefaultProgram": true
      },
      "extends": [
        "plugin:@angular-eslint/recommended",
        "plugin:@angular-eslint/template/process-inline-templates",
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:@typescript-eslint/recommended-requiring-type-checking",
        "plugin:prettier/recommended"
      ],
      "rules": {
        "@angular-eslint/component-selector": [
          "error",
          {
            "prefix": "app",
            "style": "kebab-case",
            "type": "element"
          }
        ],
        "@angular-eslint/directive-selector": [
          "error",
          {
            "prefix": "app",
            "style": "camelCase",
            "type": "attribute"
          }
        ]
      }
    },
    {
      "files": [
        "*.html"
      ],
      "extends": [
        "plugin:@angular-eslint/template/recommended",
        "plugin:prettier/recommended"
      ],
      "rules": {}
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
sardapv profile image
Pranav Sarda

great article. I did something similar to my angular12 projetct. But i don't think plugin:@typescript-eslint is required anymore. angular-eslint alone works nicely for me since there is no tslint in angular 12 anyways

Collapse
 
nelisbijl profile image
nelisbijl

Great article. However...

"plugin:@angular-eslint/template/process-inline-templates"
seems to extract an inline template and feed it to the html linter
The prettier extension for *.html ("plugin:prettier/recommended") produces errors regarding the code formatting. The indicated error position is wrong and you won't be able to resolve this anyhow inside your inline template.

Either use external templates or stop using the process-inline-templates extension. It does not seem to kill all linting as I still noticed a warning for a missing closing tag. What exactly you do loose, I don't know.

This issue seems related:
github.com/angular-eslint/angular-...

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

Yeah, I don't use inline templates so I didn't notice anything related to that, I will link this issue in the article. Thanks for the head up

Collapse
 
gingi profile image
Shiran Pasternak

Is it possible to run this migration without ng? I have an existing Angular project without ng. Retrofitting an ng workspace seems like a science project.

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

Automatically I dont think so because of the schematics. You can however do the migration full manual, see the repo

Collapse
 
glyder profile image
George Pagotelis

Agree with the other person about .scss requires a link!

SCSS is difficult to find ESLINT that works in Angular. For example:
a {
@include flexbox(row)
height: 100%;
padding: 20px;
border-radius: 3px;
}

The above should be picked up in linting (missing semicolon on flexbox) but ESLINT doesn't.

If I open the file vscode-prettier or eslint IS picking it up.

I have tried (npmjs.com/package/eslint-plugin-cs...) but doesn't appear to trigger:
"overrides": [
{
// TODO: this isn't triggering on flexbox semicolon errors
"files": "*.scss",
"plugins": ["css-modules"],
"extends": ["plugin:css-modules/recommended"],
"rules": {
"css-modules/no-unused-class": [2, { "camelCase": true }],
"css-modules/no-undef-class": [2, { "camelCase": true }]
}
},

Collapse
 
jtsom profile image
John Tsombakos

Unless I miss something, it looks like this change removes the TSlint requirement of semi-colons on the end of lines? And mis-aligned statements (whitespace)? Is there a way to get this back?

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

This config enforces semi-colons at the end of lines and should fix alignment with Prettier. Did you run the eslint command or are trying to see in your editor? In the latter case, install the eslint extension to allow for the editor to lint when you save etc

Collapse
 
jtsom profile image
John Tsombakos

This is what I had to add to my eslintrc.json to get the behavior I wanted:

"@typescript-eslint/member-delimiter-style": [
"error",
{
"multiline": {
"delimiter": "semi",
"requireLast": true
},
"singleline": {
"delimiter": "semi",
"requireLast": false
}
}
],
"quotes": "off",
"@typescript-eslint/quotes": [
"error",
"single",
{
"allowTemplateLiterals": true
}
],
"@typescript-eslint/semi": [
"error",
"always"
],

(copied from ./node_modules/@angular-eslint/eslint-plugin/dist/configs/ng-cli-compat--formatting-add-on.json)

Collapse
 
jtsom profile image
John Tsombakos

I didn’t install the prettier part (I don’t like it’s default settings and some I can’t configure). I have the eslint extension in VSCode but it does not show an error if I don’t have a semi. Before I made the changes you suggest for the “recommended” settings, it does show a message for missing semi’s. Once I made the changes, that setting is not there. I had to copy the rule from the “all” rules into my eslint config to get it to work. I also could not get it to mark mis-aligned lines (though formatting the document fixes that)

Thread Thread
 
gsarciotto profile image
Giovanni Sarciotto

Fair enough, maybe eslint-angular is disabling these rules since I know that at least the semi one is enabled by @typescript-eslint/recommended, will check it out later. Glad you could figure it out tho!

Thread Thread
 
jtsom profile image
John Tsombakos • Edited

As far as I can see, looking through the rules, semi is not enabled by @typescript-eslint/recommended

rules: {
        '@typescript-eslint/adjacent-overload-signatures': 'error',
        '@typescript-eslint/ban-ts-comment': 'error',
        '@typescript-eslint/ban-types': 'error',
        '@typescript-eslint/explicit-module-boundary-types': 'warn',
        'no-array-constructor': 'off',
        '@typescript-eslint/no-array-constructor': 'error',
        'no-empty-function': 'off',
        '@typescript-eslint/no-empty-function': 'error',
        '@typescript-eslint/no-empty-interface': 'error',
        '@typescript-eslint/no-explicit-any': 'warn',
        '@typescript-eslint/no-extra-non-null-assertion': 'error',
        'no-extra-semi': 'off',
        '@typescript-eslint/no-extra-semi': 'error',
        '@typescript-eslint/no-inferrable-types': 'error',
        '@typescript-eslint/no-misused-new': 'error',
        '@typescript-eslint/no-namespace': 'error',
        '@typescript-eslint/no-non-null-asserted-optional-chain': 'error',
        '@typescript-eslint/no-non-null-assertion': 'warn',
        '@typescript-eslint/no-this-alias': 'error',
        'no-unused-vars': 'off',
        '@typescript-eslint/no-unused-vars': 'warn',
        '@typescript-eslint/no-var-requires': 'error',
        '@typescript-eslint/prefer-as-const': 'error',
        '@typescript-eslint/prefer-namespace-keyword': 'error',
        '@typescript-eslint/triple-slash-reference': 'error',
    },
Enter fullscreen mode Exit fullscreen mode

which extends:

           rules: {
                'constructor-super': 'off',
                'getter-return': 'off',
                'no-const-assign': 'off',
                'no-dupe-args': 'off',
                'no-dupe-class-members': 'off',
                'no-dupe-keys': 'off',
                'no-func-assign': 'off',
                'no-import-assign': 'off',
                'no-new-symbol': 'off',
                'no-obj-calls': 'off',
                'no-redeclare': 'off',
                'no-setter-return': 'off',
                'no-this-before-super': 'off',
                'no-undef': 'off',
                'no-unreachable': 'off',
                'no-unsafe-negation': 'off',
                'no-var': 'error',
                'prefer-const': 'error',
                'prefer-rest-params': 'error',
                'prefer-spread': 'error',
                'valid-typeof': 'off', // ts(2367)
            },

Enter fullscreen mode Exit fullscreen mode

the no-extra-semi rule just warns if there is more than one semi-colon (;;)

Collapse
 
ryandmello1198 profile image
Ryan

Very beginner friendly explanation ! Got me started. Thanks.

Collapse
 
bogac profile image
Bogac Guven • Edited

Thanks for putting this together.

Collapse
 
pkhand0 profile image
pkhand0 • Edited

Hi Giovanni!

Thanks for the wonderful article. Actually i tried all the above steps but not getting error in VSS code . But when running ng lint seeing bunch of error.

Can you suggest what may be issue

This is the error i am seeing in ESLint COnsole
doesn't export a CLIEngine. You need at least eslint@1.0.0

Collapse
 
rkristelijn profile image
Remi Kristelijn

Very good article also that you keep improving it. It may be worth noting that the status of having it integrated with Angular is available here: github.com/angular/angular-cli/iss...

Collapse
 
julienb37 profile image
Julien BONNET

Thanks for this, it was interesting. But but i don't understand a thing : what is the advantage using Prettier when every options are present in ESlint ??

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

Yes, ESLint has a lot of styling options, however I prefer to use Prettier for 2 reasons:

  1. ESLint is a linter, therefore its main job is to analyze code to find errors, potential bugs and code structure (not style, i.e big functions/classes) not conforming to some standard. Prettier is a formatter, therefore its only job is to format the code. I prefer this logic separation. If you have extremely customized linting rules, it can be hard to find the styles rules if they are in the same logic unit (in the .eslintrc), you can workaround this by using a separate .eslintrc file just for your style rules but I don't like it.

  2. The main reason though is because Prettier is opinionated and have some pretty strong defaults, you don't actually need to configure Prettier if you don't want to and it will work just fine.

Collapse
 
julienb37 profile image
Julien BONNET

hmm ... i understand. Thank you

Collapse
 
singhshubham97 profile image
singhshubham97

I have two questions:
1)The Html plugins mentioned here (those prettier ones) are giving parse errors.
2)Do we have other plugins for using ESLINT for HTML templates in angular

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

1) What does the error says?

2) There are pure HTML plugins for eslint, however I dont think they would play nice with the features of angular (template binding, etc)

Collapse
 
singhshubham97 profile image
singhshubham97 • Edited

The parse error in index.html
dev-to-uploads.s3.amazonaws.com/i/...

Thread Thread
 
gsarciotto profile image
Giovanni Sarciotto

Alright, I managed to solve the problem in my machine. It seems like prettier isn't able to decide which parser to use for some reason. Add the following in your .prettierrc:

"overrides": [
        {
            "files": "*.component.html",
            "options": {
                "parser": "angular"
            }
        },
        {
            "files": "*.html",
            "options": {
                "parser": "html"
            }
        }
    ]
Enter fullscreen mode Exit fullscreen mode

If this fixes your problem, please tell me so that I can update the article!

Thread Thread
 
singhshubham97 profile image
singhshubham97

Thanks Giovanni for the help!! Its working in my machine

Collapse
 
jwuliger profile image
Jared

Thanks for this article. It was beneficial and needed.

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

Thanks! Glad I could help :)

Collapse
 
qarunqb profile image
Bearded JavaScripter

This article really helped me a lot!
I had to lint a huge codebase and this really targeted all the rules that we wanted. Super cool stuff 😁

Collapse
 
romulos profile image
Rômulo Sorato • Edited

Help!
When run command: ng g @angular-eslint/schematics:convert-tslint-to-eslint codex-frontend --force
it showns an error:

Cannot find angular.json

Collapse
 
tamusjroyce profile image
tamusjroyce

aw. that is mean to mention you could have added linting for .scss and then not or a even a link. good article otherwise!

Collapse
 
romulos profile image
Rômulo Sorato

I have a problem when do ng g @angular-eslint/schematics:convert-tslint-to-eslint codex-frontend --force
"Cannot find angular.json"

Collapse
 
fabio984 profile image
fabio984 • Edited

I don't know what I did wrong but I'm only getting ts warns (not eslint).
see (open in new tab) :
dev-to-uploads.s3.amazonaws.com/i/...

Collapse
 
gsarciotto profile image
Giovanni Sarciotto

Did you integrated VSCode to lint on save? (see this extension)

If you did, are you using the same rules as in the article?