Babel 插件开发&访问节点
携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
1. 前言
大家好,我是小鑫同学。一位从事过Android开发、混合开发,现在长期从事前端开发的编程爱好者,我觉得在编程之路上最重要的是知识的分享,所谓三人行必有我师。所以我开始在社区持续输出我所了解到、学习到、工作中遇到的各种编程知识,欢迎有想法、有同感的伙伴加我fe-xiaoxin微信交流~
整理一下 Babel 插件开发时用得到的转换操作相关的 API~
2. 访问节点
2.1 获取子节点的Path:
我们在处理节点的属性之前必须要拿到节点对象才能进行操作,我们使用path.node.property
来访问属性~
BinaryExpression(path) { path.node.left; path.node.right; path.node.operator; }
我们还可以使用 path 内置的 get 函数来指定属性名获取属性值~
BinaryExpression(path) { path.get('left'); } Program(path) { path.get('body.0'); }
2.2 检查节点的类型:
检查节点的类型我们可以使用内置的工具类函数isXxx()
~
BinaryExpression(path) { if (t.isIdentifier(path.node.left)) { // ... } }
我们在检查类型的时候还可以顺便检查其中的某些属性是否达到预期~
BinaryExpression(path) { if (t.isIdentifier(path.node.left, { name: "n" })) { // ... } } // 简化前的代码 BinaryExpression(path) { if ( path.node.left != null && path.node.left.type === "Identifier" && path.node.left.name === "n" ) { // ... } }
2.3 检查路径(Path)类型:
路径具有相同的方法检查节点的类型~
BinaryExpression(path) { if (path.get('left').isIdentifier({ name: "n" })) { // ... } } // 等价于 BinaryExpression(path) { if (t.isIdentifier(path.node.left, { name: "n" })) { // ... } }
2.4 检查标识符(Identifier)是否被引用:
Identifier(path) { if (path.isReferencedIdentifier()) { // ... } } // 或者 Identifier(path) { if (t.isReferenced(path.node, path.parent)) { // ... } }
2.5 找到特定的父路径:
向上查找特定节点可以使用~
path.findParent((path) => path.isObjectExpression());
如果也需要遍历当前节点~
path.find((path) => path.isObjectExpression());
查找最接近的父函数或程序~
path.getFunctionParent();
向上遍历语法树,直到找到在列表中的父节点路径~
path.getStatementParent();
2.6 获取同级路径:
如果一个路径是在一个 Function
/Program
中的列表里面,它就有同级节点。
- 使用
path.inList
来判断路径是否有同级节点, - 使用
path.getSibling(index)
来获得同级路径, - 使用
path.key
获取路径所在容器的索引, - 使用
path.container
获取路径的容器(包含所有同级节点的数组) - 使用
path.listKey
获取容器的key
这些API用于 babel-minify 中使用的 transform-merge-sibling-variables 插件.
var a = 1; // pathA, path.key = 0 var b = 2; // pathB, path.key = 1 var c = 3; // pathC, path.key = 2
export default function({ types: t }) { return { visitor: { VariableDeclaration(path) { // if the current path is pathA path.inList // true path.listKey // "body" path.key // 0 path.getSibling(0) // pathA path.getSibling(path.key + 1) // pathB path.container // [pathA, pathB, pathC] } } }; }
2.7 停止遍历:
当我们遍历完成目的后应该尽早结束而不是继续遍历下去~
BinaryExpression(path) { if (path.node.operator !== '**') return; }
如果您在顶级路径中进行子遍历,则可以使用2个提供的API方法~
path.skip()
跳过遍历当前路径的子路径~
path.stop()
完全停止遍历~
outerPath.traverse({ Function(innerPath) { innerPath.skip(); // if checking the children is irrelevant }, ReferencedIdentifier(innerPath, state) { state.iife = true; innerPath.stop(); // if you want to save some state and then stop traversal, or deopt } });
如果看完觉得有收获,欢迎点赞、评论、分享支持一下。你的支持和肯定,是我坚持写作的动力~
最后可以关注我@小鑫同学。欢迎点此扫码加我微信fe-xiaoxin交流,共同进步(还可以帮你fix🐛)~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)