文章目录
什么是AST
AST 叫虚拟语法树(Abstract Syntax Tree)它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。
大白话来讲:就是通过这种方式可以去解剖你身上的所有细节来描述你的身体。
注意:抽象语法树并不是单纯指在前端存在,其实所有的语言都可以抽象成语法树,它只是一种语法分析的结果
例如这段python代码:
AST解析:
AST在线可视化网站
https://astexplorer.net/
利用这个网站我们可以很清晰的看到各种语言的 AST 结构
代码如何转化AST
这里我们只针对JavaScript如何去转化成AST,可以使用两款插件acorn
和recast
,
- acorn
acron 代码比较精简,且 webpack 和 eslint 都依赖 acorn
acorn基本使用
import * as acorn from 'acorn'; //引入
const code = 'xxx'; //解析代码块
const ast = acorn.parse(code, options) //解析
options的定义如下
如果你学过Ts应该就很清楚下面的参数在讲什么,简单来说除了ecmaVersion是必填参数,其他都是可选参数,ecmaVersion代表你的代码是属于es的哪一个版本,如果为6,那么es5的代码就不支持了解析了。
ecmaVersion ECMA 版本,默认时 es7
locations 默认为 false,设置为 true 时节点会携带一个 loc 对象来表示当前开始与结束的行数。
onComment 回调函数,每当代码执行到注释的时候都会触发,可以获取当前的注释内容
interface Options {
ecmaVersion: 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 'latest'
sourceType?: 'script' | 'module'
onInsertedSemicolon?: (lastTokEnd: number, lastTokEndLoc?: Position) => void
onTrailingComma?: (lastTokEnd: number, lastTokEndLoc?: Position) => void
allowReserved?: boolean | 'never'
allowReturnOutsideFunction?: boolean
allowImportExportEverywhere?: boolean
allowAwaitOutsideFunction?: boolean
allowSuperOutsideMethod?: boolean
allowHashBang?: boolean
locations?: boolean
onToken?: ((token: Token) => any) | Token[]
onComment?: ((
isBlock: boolean, text: string, start: number, end: number, startLoc?: Position,
endLoc?: Position
) => void) | Comment[]
ranges?: boolean
program?: Node
sourceFile?: string
directSourceFile?: string
preserveParens?: boolean
}
还原
import * as astring from 'astring'; //引入
const code = astring.generate(ast); //ast
- recast
recast也是类似的。
recast.parse(code) //解析
recast.print(ast).code //还原
什么是ESLint
ESLint是一个用来识别 ECMAScript 并且按照规则给出报告的代码检测工具,使用它可以避免低级错误和统一代码的风格。如果每次在代码提交之前都进行一次eslint代码检查,就不会因为某个字段未定义为undefined或null这样的错误而导致服务崩溃,可以有效的控制项目代码的质量。
在许多方面,它和 JSLint、JSHint 相似,除了少数的例外:
- ESLint 使用 Espree 解析 JavaScript
- ESLint 使用 AST 去分析代码中的模式
- ESLint 是完全插件化的。每一个规则都是一个插件并且你可以在运行时添加更多的规则。
因此,在介绍ESLint之前需要先明白AST的相关知识。
ESLint解析原理
- 解析:将源代码解析成AST
- 遍历:遍历每个选择器两次
- 回调:根据规则触发回调
- 生成:修改AST,生成代码
如何制作ESLint插件
官方是非常欢迎开发者参与到ESLint插件的制作,为此官方提供了一个脚手架便于开发者快速开发插件—— yeoman
安装yeoman
npm install -g yo generator-eslint
创建插件
yo eslint:plugin
创建规则
目录结构
实现警告console.error()方法
1.修改lib/rules/no-console-error.js文件
/**
* @fileoverview no console.error() in your code
* @author yjian
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem', // `problem`, `suggestion`, or `layout`
docs: { //文档描述
description: "no console.error() in your code",
recommended: false,
url: null, // URL to the documentation page for this rule
},
fixable: null, // Or `code` or `whitespace`
schema: [], // Add a schema if the rule has options
// 报错信息描述
messages: {
avoidMethod: "'{{name}}' function of console is forbidden in code",
},
},
create(context) {
return {
// 'MemberExpression' 这个就是AST的节点选择器,在遍历AST时,如果命中该选择器,就会触发回调
// 关于选择器的名称,我们可以事先在 https://astexplorer.net/ 中找到目标解析器然后将其作为key即可
// 这里的选择器会在AST"自上至下"过程中触发,如果希望是"自下至上"过程中触发,需要加':exit'即MemberExpression:exit
'MemberExpression': (node) => {
// 如果在AST遍历中满足以下条件,就用 context.report() 进行对外警告⚠️
if (node.property.name === 'error' && node.object.name === 'console') {
context.report({
node,
messageId: 'avoidMethod', //错误类型
data: {
name: node.property.name, //这里的name会传递到上面的messages中的name,类似于vue的双向绑定
},
});
}
},
};
},
};
2.导出规则lib/rules/index
/**
* @fileoverview 自定义eslint规则
* @author yjian
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Plugin Definition
//------------------------------------------------------------------------------
// import all rules in lib/rules
module.exports = {
rules: {
// 项目在使用时,对应的规则名称
'no-console-error': require('./rules/no-console-error'),
},
configs: {
recommended: {
rules: {
'demo/no-console-error': 2, // 可以省略 eslint-plugin 前缀
},
},
},
}
3.单元测试./tests/lib/rules/no-console-error.js
/**
* @fileoverview 不允许使用 console.time()
* @author lzx
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const rule = require("../../../lib/rules/no-console-time"),
RuleTester = require("eslint").RuleTester;
//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
const ruleTester = new RuleTester();
ruleTester.run("no-console-error", rule, {
valid: [
// give me some code that won't trigger a warning
],
invalid: [
{
code: "console.error('test');",
errors: [{ message: "Fill me in.", type: "Me too" }],
},
],
});
4.最后执行测试
npm run test
npm发布
如何注册
https://www.npmjs.com/
登录官网,输入用户名,邮箱和密码,完成邮箱校验之后即注册成功,过程需要梯子。
如何登录
输入命令:需要梯子,需要修改npm地址
npm login
输入成功之后,会提示你输入用户名,密码和邮箱,同样需要校验之后就登录成功了,
可以通过npm whoami
命令去查看是否登录成功,如果成功,即会弹出用户名。
注意:输入密码的时候,命令行可能是空的,这是正常的,只需要确保密码正确能进入下一步即可
发布
npm publish
可以在下方链接查看到,
https://www.npmjs.com/settings/用户名/packages
应用
安装eslint
npm i eslint --save-dev
安装eslint插件
npm install eslint-plugin-noconsole --save-dev
初始化配置
npx eslint --init
配置
{
"plugins": [
// 这是此前使用yo eslint:plugin 生成自定义插件的ID
"diylint"
],
extends: ["plugin:ecdemo/recommended"],
}
问题
如果你在使用npm login命令出现了以下错误,说明你的npm镜像地址不是原生地址,需要进行更换
解决方案:
npm set registry https://registry.npmjs.org/
推荐使用nrm
进行镜像源地址的管理,请看下方说明!
nrm
nrm(npm registry manager )是npm的镜像源管理工具,可以通过nrm方便切换镜像源。
nvm是一个可以便捷切换node版本的工具,与nrm类似
下载
npm install -g nrm
查看可用镜像源
// 显示当前可使用的镜像源列表
nrm ls
* npm -------- https://registry.npmjs.org/
yarn ------- https://registry.yarnpkg.com/
cnpm ------- http://r.cnpmjs.org/
taobao ----- https://registry.npm.taobao.org/
nj --------- https://registry.nodejitsu.com/
npmMirror -- https://skimdb.npmjs.com/registry/
edunpm ----- http://registry.enpmjs.org/
切换镜像源
nrm use taobao
结束
想必,你应该大致清楚,babel是如何做到es6语法转es5,ESLint又是如何做到规则校验的。如果要进一步深入了解,还需要清楚,代码和AST之间是如何互相转化的。
参考文章
https://blog.csdn.net/qq_33396780/article/details/129935377
https://blog.csdn.net/KlausLily/article/details/124486883
https://blog.csdn.net/WTUDAN/article/details/127383560