关于制定eslint规则
制定eslint规则
前端的日常发展离不开各种皮棉的支撑。使用lint的一个误区是个人能力不足,编写标准化代码需要lint规范。其实规范的定义主要取决于开源项目作者的习惯或者公司团队的编码习惯。甚至两个前端专家也会写出不同的代码规范。
今天的话题讲的是eslint,它作为最主流的JavaScript lint工具,深受大家的喜爱,但是JSHint已经逐渐淡出了大家的视线,使用的也越来越少
常见的eslint扩展包括standard、airbnb等
分析eslint扩展
扩张无非是做两件事
在原有eslint的基础上,配置一些配置(具体的规则参数、全局变量、运行环境等。)并定制一些自己的规则来满足需求。原理是使用eslint的继承模式,理论上可以无限继承和覆盖上一级的规则
第一个没有详细介绍。官网说得很详细。基本上,每个规则都支持用户定义的参数,并且覆盖范围很广。基本上,所有的语法都有规则。
第二个定制规则是本文的亮点,因为特殊的业务场景已经不能满足eslint自身配置的业务需求,比如:
ESL int-plugin-vue ESL int-plugin-react ESL int-plugin-jest针对一般特殊场景的自定义规则以eslint-plugin-*命名,使用时可以轻松编写。
{plugins : ['vue ',' react ',' jest']}当然eslint-config-*也是一样的,但是配置的时候需要写
{ extends: ' standard ' }以下描述了开发过程
创建eslint插件项目
官方建议使用yeoman来生成项目。我觉得生成的项目比较老套。我建议习惯我的项目结构
ESL int-plugin-skr |-_ _ tests _ _ | |-rules | |-utils | |-lib | |-rules | |-utils | |-index . js | |-jest . config . js | |-package . JSON | |-readme . MD发现整体上jest配置文件比较多。是的,yeoman生成的项目默认使用Mocha作为测试框架。个人觉得调试起来比较麻烦,但不如jest灵活,vscode可以轻松调试。
搜了很多教程,给手游党一个链接调试-jest-tests
关于jest的配置文件也已发布。都是基本配置,比较复杂,不需要。下面将详细介绍测试部分
module . exports={ testenvironments : ' node ',roots: ['_ _ tests _ _'],resetmocks 3360 true,clearmocks 3360 true,verbose 3360 true}所有自定义规则都在lib/rules下,每个规则都有一个单独的文件就足够了
以下是打通任督二脉的一个简单例子
制定规则
初步准备
官方开发文档AST抽象语法树,一个官方文档,写得密密麻麻,有几十个属性。其实只是冰山一角,还有很多复杂的场景需要考虑
有人质疑:一定要精通AST吗?
我的答案当然是否定的,但是一个简单的理解是至少知道解析后的语法树的一般结构是什么样子的
那就给自己写个命题吧!写一个超级简单的
模块。导出={ meta : { docs : { description : '禁止块级注释',category: '文体问题',建议: true}},创建(上下文){ const source code=context . getsource code()返回{ Program(){ const comments=source code . getallcomments()(const blockComments=comments . filter({ type })=type===' Block comments ' . length context . report({ message : '无块注释' })这个例子也很简单,调用环境变量上下文中的方法来获取所有的注释
稍微复杂一点的场景
要要求lint bar对象中属性的顺序,假设规则如下
//goodconst bar={meta: {},double : num=num * 2 }//bed const bar={ double 3360 num=num * 2,meta3360 {},}这第一次有点混乱。官网没有提供具体的例子。解决方法很简单。我们推荐一种锋利的武器作为探索者。
点击进入,不要着急复制代码查看AST结果。首先选择espree(eslint使用的eslint解析库),如下所示
这短短的四行代码会对应着一个抽象语法树,如下图:
由于全展开太长了哈,感兴趣的自行尝试,会发现层级嵌套的特别深,找到酒吧的属性需要Program.body[0].声明[0].init.properties
当然不至于每次都从最顶级的程序找下来,从上面的例子可以看出创造方法的返回返回的是一个对象,里面可以定义很多检测类型,如官网的例子:
函数checkLastSegment(节点){ //如果最后一个代码路径段是可到达的,则报告函数的问题} module.exports={ meta: {.},create:函数(上下文){ //在ReturnStatement:函数(节点)处声明规则返回的状态{ //在return语句节点处向下)},//在函数表达式节点处向上: '函数表达式3360退出' : checklast段',箭头函数表达式3360退出' 3: checklast段,onCodePathStart:函数(代码路径,节点){ //在分析代码路径开始时)},这里可以使用变量选择器类型作为检察目标,从下面的解析树可以分析出筛选条件
以变量选择器对象作为当前的结节
当变量名为酒吧,即node.id.name==='bar ',且值为对象,即节点。初始化。type==='对象表达式',代码如下:
module.exports={ meta: {.},create(context){ return { VariableDeclarator(node){ const isBarObj=node。身份证。name==' bar '节点。初始化。type==' object expression ' if(!isBarObj)返回//checker } } }}就这样成功取到酒吧对象后就可以检测属性的顺序了,排序算法一大把,挑一个喜欢的用就行了,这里不啰嗦了,直接上结果:
const ORDER=['meta ',' double ']函数getOrderMap(){ const ORder Map=new Map()ORder。foreach((name,i)={ orderMap.set(name,i) })返回orderMap }模块。导出={ create(context){ const ORder Map=GetOrdermap()}函数检查ORder(属性节点){ const properties=properties节点.筛选器(属性=Property . type===“Property”).map(property=property。键)属性。foreach((property,I)={ const properties upper=properties。切片(0,1)常量未定义属性=属性上限.筛选器(p=顺序图。获取(p . name)订单图。get(property。姓名).排序((p1,p2)=顺序图。得到(P1。名称)顺序图。get(p2。name))const first unrudered property=unrudered property[0]if(first unrudered property){ const line=first unrudered property。锁定。开始吧。行上下文。报告({ node :属性,message : ` ` ` { name } '属性应该在第{{line}}行的{ firstUnorderedPropertyName } '属性之上`,数据` isBarObj)返回CheckOrder(节点。初始化。属性)} } }这里代码有点多,耐心看完其实挺简单的,大致解释下
getOrderMap方法将数组转成地图类型,方面通过得到获取下标,这里也可以处理多纬数组,例如两个键希望在相同的排序等级,不分上下,可以写成:
const order=[ 'meta' ['double ',' treble ']]函数getOrderMap(){ const orderMap=new Map()order。foreach((name,I)={ if(array。isarray(property)){ property。foreach(p=orderMap)。set(p,i)) } else { orderMap.set(property,i) })返回orderMap}这样两倍和三倍就拥有相同的等级了,方便后面扩展,当然实际情况会有n个属性的排序规则,也可以在这个规则上轻松扩展,内部的分类逻辑就不赘述了。
开发就介绍到这里,通过上面安利的在线语法解析工具可以轻松反推出线头逻辑。
如果规则比较复杂,就需要大量的utils支持,否则每一条规则都会显得一团糟,这就考验了公共代码抽取的能力
试验
如前所述,建议使用jest进行测试。这里的测试与普通的单元测试有很大的不同。eslint是一个基于结果的测试。你什么意思?
Lint只有两种情况,通过和失败,只是把通过和失败的情况整理成两个数组,剩下的交给eslint的RuleTester
上面的属性排序规则,测试如下:
const RuleTester=require(' ESL int ')。RuleTesterconst规则=require('././lib/rules/test)const rule tester=new rule tester({ parserroptions : { ecmaversi : 6 } })rule tester . run(' test rule ',rule,{ valid :[` const bar={ meta : } },double: num=num * 2 }` ],invalid :[{ code : ` const bar={ double 3: num=num * 2,meta : } }}]})有效的是你想传递的代码,无效的是你不想传递的代码和错误消息,所以一个规则就真的在这里完成了。
打包输出
最后,需要将编写好的规则发送到npm包中,以便在项目中轻松使用,因此我不会详细讨论如何发布规则,而只是简单地讨论如何优雅地导出规则。
直接编码:
const requireindex=require(' require index ')//导入lib/rulesmodule . exports . rules=require index(`$ { _ _ dirname }/rules `)这里使用了三方依赖关系require index,这对于批量导出文件夹中的所有文件来说要简单得多。
当然,前提是确保rules文件夹中充满了规则文件,并且不要在其中写入utils
摘要
本文的目的是国内外与定制eslint规则相关的资源很少,所以希望分享一些编写定制规则的经验。
不要浪费时间学习AST。不同的库有不同的AST实现。下次编写babel插件时,应该学习其他AST规则。再一次安利AST神器as explorer,只需要把需要验证的代码放在as explorer中再次运行,然后总结规则。逻辑其实很简单,可以判断AST结果。
从团队层面来说,希望所有团队都有自己的eslint规则库,这样可以大大降低代码评审的成本,保证代码的一致性,一劳永逸。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。
版权声明:关于制定eslint规则是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。