function parseNodeRecursive(rootNode, childNode, cb, skipConditional = false, isolate = false, ignoredKeys = []) {
  // TODO allow vue non litteral
  let originalClassNamesValue;
  let classNames;
  if (childNode === null) {
    originalClassNamesValue = extractValueFromNode(rootNode);
    ({ classNames } = extractClassnamesFromValue(originalClassNamesValue));
    classNames = removeDuplicatesFromArray(classNames);
    if (classNames.length === 0) {
      // Don't run for empty className
      return;
    }
    cb(classNames, rootNode);
  } else if (childNode === undefined) {
    // Ignore invalid child candidates (probably inside complex TemplateLiteral)
    return;
  } else {
    const forceIsolation = skipConditional ? true : isolate;
    let trim = false;
    switch (childNode.type) {
      case 'TemplateLiteral':
        childNode.expressions.forEach((exp) => {
          parseNodeRecursive(rootNode, exp, cb, skipConditional, forceIsolation, ignoredKeys);
        });
        childNode.quasis.forEach((quasis) => {
          parseNodeRecursive(rootNode, quasis, cb, skipConditional, isolate, ignoredKeys);
        });
        return;
      case 'ConditionalExpression':
        parseNodeRecursive(rootNode, childNode.consequent, cb, skipConditional, forceIsolation, ignoredKeys);
        parseNodeRecursive(rootNode, childNode.alternate, cb, skipConditional, forceIsolation, ignoredKeys);
        return;
      case 'LogicalExpression':
        parseNodeRecursive(rootNode, childNode.right, cb, skipConditional, forceIsolation, ignoredKeys);
        return;
      case 'ArrayExpression':
        childNode.elements.forEach((el) => {
          parseNodeRecursive(rootNode, el, cb, skipConditional, forceIsolation, ignoredKeys);
        });
        return;
      case 'ObjectExpression':
        childNode.properties.forEach((prop) => {
          const isUsedByClassNamesPlugin = (rootNode.callee && rootNode.callee.name === 'classnames') || rootNode.type === 'VAttribute';

          if (prop.type === 'SpreadElement') {
            // Ignore spread elements
            return;
          }

          if (prop.key.type === 'Identifier' && ignoredKeys.includes(prop.key.name)) {
            // Ignore specific keys defined in settings
            return;
          }

          parseNodeRecursive(
            rootNode,
            isUsedByClassNamesPlugin ? prop.key : prop.value,
            cb,
            skipConditional,
            forceIsolation,
            ignoredKeys
          );
        });
        return;
      case 'Property':
        parseNodeRecursive(rootNode, childNode.key, cb, skipConditional, forceIsolation, ignoredKeys);
        return;
      case 'Literal':
        trim = true;
        originalClassNamesValue = childNode.value;
        break;
      case 'TemplateElement':
        originalClassNamesValue = childNode.value.raw;
        break;
    }
    ({ classNames } = extractClassnamesFromValue(originalClassNamesValue));
    classNames = removeDuplicatesFromArray(classNames);
    if (classNames.length === 0) {
      // Don't run for empty className
      return;
    }
    const targetNode = isolate ? null : rootNode;
    cb(classNames, targetNode);
  }
}
Inspect and parse an abstract syntax node and run a callback function

@param {ASTNode} rootNode The current root node being parsed by eslint
@param {ASTNode} childNode The AST node child argument being checked
@param {Function} cb The callback function
@param {Boolean} skipConditional Optional, indicate distinct parsing for conditional nodes
@param {Boolean} isolate Optional, set internally to isolate parsing and validation on conditional children
@param {Array} ignoredKeys Optional, set object keys which should not be parsed e.g. for `cva`
@returns {void}