一款检测代码中TODO的eslint插件

前言

看了我标题进来的同学应该也知道我做的是个啥东西

没错是一个eslint插件,前端魔法师们日常所使用的工具之一

什么?你不知道eslint是干嘛的--吃鲸.jpg

  • ESLint 是一个开源的 JavaScript 代码检查工具
  • 能在多人协作项目中帮助统一代码风格

百闻不如一见,先带大家看看插件效果

因为在工作中临时会插入许多其它的事,或者有些代码,接口是有时效性的需要手动下线

插件功能简单讲就是为不完善的代码做标记,提醒自己如期修改完善bug

想亲自尝试一下的同学戳这里---顺手Star

下面开始介绍为什么会写这个插件,以及过程中遇到的问题,和插件的一些核心方法

背景

前端魔法师们,经常不经意间就会在代码中下毒,坑害同事,甚至直接使"线上爆炸"

当时遇到一个线上问题就是如此:

  • 一个有实效性的活动入口,需要到点下线,一个需要常驻的活动入口,不需要下线
  • 由于实效性活动下线时间还有大半年

coder为了图方便,直接把实效性活动业务组件拿着复用,想着到时候改,反正时间还长

半年后的一天,客述就来了,说xx入口怎么不见了,彼时开发此页面的同事已经调走,目前的项目组基本都是后来的

第一反应是不是昨晚提交了什么代码,把入口吓掉了,然后就开始翻发布记录,发现并没有发版,就奇怪了,好好地怎么就不见了

然后同事们就开始排查代码了,不一会儿就发现了问题所在,然后快速的改掉上线,避免影响扩大

事后复盘

  • Leader: 咱能不能用啥工具避免此类问题发生
  • 菜鸡: 阿巴阿巴一堆话
  • Leader: 那行你说了这么多,这个任务就交给你了,下周给个方案
  • 菜鸡: ...哦豁,要被卷铺盖走人了

个人思考

在不完善的代码或有时效性的代码处搞一个flag,通过工具检测这个flag然后提醒此项目的所有开发者

开发者打开这个项目就能知道xx flag处有隐患

调研-可行方案

cli工具

项目启动后,跑一个npm script

扫描目标文件,然后通过正则匹配flag关键字基于提示

vs code插件

前端魔法师基本都用的Vs Code

项目启动后,自动扫描目标文件

通过正则匹配flag,然后基于提示

这个有现成的todo-tree

eslint规则

前端项目都会引入eslint 来统一团队成员的编码风格

只要编写一个自定义的规则,通过分析eslint 提供的AST,就能轻易的拿到标记的flag

对比

方案 优点 缺点 学习成本
cli工具 不限制语言/文件类型,通过正则就能进行轻易的匹配 需要为每个项目单独编写一个指令,通过hook或者人工触发
vs code插件 只需要用户为编辑器安装一个插件,即可在打开的时候进行提示 魔法师们可能用sublime/webStorm ,需要重复开发插件
eslint规则 只需要在项目的配置文件中加入一行规则代码,即可实时的提示 需要安装依赖,首次需要手动引入

最终选择

基于eslint编写一个eslint-plugin来实现自定义校验规则

原因

  • 前端项目都会引入eslint
  • 不限制编辑器
  • 理论上不限制前端开发常用的语言/框架的检测
  • 标准化团队统一了所有仓库的lint规则,只需在标准化规则中加入即可
    • 其余仓库,只要执行依赖安装(yarn/npm i) 就会将最新的规则引入项目,使用者能0配置接入

实现过程

下面会展开介绍插件的实现原理,会涉及到一些代码的展示

首先eslint会将响应的文件用AST描述出来,并且提供了一些简单的API进行操作

create(context) {
// 取得AST
const sourceCode = context.getSourceCode()
// 获取所有的注释节点
let comments = sourceCode.getAllComments()
}

注释节点有两种

// 注释
/**
* Block
**/

从注释节点中过滤出以flag关键字开始的注释节点

// 过滤出包含关键词的注释节点
comments = comments.filter(comment => {
let { value, type } = comment
// 展平块状注释
if (type === 'Block') {
value = value.replace(/\*|\n/g, '')
}
value = value.toLowerCase().trim()
// 保存格式化后的字符串
comment.newValue = value
for (const flag of dFlag) {
// 检测是否一关键字开头
if (value.startsWith(flag)) {
// 保存上flag
comment.flag = flag
return true
}
}
return false
})

此时过滤后的注释基本格式如下

// flag ddl:time  xxxxx

接下来从中解析出ddl和提示信息

首先ddl也是一个可配置的关键字

需要检测的日期格式如下

format1 demo format2 demo format3 demo
yyyy-mm-dd 2020-06-01 yyyy/mm/dd 2020/06/01 yyyymmdd 20200601
2020-06-1 2020/06/1 200601
2020-6-01 2020/6/01
20-06-01 20/06/01
20-6-1 20/6/1
20-6-01 20/6/01

所以代码中做了许多判断

  • 代码可能有点shi,看官们可以给点建议优化下
// 匹配日期的正则
const rDate = [{
reg: /((\d{4})|(\d{2}))(-((0\d)|(\d{2})|(\d{1}))){2}/,
flag: '-' // yyyy-mm-dd|yy-mm-dd
},
{
reg: /((\d{4})|(\d{2}))(\/((0\d)|(\d{2})|(\d{1}))){2}/,
flag: '/'// yyyy/mm/dd|yy/mm/dd
},
{
reg: /(\d{8})|(\d{6})/,
flag: 'number'// yyyymmdd|yymmdd
}]
/**
* 获取TODO注释中的DDL,是则返回日期值及其todo内容
* @param {String} value 待操作字符串
* @param {String[]} ddlSymbol 截止时间标识符
* @param {STring} todoSymbol
* @return {Object}
*/
function getDDLAndText(value, ddlSymbol, todoSymbol) {
let text = value.slice(value.indexOf(ddlSymbol) + ddlSymbol.length),
date = ''
for (const rdate of rDate) {
const { reg, flag } = rdate
const res = text.match(reg)
if (res) {
const [dateStr] = res
// 再次校验匹配的日期日期是否合法
if (reg.test(dateStr)) {
let year, month, day
if (flag !== 'number') {
let ymd = dateStr.split(flag)
ymd = ymd.map(v => {
return v.length === 1 ? `0${v}` : v
})
year = ymd[0]
month = ymd[1]
day = ymd[2]
} else {
const { length } = dateStr
day = dateStr.slice(length - 2)
month = dateStr.slice(length - 4, length - 2)
year = dateStr.slice(0, length - 4)
}
if (year.length === 2) {
year = new Date().getFullYear().toString().slice(0, 2) + year
}
text = text.slice(text.indexOf(dateStr) + dateStr.length)
date = `${year}-${month}-${day}`
// 日期不合格也pass掉
if (month > 12 || day > 31) {
date = ''
}
break
}
}
}
return {
text,
date
}
}

这样就拿到了flag的截止日期内容

接下来只需要根据设置的时间警戒线进行提示即可

// 未设置DDL或者DDL不合法情况
if (!date) {
errMsg = '没有设置有效的Deadline,设置方法(https://github.com/ATQQ/eslint-plugin-todo-ddl)'
} else {
const TODODate = new Date(date).getTime()
const interval = TODODate - Date.now()
// 如果已经到期
if (interval < 0 || interval < oneDay) {
errMsg = '已经过截止日期,请立即修改'
} else {
// 剩余天数(向下取整)
const theRestDays = ~~(interval / oneDay)
errMsg = theRestDays <= dWarnLine ? `还有${theRestDays}天截止,请尽快修改` : ''
}
}
if (errMsg) {
context.report({
node: comment,
message: `TODO WARN: ${errMsg} --> ${text}`
})
}

到此就大工搞成了,可以发包上线了

总结思考

不足之处

  1. 代码还有可改进之处
  2. 插件上线后并没大改过,功能感觉还可以增强,需要朋友们多给点意见

收获

  1. leader还是评价不错,毕竟那时候实习生身份刚入职几天
  2. 自己也有所提升,对eslint的工作原理有了新的认识
  3. 也为团队日后各种花式校验规则提供了技术积累

未来

  1. 找时间根据反馈,迭代一下插件,提高其可玩性

其它

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情

一款检测代码中TODO的eslint插件的更多相关文章

  1. vscode中检测代码中的空白行并去除的方法【转】

    按下ctrl+h键进行正则匹配:^\s*(?=\r?$)\n 然后直接替换,再看代码发现空行已经不见了.

  2. 检测浏览器中是否有Flash插件

    由于IE和非IE浏览器检测方式不同,所以代码如下 function hasPlugin(name){ debugger; name = name.toLowerCase(); for(var i=0; ...

  3. 【转】代码中特殊的注释技术——TODO、FIXME和XXX的用处

    (转自:http://blog.csdn.net/reille/article/details/7161942) 作者:reille 本博客网址:http://blog.csdn.net/reille ...

  4. 如何解救在异步Java代码中已检测的异常

    Java语言通过已检测异常语法所提供的静态异常检测功能非常实用,通过它程序开发人员可以用很便捷的方式表达复杂的程序流程. 实际上,如果某个函数预期将返回某种类型的数据,通过已检测异常,很容易就可以扩展 ...

  5. 代码中特殊的注释技术——TODO、FIXME和XXX的用处

    本文内容概要: 代码中特殊的注释技术--TODO.FIXME和XXX的用处. 前言:今天在阅读Qt  Creator的源代码时,发现一些注释中有FIXME英文单词,用英文词典居然查不到其意义!实际上, ...

  6. 带你开发一款给Apk中自动注入代码工具icodetools(完善篇)【申明:来源于网络】

    带你开发一款给Apk中自动注入代码工具icodetools(完善篇)[申明:来源于网络] 带你开发一款给Apk中自动注入代码工具icodetools(完善篇):http://blog.csdn.net ...

  7. 编程风格——代码中特殊的注释技术——TODO、FIXME和XXX的用处

    代码中特殊的注释技术——TODO.FIXME和XXX的用处 前言:今天在阅读Qt Creator的源代码时,发现一些注释中有FIXME英文单词,用英文词典居然查不到其意义!实际上,在阅读一些开源代码时 ...

  8. 代码中特殊的注释技术——TODO、FIXME和XXX的用处 (转载)

    转自:http://blog.csdn.net/reille/article/details/7161942 作者:reille 本博客网址:http://blog.csdn.net/reille/, ...

  9. 代码中特殊的注释技术——TODO、FIXME和XXX的用处 (转载)

    转自:http://blog.csdn.net/reille/article/details/7161942 作者:reille 本博客网址:http://blog.csdn.net/reille/, ...

随机推荐

  1. Dockfile搭建极简LNMP环境

    最近才发现ThinkPHP6.0和CI4.x都要求php版本为7.1以上了,本机的php版本还停留在7.0.3x,又懒得升级,于是考虑使用Docker来运行一个lnmp环境. 常规环境搭建的方式有两种 ...

  2. Linux下开发STM32单片机

    一开始学习51单片机就是用的MDK这个IDE软件,IDE软件虽然看起来直观好像更加容易入门(因为有界面看起来很形象),但是实际上IDE却是向我们这些入门人员隐藏了背后真实存在的过程,让我们以为编译就是 ...

  3. js 快速排序 All In One

    js 快速排序 All In One 快速排序 / Quick Sort "use strict"; /** * * @author xgqfrms * @license MIT ...

  4. How to implement an accurate countdown timer with js

    How to implement an accurate countdown timer with js 如何用 js 实现一个精确的倒计时器 原理剖析 web worker js custom ti ...

  5. vs code & macOS services

    vs code & macOS services Mac OS X, Open Folder With VS Code (right click) https://github.com/Mic ...

  6. CSS & SASS & SCSS & less

    CSS & SASS & SCSS & less less vs scss https://github.com/vecerek/less2sass/wiki/Less-vs. ...

  7. TypeScript 3.7 RC & Nullish Coalescing

    TypeScript 3.7 RC & Nullish Coalescing null, undefined default value https://devblogs.microsoft. ...

  8. Web Animations API & SVG & requestAnimationFrame

    Web Animations API WWAPI https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API https:/ ...

  9. NGK全球启动大会正式启动,资产上链的前景与机会在哪?

    据彭博社报道,加州时间11月25日,NGK全球启动大会在美国硅谷圆满落幕,本次NGK全球启动大会为NGK全球化进程正式拉开了帷幕. 众多业界人士共襄盛举,共同进行探讨未来公链发展的去向和契机. 当前, ...

  10. [C#] 尝鲜.net6.0的C#代码热重载

    看到.NET 6 Preview 1 发布,里面"除了 XAML 热重载之外,还将支持 C# 代码的热重载"一句,觉得有必要试试看,因为XAML热重载功能用起来确实很爽. 首先要下 ...