First there is a package called ESLint, from which all your Lint validations will come – official documentation

Related to the prior knowledge points

1. AST – Abstract syntax tree

AST is short for Abstract Syntax Tree

The AST abstracts code into a tree-like data structure for subsequent analysis and inspection

Ps: Here I have a question, why do you want to convert into a tree structure? Is it because it costs the least or because it is the most suitable for analysis and testing? If so, what are the advantages?

The code is parsed as an AST

Astexplorer.net is a tool site that allows you to see what the code looks like when it’s turned into an AST

As shown below, the selected part (printTips) on the left is highlighted in real time on the right

AST selector

The circled sections in the figure below are called AST selectors.

What it does: Uses code to select specific snippets of code through a selector, and then statically analyzes the code.

The AST has many selectors, and ESLint officially lists all types of selectors: Estree

The process of understanding will use the selector, now understand can be.

How ESLint works

1. Parse the code into AST

Eslint uses the Javascript parser espree by default to parse JS code into an AST

PS: parser: a tool that parses code into an AST. ES6 vUE React has developed parsers so THAT ESLint can parse them and esLint has a front end.

This is also the parser option to be configured in the ESLint configuration file. Since Babel is now indispensable in front-end projects, the base parser commonly used is: babel-esLint.

2. Deeply traverse the AST to monitor the matching process

After the first step,ESlint takes the AST results and iterates through each selector twice in ‘top down’ and then ‘bottom up’ order.

3. Trigger the rule callback for the listener selector

During deep traversal, each rule that takes effect listens on one or more of the selectors, and whenever a selector is matched, the rule that listens on that selector triggers the corresponding callback.

PS: A piece of code may contain the same selector multiple times, and the selector’s hook will fire multiple times

4. Specific testing details…

# # Eslint use

The use of ESLint consists of two parts: * Configuring lint rules through configuration files; * * Run Lint on the command line to find non-conformities to the specification (of course some non-conformities can also be fixed)

ESLint also works well with the editor plug-in, and in fact, many people may be more used to it.

  1. Use eslint –init to generate the.eslintrc.js configuration file as prompted
  2. Use ESlint — Lint to perform checks

The configuration file

  1. An example of the eslint configuration file.eslintrc.js:

    • Env – Pre-define the environment variables needed in the environment, available parameters es6 / node/browser, etc

      Browser – Adds all browser environment variables, such as Window

      Node – Adds all nodeJS runtime environment variables, such as global Process, etc

      Es6 — Enable all ECMAScript 6 features except modules (this automatically sets the ecmaVersion parser option to 6)

      Commonjs – CommonJS global variables and CommonJS scopes (for Browserify/WebPack packaged browser-only code to run

      Official Documentation Reference

    • Extends – Specifies the configuration of an extension that supports recursive extension, overwriting and aggregation of rules.

      Optional attribute values:

      1. String to specify configuration – (path to configuration file, name of shareable configuration (Airbnb), ESLint :recommended, or ESLint :all)

      2. Array of strings – The configuration of each item in the array inherits the configuration written in the front – for example

        Extends: [‘ esLint :recommended ‘, ‘ESLint :all’], // A configuration of ESLint :all inherits a configuration of ESLint :recommended

    Eslint :recommended – Enables core rules of ESLint, which are marked ✅ on the rules page and updated with the main VERSION of ESLint.

    Eslint :all – Starts all of the core rules for the currently installed ESLint – not recommended for projects as these rules can be modified in both large and small versions.

    Airbnb – The community is said to use the best code Lint rule

    The official link portal

    • Plugins – This is where we configure the Lint plugin we need

    • Parser-parser option – parser, which tells ESLint which parser you want to use to parse code. It supports the following options:

    Espree – This is the default parser used by ESLint – but once we use Babel, we need to use babel-esLint

Babel-eslint – This dependency package allows you to use esLint syntax checks while using experimental features. Es6, ES7, etc.

@typescript-eslint/parser – A parser for typescript syntax, deprecated by tsLint and no longer maintained by it. This mainly supports typescript syntax such as type assertion const a! “: number” or “a as number” when used.

  • ParserOptions – This option must be configured when parser is configured to babel-esLint. The optional value (.

    EcmaVersion – Default 5, 3, 5, 6, 7, 8, 9, 10 can be specified to specify which ECMAScript version of the syntax to use. You can also set JS standards based on year, such as 2015(ECMA 6)

    SourceType – This field is configured as module if your code is written in an ECMAScript module, script otherwise (default)

    EcmaFeatures – This object indicates additional language features you want to use

    GlobalReturn: Allows globally scoped return statements

    ImpliedStrict: Indicates that the strict mode is enabled globally

    JSX: JSX is enabled

    )

    • Rules – Custom rule Configuration – has the highest priority and can override the rules we enable in the extends configuration
    • Settings – This field defines data that can be shared across all plug-ins. This allows each rule execution to access the data defined within it
module.exports = {
  env: 'es6',
  parser: '@typescript-eslint/parser',
  parserOptions: {
    sourceType: 'module'
  },
  extends: ['eslint:recommended`'.'eslint:all'].// ESLint: A configuration for all inherits esLint :recommended configuration
  rules: {
    'no-unused-vars': [
      'error'.// we are only using this rule to check for unused arguments since TS
      // catches unused variables but not args.
      { varsIgnorePattern: '*', args: 'after-used', argsIgnorePattern: '^ _'}].// most of the codebase are expected to be env agnostic
    'no-restricted-globals': ['error'. DOMGlobals, ... NodeGlobals],// since we target ES2015 for baseline support, we need to forbid object
    // rest spread usage (both assign and destructure)
    'no-restricted-syntax': [
      'error'.'ObjectExpression > SpreadElement'.'ObjectPattern > RestElement'
    ]
  },
  settings: {
    'import/resolver': { // This config is used by eslint-import-resolver-webpack
      webpack: {
        config: './webpack/webpack-common-config.js'
      }
    },
  },
  overrides: [
    // tests, no restrictions (runs in Node / jest with jsdom)
    {
      files: ['**/__tests__/**'.'test-dts/**'],
      rules: {
        'no-restricted-globals': 'off'.'no-restricted-syntax': 'off'}},// shared, may be used in any env
    {
      files: ['packages/shared/**'],
      rules: {
        'no-restricted-globals': 'off'}},// Packages targeting DOM
    {
      files: ['packages/{vue,runtime-dom}/**'],
      rules: {
        'no-restricted-globals': ['error'. NodeGlobals] } },// Packages targeting Node
    {
      files: ['packages/{compiler-sfc,compiler-ssr,server-renderer}/**'],
      rules: {
        'no-restricted-globals': ['error'. DOMGlobals],'no-restricted-syntax': 'off'}},// Private package, browser only + no syntax restrictions
    {
      files: ['packages/template-explorer/**'],
      rules: {
        'no-restricted-globals': ['error'. NodeGlobals],'no-restricted-syntax': 'off'}}}]Copy the code

Description of personal common configuration items:

Common configuration options for esLint configuration files Note:

  1. Parser – First you need to configure a parser, which is a must in current front-end environments. Otherwise you use the default javascript parser -espree, which we generally use with babel-esLint. Sometimes parsing options are also parserOptions that need to be configured

  2. Env – Specifies the runtime environment in which the script is run. You can specify more than one at a time

  3. Extends – Specifies which set of ESLint rules we enable. Generally, standard rules are enabled. Here you can write multiple rule enable items that override the previous ones, or introduce our own custom rules (if so, we can introduce our own custom rules, or configure our own custom plugins here)

  4. Plugins – This is where third-party plugins are configured. Plugins need to install the corresponding NPM package. Multiple plugins can be written in the form of string arrays with the prefix eslint-plugin- omitted

  5. Rules – The enabled rules have the highest priority. You can configure esLint’s own rules or rules imported from third-party plug-ins (ensure that the NPM package of the third-party plug-ins has been installed).

  6. Overrides & Files – Disables the ESLint rule for a specific part of the file in the project, which can be configured as follows:

    overrides: [
     // tests, no restrictions (runs in Node / jest with jsdom)
     {
       files: ['**/__tests__/**', 'test-dts/**'], rules: { 'no-restricted-globals': 'off', 'no-restricted-syntax': 'off' } }, // shared, may be used in any env { files: ['packages/shared/**'], rules: { 'no-restricted-globals': 'off' } }, // Packages targeting DOM { files: ['packages/{vue,runtime-dom}/**'], rules: { 'no-restricted-globals': ['error', ...NodeGlobals] } }, // Packages targeting Node { files: ['packages/{compiler-sfc,compiler-ssr,server-renderer}/**'], rules: { 'no-restricted-globals': ['error', ...DOMGlobals], 'no-restricted-syntax': 'off' } }, // Private package, browser only + no syntax restrictions { files: ['packages/template-explorer/**'], rules: { 'no-restricted-globals': ['error', ...NodeGlobals], 'no-restricted-syntax': 'off' } } ]Copy the code

Write an Eslint plug-in

Since you’re writing a plugin, you need to be clear about what the plugin is supposed to do. The ESLint plugin can implement a validation rule.

#### what the plug-in is intended to achieveCopy the code
  1. The second argument to disallow setTimout is a number.
  2. Advancements – Rule Enhancement – Magic variables are not allowed in projects.

Development preparation

  1. To make it easier for developers to develop plug-ins, ESLint officially provides the Yeoman template (Generator-ESLint), which is a scaffolding tool for generating engineered directory structures containing specified framework structures.

    npm install -g yo generator-eslint
    Copy the code
  2. Create a folder to store your projects in

    mkdir -p eslint-plugin-demo && cd eslint-plugin-demo
    Copy the code
  3. Initialize the directory structure of the ESLint plug-in

    yo eslint:plugin
    Copy the code

    Initialize template files for esLint rules

       yo eslint:rule
    Copy the code

    Generated file directory structure

    ├ ─ ─ the README. Md ├ ─ ─ docs / / using document │ └ ─ ─ rules / / all rules of document │ └ ─ ─ settimeout - no - number. Specific rules md / / document ├ ─ ─ lib / / eslint │ rules of development ├ ─ ─ index. Introduced js + export rules folder rule │ └ ─ ─ rules / / this directory can build multiple rules │ └ ─ ─ settimeout - no - number. Js / / rules details ├ ─ ─ package. The json └ ─ ─ └ // uninhibit-imp // uninhibit-imp // uninhibit-imp // uninhibit-imp // uninhibit-imp // uninhibit-imp // uninhibit-imp /Copy the code

Install dependencies

npm install
Copy the code

To start developing

There are two files that need our attention:

Lib /rules/settimeout-no-number.js, this is the rule template file that we used to develop our custom rules:

module.exports = {
 meta: {
     docs: {
         description: "SetTimeout the second argument must not be a number",},fixable: null.// Fix the function
 },
The core / / rule
 create: function(context) {
    // Public variables and functions should be defined here
     return {
         // Returns the event hook}; }};Copy the code

Complete code:

/ * * *@fileoverview The second parameter of setTimeout is forbidden to be a number
 * @author Arche* /
"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
    meta: {
        docs: {
            description: "The second parameter of setTimeout is forbidden to be a number".category: "Fill me in".recommended: false
        },
        fixable: 'code'.Or "code" or "whitespace" // Whether to enable the file repair function
        schema: [
            // fill in your schema]},create: function(context) {

        // variables should be defined here

        //----------------------------------------------------------------------
        // Helpers
        //----------------------------------------------------------------------

        // any helper functions should go here or else delete this section

        //----------------------------------------------------------------------
        // Public
        //----------------------------------------------------------------------

        return {
          'CallExpression': (node) = > {
            if(node.callee.name ! = ='setTimeout') return // Check whether it is a timer, not a return
            constparameterTime = node? .arguments[1]; // Get the second argument
            if(! parameterTime)return // Return if the second argument does not exist
            if(parameterTime.type === 'Literal' && typeof parameterTime.value === 'number') {
              context.report({
                node,
                message: 'The second parameter is forbidden to be a number'.// This prompt needs to be exactly the same as in the test case
                fix: (fixer) = > { // This is file repair function
                  const numberValue = parameterTime.value;
                  const statementString = `const countNumber = ${numberValue}\n`; // Fix the statement
                  return [
                    fixer.replaceTextRange(node.arguments[1], numberValue), // Replace the second argument position
                    fixer.insertTextBeforeRange(node.range, statementString) // Add a statement to the timer]}})}}}; }};Copy the code

The second tests/lib/rules/settimeout – no – number. This is js test case file.

/ * * *@fileoverview The seconed parameter of setTimeout is forbidden to be a
* @author Arche* /
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var rule = require(".. /.. /.. /lib/rules/setTimeout-no-number"),

   RuleTester = require("eslint").RuleTester;


//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

var ruleTester = new RuleTester({
 parserOptions: {
   ecmaVersion: 7.// Es5 is supported by default}}); ruleTester.run("setTimeout-no-number", rule, {

   valid: [{code: "let num = 1000; setTimeout(() => { console.log(1) }, num)"
     },
     {
       code: 'setTimeout(()=>{ console.log(11) },someNumber)'}].invalid: [{code: "setTimeout(() => {}, 1000)".errors: [{
               message: "The second parameter is forbidden to be a number".type: "CallExpression"}}}]]);Copy the code

References:

Write an ESLint plugin and learn how ESLint works

The most complete Eslint configuration template to unify team programming habits

The official documentation