I am writing a babel plugin and I would like to collect all the variables used in a particular expression. For this I am using path.traverse()
with Identifier
visitor. However, this approach is not ideal as things like property names are also classified as identifiers, while I am interested only in actual variables (identifiers that declare/access bindings in scope + global variables). So, currently, I'm doing something like this:
expressionPath.traverse({
Identifier(path) {
const {node, parent} = path;
if (!isVariableName({node, parent})) {
return;
}
console.log('identifier is a variable name', node.name);
}
});
function isVariableName({node, parent}) {
if (types.isProperty(parent) || types.isMethod(parent)) {
const isPropertyName = !parent.computed && node === parent.key;
return !isPropertyName;
}
if (
types.isMemberExpression(parent) ||
types.isOptionalMemberExpression(parent)
) {
const isPropertyName = !parent.computed && node === parent.property;
return !isPropertyName;
}
if (types.isPrivateName(parent)) {
return false;
}
return true;
}
I find this not ideal either, as I need to consider all possible use cases for identifiers (I can miss some or there can be more with new JS features - e.g. private class property names are also identifiers). scope.getBinding()
also doesn't help much here, as global variables do not have bindings (just like property names do not) and there can actually be a binding for a certain identifier name in scope, but the identifier itself can be a property name.
Do you know a better solution? I would also like to know if a variable is being declared or accessed/mutated but this is a different question I think.
Thank you!