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;
}