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

Support JsxMemberExpression

Joe Savona committed Mar 17, 2023 at 16:18 UTC a7ac20973f52f09ad1f087c4bd54067e92e7bf3a
5 files changed +127 -37
compiler/forget/src/HIR/BuildHIR.ts
+53 -16
@@ -1760,7 +1760,27 @@ function lowerJsxElementName(
1760 ): Place {
1761 const exprNode = exprPath.node;
1762 const exprLoc = exprNode.loc ?? GeneratedSource;
1763 - if (!exprPath.isJSXIdentifier()) {
1763 + if (exprPath.isJSXIdentifier()) {
1764 + const tag: string = exprPath.node.name;
1765 + if (tag.match(/^[A-Z]/)) {
1766 + return lowerIdentifier(builder, exprPath);
1767 + } else {
1768 + const place: Place = buildTemporaryPlace(builder, exprLoc);
1769 + builder.push({
1770 + id: makeInstructionId(0),
1771 + value: {
1772 + kind: "Primitive",
1773 + value: tag,
1774 + loc: exprLoc,
1775 + },
1776 + loc: exprLoc,
1777 + lvalue: { ...place },
1778 + });
1779 + return place;
1780 + }
1781 + } else if (exprPath.isJSXMemberExpression()) {
1782 + return lowerJsxMemberExpression(builder, exprPath);
1783 + } else {
1784 builder.errors.push({
1785 reason: `(BuildHIR::lowerJsxElementName) Handle ${exprPath.type} tags`,
1786 severity: ErrorSeverity.Todo,
@@ -1779,23 +1799,40 @@ function lowerJsxElementName(
1799 });
1800 return { ...place };
1801 }
1782 - const tag: string = exprPath.node.name;
1783 - if (tag.match(/^[A-Z]/)) {
1784 - return lowerIdentifier(builder, exprPath);
1802 +}
1803 +
1804 +function lowerJsxMemberExpression(
1805 + builder: HIRBuilder,
1806 + exprPath: NodePath<t.JSXMemberExpression>
1807 +): Place {
1808 + const loc = exprPath.node.loc ?? GeneratedSource;
1809 + const object = exprPath.get("object");
1810 + let objectPlace: Place;
1811 + if (object.isJSXMemberExpression()) {
1812 + objectPlace = lowerJsxMemberExpression(builder, object);
1813 } else {
1786 - const place: Place = buildTemporaryPlace(builder, exprLoc);
1787 - builder.push({
1788 - id: makeInstructionId(0),
1789 - value: {
1790 - kind: "Primitive",
1791 - value: tag,
1792 - loc: exprLoc,
1793 - },
1794 - loc: exprLoc,
1795 - lvalue: { ...place },
1796 - });
1797 - return place;
1814 + invariant(
1815 + object.isJSXIdentifier(),
1816 + "TypeScript refinement fail: expected 'JsxIdentifier', got '%s'",
1817 + object.node.type
1818 + );
1819 + objectPlace = lowerIdentifier(builder, object);
1820 }
1821 + const place = buildTemporaryPlace(builder, loc);
1822 + const property = exprPath.get("property").node.name;
1823 + builder.push({
1824 + id: makeInstructionId(0),
1825 + lvalue: { ...place },
1826 + value: {
1827 + kind: "PropertyLoad",
1828 + object: objectPlace,
1829 + property,
1830 + optional: false,
1831 + loc,
1832 + },
1833 + loc,
1834 + });
1835 + return place;
1836 }
1837
1838 function lowerJsxElement(
compiler/forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+29 -12
@@ -651,29 +651,26 @@ function codegenInstructionValue(
651 }
652 }
653 let tagValue = codegenPlace(cx, instrValue.tag);
654 - let tag: string;
654 + let tag: t.JSXIdentifier | t.JSXMemberExpression;
655 if (tagValue.type === "Identifier") {
656 - tag = tagValue.name;
656 + tag = t.jsxIdentifier(tagValue.name);
657 + } else if (tagValue.type === "MemberExpression") {
658 + tag = convertMemberExpressionToJsx(tagValue);
659 } else {
660 invariant(
661 tagValue.type === "StringLiteral",
660 - "Expected JSX tag to be an identifier or string"
662 + "Expected JSX tag to be an identifier or string, got '%s'",
663 + tagValue.type
664 );
662 - tag = tagValue.value;
665 + tag = t.jsxIdentifier(tagValue.value);
666 }
667 const children =
668 instrValue.children !== null
669 ? instrValue.children.map((child) => codegenJsxElement(cx, child))
670 : [];
671 value = t.jsxElement(
669 - t.jsxOpeningElement(
670 - t.jsxIdentifier(tag),
671 - attributes,
672 - instrValue.children === null
673 - ),
674 - instrValue.children !== null
675 - ? t.jsxClosingElement(t.jsxIdentifier(tag))
676 - : null,
672 + t.jsxOpeningElement(tag, attributes, instrValue.children === null),
673 + instrValue.children !== null ? t.jsxClosingElement(tag) : null,
674 children,
675 instrValue.children === null
676 );
@@ -896,6 +893,26 @@ function codegenJsxElement(
893 }
894 }
895
896 +function convertMemberExpressionToJsx(
897 + expr: t.MemberExpression
898 +): t.JSXMemberExpression {
899 + invariant(
900 + expr.property.type === "Identifier",
901 + "Expected JSX member expression property to be a string"
902 + );
903 + const property = t.jsxIdentifier(expr.property.name);
904 + if (expr.object.type === "Identifier") {
905 + return t.jsxMemberExpression(t.jsxIdentifier(expr.object.name), property);
906 + } else {
907 + invariant(
908 + expr.object.type === "MemberExpression",
909 + "Expected JSX member expression to be an identifier or nested member expression"
910 + );
911 + const object = convertMemberExpressionToJsx(expr.object);
912 + return t.jsxMemberExpression(object, property);
913 + }
914 +}
915 +
916 function codegenLValue(
917 pattern: Pattern | Place | SpreadPattern
918 ): t.ArrayPattern | t.ObjectPattern | t.RestElement | t.Identifier {
compiler/forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md
-9
@@ -166,15 +166,6 @@ let moduleLocal = false;
166 20 |
167 21 | const j = function bar([quz, qux], ...args) {};
168
169 -[ReactForget] TodoError: (BuildHIR::lowerJsxElementName) Handle JSXMemberExpression tags
170 - 17 | <Button haha={1}></Button>;
171 - 18 | <Button>{/** empty */}</Button>;
172 -> 19 | <DesignSystem.Button />;
173 - | ^^^^^^^^^^^^^^^^^^^
174 - 20 |
175 - 21 | const j = function bar([quz, qux], ...args) {};
176 - 22 |
177 -
169 [ReactForget] TodoError: (BuildHIR::lower) Handle RestElement params
170 19 | <DesignSystem.Button />;
171 20 |
compiler/forget/src/__tests__/fixtures/compiler/jsx-member-expression.expect.md new
+38
@@ -0,0 +1,38 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + return (
7 + <Sathya.Codes.Forget>
8 + <Foo.Bar.Baz />
9 + </Sathya.Codes.Forget>
10 + );
11 +}
12 +
13 +```
14 +
15 +## Code
16 +
17 +```javascript
18 +function Component(props) {
19 + const $ = React.unstable_useMemoCache(2);
20 + let t0;
21 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 + t0 = <Foo.Bar.Baz></Foo.Bar.Baz>;
23 + $[0] = t0;
24 + } else {
25 + t0 = $[0];
26 + }
27 + let t1;
28 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
29 + t1 = <Sathya.Codes.Forget>{t0}</Sathya.Codes.Forget>;
30 + $[1] = t1;
31 + } else {
32 + t1 = $[1];
33 + }
34 + return t1;
35 +}
36 +
37 +```
38 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/jsx-member-expression.js new
+7
@@ -0,0 +1,7 @@
1 +function Component(props) {
2 + return (
3 + <Sathya.Codes.Forget>
4 + <Foo.Bar.Baz />
5 + </Sathya.Codes.Forget>
6 + );
7 +}