ESLint Handbook

What is ESLint

ESLint is an inspection tool for ECMAScript/JavaScript whose goal is to make code more consistent and avoid errors, with the following features:

  • Use Espress as the parser;
  • Perform actions in code based on the AST
  • Fully pluggable (every rule is a plugin)

To sum up: ESLint is an AST-based inspection tool for implementing ECMAScript/JavaScript, mainly used to ensure code quality, and has a lot of configurable items.

ESLint's golden partner Prettier

In team development, it is necessary to ensure the quality of the code, but the style of the code also needs to be unified. The solution is Prettier .

Prettier thinks that the code style is very important, but you don't need to customize it. I'm done with it, and you don't need to worry about the rest.

Which are the code styles:

Because Prettier and ESLint actually have some overlapping functions, there must be conflicting items. If you want ESLint and Prettier to run harmoniously, you need to use the eslint-config-prettier plugin to resolve small conflicts between partners.

npm install --save-dev eslint-config-prettier
复制代码
{
  "extends": [
    "some-other-config-you-use",
    "prettier" // 一定要将 prettier 声明在最后,才能有效的覆盖有冲突的配置项
  ]
}
复制代码

If you want all error messages to be handled by ESLint, you can use the eslint-plugin-prettier plugin, and use prettier as an ESLint plugin.

How to configure ESLint

There are two ways to configure:

  1. Configure via comments in the file
  2. through a specific configuration file

Typical configuration items in configuration files :

  • Environments 指定 js 运行的环境,以自动注入全局变量,比如 nodejs es5 es6
  • Globals 指定自定义的全局变量
  • Rules 所有的规则配置项
  • Plugins 插件配置项目

微信小程序中使用的 .eslintrc.js 文件 DEMO :

module.exports = {
  env: {
    browser: true,
    es6: true,
    node: true,
  },
  extends: ['eslint:recommended'],
  parserOptions: {
    sourceType: 'module',
    ecmaVersion: 12
  },
  rules: {
    indent: ['error', 'tab', { SwitchCase: 1 }],
    'linebreak-style': ['error', 'unix'],
    quotes: ['error', 'single'],
    semi: ['error', 'always'],
    'object-shorthand': ['error', 'always'],
    'one-var': [
      2,
      {
        var: 'never',
        let: 'never',
        const: 'never',
      },
    ],
    'no-invalid-regexp': 'error'
  },
  globals: {
    getApp: false,
    wx: false,
    Page: false,
    getCurrentPages: false,
    Component: false,
    App: false,
    Behavior: false,
    requirePlugin: false,
  },
};
复制代码

因为 ESLint 的可配置项非常多,最佳实践就是继承通用的校验规则,比如 "extends": ["eslint:recommended"],然后再进行团队定制。

这个地方需要知道的小知识:

.eslintrc.* 会从检查的文件路径开始逐级向上搜索配置文件,直到根目录或则配置中包含有 root: true 才会停止搜索,途中所有的 .eslintrc.* 文件配置会被合并,根据就近优先原则,对冲突配置进行覆盖。

如果项目的 package.json 文件中指定了 eslintConfig 项,那么这个配置会被生效于项目的所有子目录,根据就近优先原则对冲突配置进行覆盖。

怎么基于 ESLint 进行创作

官方为了简化开发操作,已经基于 yo 定制了脚手架 generator-eslint,因此我们只需要几行命令就可以轻松搭建好开发环境。

# 全局安装 yo
npm install -g yo

# 安装 eslint 官方脚手架
npm install -g generator-eslint
复制代码

创建插件

# 创建一个项目文件夹比如
mkdir eslint-wd

# 进入目录
cd eslint-wd

# 通过脚手架创建基础文件,根据引导选择包含 rules

yo eslint:plugin

# 最后会生成如下结构的目录
# .
# ├── README.md
# ├── lib
# │   ├── index.js
# │   ├── processors
# │   └── rules
# ├── package-lock.json
# ├── package.json
# └── tests
#     └── lib
#        ├── processors
#        └── rules
复制代码

创建 rule

# 创建自定义 rule 我们依然使用脚手架,在 eslint-wd 目录中执行
# 我们创建一个文件中不允许使用 require 引入模块变量为例子

yo eslint:rule

# 根据引导完成配置
#? What is your name? amin
#? Where will this rule be published? ESLint Plugin
#? What is the rule ID? no-require
#? Type a short description of this rule: 禁止项目中使用 require
#? Type a short example of the code that will fail: const fs = require('fs');
#   create docs/rules/no-require.md
#   create lib/rules/no-require.js
#   create tests/lib/rules/no-require.js
复制代码

根据脚手架引导之后,会生成三个文件:

  • docs/rules/no-require.md 文档描述
  • lib/rules/no-require.js 核心逻辑
  • tests/lib/rules/no-require.js 单元测试

进入 lib/rules/no-require.js 文件中,主要有两个核心部分:

  • meta 元数据
    • type 指定规则的类型

      • "problem" 可能会导致错误或者异常行为
      • "suggestion" 一些优化建议,不修改也不会导致错误
      • "layout"code style
    • docSome textual descriptions of the rules and document addresses

      • description(string)
      • category(string)
      • recommended(boolean) "extends": "eslint:recommended"whether to enable this rule
      • url(string)
    • fixable

      • "code"
      • "whitespace"
    • schema Configuration instructions

  • create(context) {}The function returns an object, which defines various callback functions in ASTdeep traversal. For simple traversal, you can directly use the node type in the syntax tree as the function name. For more usage, see the documentation .

Let me take 禁用 require()the plugin to explain the process:

First, go to the Syntax Tree Online Preview site to viewAST

const fs = require('fs');
const { exec } = require('child_process');
复制代码

After pasting the code in, you can see ASTthe :

WechatIMG328.png

As you can see, we need to identify that requirewe only need to pay attention to the node of CallExpressiontype , and the calleeattribute of this node is one Identifier, and name="require", then the code is easy to implement:

/**
 * @fileoverview 禁止项目中使用 require
 * @author amin`
 */
"use strict";

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

/**
 * @type {import('eslint').Rule.RuleModule}
 */
module.exports = {
  meta: {
    type: "suggestion", // `problem`, `suggestion`, or `layout`
    docs: {
      description: "禁止项目中使用 require",
      category: "Fill me in",
      recommended: false,
      url: null, // URL to the documentation page for this rule
    },
    fixable: null, // Or `code` or `whitespace`
    schema: [], // Add a schema if the rule has options
    messages: {
      avoidRequire: 'avoid use {{name}}',
    }
  },

  create(context) {
    // variables should be defined here

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

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

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

    return {
      // visitor functions for different types of nodes
      CallExpression(node) {
        const callee = node.callee;
        if (callee && callee.type === 'Identifier' && callee.name === 'require') {
          context.report({
            node,
            messageId: 'avoidRequire', // 对应 meta 中定义的 messages 部分
            data: {
              name: callee.name,
            },
          });
        }
      }
    };
  },
};
复制代码

Perfect Jesttest :

/**
 * @fileoverview 禁止项目中使用 require
 * @author fumin
 */
"use strict";

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

const rule = require("../../../lib/rules/no-require"),
  RuleTester = require("eslint").RuleTester;


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

const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2015, sourceType: 'module' } });
ruleTester.run("no-require", rule, {
  valid: [
    'import fs from "fs"',
    'import { exec } from "child_process"'
  ],

  invalid: [
    {
      code: "const fs = require('fs');",
      // output: 'avoid use require',
      errors: [{ messageId: 'avoidRequire' }],
    },
  ],
});
复制代码

After writing the test case, you can execute npm run testto verify whether the function we wrote is correct. After the verification is passed, we can go to the actual project to verify.

In the root directory of the plugin, use the npm linkcommand to softly link the plugin to the global directory

In the need to use the project, execute ,npm link eslint-plugin-wd the plug-in soft chain will be created in the projectnode_modules

Introduce the plugin in the .eslintrc.*file and configure the rules in the rules:

{
  plugins: ['eslint-plugin-wd'],
  rules: {
    'eslint-plugin-wd/no-require': 'error',
  },
}
复制代码

In this way, the project is enabled, and the rules just created.

Guess you like

Origin juejin.im/post/7086102578916196366