Eslint 可以检查出代码中的错误和一些格式问题,并能自动修复,它的实现原理就是基于 AST (抽象语法树)。

通过 Parser 把源码解析成 AST 对象树,源码字符串中的各种信息就被保存到了这个对象树里,然后遍历 AST,对每一部分做检查就能实现 Lint 的功能,而自动 fix 的功能则是基于字符串替换实现的,指定某一段 range,替换成另一段文本即可。

说起来,Babel 也是基于 AST 实现的代码分析和转换,但是却不能检查和修复格式的问题,这是为什么呢?为什么 Eslint 可以检查格式而 Babel 不可以呢?

我们先写一个 Eslint 的 rule 来感受下 Eslint 是怎么检查和修复格式问题的。

Eslint 检查格式的 rule

大括号有两种主流写法,一种是同一行写:

if (name === 'guang') {
}

另一种是新开一行写:

if (name === 'guang')
{
}

我们写一个 eslint 的 rule 来检查大括号的格式并自动修复成同一行的格式。

思路分析

Eslint 的检查是基于 AST,我们要检查的 AST 是块语句 BlockStatement。

关于什么代码是什么 AST 可以用 astexplorer.net 可视化的查看,parser 选择 espree (Eslint 默认的 parser):

具体来说检查的是 BlockStatement 的最开始的 token { 的行号,是不是和它前一个 token 在同一行。(token 是指最小的不可再细分的单词,比如关键字、变量名等标识符、各种分隔符等)

如果是同一行,则说明了是符合规范的。当然我们还可以进一步检查一下大括号 { 和前一个 token 之间有没有空格。

思路理清了,我们来写下代码:

代码实现

Eslint 的 rule 的格式是这样的:

module.exports = {meta: {docs: {description: "enforce consistent brace style for blocks"},fixable: true},create(context) {return {BlockStatement(node) {}}}};

分为 meta 和 create 两部分:

  • meta:描述各种元信息,比如文档描述、是否可自动修复等

  • create:实现对各种 AST 检查的代码,rule 的主体部分

我们在 create 里声明了对 BlockStatement 节点的检查,它的参数就是对应的节点对象。

但是我们要检查的是 token,这个用 context 里的一个 api:

create(context) {const sourceCode = context.getSourceCode();return {BlockStatement(node) {const firstToken = sourceCode.getFirstToken(node);const beforFirstToken = sourceCode.getTokenBefore(node);}}
}

我们从 context 中拿到了 sourceCode 的 api,它就是用来取 token 的。

我们用 getFirstToken 拿到了当前 node 的开始 token,也就是 {,

然后又拿到了当前 node 的前面一个节点的 token,也就是 ):

token 中保存了行列号信息,那么对比下行列号就知道是否有格式问题了:

如果 { 所在的行和 ) 所在的行不是同一行,就报错

if (firstToken.loc.start.line !== beforFirstToken.loc.start.line) {context.report({node,loc: firstToken.loc,message: '大括号格式不对'});
}

修复的方式自然就是把 { 和 ) 之间的部分替换成一个空格,这个使用 fixer 提供的 api:replaceTextRange:

if (firstToken.loc.start.line !== beforFirstToken.loc.start.line) {context.report({node,loc: firstToken.loc,message: '大括号格式不对'fix: fixer => {return fixer.replaceTextRange([beforFirstToken.range[1], firstToken.range[0]], ' ');}});
}

同理,也可以检查出 { 和 ) 之间没有空格的格式错误,修复方式一样:

if (firstToken.loc.start.column === beforFirstToken.loc.start.column + 1){context.report({node,loc: firstToken.loc,message: '大括号前缺少空格',fix: fixer => {return fixer.replaceTextRange([beforFirstToken.range[1], firstToken.range[0]], ' ');}});
}

这样就实现了大括号格式的检查和自动修复。

我们来试下效果:

测试 rule

Eslint 除了提供命令行外,也提供了 api,我们调用它的 api 来测试 rule:

先创建 ESLint 对象,指定 rulePaths 也就是查找 rule 的目录为当前目录:

const { ESLint } = require("eslint");const engine = new ESLint({fix: false,overrideConfig: {parserOptions: {ecmaVersion: 6,},rules: {'my-brace-style': ['error']}},rulePaths: [__dirname],useEslintrc: false
});

useEslintrc 为 false 是不查找配置文件, fix 为 false 是不自动修复。

然后调用它的 lintText 代码来测试,返回的结果使用 formatter 打印:

(async function main() {const results = await engine.lintText(`if (print) {const num = a + b;console.log(num);}for(let i = 0;i<100;i++){console.log(i);}`);console.log(results[0].output);const formatter = await engine.loadFormatter("stylish");const resultText = formatter.format(results);console.log(resultText);})();

我们来试一下效果:

三处格式错误都检查出来了!

然后把 fix 设为 true,来测试下自动修复:

格式自动修复了!

这样我们就通过 Eslint 的 rule 实现了代码格式的检查和自动修复。

代码上传到了 github:https://github.com/QuarkGluonPlasma/eslint-plugin-exercize

那么再回到最开始的问题,为什么 Eslint 可以检查代码格式,而 Babel 不可以呢?

为什么 Eslint 可以检查格式 Babel 不可以

我们写了一个检查大括号格式的 rule,可以发现能够做格式检查关键是能找到关联的 token。

Eslint 的 AST 记录了所有的 token,token 中有行列号信息,而且 AST 中也保存了 range,也就是当前节点的开始结束位置。并且还提供了 SourceCode 的 api 可以根据 range 去查询 token。这是它能实现格式检查的原因。

而 Babel 其实也支持 range 和 token,但是却没有提供根据 range 查询 token 的 api,这是它不能做格式检查的原因。

其实 Babel 和 Eslint 原理差不多,但是 Eslint 是被设计来做代码错误和格式检查与修复的,而 Babel 是被设计用来做代码分析和转换的,目的不同,所以也就提供了不同的 api,能够做不同的事情。

总结

Eslint 是用来检查代码中的错误和格式问题的,基于 AST,Babel 也是基于 AST 做的代码分析和转换,但是却不能检查格式。

为了探究原因,我们写了一个 EsLint 的检查大括号格式的 rule,通过 SourceCode 的 api 拿到 { 和 ) 的 token,对比下行列号来做检查。并且通过 fixer 的字符串替换做了自动修复。

写完之后,我们发现 EsLint 能做格式检查的原因是因为 AST 中记录了 range,也保留了 token信息,并且提供了根据 range 查询 token 的 api,而 Babel 没有。

EsLint 和 Babel 原理大同小异,但是有不同的设计目的,所以提供了不同的 api,有着不同的功能。

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

为什么 Eslint 可以检查和修复格式问题,而 Babel 不可以?相关推荐

  1. mysql检查文件_如何检查和修复MySQL数据文件?

    如何检查和修复MySQL数据文件?今天服务器突然宕机! 郁闷,这家伙又跟我找麻烦!让IDC数据中心重启以后,发现游戏服务器程序竟然无法启动了! 我晕!赶紧查看日志!原来mysql某个表竟然无法读取 0 ...

  2. 按照eslint 规则一键修复

    有时候虽然我们知道怎么去处理eslint规则所报的错误,但是如果项目过大,文件过多,未免会有些不耐烦.如果这时我们能够按照eslint规则一键修复,那简直不要太爽了啊.接下来我就说下怎么解决这个问题 ...

  3. (26)ESLint一JS代码格式校验

    一.什么是代码格式 代码格式即为代码风格,每个程序员再开发的时候,书写代码的风格都是不一样的,比如说,有的人喜欢书写字符串时用双引号,有的喜欢用单引号,有的再书写标签代码缩进时,喜欢用2个空格,有的喜 ...

  4. 在Linux中使用fsck命令检查和修复文件系统错误

    有几种情况下,您可能需要使用fsck.如果您的系统无法启动,设备(外部驱动器或存储介质)无法正常运行,或者您看到了文件损坏的证据,您会想要运行此命令. Fsck实际上是一个 "前端" ...

  5. hbck源码系列(四)--表的完整性检查和修复Check

    一,完整性检查 1检查表在HDFS的完整性 二.checkRegionChain方法 完整性检查,主要检查工作在checkRegionChain方法,该方法主要两个作用: 1.根据检查条件,检查异常 ...

  6. oracle收回dba权限后的检查,Oracle RAC GI 权限 检查和修复 方法

    Oracle RAC 环境的权限是比较复杂的,如果误操作导致了相关目录或者文件权限不正确就会影响到GI的运行,比如常见的crsctl 资源显示为:UNKNOWN,或者通过srvctl 无法控制资源,只 ...

  7. tips:webstorm 使用eslint快捷键 快速修复文件里的错误

    每个项目产品都要加埋点,加500行埋点是不是会占用你一两天的时间而且很容易犯错,想只用一小时准确加完这500行埋点剩下一天喝茶聊天么?来试试这520工具,高效加埋点,目前我们公司100号前端都在用,因 ...

  8. 更新版-梳理前端开发使用eslint和prettier来检查和格式化代码问题

    更新版,之前的版本可以看这里:梳理前端开发使用eslint和prettier来检查和格式化代码问题 一.问题痛点 在团队的项目开发过程中,代码维护所占的时间比重往往大于新功能的开发.因此编写符合团队编 ...

  9. 怎样检查一张 SIMATIC 存储卡(SMC)有非一致性或者是格式错误?如何修复?

    描述 SIMATIC 存储卡(SMC)使用的是特殊的 FAT32 文件系统,且能够在 Windows 系统中使 - 描述 SIMATIC 存储卡(SMC)使用的是特殊的 FAT32 文件系统,且能够在 ...

最新文章

  1. vb.net 设置打印纸张与页边距_文字办公—Word文档如何设置装订线
  2. c语言如何实现不定参数,C语言中不定参数的实现
  3. python 调用c++库接口出错
  4. 【2017年第1期】金融大数据标准规范体系比较研究
  5. openmvide使用需要什么插件_在Django中使用Webpack:再也不需要插件了!
  6. 【招聘内推】猎聘网招聘推荐算法工程师
  7. volatile 变量
  8. 迅捷CAD格式转换器专业版
  9. html5 磁力链播放器,磁力链接播放器
  10. Linux下批量tiff转pdf
  11. 阿里云对象存储OSS简介
  12. 关于Label Bais 最好的解释方式
  13. 汉诺塔python非递归实现,[Python3 练习] 006 汉诺塔2 非递归解法
  14. 云计算与大数据系列:1分钟看懂弹性计算云EC2的关键技术
  15. 网页设计与制作(五)
  16. Golang-RSA加密解密-数据无大小限制(gophp)
  17. 路由器wifi密码设置
  18. dialog设置最大高度占比
  19. Python定时任务库schedule的使用
  20. windows输入模式切换

热门文章

  1. 岁月的剪影【九月点灯前行】
  2. 【阿尼亚喜欢BigData】“红亚杯”大数据环境搭建与数据采集技能线上专题赛——满分解析⑤
  3. 2008春节值得回忆的琐记
  4. 梁静茹正式出嫁 说“我愿意”热泪盈眶
  5. 骑砍战团如何修改服务器设置,骑马与砍杀:战团修改领主关系秘籍
  6. 黑客、骇客、红客、蓝客、飞客是什么?有什么区别?
  7. Android中的状态保存-SharedPreferences和Bundle(文末小彩蛋)
  8. 42步进电机控制入门
  9. c语言 不用时间 怎么随机数,C语言时间与随机数问题
  10. 程序员要注意身体健康