Skip to content

Commit

Permalink
[compiler] Outline JSX with non-jsx children (#31442)
Browse files Browse the repository at this point in the history
Previously, we bailed out on outlining jsx that had children that were
not part of the outlined jsx.

Now, we add support for children by treating as attributes.
  • Loading branch information
gsathya authored Nov 6, 2024
1 parent 09197bb commit a88b9e5
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,20 @@ type OutlinedJsxAttribute = {
function collectProps(
instructions: Array<JsxInstruction>,
): Array<OutlinedJsxAttribute> | null {
let id = 1;

function generateName(oldName: string): string {
let newName = oldName;
while (seen.has(newName)) {
newName = `${oldName}${id++}`;
}
seen.add(newName);
return newName;
}

const attributes: Array<OutlinedJsxAttribute> = [];
const jsxIds = new Set(instructions.map(i => i.lvalue.identifier.id));
const seen: Set<string> = new Set();
let id = 1;

for (const instr of instructions) {
const {value} = instr;
Expand All @@ -233,25 +243,29 @@ function collectProps(
}

if (at.kind === 'JsxAttribute') {
let newName = at.name;
while (seen.has(newName)) {
newName = `${at.name}${id++}`;
}
const newName = generateName(at.name);
attributes.push({
originalName: at.name,
newName,
place: at.place,
});
seen.add(newName);
}
}

// TODO(gsn): Add support for children that are not jsx expressions
if (
value.children &&
value.children.some(child => !jsxIds.has(child.identifier.id))
) {
return null;
if (value.children) {
for (const child of value.children) {
if (jsxIds.has(child.identifier.id)) {
continue;
}

promoteTemporary(child.identifier);
const newName = generateName('t');
attributes.push({
originalName: child.identifier.name!.value,
newName: newName,
place: child,
});
}
}
}
return attributes;
Expand Down Expand Up @@ -387,6 +401,7 @@ function emitUpdatedJsx(
oldToNewProps: Map<IdentifierId, OutlinedJsxAttribute>,
): Array<JsxInstruction> {
const newInstrs: Array<JsxInstruction> = [];
const jsxIds = new Set(jsx.map(i => i.lvalue.identifier.id));

for (const instr of jsx) {
const {value} = instr;
Expand All @@ -412,11 +427,30 @@ function emitUpdatedJsx(
});
}

let newChildren: Array<Place> | null = null;
if (value.children) {
newChildren = [];
for (const child of value.children) {
if (jsxIds.has(child.identifier.id)) {
newChildren.push({...child});
continue;
}

const newChild = oldToNewProps.get(child.identifier.id);
invariant(
newChild !== undefined,
`Expected a new prop for ${printIdentifier(child.identifier)}`,
);
newChildren.push({...newChild.place});
}
}

newInstrs.push({
...instr,
value: {
...value,
props: newProps,
children: newChildren,
},
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ function Component({arr}) {
return (
<Bar key={id} x={x}>
<Baz i={i}>Test</Baz>
<Foo k={i} />
</Bar>
);
})}
</>
);
}

function Bar({x, children}) {
return (
<>
Expand All @@ -26,8 +28,17 @@ function Bar({x, children}) {
);
}

function Baz({i}) {
return i;
function Baz({i, children}) {
return (
<>
{i}
{children}
</>
);
}

function Foo({k}) {
return k;
}

function useX() {
Expand All @@ -53,11 +64,11 @@ function Component(t0) {
if ($[0] !== arr || $[1] !== x) {
let t2;
if ($[3] !== x) {
t2 = (i, id) => (
<Bar key={id} x={x}>
<Baz i={i}>Test</Baz>
</Bar>
);
t2 = (i, id) => {
const t3 = "Test";
const T0 = _temp;
return <T0 i={i} t={t3} k={i} key={id} x={x} />;
};
$[3] = x;
$[4] = t2;
} else {
Expand All @@ -80,6 +91,43 @@ function Component(t0) {
}
return t2;
}
function _temp(t0) {
const $ = _c(9);
const { i: i, t: t, k: k, x: x } = t0;
let t1;
if ($[0] !== i || $[1] !== t) {
t1 = <Baz i={i}>{t}</Baz>;
$[0] = i;
$[1] = t;
$[2] = t1;
} else {
t1 = $[2];
}
let t2;
if ($[3] !== k) {
t2 = <Foo k={k} />;
$[3] = k;
$[4] = t2;
} else {
t2 = $[4];
}
let t3;
if ($[5] !== t1 || $[6] !== t2 || $[7] !== x) {
t3 = (
<Bar x={x}>
{t1}
{t2}
</Bar>
);
$[5] = t1;
$[6] = t2;
$[7] = x;
$[8] = t3;
} else {
t3 = $[8];
}
return t3;
}

function Bar(t0) {
const $ = _c(3);
Expand All @@ -102,8 +150,28 @@ function Bar(t0) {
}

function Baz(t0) {
const { i } = t0;
return i;
const $ = _c(3);
const { i, children } = t0;
let t1;
if ($[0] !== children || $[1] !== i) {
t1 = (
<>
{i}
{children}
</>
);
$[0] = children;
$[1] = i;
$[2] = t1;
} else {
t1 = $[2];
}
return t1;
}

function Foo(t0) {
const { k } = t0;
return k;
}

function useX() {
Expand All @@ -118,4 +186,4 @@ export const FIXTURE_ENTRYPOINT = {
```
### Eval output
(kind: ok) xfooxbar
(kind: ok) xfooTestfooxbarTestbar
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ function Component({arr}) {
return (
<Bar key={id} x={x}>
<Baz i={i}>Test</Baz>
<Foo k={i} />
</Bar>
);
})}
</>
);
}

function Bar({x, children}) {
return (
<>
Expand All @@ -22,8 +24,17 @@ function Bar({x, children}) {
);
}

function Baz({i}) {
return i;
function Baz({i, children}) {
return (
<>
{i}
{children}
</>
);
}

function Foo({k}) {
return k;
}

function useX() {
Expand Down

0 comments on commit a88b9e5

Please sign in to comment.