前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >给 eslint 写一个插件

给 eslint 写一个插件

作者头像
疯狂的技术宅
发布2020-12-15 11:02:05
8270
发布2020-12-15 11:02:05
举报
文章被收录于专栏:京程一灯
代码语言:javascript
复制

eslint 是非常有名的 linter,地球上每一个 JavaScript 程序员都应该知道。

linter 是一种代码静态分析工具,它可以帮你找到代码中可能存在的错误与 bug,也能找出代码风格的问题,不过因为只是静态分析,对 js 这种动态类型的语言所能做的就比较有限了,毕竟在 js 中,变量的类型如果不执行就不容易知道,有些错误就不那么容易被找出来,虽然如此,能做的检查还是很多了。

安装

安装 eslint 本身只需要安装 eslint 本身就够了,而且 eslint 自带一些规则,不安装任何插件就做到基本的检查,但一般还是需要安装一些插件。

代码语言:javascript
复制
$ yarn add --dev eslint

eslint 除了可以安装插件外,还可以安装另外两个东西,总共有 3 种:

  • plugin:eslint 的插件可以帮 eslint 增加规则,另外也可以通过配置文件让程序员添加自己的规则,插件可以提供一份默认的推荐配置
  • config:可以重复使用的规则配置文件,比较有名的是 standardairbnb 的规则,配置文件有可能会有依赖的插件,需要自己去安装
  • parser:用来扩充 eslint 可以处理的语法,有用 babel 转换 js 的 babel-eslint ,让 eslint 可以处理实验性的语法;@typescript-eslint/parser 可以让 eslint 处理 Typescript;还有 vue-eslint-parser 用来处理 vue 代码。

使用

虽然安装很简单,但不对 eslint 进行配置是什么都不能做的,所以还要提供一个基本的配置,而 eslint 提供一个简单的初始化命令,通过执行这个命令并回答几个问题,eslint 就会产生一个基本的配置:

代码语言:javascript
复制
$ yarn eslint --init

eslint 的配置文件可以是 js、json 或 yml 的格式,在这里我们用 js 格式,文件要取名为 .eslintrc.js,这里就用基本的配置,即只用 eslint:recommended 这组设置,如果有其它的插件也像这样进行基本的设置:

代码语言:javascript
复制
module.exports = {
  extends: 'eslint:recommended',
}

eslint 的配置文件有几个基本的配置项,在这里也顺便说几个我常用的 config 的插件,首先是 config 的部分:

  • eslint-config-standard:很有名的配置,它还需要另外安装 4 个插件
  • eslint-config-prettier:用来关掉排版相关配置项的配置文件,因为要交给 prettier 处理,关掉就不会引发冲突了。

我还没有列出 standard 所相依的插件:

  • eslint-plugin-simple-import-sort:能够自动排序 import 的一个插件
  • eslint-plugin-eslint-comments:用来检查 eslint 的特殊注解的一个插件,eslint 可以用特殊的注解开关规则,这些等下会讲到,这个插件的用途是不允许关闭了规则后不再打开,以及关掉所有规则。

把上面的内容都写到配置文件中应该是这样:

代码语言:javascript
复制
module.exports = {
  extends: [
    'standard',
    // 加上 prettier 的配置,关掉部份样式检查,顺序很重要
    'prettier',
    'prettier/standard',
    // 如果是插件提供的配置项需要以 `plugin:` 开始
    'plugin:eslint-comments/recommended',
  ],
  // 额外的规则,这里也可以决定是否要关掉某些规则
  rules: {
    // 设置 plugin `eslint-plugin-simple-import-sort` 的 `sort` 规则是 `error`,也就是不符合时是会报错的
    // 另外还可以设置为 `warn` 只警告,或是 `off` 关掉
    // 有的规则也有选项,这是就要用 ['error', {<options>}] 这种像 babel 的格式了
    'simple-import-sort/sort': 'error',
  },
  // 配置指定的环境,这会影响到判断哪些是全局变量
  env: {
    browser: true
  },
  // 设置 eslint 自己的 parser 用的是哪一版本的 js ,一般设置为 eslint --init 就行了
  parserOptions: {
    ecmaVersion: 12,
  },
}

运作原理

eslint 跟 babel 很相似,都是先把文件转成 AST,如果想查看 eslint 转出来的 AST ,可以到 AST Explorer (https://astexplorer.net/) 选择 espree 解析器,这是 eslint 内置的解析器,它和 babel 的解析器不太一样,应该说是 babel 的解析器和别人不一样才对,ECMAScript 定义了一套 js 的 AST 该怎样定义的规则,是 babel 和别人不同,另外 eslint 的解析器需要很详细的信息,不能只有代码的同步而已,而这样才能做好 lint 的工作。

它的运作方式也像 babel 一样,让 plugin XML visitor 对特定的节点进行检查,如果发现有问题就通过它的 API 来报告,也可以通过它的 API 提供修正的程序。

写一个自己的 eslint 插件

接下来写一个 eslint 插件,虽说是写插件,但实际上写的是 eslint 的规则,假设我们希望 js 的对象是这样的(比如 vue 的 object):

代码语言:javascript
复制
export default {
  name: 'Foo',

  props: {},

  data: () => ({}),
}

像上面这样中间都有个空行,可以用两种很简单的方法来判断是不是 vue 的对象:

  • export default 之后
  • 包含在 Vue.extend

eslint 的规则大致分为metacreate 两个部分:

  • meta:这个规则的描述,如果这个规则可以被自动修复,也必须要定义在这里
  • create:建立规则的 AST visitor,规则的检查是在这里做的

与 babel 插件很像,第一步是先打开 AST Explorer,选 eslint 用的解析器 espree,这里要替换的是 ObjectExpress

代码语言:javascript
复制
module.exports = {
  meta: {
    // 可以被修复的规则一定要定义,这里除了 'whitespace' 外还有 'code' ,不过知识分类上的问题
    // 这里因为是要加换行,所以选 'whitespace'
    fixable: 'whitespace',
    // 可以定义可能会出现的信息,这就可以进行统一管理。这是 eslint 的推荐做法
    // 你也可以直接把数据写在 context.report
    messages: {
      requireNewline: 'require newline between',
    },
  },
  create: function (context) {
    return {
      ObjectExpression(node) {
        if (
          // 判断副节点是否为 export default
          node.parent.type === 'ExportDefaultDeclaration' ||
          // 或父节点是 `Vue.extend`
          (node.parent.type === 'CallExpression' && isVueExtend(node.parent.callee))
        ) {
          // 得到 source code 对象,后面的 fixer 需要用到
          const sourceCode = context.getSourceCode()
          // 用 for 循环把对象的属性每两个氛围一组,检查中间有没有加空行
          for (let i = 0; i < node.properties.length - 1; ++i) {
            // 这里的判断方法很简单,上一个属性结尾的行号必须与下一个属性结尾的行号相差 2 以上
            // 也就是中间有两个以上的空行
            if (node.properties[i + 1].loc.start.line - node.properties[i].loc.end.line < 2) {
              context.report({
                // 用预先定义的数据
                messageId: 'requireNewline',
                // 或是你可以直接把数据写进来
                // message: 'require newline between',
                // 指定出错的位置,因为是在两个属性之间,所以就用上一个的 end 与后一个的 start 来指定
                loc: {
                  start: node.properties[i].loc.end,
                  end: node.properties[i + 1].loc.start,
                },
                // 如果出错的位置正好是某个 AST 的节点,那也可以传入节点
                // node: node
                fix(fixer) {
                  // 这里是自动修复的部分稍后再加上
                },
              })
            }
          }
        }
      },
    }
  },
}

正常来说 eslint 的插件需要照着 eslint 的命名规则才能加载,不过为了方便测试,就直接调用 eslint 的函数把自定义的规则加入:

代码语言:javascript
复制
// eslint 的 Linter
const { Linter } = require('eslint')
// 我们要定义的规则
const rule = require('./space-between-properties')

const linter = new Linter()

// 为规则设定一个 id
const id = 'space-between-properties'
// 加入规则定义,也就是上面的那个东西
linter.defineRule(id, rule)

// 执行规则
const res = linter.verify(
  `
export default {
  name: 'Foo',
  props: {},
}
`,

  {
    rules: {
      [id]: 'error',
    },
    parserOptions: {
      sourceType: 'module',
      ecmaVersion: 2015,
    },
  }
)

// 如果有错误的话就打印出来
if (res.length) {
  console.log(res)
}

不出意外的话应该会看到有内容输出,接着要加上自动修复的部分:

代码语言:javascript
复制
// 接上面的 fix 部份
fix(fixer) {
  // 取得两个节点中间的 token
  const tokens = sourceCode.getTokensBetween(node.properties[i], node.properties[i + 1])
  // “通常”中间只会有逗号,所以唯一的节点就是逗号
  const comma = tokens[0]
  // 要求 eslint 在都好后面加上换行
  return fixer.insertTextAfterRange(comma.range, '\n')
}

修复也很简单,就是在逗号后面加上换行而已,不过上面也特别说了是“通常”,其实这个插件你只要在 , 后面加上注解就会出现问题了

eslint 会在最后一次把修复加上去,然后再跑一次所有规则,如果还是有可以修复的问题就再跑一次,直到没有可以自动修复的问题为止,所以也不用担心会破坏其他插件所提供的规则。不过如果出现了规则互相冲突会怎样呢,如果有兴趣的话可以自己来试试。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端先锋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 安装
  • 使用
  • 运作原理
  • 写一个自己的 eslint 插件
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档