@samitouri / QOS-React-2 / commits / e54e2e6ee4

Distinguish JSXText/StringLiteral during codegen

Fixes an issue with incorrect spacing where spaces were getting dropped, despite an explicit `{" "}` in the input. The issue is that we didn't maintain JSXText all the way through compilation. BuildHIR distinguishes string literals (such as the above, inside an expressioncontainer) from JSXText, and we propagate this distinction all the way through to codegen. But then codegen stores temporary values as `t.Expression` nodes, which means we have to convert the JSXText nodes to StringLiteral and we lose the distinction. This PR updates codegen to save temporaries as `t.Expression | t.JSXText` so that we can preserve the difference. In most places we just coerce the value to an expression, but the code for emitting JSX child items looks at the raw value so it can distinguish them. JSXText is emitted as-is, while StringLiterals are always wrapped in an expression container. See the new test case which demonstrates the expression being preserved.

Joe Savona committed Oct 2, 2023 at 14:58 UTC e54e2e6ee4a74da6ead96a04e200014c926ecc19
9 files changed +226 -73
compiler/packages/babel-plugin-react-forget/src/HIR/PrintHIR.ts
+4 -1
@@ -335,7 +335,10 @@ export function printInstructionValue(instrValue: ReactiveValue): string {
335 )}(${instrValue.args.map((arg) => printPattern(arg)).join(", ")})`;
336 break;
337 }
338 - case "JSXText":
338 + case "JSXText": {
339 + value = `JSXText ${JSON.stringify(instrValue.value)}`;
340 + break;
341 + }
342 case "Primitive": {
343 if (instrValue.value === undefined) {
344 value = "<undefined>";
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+105 -69
@@ -418,9 +418,9 @@ function codegenTerminal(
418 case "for": {
419 return t.forStatement(
420 codegenForInit(cx, terminal.init),
421 - codegenInstructionValue(cx, terminal.test),
421 + codegenInstructionValueToExpression(cx, terminal.test),
422 terminal.update !== null
423 - ? codegenInstructionValue(cx, terminal.update)
423 + ? codegenInstructionValueToExpression(cx, terminal.update)
424 : null,
425 codegenBlock(cx, terminal.loop)
426 );
@@ -504,7 +504,7 @@ function codegenTerminal(
504 createVariableDeclaration(iterableItem.value.loc, varDeclKind, [
505 t.variableDeclarator(lval, null),
506 ]),
507 - codegenInstructionValue(cx, iterableCollection.value),
507 + codegenInstructionValueToExpression(cx, iterableCollection.value),
508 codegenBlock(cx, terminal.loop)
509 );
510 } else {
@@ -514,13 +514,13 @@ function codegenTerminal(
514 createVariableDeclaration(iterableItem.value.loc, varDeclKind, [
515 t.variableDeclarator(lval, null),
516 ]),
517 - codegenInstructionValue(cx, iterableCollection.value),
517 + codegenInstructionValueToExpression(cx, iterableCollection.value),
518 codegenBlock(cx, terminal.loop)
519 );
520 }
521 }
522 case "if": {
523 - const test = codegenPlace(cx, terminal.test);
523 + const test = codegenPlaceToExpression(cx, terminal.test);
524 const consequent = codegenBlock(cx, terminal.consequent);
525 let alternate: t.Statement | null = null;
526 if (terminal.alternate !== null) {
@@ -532,7 +532,7 @@ function codegenTerminal(
532 return t.ifStatement(test, consequent, alternate);
533 }
534 case "return": {
535 - const value = codegenPlace(cx, terminal.value);
535 + const value = codegenPlaceToExpression(cx, terminal.value);
536 if (value.type === "Identifier" && value.name === "undefined") {
537 // Use implicit undefined
538 return t.returnStatement();
@@ -541,24 +541,26 @@ function codegenTerminal(
541 }
542 case "switch": {
543 return t.switchStatement(
544 - codegenPlace(cx, terminal.test),
544 + codegenPlaceToExpression(cx, terminal.test),
545 terminal.cases.map((case_) => {
546 const test =
547 - case_.test !== null ? codegenPlace(cx, case_.test) : null;
547 + case_.test !== null
548 + ? codegenPlaceToExpression(cx, case_.test)
549 + : null;
550 const block = codegenBlock(cx, case_.block!);
551 return t.switchCase(test, [block]);
552 })
553 );
554 }
555 case "throw": {
554 - return t.throwStatement(codegenPlace(cx, terminal.value));
556 + return t.throwStatement(codegenPlaceToExpression(cx, terminal.value));
557 }
558 case "do-while": {
557 - const test = codegenInstructionValue(cx, terminal.test);
559 + const test = codegenInstructionValueToExpression(cx, terminal.test);
560 return t.doWhileStatement(test, codegenBlock(cx, terminal.loop));
561 }
562 case "while": {
561 - const test = codegenInstructionValue(cx, terminal.test);
563 + const test = codegenInstructionValueToExpression(cx, terminal.test);
564 return t.whileStatement(test, codegenBlock(cx, terminal.loop));
565 }
566 case "label": {
@@ -603,10 +605,10 @@ function codegenInstructionNullable(
605 ? InstructionKind.Reassign
606 : kind;
607 lvalue = instr.value.lvalue.place;
606 - value = codegenPlace(cx, instr.value.value);
608 + value = codegenPlaceToExpression(cx, instr.value.value);
609 } else if (instr.value.kind === "StoreContext") {
610 lvalue = instr.value.lvalue.place;
609 - value = codegenPlace(cx, instr.value.value);
611 + value = codegenPlaceToExpression(cx, instr.value.value);
612 } else if (
613 instr.value.kind === "DeclareLocal" ||
614 instr.value.kind === "DeclareContext"
@@ -643,7 +645,7 @@ function codegenInstructionNullable(
645 } else if (hasReasign) {
646 kind = InstructionKind.Reassign;
647 }
646 - value = codegenPlace(cx, instr.value.value);
648 + value = codegenPlaceToExpression(cx, instr.value.value);
649 }
650 switch (kind) {
651 case InstructionKind.Const: {
@@ -749,7 +751,7 @@ function codegenForInit(
751 });
752 return declaration;
753 } else {
752 - return codegenInstructionValue(cx, init);
754 + return codegenInstructionValueToExpression(cx, init);
755 }
756 }
757
@@ -804,7 +806,7 @@ const createJsxText = withLoc(t.jsxText);
806 const createJsxClosingElement = withLoc(t.jsxClosingElement);
807 const createStringLiteral = withLoc(t.stringLiteral);
808
807 -type Temporaries = Map<IdentifierId, t.Expression | null>;
809 +type Temporaries = Map<IdentifierId, t.Expression | t.JSXText | null>;
810
811 function codegenLabel(id: BlockId): string {
812 return `bb${id}`;
@@ -813,48 +815,69 @@ function codegenLabel(id: BlockId): string {
815 function codegenInstruction(
816 cx: Context,
817 instr: ReactiveInstruction,
816 - value: t.Expression
818 + value: t.Expression | t.JSXText
819 ): t.Statement {
820 if (t.isStatement(value)) {
821 return value;
822 }
823 if (instr.lvalue === null) {
822 - return t.expressionStatement(value);
824 + return t.expressionStatement(convertValueToExpression(value));
825 }
826 if (instr.lvalue.identifier.name === null) {
827 // temporary
828 cx.temp.set(instr.lvalue.identifier.id, value);
829 return t.emptyStatement();
830 } else {
831 + const expressionValue = convertValueToExpression(value);
832 if (cx.hasDeclared(instr.lvalue.identifier)) {
833 return createExpressionStatement(
834 instr.loc,
835 t.assignmentExpression(
836 "=",
837 convertIdentifier(instr.lvalue.identifier),
835 - value
838 + expressionValue
839 )
840 );
841 } else {
842 return createVariableDeclaration(instr.loc, "const", [
840 - t.variableDeclarator(convertIdentifier(instr.lvalue.identifier), value),
843 + t.variableDeclarator(
844 + convertIdentifier(instr.lvalue.identifier),
845 + expressionValue
846 + ),
847 ]);
848 }
849 }
850 }
851
846 -function codegenInstructionValue(
852 +function convertValueToExpression(
853 + value: t.JSXText | t.Expression
854 +): t.Expression {
855 + if (value.type === "JSXText") {
856 + return createStringLiteral(value.loc, value.value);
857 + }
858 + return value;
859 +}
860 +
861 +function codegenInstructionValueToExpression(
862 cx: Context,
863 instrValue: ReactiveValue
864 ): t.Expression {
850 - let value: t.Expression;
865 + const value = codegenInstructionValue(cx, instrValue);
866 + return convertValueToExpression(value);
867 +}
868 +
869 +function codegenInstructionValue(
870 + cx: Context,
871 + instrValue: ReactiveValue
872 +): t.Expression | t.JSXText {
873 + let value: t.Expression | t.JSXText;
874 switch (instrValue.kind) {
875 case "ArrayExpression": {
876 const elements = instrValue.elements.map((element) => {
877 if (element.kind === "Identifier") {
855 - return codegenPlace(cx, element);
878 + return codegenPlaceToExpression(cx, element);
879 } else if (element.kind === "Spread") {
857 - return t.spreadElement(codegenPlace(cx, element.place));
880 + return t.spreadElement(codegenPlaceToExpression(cx, element.place));
881 } else {
882 return null;
883 }
@@ -863,8 +886,8 @@ function codegenInstructionValue(
886 break;
887 }
888 case "BinaryExpression": {
866 - const left = codegenPlace(cx, instrValue.left);
867 - const right = codegenPlace(cx, instrValue.right);
889 + const left = codegenPlaceToExpression(cx, instrValue.left);
890 + const right = codegenPlaceToExpression(cx, instrValue.right);
891 value = createBinaryExpression(
892 instrValue.loc,
893 instrValue.operator,
@@ -876,7 +899,7 @@ function codegenInstructionValue(
899 case "UnaryExpression": {
900 value = t.unaryExpression(
901 instrValue.operator as "throw", // todo
879 - codegenPlace(cx, instrValue.value)
902 + codegenPlaceToExpression(cx, instrValue.value)
903 );
904 break;
905 }
@@ -885,13 +908,16 @@ function codegenInstructionValue(
908 break;
909 }
910 case "CallExpression": {
888 - const callee = codegenPlace(cx, instrValue.callee);
911 + const callee = codegenPlaceToExpression(cx, instrValue.callee);
912 const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
913 value = createCallExpression(instrValue.loc, callee, args);
914 break;
915 }
916 case "OptionalExpression": {
894 - const optionalValue = codegenInstructionValue(cx, instrValue.value);
917 + const optionalValue = codegenInstructionValueToExpression(
918 + cx,
919 + instrValue.value
920 + );
921 switch (optionalValue.type) {
922 case "OptionalCallExpression":
923 case "CallExpression": {
@@ -938,7 +964,7 @@ function codegenInstructionValue(
964 break;
965 }
966 case "MethodCall": {
941 - const memberExpr = codegenPlace(cx, instrValue.property);
967 + const memberExpr = codegenPlaceToExpression(cx, instrValue.property);
968 CompilerError.invariant(
969 t.isMemberExpression(memberExpr) ||
970 t.isOptionalMemberExpression(memberExpr),
@@ -954,7 +980,7 @@ function codegenInstructionValue(
980 CompilerError.invariant(
981 t.isNodesEquivalent(
982 memberExpr.object,
957 - codegenPlace(cx, instrValue.receiver)
983 + codegenPlaceToExpression(cx, instrValue.receiver)
984 ),
985 {
986 reason:
@@ -970,7 +996,7 @@ function codegenInstructionValue(
996 break;
997 }
998 case "NewExpression": {
973 - const callee = codegenPlace(cx, instrValue.callee);
999 + const callee = codegenPlaceToExpression(cx, instrValue.callee);
1000 const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
1001 value = t.newExpression(callee, args);
1002 break;
@@ -983,7 +1009,7 @@ function codegenInstructionValue(
1009
1010 switch (property.type) {
1011 case "property": {
986 - const value = codegenPlace(cx, property.place);
1012 + const value = codegenPlaceToExpression(cx, property.place);
1013 properties.push(
1014 t.objectProperty(
1015 key,
@@ -1029,14 +1055,16 @@ function codegenInstructionValue(
1055 );
1056 }
1057 } else {
1032 - properties.push(t.spreadElement(codegenPlace(cx, property.place)));
1058 + properties.push(
1059 + t.spreadElement(codegenPlaceToExpression(cx, property.place))
1060 + );
1061 }
1062 }
1063 value = t.objectExpression(properties);
1064 break;
1065 }
1066 case "JSXText": {
1039 - value = createStringLiteral(instrValue.loc, instrValue.value);
1067 + value = createJsxText(instrValue.loc, instrValue.value);
1068 break;
1069 }
1070 case "JsxExpression": {
@@ -1046,7 +1074,7 @@ function codegenInstructionValue(
1074 }
1075 let tagValue =
1076 instrValue.tag.kind === "Identifier"
1049 - ? codegenPlace(cx, instrValue.tag)
1077 + ? codegenPlaceToExpression(cx, instrValue.tag)
1078 : t.stringLiteral(instrValue.tag.name);
1079 let tag: t.JSXIdentifier | t.JSXNamespacedName | t.JSXMemberExpression;
1080 if (tagValue.type === "Identifier") {
@@ -1130,15 +1158,15 @@ function codegenInstructionValue(
1158 value = t.assignmentExpression(
1159 "=",
1160 t.memberExpression(
1133 - codegenPlace(cx, instrValue.object),
1161 + codegenPlaceToExpression(cx, instrValue.object),
1162 t.identifier(instrValue.property)
1163 ),
1136 - codegenPlace(cx, instrValue.value)
1164 + codegenPlaceToExpression(cx, instrValue.value)
1165 );
1166 break;
1167 }
1168 case "PropertyLoad": {
1141 - const object = codegenPlace(cx, instrValue.object);
1169 + const object = codegenPlaceToExpression(cx, instrValue.object);
1170 // We currently only lower single chains of optional memberexpr.
1171 // (See BuildHIR.ts for more detail.)
1172 value = t.memberExpression(
@@ -1152,7 +1180,7 @@ function codegenInstructionValue(
1180 value = t.unaryExpression(
1181 "delete",
1182 t.memberExpression(
1155 - codegenPlace(cx, instrValue.object),
1183 + codegenPlaceToExpression(cx, instrValue.object),
1184 t.identifier(instrValue.property)
1185 )
1186 );
@@ -1162,17 +1190,17 @@ function codegenInstructionValue(
1190 value = t.assignmentExpression(
1191 "=",
1192 t.memberExpression(
1165 - codegenPlace(cx, instrValue.object),
1166 - codegenPlace(cx, instrValue.property),
1193 + codegenPlaceToExpression(cx, instrValue.object),
1194 + codegenPlaceToExpression(cx, instrValue.property),
1195 true
1196 ),
1169 - codegenPlace(cx, instrValue.value)
1197 + codegenPlaceToExpression(cx, instrValue.value)
1198 );
1199 break;
1200 }
1201 case "ComputedLoad": {
1174 - const object = codegenPlace(cx, instrValue.object);
1175 - const property = codegenPlace(cx, instrValue.property);
1202 + const object = codegenPlaceToExpression(cx, instrValue.object);
1203 + const property = codegenPlaceToExpression(cx, instrValue.property);
1204 value = t.memberExpression(object, property, true);
1205 break;
1206 }
@@ -1180,8 +1208,8 @@ function codegenInstructionValue(
1208 value = t.unaryExpression(
1209 "delete",
1210 t.memberExpression(
1183 - codegenPlace(cx, instrValue.object),
1184 - codegenPlace(cx, instrValue.property),
1211 + codegenPlaceToExpression(cx, instrValue.object),
1212 + codegenPlaceToExpression(cx, instrValue.property),
1213 true
1214 )
1215 );
@@ -1189,7 +1217,7 @@ function codegenInstructionValue(
1217 }
1218 case "LoadLocal":
1219 case "LoadContext": {
1192 - value = codegenPlace(cx, instrValue.place);
1220 + value = codegenPlaceToExpression(cx, instrValue.place);
1221 break;
1222 }
1223 case "FunctionExpression": {
@@ -1223,14 +1251,14 @@ function codegenInstructionValue(
1251 case "TaggedTemplateExpression": {
1252 value = createTaggedTemplateExpression(
1253 instrValue.loc,
1226 - codegenPlace(cx, instrValue.tag),
1254 + codegenPlaceToExpression(cx, instrValue.tag),
1255 t.templateLiteral([t.templateElement(instrValue.value)], [])
1256 );
1257 break;
1258 }
1259 case "TypeCastExpression": {
1260 value = t.typeCastExpression(
1233 - codegenPlace(cx, instrValue.value),
1261 + codegenPlaceToExpression(cx, instrValue.value),
1262 instrValue.type
1263 );
1264 break;
@@ -1239,17 +1267,17 @@ function codegenInstructionValue(
1267 value = createLogicalExpression(
1268 instrValue.loc,
1269 instrValue.operator,
1242 - codegenInstructionValue(cx, instrValue.left),
1243 - codegenInstructionValue(cx, instrValue.right)
1270 + codegenInstructionValueToExpression(cx, instrValue.left),
1271 + codegenInstructionValueToExpression(cx, instrValue.right)
1272 );
1273 break;
1274 }
1275 case "ConditionalExpression": {
1276 value = createConditionalExpression(
1277 instrValue.loc,
1250 - codegenInstructionValue(cx, instrValue.test),
1251 - codegenInstructionValue(cx, instrValue.consequent),
1252 - codegenInstructionValue(cx, instrValue.alternate)
1278 + codegenInstructionValueToExpression(cx, instrValue.test),
1279 + codegenInstructionValueToExpression(cx, instrValue.consequent),
1280 + codegenInstructionValueToExpression(cx, instrValue.alternate)
1281 );
1282 break;
1283 }
@@ -1288,11 +1316,11 @@ function codegenInstructionValue(
1316 }
1317 });
1318 if (expressions.length === 0) {
1291 - value = codegenInstructionValue(cx, instrValue.value);
1319 + value = codegenInstructionValueToExpression(cx, instrValue.value);
1320 } else {
1321 value = createSequenceExpression(instrValue.loc, [
1322 ...expressions,
1295 - codegenInstructionValue(cx, instrValue.value),
1323 + codegenInstructionValueToExpression(cx, instrValue.value),
1324 ]);
1325 }
1326 break;
@@ -1301,7 +1329,7 @@ function codegenInstructionValue(
1329 value = createTemplateLiteral(
1330 instrValue.loc,
1331 instrValue.quasis.map((q) => t.templateElement(q)),
1304 - instrValue.subexprs.map((p) => codegenPlace(cx, p))
1332 + instrValue.subexprs.map((p) => codegenPlaceToExpression(cx, p))
1333 );
1334 break;
1335 }
@@ -1314,21 +1342,21 @@ function codegenInstructionValue(
1342 break;
1343 }
1344 case "Await": {
1317 - value = t.awaitExpression(codegenPlace(cx, instrValue.value));
1345 + value = t.awaitExpression(codegenPlaceToExpression(cx, instrValue.value));
1346 break;
1347 }
1348 case "NextIterableOf": {
1321 - value = codegenPlace(cx, instrValue.value);
1349 + value = codegenPlaceToExpression(cx, instrValue.value);
1350 break;
1351 }
1352 case "NextPropertyOf": {
1325 - value = codegenPlace(cx, instrValue.value);
1353 + value = codegenPlaceToExpression(cx, instrValue.value);
1354 break;
1355 }
1356 case "PostfixUpdate": {
1357 value = t.updateExpression(
1358 instrValue.operation,
1331 - codegenPlace(cx, instrValue.lvalue),
1359 + codegenPlaceToExpression(cx, instrValue.lvalue),
1360 false
1361 );
1362 break;
@@ -1336,7 +1364,7 @@ function codegenInstructionValue(
1364 case "PrefixUpdate": {
1365 value = t.updateExpression(
1366 instrValue.operation,
1339 - codegenPlace(cx, instrValue.lvalue),
1367 + codegenPlaceToExpression(cx, instrValue.lvalue),
1368 true
1369 );
1370 break;
@@ -1382,7 +1410,7 @@ function codegenJsxAttribute(
1410 createJsxIdentifier(attribute.place.loc, name)
1411 );
1412 }
1385 - const innerValue = codegenPlace(cx, attribute.place);
1413 + const innerValue = codegenPlaceToExpression(cx, attribute.place);
1414 let value;
1415 switch (innerValue.type) {
1416 case "StringLiteral": {
@@ -1401,7 +1429,9 @@ function codegenJsxAttribute(
1429 return createJsxAttribute(attribute.place.loc, propName, value);
1430 }
1431 case "JsxSpreadAttribute": {
1404 - return t.jsxSpreadAttribute(codegenPlace(cx, attribute.argument));
1432 + return t.jsxSpreadAttribute(
1433 + codegenPlaceToExpression(cx, attribute.argument)
1434 + );
1435 }
1436 default: {
1437 assertExhaustive(
@@ -1423,7 +1453,7 @@ function codegenJsxElement(
1453 | t.JSXFragment {
1454 const value = codegenPlace(cx, place);
1455 switch (value.type) {
1426 - case "StringLiteral": {
1456 + case "JSXText": {
1457 return createJsxText(place.loc, value.value);
1458 }
1459 case "JSXElement":
@@ -1448,6 +1478,7 @@ function codegenJsxFbtChildElement(
1478 const value = codegenPlace(cx, place);
1479 switch (value.type) {
1480 // fbt:param only allows JSX element or expression container as children
1481 + case "JSXText":
1482 case "JSXElement": {
1483 return value;
1484 }
@@ -1565,13 +1596,18 @@ function codegenArgument(
1596 arg: Place | SpreadPattern
1597 ): t.Expression | t.SpreadElement {
1598 if (arg.kind === "Identifier") {
1568 - return codegenPlace(cx, arg);
1599 + return codegenPlaceToExpression(cx, arg);
1600 } else {
1570 - return t.spreadElement(codegenPlace(cx, arg.place));
1601 + return t.spreadElement(codegenPlaceToExpression(cx, arg.place));
1602 }
1603 }
1604
1574 -function codegenPlace(cx: Context, place: Place): t.Expression {
1605 +function codegenPlaceToExpression(cx: Context, place: Place): t.Expression {
1606 + const value = codegenPlace(cx, place);
1607 + return convertValueToExpression(value);
1608 +}
1609 +
1610 +function codegenPlace(cx: Context, place: Place): t.Expression | t.JSXText {
1611 let tmp = cx.temp.get(place.identifier.id);
1612 if (tmp != null) {
1613 return tmp;
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/fbt-preserve-jsxtext.expect.md new
+54
@@ -0,0 +1,54 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +import fbt from "fbt";
6 +
7 +function Foo(props) {
8 + return (
9 + <fbt desc="Some text to be translated">
10 + <fbt:enum
11 + enum-range={{ "0": "hello", "1": "goodbye" }}
12 + value={props.value ? "0" : "1"}
13 + />{" "}
14 + <fbt:param name="value">{props.value}</fbt:param>
15 + {", "}
16 + </fbt>
17 + );
18 +}
19 +
20 +```
21 +
22 +## Code
23 +
24 +```javascript
25 +import { unstable_useMemoCache as useMemoCache } from "react";
26 +import fbt from "fbt";
27 +
28 +function Foo(props) {
29 + const $ = useMemoCache(2);
30 + const c_0 = $[0] !== props.value;
31 + let t0;
32 + if (c_0) {
33 + t0 = fbt._(
34 + { "0": "hello {value},", "1": "goodbye {value}," },
35 + [
36 + fbt._enum(props.value ? "0" : "1", { "0": "hello", "1": "goodbye" }),
37 + fbt._param(
38 + "value",
39 +
40 + props.value
41 + ),
42 + ],
43 + { hk: "Ri5kJ" }
44 + );
45 + $[0] = props.value;
46 + $[1] = t0;
47 + } else {
48 + t0 = $[1];
49 + }
50 + return t0;
51 +}
52 +
53 +```
54 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/fbt-preserve-jsxtext.js new
+14
@@ -0,0 +1,14 @@
1 +import fbt from "fbt";
2 +
3 +function Foo(props) {
4 + return (
5 + <fbt desc="Some text to be translated">
6 + <fbt:enum
7 + enum-range={{ "0": "hello", "1": "goodbye" }}
8 + value={props.value ? "0" : "1"}
9 + />{" "}
10 + <fbt:param name="value">{props.value}</fbt:param>
11 + {", "}
12 + </fbt>
13 + );
14 +}
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/jsx-fragment.expect.md
+1 -2
@@ -43,8 +43,7 @@ function Foo(props) {
43 if (c_1) {
44 t1 = (
45 <>
46 - Hello {props.greeting}
47 - {t0}
46 + Hello {props.greeting} {t0}
47 </>
48 );
49 $[1] = props.greeting;
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/preserve-jsxtext-stringliteral-distinction.expect.md new
+38
@@ -0,0 +1,38 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Foo() {
6 + return <div> {", "}</div>;
7 +}
8 +
9 +export const FIXTURE_ENTRYPOINT = {
10 + fn: Foo,
11 + params: [{}],
12 +};
13 +
14 +```
15 +
16 +## Code
17 +
18 +```javascript
19 +import { unstable_useMemoCache as useMemoCache } from "react";
20 +function Foo() {
21 + const $ = useMemoCache(1);
22 + let t0;
23 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
24 + t0 = <div> {", "}</div>;
25 + $[0] = t0;
26 + } else {
27 + t0 = $[0];
28 + }
29 + return t0;
30 +}
31 +
32 +export const FIXTURE_ENTRYPOINT = {
33 + fn: Foo,
34 + params: [{}],
35 +};
36 +
37 +```
38 +
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/preserve-jsxtext-stringliteral-distinction.js new
+8
@@ -0,0 +1,8 @@
1 +function Foo() {
2 + return <div> {", "}</div>;
3 +}
4 +
5 +export const FIXTURE_ENTRYPOINT = {
6 + fn: Foo,
7 + params: [{}],
8 +};
compiler/packages/snap/src/compiler-worker.ts
+1 -1
@@ -88,7 +88,7 @@ export async function compile(
88 runReactForgetBabelPlugin
89 ).code;
90 } catch (e) {
91 - e.message = e.message.replace(/\u001b[^m]*m/g, '');
91 + e.message = e.message.replace(/\u001b[^m]*m/g, "");
92 error = e;
93 }
94
compiler/packages/sprout/src/SproutTodoFilter.ts
+1
@@ -459,6 +459,7 @@ const skipFilter = new Set([
459 "fbtparam-with-jsx-element-content",
460 "fbtparam-text-must-use-expression-container",
461 "fbtparam-with-jsx-fragment-value",
462 + "fbt-preserve-jsxtext",
463 ]);
464
465 export default skipFilter;