preface

As the project grows larger, the number of people in charge of the project will increase or change, which means that people with different code styles will participate in the same project development at the same time, which will lead to “a hundred flowers bloom” in the code style of the project.

The question is, how do you maintain a uniform code style?

Isn’t that easy?

Use the ESLint plugin to load various rules to verify git commit in real time.

The problem is that when the rules provided by the community do not meet some of the more customized specifications, we need to use the ESLint plugin mechanism to validate the custom rules.

Some time ago, a plugin named ESlint-plugin-imports -sorter was implemented for some of the group’s more specific specifications, providing the following validation.

  • Check import sequence: Third-party library/official library > Absolute Path Resource > Relative path Resource

    // error
    import React from 'react'
    import { BarEnum } from '.. /.. /const'
    import style from '.. /index.less'
    import utils from 'src/utils'
    import { pick } from 'loadsh'
    
    // right
    import React from 'react'
    import { pick } from 'loadsh'
    import utils from 'src/utils'
    import { BarEnum } from '.. /.. /const'
    import style from '.. /index.less'
    Copy the code
  • Different import order newline split (configurable)

    import React from 'react'
    import { pick } from 'loadsh'
    
    import utils from 'src/utils'
    
    import { BarEnum } from '.. /.. /const'
    import style from '.. /index.less'
    Copy the code
  • Same import order, hierarchical sort (configurable)

    import React from 'react'
    import { pick } from 'loadsh'
    
    import utils from 'src/utils'
    
    import style from '.. /index.less'
    import { BarEnum } from '.. /.. /const'
    Copy the code
  • Absolute path resource configuration alias

Since the automatic repair logic is provided, the configuration with VsCode (seeVsCode configures ESLint to save fixes), the following effects can be achieved.


The ESLint verification process

Here’s a preview of some general properties in ESLint configurations that affect final rule validation.

  • extendsEach item in ESLint eventually points to an object with the same configuration rules as ESLint itself, and thenESLintAll inherited configurations are merged recursively
  • ESLintAt its core are rules (rule), each rule is independent of the other.
  • The final check is the combinationextends,rule,Nodes included by the ESLint comment commandWhile the generated configuration finally takes effect, callrunRulesGenerated throughoutASTVerify the node specified in the rule.

1. Obtain the rule configuration that takes effect

For example, 🌰

For rule no-var

Inherited rule :no-var:2

User-defined rule: no-var:1

User-defined command modification rule: no-var:0

Finally, no-var:0 takes effect

eslint/lib/linter/linter.js

The configuration structure of a custom command is as follows

/* Custom directive configuration generated according to AST+ annotations */
const commentDirectivesConfg = {
    /* Partially modify the configuration information of some rules */
    configuredRules: {},
    /* Exported variableName specifies the exposed variables */
    enabledGlobals: {},
    exportedVariables: {},
    problems: [].disableDirectives: [{type: 'disable-next-line'.line: xxx,
            column: xxx,
            ruleId: 'ruleId'}}]Copy the code

2,runRulesAll rules are traversed and verified

Finally, each rule sets a listener to verify the current node.


conclusion
  • Plug-ins integrate rules + rule configuration, and the implementation of the core logic of rules iscreateMethods.
  • ESLintThe essence of the verification process is that the type node selected by the rule is traversed and verified based on all the valid rule configurations.

Ii. Disassemble into AST (omitted)

AST Explorer

3. Plug-in development process

2.1 To do a good job, you must sharpen your tools

Here we use the Yeoman scaffolding and the generator-ESLint generator to generate the default development template for the ESLint plug-in.

  • Install dependencies
// Install Yeoman (sudo) NPM i-g yo // Install ESLint plugin template generator (sudo) NPM i-g generator-esLintCopy the code
  • Create a template
// Find the folder where you want to create the plugin // initialize the template Yo eslint:pluginCopy the code
  • Create a rule template
yo eslint:rule
Copy the code

Add some information about the rules

Note: Plugins are usually named eslint-plugin-xxx, but @xx/eslint-plugin-xx is also supported. In plug-in configuration, you only need to omit the beginning or middle eslint-plugin-xxx

  • The directory structure

    Once generated, our default template should look like this figure.

Just a quick explanation of what these directories do

-docs /rules: lib/rules - Tests /libs/rules: unit tests under lib/rules - lib/rules: all rules - lib/index: plug-in configurationCopy the code

2.2 Local Real-time Debugging

Due to some limitations in the strings-only code that don’t fit perfectly into the real-world ESLint validation process, you need to link 🔗 locally to test.

// In the plugin project sudo NPM linkCopy the code

When the link is successful, we get the following result

// Switch to project project NPM link XXXXCopy the code

Next, the changes we make in the plug-in are synchronized to the project

2.3 Write rule logic

1. Configure plug-in parameters (click me to view) : specify which configuration items can be accepted by this rule, whether it can be automatically repaired, document link address, error message copywriting, etc.

Some of the more important parameters

  • meta
    • docs
      • It: the rulesThe configuration fileIn the"extends": "eslint:recommended"Property whether to enable the rule.
    • Fixable: Whether the error behavior provides a fix. If there is nofixableProperty, even if the rule is implementedfixFunctionality, ESLint doesn’t eitherTo repair. If the rule is not fixable, omit itfixableProperties.
    • Schema: Specifies the types of rule configuration parameters
    interface Config {
      /* The default value is true */
      isCheckEmptyLine: boolean
      /* Parity level: default false */
      isCheckDeepth: boolean
      /* Absolute path alias: default [' SRC ','@'] */
      alias: Array<string>}Copy the code

The configuration items are in the following format when used

  • Create (function) returns an object containing the methods ESLint uses to access nodes while iterating through the abstract syntax tree AST (the AST defined by ESTree) of JavaScript code.

    • If a key is a node type or selector, ESLint calls the visitor function while iterating down the tree
    • If a key is a node type orselectorwith:exitIn theupwardESLint is called when traversing the treevisitorfunction
    • If a key is an event name, ESLint calls handler functions for code path analysis

2, find the verification rule failure condition (click let me see)

  • Firstly, it is relatively important for me to make clear which kind of node the scene to be verified is on.

Some rules can be implemented in multiple ways, but the cost of implementation depends to some extent on the type of AST node you set

// Here are all the node types supported by ESlint, which can be viewed at [https://astexplorer.net/]
const KNOWN_NODES = new Set([
    "AssignmentExpression"."AssignmentPattern"."ArrayExpression"."ArrayPattern"."ArrowFunctionExpression"."AwaitExpression"."BlockStatement"."BinaryExpression"."BreakStatement"."CallExpression"."CatchClause"."ChainExpression"."ClassBody"."ClassDeclaration"."ClassExpression"."ConditionalExpression"."ContinueStatement"."DoWhileStatement"."DebuggerStatement"."EmptyStatement"."ExperimentalRestProperty"."ExperimentalSpreadProperty"."ExpressionStatement"."ForStatement"."ForInStatement"."ForOfStatement"."FunctionDeclaration"."FunctionExpression"."Identifier"."IfStatement"."Literal"."LabeledStatement"."LogicalExpression"."MemberExpression"."MetaProperty"."MethodDefinition"."NewExpression"."ObjectExpression"."ObjectPattern"."Program"."Property"."RestElement"."ReturnStatement"."SequenceExpression"."SpreadElement"."Super"."SwitchCase"."SwitchStatement"."TaggedTemplateExpression"."TemplateElement"."TemplateLiteral"."ThisExpression"."ThrowStatement"."TryStatement"."UnaryExpression"."UpdateExpression"."VariableDeclaration"."VariableDeclarator"."WhileStatement"."WithStatement"."YieldExpression"."JSXFragment"."JSXOpeningFragment"."JSXClosingFragment"."JSXIdentifier"."JSXNamespacedName"."JSXMemberExpression"."JSXEmptyExpression"."JSXExpressionContainer"."JSXElement"."JSXClosingElement"."JSXOpeningElement"."JSXAttribute"."JSXSpreadAttribute"."JSXText"."ExportDefaultDeclaration"."ExportNamedDeclaration"."ExportAllDeclaration"."ExportSpecifier"."ImportDeclaration"."ImportSpecifier"."ImportDefaultSpecifier"."ImportNamespaceSpecifier"."ImportExpression"
]);
Copy the code
  • Read configuration items and write the logic for error determination. (Depending on the function of your rule)

3, throw error message and repair behavior (click me to view)

Whether the rule can be repaired depends on whether the cause of verification failure is subjective.

For example, 🌰

Scenario 1: During debugging, modify some variables and parameters temporarily for easy debugging. But occasionally I forget to revise it back.

/* esLint :todo */

The error with plugins like the one above is that they tend to be subjective, and you can’t be sure from context that the error can be fixed.

Specify the fix function when you use context.report(). The fix function takes an argument, a Fixer object, that you can use to fix. Such as:

context.report({
    node: node,
    message: "Missing semicolon".fix: function(fixer) {
        return fixer.insertTextAfter(node, ";"); }});Copy the code

Important: ESLint will not fixa rule unless it outputs a meta.fixable attribute, even if the rule implements the fix function.

Note âš  :

  1. Avoid any fixes that could change the runtime behavior of your code and cause it to stop working.
  2. Make as small a fix as possible. Fixes that are not necessary may conflict with other fixes and should be avoided.
  3. Make each message have only one fix. It’s mandatory because you have to go fromfix()Returns the result of the repair operation.

reference

  • Discusses how ESLint works
  • Eslint.bootcss.com/docs/develo…