Ten minutes to understand eslint configuration & write custom eslint rules

Posted by danf_1979 on Tue, 06 Aug 2019 13:09:25 +0200

Introduction to eslint

ESLint is an open source JavaScript code checking tool created by Nicholas C. Zakas in June 2013. Code checking is a static analysis, often used to find problematic patterns or code, and does not depend on the specific coding style. For most programming languages, there will be code checking. Generally speaking, compilers will have built-in checking tools.

JavaScript is a dynamic, weakly typed language, which is prone to errors in development. Because there is no compiler, it is often necessary to debug constantly during execution in order to find JavaScript code errors. Programmers like ESLint can find problems in coding rather than in execution.

ESLint was designed to allow programmers to create their own detection rules. All rules of ESLint are designed to be pluggable. To make it easy for people to use, ESLint has built-in rules. Of course, you can customize rules in the process of using them. All rules are disabled by default.

ESLint is written using Node.js.

eslint configuration

collocation method
  1. Configuration files of. eslintrc. * are generally used to configure the project. If placed in the root directory of the project, it will affect the entire project. If the. eslintrc file is also included in the subdirectory of the project, the configuration in the root directory will be ignored and the configuration in the subdirectory will be adopted directly. This makes it possible to apply different checking rules in different directory ranges, which is more flexible. ESLint looks up. eslintrc. * files step by step. When it finds. eslintrc. * files with "root": true configuration item, it stops looking up.
  2. Configure the eslintConfig field in the package.json file.
Specific configuration rules

Taking the use of the project as an example, this paper briefly introduces the specific configuration and function of eslint.

module.exports = {
    parser: 'babel-eslint', // Parser specifies the parser, default is espree. Babel-eslink is a wrapper for Babel parser, which enables Babel parser to work in coordination with ESLint.
    parserOptions: {
        sourceType: 'module', // Set it to "script" (default) or "module" (ES6).
        ecmaFeatures: { // This is an object that represents the additional language features you want to use:
            jsx: true // Enabling JSX
        }
    },
    extends: ['eslint:recommended'], // Using the rules recommended by eslint as the basic configuration, you can override them in rules
    plugins: ['html', 'vue', 'prettier', 'import'], // Vue is the abbreviation of eslint-plugin-vue. The function of this plug-in is to enable eslint to recognize script code in. vue.
    rules: { // 0 or off means that the rule is closed and the error is ignored; 1 or warn means that if there is an error, a warning will be given (which will not lead to program exit); 2 or error means that if there is an error, an error will be reported (which will lead to program exit, exit code is 1).
        'no-console': 'off',
        'prefer-const': 'error',
        'prettier/prettier': 'warn',
        'prefer-arrow-callback': 'warn',
        'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
    },
    globals: { // Allow global variables to be used in code
        location: true,
        setTimeout: true
    }
};

Specific configuration documents: http://eslint.cn/docs/user-guide/configuring
Specific eslint:recommended supported rules: https://cn.eslint.org/docs/rules/

In addition to recommendation rules, extends can also introduce other customized rules in the form of files, and then use rules to define individual rules on the basis of these customized rules, so as to override the rules introduced in extends.

{
    "extends": [
        "./node_modules/coding-standard/eslintDefaults.js",
        // Override eslintDefaults.js
        "./node_modules/coding-standard/.eslintrc-es6",
        // Override .eslintrc-es6
        "./node_modules/coding-standard/.eslintrc-jsx",
    ],
    "rules": {
        // Override any settings from the "parent" configuration
        "eqeqeq": "warn"
    }
}

In addition to specifying rules in the configuration file, rules can also be specified in the code. Rules that are configured with annotations in the code file override the rules in the configuration file, that is, higher priority. Usually we use `eslint-disable-next-line'.
`

Ignore checks

You can create. eslintignore files in the project directory and configure them to ignore checking for which files. It's important to note that whether you configure it in. eslintignore or not, eslint defaults to ignoring the check for / node_modules/**. You can also configure it in the eslintIgnore field of the package.json file.

Principle of eslint checking

To achieve static analysis, you need to build a precompiled phase to parse the code.

First, let's look at the three stages in which most compilers work:

Parsing: Parsing untreated code into more abstract expressions, usually an abstract syntax tree, or AST.
Conversion: By modifying the parsed code expression, it is converted into a new format that meets expectations.
Code generation: Generate the converted expression into a new object code.

For eslint, rule validation occurs when JavaScript code is parsed into AST and traversed through AST. Eslint uses Espree to generate AST. Specific generation methods are as follows: Here.
We can use it. AST explorer To see the AST generated after the code has been parsed.

Principle of rules

First, let's look at the writing of rules in the eslink source code. The rules source code in eslint exists under lib/rules. Each rule is a node module, which uses module.exports to export a meta object and a create function.

module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "disallow unnecessary semicolons",
            category: "Possible Errors",
            recommended: true,
            url: "https://eslint.org/docs/rules/no-extra-semi"
        },
        fixable: "code",
        schema: [] // no options
    },
    create: function(context) {
        return {
            // callback functions
        };
    }
};

meta represents the metadata of the rule, such as the category of the rule, the document, the acceptable parameter schema, and so on.

create returns an object, which defines some methods that need to be executed to access the corresponding node in AST traversal, and so on. The function accepts a context object as a parameter, which contains such methods as context.report(), which can report errors or warnings, context.getSourceCode(), which can obtain source code, and so on. It can simplify the writing of rules.

function checkLastSegment (node) {
    // report problem for function if last code path segment is reachable
}

module.exports = {
    meta: { ... },
    create: function(context) {
        // declare the state of the rule
        return {
            ReturnStatement: function(node) {
                // Execute when AST traverses from top to bottom to Return Statement node
            },
            // Execute when AST traverses from bottom to top to function expression node:
            "FunctionExpression:exit": checkLastSegment,
            "ArrowFunctionExpression:exit": checkLastSegment,
            onCodePathStart: function (codePath, node) {
                // Execution at the beginning of the analysis code path
            },
            onCodePathEnd: function(codePath, node) {
                // Execution at the end of analysis code path
            }
        };
    }
};

In the process of traversing the AST, the nodes will pass through two times in the order of "top-down" and "bottom-up". By default, the selector will execute the corresponding access function in the downlink process. If it needs to execute in the upstream process, it needs to add: exit.

Detailed principles are described in official documents. Here.
Detailed code path analysis in Here.

How to write a rule

Knowing the principles of rules, you can then customize a rule. Each rule requires three files named after the rule, namely:

  • In the lib/rules directory: a source file (for example, no-extra-semi.js)
  • In the tests/lib/rules directory: a test file (for example, no-extra-semi.js)
  • In the docs/rules directory: a markdown document file (for example, no-extra-semi)

Next, let's write a simple rule, such as forbidding block-level annotations. When block-level annotations are used in code, eslint will report errors.

rules file:

// lib/rules/no-block-comments.js
module.exports = {
  meta: {
    docs: {
      description: 'Block-level annotations are prohibited',
      category: 'Stylistic Issues',
      recommended: true
    }
  },

  create (context) {
    // Get the source code
    const sourceCode = context.getSourceCode()

    return {
      Program () {
        // Get all the comments in the source code
        const comments = sourceCode.getAllComments()

        const blockComments = comments.filter(({ type }) => type === 'Block')

        blockComments.length && context.report({
          node: node,
          message: 'No block comments'
        })
      }
    }
  }
}

rules test file:

// tests/lib/rules/no-block-comments.js
const RuleTester = require("eslint").RuleTester;
const rule = require("../../../lib/rules/no-block-comments");

const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } }); // You do have to tell eslint what js you're using

ruleTester.run("no-block-comments", rule, {
    valid: ["var a = 1; console.log(a)"],
    invalid: [
        {
            code: "var a = 1; /* block comments */ console.log(a)",
            errors: [
                {
                    messageId: "blockComments",
                    line: 1,
                    nodeType: "Block"
                }
            ]
        }
    ]
});

Official website working with rules Detailed instructions on how to write a rules are included in the document.

How to use custom rules

Written rules need to be published on npm, as an eslint-plugin, downloaded from the project before they can be used. The NPM of the code in the example Ad locum.

Configuration in the project:

// .eslintrc.js
module.exports = {
    ...
    "plugins": [
        "eslint-plugin-no-block-comments"
        // The npm package name of your publish can be omitted from eslint-plugin
      ],
    "rules": { // Enabled rules and their respective error levels
        'no-console': 'off',
        "no-block-comments/no-block-comments": 2 // Reference to no-block-comments rules in no-block-comments plug-ins
    }
};

The code can then be checked. For example, the code I want to check is as follows:

// src/index.js
const a = 1;
/*
    Here are block-level annotations
*/
console.log(a);

If you execute eslint src from the command line, you can see the result of the error.

Reference article:

ESlink official website

Discussion on the Working Principle of ESLint

Developing eslint rules

Topics: Javascript Vue npm JSON