zoukankan      html  css  js  c++  java
  • [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. 

  • 相关阅读:
    java 原子性 可见性 有序性
    java中Array/List/Map/Object与Json互相转换详解(转载)
    观察者模式(转载)
    TCP协议
    “数字签名”与“数字证书”
    两道笔试题
    定时任务处理过程中的问题
    行数据库VS列数据库
    B树和B+树
    ThreadPoolTaskExecutor介绍
  • 原文地址:https://www.cnblogs.com/Answer1215/p/7588047.html
Copyright © 2011-2022 走看看