We port Angular project on ESLint, with Prettier, Husky and lint-staged

Hello, Habr! My name is Bogdan, I work in PIC Digital Front-End team lead. We are developing most of the projects on Angular, and recently I decided to review our style guides, as well as add new tools for more convenient work.

As a linter, I decided to use ESLint, as they plan to transfer Angular to it soon. And in this article I want to share instructions on switching from TSLint to ESLint, and at the same time tell how to run Prettier from ESLint, how to add AirBnB style guide rules, and how to make linting convenient and invisible by setting VS Code and Git hooks.

Prettier & ESLint


ESLint is a tool for static code analysis, the rules are divided into two groups:

  • Formatting - to bring the code into one view: line lengths, commas, semicolons, etc.
  • Code quality - searching for problematic code templates: unnecessary code, errors.

Prettier is an automatic code formatting tool.

The question that interested me was: why use Prettier if ESLint can also format code?

The answer is simple - Prettier formats the code much better: removes all formatting and completely rewrites the code in a single style. This allows developers to forget about code formatting and not waste time discussing code style for review. For example, we have a long line of code:

image

If we try to change the formatting through ESLint, it will simply give us an error:

eslint example.ts --fix

output:
error    This line has a length of 97. Maximum allowed is 80

Such an example shows that linter can not always help with code formatting, and developers can format this code in different ways, based on their personal considerations.

If we save or format the file with Prettier, the line will take the form:

image

Prettier provides a uniform style throughout the code base. Therefore, it can and should be used together with ESLint, but you need to configure them so that they do not interfere with each other.

ESLint setup


The essence of linting using ESLint is in parsers that transform the code into AST (Abstract Syntax Tree) for further software processing, and plugins that contain rules, for example, recommended rules for TypeScript linting or AirBnB code guide rules.

Installation


To migrate an Angular application to ESLint, we need the following dependencies:


To install them, just run the command:

ng add @angular-eslint/schematics

At the time of writing, typescript-eslint and angular-eslint do not have all the equivalents for the rules from the standard Codelyzer configuration for TSLint, but most of them already exist. You can monitor the current status of transferring rules from TSLint to ESLint in the Angular ESLint and TypeScript ESLint mono-repositories .

Config configuration


Everything we need to lint Angular applications, we installed. Now let's move on to configuring ESLint. Let's create a .eslintrc.js file and add the recommended settings for Angular ESLint:

module.exports = {
  extends: ['plugin:@angular-eslint/recommended'],
  rules: {
    '@angular-eslint/directive-selector': [
      'error',
      { type: 'attribute', prefix: 'app', style: 'camelCase' },
    ],
    '@angular-eslint/component-selector': [
      'error',
      { type: 'element', prefix: 'app', style: 'kebab-case' },
    ],
  },
  overrides: [
    //   ,       *.component.ts
    {
      files: ['*.component.ts'],
      parser: '@typescript-eslint/parser',
      parserOptions: {
        ecmaVersion: 2020,
        sourceType: 'module',
      },
      plugins: ['@angular-eslint/template'],
      processor: '@angular-eslint/template/extract-inline-html',
    },
  ],
};

Configs can be described in different formats: JavaScript, JSON or YAML file. You can leave comments in JavaScript.

“Plugin: @ angular-eslint / recommended” contains settings for 3 plugins at once: “@ typescript-eslint / eslint-plugin”, “@ angular-eslint / eslint-plugin” and “@ angular-eslint / eslint-plugin-template " You can read what rules he sets here .

Ng lint command update


Also in the angular.json configuration, you need to update the ng lint command to run @ angular-eslint / builder :

"lint": {
  "builder": "@angular-eslint/builder:lint",
  "options": {
    "eslintConfig": ".eslintrc.js",
    "tsConfig": [
      "tsconfig.app.json",
      "tsconfig.spec.json",
      "e2e/tsconfig.json"
    ],
    "exclude": [
      "**/node_modules/**"
    ]
  }
},

The basic ESLint setup is ready, now you can start ESLint with the standard ng lint command .

Install additional plugins


To install the plugin for ESLint, for example, for linting unit tests in Angular, you need to download and add the Jasmine plugin to the settings :

npm install eslint-plugin-jasmine --save-dev

And add a new settings block for files with the * .spec.ts extension to the “overrides” property:

overrides: [
  ...,
  {
    files: ['src/**/*.spec.ts', 'src/**/*.d.ts'],
    parserOptions: {
      project: './src/tsconfig.spec.json',
    },
    //   
    extends: ['plugin:jasmine/recommended'],
    //    
    plugins: ['jasmine'],
    env: { jasmine: true },
    //   'no-unused-vars'
    rules: {
      '@typescript-eslint/no-unused-vars': 'off',
    },
  }
],

By analogy, you can add other plugins for different file extensions.

Adding Style Guide Guides


To achieve greater consistency of the code base, you can select and add the rules of one of the popular style guides to the ESLint config:

  • AirBnB : The most popular and strictest of the three, mandatory trailing commas and semicolons.
  • Google : similar to AirBnB in terms of formatting, but less strict, mandatory JSDoc comments.
  • StandartJS : prohibits the use of trailing commas and semicolons.

Choose a style guide that is more suitable for your team. You can take turns trying all the style guides on some large project, see what errors the linter produces and based on this make a choice.

Choose an implementation of the TypeScript style guide because JavaScript rules may not work correctly on TypeScript.

As an example, let's add the AirBnB style guide to our ESLint config. To do this, install the config with AirBnB rules for TypeScript and the plugin with rules for working with import / export syntax:

npm install eslint-plugin-import eslint-config-airbnb-typescript --save-dev

In order not to change the top-level settings, we will create a new block of rules in the “overrides” property with the AirBnB style guide rules and the TypeScript parser necessary for their work:

module.exports = {
  ...,
  overrides: [
    ...,
    {
      files: ['*.ts'],
      extends: [
        'airbnb-typescript/base',
      ],
      parser: '@typescript-eslint/parser',
      parserOptions: {
        ecmaVersion: 2020,
        sourceType: 'module',
      },
    },
  ]
}

To add another style guide, you need to install a set of rules for TypeScript, create a new block of rules in "overrides" with the rules of the style guide and specify the parser necessary for their work.

Customization rules


If you want to disable or redefine some rules in the style of the guide, you can do this in the "rules" property:

module.exports = {
  ...,
  overrides: [
    ...,
    {
      files: ['*.ts'],
      extends: [
        'airbnb-typescript/base',
      ],
      parser: '@typescript-eslint/parser',
      parserOptions: {
        ecmaVersion: 2020,
        sourceType: 'module',
      },
      //  
      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,
          },
        ],
      },
    },
  ]
}


Configure Prettier


To add Prettier to our configuration, we need to install Prettier itself, a plugin with Prettier rules, as well as a config that will disable all rules that may conflict with Prettier:

npm i prettier eslint-config-prettier eslint-plugin-prettier --save-dev

In the "overrides" in the block with the rules of files with the extension * .ts in the property "extends" at the very bottom add the rules and Prettier settings:

module.exports = {
  ...,
  overrides: [
    ...,
    {
      files: ['*.ts'],
      extends: [
        //   AirBnB
	'airbnb-typescript/base',
	//   Prettier
	'prettier/@typescript-eslint',
	'plugin:prettier/recommended',
      ],
      ...,
    },
  ]
}

The configuration for Prettier must always be at the very bottom of the list to overwrite any rules that may conflict with Prettier.

`prettier / @ typescript-eslint` disables the` @ typescript-eslint` rules, which may conflict with Prettier, and `plugin: prettier / recommended` does three things:

  • includes eslint-plugin-prettier,
  • prints prettier / prettier rule errors to the console as "error",
  • Adds Prettier eslint-config-prettier formatting rules.

Config for Prettier:


Prettier can format code without any settings, but to match the AirBnB style guide, you need to add some settings. Create the .prettierrc.js file in the root of the application:

module.exports = {
  trailingComma: "all",
  tabWidth: 2,
  semi: true,
  singleQuote: true,
  bracketSpacing: true,
  printWidth: 100
};

These settings will be used by both ESLint and Prettier if you use it to format files in VS Code or with the command:

prettier "--write ."

Configure VS Code


VS Code can highlight and correct ESLint found errors when saving errors. To do this, download the ESLint plugin for VS Code and create a file inside the project with the settings for the workspace .vscode / settings.json:

  "eslint.validate": [ "javascript", "typescript", "html"],

  "eslint.options": {
    "extensions": [".js", ".ts", "html"]
  },

  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
  },

Here we configure ESLint to emphasize and correct errors when saving files with the .js, .ts and .html extensions.

And to format a document using the “shift + option + F” or “shift + alt + F” key combinations, download the Prettier plugin for VS Code and set it as the default formatter.

Setting up git hooks


Git hooks are scripts that Git invokes on certain events: commit, push, recieve.

With the help of them, we can start linting the code when creating a commit so that fewer errors get into the request pool. For more convenient work with Git hooks, install Husky , and to check only the code that is added to the commit (this is useful in large projects where linting takes a lot of time) lint-staged :

npm i husky lint-staged --save-dev

Add the settings for these plugins to package.json:

"scripts": {
  ...
},
"husky": {
  "hooks": {
    "pre-commit": "lint-staged --relative"
  }
},
"lint-staged": {
  "*.{js,ts}": [
     "eslint --fix"
  ]
},

lint-staged passes an array of modified files to the called command. The ng lint command does not know how to accept an array of files, and to use it, you need to write an additional script handler. Or you can just call ESLint, as in this example. This solution can be used for precommits, and you can run ng lint to lint the entire project, for example, in the CI pipeline.

findings


In future versions of Angular, ESLint with basic rules will be out of the box. Now the ESLint configuration process requires some additional steps, ESLint has no equivalents for some rules from TSLint, and Angular ESLint is still in alpha version. Therefore, to switch to ESLint now or not is up to you.

However, the guide code, additional rules, Prettier, Husky and lint-staged you will have to configure yourself. I hope this article has helped you figure out how all these things work together.

Configuring linters may seem like a trivial task, but it includes several important organizational issues: choosing style guides, synchronizing various solutions with each other.

But the time spent on configuring the linter in the future will significantly save you time discussing the style and formatting of the code in the code review process, reduce the number of errors that fall into the request pool, and ensure the consistency of the code base.

An example implementation can be found on Github .

If you find a mistake in the config, or you have add-ons - write!

References



All Articles