async function findMembersPerAstEntry(babelAst, fullCurrentFilePath, projectPath) {
  // The transformed entry
  const classesFound = [];
  /**
   * Detects private/publicness based on underscores. Checks '$' as well
   * @param {string} name
   * @returns {'public'|'protected'|'private'}
   */
  function computeAccessType(name) {
    if (name.startsWith('_') || name.startsWith('$')) {
      // (at least) 2 prefixes
      if (name.startsWith('__') || name.startsWith('$$')) {
        return 'private';
      }
      return 'protected';
    }
    return 'public';
  }

  /**
   * @param {{node:ClassMethod}} cfg
   * @returns
   */
  function isStaticProperties({ node }) {
    return node.static && node.kind === 'get' && node.key.name === 'properties';
  }

  // function isBlacklisted({ node }) {
  //   // Handle static getters
  //   const sgBlacklistPlatform = ['attributes'];
  //   const sgBlacklistLitEl = ['properties', 'styles'];
  //   const sgBlacklistLion = ['localizeNamespaces'];
  //   const sgBlacklist = [...sgBlacklistPlatform, ...sgBlacklistLitEl, ...sgBlacklistLion];
  //   if (node.kind === 'get' && node.static && sgBlacklist.includes(node.key.name)) {
  //     return true;
  //   }
  //   // Handle getters
  //   const gBlacklistLitEl = ['updateComplete'];
  //   const gBlacklistLion = ['slots'];
  //   const gBlacklist = [...gBlacklistLion, ...gBlacklistLitEl];
  //   if (node.kind === 'get' && !node.static && gBlacklist.includes(node.key.name)) {
  //     return true;
  //   }
  //   // Handle methods
  //   const mBlacklistPlatform = ['constructor', 'connectedCallback', 'disconnectedCallback'];
  //   const mBlacklistLitEl = [
  //     'requestUpdate',
  //     'createRenderRoot',
  //     'render',
  //     'updated',
  //     'firstUpdated',
  //     'update',
  //     'shouldUpdate',
  //   ];
  //   const mBlacklistLion = ['onLocaleUpdated'];
  //   const mBlacklist = [...mBlacklistPlatform, ...mBlacklistLitEl, ...mBlacklistLion];
  //   if (!node.static && mBlacklist.includes(node.key.name)) {
  //     return true;
  //   }
  //   return false;
  // }

  /**
   *
   * @param {NodePath} astPath
   * @param {{isMixin?:boolean}} opts
   */
  async function traverseClass(astPath, { isMixin = false } = {}) {
    const classRes = {};
    classRes.name = astPath.node.id && astPath.node.id.name;
    classRes.isMixin = Boolean(isMixin);
    if (astPath.node.superClass) {
      const superClasses = [];

      // Add all Identifier names
      let parent = astPath.node.superClass;
      while (parent.type === 'CallExpression') {
        superClasses.push({ name: parent.callee.name, isMixin: true });
        // As long as we are a CallExpression, we will have a parent
        [parent] = parent.arguments;
      }
      // At the end of the chain, we find type === Identifier
      superClasses.push({ name: parent.name, isMixin: false });

      // For all found superclasses, track down their root location.
      // This will either result in a local, relative astPath in the project,
      // or an external astPath like '@lion/overlays'. In the latter case,
      // tracking down will halt and should be done when there is access to
      // the external repo... (similar to how 'match-imports' analyzer works)

      for (const classObj of superClasses) {
        // Finds the file that holds the declaration of the import
        classObj.rootFile = await trackDownIdentifierFromScope(
          astPath,
          classObj.name,
          fullCurrentFilePath,
          projectPath,
        );
      }
      classRes.superClasses = superClasses;
    }

    classRes.members = {
      // meta: private, public, getter/setter, (found in static get properties)
      props: [],
      // meta: private, public, getter/setter
      methods: [],
    };

    const handleMethodDefinitionOrClassMethod = astPath => {
      // if (isBlacklisted(astPath)) {
      //   return;
      // }
      if (isStaticProperties(astPath)) {
        let hasFoundTopLvlObjExpr = false;
        astPath.traverse({
          ObjectExpression(astPath) {
            if (hasFoundTopLvlObjExpr) return;
            hasFoundTopLvlObjExpr = true;
            astPath.node.properties.forEach(objectProperty => {
              if (!isProperty(objectProperty)) {
                // we can also have a SpreadElement
                return;
              }
              const propRes = {};
              const { name } = objectProperty.key;
              propRes.name = name;
              propRes.accessType = computeAccessType(name);
              propRes.kind = [...(propRes.kind || []), objectProperty.kind];
              classRes.members.props.push(propRes);
            });
          },
        });
        return;
      }

      const methodRes = {};
      const { name } = astPath.node.key;
      methodRes.name = name;
      methodRes.accessType = computeAccessType(name);

      if (astPath.node.kind === 'set' || astPath.node.kind === 'get') {
        if (astPath.node.static) {
          methodRes.static = true;
        }
        methodRes.kind = [...(methodRes.kind || []), astPath.node.kind];
        // Merge getter/setters into one
        const found = classRes.members.props.find(p => p.name === name);
        if (found) {
          found.kind = [...(found.kind || []), astPath.node.kind];
        } else {
          classRes.members.props.push(methodRes);
        }
      } else {
        classRes.members.methods.push(methodRes);
      }
    };

    astPath.traverse({
      ClassMethod: handleMethodDefinitionOrClassMethod,
      MethodDefinition: handleMethodDefinitionOrClassMethod,
    });

    classesFound.push(classRes);
  }

  const classesToTraverse = [];

  oxcTraverse(babelAst, {
    ClassDeclaration(astPath) {
      classesToTraverse.push({ astPath, isMixin: false });
    },
    ClassExpression(astPath) {
      classesToTraverse.push({ astPath, isMixin: true });
    },
  });

  for (const klass of classesToTraverse) {
    await traverseClass(klass.astPath, { isMixin: klass.isMixin });
  }

  return classesFound;
}
Finds import specifiers and sources
@param {File} babelAst
@param {string} fullCurrentFilePath the file being currently processed