@farm.js/react 0.1.0-beta.1 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.cjs CHANGED
@@ -260,6 +260,31 @@ function isTextExpression(expression, statesByValue, safeGlobals) {
260
260
  function jsxAttributeName(attribute) {
261
261
  return core.types.isJSXIdentifier(attribute.name) ? attribute.name.name : void 0;
262
262
  }
263
+ function jsxAttribute(element, name) {
264
+ return element.openingElement.attributes.find(
265
+ (attribute) => core.types.isJSXAttribute(attribute) && jsxAttributeName(attribute) === name
266
+ );
267
+ }
268
+ function staticJsxAttributeString(attribute) {
269
+ if (!attribute?.value) return attribute ? "" : void 0;
270
+ if (core.types.isStringLiteral(attribute.value)) return attribute.value.value;
271
+ if (core.types.isJSXExpressionContainer(attribute.value) && core.types.isStringLiteral(attribute.value.expression)) {
272
+ return attribute.value.expression.value;
273
+ }
274
+ return void 0;
275
+ }
276
+ function controlledSelectHasDynamicOptions(element) {
277
+ let dynamic = false;
278
+ core.types.traverseFast(element, (node) => {
279
+ if (dynamic || !core.types.isJSXElement(node)) return;
280
+ const tag = node.openingElement.name;
281
+ if (!core.types.isJSXIdentifier(tag) || tag.name !== "option" && tag.name !== "optgroup") return;
282
+ dynamic = node.openingElement.attributes.some(
283
+ (attribute) => core.types.isJSXSpreadAttribute(attribute) || core.types.isJSXAttribute(attribute) && core.types.isJSXExpressionContainer(attribute.value) && !core.types.isJSXEmptyExpression(attribute.value.expression)
284
+ );
285
+ });
286
+ return dynamic;
287
+ }
263
288
  function isEmptyConditionalBranch(expression) {
264
289
  return core.types.isNullLiteral(expression) || core.types.isBooleanLiteral(expression, { value: false });
265
290
  }
@@ -485,7 +510,87 @@ function validateKeyedRowEventHandler(handler) {
485
510
  });
486
511
  return reason;
487
512
  }
488
- function analyzeKeyedRowTree(row, renderCallback, safeGlobals) {
513
+ function stabilizeDeferredEventCurrentTarget(handler, compilerSetters) {
514
+ const cloned = core.types.cloneNode(handler, true);
515
+ const eventParameter = cloned.params[0];
516
+ if (!core.types.isIdentifier(eventParameter) || compilerSetters.size === 0) return cloned;
517
+ const file = expressionFile(cloned);
518
+ const expression = file.program.body[0].expression;
519
+ if (!core.types.isArrowFunctionExpression(expression) && !core.types.isFunctionExpression(expression)) {
520
+ return cloned;
521
+ }
522
+ const rootBindingIdentifier = expression.params[0];
523
+ const snapshots = [];
524
+ const snapshotsByProperty = /* @__PURE__ */ new Map();
525
+ const replaceCurrentTarget = (path) => {
526
+ const property = path.node.property;
527
+ const isCurrentTarget = path.node.computed ? core.types.isStringLiteral(property, { value: "currentTarget" }) : core.types.isIdentifier(property, { name: "currentTarget" });
528
+ if (!isCurrentTarget || !core.types.isIdentifier(path.node.object, { name: eventParameter.name }))
529
+ return;
530
+ const binding = path.scope.getBinding(eventParameter.name);
531
+ if (!binding || binding.identifier !== rootBindingIdentifier) return;
532
+ const setterCall = path.findParent(
533
+ (parent2) => parent2.isCallExpression() && core.types.isIdentifier(parent2.node.callee) && compilerSetters.has(parent2.node.callee.name)
534
+ );
535
+ if (!setterCall) return;
536
+ const deferredFunction = path.findParent(
537
+ (parent2) => parent2.isFunction() && parent2.node !== expression
538
+ );
539
+ if (!deferredFunction || !deferredFunction.findParent((parent2) => parent2 === setterCall))
540
+ return;
541
+ const parent = path.parentPath;
542
+ const propertyRead = (parent?.isMemberExpression() || parent?.isOptionalMemberExpression()) && parent.node.object === path.node ? parent : path;
543
+ const snapshotProperty = propertyRead === path ? "element" : propertyRead.node.computed ? core.types.isStringLiteral(propertyRead.node.property) ? propertyRead.node.property.value : `computed${snapshots.length}` : core.types.isIdentifier(propertyRead.node.property) ? propertyRead.node.property.name : `property${snapshots.length}`;
544
+ let snapshot = snapshotsByProperty.get(snapshotProperty);
545
+ if (!snapshot) {
546
+ const suffix = snapshotProperty.replace(/[^A-Za-z0-9_$]/g, " ").replace(
547
+ /(?:^|\s+)([A-Za-z0-9_$])/g,
548
+ (_match, character) => character.toUpperCase()
549
+ ).replace(/\s/g, "");
550
+ snapshot = uniqueLocalIdentifier(`_farmCurrentTarget${suffix}`, [
551
+ expression,
552
+ ...snapshots.map((entry) => entry.identifier)
553
+ ]);
554
+ snapshotsByProperty.set(snapshotProperty, snapshot);
555
+ snapshots.push({
556
+ identifier: snapshot,
557
+ value: core.types.cloneNode(propertyRead.node, true)
558
+ });
559
+ }
560
+ propertyRead.replaceWith(core.types.cloneNode(snapshot));
561
+ propertyRead.skip();
562
+ };
563
+ core.traverse(file, {
564
+ MemberExpression: replaceCurrentTarget,
565
+ OptionalMemberExpression: replaceCurrentTarget
566
+ });
567
+ if (snapshots.length === 0) return expression;
568
+ const declarations = snapshots.map(
569
+ ({ identifier, value }) => core.types.variableDeclaration("const", [core.types.variableDeclarator(core.types.cloneNode(identifier), value)])
570
+ );
571
+ if (core.types.isBlockStatement(expression.body)) {
572
+ expression.body.body.unshift(...declarations);
573
+ } else {
574
+ expression.body = core.types.blockStatement([...declarations, core.types.returnStatement(expression.body)]);
575
+ }
576
+ return expression;
577
+ }
578
+ function stabilizeDeferredEventCurrentTargets(root, compilerSetters) {
579
+ const file = expressionFile(core.types.cloneNode(root, true));
580
+ core.traverse(file, {
581
+ JSXAttribute(path) {
582
+ if (!core.types.isJSXIdentifier(path.node.name) || !/^on[A-Z]/.test(path.node.name.name)) return;
583
+ const value = path.node.value;
584
+ if (!core.types.isJSXExpressionContainer(value) || !core.types.isArrowFunctionExpression(value.expression) && !core.types.isFunctionExpression(value.expression)) {
585
+ return;
586
+ }
587
+ value.expression = stabilizeDeferredEventCurrentTarget(value.expression, compilerSetters);
588
+ }
589
+ });
590
+ const expression = file.program.body[0].expression;
591
+ return core.types.isJSXElement(expression) ? expression : core.types.cloneNode(root, true);
592
+ }
593
+ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters) {
489
594
  if (renderCallback.async || renderCallback.generator || renderCallback.params.length < 1 || renderCallback.params.length > 2 || !core.types.isIdentifier(renderCallback.params[0]) || renderCallback.params[1] !== void 0 && !core.types.isIdentifier(renderCallback.params[1])) {
490
595
  return { reason: "compiled keyed rows require item and optional index identifiers" };
491
596
  }
@@ -501,8 +606,25 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals) {
501
606
  const attributeNames = new Set(
502
607
  element.openingElement.attributes.filter((attribute) => core.types.isJSXAttribute(attribute)).map(jsxAttributeName).filter((name) => name !== void 0)
503
608
  );
504
- if ((tag.name === "input" || tag.name === "textarea" || tag.name === "select") && (attributeNames.has("value") || attributeNames.has("checked")) && [...attributeNames].some((name) => /^on[A-Z]/.test(name))) {
505
- return "controlled form elements in interactive compiled keyed rows require React ownership";
609
+ const controlled = attributeNames.has("value") || attributeNames.has("checked");
610
+ if (attributeNames.has("contentEditable")) {
611
+ return "content-editable keyed rows require React ownership";
612
+ }
613
+ if (tag.name === "input" && controlled) {
614
+ const type = jsxAttribute(element, "type");
615
+ const staticType = staticJsxAttributeString(type);
616
+ if (type && staticType === void 0) {
617
+ return "controlled keyed row inputs require a static type";
618
+ }
619
+ if (attributeNames.has("value") && staticType?.toLowerCase() === "file") {
620
+ return "file inputs require React ownership";
621
+ }
622
+ }
623
+ if (tag.name === "textarea" && controlled && meaningfulJsxChildren(element).length > 0) {
624
+ return "controlled keyed row textareas cannot also use children";
625
+ }
626
+ if (tag.name === "select" && controlled && controlledSelectHasDynamicOptions(element)) {
627
+ return "controlled keyed row selects require static options";
506
628
  }
507
629
  for (const attribute of element.openingElement.attributes) {
508
630
  if (core.types.isJSXSpreadAttribute(attribute)) {
@@ -533,7 +655,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals) {
533
655
  id: events.length,
534
656
  path: [...path],
535
657
  name,
536
- value: core.types.cloneNode(handler, true)
658
+ value: stabilizeDeferredEventCurrentTarget(handler, compilerSetters)
537
659
  });
538
660
  continue;
539
661
  }
@@ -647,26 +769,14 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals) {
647
769
  const reason = visit(row, []);
648
770
  return reason ? { reason } : { bindings, events, conditionals };
649
771
  }
650
- function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNames) {
651
- if (container.openingElement.attributes.some(
652
- (attribute) => core.types.isJSXSpreadAttribute(attribute) || core.types.isJSXAttribute(attribute) && core.types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
653
- )) {
654
- return void 0;
655
- }
656
- for (const attribute of container.openingElement.attributes) {
657
- if (core.types.isJSXAttribute(attribute) && core.types.isJSXIdentifier(attribute.name) && core.types.isJSXExpressionContainer(attribute.value) && !core.types.isJSXEmptyExpression(attribute.value.expression) && !/^on[A-Z]/.test(attribute.name.name) && collectStateDependencies(attribute.value.expression, statesByValue).length > 0) {
658
- return void 0;
659
- }
660
- }
661
- const children = meaningfulJsxChildren(container);
662
- if (children.length !== 1) return void 0;
772
+ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames) {
663
773
  let collection;
664
774
  let keyCallback;
665
775
  let renderCallback;
666
776
  let row;
667
777
  let dependencies;
668
778
  let syntax;
669
- const child = children[0];
779
+ let source;
670
780
  if (core.types.isJSXExpressionContainer(child) && !core.types.isJSXEmptyExpression(child.expression) && isMapCall(child.expression)) {
671
781
  const result = analyzeMapList(child.expression, statesByValue, safeGlobals);
672
782
  if (result.reason) return void 0;
@@ -686,6 +796,7 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
686
796
  row = returned;
687
797
  dependencies = result.dependencies || [];
688
798
  syntax = "map";
799
+ source = child.expression;
689
800
  } else if (core.types.isJSXElement(child) && isPublicListElement(child, listNames)) {
690
801
  const result = analyzePublicList(child, statesByValue, safeGlobals);
691
802
  if (result.reason) return void 0;
@@ -703,12 +814,19 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
703
814
  row = returned;
704
815
  dependencies = result.dependencies || [];
705
816
  syntax = "list";
817
+ source = child;
706
818
  } else {
707
819
  return void 0;
708
820
  }
709
- const rowAnalysis = analyzeKeyedRowTree(row, renderCallback, safeGlobals);
821
+ const rowAnalysis = analyzeKeyedRowTree(
822
+ row,
823
+ renderCallback,
824
+ safeGlobals,
825
+ new Set([...statesByValue.values()].map((state) => state.setterName))
826
+ );
710
827
  if (rowAnalysis.reason) return void 0;
711
828
  return {
829
+ source,
712
830
  collection,
713
831
  keyCallback,
714
832
  renderCallback,
@@ -720,6 +838,76 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
720
838
  syntax
721
839
  };
722
840
  }
841
+ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNames) {
842
+ if (container.openingElement.attributes.some(
843
+ (attribute) => core.types.isJSXSpreadAttribute(attribute) || core.types.isJSXAttribute(attribute) && core.types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
844
+ )) {
845
+ return void 0;
846
+ }
847
+ for (const attribute of container.openingElement.attributes) {
848
+ if (core.types.isJSXAttribute(attribute) && core.types.isJSXIdentifier(attribute.name) && core.types.isJSXExpressionContainer(attribute.value) && !core.types.isJSXEmptyExpression(attribute.value.expression) && !/^on[A-Z]/.test(attribute.name.name) && collectStateDependencies(attribute.value.expression, statesByValue).length > 0) {
849
+ return void 0;
850
+ }
851
+ }
852
+ const children = meaningfulJsxChildren(container);
853
+ if (children.length !== 1) return void 0;
854
+ return analyzeKeyedRowChild(children[0], statesByValue, safeGlobals, listNames);
855
+ }
856
+ function isStaticKeyedRangeSibling(element, statesByValue, safeGlobals) {
857
+ for (const child of meaningfulJsxChildren(element)) {
858
+ if (core.types.isJSXFragment(child)) return false;
859
+ if (core.types.isJSXElement(child)) {
860
+ if (!isHostElement(child) || !isStaticKeyedRangeSibling(child, statesByValue, safeGlobals)) {
861
+ return false;
862
+ }
863
+ continue;
864
+ }
865
+ if (core.types.isJSXExpressionContainer(child) && !core.types.isJSXEmptyExpression(child.expression) && !isTextExpression(child.expression, statesByValue, safeGlobals)) {
866
+ return false;
867
+ }
868
+ }
869
+ return true;
870
+ }
871
+ function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
872
+ if (container.openingElement.attributes.some(
873
+ (attribute) => core.types.isJSXSpreadAttribute(attribute) || core.types.isJSXAttribute(attribute) && core.types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
874
+ )) {
875
+ return void 0;
876
+ }
877
+ const children = meaningfulJsxChildren(container);
878
+ if (children.length < (allowSingleRange ? 1 : 2)) return void 0;
879
+ const ranges = [];
880
+ const dependencies = /* @__PURE__ */ new Set();
881
+ let staticBefore = 0;
882
+ for (const child of children) {
883
+ const range = analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames);
884
+ if (range) {
885
+ if (range.events.length > 0 || range.conditionals.length > 0) return void 0;
886
+ ranges.push({
887
+ before: staticBefore,
888
+ source: range.source,
889
+ collection: range.collection,
890
+ keyCallback: range.keyCallback,
891
+ renderCallback: range.renderCallback,
892
+ row: range.row,
893
+ bindings: range.bindings,
894
+ syntax: range.syntax
895
+ });
896
+ for (const dependency of range.dependencies) dependencies.add(dependency);
897
+ staticBefore = 0;
898
+ continue;
899
+ }
900
+ if (!core.types.isJSXElement(child) || !isHostElement(child)) return void 0;
901
+ if (!isStaticKeyedRangeSibling(child, statesByValue, safeGlobals)) return void 0;
902
+ staticBefore += 1;
903
+ }
904
+ if (ranges.length === 0) return void 0;
905
+ return {
906
+ ranges,
907
+ trailing: staticBefore,
908
+ dependencies: [...dependencies].sort((left, right) => left - right)
909
+ };
910
+ }
723
911
  function analyzeHostConditionalTree(branch, safeGlobals) {
724
912
  const bindings = [];
725
913
  const visit = (element, path) => {
@@ -1209,6 +1397,74 @@ function keyedRowsBoundary(blockRuntime, plan) {
1209
1397
  true
1210
1398
  );
1211
1399
  }
1400
+ function keyedRangeObject(range) {
1401
+ const parameters = range.renderCallback.params.map(
1402
+ (parameter) => core.types.cloneNode(parameter, true)
1403
+ );
1404
+ return core.types.objectExpression([
1405
+ core.types.objectProperty(core.types.identifier("before"), core.types.numericLiteral(range.before)),
1406
+ core.types.objectProperty(
1407
+ core.types.identifier("items"),
1408
+ core.types.arrowFunctionExpression([], cloneExpression(range.collection))
1409
+ ),
1410
+ core.types.objectProperty(core.types.identifier("rowKey"), core.types.cloneNode(range.keyCallback, true)),
1411
+ core.types.objectProperty(
1412
+ core.types.identifier("create"),
1413
+ core.types.arrowFunctionExpression(
1414
+ parameters.map((parameter) => core.types.cloneNode(parameter, true)),
1415
+ hostElementDescriptor(range.row)
1416
+ )
1417
+ ),
1418
+ core.types.objectProperty(
1419
+ core.types.identifier("bindings"),
1420
+ core.types.arrayExpression(
1421
+ range.bindings.map((binding) => keyedRowBindingObject(binding, parameters))
1422
+ )
1423
+ )
1424
+ ]);
1425
+ }
1426
+ function keyedRangesBoundary(blockRuntime, plan) {
1427
+ const name = core.types.jsxMemberExpression(
1428
+ core.types.jsxIdentifier(blockRuntime.name),
1429
+ core.types.jsxIdentifier("KeyedRanges")
1430
+ );
1431
+ const source = core.types.cloneNode(plan.source, true);
1432
+ let rootRef;
1433
+ source.openingElement.attributes = source.openingElement.attributes.filter((attribute) => {
1434
+ if (!core.types.isJSXAttribute(attribute) || jsxAttributeName(attribute) !== "ref" || !core.types.isJSXExpressionContainer(attribute.value) || core.types.isJSXEmptyExpression(attribute.value.expression)) {
1435
+ return true;
1436
+ }
1437
+ rootRef = cloneExpression(attribute.value.expression);
1438
+ return false;
1439
+ });
1440
+ return core.types.jsxElement(
1441
+ core.types.jsxOpeningElement(
1442
+ name,
1443
+ [
1444
+ core.types.jsxAttribute(core.types.jsxIdentifier("id"), core.types.jsxExpressionContainer(core.types.numericLiteral(plan.id))),
1445
+ core.types.jsxAttribute(
1446
+ core.types.jsxIdentifier("render"),
1447
+ core.types.jsxExpressionContainer(core.types.arrowFunctionExpression([], source))
1448
+ ),
1449
+ ...rootRef ? [core.types.jsxAttribute(core.types.jsxIdentifier("rootRef"), core.types.jsxExpressionContainer(rootRef))] : [],
1450
+ core.types.jsxAttribute(
1451
+ core.types.jsxIdentifier("ranges"),
1452
+ core.types.jsxExpressionContainer(
1453
+ core.types.arrayExpression(plan.ranges.map((range) => keyedRangeObject(range)))
1454
+ )
1455
+ ),
1456
+ core.types.jsxAttribute(
1457
+ core.types.jsxIdentifier("trailing"),
1458
+ core.types.jsxExpressionContainer(core.types.numericLiteral(plan.trailing))
1459
+ )
1460
+ ],
1461
+ true
1462
+ ),
1463
+ null,
1464
+ [],
1465
+ true
1466
+ );
1467
+ }
1212
1468
  function hostConditionalBranchObject(branch, bindings) {
1213
1469
  return core.types.objectExpression([
1214
1470
  core.types.objectProperty(
@@ -1421,6 +1677,23 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1421
1677
  }
1422
1678
  if (core.types.isJSXElement(child)) {
1423
1679
  if (isHostElement(child)) {
1680
+ const keyedRanges = insideConditional ? void 0 : analyzeKeyedRangesContainer(child, statesByValue, safeGlobals, listNames);
1681
+ if (keyedRanges) {
1682
+ plans.push({
1683
+ kind: "keyed-ranges",
1684
+ id: nextId++,
1685
+ parent,
1686
+ dependencies: keyedRanges.dependencies,
1687
+ source: child,
1688
+ ranges: keyedRanges.ranges,
1689
+ trailing: keyedRanges.trailing
1690
+ });
1691
+ for (const range of keyedRanges.ranges) {
1692
+ if (core.types.isJSXElement(range.source)) ownedElements.add(range.source);
1693
+ else keyedExpressions.add(range.source);
1694
+ }
1695
+ continue;
1696
+ }
1424
1697
  const hostConditional = analyzeHostConditionalContainer(
1425
1698
  child,
1426
1699
  statesByValue,
@@ -1553,6 +1826,28 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1553
1826
  }
1554
1827
  return { dependencies };
1555
1828
  };
1829
+ const rootRanges = analyzeKeyedRangesContainer(root, statesByValue, safeGlobals, listNames, true);
1830
+ if (rootRanges) {
1831
+ plans.push({
1832
+ kind: "keyed-ranges",
1833
+ id: nextId++,
1834
+ dependencies: rootRanges.dependencies,
1835
+ source: root,
1836
+ ranges: rootRanges.ranges,
1837
+ trailing: rootRanges.trailing
1838
+ });
1839
+ for (const range of rootRanges.ranges) {
1840
+ if (core.types.isJSXElement(range.source)) ownedElements.add(range.source);
1841
+ else keyedExpressions.add(range.source);
1842
+ }
1843
+ return {
1844
+ plans,
1845
+ componentElements,
1846
+ conditionalExpressions,
1847
+ ownedElements,
1848
+ keyedExpressions
1849
+ };
1850
+ }
1556
1851
  const result = visitHost(root, void 0, false);
1557
1852
  if (result.reason) return { reason: result.reason };
1558
1853
  return {
@@ -1589,6 +1884,10 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
1589
1884
  const planBySource = new Map(
1590
1885
  plans.map((plan) => [plan.source, plan])
1591
1886
  );
1887
+ const rootPlan = planBySource.get(root);
1888
+ if (rootPlan?.kind === "keyed-ranges") {
1889
+ return keyedRangesBoundary(blockRuntime, rootPlan);
1890
+ }
1592
1891
  const visit = (element) => {
1593
1892
  element.children = element.children.map((child) => {
1594
1893
  if (core.types.isJSXElement(child)) {
@@ -1599,6 +1898,9 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
1599
1898
  if (plan2?.kind === "keyed-rows") {
1600
1899
  return keyedRowsBoundary(blockRuntime, plan2);
1601
1900
  }
1901
+ if (plan2?.kind === "keyed-ranges") {
1902
+ return keyedRangesBoundary(blockRuntime, plan2);
1903
+ }
1602
1904
  if (plan2?.kind === "component") {
1603
1905
  return componentIslandBoundary(blockRuntime, plan2, child);
1604
1906
  }
@@ -2032,11 +2334,15 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2032
2334
  const handlerRewrite = rewriteHandlerAccess(rootWithDerived, handlersByName);
2033
2335
  if (handlerRewrite.reason) return handlerRewrite.reason;
2034
2336
  if (!handlerRewrite.root) return "event handlers could not be prepared safely";
2035
- const expandedRoot = rewriteDestructuredPropAccess(
2337
+ const expandedRootWithDeferredEvents = rewriteDestructuredPropAccess(
2036
2338
  handlerRewrite.root,
2037
2339
  propsPlan.destructuredNames,
2038
2340
  propsPlan.definitionParameter
2039
2341
  );
2342
+ const expandedRoot = stabilizeDeferredEventCurrentTargets(
2343
+ expandedRootWithDeferredEvents,
2344
+ new Set(states.map((state) => state.setterName))
2345
+ );
2040
2346
  const setterReason = validateSetterUsage(expandedRoot, statesBySetter);
2041
2347
  if (setterReason) return setterReason;
2042
2348
  const referencedComponentNames = /* @__PURE__ */ new Set();