[compiler] Store original and new prop names (#31440)
Previously, we'd directly store the original attributes from the jsx expressions. But this isn't enough as we want to rename duplicate attributes. This PR refactors the prop collection logic to store both the original and new names for jsx attributes in the newly outlined jsx expression. For now, both the new and old names are the same. In the future, they will be different when we add support for outlining expressions with duplicate attribute names.
Sathya Gunasekaran committed
Nov 6, 2024 at 17:44 UTC
2df8f6188537514a538741064ae83682c2bef7c1
1 file changed
+49
-27
compiler/packages/babel-plugin-react-compiler/src/Optimization/OutlineJsx.ts
+49
-27
@@ -210,10 +210,16 @@ function process(
210
return {instrs: newInstrs, fn: outlinedFn};
211
}
212
213
+type OutlinedJsxAttribute = {
214
+ originalName: string;
215
+ newName: string;
216
+ place: Place;
217
+};
218
+
219
function collectProps(
220
instructions: Array<JsxInstruction>,
215
-): Array<JsxAttribute> | null {
216
- const attributes: Array<JsxAttribute> = [];
221
+): Array<OutlinedJsxAttribute> | null {
222
+ const attributes: Array<OutlinedJsxAttribute> = [];
223
const jsxIds = new Set(instructions.map(i => i.lvalue.identifier.id));
224
const seen: Set<string> = new Set();
225
for (const instr of instructions) {
@@ -234,7 +240,11 @@ function collectProps(
240
241
if (at.kind === 'JsxAttribute') {
242
seen.add(at.name);
237
- attributes.push(at);
243
+ attributes.push({
244
+ originalName: at.name,
245
+ newName: at.name,
246
+ place: at.place,
247
+ });
248
}
249
}
250
@@ -252,9 +262,15 @@ function collectProps(
262
function emitOutlinedJsx(
263
env: Environment,
264
instructions: Array<Instruction>,
255
- props: Array<JsxAttribute>,
265
+ outlinedProps: Array<OutlinedJsxAttribute>,
266
outlinedTag: string,
267
): Array<Instruction> {
268
+ const props: Array<JsxAttribute> = outlinedProps.map(p => ({
269
+ kind: 'JsxAttribute',
270
+ name: p.newName,
271
+ place: p.place,
272
+ }));
273
+
274
const loadJsx: Instruction = {
275
id: makeInstructionId(0),
276
loc: GeneratedSource,
@@ -290,7 +306,7 @@ function emitOutlinedJsx(
306
function emitOutlinedFn(
307
env: Environment,
308
jsx: Array<JsxInstruction>,
293
- oldProps: Array<JsxAttribute>,
309
+ oldProps: Array<OutlinedJsxAttribute>,
310
globals: LoadGlobalMap,
311
): HIRFunction | null {
312
const instructions: Array<Instruction> = [];
@@ -299,9 +315,11 @@ function emitOutlinedFn(
315
const propsObj: Place = createTemporaryPlace(env, GeneratedSource);
316
promoteTemporary(propsObj.identifier);
317
302
- const destructurePropsInstr = emitDestructureProps(env, propsObj, [
303
- ...oldToNewProps.values(),
304
- ]);
318
+ const destructurePropsInstr = emitDestructureProps(
319
+ env,
320
+ propsObj,
321
+ oldToNewProps,
322
+ );
323
instructions.push(destructurePropsInstr);
324
325
const updatedJsxInstructions = emitUpdatedJsx(jsx, oldToNewProps);
@@ -368,7 +386,7 @@ function emitLoadGlobals(
386
387
function emitUpdatedJsx(
388
jsx: Array<JsxInstruction>,
371
- oldToNewProps: Map<IdentifierId, ObjectProperty>,
389
+ oldToNewProps: Map<IdentifierId, OutlinedJsxAttribute>,
390
): Array<JsxInstruction> {
391
const newInstrs: Array<JsxInstruction> = [];
392
@@ -390,7 +408,8 @@ function emitUpdatedJsx(
408
`Expected a new property for ${printIdentifier(prop.place.identifier)}`,
409
);
410
newProps.push({
393
- ...prop,
411
+ kind: 'JsxAttribute',
412
+ name: newProp.originalName,
413
place: newProp.place,
414
});
415
}
@@ -409,31 +428,21 @@ function emitUpdatedJsx(
428
429
function createOldToNewPropsMapping(
430
env: Environment,
412
- oldProps: Array<JsxAttribute>,
413
-): Map<IdentifierId, ObjectProperty> {
431
+ oldProps: Array<OutlinedJsxAttribute>,
432
+): Map<IdentifierId, OutlinedJsxAttribute> {
433
const oldToNewProps = new Map();
434
435
for (const oldProp of oldProps) {
417
- invariant(
418
- oldProp.kind === 'JsxAttribute',
419
- `Expected only attributes but found ${oldProp.kind}`,
420
- );
421
-
436
// Do not read key prop in the outlined component
423
- if (oldProp.name === 'key') {
437
+ if (oldProp.originalName === 'key') {
438
continue;
439
}
440
427
- const newProp: ObjectProperty = {
428
- kind: 'ObjectProperty',
429
- key: {
430
- kind: 'string',
431
- name: oldProp.name,
432
- },
433
- type: 'property',
441
+ const newProp: OutlinedJsxAttribute = {
442
+ ...oldProp,
443
place: createTemporaryPlace(env, GeneratedSource),
444
};
436
- newProp.place.identifier.name = makeIdentifierName(oldProp.name);
445
+ newProp.place.identifier.name = makeIdentifierName(oldProp.newName);
446
oldToNewProps.set(oldProp.place.identifier.id, newProp);
447
}
448
@@ -443,8 +452,21 @@ function createOldToNewPropsMapping(
452
function emitDestructureProps(
453
env: Environment,
454
propsObj: Place,
446
- properties: Array<ObjectProperty>,
455
+ oldToNewProps: Map<IdentifierId, OutlinedJsxAttribute>,
456
): Instruction {
457
+ const properties: Array<ObjectProperty> = [];
458
+ for (const [_, prop] of oldToNewProps) {
459
+ properties.push({
460
+ kind: 'ObjectProperty',
461
+ key: {
462
+ kind: 'string',
463
+ name: prop.newName,
464
+ },
465
+ type: 'property',
466
+ place: prop.place,
467
+ });
468
+ }
469
+
470
const destructurePropsInstr: Instruction = {
471
id: makeInstructionId(0),
472
lvalue: createTemporaryPlace(env, GeneratedSource),