作为一个前端开发,要保证团队的代码质量和代码风格保持一致,就需要使用eslint来进行代码检查,一个好的eslint配置可以让团队提高开发的幸福感。市面上已经有很多大公司团队定制了自己的eslint规则,例如:国外有airbnb的eslint-config-airbnb,谷歌的eslint-config-google,国内的有阿里的eslint-config-ali等等。

当然并非大厂的规则就一定适用于我们日常工作,所以有时候拿来即用也不是很方便,我们需要根据自己的项目需求来定制一套适合自己的eslint规则。

插件概念

要开发一个 eslint 插件,需要先了解以下几个概念:

AST

抽象语法树,一种用于表示代码结构的树形数据结构。eslint 默认使用 espree 作为其解析器,将源代码转换为AST(抽象语法树)。espree 适用于ES5、ES6和ES7的JavaScript解析器,当然我们也可以配置其他解析器,例如@babel/eslint-parser,@typescript-eslint/parser等等。

Context

eslint 执行过程的上下文环境变量,可以在当前执行检测的时候获取规则配置,访问源码,报告错误,修复语法等功能。

Rule

eslint 规则是一个函数,接受一个 context 对象,返回一个 visitor 对象,可以通过visitor获取解析的节点信息并进行相应的处理。

eslint插件脚手架

可以通过 yo 命令来安装脚手架,yo 是 yeoman 的命令行工具,yeoman 是一个脚手架工具,可以通过它来快速搭建项目的基础结构。如果没有安装 yo,可以通过npm install -g yo来安装。

接下来安装 eslint 插件脚手架

npm install -g generator-eslint

安装完成后,我们创建一个文件夹,命名最好以eslint-plugin-xxx开头,然后到文件夹下运行yo eslint:plugin,会帮我们创建一个简单的eslint项目文件。

自定义规则实现

我们在刚创建的eslint-plugin-xxx文件夹根目录下运行yo eslint:rule,我们来简单实现一个功能,限制不在项目里面写console.log(xx)等代码

我们在lib/rules/no-console-log.js文件编写代码如下:

module.exports = {
  meta: {
    type: "problem", // `problem`, `suggestion`, or `layout`
    messages: {
      noConsoleLog: "no console log",
    },
    docs: {
      description: "no console log",
      recommended: false,
      url: null, 
    },
    fixable: null, // Or `code` or `whitespace`
    schema: [], // Add a schema if the rule has options
  },

  create(context) {
    // 返回访问者对象
    return {
      // 当访问到CallExpression节点时触发
      CallExpression(node) {
        // 如果调用表达式的callee是console.log
        if (
          node.callee.type === "MemberExpression" &&
          node.callee.object.name === "console" &&
          node.callee.property.name === "log"
        ) {
          // 报告错误
          context.report({
            node,
            messageId: "noConsoleLog",
          });
        }
      },
    };
  },
};

tests/lib/rules/no-console-log.js文件的内容如下:

/**
 * @fileoverview no console log
 * @author kelen
 */
"use strict";

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

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

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

const ruleTester = new RuleTester();
ruleTester.run("no-console-log", rule, {
  // 有效的测试用例
  valid: [
    // 不包含console.log的代码
    "console.error('error');",
    "console.warn('warn');",
    "console.info('info');",
    "console.debug('debug');",
    "console.trace('trace');",
    "console.dir('dir');",
    "console.dirxml('dirxml');",
    "console.table('table');",
    "console.time('time');",
    "console.timeLog('timeLog');",
    "console.timeEnd('timeEnd');",
    "console.group('group');",
    "console.groupCollapsed('groupCollapsed');",
    "console.groupEnd('groupEnd');",
    "console.clear('clear');",
    "console.count('count');",
    "console.countReset('countReset');",
    "console.assert('assert');",
  ],
  // 无效的测试用例
  invalid: [
    // 包含console.log的代码,应该报告错误
    {
      code: "console.log('log');",
      errors: [{ messageId: "noConsoleLog" }],
    },
  ],
});

接下来,执行npm run test,可以看到输出结果如下

最后我们可以把eslint发布到npm仓库里,然后就可以在其他项目里面使用了。

项目引入

在需要引入自定义插件的项目里安装该插件,npm i eslint-plugin-kelen -D,然后在.eslintrc.js文件配置如下。

module.exports = {
  root: true,
  plugins: ["kelen"],
  rules: {
    "kelen/no-console-log": "error",
  },
};

在项目输入console.log(12313);可以看到规则生效了。