As you know, Palantir decided to deprecate TSLint and focus on the improvement of TypeScript support in ESLint in order to avoid the developing of similar tools.
Although Angular 10 will not be migrated to ESLint, I decided to migrate some projects to ESLint and at the same time revise style guides and add some new tools. This post is about how to migrate Angular TypeScript app from TSLint to ESLint, add Prettier, configure Git hooks and VS Code.
Prettier & ESLint
ESLint is a tool for static code analysis. Rules in ESLint fall into two groups:
- Formatting — to transform code in consistent style: string length, commas, semicolons and other.
- Code quality — to search and fix problem code patterns: unnecessary code, errors.
Prettier is an opinionated code formatter that can automatically format code on file save with no configuration.
The question I am interested in: why do we need to use Prettier together with ESLint, if ESLint can do all the things by itself? The answer is pretty easy — Prettier formats code much better. It removes all formatting and reprints code in the consistent style from scratch. This allows developers to forget about formatting the code and not waste time discussing the code style on code reviews.
For example, we have this long string of code:
const example = ['1', 'long string', 'another string', 0123456789, '1', 'long string', 'another string'];
If we try to format this string with ESLint, it just throws an error in console:
eslint example.ts --fix
output:
error This line has a length of 105. Maximum allowed is 80
This example shows that linters don't always help with code formatting. And so developers format code by themselves in different ways, depending on their personal consideration.
If we save and format the file with Prettier, the string will be reprinted to:
const example = [
'1',
'long string',
'another string',
0123456789,
'1',
'long string',
'another string'
];
Prettier provides consistent formatting style through a whole code base. Therefore it must be used together with ESLint. However, we have to configure them so that they don't conflict with each other.
Configuring ESLint
ESLint works with parsers that transform code into AST (Abstract Syntax Tree) for software processing and plugins, which contain rules, for example, recommended rules for linting TypeScript or rules from style guides.
Dependency installation
To migrate Angular app to ESLint we will use these dependencies:
-
@angular-eslint/builder
— Angular CLI Builder to run ESLint for Angular apps with standart commandng lint
, -
@angular-eslint/eslint-plugin
— plugin with rules for linting Angular apps, -
@angular-eslint/template-parser
— parser, which in conjunction with@angular/compiler
makes it possible to write and use rules for linting Angular templates, -
@angular-eslint/eslint-plugin-template
— plugin, which in conjunction with@angular-eslint/template-parser
, run rules to lint Angular templates, -
@typescript-eslint/parser
— plugin to parse TypeScript code, -
@typescript-eslint/eslint-plugin
— plugin, which run rules to lint TypeScript.
To install them just run:
ng add @angular-eslint/schematics
At the moment, not all ESLint rules from basic TSLint configuration by Codelyzer have equivalents in @typescript-eslint
and @angular-eslint
, but most of them are already there. You can track current state of rules development in official monorepos Angular ESLint and TypeScript ESLint.
Configuring
We have installed everything we need to lint Angular app, now we can start configuring ESLint. Let's create a configuration file .eslintrc.js
in app root and add recommended settings from Angular ESLint:
module.exports = {
root: true,
overrides: [
{
files: ["*.ts"],
parserOptions: {
project: [
"tsconfig.*?.json",
"e2e/tsconfig.json"
],
createDefaultProgram: true
},
extends: ["plugin:@angular-eslint/recommended"],
rules: {
...
}
},
{
files: ["*.component.html"],
extends: ["plugin:@angular-eslint/template/recommended"],
rules: {
"max-len": ["error", { "code": 140 }]
}
},
{
files: ["*.component.ts"],
extends: ["plugin:@angular-eslint/template/process-inline-templates"]
}
]
}
You can create config in different extensions: JS, JSON and YAML. In JavaScript you can write comments.
plugin:@angular-eslint/recommended
containes rules for three plugins: @typescript-eslint/eslint-plugin
, @angular-eslint/eslint-plugin
and @angular-eslint/eslint-plugin-template
.
Update ng lint command
Also we need to update ng lint
command in angular.json
to run @angular-eslint/builder
:
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.component.html"
]
}
},
The basic setup is ready. Now, to start ESLint you just need to run ng lint
.
Install additional ESLint Plugins
If you want to install another plugin for ESLint, for example, to lint Jasmine spec files, install appropriate npm-package:
npm install eslint-plugin-jasmine --save-dev
And add a new block of rules in "overrides" for files with *.spec.ts
extension:
overrides: [
...,
{
files: ['src/**/*.spec.ts', 'src/**/*.d.ts'],
parserOptions: {
project: './src/tsconfig.spec.json',
},
// Jasmine rules
extends: ['plugin:jasmine/recommended'],
// Plugin to run Jasmine rules
plugins: ['jasmine'],
env: { jasmine: true },
// Turn off 'no-unused-vars' rule
rules: {
'@typescript-eslint/no-unused-vars': 'off'
}
}
],
You can add this way any plugin to your ESLint configuration.
Add Style Guides rules
For better code base consistency, let's choose and add to ESLint config rules from one of popular style guides:
- AirBnB: most popular and strict from these three, requires trailing commas and semicolons,
- Google: has a lot in common with AirBnB but less strict, requires JSDoc.
- StandartJS: forbids using of trailing commas and semicolons.
Choose style guide that fit better to your team requirements. You can try each style guide on one of your projects, looking at what errors are showed by the linter and make a decision which rules you agree with.
Choose TypeScript realization of style guides rules, because rules for JavaScript can work wrong with TypeScript.
For example, let's add to ESLint config rules from AirBnB Styleguide. To do this we need to install config with AirBnB rules for TypeScript and plugin to work with import/export syntax:
npm install eslint-plugin-import eslint-config-airbnb-typescript --save-dev
In order not to change top level settings, add AirBnB Styleguide rules in a block of the ['*.ts']
rules in "overrides" property:
module.exports = {
...,
overrides: [
{
files: ["*.ts"],
parserOptions: {
project: [
"tsconfig.*?.json",
"e2e/tsconfig.json"
],
createDefaultProgram: true
},
extends: [
"plugin:@angular-eslint/recommended",
// AirBnB Styleguide rules
'airbnb-typescript/base'
],
rules: {
...
}
},
...
]
}
If you want to use another style guide, create a new block of the rules in "overrides" property with style guide rules and necessary parser to run them as in an example.
Rules customization
If you want to turn off or redefine some of the rules, you can do it in "rules" property:
module.exports = {
...,
overrides: [
{
files: ["*.ts"],
parserOptions: {
project: [
"tsconfig.*?.json",
"e2e/tsconfig.json"
],
createDefaultProgram: true
},
extends: [
"plugin:@angular-eslint/recommended",
// AirBnB Styleguide rules
'airbnb-typescript/base'
],
rules: {
// Custom rules
'import/no-unresolved': 'off',
'import/prefer-default-export': 'off',
'class-methods-use-this': 'off',
'lines-between-class-members': 'off',
'@typescript-eslint/unbound-method': [
'error',
{
ignoreStatic: true,
}
]
}
},
...
]
}
Configuring Prettier
To add Prettier in ESLint config, we need to install Prettier itself, plugin with Prettier rules and config that turns off all rules that conflict with Prettier:
npm i prettier eslint-config-prettier eslint-plugin-prettier --save-dev
In ESLint config "overrides" in the bottom of "extends" property in the block of rules for files with .ts
extension add Prettier settings:
module.exports = {
...,
overrides: [
{
files: ["*.ts"],
parserOptions: {
project: [
"tsconfig.*?.json",
"e2e/tsconfig.json"
],
createDefaultProgram: true
},
extends: [
"plugin:@angular-eslint/recommended",
// AirBnB Styleguide rules
'airbnb-typescript/base',
// Settings for Prettier
'prettier/@typescript-eslint',
'plugin:prettier/recommended'
],
rules: {
...
}
},
...
]
}
Configuration for Prettier should always be in the bottom of "extends" property, to turn off all the previous rules, which can conflict with Prettier.
prettier/@typescript-eslint
turns off rules from @typescript-eslint
, which can conflict with Prettier, and plugin:prettier/recommended
does three things:
- enables
eslint-plugin-prettier
, - marks
prettier/prettier
rules as"error"
, - adds Prettier formatting rules
eslint-config-prettier
.
Prettier config
Prettier can format files with no configuration but for AirBnB code guide we need to specify some settings. Create .prettierrc.js
in app root folder:
module.exports = {
trailingComma: "all",
tabWidth: 2,
semi: true,
singleQuote: true,
bracketSpacing: true,
printWidth: 100
};
This configuration will be used by ESLint and by Prettier if you want to run it separately. You can format your code with Prettier itself with prettier --write .
or with Prettier Plugin for VS Code.
Configuring VS Code
VS Code can highlight errors which are found by ESLint and fix them on file save. To enable this feature install ESLint plugin for VS Code and create a file with configuration for workspace .vscode/settings.json
:
"eslint.validate": [ "javascript", "typescript", "html"],
"eslint.options": {
"extensions": [".js", ".ts", "html"]
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
},
You can also install Prettier plugin for VS Code to format files with shift + option + f
with Prettier.
Configuring Git Hooks
Git Hooks are scripts, which Git calls on certain events: commit, push, recieve.
With Hooks we can run linting on commit creation to reduce errors falls into pull requests. For better work with Git Hooks install Husky, and to lint only staged files (it is necessary on big projects where linting can be very long) lint-staged:
npm i husky lint-staged --save-dev
Add new settings for this plugins in package.json
:
"scripts": {
...,
},
"husky": {
"hooks": {
"pre-commit": "lint-staged --relative"
}
},
"lint-staged": {
"*.{js,ts}": [
"eslint --fix"
]
},
Lint-staged sends to the called command array of staged files. ng lint
can't accept arrays of files and to use it we have to write an additional handler script. Or we can just run ESLint like in this example. You can use this solution for precommits, and ng lint
for linting all files of project, for example, in CI pipeline.
Final thoughts
In the future versions of Angular ESLint will be out of the box. Now ESLint configuring requires some additional actions, some of the rules don't have equivalent in ESLint and Angular ESLint monorepo is in alpha version. So, whether to migrate to ESLint or not — is up to you.
However, code guides, additional rules, Prettier, Husky and lint-staged you should install by yourself. I hope this article give you understanding of how this things work together. You can find configured Angular project on Github.
Linters set up can look like a very trivial task but include some important organizational questions: what style guide to choose, what the rules do plugins include and how they work together. It is worth spending your time on this process, because it will save a lot of time on discussing code style on code reviews, provide code base consistency and reduce number or errors sent in pull requests.
Thank you for reading! Don't hesitate to leave a comment if you have any questions or additions.
Top comments (17)
Thank you for your guide, I've followed several guides but none seemed to help me. You've been very helpful
I'm happy that my article is still helpful, I need to update it :)
Hi, I followed your tutorial with Angular 10, and I got an error on the first line of each files :
or
Any idea? Thanks!
Ok, we just have to wait :-)
github.com/angular-eslint/angular-...
👍
Amazing tutorial thanks Bogdan !!!! Helped me a lot trough my process.
You mention that ESLint will be out-of-the-box in future versions of Angular and sadly this is not the case !
"That being said, it’s most likely that in future the CLI will not providing a linting solution out of the box, and it will be up to the users to choose a builder similar to how it's currently done for ng deploy."
github.com/angular/angular-cli/iss...
Thanks for your comment and link to the discussion! I think
@angular-eslint
will be a common choice in the future.Nice! Ty for that Tutotial. Help me a lot.
I'm glad it helped!
Finally a complete step by step easy to follow guide on how to achieve this. Thanks, Bogdan.
I'm glad it helped!
Great tutorial, definitely helped me. Thank you.
Thanks for your comment :)
Nice! Great work!
Thanks! 💪
Seems lot of work and libraries we need to install to achieve this. That gives me a thought is it worth it
For basic ESLint setup you can run schematics, change builder and copy ESLint config from official Angular ESLint repo.