ESLint is a common code specification and error checking tool for front-end developers. ESLint is very powerful and provides a large number of common rules as well as a number of useful ESLint plugins to meet various requirements. However, as the project evolves iteratively, there may be situations where existing ESLint plugins may not be sufficient for the current team’s development. Eslint Shareable Config, Eslint Plugins.

  • ESLint Shareable Config, a Shareable extension configuration (eslint-config-

    ).
  • ESLint Plugins (eslint-plugin- )

ESLint Shareable Config development

The shareable extension configuration (eslint-config-

) is an ESLint configuration object NPM package, The module names are eslint-config-

, @

/eslint-config-

. You can create a simple export configuration rule.



Creating an extension Configuration

Creating the extension configuration is as simple as creating a new index.js file and exporting an object containing the configuration:

module.exports = {

    globals: {
        MyGlobal: true
    },

    rules: {
        semi: [2."always"]}}Copy the code

For more configuration fields, see Configuring ESLint

Using extension Configuration

NPM releases extensions to introduce ESLint configuration:

module.exports = {
// extends: ['antife', 'myconfig'],
  extends: ['eslint-config-antife'.'eslint-config-myconfig'].globals: {
    'EVENT': true.'PAGE': true.'SCENE': true.'AlipayJSBridge': true,},plugins: [
    'babel'.// 'HTML ', // eslint-plugin-html extracts content from the 
    'vue']},Copy the code

Eslint plugin development

The plugin (eslint-plugin- ) is an NPM package named eslint-plugin- . Module names are eslint-plugin- , @

/eslint-plugin- .

Eslint plugin directory

We can use Yeoman and Generator-esLint to build the plugin’s directory structure for development. Here we use a custom directory, as follows:

├ ─ ─ the README. Md ├ ─ ─ _tests__ ├ ─ ─ docs ├ ─ ─ index. The js └ ─ ─ rules └ ─ ─ my - rule. JsCopy the code

Plug-in main entry component

  • Rules – The plug-in must output a Rules object containing the rule ID and a key-value pair for the corresponding rule.

  • Environments – Plugins can expose additional Environments for use in ESLint.

  • Processors – A file that defines how the plug-in handles validations.

  • Configs – Configs can be configured to specify how plug-ins are packaged, compiled, and to provide a variety of style verification configurations.

module.exports = {
    rules: {
        "my-rules": {
            create: function (context) {
                // rule implementation ...}}},env: {
        jquery: {
            globals: {
                $: false}}},configs: {
        myConfig: {
            parser: require.resolve('vue-eslint-parser'),
            parserOptions: {
                ecmaVersion: 2018.sourceType: 'module'.ecmaFeatures: {
                    jsx: true}},plugins: ["myPlugin"].env: ["browser"].rules: {
                "myPlugin/my-rule": "error",}},myOtherConfig: {
            plugins: ["myPlugin"].env: ["node"].rules: {
                "myPlugin/my-rule": "off",}}},processors: {
        '.vue': {
            // takes text of the file and filename
            preprocess: function(text, filename) {
                // here, you can strip out any non-JS content
                // and split into multiple strings to lint

                return [string];  // return an array of strings to lint
            },

            // takes a Message[][] and filename
            postprocess: function(messages, filename) {
                // `messages` argument contains two-dimensional array of Message objects
                // where each top-level array item contains array of lint messages related
                // to the text that was returned in array from preprocess() method

                // you need to return a one-dimensional array of the messages you want to keep
                return messages[0];
            },

            supportsAutofix: true // (optional, defaults to false)}}}Copy the code

Rules to create

Before you start writing new rules, read the official ESLint guide to understand the features of ESLint:

  • ESLint uses Espree for JavaScript parsing.
  • ESLint uses the AST to evaluate validation code.
  • ESLint is fully pluggable, and each rule can be a plug-in.
  • ESLint rules are independent of each other and can be disabledoff, warning,warn⚠ ️ and errorerror❌, of course, there is no need to give any hints through the normal.

You can see how ESLint evaluates validation code using AST by using AstExplorer.net, which is very powerful and supports Vue templates.

Rule component

  • The meta object contains metadata for the rule

    • typeProperty represents the type of rule, which is one"problem"."suggestion"or"layout"
    • docsAttributes are the description class information required by the core rules of ESLint
    • fixableAttributes are"code""whitespace"If the rule cannot be fixed, omit the fixable attribute
    • schemaThe specifiedoptionsSo that ESLint can prevent invalidRule configuration
    • deprecatedProperty indicates whether the rule has been deprecated
    • replacedByProperty to specify a replacement rule in case of a rule that is not recommended
  • The create function returns an ESLint call method object that accesses nodes in the abstract syntax tree (AST defined by ESTree) of JavaScript code

  • The context object contains information related to the context of the rule

    • Properties:

      • ParserOptions – parserOptions for plug-in configuration

      • Id – ID of the rule.

      • Options – Configuration options of the rule

      • Settings – Share Settings in the configuration

      • ParserPath parser-from Specifies the name of the configuration

      • ParserServices – An object that contains the services provided by the parser for the rule

    • Methods:

      • Getrooted () – returns the array of ancestors of the currently traversed node, starting at the root of the AST and ending at the immediate parent of the current node

      • GetCwd () – returns the content passed by CWD to Linter as the current working directory

      • GetDeclaredVariables – Returns the variables declared by the given node

      • GetFilename () – Returns the file name associated with the source

      • GetScope () – Returns the scope of the node currently traversed, used to track references to variables

      • GetSourceCode () – Returns a SourceCode object that can be used to process sources passed to ESLint

      • MarkVariableAsUsed (name) – Marks variables with the given name in the current scope

      • Report (Descriptor) – Reports problems in code

"use strict";

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

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 {
          Identifier(node) {
              if (node.name === "foo") {
                  context.report({
                      node,
                      messageId: "avoidName".data: {
                          name: "foo",
                      }
                  })
              }
            },
            ExportDefaultDeclaration(node){
              context.report({
                node,
                message: "test"})}}}}Copy the code

If we need to validate Vue templates, it is important to note that since the individual file components in Vue are not plain JavaScript, the default parser cannot be used, so a new parser, VUe-eslint-Parser, was introduced.

To learn more about vue AST, check it out

  • eslint-plugin-vue

  • ESTree docs

  • vue-eslint-parser AST docs

Custom Processors

The ESLint plugin was developed to allow custom handlers to handle files other than JavaScript. Custom handlers have two procedures: preProcess and PostProcess. The general structure of a custom processor is as follows:

module.exports = {
    processors: {

        // assign to the file extension you want (.js, .jsx, .html, etc.)
        ".ext": {
            // takes text of the file and filename
            preprocess: function(text, filename) {
                // here, you can strip out any non-JS content
                // and split into multiple strings to lint

                return [string];  // return an array of strings to lint
            },

            // takes a Message[][] and filename
            postprocess: function(messages, filename) {
                // `messages` argument contains two-dimensional array of Message objects
                // where each top-level array item contains array of lint messages related
                // to the text that was returned in array from preprocess() method

                // you need to return a one-dimensional array of the messages you want to keep
                return messages[0];
            },

            supportsAutofix: true // (optional, defaults to false)}}};Copy the code

Plug-in test

ESLint provides the RuleTester utility to easily test rules in your plugin, in peerDependency pointing to ESLint 0.8.0 or later.

{
    "peerDependencies": {
        "eslint": "> = 0.8.0"}}Copy the code

The purpose of peerDependencies is to prompt the host environment to install packages that meet the dependencies specified in the plugin peerDependencies, and then always refer to the NPM packages installed by the host environment when the plugin import or require the packages. Finally solve the problem of inconsistency between plug-ins and dependent packages.

// in the file to lint:

var foo = 2;
// ^ error: Avoid using variables named 'foo'

// In your tests:
var rule = require(".. /rules/no-avoid-name")
var RuleTester = require("eslint").RuleTester

var ruleTester = new RuleTester()
ruleTester.run("no-avoid-name", rule, {
  valid: ["bar"."baz"].// right data
  invalid: [  // error data
    {
      code: "foo".errors: [{messageId: "avoidName"}]}]})Copy the code

Practice developing the Vue template Eslint plugin

Before development, note here that since the individual file components in Vue are not plain JavaScript, the default parser cannot be used, so a new parser, VUe-eslint-Parser, was introduced.

{
    "parser": "vue-eslint-parser"."parserOptions": {
        "parser": "babel-eslint"."sourceType": "module"."allowImportExportEverywhere": false}}Copy the code

Develop vue ESLint rules

Note here that you need to use context.parserServices to access the abstract syntax tree content parsed by a custom parser.

  • Vue plug-in rules, examples
module.exports = {
    meta: {
      docs: {
        description: 'disallow unnecessary `v-bind` directives'.url: 'https://eslint.vuejs.org/rules/no-useless-v-bind.html'
      },
      fixable: 'code'.type: 'suggestion'
    },

    create(context) {
      if (context.parserServices.defineTemplateBodyVisitor == null) {
        context.report({
          loc: { line: 1.column: 0 },
          message:
            'Use the latest vue-eslint-parser. See also https://eslint.vuejs.org/user-guide/#what-is-the-use-the-latest-vue-eslint-parser-error'
        })
        return{}}return context.parserServices.defineTemplateBodyVisitor({
        VElement(node){
          if(node.name === "template"){
            context.report({
              node,
              message: "The template tag",
            })
          }
        },
  
        Identifier(node){
          console.error('Identifier.name', node.name)
        }
  
      })
    }
  }
Copy the code

If check js, not through the context. ParserServices. DefineTemplateBodyVisitor syntax tree information

  • Sample Vue plug-in entry file:
module.exports = {
    configs: {
      base: {
        parser: require.resolve('vue-eslint-parser'),
        plugins: ['boilerplate'].rules: {
          'boilerplate/no-avoid-name': 'error'.'boilerplate/no-useless-v-bind': 'error'}}},env: {
      browser: true.es6: true
    },
    rules: {
      'no-avoid-name': require('./rules/no-avoid-name'),
      'no-useless-v-bind': require('./rules/no-useless-v-bind'),}}Copy the code
  • Configured to use
module.exports = {
    parser: 'vue-eslint-parser'.parserOptions: {
      parser: 'babel-eslint'.ecmaVersion: 2018.sourceType: 'module'
    },
    // Vue plugins should be placed before extend to prevent overwriting, "esLint :recommended" is the default recommended rule
    extends: ['plugin:vue/recommended'.'plugin:boilerplate/base'."eslint:recommended"].plugins: [
      'babel',]}Copy the code

For more information about vue AST, see

  • eslint-plugin-vue

  • ESTree docs

  • vue-eslint-parser AST docs

eslint-plugin-boilerplate

Eslint-plugin-boilerplate — Rapid development of esLint template samples.

To the end. – Recruiting

Ant International Business Group Shenzhen wireless front-end team a large number of social recruitment positions HC, details please pay attention to Alipay Payment Services Hong Kong Limited- senior front-end R&D engineer/expert, friends, you can add my wechat Gluuu1 internal promotion, more opportunities ~ ~

Other Resource

Eslint

The ESLint Vue Plugin Developer Guide

Working with Rules

Develop a plugin

Shareable Configs

vue-eslint-parser AST docs

How to write Eslint plugins