当前位置: 首页 > 科技观察

为什么Eslint可以检查和修复格式问题,而Babel

时间:2023-03-15 00:02:17 科技观察

Eslint可以检查代码中的错误和一些格式问题,并可以自动修复它们。其实现原理基于AST(抽象语法树)。将源代码通过Parser解析成一个AST对象树,将源代码字符串中的各种信息保存在这个对象树中,然后遍历AST对每一部分进行检查,实现Lint的功能,而自动fix是基于字符串替换实现的,只需要指定一定的范围,用另外的文本替换即可。说起来,Babel也是基于AST的代码分析和转换,但是它不能检查和修复格式问题。为什么?为什么Eslint可以检查格式,而Babel不能?我们先写一个Eslint规则,感受一下Eslint是如何检查和修复格式问题的。Eslint校验格式中规则大括号的写法主要有两种,一种写在同一行:if(name==='guang'){}另一种写在换行:if(name==='guang'){}我们写一个eslint规则来检查花括号的格式并自动将它们固定为相同的行格式。思路分析Eslint的检查是基于AST的,而我们要检查的AST是块语句BlockStatement。astexplorer.net可以直观的查看是什么代码,什么AST,解析器选择espree(Eslint默认的解析器):具体检查BlockStatement的初始token{的行号是否和它前面的token一致同一行。(token是指不能再细分的最小词,如关键字、变量名和其他标识符、各种分隔符等)如果它们在同一行,则表示它们符合规范。当然我们可以进一步检查大括号{和前面的token之间是否有空格。思路清晰了,来写代码吧:实现Eslint规则的代码格式如下:真},创建(上下文){返回{BlockStatement(节点){}}}};分为meta和create两部分:meta:描述各种meta信息,比如文档描述,是否可以自动修复等create:实现各种AST校验在规则的代码中,规则的主体部分我们在create中声明BlockStatement节点的校验,其参数为对应的节点对象。但是我们要检查的是令牌,它在上下文中使用API:create(context){constsourceCode=context.getSourceCode();返回{BlockStatement(node){constfirstToken=sourceCode.getFirstToken(node);constbeforFirstToken=sourceCode.getTokenBefore(node);}}}我们从context中获取了sourceCodeapi,用于获取token。我们使用getFirstToken获取当前节点的起始token,即{,然后获取当前节点的前一个节点的token,即(:token中保存的是行列号信息,然后比较行列号知道是否有格式问题:如果{所在的行和)所在的行不是同一行,if(firstToken.loc.start.line!==beforFirstToken.loc.start.line){context.report({node,loc:firstToken.loc,message:'大括号的格式不正确'});}修复的方法自然是把{之间的部分替换掉和)有一个空格。这使用修复程序提供的api:replaceTextRange:if(firstToken.loc.start.line!==beforFirstToken.loc.start.line){context.report({node,loc:firstToken.loc,message:'Thecurlybraceformatisincorrect'fix:fixer=>{returnfixer.replaceTextRange([beforFirstToken.range[1],firstToken.range[0]],'');}});}同样也可以查看格式{和)之间没有空格的错误),并以相同的方式修复它:loc,message:'大括号前缺少空格',fix:fixer=>{returnfixer.replaceTextRange([beforFirstToken.range[1],firstToken.range[0]],'');}});}这样就实现了大括号格式的检查和自动修复来试试效果吧:testruleEslint除了提供命令行之外,还提供了一个api,我们调用它的api来测试规则:首先创建一个ESLint对象,指定rulePaths,即找到rule的目录为当前目录:const{ESLint}=require("eslint");constengine=newESLint({fix:false,overrideConfig:{parserOptions:{ecmaVersion:6,},rules:{'my-brace-style':['error']}},rulePaths:[__dirname],useEslintrc:错误的});useEslintrc为false为不搜索配置文件,fix为False不自动修复。然后调用它的lintText代码进行测试,使用formatter打印返回结果:);}for(leti=0;i<100;i++){console.log(i);}`);console.log(results[0].output);constformatter=awaitengine.loadFormatter("时尚");constresultText=formatter.format(results);console.log(resultText);})();试试效果吧:三个格式错误都检查过了!然后将fix设置为true来测试自动Repair:格式自动修复!这样我们就可以通过Eslint的规则来检查和自动修复代码格式了。代码上传到github:https://github.com/QuarkGluonPlasma/eslint-plugin-exercize那么回到最初的问题,为什么Eslint可以检查代码格式,而Babel不能呢?为什么Eslint可以检查格式而Babel不能我们写了一个检查大括号格式的规则,我们可以发现检查格式的关键是找到关联的令牌。Eslint的AST记录了所有的token,token中包含行列号信息,AST中也保存了range,也就是当前节点的起始位置和结束位置。并且还提供了SourceCodeapi,可以根据范围查询token。这就是它进行格式检查的原因。其实Babel也支持range和token,只是没有提供基于range查询token的API,所以不能做格式检查。其实Babel和Eslint的原理是差不多的,只是Eslint是做代码错误和格式检查修复的,而Babel是做代码分析和转换的,目的不同,所以提供了不同的API,可以做事情不同。总结Eslint用于检查代码中的错误和格式问题。基于AST,Babel也是基于AST进行代码解析和转换,但是不能检查格式。为了探究原因,我们写了一个EsLint规则来检查花括号的格式,通过SourceCodeapi获取{和(的token,比较行列号来检查。并通过fixer的字符串替换,自动修复Write完成后,我们发现EsLint之所以可以检查格式,是因为AST中记录了range,同时也保留了token信息,它提供了一个查询token的api,基于范围,而Babel则没有。EsLint和Babel的原理是相似的,但是有不同的设计目的,不同的API提供了不同的功能。