乐趣区

关于前端:给-eslint-写一个插件

eslint 是很有名的 linter,地球上每一个 JavaScript 程序员都应该晓得。

linter 是一种代码动态剖析工具,它能够帮你找到代码中可能存在的谬误与 bug,也能找出代码格调的问题,不过因为只是动态剖析,对 js 这种动静类型的语言所能做的就比拟无限了,毕竟在 js 中,变量的类型如果不执行就不容易晓得,有些谬误就不那么容易被找进去,尽管如此,能做的查看还是很多了。

装置

装置 eslint 自身只须要装置 eslint 自身就够了,而且 eslint 自带一些规定,不装置任何插件就做到根本的查看,但个别还是须要装置一些插件。

$ 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 就会产生一个根本的配置:

$ yarn eslint --init

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

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 能够用非凡的注解开关规定,这些等下会讲到,这个插件的用处是不容许敞开了规定后不再关上,以及关掉所有规定。

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

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 抉择 espree 解析器,这是 eslint 内置的解析器,它和 babel 的解析器不太一样,应该说是 babel 的解析器和他人不一样才对,ECMAScript 定义了一套 js 的 AST 该怎么定义的规定,是 babel 和他人不同,另外 eslint 的解析器须要很具体的信息,不能只有代码的同步而已,而这样能力做好 lint 的工作。

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

写一个本人的 eslint 插件

接下来写一个 eslint 插件,虽说是写插件,但实际上写的是 eslint 的规定,假如咱们心愿 js 的对象是这样的(比方 vue 的 object):

export default {
  name: 'Foo',

  props: {},

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

像下面这样两头都有个空行,能够用两种很简略的办法来判断是不是 vue 的对象:

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

eslint 的规定大抵分为 metacreate 两个局部:

  • meta:这个规定的形容,如果这个规定能够被主动修复,也必须要定义在这里
  • create:建设规定的 AST visitor,规定的查看是在这里做的

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

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 的函数把自定义的规定退出:

// 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)
}

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

// 接下面的 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 会在最初一次把修复加上去,而后再跑一次所有规定,如果还是有能够修复的问题就再跑一次,直到没有能够主动修复的问题为止,所以也不必放心会毁坏其余插件所提供的规定。不过如果呈现了规定互相冲突会怎么呢,如果有趣味的话能够本人来试试。


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章


欢送持续浏览本专栏其它高赞文章:

  • 深刻了解 Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13 个帮你进步开发效率的古代 CSS 框架
  • 疾速上手 BootstrapVue
  • JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
  • WebSocket 实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30 分钟用 Node.js 构建一个 API 服务器
  • Javascript 的对象拷贝
  • 程序员 30 岁前月薪达不到 30K,该何去何从
  • 14 个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把 HTML 转成 PDF 的 4 个计划及实现

  • 更多文章 …
退出移动版