记一次 ESlint 的经历

背景

准备重构 tingle 这个项目,首先就是进行了 Button 这个组件的重构以便熟悉熟悉项目。

tingle 采用 gulp + webpack 对代码进行实时编译开发和构建,在目前这个项目中,现有工具不具备代码质量的校验,这样非常不利于项目日后的社区化贡献代码和标准化,因此在这次重构中,决定将 js 的代码质量检查加进去,本来想使用 jshint 的,但由于 tingle 是走 es6 风格的 react 组件集,而我发现 jshintes6react 的支持不如 eslint 优雅,所以在最后决定使用 eslint

设计原则

优雅简单

在决定使用 eslint 之后,第一件事就是思考如何将 eslint 用起来,一定要对用户优雅简单。首先就是改造工具,tingle 的构建工具是基于 gulp 的,在开发的时候我们只需要使用下面这个命令

gulp d

就可以启动一个带有实时编译 stylusjsxbrowser-sync server,非常方便(关于如何使用 gulp 请自行参考官方文档)。

那么 eslint 的代码质量检查应该放在哪一步好呢?我得出了以下两个答案:

  • 代码修改后实时进行质量检查
  • 代码压缩前进行质量检查

两者都应该做,因为无论是在修改 es6 代码之后,还是压缩打包 es6 代码之前这两步都需要对代码的质量进行验证。

人性化的错误通知

紧接着就是发现错误后的通知,这个非常重要,关乎用户体验。原来使用过 grunt + grunt-contrib-jshint,当发现错误时会让 terminal 不停得跳,然后在 terminal 打开之前图标的右上角还有红色的气泡,非常的人性化,这次我也想使用这个设计,在 eslint 发现错误的时候进行这种交互的提示,但查找了一些资料,无从下手,最后改成了使用 node-notifier 这个包来进行 Mac 原生的信息提示,也算是可以接受的,代码质量校验失败时如下图:

记录下过程

配置 .eslintrc.json 文件

在参考了 eslint 官网及 airbnb javascript guide 之后,暂时写出了下面的配置文件,后面会考虑学习 airbnb 一样,抽离公共的 eslint 配置文件 eslint-config-tingle 来进行专门的代码规则维护。

.eslintrc.json文件:

{
  "env": {
    "browser": true,
    "node": true
  },
  "parser": "babel-eslint",
  "parserOptions": {
    "ecmaVersion": 6,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "rules": {
    "comma-dangle": ["error", "never"],
    "no-unreachable": "error",
    "no-inner-declarations": "error",
    "valid-typeof": "error",
    "block-scoped-var": "error",
    "curly": "error",
    "vars-on-top": "error",
    "no-undefined": "error",
    "no-unused-vars": "error",
    "no-use-before-define": "error",
    "no-var": "error",
    "indent": ["error", 2],
    "arrow-spacing": "error"
  },
  "plugins": [
    "react"
  ]
}

编写 eslint 功能

主要是用 eslintCLIEngine 对象来进行编码,用 tableFormatter 来美化控制台打印结果:

let notifier = require('node-notifier')
let CLIEngine = require('eslint').CLIEngine;
function eslint() {
  let cli = new CLIEngine({
    useEslintrc: true
  })

  let report = cli.executeOnFiles(['src/**/*.js'])
  let tableFormatter = cli.getFormatter('table')
  if (report.errorCount || report.warningCount) {
    console.log('The eslint went wrong')
    console.log(tableFormatter(report.results))
    notifier.notify({
      title: 'Tingle Notification',
      message: `${report.errorCount} errors and ${report.warningCount} warnings found, please view terminal.`
    })
    return false
  }
  return true
}

将 eslint 功能集成进 gulp 任务

gulp.task('eslint', function(cb) {
  let eslintRes = eslint()
  if (eslintRes) {
    // ... do something
    cb();
  } else {
    cb('eslint error');
  }
});

// 在打包前执行
gulp.task('build', ['eslint'], function () {
  // ... do something
}).on('error', (err) => console.log(err))

遇到的问题

1.进行 es6react 代码质量检查需要使用 babel-eslinteslint-plugin-react,然后如果你是全局或项目中使用 eslint 你分别要全局或着项目中安装这两个包

{
  "parser": "babel-eslint",
  // ...... 其他配置省略
  "plugins": [
    "react"
  ]
  // ...... 其他配置省略
}

2.gulp 任务执行失败后不执行后续任务,并且不能中断当前的 watch 任务,具体实现机制如下:

gulp.task('前置任务', function(callback){
  if (eslint任务执行成功) {
    callback();
  } else {
    callback('错误描述')
  }
})

gulp.task('后置任务', ['前置任务'], function(){
  // 继续执行后置任务
}).on('error', (e) => {console.log(e)})

总结一下package

  • node-notifier
  • eslint
  • eslint-config-tingle (未来会把 .eslintrc.json 配置单独抽离出来,形成这个配置文件)
  • babel-eslint
  • eslint-plugin-react

待优化

  1. 不用每次保存都去检查每一个js文件,需要做一下缓存
  2. 优化 terminal 的提示方式
  3. 抽离公共 eslintrc 文件,让脚手架中的 .eslintrc.json 继承公共文件,类似 airbnb

参考资料