Open Genluo opened 4 years ago
用babbel创造自定义JS语法 @babel/parser 已经支持了很多 JavaScript 语法特性,Parser也不支持扩展. 因此plugin-syntax-*实际上只是用于开启或者配置Parser的某个功能特性。
@babel/template: 某些场景直接操作AST太麻烦,就比如我们直接操作DOM一样,所以Babel实现了这么一个简单的模板引擎,可以将字符串代码转换为AST。比如在生成一些辅助代码(helper)时会用到这个库
@babel/types: AST 节点构造器和断言. 插件开发时使用很频繁
@babel/helper-*: 一些辅助器,用于辅助插件开发,例如简化AST操作
@babel/helper: 辅助代码,单纯的语法转换可能无法让代码运行起来,比如低版本浏览器无法识别class关键字,这时候需要添加辅助代码,对class进行模拟。
修改了NodePath中对应的Node, 修改完成之后需要手动映射到NodePath变量中, path.replaceWith(path.node);
我们可以对 AST 进行任意的操作,比如删除父节点的兄弟节点、删除第一个子节点、新增兄弟节点... 当这些操作'污染'了 AST 树后,访问者需要记录这些状态,响应式(Reactive)更新 Path 对象的关联关系, 保证正确的遍历顺序,从而获得正确的转译结果。
如果修改了scope,也需要使用相关的path.scope来进行更新作用域信息
我们可以对 AST 进行任意的操作,比如删除父节点的兄弟节点、删除第一个子节点、新增兄弟节点... 当这些操作'污染'了 AST 树后,访问者需要记录这些状态,响应式(Reactive)更新 Path 对象的关联关系, 保证正确的遍历顺序,从而获得正确的转译结果。
提升语言的表现能力
示例
import evalm from 'eval.macro' const x = evalm` function fib(n) { const SQRT_FIVE = Math.sqrt(5); return Math.round(1/SQRT_FIVE * (Math.pow(0.5 + SQRT_FIVE/2, n) - Math.pow(0.5 - SQRT_FIVE/2, n))); }
fib(20) `
// ↓ ↓ ↓ ↓ ↓ ↓
const x = 6765
#### 使用Macro相比Plugin带来的好处
1. 很显然,Macro不需要配置.babelrc(当然babel-plugin-macros这个基座需要装好). 这个对于CRA这种不推荐配置构建脚本的工具来说很有帮助
2. 由隐式转换为了显式。上一节就说了“显式好于隐式”。你必须在源代码中通过导入语句声明你使用了 Macro; 而基于插件的方式,你可能不知道preval这个标识符哪里来的? 如何被应用?何时被应用?而且通常你还需要和其他工具链的配合,例如ESlint、Typescript声明等等。
3. Macro相比Plugin 更容易被实现。因为它专注于具体的 AST 节点,见下文
4. 另外,当配置出错时,Macro可以得到更好的错误提示
export function getJsxValue(path: NodePath<babelTypes.JSXAttribute>) {
// generate
// 单纯字符串
if (babelTypes.isStringLiteral(path.node.value)) {
return path.node.value.value;
}
// {} 这种类型
if (babelTypes.isJSXExpressionContainer(path.node.value)) {
const JSXExpressionContainerExpressionNode = path.node.value.expression;
if (
babelTypes.isStringLiteral(JSXExpressionContainerExpressionNode) ||
babelTypes.isBooleanLiteral(JSXExpressionContainerExpressionNode) ||
babelTypes.isNumericLiteral(JSXExpressionContainerExpressionNode)
) {
return JSXExpressionContainerExpressionNode.value;
}
if (babelTypes.isObjectExpression(JSXExpressionContainerExpressionNode)) {
const result = generate(JSXExpressionContainerExpressionNode);
return eval(`(${result.code})`);
}
}
if (path.node.value === null) {
return true;
}
throw path.buildCodeFrameError("不能赋值为非基本类型");
}
通过内置api可以进行求值
t.evaluate(parse("5 + 5")) // { confident: true, value: 10 }
t.evaluate(parse("!true")) // { confident: true, value: false }
// ❌两个变量相加无法求值,因为变量值在运行时才存在,这里confident为false:
t.evaluate(parse("foo + foo")) // { confident: false, value: undefined }