text: const codeToAst = require('../../../compiler-utils/code-to-ast');
  const walk = require('../../../compiler-utils/walk');
  
  function transformJSXText(node) {
    const textValue = node.value.trim().split('\n').map(line => line.trim()).join(' ');
  
    if (!textValue) {
      return null;
    }
    return Object.assign(node, {
      type: 'Literal',
      raw: `'${textValue}'`,
      value: textValue,
    });
  }
  function transformJSXExpressionContainer(node, parentNode) {
    const parentElements = parentNode.children || parentNode.arguments;
    parentElements[parentElements.indexOf(node)] = node.expression;
    delete node.expression;
  }
  function createSlot(node, parentNode, functional, renderArgs) {
    let slotName = 'default';
    node.openingElement.attributes.forEach((attr) => {
      if (attr.name.name === 'name') slotName = attr.value.value;
    });
    const slotObj = functional ? renderArgs[2] : 'this.slots';
    const slotNode = codeToAst(`${slotObj}['${slotName}']`).body[0].expression;
    const children = node.children;
    delete node.children;
    delete node.openingElement;
    delete node.closingElement;
    Object.assign(node, slotNode);
  
    let index = 0;
    const parentElements = parentNode.arguments || parentNode.children || parentNode.elements;
  
    children.forEach((slotChild) => {
      let child = slotChild;
      if (child.type === 'JSXText') {
        child = transformJSXText(slotChild);
        if (!child) return;
      }
      const slotChildNode = codeToAst(`!${slotObj}.${slotName} && child`).body[0].expression;
      slotChildNode.right = child;
  
      if (parentElements && Array.isArray(parentElements)) {
        parentElements.splice(parentElements.indexOf(node) + index + 1, 0, slotChildNode);
      }
      index += 1;
    });
  }
  
  function transformJSXElement(node, parentNode, helpers, functional, renderArgs) {
    const tagName = node.openingElement.name.name;
    if (tagName === 'slot') {
      helpers.slots = true;
      createSlot(node, parentNode, functional, renderArgs);
      return;
    }
    const newNode = {
      type: 'CallExpression',
      callee: {
        type: 'MemberExpression',
        computed: false,
        object: {
          type: 'Identifier',
          name: 'React',
        },
        property: {
          type: 'Identifier',
          name: 'createElement',
        },
      },
      arguments: [],
    };
    if (tagName.match(/[A-Z]/)) {
      newNode.arguments.push({
        type: 'Identifier',
        name: tagName,
      });
    } else {
      newNode.arguments.push({
        type: 'Literal',
        raw: `'${tagName}'`,
        value: tagName,
      });
    }
    if (node.openingElement.attributes.length === 0) {
      newNode.arguments.push({
        type: 'Literal',
        raw: 'null',
        value: null,
      });
    } else {
      const propsObj = {
        type: 'ObjectExpression',
        properties: [],
      };
      node.openingElement.attributes.forEach((attr) => {
        let prop;
        if (attr.type === 'JSXAttribute') {
          const namespaced = attr.name.type === 'JSXNamespacedName';
          const propKeyName = namespaced ? `${attr.name.namespace.name}:${attr.name.name.name}` : attr.name.name;
          const isStringName = propKeyName.indexOf('-') >= 0;
          prop = {
            type: 'Property',
            computed: false,
            kind: 'init',
            method: false,
            shorthand: false,
            key: {
              type: namespaced || isStringName ? 'Literal' : 'Identifier',
              name: propKeyName,
              raw: namespaced || isStringName ? `'${propKeyName}'` : propKeyName,
              value: propKeyName,
            },
          };
          if (!attr.value) {
            prop.value = {
              type: 'Literal',
              raw: 'true',
              value: true,
            };
          } else if (attr.value.type === 'JSXExpressionContainer') {
            prop.value = attr.value.expression;
          } else {
            prop.value = attr.value;
          }
          if (propKeyName === 'ref' && attr.value) {
            helpers.refs = true;
            const refValueNode = codeToAst('(__reactNode) => { this.__reactRefs[replaceMe] = __reactNode }').body[0].expression;
            refValueNode.body.body[0].expression.left.property = prop.value;
            prop.value = refValueNode;
          }
        } else if (attr.type === 'JSXSpreadAttribute') {
          prop = {
            type: 'SpreadElement',
            argument: attr.argument,
          };
        }
        if (prop) propsObj.properties.push(prop);
      });
  
      newNode.arguments.push(propsObj);
    }
    if (node.children.length > 0) {
      node.children.forEach((child) => {
        if (child.type === 'JSXText') {
          const transformedJSXText = transformJSXText(child);
          if (transformedJSXText) {
            newNode.arguments.push(child);
          }
          return;
        }
        if (child.type === 'JSXExpressionContainer') {
          // eslint-disable-next-line
          child = child.expression;
        }
        if (child.type === 'JSXElement') {
          transformJSXElement(child, node, helpers);
        }
        newNode.arguments.push(child);
      });
    }
    Object.assign(node, newNode);
    delete node.children;
    delete node.closingElement;
    delete node.openingElement;
  }
  
  const transform = ({ ast, functional, componentNode }) => {
    const helpers = {};
  
    const renderArgs = ['props', 'children', 'slots'];
    componentNode.properties.forEach((prop) => {
      if (prop.key.name === 'render' && prop.method === true && functional) {
        let needProps;
        let needChildren;
        let needSlots;
        prop.value.params.forEach((param, index) => {
          if (index === 0) needProps = true;
          if (index === 1) needChildren = true;
          if (index === 2) needSlots = true;
          renderArgs[index] = param.name;
        });
        if (needProps) {
          helpers.props = true;
          prop.value.params = [
            { type: 'Identifier', name: renderArgs[0] },
          ];
        }
        const declareVars = [];
        if (needChildren) {
          const childrenNode = codeToAst(`const ${renderArgs[1]} = ${renderArgs[0]}.children;`).body[0];
          declareVars.push(childrenNode);
        }
        if (needSlots) {
          helpers.slots = true;
          const slotsNode = codeToAst(`const ${renderArgs[2]} = __reactComponentSlots(${renderArgs[0]});`).body[0];
          declareVars.push(slotsNode);
        }
        prop.value.body.body.unshift(...declareVars);
      }
    });
  
    walk(ast, {
      JSXEmptyExpression(node, path) {
        const parent = path[path.length - 3];
        if (parent && parent.children && parent.children.length) {
          parent.children.forEach((parentNode, parentNodeIndex) => {
            if (parentNode.expression === node) {
              parent.children.splice(parentNodeIndex, 1);
            }
          });
        }
      },
      JSXExpressionContainer(node, path) {
        transformJSXExpressionContainer(node, path[path.length - 2]);
      },
      JSXElement(node, path) {
        transformJSXElement(node, path[path.length - 2], helpers, functional, renderArgs);
      },
    });
  
    walk(ast, {
      JSXElement(node, path) {
        transformJSXElement(node, path[path.length - 2], helpers, functional, renderArgs);
      },
    });
  
    return { ast, helpers };
  };
  
  module.exports = transform;
  