To write a simple Babel plugin, we can use http://astexplorer.net/ to help us.

The plugin we want to write is:

var foo = 'o'
var bar = 'o'
foo === bar
function foo(foo, bar) {
foo === bar;
}

We want to trasnform the code which highlighted in foo() function to:

_foo === _bar

Notice that, we also have 'foo === bar' which is not inside foo() function. But we don't want to touch that.

To get started, we have code below:

export default function(babel) {
const { types: t } = babel; return {
name: "add_underscore",
visitor: {
// your code here
}
};
}

All the code is under 'visitor' prop.

By hightlight the code we can see, it is a 'BinaryExpression':

So we focus on 'BinaryExpression':

export default function(babel) {
const { types: t } = babel; return {
name: "add_underscore",
visitor: {
BinaryExpression(path) { }
}
};
}

'path' param contains all the information we need.

If add a console.log() inside BinarayExpression() function, we can see two logs, one is from global scope, another one is from 'foo()' scope. We only want to get one from foo() function scope:

The way to do is check 'path.scope.block.type', which is current code' block scope.

'foo === bar' exisits on global belongs to 'Program':

the one exists in foo() belongs to 'BlockStatement':

BinaryExpression(path) {
// remove global one
if (path.scope.block.type === "Program") {
return;
}
}

And the opreator we want to check is '===':

      BinaryExpression(path) {
if (path.scope.block.type === "Program") {
return;
} if (path.node.operator !== "===") {
return;
}
}

Now we have located the one 'foo === bar' we want, we can start to replace the name:

export default function(babel) {
const { types: t } = babel; return {
name: "add_underscore",
visitor: {
BinaryExpression(path) {
if (path.scope.block.type === "Program") {
return;
} if (path.node.operator !== "===") {
return;
} // locate the 'foo' and 'bar'
// as left and right Identifier
const leftIdentifier = path.node.left;
const rightIndentifier = path.node.right; // generate a new identifier
const newLeftIdentifier = path.scope.generateUidIdentifier(leftIdentifier.name);
const newRightIdentifier = path.scope.generateUidIdentifier(
rightIndentifier.name
); // replace the old with new one
path.node.left = t.identifier(newLeftIdentifier.name);
path.node.right = t.identifier(newRightIdentifier.name);
}
}
};
}

Now the generate code looks like:

var foo = 'o'
var bar = 'o'
foo === bar
function foo(foo, bar) {
_foo === _bar;
}

The code have successfully transform to '_foo === _bar'.

But clearly the code won't work, because _foo and _bar is undefined.

We need to update the params in the function as well.

        // update params in the function
const [fooParam, barParam] = path.scope.block.params;
fooParam.name = t.identifier(newLeftIdentifier.name).name;
barParam.name = t.identifier(newRightIdentifier.name).name;

All Code:

export default function(babel) {
const { types: t } = babel; return {
name: "add_underscore",
visitor: {
BinaryExpression(path) {
if (path.scope.block.type === "Program") {
return;
} if (path.node.operator !== "===") {
return;
} // locate the 'foo' and 'bar'
// as left and right Identifier
const leftIdentifier = path.node.left;
const rightIndentifier = path.node.right; // generate a new identifier
const newLeftIdentifier = path.scope.generateUidIdentifier(leftIdentifier.name);
const newRightIdentifier = path.scope.generateUidIdentifier(
rightIndentifier.name
); // replace the old with new one
path.node.left = t.identifier(newLeftIdentifier.name);
path.node.right = t.identifier(newRightIdentifier.name); // update params in the function
const [fooParam, barParam] = path.scope.block.params;
fooParam.name = t.identifier(newLeftIdentifier.name).name;
barParam.name = t.identifier(newRightIdentifier.name).name;
}
}
};
}

[Notice]: this is a just learning note for myself. The approache might not be optimal.

[Javascript AST] 0. Introduction: Write a simple BabelJS plugin的更多相关文章

  1. [Javascript AST] 2. Introduction: Write a simple ESLint rule

    What we want to do is checking if user write nested if statements which actually can combine to one: ...

  2. [Javascript AST] 1. Continue: Write a simple Babel plugin

    We want to write a Babel Plugin, which move 'const versionRegex = /(/d+)\.(/d+)\.(/d+)/gi' out of fu ...

  3. js中 javascript:void(0) 用法详解

    点击链接不做任何事情: <a href="#" onclick="return false">test</a> <a href=& ...

  4. html 空链接 href="#"与href="javascript:void(0)"的区别

    #包含了一个位置信息 默认的锚是#top 也就是网页的上端 而javascript:void(0) 仅仅表示一个死链接 这就是为什么有的时候页面很长浏览链接明明是#但跳动到了页首 而javascrip ...

  5. a href=#与 a href=javascript:void(0) 的区别

    a href="#"> 点击链接后,页面会向上滚到页首,# 默认锚点为 #TOP <a href="javascript:void(0)" onCl ...

  6. href使用 javascript:;与javascript:void(0)防跳到顶部

    有时候我们在编写js过程中,需要触发事件而不需要返回值,那么就可能需要这样的写法   href=”#”,包含了一个位置信息.默认的锚是#top,也就是网页的上端,当连续快速点击此链接时会导致浏览器巨慢 ...

  7. javascript:void(0)

    这是不是一个设计缺陷呢 void(0)这种用法巧妙利用void关键字的特性返回undefined(且没有副作用).因为不是关键字,比如直接使用undefined,内容可能被改写. 再来看为啥使用0,而 ...

  8. <a href=”#”>与 <a href=”javascript:void(0)” 的区别

    <a href=”#”>中的“#”其实是锚点的意思,默认为#top,所以当页面比较长的时候,使用这种方式会让页面刷新到页首(页面的最上部) javascript:void(0)其实是一个死 ...

  9. 超级链接a中javascript:void(0)弹出另外一个框问题

    转字:http://my.oschina.net/castusz/blog/68186 结果在IE.Firefox.Chrome都是先执行的onclick事件,在项目中我们尽量不要同时使用这两种方式. ...

随机推荐

  1. HDU 5371 Hotaru's problem Manacher+尺取法

    题意:给你一个序列,求最长的两段回文子串,要求他们共用中间的一半. 思路:利用Manacher求出p[i]表示的当前位置的最长回文串长度,然后把每一个长度大于等于2的回文串的左区间和右区间分别放到两个 ...

  2. 《读书报告 – Elasticsearch入门 》----Part II 深入搜索(2)

    第十三章 全文检索 这一章开始介绍 全文检索 :怎样对全文字段(full-text fields)进行检索以找到相关度最高的文档. 全文检索最重要的两个方面是: 相关度(Relevance) 根据文档 ...

  3. K-序列(埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛)

    题目描述 给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”.现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K 序列.  输入描述: 第一行为 ...

  4. XT800 在域环境在运行设置

    测试时先将防火墙关了,不然无法远程,找个问题后再打开防火墙 0. 防火墙中建立规则,允许XT800通过. 1.在客户端当前用户下运行XT800安装程序 2.输入管理员账号及密码 3.安装时选择D或E盘 ...

  5. 学习“花书“《深度学习》中文PDF和英文PDF

    个人觉得github上的中文版翻译的不错,有700多页,深度学习入门经典书籍,前几章的数学基础介绍的相当不错. 第一部分基本就是统计学习最基础的线性代数,概率论等,第4章值得一读,讲了些数值分析里常涉 ...

  6. Supervisor 的安装与配置教程

    简介 Supervisor是一个进程控制系统. 它是一个C/S系统(注意: 其提供WEB接口给用户查询和控制), 它允许用户去监控和控制在类UNIX系统的进程. 它的目标与launchd, daemo ...

  7. [Zabbix] 怎样实现邮件报警通知以及免费短信报警通知

     前提条件: (1) zabbixserver端已经安装成功而且执行. (2) zabbixclient已经成功建立而且执行. 1 下载而且安装msmtp软件 Wget http://sourcefo ...

  8. .Net中常用的几种ActionResult

    1.ViewResult 表示一个视图结果,它根据视图模板产生应答内容.对应得Controller方法为View. 2.PartialViewResult 表示一个部分视图结果,与ViewResult ...

  9. android--动态加载、插件化

    需求驱动 随着业务发展需要和无线部门的拆分,各业务产品模块归属到各业务BU,原有无线App开发团队被分为基础框架.业务A.业务B.业务C等多个开发团队,从此App的开发和发布进入了一个全新模式.在这种 ...

  10. android 移植ffmpeg后so库的使用

    今天折腾了一天,可算是有所收获,成功的用jni调用了libffmpeg中的一个方法-----avcodec_version(),至于avcodec_version()是干什么用的我不大清楚,应该是获取 ...