[Javascript AST] 0. Introduction: Write a simple BabelJS plugin

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.

时间: 2024-10-26 16:45:08

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

[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 function scope and put it into global scope. Code: function getVersion(versionString) { const versionRegex = /(\d+)\.(\d+)\.(\d+)/gi var x = /foo/.text(

javascript:;与javascript:void(0);

href=”#”,包含了一个位置信息,默认的锚是#top,也就是网页的上端,当连续快速点击此链接时会导致浏览器巨慢甚至崩溃. javascript中void是一个操作符,该操作符指定要计算一个表达式但是不返回值. javascript:void(0);据说某些情况下有浏览器兼容bug.(此点bug我也不知道什么时候能出现,知道的童鞋请指教). 我感觉这两者之间没有什么差别,都是执行一个空事件.javascript:;甚至少了7个字符. 新浪微博写的都是javascript:void(0); QQ

An internal error occurred during: "Requesting JavaScript AST from selection". GC overhead limit exc

1.错误描述 An internal error occurred during: "Requesting JavaScript AST from selection". GC overhead limit exceeded 单击"OK"后,提示如下图所示: 2.错误原因 由于用Eclipse编写JavaScript时,出现了return,位置不对,导致错误 3.解决办法 Windows--->Preference--->JavaScript (1)图一

javascript:;与javascript:void(0)使用介绍

有时候我们在编写js过程中,需要触发事件而不需要返回值,那么就可能需要这样的写法 href=”#”,包含了一个位置信息.默认的锚是#top,也就是网页的上端,当连续快速点击此链接时会导致浏览器巨慢甚至崩溃. 当然我们一般用三个 href="###",不过看了这篇文章我们以后就可以使用javascript:;(一个冒号一个分号) javascript中void是一个操作符,该操作符指定要计算一个表达式但是不返回值. javascript:;好些,javascript:void(0);据说

JavaScript中“javascript:void(0) ”是什么意思

来源: <a href="javascript:test();void(0);">here</a> 此处:Javascript中void是一个操作符,该操作符指定要计算一个表达式但是不返回值. void 操作符用法格式如下:1. javascript:void (expression)2. javascript:void expression expression 是一个要计算的 Javascript 标准的表达式.表达式外侧的圆括号是可选的,鉴于规范化,以及养

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

<a href="#"> 点击链接后,页面会向上滚到页首,# 默认锚点为 #TOP <a href="javascript:void(0)" onClick="window.open()"> 点击链接后,页面不动,只打开链接 <a href="#" onclick="javascript:return false;"> 作用同上,不同浏览器会有差异. 点击链接后,不想使页

href=&quot;javascript:void(0);&quot;与#的区别

将<a>标签设置为空链接有两种方式,第一种是href="#",另外一种是href="javascript:void(0);".两种方式都设置了标签为空链接,但是两种方式还是有些不同的地方. href="#",当点击的时候会跳转到页面的顶部,相当于点击了一个锚点,在URL的后面也会出现一个#的标识符号. 而href="javascript:void(0);"则是要执行一个javascript的表达式.void(0)不

javascript:void(0) 含义

Javascript中void是一个操作符,该操作符指定要计算一个表达式但是不返回值. void 操作符用法格式如下:1. javascript:void (expression)2. javascript:void expression expression 是一个要计算的 Javascript 标准的表达式.表达式外侧的圆括号是选的,但是写上去是一个好习惯. (实现版本 Navigator 3.0 ) 你以使用 void 操作符指定超级链接.表达式会被计算但是不会当前文档处装入任何内容. 下

javascript:void(0)的作用示例

在做页面时,如果想做一个链接点击后不做任何事情,或者响应点击而完成其他事情,可以设置其属性 href = "#",但是,这样会有一个问题,就是当页面有滚动条时,点击后会返回到页面顶端,或者是跳到设置的锚点的地方,用户体验不好. 通常的用法为: <a href="javascript:void(0)">单击此处看看效果</a> 实际上单击此处什么也不会发生,其中的javascript:void(0);形式是一个javascript的伪协议,是表