babel分享
babel介绍
js 转译器 es6 to es5 es7 to es5 简称 es typescript 代码转换 除了这些 我们还可以做一些 静态分析 eslint等
babel 是源码 => 源码的过程
babel 代码转换的过程分为三步
1. parse 代码 => ast
2. transform 操作语法书 增删改 ast => ast (修改后的)
3. generate 修改后的 ast => 新的代码
复制代码
ast 介绍
抽象语法书 (Abstract Syntax Tree,AST)以树形结构表达 编程语言 的语法
复制代码
ast语法树 代码是从 经过词法分析 和 语法分析 最终生成ast
举一个例子
我是张三 词法分析的过程就相当于 我
、是
、张三
语法分析 是
一个赋值语句 张三
赋值给 我
me = 'zhangsan'
词法分析过程最终生成结果 token (不能再拆分的单词)
me、= 、'zhangsan'
语法分析的过程 发现 有一个等号(operator
操作符) 发现是赋值操作
left 是 me (标识符 identifier
)right
是 (字符串字面量 StringLiteral
) 'xiaomenggang '
- 举个例子
javascript:
console.log(1)
复制代码
他的树形结构是这样的
ast 常见节点
Literal 字面量
字符串字面量 StringLiteral, 例如 ‘xmg’
数字字面量 NumericLiteral, 例如 1
布尔字面量 BooleanLiteral , 例如 true or false
正则表达式字面量 RegExpLiteral , 例如 /^[0-9]/
Identifier 标识符
Statement 语句
代码 | 节点名称 | 中文名 |
---|---|---|
for | ForStatement | 循环语句 |
while | WhileStatement | while语句 |
continue | ContinueStatement | continue语句 |
Switch | SwitchStatement | Switch语句 |
Declaration 声明语句
代码 | 节点名称 | 中文名 |
---|---|---|
var a = ‘xmg’; | variableDeclaratio | 变量声明 |
function fn(){} | functionDeclaratio | 函数声明 |
import d from ‘e’; | importDeclaratio | 导出声明 |
Switch | SwitchStatement | Switch语句 |
Expression 表达式
代码 | 节点名称 | 中文名 |
---|---|---|
[1,2,3] | ArrayExpression | 数组表达式 |
a = 1 | AssignmentExpression | 赋值表达式 |
1 + 2 | BinaryExpression | 二元表达式 |
funciton () {} | FunctionExpression | 函数表达式 |
() => {} | ArrowFunctionExpression | 箭头函数表达式 |
超级微小编译器
简单的从 词法分析 到 语法分析的一个简单实现过程
我们先来看下babel 的api
parse
parse @babel/parser 将代码转换成 ast
参数 plugin
,souseType
(script
script 则不解析 es module 语法 module
是解析 es module 语法 unambiguous
根据环境自动判断 )
transform @babel/traverse 遍历AST 使用访问者 模式 对ast修改
traverse(ast, visitor)
visitor: {
StringLiteral(path) {
debugger
console.log(path.node.value)
}
}
复制代码
path 上面有很多方法
比如 path.node 当前节点
比如 path.parent 获取父级节点
path.insertBefore 向前插入
path.repalceWith 替换
path.remove 删除
path.stop 停止遍历
generate @babel/generate 将代码 从 ast 转成 code
@babel/types 提供快速生成 ast 和 断言的方法
创建 ast和判断ast的节点类型
比如 types.IfStatement()
if(1){}
复制代码
types.ifStatement(types.NumericLiteral(1),types.blockStatement([]))
复制代码
types.isIfStatement():boolean
我们再来做一个例子 删除console代码
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const types = require('@babel/types');
// console.log(types.ifStatement(types.NumericLiteral(1),types.blockStatement([])))
// console.log(generate(types.ifStatement(types.NumericLiteral(1),types.blockStatement([]))))
const sourceCode = `
console.log(1);
function func() {
var a = 2
console.info(a);
}
export default class Clazz {
say() {
var b = 3
console.debug(b);
}
render() {
let bbb = 333
console.log(bbb)
return <div>{bbb}</div>
}
}
`;
const ast = parser.parse(sourceCode, {
sourceType: 'unambiguous',
plugins: ['jsx']
});
traverse(ast, {
StringLiteral(path) {
debugger
console.log(path)
},
CallExpression(path) {
// if(path.scope) {
// path.scope.generateUid('maidian')
// console.log(path.scope.generateUid('maidian'))
// }
// types.ifStatement
if ( types.isMemberExpression(path.node.callee)
&& path.node.callee.object.name === 'console'
) {
path.remove()
}
}
});
const { code, map } = generate(ast);
console.log(code);
复制代码
我们再来做一个例子 实现代码混淆
我们要说下 path 上的 scope 是作用域信息 生成作用域的就是模块、函数、块
作用域之间会形成嵌套关系,也就是作用域链
scope.bindings 当前作用域内声明的所有变量
const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const types = require('@babel/types');
let num=0;
const string = `abcdefghigklsisysjsks`
const sourceCode = `
function getScr() {
const num1 = 3
const num3 = num1**num1
const num5 = num3**num3
function add () {
return num5 + num3
}
const sum = add()
return sum
}
`;
const ast = parser.parse(sourceCode, {
sourceType: 'unambiguous',
});
traverse(ast, {
StringLiteral(path) {
console.log(path)
},
Scopable(path, state) {
if(path.scope.bindings) {
Object.entries(path.scope.bindings).forEach(([key,binding] )=> {
binding.scope.rename(key,binding.scope.generateUid(string[num++]))
})
}
}
});
const { code, map } = generate(ast);
console.log(code);
复制代码