static fromBabelNode()

in packages/babel-plugin-fbt/src/fbt-nodes/FbtImplicitParamNode.js [186:304]


  static fromBabelNode({
    moduleName,
    node,
  }: FromBabelNodeFunctionArgs): ?FbtImplicitParamNode {
    if (!isJSXElement(node)) {
      return null;
    }
    const implicitParam = new FbtImplicitParamNode({
      moduleName,
      node,
    });

    const fbtChildren: Array<?FbtChildNode> = [];
    // The last BabelNode child converted to FbtChildNode and added to `fbtChildren`
    let lastAddedChild = null;
    // Keep track of the last whitespace that succeeds a non JSXText child,
    // and we will convert it to a FbtTextNode and add it to `fbtChildren`
    // ONLY IF the succeeding child is a JSXText.
    let unusedWhitespaceChild: ?BabelNodeJSXText = null;
    const firstChild = node.children[0];
    const lastChild = node.children[node.children.length - 1];
    for (const child of node.children) {
      switch (child.type) {
        case 'JSXText':
          // TODO: T38897324 (#32) Fix space normalization.
          // Here we voluntarily ignore white spaces that don't neighbor raw text
          // for the sake of being consistent with the logic in PHP
          if (child.value.trim() === '') {
            if (
              // Do not skip leading and trailing whitespaces
              firstChild !== child &&
              lastChild !== child &&
              lastAddedChild?.type !== 'JSXText'
            ) {
              unusedWhitespaceChild = child;
              break;
            }
          } else if (unusedWhitespaceChild != null) {
            fbtChildren.push(
              FbtTextNode.fromBabelNode({
                moduleName,
                node: unusedWhitespaceChild,
              }),
            );
            unusedWhitespaceChild = null;
          }
          fbtChildren.push(
            FbtTextNode.fromBabelNode({moduleName, node: child}),
          );
          lastAddedChild = child;
          break;

        case 'JSXExpressionContainer': {
          const {expression} = child;
          if (
            isBinaryExpression(expression) ||
            isStringLiteral(expression) ||
            isTemplateLiteral(expression)
          ) {
            const elements =
              convertToStringArrayNodeIfNeeded(moduleName, expression)
                .elements || ([]: Array<null>);

            elements.forEach(elem => {
              if (elem == null) {
                return;
              }
              if (elem.type !== 'StringLiteral') {
                throw errorAt(
                  child,
                  `${moduleName}: only string literals (or concatenations of string literals) ` +
                    `are supported inside JSX expressions, ` +
                    `but we found the node type "${elem.type}" instead.`,
                  {suggestOSSWebsite: true},
                );
              }
              fbtChildren.push(
                FbtElementNode.createChildNode({moduleName, node: elem}),
              );
            });
            unusedWhitespaceChild = null;
            lastAddedChild = child;
            continue;
          }

          if (expression.type === 'JSXEmptyExpression') {
            // usually, it's a comment inside a JSX expression
            continue;
          }

          fbtChildren.push(
            FbtElementNode.createChildNode({moduleName, node: expression}),
          );
          unusedWhitespaceChild = null;
          lastAddedChild = child;
          break;
        }

        case 'JSXElement': {
          fbtChildren.push(
            FbtElementNode.createChildNode({moduleName, node: child}),
          );
          unusedWhitespaceChild = null;
          lastAddedChild = child;
          break;
        }

        default:
          throw errorAt(
            child,
            `${moduleName}: unsupported babel node: ${child.type}`,
            {suggestOSSWebsite: true},
          );
      }
    }

    fbtChildren.forEach(child => implicitParam.appendChild(child));
    return implicitParam;
  }