您的位置 首页 > 新农资讯

eslint 规范,eslint 原理

作者:叶鹏

简介代码是写给人们阅读的,所以好的代码应该帮助各个层次的读者理解代码的真正含义。例如,如果有的文件使用两个空格缩进,有的文件使用四个空格,有的文件使用下划线,有的文件使用驼峰式,那么阅读体验就很差。

因此,如何约束代码,使得一个团队的代码风格尽可能一致,同时又不增加理解成本,是一个需要解决的问题。众所周知,懒惰是社会生产力的原动力。

前端工程的标准之一是自动化,其中包括自动化代码规范。自动化代码规范可以释放团队的生产力并提高他们的生产力……这就是ESLint、TSLint、StyleLint 等工程插件应时而生的地方。

最近,作者团队也统一了各个项目之间标准的差异。相信大家都遇到过红斑的现象。今天我就简单解释一下其背后的原理。

什么是ESLint/Lint? 首先,当你提到ESLint 时,你应该想到两件事。一种是devDep上的ESLint npm包,另一种是VSCode等ESLint插件。这两件事?

npm 包:这些是实际的Lint 规则,控制运行Lint 时代码的格式。

VSCode 插件: 实际上指向项目的/node_modules/eslint 或全局eslint,并告诉IDE 根据ESLint 的规则哪些位置应该为红色。这意味着该插件会解析打开的文件并将它们与规则进行比较,以查看是否存在任何ESLint 问题。 您还可以通过IDE 设置在不同时间运行Lint,包括保存自动格式。

总体而言,ESLint 规则是对编码风格的一种约束,代码中的一些潜在错误和不规范的使用会通过npm 包的形式同时通过IDE 插件引入到项目中。修改npm包规则并修复错误消息。

如何使用它?本文不解释如何在项目中配置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' (默认) 或'module' ,使用的代码显示模块为还是一个脚本'ecma features': { //是否支持某些特性,默认为false 'globalReturn': true, //是否允许全局返回是否'impliedStrict': true, //全局严格模式'jsx': true } }, //自定义解析器,官方支持以下四种类型,你也可以定义自己的解析器。 'parse': 'espree' | 'Babel-ESLint', 'plugins': ['a-plugin'], //第3 方插件': ' 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.eslintrcpackage.json 同时,ESLint 还支持为每个目录配置不同的规则,并且对于mono 仓库,每个仓库的ESLint 都会略有不同。可能是个案。目前可以使用以下目录格式:根目录有基本规则,子应用程序有特定规则。子rc是对父rc的覆盖,但是如果在app/.eslintrc.js中设置root:true,对于test.js,父目录中的rc中使用的规则将不会在应用程序中生效。

Package package.json .eslintrc.js lib test.js app .eslintrc.js test.js 为什么它可以工作?谷草转氨酶

为什么有效? 这里我们要讨论的是AST,它涉及到前端的方方面面。

ESLint 基于抽象语法树工作。 ESLint 使用的默认编译器(解析)可以解析你的JS 代码,并根据AST 检查和总结你编辑的代码。

Babel 编译通常分为编译/转换/生成步骤,如下所示。与ESlint相比,只有第一步是一致的。这只需要检索AST中的部分信息,同时需要直接在源代码中进行提示和操作,无需任何转换或后续生成代码。

来分析

然后使用demo来探索其背后的原理以及如何转换。 首先,我们需要加载并解析源代码。 这是编译器将代码转换为AST 树的过程。由于我完全接受typescript(主要是因为espree 没有类型注释,这让我感到不舒服),因此我将在本文中使用@typescript-eslint/parser 作为我的编译器。 这里有一个小问题。如果您在VSCode 中安装了Import Costs 插件,则可以暂时禁用它,因为此解析器在特定解析时会卡住。

const foo='anthony' const bar='dst' 从'fs' 导入fs; 从'path' 导入路径; 从'@typescript-eslint/parser' 导入* 作为tsParser; const filePath=path.resolve('./src/test.ts')const text=fs.readFileSync(filePath, 'utf8')//这个和eslint配置项一样吗是的,是透明提交。 text,{ comment: true, //创建一个包含所有评论的顶级注释数组ecmaVersion: 6, //JS 版本////指定其他语言特性, //ecma features: { //jsx: true, //启用JSX 解析//globalReturn: true //启用全局返回(如果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/source_code.js部分。简而言之,它构造了一个SourceCode 实例,接受两个参数:原始文本和解析后的AST,并返回一个包含多个方法的实例对象。

让我们在我们的演示项目中安装eslint,引入SourceCode,看看构造的对象是什么。

import { SourceCode } from 'eslint';//.//const sourceCode=new SourceCode(text,ast);//我们创建一个断点,看一下sourceCode 结构。让我们简单解释一下(摘录)里面的内容。实例对象__proto__的一些属性和方法,完整的属性可以在官网/源码/类型注释上找到。

hasBOM: 是否包含Unicode bom[5]; rows: 将每一行分割成单独的行而形成的数组; tokenAndComments: 标记和注释的有序集合; ESTree.Node, beforeCount : number): string ; isSpaceBetweenTokens(first: AST.Token, Second: AST.Token): boolean; 两个token 之间是否有空格。现在,我已经介绍了大部分必备知识。 接下来我会根据一个实际的规则demo来进行讲解。规则模板

写过VSCode 插件的同学应该对Yeoman 很熟悉。 ESLint 还提供了一套基于Yeoman 的支架用于生成模板。

首先,全局安装eslint脚手架(npm install -g yogenerator-eslint),然后通过以下交互式命令行操作初始化操作:

通过初始化,您将看到包含以下文件的shell:该文件添加了上面提到的一些内容。打开生成的规则模板文件,添加一些规则和提示(注意,我这里写得不规范;我在一个规则文件里有两条不相关的规则))。

'use strict';/** @type {import('eslint').Rule.RuleModule} */module.exports={meta: { type: '问题', //`问题`、`建议`或`布局` docs: { description: 'xxxx', suggest: false, url: null, //此规则文档页面的URL },messages:{ temp: '当不使用文字作为函数参数时', novar: '当不使用var 语句时', noExport: '执行此exit' }, fixable: 'code', //或`code` 或`whitespace` schema: [], //如果规则有选项则添加模式}, create(context) { //必须在此处定义变量const sourceCode=context .getSourceCode(); return { ArrowFunctionExpression:(node)={ if(node.callee.name !=='abcd') return;forEach((argNode,index)={ argNode.type==='Literal' 上下文。报告({ 节点,messageId: 'temp',修复(修复程序){const val=argNode.value;const statementsString=`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')} }) } } }; 关键函数

在此演示中,我们将了解一些内容。一是创建函数的参数上下文及其返回值,上下文中提供的报表方法以及报表接受的修改参数。 这些被总结起来形成规则之一的验证逻辑。如果特定AST 节点满足您创建的规则,则使用修复功能。使用标记或范围来决定是否在某处替换文本。

现在我们就来一一讨论一下。首先是语境形成。其实这个也没什么好说的。提供了多种方法来访问插件中的上下文。然后使用createRuleListener 为每个规则创建一个侦听器。稍后我会在完成整个过程后再次解释。

现在我们来简单分析一下这段代码。这段代码实际上经历了一系列操作,并将问题推入lintingProblem 数组中。 该问题包括一些错误消息、AST 信息等。

最后,修复。上面使用的所有替换方法实际上都达到了相同的目的。最后用简单的切片和+=完成修复动作。

基本上,它简单地解释了与插件相关的核心内容。 现在我们来看看整个检测修复过程。这是源码中linter.js的runRules方法。整体流程

每当规则执行时,它必须遍历AST 并同时执行多个操作。首先发生了什么?我们调用实例方法Traverser.traverse,向其传递一个ast 和一个对象(enter、leave、visitorKeys 等)。该函数的作用是进行递归遍历。同时,我们通过遍历过程中的进入和退出对两个相同的节点进行排队。这使得进一步处理变得更加容易。这包括设计模式访问者模式(用于分离数据和操作)。通过在遍历期间添加isEntering,您可以决定是否在开始或结束时运行访问者逻辑。

接下来,您需要将所有规则创建为上面提到的ruleListener,然后在遍历nodeQueue时触发某些逻辑。 当然,在座的大家可能都想到了订阅发布模式,这也是整个逻辑的重要组成部分。这决定了消息在遍历时是否应该通过提交进行推送,然后执行一定的逻辑。您必须配置侦听器来订阅特定事件。

然后遍历nodeQueue并使用节点的标签来决定是否执行开始或离开逻辑。其实这里我们不去深究具体细节,只是简单理解进入和退出会触发访客的各种动作。

在下一篇文章中,我将简要解释其背后的原理,但不会详细介绍您将来可以做什么,例如ESLint 插件。

最后,Linus 所说的也是我所相信的。 “如果你想了解一些东西,请给我看代码。最好的方法是实现它。”分析该过程后,任何人都可以轻松实现ESLint 的小型演示并使用eslint 插件,我相信它可以手动创建。

参考文献[1] ESLint 官网文档: https://eslint.org/docs/latest/user-guide/cconfiguration/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

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023