Each coder has more or less different code writing habits, so it is very difficult to unify code styles as long as there are more people developing the project. Fortunately, there are plenty of tools to help us format code, and Eslint is one of them.

This article provides a detailed introduction to the ESLint tool, including the following:

  • How do I configure ESLint
  • How to use ESLint and Prettier together
  • How do I format Typescript code

Initialize the project

// create folder mkdir eslint-learn && CD eslint-learn // initialize NPM project yarn init -y // install eslint yarn add eslint --devCopy the code

Automatically generate configuration files

ESLint runtime requires a configuration file. This configuration file can be generated automatically or configured manually. Let’s start with an auto-generated example by executing the following command:

npx eslint --init
Copy the code

You will then be given a variety of options to choose from, using the following image:

At the end, you will be asked if you want to install a set of dependencies. If you choose “Yes”, the dependencies will be installed as NPM. You can also install the above dependencies through YARN. Mainly the following dependencies:

  • @typescript-eslint/eslint-plugin
  • eslint-config-standard
  • eslint
  • eslint-plugin-import
  • eslint-plugin-node
  • slint-plugin-promise
  • @typescript-eslint/parser@latest
@ typescript - eslint/eslint - plugin @ latest eslint - config - standard @ latest eslint @ ^ 7.12.1 eslint plugin - import @ ^ 2.22.1 Eslint plugin - node @ ^ 11.1.0 eslint plugin - promise @ ^ 2 | | ^ 5.0.0 @ typescript - eslint/parser @ latestCopy the code

Finally, a configuration file is generated in the project root directory:

{
    "env": {
        "commonjs": true,
        "es2021": true,
        "node": true
    },
    "extends": [
        "standard"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaVersion": 13
    },
    "plugins": [
        "@typescript-eslint"
    ],
    "rules": {
    }
}
Copy the code

If you can fully understand the configuration file above, then you already understand the configuration of ESLint, so there is no need to read further; If you don’t understand the above configuration information, this article will certainly make sense to you.

Step By Step

The preparatory work

  • Create a SRC folder in the root directory and create one in the folderindex.jsFile, this is where we write the code.

  • inpackage.jsonFile to create a script"lint":"eslint src/index.js"forindex.jsThe file executes the eslint command.
"scripts": {
  "lint": "eslint src/index.js"
}
Copy the code
  • empty.eslintrcFile, we add our own step by step configuration information.

.eslintrc can have suffixes such as.js or.json. I personally don’t like to add suffixes, because if it’s a JS file, you need multiple export characters, and if it’s a JSON file, you can’t add comments.

Empty configuration execution

Add the following code to the SRC /index.js file:

function foo(a,b) {
  return a+b
}
var a1 = "a1"
var b1 = 'b1'
var res = foo(a1,b1)
console.log(res)
Copy the code

If there is no output from the console after NPM run Lint is executed, everything is fine.

A quick note on how ESLint works: ESLint will split your code (AST) using a lexical analyzer, then combine it according to certain rules, then compare the new combined code to your code, and if there is any discrepancy, it will prompt you with an error message on the console.

This rule is important, and our example does not have a configuration rule, so there is no error. Next we configure some rules.

Rules configuration

Configure two rules in the.eslintrc file:

  • Semi: Do you want a semicolon
  • Quotes: Single or double quotes

Rule configuration is in the form of key-value. Key is a rule. The first value of value indicates the limit of the rule.

  • “Off” or 0 – Turns off the rule
  • “Warn” or 1 – Enable the rule. Non-compliance will be warned
  • “Error” or 2 – Enable the rule and report an error if it does not conform
// .eslintrc { "rules": {/** * key is the first value of the rule * value, which indicates the limit of the rule. The value ranges from * "off" or 0 - close the rule "warn" or 1 - enable the rule, non-compliance will warn" error" or 2 - enable the rule, Do not conform to the direct error * / "semi" : [" error ", "always"], "quotes" : [" error ", "double"]}}Copy the code

NPM run Lint the console outputs the following error message:

2:13 error Missing semicolon semi 4:14 error Missing semicolon semi 5:10 error Strings must use doublequote quotes 5:14 Error Missing Semicolon semi 6:21 Error Missing Semicolon semi 7:17 Error Missing Semicolon semi disk 6 problems (6 errors,  0 warnings) 6 errors and 0 warnings potentially fixable with the `--fix` option.Copy the code

Based on the above information, we can see what rules are not satisfied in which rows and columns.

Extends the configuration

Rules can point out code style issues, but it’s dehumanizing to have so many rules written by hand. Therefore, there are some recommended rules that can be used directly. The official one is ESLint :recommended, and we can extend our configuration with the extends property. For its specific rules, please refer to eslint.org/docs/rules/.

In addition to ESLint’s own rules, there are a number of open source configuration rules, and any search on GitHub that starts with esLint-config – is basically an extendable rule. For example, eslint-config-standard, eslint-config-prettier, etc. If you need to configure multiple rule libraries, you can write extends as an array.

Then add the extended information to the.eslintrc file:

// .eslintrc { "extends": "eslint:recommended", "rules": {/** * key is the first value of the rule * value, which indicates the limit of the rule. The value ranges from * "off" or 0 - close the rule "warn" or 1 - enable the rule, non-compliance will warn" error" or 2 - enable the rule, Do not conform to the direct error * / "semi" : [" error ", "always"], "quotes" : [" error ", "double"]}}Copy the code

NPM run Lint the console outputs the following error message:

2:13 error Missing semicolon semi 4:14 error Missing semicolon semi 5:10 error Strings must use doublequote quotes 5:14 error Missing semicolon semi 6:21 error Missing semicolon semi 7:1 error 'console' is not defined no-undef 7:17 error Missing Semicolon semi disk 7 problems (7 errors, 0 warnings) 6 errors and 0 warnings potentially fixable with the `--fix` option.Copy the code

Env Environment Configuration

There is a no-undef error message indicating that console is not defined. This is because we don’t specify the environment in which the code runs, so ESLint doesn’t know if console is built-in to the environment. At this point we need to configure the runtime environment. Configure the corresponding env attribute as follows, indicating that the code may run in the browser and Node environment. Also, in the interest of keeping the console clean, let’s leave the semicolon and quote rules off for now.

{
    "extends": "eslint:recommended",
    "env": {
        "browser": true,
        "node": true
    },
    "rules": {
    }
}
Copy the code

At this point, when we perform ESLint again, there is no error message on the console, meaning console can be used.

ES 6 syntax support

Now let’s go back and look at the code being tested. It’s all ES5 code. Now let’s change this to ES6 code.

function foo(a,b) {
  return a+b
}
let a1 = "a1"
let b1 = 'b1'
let res = foo(a1,b1)
console.log(res)

let p1 = new Set();
console.log(p1)
Copy the code

After performing the code check, the console outputs the following error message: Parsing error.

Error Parsing error: Unexpected token A1 * 1 problem (1 error, 0 warnings)Copy the code

This is because ESLint only compiles ES5 code by default, not ES6 code.

ESLint allows you to specify the JavaScript language options you want to support. By default, ESLint expects ECMAScript 5 syntax.

To compile ES6 code, you need to add the configuration of the parserOptions property.

Modify the configuration file as follows:

{ "extends": "eslint:recommended", "env": { "browser": true, "node": true }, "parserOptions": { "ecmaVersion": "Latest" // supports ES6 syntax}, "rules": {}}Copy the code

After re-checking the code, the console outputs the following error message: Set is not defined.

  9:14  error  'Set' is not defined  no-undef

✖ 1 problem (1 error, 0 warnings)
Copy the code

Set is new to ES6, but it is one of the new built-in objects. For such built-in objects, ESLint also requires special configuration, adding “ES6 “: true to the environment property.

{"extends": "eslint:recommended", // extension "env": {"browser": true, "node": true, "es6": Set}, "parserOptions": {"ecmaVersion": "latest" // Supports ES6 syntax}, "rules": {// Specific rules}}Copy the code

NPM run Lint returns no error message on the console.

Globals configuration

Globals is a less common configuration item that is typically used in framework development. Frameworks are often designed to mount variables on the Window, which can then be used in plug-ins or projects. We now assume that var1 and var2 are already mounted on the window, so we can use them directly in our project, as follows:

function foo(a,b) {
  return a+b
}
let a1 = "a1"
let b1 = 'b1'
let res = foo(a1,b1)
console.log(res)

let p1 = new Set();
console.log(p1)

console.log(var1);
console.log(var2);
Copy the code

If we perform the test, we will get the following error message:

  12:13  error  'var1' is not defined  no-undef
  13:13  error  'var2' is not defined  no-undef

✖ 2 problems (2 errors, 0 warnings)
Copy the code

If we don’t want an error message, we can configure the globals attribute as follows:

{"extends": "eslint:recommended", // extension "env": {"browser": true, "node": true, "es6": Set}, "parserOptions": {"ecmaVersion": "latest" // Supports ES6 syntax}, "globals": {"var1": "Writable", "var2" : "readonly"}, "rules" : {specific rules / /}}Copy the code

This is equivalent to declaring var1 and var2 globally, where writable indicates that the variable can be reassigned; Readonly means that the variable is read-only. NPM run Lint returns no error message on the console.

Now that we’ve covered the most basic configuration items in ESLint, there are only two remaining configuration items: Parser and plugins, compared to the configuration files generated automatically at the beginning of this article.

Go On

Plugins configuration

ESLint supports third-party plugins. Of course, you need to install the plug-in before you can use it. It’s also easy to configure a plug-in, similar to the extends configuration. Plugins usually start with eslint-plugin-.

Two plugins are declared as plugin1 and plugin2, where the eslint-plugin- prefix can be omitted. This declaration essentially tells ESLint to automatically load the corresponding plugin at runtime: require(‘ eslint-plugin-pluginname ‘).

{
    "plugins": [
        "plugin1",
        "eslint-plugin-plugin2"
    ]
}
Copy the code

Next, let’s use esLint + Prettier as an example of how plug-ins can be used.

Prettier first, prettier: Prettier is a formatting tool that opinionated code and can itself be used to format code. Because it has defined rules, it can also be used in conjunction with ESLint as rules to examine code.

In addition, it has the corresponding vscode plug-in, after installation, you can write code while formatting code style, very convenient.

The main plugin used here is eslint-plugin-prettier. There is also the rule for prettier, so prettier needs to be installed.

yarn add eslint-plugin-prettier prettier -D
Copy the code

After the installation is complete, we need to configure the corresponding plug-in and enable the corresponding configuration items:

{// "extends": "eslint:recommended", // Extension "env": {"browser": true, "node": true, "es6": Set}, "parserOptions": {"ecmaVersion": "latest" // support ES6 syntax}, "plugins": ["prettier"], "globals": {"var1": "writable", "var2": "readonly"}, "rules": {"prettier/prettier": "Error" // Enable prettier rule}}Copy the code

NPM run Lint: NPM run Lint:

1:16 Error Insert '·' prettier/prettier 2:11 Error Replace '+b' with '·+·b; ` prettier/prettier 4:14 error Insert `; ` prettier/prettier 5:10 error Replace `'b1'` with `"b1"; 'prettier/prettier 6:18 error Replace' b1) 'with' ·b1); ` prettier/prettier 7:17 error Insert `; ` prettier/prettier 10:16 error Insert `; 'prettier/prettier 13:19 Error Insert' ⏎ 'prettier/prettier * 8 problems (8 errors, 0 warnings) 8 errors and 0 warnings potentially fixable with the `--fix` option.Copy the code

When Prettier was used, the console reported more errors than it normally did.

If you think this completes the configuration of Prettier, you’re thinking too simple. Prettier vs. esLint

In short, Prettier just formatting, but not code quality control, which ESLint would do.

Therefore, esLint’s rules would have to be added to the above configuration, which led to a new problem: esLint and Prettier had rules that conflicted and duplicated. This can lead to inconsistency in style formatting. So that’s what happenseslint-config-prettierEslint, where prettier conflicts with esLint, turns off the rule where prettier conflicts with esLint. Source code:link.

Install eslint-config-prettier and modify the configuration file:

{"extends": ["eslint:recommended","prettier"] // Extension "env": {"browser": true, "node": true, "es6": Set}, "parserOptions": {"ecmaVersion": "latest" // support ES6 syntax}, "plugins": ["prettier"], "globals": {"var1": "writable", "var2": "readonly"}, "rules": {"prettier/prettier": "Error" // Enable prettier rule}}Copy the code

Since eslint-plugin-prettier and eslint-config-prettier usually go together, there is a more concise way of writing eslint-plugin-prettier:

{" extends ": [" eslint: it", "plugin: prettier/it"] / / extension "env" : {" browser ": true," node ": true," es6 ": Set}, "parserOptions": {"ecmaVersion": "latest" // Supports ES6 syntax}, "globals": {"var1": "Writable", "var2" : "readonly"}, "rules" : {specific rules / /}}Copy the code

And the configuration items “plugin: prettier/it”, actually is extended to:

{   
  "extends": ["prettier"],   
  "plugins": ["prettier"],   
  "rules": {     
    "prettier/prettier": "error",     
    "arrow-body-style": "off",     
    "prefer-arrow-callback": "off"   
  } 
}
Copy the code

See arrow-body-style and prefer-arrow-callback issue for details on why there are two more rules.

Here we have esLint + prettier, which illustrates the use of plugin.

Parser configuration

As mentioned at the beginning of this article, ESLint will first split the code you write using a lexical analyzer (AST). And that lexical analyzer is parser. Different parsers produce different conversion results. The default parser for ESLint is Espree. The user can specify the desired parser using the Parser attribute. There are three common syntax parsers:

  • Esprima
  • @babel/eslint-parser – A layer of encapsulation on top of the Babel compiler to work with ESLint
  • @typescript-eslint/parser – the compiler used to convert typescript syntax

For general javascript projects, we can keep the default, or use @babel/eslint-parser; For typescript projects, @typescript-esLint /parser is used.

In addition, there are TS rules for TS projects, and we usually use the @typescript-eslint/eslint-plugin plugin.

First, we install the corresponding dependency:

yarn add @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript -D
Copy the code

Modify the corresponding configuration file:

{ "extends": ["plugin:@typescript-eslint/recommended","plugin:prettier/recommended"], "env": { "browser": true, "node": // Support global ES6 syntax Set}, "parser": "@typescript-eslint/parser", "parserOptions": {"ecmaVersion": "Latest ", // support ES6 syntax "sourceType": "module"}, "rules": {}}Copy the code

In the meantime, change the SRC /index.js file to SRC /index.ts:

function foo(a:string,b:string) {
  return a+b
}
let a1 = "a1"
let b1 = 'b1'
let res = foo(a1,b1)
console.log(res)
Copy the code

Finally, run “lint”: “eslint SRC /index.ts” and the console displays the following error message:

1:16 Error Replace 'string, B:' with 'string,·b:·' prettier/prettier 2:11 Error Replace '+b' with '·+·b; ` prettier/prettier 4:5 error 'a1' is never reassigned. Use 'const' instead prefer-const 4:14 error Insert `; ` prettier/prettier 5:5 error 'b1' is never reassigned. Use 'const' instead prefer-const 5:10 error Replace `'b1'` with `"b1"; ` prettier/prettier 6:5 error 'res' is never reassigned. Use 'const' instead prefer-const 6:18 error Replace `b1)` with ` b1); ` prettier/prettier 7:17 error Insert `; ` prettier/prettierCopy the code

Check the Typescript file successfully!

One More

So far, we’ve covered pretty much everything in ESLint configuration files. We can also get the corresponding information on the console by executing detection commands, but it would be a lot of work if we had to manually change every error. Fortunately, ESLint has an automatic fix, just add –fix to the end of the check command.

Json we add a new script to package.json: “lint:fix”: “eslint SRC /index.ts –fix” and then execute NPM run Lint :fix to recheck and fix automatically. At this point, the console does not report any errors. Looking at the source file, you find that it has been automatically formatted: Spaces, semicolons, etc.

function foo(a: string, b: string) {
  return a + b;
}
const a1 = "a1";
const b1 = "b1";
const res = foo(a1, b1);
console.log(res);
Copy the code

The last

The above implementation of ESLint formatting code requires manual execution of the NPM command, which means that there will always be people who, intentionally or unintentionally, commit untested code to the repository. Hence the Husky + Lint-stage approach. Husky can provide hook functions for git commands, so we can check ESLint before committing code. However, it would be too time-consuming to fully test our code each time, so we can use the lint-stage library to test only the files that are involved in this commit.

First install the corresponding dependency:

yarn add husky lint-staged -D
Copy the code

Configure the package.json file:

"husky":{
  "hooks":{
  "pre-commit": "lint-staged"
  }
},
"lint-staged":{
  "src/*/**.{ts,js}":"eslint --fix"
}
Copy the code

The above configuration is only valid for HusKY V4. The new configuration method is required in V7.

First, configure package.json so that husky Install is automatically executed after the application installs dependencies.

"scripts": {
  "prepare": "husky install"
}
Copy the code

At this point, a.husky folder is generated under the project, where the user can configure the relevant Git hooks.

Next, add the pre-commit file and write the configuration, using the following command:

npx husky add .husky/pre-commit "npx lint-staged --allow-empty $1"
Copy the code

The.husky/pre-commit file is generated:

#! /bin/sh . "$(dirname "$0")/_/husky.sh" npx lint-staged --allow-empty $1Copy the code

This is associated with Lint-staged configuration to test files when code is submitted. For more information: Husky.

At this point, the full end!