Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I prevent Babel from traversing code inserted by a plugin?

Tags:

I'm building a plugin that inserts enterFunction() in front of every existing function call by calling path.insertBefore. So my code is transformed from:

myFunction(); 

To:

enterFunction(); myFunction(); 

The problem is that when I insert the node Babel once again traverses the inserted node. Here's the logging output:

'CallExpression', 'myFunction'
'CallExpression', 'enterFunction'

How can I prevent Babel from entering the enterFunction call expression and its children?

This is the code I'm currently using for my Babel plugin:

function(babel) {     return {         visitor: {             CallExpression: function(path) {                 console.log("CallExpression", path.node.callee.name)                 if (path.node.ignore) {                     return;                 }                 path.node.ignore = true                  var enterCall = babel.types.callExpression(                     babel.types.identifier("enterFunction"), []                 )                 enterCall.ignore = true;                 path.insertBefore(enterCall)             }         }     } } 
like image 790
Matt Zeunert Avatar asked Oct 10 '16 15:10

Matt Zeunert


People also ask

How does Babel JS work?

Babel is a tool that lets you write your Javascript code using all the latest syntax and features, and run it in browsers that may not support those features. Babel is a transpiler that will translate your modern JS code into an older version of Javscript that more browsers are able to understand.


1 Answers

The Babel Handbook mentions the following section:

If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.

BinaryExpression(path) {   if (path.node.operator !== '**') return; } 

If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:

path.skip() skips traversing the children of the current path. path.stop() stops traversal entirely.

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   } }); 

In short, use path.skip() to skip traversing the children of the current path. One application of this method is illustrated in this snippet using Visitors, CallExpression and skip():

export default function (babel) {   const { types: t } = babel;    return {     name: "ast-transform", // not required     visitor: {       CallExpression(path) {         path.replaceWith(t.blockStatement([           t.expressionStatement(t.yieldExpression(path.node))         ]));         path.skip();       }     }   }; } 
like image 193
meisterluk Avatar answered Sep 29 '22 10:09

meisterluk