代码是写给人看的,所以一份好的代码,是要让水平不一的阅读者,都能够理解代码的本意。每个人的代码风格是不可能完全相同的,例如在一个文件里,有的以两个空格做缩进,有的以四个空格做缩进,有的使用下划线,有的使用驼峰,那么它的阅读体验就会变得很差。
所以如何来对代码进行约束,使团队的代码风格尽量统一,不产生更多的理解成本,是一个需要解决的问题。众所周知,懒是社会生产力进步的源动力,所以...
在前端工程化的标准中有一项就是自动化,自动化当中就包括了代码规范自动化。实现代码规范自动化可以解放团队生产力,提升团队生产效率,balabla... 所以 ESlint、TSLint、StyleLint这些工程化插件应运而生。
而最近在笔者团队也在统一不同的项目之间的规范差异,相信大家也都遇到了大段飘红的现象,今天咱来简单探究一下背后涉及到的原理。
首先,提到ESlint,应该会想到两种东西,一个是ESLint的npm包,也就是我们 devDep里面的, 另一个是我们所安装的比如VSCode的ESLint插件,那么这两个东西有什么联系呢。
Npm包:是实际的lint规则以及我们执行lint的时候,控制代码如何去进行格式化的。
Vscode插件: 实际指向我们项目的 /node_modules/eslint 或者全局的eslint,通过eslint的规则,告诉IDE,哪些地方需要飘红。也就是说插件是在解析我们的打开的文件,同时和规则对比,是否存在eslint问题。以及可以通过我们的IDE配置,在不同的时机去执行我们的lint,比如保存自动格式化。
总而言之, eslint 规则就是对我们的代码风格和代码中潜在的一些错误和不规范用法的一个约束,通过npm包的形式引入项目,同时通过IDE的插件,读取npm包规则,对我们的代码进行错误提示,
如何在项目配置ESlint就不在本文赘述了。大多数脚手架其实都会给你初始化好基本的ESlint。涉及到的工具不同可能会有些许的不一样,不过都大差不差。这段讲一下ESLint中的主要配置项。如果有兴趣深一步研究,可以移步eslint的官网文档 [1],对默认的规则集[2]感兴趣 也可以移步 。
打开一个eslintrc文件,一般来说,有几个选项。这里以json为例,来简单说明下每个字段。
https://mp.weixin.qq.com/s/wYYDG7yU9h3-6DBYTCkuiA
eslint支持以下几种格式的配置文件,如果同一个目录下有多个配置文件,ESLint 只会使用一个。优先级顺序如下:
.eslintrc.js
.eslintrc.yaml
.eslintrc.yml
.eslintrc.json
.eslintrc
package.json
同时 eslint 也支持对每个目录配置不一样的规则,对于mono仓库下,可能每个repo的eslint都有些许的区别,这个时候我们就可以采用下面的目录格式,根目录下存在基本规则,子app下存在特定的规则。子rc是对父rc的一个override,但是如果我们在app/.eslintrc.js中设置了root:true,那么对于test.js,父目录中rc使用的规则,在app中不会生效。
packages
├── package.json
├──.eslintrc.js
├── lib
│ └── test.js
└─┬ app
├── .eslintrc.js
└── test.js
他是为什么能够生效的。这里就要提到我们前端方方面面都要涉及到的AST了,感谢新时代。
ESLint是基于抽象语法树来进行工作的,ESLint默认使用的编译器(parse)是 Espree[3],通过它来解析我们的JS代码生成AST,基于AST,我们就可以对我们的代码进行检查和修改了。
通常我们的Babel编译分为下图这几步,编译/转换/生成。ESlint和它对比,只有第一步是一致的, 因为我们只需要拿到ast中的部分信息,同时直接在源码中进行提示和操作就行,并不需要transform和后续的生成代码。
现在我们通过demo来探究他背后的原理以及转换的方式。首先,我们需要加载和解析我们的源代码。这就是编译器将我们的代码转换成AST树的一个过程。因为已经全面拥抱typescript(主要是因为espree没有类型注解,我难受),所以本文使用 @typescript-eslint/parser
来作为我们的编译器。这里有个小坑,如果在VSCode安装了import cost插件的话,他去解析这个parser会特别卡,所以可以暂时禁用。
const foo = "anthony"
const bar = "dst"
import fs from 'fs';
import path from 'path';
import * as tsParser from '@typescript-eslint/parser';
const filePath = path.resolve('./src/test.ts')
const text = fs.readFileSync(filePath, "utf8")
// 编译成 AST 这里是不是和eslint的配置项对上了,没错就是透传而已
const ast = tsParser.parse(text,{
comment: true, // 创建包含所有注释的顶级注释数组
ecmaVersion: 6, // JS 版本
// // 指定其他语言功能,
// ecmaFeatures: {
// jsx: true, // 启用JSX解析
// globalReturn: true // 在全局范围内启用return(当sourceType为“commonjs”时自动设置为true)
// },
loc: true, // 将行/列位置信息附加到每个节点
range: true, // 将范围信息附加到每个节点
tokens: true // 创建包含所有标记的顶级标记数组
})
然后我们将获得的ast打印一下,简单从下图可以看到主要包含的内容。本地打印出来可能不太方便阅读,也可以使用在线的工具[4],将解析器设置为@typescript-eslint/parser
。相对于espree来说,ts解析多出来的部分中,比较关键的就是右图这段,决定我们如何去解析他的类型。
AST 就是记录了读取源文件之后的文本内容的各个单位的位置信息,这样我们就可以通过操作 AST 修改需要修改的内容,然后再根据修改后的 AST 信息进行修改对应的文本内容。比如我们把上文中的 const
关键字修改成 let
,那么我们就先对 AST 对应的const
内容进行修改为 let
,得到修改之后的 AST 数据,再根据修改后的 AST 数据去修改对应的文本内容。所谓的修改就是字符串替换,因为我们已经知道了对应的位置信息。
但是根据上面我们可以看到,直接根据ast去查找然后比对替换,效率是很低的,而且嵌套比较深。这个时候ESlint是怎么干的呢?他生成了一个新的结构用于我们操作,也就是SourceCode
。有兴趣进一步探究可以自行查阅源码的 sourcecode/source_code.js
部分。简单来说,就是构建了一个SourceCode实例,接受两个参数,原文text 和解析后的ast,然后返回我们一个包含茫茫多方法的实例对象。
我们在demo项目中装一个eslint 然后引入SourceCode,看看构造后的对象是个什么玩意。
import { SourceCode } from 'eslint';
// ....
//
const sourceCode = new SourceCode(text,ast);
// 这打个断点,看看sourceCode结构
__proto__
上的方法,完整属性可以查阅官网[5]/源码/类型注解。getText(node?: ESTree.Node, beforeCount?: number, afterCount?: number): string;
isSpaceBetweenTokens(first: AST.Token, second: AST.Token): boolean;
两个token间是否有空格。好了 前置的一些知识我们已经介绍的差不多了。接下来 结合实际的rules demo来进行讲解。
相信如果有写过vscode插件的同学应该对 Yeoman 不陌生,eslint也有提供基于Yeoman的一套脚手架用于生成模版。
首先全局安装eslint 的脚手架,npm install -g yo generator-eslint
,然后通过下面的一些交互式命令行操作来初始化我们的操作。
通过初始化,我们可以看到一个以下的文件的壳子,我们在里面添加一些我们上面所讲到的东西。打开我们生成的规则模版文件,同时在里面添加一些规则和提示(注意,这里我的写法不规范,我将两种无关规则放在了一个规则文件里)。
"use strict";
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem', // `problem`, `suggestion`, or `layout`
docs: {
description: "xxxx",
recommended: false,
url: null, // URL to the documentation page for this rule
},
messages:{
temp:'不样你用字面量作为函数的参数传入',
novar: '不样你用var声明',
noExport: '退出时执行这个'
},
fixable: 'code', // Or `code` or `whitespace`
schema: [], // Add a schema if the rule has options
},
create(context) {
// variables should be defined here
const sourceCode = context.getSourceCode();
return {
ArrowFunctionExpression:(node)=>{
if(node.callee.name !== 'abcd') return;
if(!node.arguments) return;
node.arguments.forEach((argNode,index)=>{
argNode.type === "Literal" && context.report({
node,
messageId: 'temp',
fix(fixer){
const val = argNode.value;
const statementString = `const val${index} = ${val}\n`;
return [
fixer.replaceTextRange(node.arguments[index].range, `val${index}`),
fixer.insertTextBeforeRange(node.range, statementString)
]
}
})
})
},
"Program:exit"(node) {
context.report({
node,
messageId: "noExport",
});
},
VariableDeclaration(node){
if(node.kind === 'var') {
context.report({
node,
messageId: 'novar',
fix(fixer) {
const varToken = sourceCode.getFirstToken(node)
return fixer.replaceText(varToken, 'let')
}
})
}
}
};
},
};
在这个demo里面,我们看到几个东西,一个是create函数的参数 context
以及他的返回值,还有就是context
上提供的report
方法 以及report接受的fix
参数。这几个加起来,形成了我们一条规则的校验逻辑,通过遍历,我们到了某个ast节点,如果某个ast节点满足了我们所写的某条规则,我们进行report,同时提供一个修复函数,修复函数通过token或者range来决定对某处进行文本替换。
接下来,挨个来讲解这些东西,首先是context的上下文形成,这个没有什么好说的,其实就是创建了一个对象,然后提供了一些一些方法,供我们在插件中访问上下文使用,然后对于每个rule都在createRuleListener中都创建了一个listener,这里我们在后面串整体流程时还会再过一遍。
接着是report 方法,简单分析下这块代码,其实就是通过一系列的操作,然后往lintingProblem这个数组里面推了一个problem。这个problem包含一些错误信息,ast信息等等。
最后是我们的fix,我们上面用到的所有replace方法,其实都殊途同归,最后回到了这里,大道至简,简单的slice和+=完成了我们的修复动作。
基本上一个插件涉及到的核心几个东西,都简单解释了下。现在我们来串一串整体检测和修复的流程,也就是源码中linter.js
中的runRules
方法。
我们在跑规则的时候,肯定需要的是对ast进行遍历,同时做一些操作。首先做了一个什么操作呢,调用了一个实例方法Traverser.traverse
,传入了ast
和一个对象,包含enter
、leave
和visitorKeys
。这个函数的作用就是进行一个递归遍历,同时在遍历的时候通过enter和leave我们在队列中存储了两个相同的节点,一个是进入时,一个是退出时,方便我们后续处理。这里涉及到一个设计模式,访问者模式(用于数据和操作解耦),通过在遍历时加上isEntering,可以让我们决定是在进入时还是退出时执行访问者逻辑。
接着我们需要把我们的所有规则都给像上面讲的给创建成ruleListener,然后在我们的nodeQueue后续遍历时,触发某些逻辑。当然,这里大家可能都想到了订阅发布模式,这个也是在我们整个逻辑中比较重要的一环,遍历时,通过emit推送消息,然后让ruleListener决定是否需要执行某些逻辑,所以,我们需要对Listener订阅上某些事件。
接下来,就是对我们的nodeQueue遍历了,通过我们节点上打上的标,来决定是在执行进入逻辑还是离开逻辑。这里我就不展开讲具体的细节了,其实简单理解就是通过enter和leave的时候去触发不同的visitor的动作。
限制于时间因素,本文成文比较仓促,可能会有一些知识点的缺失或者不对,敬请大家斧正。同时本文仅是初步的探索了其背后的原理,根据原理,后续可以做的一些例如eslint插件等等并没有详细的阐述。大家下来可以自行探索。
最后送大家一句话,linus 说的,也是我比较信奉的一句话。talk is cheap, show me the code,想了解一个东西,最好的办法就是简单实现它。我相信大家在解析完它的流程后,都能够简单实现一个eslint的小demo,以及能够上手写一写eslint-plugin。
[1]官网文档 : https://eslint.org/docs/latest/user-guide/configuring/configuration-files
[2]默认的规则集: https://eslint.org/docs/latest/rules/
[3]Espree: https://github.com/eslint/espree
[4]在线的工具: https://astexplorer.net/
[5]官网: https://eslint.org/docs/latest/developer-guide/working-with-rules
[6]unicode bom: https://en.wikipedia.org/wiki/Byte_order_mark
本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/wYYDG7yU9h3-6DBYTCkuiA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。