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

Posted Answer1215

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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. 

以上是关于[Javascript AST] 0. Introduction: Write a simple BabelJS plugin的主要内容,如果未能解决你的问题,请参考以下文章

AST抽象语法树——最基础的javascript重点知识,99%的人根本不了解

操作JavaScript的AST

AST抽象语法树——最基础的JavaScript重点知识

AST抽象语法树 Javascript版

操作JavaScript的AST

操作JavaScript的AST