Let's say I have the following code I want to transform using a custom Babel plugin:
let c;
c = document;
console.log(c.readyState);
The goal would be to replace all occurrences of document.readyState with a custom function, e.g. window.getDocumentReadyState().
So the output should look something like this:
let c;
c = document;
console.log(window.getDocumentReadyState());
The difficulty here is to determine which value the object c actually has when the MemberExpression Visitor is called, as I just want to replace MemberExpressions for document. That's why I need to find out if the current value of c is document.
Here's an implementation which just replaces every <obj>.readyState MemberExpression:
/**
* Replace document.readyState with
* window.getDocumentReadyState();
*/
MemberExpression(path) {
const { node, parent } = path;
const objName = node.object.name;
const propName = node.property.name;
if (t.isAssignmentExpression(parent)) {
return;
}
if (t.isCallExpression(parent)) {
const isCallee = parent.callee === node;
if (isCallee) return;
}
if (propName === 'readyState') {
const customReadyStateFn = t.callExpression(
t.memberExpression(
t.identifier('window'),
t.identifier('getDocumentReadyState'),
),
[t.identifier(objName)],
);
path.replaceWith(customReadyStateFn);
}
},
Using this implementation, I perform a runtime check to determine if the object is of type document (HTMLDocument) inside window.getDocumentReadyState, because I wasn't able to do the same using Babel.
But there must be a way to reliably tell if this variable has the value document using static analysis, right?
Essentially, I need to find the last AssignmentExpression of this variable in the current scope.
I already tried looking the variable up in the scope using path.scope.getBinding(<variableName>), but the problem is that the last AssignmentExpression doesn't show up in binding.references. If the value was assigned during declaration (let c = document), it would be no problem, because that reference can be accessed using the binding.
I also tried traversing through the scope, but the AssignmentExpression visitor wasn't invoked.
I am fairly new to Babel and ASTs and reached a point where I don't know what to do next, and I would really like to get rid of that runtime check.
How would you solve such a problem?