范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

ESLint机制分析与简单插件实践

  作者:Ye Peng前言
  代码是写给人看的,所以一份好的代码,是要让水平不一的阅读者,都能够理解代码的本意。每个人的代码风格是不可能完全相同的,例如在一个文件里,有的以两个空格做缩进,有的以四个空格做缩进,有的使用下划线,有的使用驼峰,那么它的阅读体验就会变得很差。
  所以如何来对代码进行约束,使团队的代码风格尽量统一,不产生更多的理解成本,是一个需要解决的问题。众所周知, 懒是社会生产力进步的源动力,所以...
  在前端工程化的标准中有一项就是自动化,自动化当中就包括了代码规范自动化。实现代码规范自动化可以解放团队生产力,提升团队生产效率...所以 ESLint、TSLint、StyleLint 这些工程化插件应运而生。
  而最近在笔者团队也在统一不同的项目之间的规范差异,相信大家也都遇到了大段飘红的现象,今天咱来简单探究一下背后涉及到的原理。 What is ESLint/Lint?
  首先,提到 ESLint,应该会想到两种东西,一个是 ESLint 的 npm 包,也就是我们 devDep 里面的, 另一个是我们所安装的比如 VSCode 的 ESLint 插件,那么这两个东西有什么联系呢。
  npm 包:是实际的 Lint 规则以及我们执行 Lint 的时候,控制代码如何去进行格式化的。
  VSCode 插件: 实际指向我们项目的 /node_modules/eslint 或者全局的 eslint,通过 ESLint 的规则,告诉 IDE,哪些地方需要飘红。也就是说插件是在解析我们的打开的文件,同时和规则对比,是否存在 ESLint 问题。 以及可以通过我们的 IDE 配置,在不同的时机去执行我们的 Lint,比如保存自动格式化。
  总而言之,ESLint 规则就是对我们的代码风格和代码中潜在的一些错误和不规范用法的一个约束,通过 npm 包的形式引入项目,同时通过 IDE 的插件,读取 npm 包规则,对我们的代码进行错误提示。 How to Use it?
  如何在项目配置 ESLint 就不在本文赘述了。 大多数脚手架其实都会给你初始化好基本的 ESLint。 涉及到的工具不同可能会有些许的不一样,不过都大差不差。这段讲一下 ESLint 中的主要配置项。 如果有兴趣深一步研究,可以移步  ESLint 的官网文档 [1] ,对 ESLint 默认的规则集[2] 感兴趣也可移步官网文档一同查看。
  打开一个 eslintrc 文件,一般来说,有几个选项。这里以 json 为例,来简单说明下每个字段。 {     "extends": "", // 规则集继承自某个规则集     "root": "true", // 找到这后,不再向上级目录寻找     // 解析选项     "parserOptions": {         "ecmaVersion": 6, //  指定你想要使用的 ECMAScript 版本 3/5/6/7/8/9         "sourceType": "module", // "script"(default) or "module",标明你的代码是模块还是script         "ecmaFeatures": { // 是否支持某些feature,默认均为false             "globalReturn": true, //是否允许全局return             "impliedStrict": true, //是否为全局严格模式             "jsx": true         }     },     //自定义解析器,官方支持下列四种,也可以自己定义解析器。     "parse": "espree" | "esprima" | "Babel-ESLint" | "@typescript-eslint/parser",     "plugins": ["a-plugin"], //第三方 插件a     "processor": "a-plugin/a-processor", // 制定处理器为插件a的处理器     "rules": {         "eqeqeq": "error"     }     // 指定一些全局变量,类似于global.d.ts的作用     "globals": {         "var1": "writable",         "var2": "readonly"     }     // 忽略哪些文件     "ignorePatterns": ["src/**/*.test.ts", "src/frontend/generated/*"] }
  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 Why does it work?
  AST
  他是为什么能够生效的。 这里就要提到我们前端方方面面都要涉及到的 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 数据去修改对应的文本内容。所谓的修改就是字符串替换,因为我们已经知道了对应的位置信息。
  SourceCode
  但是根据上面我们可以看到,直接根据 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__  上的方法,完整属性可以查阅官网/源码/类型注解。hasBOM:是否含有  unicode bom[5] ;lines:将我们的每一行切割,分行形成的一个 array; linsStartIndices:每行的开始位置; tokenAndComments:token 和 comment 的一个有序集合; getText(node?: ESTree.Node, beforeCount?: number, afterCount?: number): string;  isSpaceBetweenTokens(first: AST.Token, second: AST.Token): boolean;   两个 token 间是否有空格;visitorKeys:存在的 key 值。
  OK,前置的一些知识我们已经介绍的差不多了。 接下来结合实际的 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} `;               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] ESLint 的官网文档 : https://eslint.org/docs/latest/user-guide/configuring/configuration-files
  [2] ESLint 默认的规则集: https://eslint.org/docs/latest/rules/
  [3] Espree: https://github.com/eslint/espree
  [4] 在线的工具: https://astexplorer.net/
  [5] unicode bom: https://en.wikipedia.org/wiki/Byte_order_mark

告别宁德时代,3D打印下一代电池上场,改变游戏规则十年后,电池格局将发生变化,而现在正处于这场革命的风口浪尖,3D打印固态电池或许将是下一个风口。7月21日,中国科学院院士欧阳明高在世界动力电池大会上表示,从技术角度来看,固态电池Steam95好评的解谜游戏,你敢玩吗?背后藏着一个庞大的科幻宇宙解谜冒险一直是独立游戏厂商们最为热衷的创作形式,因为这类游戏有着极强的吸收能力,同时,这类游戏还具备着较强的叙事能力,这种极其适合讲述科幻故事极限逃生的题材,也难怪会被独立制作人所资讯供应链加速整合,宁德时代国轩高科子公司投资锂电铜箔公司文懂车帝原创魏微懂车帝原创行业近日,据企查查信息,铜陵市华创新材料有限公司(以下简称华创新材)发生工商变更,公司注册资本由7。55亿元增至9。59亿元。值得一提的是,该公司新增宁德赚超165亿元!宁德时代前三季度净利或超去年全年全球赣商网讯记者尹谱圣报道10月10日,宁德时代(证券代码300750)发布2022年前三季度业绩预告。预计2022年前三季度归属于上市公司股东的净利润165亿元至180亿元,同比资讯180亿元!宁德时代前三季度净利润将超去年全年文懂车帝原创魏微懂车帝原创行业10月10日晚间,宁德时代披露前三季度业绩预告,预计前三季度归属于上市公司股东的净利润为165亿元至180亿元,同比增长112。87至132。22。宁不是丰田大众不够硬,而是长安新车实在太强了,质量很过硬这朋友超安逸众所周知,由于我国的汽车工业起步较晚,在很长一段时间里技术质量都不够成熟,因此很多人在买车时会优先考虑合资品牌。对于手握10万左右预算第一次买车的用户来说,他们更注重车纯电续航518公里,支持快充,别克微蓝6新车型上市近日,从相关渠道获悉,别克微蓝6新增车型上市,售价19。39万元,据悉,新车目前不支持普通消费者购买,属于企业专享车型,此前别克微蓝6已经有一款汽车专享车型,目前为止一共有两款,那有读有写犹太人智慧大全集第41天鸡蛋不要放在同一个篮子里犹太商人认为,如果有闲置资金就应当用来进行投资,投资可以说是一种很好的生财之道。但这并不是说这笔资金投出去就一定能赚钱,因为任何一项投资都必然存在着一定的风险。因为在现实中,不管哪走进泰国,带你看看东南亚第三强国真实现状,和想象中不一样泰国国土面积513000平方公里,比我国四川省大了2个兰州市大小,总人口数量6617万,与我国湖南省人口数量一样。曼谷是泰国首都和最大城市,也是其政治经济贸易交通文化科技教育及各方总投资613。47亿元!成都国际陆港彭州片区正加速建设决胜四季度,奋斗一百天彭州全力以赴拼经济搞建设抓发展高质量推进成都国际陆港建设依托地处成德临港经济产业带的濛阳农副产品专业物流港以项目集群推动成都国际陆港彭州片区建设成都国际陆港彭赌徒搅肉机在中国股市,炒股要看基本面吗?大A就是巨型吞金兽,一赚二平七亏的格局一直没有改变,十年过去了,经济飞速发展,大A定力十足,还是3000点。平民入市不管是投资还是投机,就象进了搅肉机
投资大湾区!一图读懂全球招商政策大礼包12月21日,2022粤港澳大湾区全球招商大会在广州举行。这是粤港澳三地携手举办的首次全球招商大会,广东省发展改革委省工业和信息化厅省人力和社会资源保障厅省自然资源厅省生态环境厅省总质量约6000万亿吨,厚达数千公里,地球为何有这么厚的大气层综述如果人类头上的大气层,有一天突然消失了,会发生什么事情呢?这个问题很值得深思。根据预测,如果大气层突然消失,人类遭遇到的灾难,是彻彻底底的灭顶之灾。从短期来说,大量的人会因此死二手船交易指数XHSSPI正式发布服务全国统一大市场建设中证网讯(记者张冉)为打造全国性船舶交易信息平台,促进船舶交易市场信息畅通,实现船舶交易的高效有序,更好服务全国统一大市场建设,在近日举办的第四届浙江国际智慧交通产业博览会海运与物2022年终经济观察丨国企改革三年行动将迎高质量收官2022年是决战国企改革三年行动的收官之年。国资委日前表示,国企改革三年行动主体任务已基本完成,大力破除了一批体制机制障碍,切实解决了一批长期想解决而没有解决的重点难点问题,国资国关于黄金首饰如何买才不会上当呢?一些必不可少的避坑知识现在社会越来越发达,人们的购买力也越来越强。作为轻奢品的黄金首饰,也已经在普通人当中流行开来。但毕竟对这东西不熟啊你们知道该如何挑选吗?图片来源网络黄金首饰不会挑,可是要上当的。大罗湖开展泥头车从业人员警示教育为进一步规范泥头车交通出行秩序,确保岁末年终道路交通安全形势持续稳定,近日来,罗湖交警大队结合年末重点工作部署,积极组织开展泥头车交通安全专项治理行动,通过安全检查警示教育宣传培训武汉120闯红灯撞车后被指见死不救,致被撞司机溺亡,急救中心回应12月26日,一则120撞人落水后不救人致我父亲溺亡的爆料,引发不少网友关注。据死者家属沈先生反映,12月16日,60岁的父亲独自驾车在武汉市东西湖区兴工六路正常行驶途中,被一辆闯扒出梅西和周文强不为人知的至暗时刻,你对阳就没那么恐惧了倘若阳来了,你怕吗?近日,有关阳的各类消息扑面而来有令人悲伤落泪的有使人紧张不安的有让人恐惧焦虑的很多阳过阳康王重阳或是至今未阳的同胞,都对阳来了忧心忡忡。转自人民日报对于阳,除了迈过至暗时刻,苹果站在黎明前?与科技行业的许多公司一样,苹果(纳斯达克股票代码AAPL)在2022年受到更具挑战性的宏观经济环境影响。今年,投资者看到苹果股票的总回报率为负24,原因是投资者担心供应链持续中断,中央财办副主任我国经济已挺过了最困难时刻中央财办副主任我国经济已挺过了最困难时刻财联社12月24日电,中央财办副主任尹艳林24日在中国财富管理50人论坛2022年会上表示,明年中国发展有利因素增多,经济运行有望整体好转。2022年12个令人难忘的时尚时刻译者百贝翻译www。baibeitrans。com在一个戏剧性的特技表演中,巴黎品牌Coperni将一件由液体织物制成的裙子喷在了模特贝拉哈迪德的身上(图片来源GettyImage