javascriptbabeljsbabel-plugin

Is there a way to know if Identifier node is a variable name when traversing AST?


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!


Solution

  • Turned out babel already provides methods to check if a certain Identifier is a variable:

    1. path.isBindingIdentifier() returns true if the identifier creates a binding in the scope (e.g. a variable is being declared).
    2. path.isReferencedIdentifier() - if a variable (including globals) is being referenced. This one is mentioned in babel handbook: https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md#toc-check-if-an-identifier-is-referenced