@farm.js/react 0.1.0-beta.3 → 0.1.0-beta.5

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.mjs CHANGED
@@ -4,6 +4,96 @@ import { mkdir, writeFile } from 'fs/promises';
4
4
  import { resolve, dirname, relative } from 'path';
5
5
 
6
6
  // src/vite.ts
7
+ var DELEGATABLE_KEYED_ROW_EVENTS = /* @__PURE__ */ new Set([
8
+ "onBeforeInput",
9
+ "onBlur",
10
+ "onChange",
11
+ "onClick",
12
+ "onCompositionEnd",
13
+ "onCompositionStart",
14
+ "onCompositionUpdate",
15
+ "onContextMenu",
16
+ "onCopy",
17
+ "onCut",
18
+ "onDoubleClick",
19
+ "onDrag",
20
+ "onDragEnd",
21
+ "onDragEnter",
22
+ "onDragExit",
23
+ "onDragLeave",
24
+ "onDragOver",
25
+ "onDragStart",
26
+ "onDrop",
27
+ "onFocus",
28
+ "onInput",
29
+ "onKeyDown",
30
+ "onKeyPress",
31
+ "onKeyUp",
32
+ "onMouseDown",
33
+ "onMouseMove",
34
+ "onMouseOut",
35
+ "onMouseOver",
36
+ "onMouseUp",
37
+ "onPaste",
38
+ "onPointerCancel",
39
+ "onPointerDown",
40
+ "onPointerMove",
41
+ "onPointerOut",
42
+ "onPointerOver",
43
+ "onPointerUp",
44
+ "onReset",
45
+ "onSubmit",
46
+ "onTouchCancel",
47
+ "onTouchEnd",
48
+ "onTouchMove",
49
+ "onTouchStart",
50
+ "onWheel"
51
+ ]);
52
+ function canDelegateKeyedRowEvents(plan) {
53
+ if (plan.events.length === 0 || plan.conditionals.length > 0) return false;
54
+ if (plan.source.openingElement.attributes.some(
55
+ (attribute) => types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && /^on[A-Z]/.test(attribute.name.name)
56
+ )) {
57
+ return false;
58
+ }
59
+ return plan.events.every(
60
+ (event) => DELEGATABLE_KEYED_ROW_EVENTS.has(event.name.replace(/Capture$/, ""))
61
+ );
62
+ }
63
+ var COMPILER_RUNTIME_FEATURE_EXPORTS = {
64
+ conditional: "conditionalRuntimeFeature",
65
+ "host-conditional": "hostConditionalRuntimeFeature",
66
+ "conditional-ranges": "conditionalRangesRuntimeFeature",
67
+ "keyed-list": "keyedListRuntimeFeature",
68
+ "keyed-rows": "keyedRowsRuntimeFeature",
69
+ "keyed-rows-conditional": "keyedRowsConditionalRuntimeFeature",
70
+ "keyed-rows-host": "keyedRowsHostRuntimeFeature",
71
+ "keyed-rows-complete": "keyedRowsCompleteRuntimeFeature",
72
+ "keyed-ranges": "keyedRangesRuntimeFeature",
73
+ "mixed-ranges": "mixedRangesRuntimeFeature",
74
+ component: "componentRuntimeFeature"
75
+ };
76
+ function runtimeFeaturesForPlans(plans) {
77
+ const features = /* @__PURE__ */ new Set();
78
+ let keyedRowsHaveConditionals = false;
79
+ let keyedRowsHaveHostBlocks = false;
80
+ for (const plan of plans) {
81
+ if (plan.kind === "keyed-rows") {
82
+ keyedRowsHaveConditionals || (keyedRowsHaveConditionals = plan.conditionals.length > 0);
83
+ keyedRowsHaveHostBlocks || (keyedRowsHaveHostBlocks = Boolean(plan.descriptorBlocks?.size));
84
+ continue;
85
+ }
86
+ if (plan.kind in COMPILER_RUNTIME_FEATURE_EXPORTS) {
87
+ features.add(plan.kind);
88
+ }
89
+ }
90
+ if (plans.some((plan) => plan.kind === "keyed-rows")) {
91
+ features.add(
92
+ keyedRowsHaveConditionals && keyedRowsHaveHostBlocks ? "keyed-rows-complete" : keyedRowsHaveConditionals ? "keyed-rows-conditional" : keyedRowsHaveHostBlocks ? "keyed-rows-host" : "keyed-rows"
93
+ );
94
+ }
95
+ return [...features].sort();
96
+ }
7
97
  var SAFE_GLOBAL_CALLS = /* @__PURE__ */ new Set(["Boolean", "Number", "String"]);
8
98
  var SAFE_MATH_CALLS = /* @__PURE__ */ new Set(["abs", "ceil", "floor", "max", "min", "round", "sign", "trunc"]);
9
99
  function isComponentName(name) {
@@ -88,6 +178,37 @@ function rewriteDestructuredPropAccess(expression, names, propsParameter) {
88
178
  });
89
179
  return file.program.body[0].expression;
90
180
  }
181
+ function collectDestructuredPropNames(expression, names, propsParameter) {
182
+ const referenced = /* @__PURE__ */ new Set();
183
+ if (names.size === 0) return referenced;
184
+ traverse(expressionFile(cloneExpression(expression)), {
185
+ MemberExpression(path) {
186
+ const { object, property, computed } = path.node;
187
+ if (computed || !types.isIdentifier(object, { name: propsParameter.name }) || !types.isIdentifier(property) || !names.has(property.name)) {
188
+ return;
189
+ }
190
+ referenced.add(property.name);
191
+ }
192
+ });
193
+ return referenced;
194
+ }
195
+ function rewriteTrackedPropAccess(expression, propsParameter, bindingsByName) {
196
+ if (bindingsByName.size === 0) return cloneExpression(expression);
197
+ const file = expressionFile(cloneExpression(expression));
198
+ traverse(file, {
199
+ MemberExpression(path) {
200
+ const { object, property, computed } = path.node;
201
+ if (computed || !types.isIdentifier(object, { name: propsParameter.name }) || !types.isIdentifier(property)) {
202
+ return;
203
+ }
204
+ const binding = bindingsByName.get(property.name);
205
+ if (!binding) return;
206
+ path.replaceWith(types.identifier(binding.valueName));
207
+ path.skip();
208
+ }
209
+ });
210
+ return file.program.body[0].expression;
211
+ }
91
212
  function rewriteHandlerAccess(root, handlersByName) {
92
213
  if (handlersByName.size === 0) return { root: types.cloneNode(root, true) };
93
214
  const file = expressionFile(types.cloneNode(root, true));
@@ -767,7 +888,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
767
888
  const reason = visit(row, []);
768
889
  return reason ? { reason } : { bindings, events, conditionals };
769
890
  }
770
- function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames) {
891
+ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false) {
771
892
  let collection;
772
893
  let keyCallback;
773
894
  let renderCallback;
@@ -822,17 +943,29 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames) {
822
943
  safeGlobals,
823
944
  new Set([...statesByValue.values()].map((state) => state.setterName))
824
945
  );
825
- if (rowAnalysis.reason) return void 0;
946
+ if (rowAnalysis.reason && !acceptUnsupportedRow) return void 0;
947
+ const keyExpression = returnedExpression(keyCallback);
948
+ const structureDependencies = new Set(collectStateDependencies(collection, statesByValue));
949
+ if (keyExpression) {
950
+ for (const dependency of collectStateDependencies(keyExpression, statesByValue)) {
951
+ structureDependencies.add(dependency);
952
+ }
953
+ }
826
954
  return {
827
955
  source,
828
956
  collection,
957
+ structureDependencies: [...structureDependencies].sort((left, right) => left - right),
829
958
  keyCallback,
830
959
  renderCallback,
831
960
  row,
832
961
  dependencies,
833
- bindings: rowAnalysis.bindings || [],
962
+ bindings: (rowAnalysis.bindings || []).map((binding) => ({
963
+ ...binding,
964
+ dependencies: collectStateDependencies(binding.value, statesByValue)
965
+ })),
834
966
  events: rowAnalysis.events || [],
835
967
  conditionals: rowAnalysis.conditionals || [],
968
+ rowReason: rowAnalysis.reason,
836
969
  syntax
837
970
  };
838
971
  }
@@ -849,22 +982,7 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
849
982
  }
850
983
  const children = meaningfulJsxChildren(container);
851
984
  if (children.length !== 1) return void 0;
852
- return analyzeKeyedRowChild(children[0], statesByValue, safeGlobals, listNames);
853
- }
854
- function isStaticRangeSibling(element, statesByValue, safeGlobals) {
855
- for (const child of meaningfulJsxChildren(element)) {
856
- if (types.isJSXFragment(child)) return false;
857
- if (types.isJSXElement(child)) {
858
- if (!isHostElement(child) || !isStaticRangeSibling(child, statesByValue, safeGlobals)) {
859
- return false;
860
- }
861
- continue;
862
- }
863
- if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression) && !isTextExpression(child.expression, statesByValue, safeGlobals)) {
864
- return false;
865
- }
866
- }
867
- return true;
985
+ return analyzeKeyedRowChild(children[0], statesByValue, safeGlobals, listNames, true);
868
986
  }
869
987
  function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
870
988
  if (container.openingElement.attributes.some(
@@ -876,6 +994,7 @@ function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, list
876
994
  if (children.length < (allowSingleRange ? 1 : 2)) return void 0;
877
995
  const ranges = [];
878
996
  const dependencies = /* @__PURE__ */ new Set();
997
+ const staticBindings = [];
879
998
  let staticBefore = 0;
880
999
  for (const child of children) {
881
1000
  const range = analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames);
@@ -896,18 +1015,31 @@ function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, list
896
1015
  continue;
897
1016
  }
898
1017
  if (!types.isJSXElement(child) || !isHostElement(child)) return void 0;
899
- if (!isStaticRangeSibling(child, statesByValue, safeGlobals)) return void 0;
1018
+ const analysis = analyzeHostConditionalTree(child, safeGlobals, true, statesByValue);
1019
+ if (analysis.reason) return void 0;
1020
+ for (const binding of analysis.bindings || []) {
1021
+ staticBindings.push({
1022
+ ...binding,
1023
+ segment: ranges.length,
1024
+ sibling: staticBefore
1025
+ });
1026
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1027
+ dependencies.add(dependency);
1028
+ }
1029
+ }
900
1030
  staticBefore += 1;
901
1031
  }
902
1032
  if (ranges.length === 0) return void 0;
903
1033
  return {
904
1034
  ranges,
905
1035
  trailing: staticBefore,
906
- dependencies: [...dependencies].sort((left, right) => left - right)
1036
+ dependencies: [...dependencies].sort((left, right) => left - right),
1037
+ staticBindings
907
1038
  };
908
1039
  }
909
- function analyzeHostConditionalTree(branch, safeGlobals) {
1040
+ function analyzeHostConditionalTree(branch, safeGlobals, allowStaticEvents = false, statefulOnly) {
910
1041
  const bindings = [];
1042
+ const needsStaticBinding = (expression) => !statefulOnly || collectStateDependencies(expression, statefulOnly).length > 0;
911
1043
  const visit = (element, path) => {
912
1044
  const tag = element.openingElement.name;
913
1045
  if (!types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
@@ -916,20 +1048,26 @@ function analyzeHostConditionalTree(branch, safeGlobals) {
916
1048
  if (tag.name === "svg") return "compiler-owned conditionals do not support SVG yet";
917
1049
  for (const attribute of element.openingElement.attributes) {
918
1050
  if (types.isJSXSpreadAttribute(attribute)) {
1051
+ if (statefulOnly && !needsStaticBinding(attribute.argument)) continue;
919
1052
  return "compiler-owned conditionals do not support JSX attribute spreads";
920
1053
  }
921
1054
  const name = jsxAttributeName(attribute);
922
1055
  if (!name) return "compiler-owned conditionals do not support namespaced JSX attributes";
923
1056
  if (name === "ref" || name === "dangerouslySetInnerHTML" || name === "key") {
1057
+ if (statefulOnly && (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression) || !needsStaticBinding(attribute.value.expression))) {
1058
+ continue;
1059
+ }
924
1060
  return `compiler-owned conditional ${name} requires React ownership`;
925
1061
  }
926
1062
  if (/^on[A-Z]/.test(name)) {
1063
+ if (allowStaticEvents) continue;
927
1064
  return "compiler-owned conditional events require React ownership";
928
1065
  }
929
1066
  if (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression)) {
930
1067
  continue;
931
1068
  }
932
1069
  const expression = attribute.value.expression;
1070
+ if (!needsStaticBinding(expression)) continue;
933
1071
  if (name === "style") {
934
1072
  if (!types.isObjectExpression(expression)) {
935
1073
  return "compiler-owned conditional styles must use one inline object literal";
@@ -942,6 +1080,7 @@ function analyzeHostConditionalTree(branch, safeGlobals) {
942
1080
  if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
943
1081
  return "compiler-owned conditional style names must use camelCase or a CSS custom property";
944
1082
  }
1083
+ if (!needsStaticBinding(property.value)) continue;
945
1084
  const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
946
1085
  if (unsupported2) {
947
1086
  return `compiler-owned conditional style ${propertyName} cannot use ${unsupported2}`;
@@ -976,14 +1115,20 @@ function analyzeHostConditionalTree(branch, safeGlobals) {
976
1115
  const expressions = element.children.filter(
977
1116
  (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)
978
1117
  );
979
- for (const child of expressions) {
980
- const unsupported = validateDerivedExpression(child.expression, safeGlobals);
981
- if (unsupported) return `compiler-owned conditional text cannot use ${unsupported}`;
1118
+ const boundExpressions = statefulOnly ? expressions.filter((child) => needsStaticBinding(child.expression)) : expressions;
1119
+ if (boundExpressions.length > 0) {
1120
+ for (const child of expressions) {
1121
+ const unsupported = validateDerivedExpression(
1122
+ child.expression,
1123
+ safeGlobals
1124
+ );
1125
+ if (unsupported) return `compiler-owned conditional text cannot use ${unsupported}`;
1126
+ }
982
1127
  }
983
- if (nestedElements.length > 0 && expressions.length > 0) {
1128
+ if (nestedElements.length > 0 && boundExpressions.length > 0) {
984
1129
  return "compiler-owned conditionals do not support dynamic text beside nested elements yet";
985
1130
  }
986
- if (nestedElements.length === 0 && expressions.length > 0) {
1131
+ if (nestedElements.length === 0 && boundExpressions.length > 0) {
987
1132
  const parts = [];
988
1133
  for (const child of element.children) {
989
1134
  if (types.isJSXText(child)) {
@@ -1007,7 +1152,369 @@ function analyzeHostConditionalTree(branch, safeGlobals) {
1007
1152
  const reason = visit(branch, []);
1008
1153
  return reason ? { reason } : { bindings };
1009
1154
  }
1010
- function analyzeHostConditionalContainer(container, statesByValue, safeGlobals) {
1155
+ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames, parent, allocateId) {
1156
+ const bindings = [];
1157
+ const plans = [];
1158
+ const blocks = /* @__PURE__ */ new Map();
1159
+ const visit = (element, path, owner) => {
1160
+ const tag = element.openingElement.name;
1161
+ if (!types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
1162
+ return "recursive compiler-owned blocks support host elements only";
1163
+ }
1164
+ if (tag.name === "svg") return "recursive compiler-owned blocks do not support SVG yet";
1165
+ for (const attribute of element.openingElement.attributes) {
1166
+ if (types.isJSXSpreadAttribute(attribute)) {
1167
+ return "recursive compiler-owned blocks do not support JSX attribute spreads";
1168
+ }
1169
+ const name = jsxAttributeName(attribute);
1170
+ if (!name) return "recursive compiler-owned blocks do not support namespaced attributes";
1171
+ if (name === "ref" || name === "dangerouslySetInnerHTML" || name === "key") {
1172
+ return `recursive compiler-owned block ${name} requires React ownership`;
1173
+ }
1174
+ if (/^on[A-Z]/.test(name)) {
1175
+ return "recursive compiler-owned block events require React ownership";
1176
+ }
1177
+ if (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression)) {
1178
+ continue;
1179
+ }
1180
+ const expression = attribute.value.expression;
1181
+ if (name === "style") {
1182
+ if (!types.isObjectExpression(expression)) {
1183
+ return "recursive compiler-owned styles must use one inline object literal";
1184
+ }
1185
+ for (const property of expression.properties) {
1186
+ if (!types.isObjectProperty(property) || property.computed || !types.isExpression(property.value)) {
1187
+ return "recursive compiler-owned styles do not support spreads, methods, or computed properties";
1188
+ }
1189
+ const propertyName = types.isIdentifier(property.key) ? property.key.name : types.isStringLiteral(property.key) ? property.key.value : void 0;
1190
+ if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
1191
+ return "recursive compiler-owned style names must use camelCase or a CSS custom property";
1192
+ }
1193
+ const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
1194
+ if (unsupported2) {
1195
+ return `recursive compiler-owned style ${propertyName} cannot use ${unsupported2}`;
1196
+ }
1197
+ bindings.push({
1198
+ kind: "style",
1199
+ path: [...path],
1200
+ name: propertyName,
1201
+ value: cloneExpression(property.value)
1202
+ });
1203
+ }
1204
+ continue;
1205
+ }
1206
+ if (name === "children") {
1207
+ return "recursive compiler-owned children props are not supported yet";
1208
+ }
1209
+ const unsupported = validateDerivedExpression(expression, safeGlobals);
1210
+ if (unsupported) return `recursive compiler-owned ${name} cannot use ${unsupported}`;
1211
+ bindings.push({
1212
+ kind: "attribute",
1213
+ path: [...path],
1214
+ name,
1215
+ value: cloneExpression(expression)
1216
+ });
1217
+ }
1218
+ const children = meaningfulJsxChildren(element);
1219
+ const hasConditionalRange = children.some(
1220
+ (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression) && conditionalBlockShape(child.expression) !== null
1221
+ );
1222
+ const hasKeyedRange = children.some(
1223
+ (child) => analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, true) !== void 0
1224
+ );
1225
+ if (hasConditionalRange && hasKeyedRange) {
1226
+ const id = allocateId();
1227
+ const mixed = analyzeMixedRangesContainer(
1228
+ element,
1229
+ statesByValue,
1230
+ safeGlobals,
1231
+ listNames,
1232
+ id,
1233
+ allocateId
1234
+ );
1235
+ if (mixed) {
1236
+ plans.push(...mixed.nestedPlans);
1237
+ for (const [nestedElement, plan2] of mixed.descriptorBlocks) {
1238
+ blocks.set(nestedElement, plan2);
1239
+ }
1240
+ const plan = {
1241
+ kind: "nested-host-mixed-ranges",
1242
+ id,
1243
+ parent: owner,
1244
+ dependencies: mixed.dependencies,
1245
+ source: element,
1246
+ ranges: mixed.ranges,
1247
+ trailing: mixed.trailing,
1248
+ staticBindings: mixed.staticBindings
1249
+ };
1250
+ plans.push(plan);
1251
+ blocks.set(element, plan);
1252
+ return void 0;
1253
+ }
1254
+ }
1255
+ const conditionalChildren = /* @__PURE__ */ new Map();
1256
+ let conditionalContainer = children.length > 0;
1257
+ for (const child of children) {
1258
+ if (types.isJSXElement(child) && isHostElement(child)) continue;
1259
+ if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1260
+ const shape = conditionalBlockShape(child.expression);
1261
+ if (shape && !validateDerivedExpression(shape.test, safeGlobals)) {
1262
+ conditionalChildren.set(child.expression, shape);
1263
+ continue;
1264
+ }
1265
+ }
1266
+ conditionalContainer = false;
1267
+ break;
1268
+ }
1269
+ if (conditionalContainer && conditionalChildren.size > 0) {
1270
+ const id = allocateId();
1271
+ const ranges = [];
1272
+ const staticBindings = [];
1273
+ const dependencies = /* @__PURE__ */ new Set();
1274
+ let staticBefore2 = 0;
1275
+ for (const child of children) {
1276
+ if (types.isJSXElement(child)) {
1277
+ const nested = analyzeRecursiveHostTree(
1278
+ child,
1279
+ statesByValue,
1280
+ safeGlobals,
1281
+ listNames,
1282
+ owner,
1283
+ allocateId
1284
+ );
1285
+ if (nested.reason) return nested.reason;
1286
+ plans.push(...nested.plans);
1287
+ for (const [nestedElement, plan2] of nested.blocks) blocks.set(nestedElement, plan2);
1288
+ for (const binding of nested.bindings) {
1289
+ staticBindings.push({
1290
+ ...binding,
1291
+ segment: ranges.length,
1292
+ sibling: staticBefore2
1293
+ });
1294
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1295
+ dependencies.add(dependency);
1296
+ }
1297
+ }
1298
+ staticBefore2 += 1;
1299
+ continue;
1300
+ }
1301
+ if (!types.isJSXExpressionContainer(child) || types.isJSXEmptyExpression(child.expression)) {
1302
+ return "recursive conditional ranges contain an unsupported child";
1303
+ }
1304
+ const expression = child.expression;
1305
+ const shape = conditionalChildren.get(expression);
1306
+ const truthy = shape.truthy ? analyzeRecursiveHostTree(
1307
+ shape.truthy,
1308
+ statesByValue,
1309
+ safeGlobals,
1310
+ listNames,
1311
+ id,
1312
+ allocateId
1313
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1314
+ const falsy = shape.falsy ? analyzeRecursiveHostTree(
1315
+ shape.falsy,
1316
+ statesByValue,
1317
+ safeGlobals,
1318
+ listNames,
1319
+ id,
1320
+ allocateId
1321
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1322
+ if (truthy.reason) return truthy.reason;
1323
+ if (falsy.reason) return falsy.reason;
1324
+ plans.push(...truthy.plans, ...falsy.plans);
1325
+ for (const [nestedElement, plan2] of truthy.blocks) blocks.set(nestedElement, plan2);
1326
+ for (const [nestedElement, plan2] of falsy.blocks) blocks.set(nestedElement, plan2);
1327
+ for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1328
+ dependencies.add(dependency);
1329
+ }
1330
+ for (const binding of [...truthy.bindings, ...falsy.bindings]) {
1331
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1332
+ dependencies.add(dependency);
1333
+ }
1334
+ }
1335
+ ranges.push({
1336
+ before: staticBefore2,
1337
+ source: expression,
1338
+ test: cloneExpression(shape.test),
1339
+ logical: shape.logical,
1340
+ truthy: shape.truthy,
1341
+ falsy: shape.falsy,
1342
+ truthyBindings: truthy.bindings,
1343
+ falsyBindings: falsy.bindings
1344
+ });
1345
+ staticBefore2 = 0;
1346
+ }
1347
+ const plan = {
1348
+ kind: "nested-host-conditional-ranges",
1349
+ id,
1350
+ parent: owner,
1351
+ dependencies: [...dependencies].sort((left, right) => left - right),
1352
+ source: element,
1353
+ ranges,
1354
+ trailing: staticBefore2,
1355
+ staticBindings
1356
+ };
1357
+ plans.push(plan);
1358
+ blocks.set(element, plan);
1359
+ return void 0;
1360
+ }
1361
+ const keyedRows = [];
1362
+ let keyedContainer = children.length > 0;
1363
+ let staticBefore = 0;
1364
+ const staticChildren = [];
1365
+ for (const child of children) {
1366
+ const range = analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, true);
1367
+ if (range) {
1368
+ keyedRows.push({ before: staticBefore, range });
1369
+ staticBefore = 0;
1370
+ } else if (types.isJSXElement(child) && isHostElement(child)) {
1371
+ staticChildren.push({
1372
+ element: child,
1373
+ segment: keyedRows.length,
1374
+ sibling: staticBefore
1375
+ });
1376
+ staticBefore += 1;
1377
+ } else {
1378
+ keyedContainer = false;
1379
+ break;
1380
+ }
1381
+ }
1382
+ if (keyedContainer && keyedRows.length > 0) {
1383
+ const id = allocateId();
1384
+ const keyedRanges = [];
1385
+ const staticBindings = [];
1386
+ const keyedDependencies = /* @__PURE__ */ new Set();
1387
+ for (const { before, range } of keyedRows) {
1388
+ if (range.events.length > 0) {
1389
+ return "recursive compiler-owned keyed rows cannot contain events";
1390
+ }
1391
+ const row = types.cloneNode(range.row, true);
1392
+ row.openingElement.attributes = row.openingElement.attributes.filter(
1393
+ (attribute) => !types.isJSXAttribute(attribute) || jsxAttributeName(attribute) !== "key"
1394
+ );
1395
+ const rowAnalysis = analyzeRecursiveHostTree(
1396
+ row,
1397
+ statesByValue,
1398
+ safeGlobals,
1399
+ listNames,
1400
+ id,
1401
+ allocateId
1402
+ );
1403
+ if (rowAnalysis.reason) return rowAnalysis.reason;
1404
+ plans.push(...rowAnalysis.plans);
1405
+ for (const [nestedElement, nestedPlan] of rowAnalysis.blocks) {
1406
+ blocks.set(nestedElement, nestedPlan);
1407
+ }
1408
+ keyedRanges.push({
1409
+ before,
1410
+ source: range.source,
1411
+ collection: range.collection,
1412
+ keyCallback: range.keyCallback,
1413
+ renderCallback: range.renderCallback,
1414
+ row,
1415
+ bindings: rowAnalysis.bindings,
1416
+ syntax: range.syntax
1417
+ });
1418
+ for (const dependency of range.dependencies) keyedDependencies.add(dependency);
1419
+ }
1420
+ for (const { element: child, segment, sibling } of staticChildren) {
1421
+ const nested = analyzeRecursiveHostTree(
1422
+ child,
1423
+ statesByValue,
1424
+ safeGlobals,
1425
+ listNames,
1426
+ owner,
1427
+ allocateId
1428
+ );
1429
+ if (nested.reason) return nested.reason;
1430
+ plans.push(...nested.plans);
1431
+ for (const [nestedElement, plan2] of nested.blocks) blocks.set(nestedElement, plan2);
1432
+ for (const binding of nested.bindings) {
1433
+ staticBindings.push({ ...binding, segment, sibling });
1434
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1435
+ keyedDependencies.add(dependency);
1436
+ }
1437
+ }
1438
+ }
1439
+ const plan = {
1440
+ kind: "nested-host-keyed-ranges",
1441
+ id,
1442
+ parent: owner,
1443
+ dependencies: [...keyedDependencies].sort((left, right) => left - right),
1444
+ source: element,
1445
+ ranges: keyedRanges,
1446
+ trailing: staticBefore,
1447
+ staticBindings
1448
+ };
1449
+ plans.push(plan);
1450
+ blocks.set(element, plan);
1451
+ return void 0;
1452
+ }
1453
+ if (element.children.some((child) => types.isJSXFragment(child))) {
1454
+ return "recursive compiler-owned blocks do not support fragments yet";
1455
+ }
1456
+ const nestedElements = element.children.filter(
1457
+ (child) => types.isJSXElement(child)
1458
+ );
1459
+ const expressions = element.children.filter(
1460
+ (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)
1461
+ );
1462
+ for (const child of expressions) {
1463
+ const unsupported = validateDerivedExpression(child.expression, safeGlobals);
1464
+ if (unsupported) return `recursive compiler-owned text cannot use ${unsupported}`;
1465
+ }
1466
+ if (nestedElements.length > 0 && expressions.length > 0) {
1467
+ return "recursive compiler-owned blocks do not support dynamic text beside nested elements";
1468
+ }
1469
+ if (nestedElements.length === 0 && expressions.length > 0) {
1470
+ const parts = [];
1471
+ for (const child of element.children) {
1472
+ if (types.isJSXText(child)) {
1473
+ const text = cleanJsxText(child.value);
1474
+ if (text) parts.push(types.stringLiteral(text));
1475
+ } else if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1476
+ parts.push(cloneExpression(child.expression));
1477
+ }
1478
+ }
1479
+ bindings.push({ kind: "text", path: [...path], value: types.arrayExpression(parts) });
1480
+ }
1481
+ let elementIndex = 0;
1482
+ for (const child of element.children) {
1483
+ if (!types.isJSXElement(child)) continue;
1484
+ const reason2 = visit(child, [...path, elementIndex], owner);
1485
+ if (reason2) return reason2;
1486
+ elementIndex += 1;
1487
+ }
1488
+ return void 0;
1489
+ };
1490
+ const reason = visit(branch, [], parent);
1491
+ return { bindings, plans, blocks, reason };
1492
+ }
1493
+ function analyzeCompilerOwnedKeyedRowHostBlocks(shape, statesByValue, safeGlobals, listNames, parent, allocateId) {
1494
+ if (shape.events.length > 0) return void 0;
1495
+ const row = types.cloneNode(shape.row, true);
1496
+ row.openingElement.attributes = row.openingElement.attributes.filter(
1497
+ (attribute) => !types.isJSXAttribute(attribute) || jsxAttributeName(attribute) !== "key"
1498
+ );
1499
+ const analysis = analyzeRecursiveHostTree(
1500
+ row,
1501
+ statesByValue,
1502
+ safeGlobals,
1503
+ listNames,
1504
+ parent,
1505
+ allocateId
1506
+ );
1507
+ if (analysis.reason || analysis.plans.length === 0) {
1508
+ return void 0;
1509
+ }
1510
+ return {
1511
+ row,
1512
+ bindings: analysis.bindings,
1513
+ descriptorBlocks: analysis.blocks,
1514
+ nestedPlans: analysis.plans
1515
+ };
1516
+ }
1517
+ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals, listNames, parent, allocateId) {
1011
1518
  const containerName = container.openingElement.name;
1012
1519
  if (!types.isJSXIdentifier(containerName) || !/^[a-z]/.test(containerName.name) || containerName.name === "svg") {
1013
1520
  return void 0;
@@ -1029,8 +1536,22 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals)
1029
1536
  }
1030
1537
  const shape = conditionalBlockShape(children[0].expression);
1031
1538
  if (!shape || validateDerivedExpression(shape.test, safeGlobals)) return void 0;
1032
- const truthyAnalysis = shape.truthy ? analyzeHostConditionalTree(shape.truthy, safeGlobals) : { bindings: [] };
1033
- const falsyAnalysis = shape.falsy ? analyzeHostConditionalTree(shape.falsy, safeGlobals) : { bindings: [] };
1539
+ const truthyAnalysis = shape.truthy ? analyzeRecursiveHostTree(
1540
+ shape.truthy,
1541
+ statesByValue,
1542
+ safeGlobals,
1543
+ listNames,
1544
+ parent,
1545
+ allocateId
1546
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1547
+ const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1548
+ shape.falsy,
1549
+ statesByValue,
1550
+ safeGlobals,
1551
+ listNames,
1552
+ parent,
1553
+ allocateId
1554
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1034
1555
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1035
1556
  const dependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1036
1557
  for (const binding of [...truthyAnalysis.bindings || [], ...falsyAnalysis.bindings || []]) {
@@ -1045,10 +1566,12 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals)
1045
1566
  truthy: shape.truthy,
1046
1567
  falsy: shape.falsy,
1047
1568
  truthyBindings: truthyAnalysis.bindings || [],
1048
- falsyBindings: falsyAnalysis.bindings || []
1569
+ falsyBindings: falsyAnalysis.bindings || [],
1570
+ descriptorBlocks: new Map([...truthyAnalysis.blocks, ...falsyAnalysis.blocks]),
1571
+ nestedPlans: [...truthyAnalysis.plans, ...falsyAnalysis.plans]
1049
1572
  };
1050
1573
  }
1051
- function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals, allowSingleRange = false) {
1574
+ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals, allowSingleRange, listNames, parent, allocateId) {
1052
1575
  const containerName = container.openingElement.name;
1053
1576
  if (!types.isJSXIdentifier(containerName) || !/^[a-z]/.test(containerName.name) || containerName.name === "svg" || container.openingElement.attributes.some(
1054
1577
  (attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
@@ -1059,14 +1582,34 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1059
1582
  if (children.length < (allowSingleRange ? 1 : 2)) return void 0;
1060
1583
  const ranges = [];
1061
1584
  const dependencies = /* @__PURE__ */ new Set();
1585
+ const descriptorBlocks = /* @__PURE__ */ new Map();
1586
+ const nestedPlans = [];
1587
+ const staticBindings = [];
1062
1588
  let staticBefore = 0;
1063
1589
  for (const child of children) {
1064
1590
  if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1065
1591
  const shape = conditionalBlockShape(child.expression);
1066
1592
  if (shape && !validateDerivedExpression(shape.test, safeGlobals)) {
1067
- const truthyAnalysis = shape.truthy ? analyzeHostConditionalTree(shape.truthy, safeGlobals) : { bindings: [] };
1068
- const falsyAnalysis = shape.falsy ? analyzeHostConditionalTree(shape.falsy, safeGlobals) : { bindings: [] };
1593
+ const truthyAnalysis = shape.truthy ? analyzeRecursiveHostTree(
1594
+ shape.truthy,
1595
+ statesByValue,
1596
+ safeGlobals,
1597
+ listNames,
1598
+ parent,
1599
+ allocateId
1600
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1601
+ const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1602
+ shape.falsy,
1603
+ statesByValue,
1604
+ safeGlobals,
1605
+ listNames,
1606
+ parent,
1607
+ allocateId
1608
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1069
1609
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1610
+ nestedPlans.push(...truthyAnalysis.plans, ...falsyAnalysis.plans);
1611
+ for (const [element, plan] of truthyAnalysis.blocks) descriptorBlocks.set(element, plan);
1612
+ for (const [element, plan] of falsyAnalysis.blocks) descriptorBlocks.set(element, plan);
1070
1613
  const rangeDependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1071
1614
  for (const binding of [
1072
1615
  ...truthyAnalysis.bindings || [],
@@ -1092,14 +1635,158 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1092
1635
  }
1093
1636
  }
1094
1637
  if (!types.isJSXElement(child) || !isHostElement(child)) return void 0;
1095
- if (!isStaticRangeSibling(child, statesByValue, safeGlobals)) return void 0;
1638
+ const staticAnalysis = analyzeHostConditionalTree(child, safeGlobals, true, statesByValue);
1639
+ if (staticAnalysis.reason) return void 0;
1640
+ for (const binding of staticAnalysis.bindings || []) {
1641
+ staticBindings.push({
1642
+ ...binding,
1643
+ segment: ranges.length,
1644
+ sibling: staticBefore
1645
+ });
1646
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1647
+ dependencies.add(dependency);
1648
+ }
1649
+ }
1096
1650
  staticBefore += 1;
1097
1651
  }
1098
1652
  if (ranges.length === 0) return void 0;
1099
1653
  return {
1100
1654
  ranges,
1101
1655
  trailing: staticBefore,
1102
- dependencies: [...dependencies].sort((left, right) => left - right)
1656
+ dependencies: [...dependencies].sort((left, right) => left - right),
1657
+ staticBindings,
1658
+ descriptorBlocks,
1659
+ nestedPlans
1660
+ };
1661
+ }
1662
+ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, listNames, parent, allocateId) {
1663
+ const containerName = container.openingElement.name;
1664
+ if (!types.isJSXIdentifier(containerName) || !/^[a-z]/.test(containerName.name) || containerName.name === "svg" || container.openingElement.attributes.some(
1665
+ (attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
1666
+ )) {
1667
+ return void 0;
1668
+ }
1669
+ const children = meaningfulJsxChildren(container);
1670
+ if (children.length < 2) return void 0;
1671
+ const ranges = [];
1672
+ const dependencies = /* @__PURE__ */ new Set();
1673
+ const descriptorBlocks = /* @__PURE__ */ new Map();
1674
+ const nestedPlans = [];
1675
+ const staticBindings = [];
1676
+ let conditionalCount = 0;
1677
+ let keyedCount = 0;
1678
+ let staticBefore = 0;
1679
+ const mergeAnalysis = (analysis) => {
1680
+ if (analysis.reason) return false;
1681
+ nestedPlans.push(...analysis.plans);
1682
+ for (const [element, plan] of analysis.blocks) descriptorBlocks.set(element, plan);
1683
+ for (const binding of analysis.bindings) {
1684
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1685
+ dependencies.add(dependency);
1686
+ }
1687
+ }
1688
+ return true;
1689
+ };
1690
+ for (const child of children) {
1691
+ if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1692
+ const shape = conditionalBlockShape(child.expression);
1693
+ if (shape && !validateDerivedExpression(shape.test, safeGlobals)) {
1694
+ const truthyAnalysis = shape.truthy ? analyzeRecursiveHostTree(
1695
+ shape.truthy,
1696
+ statesByValue,
1697
+ safeGlobals,
1698
+ listNames,
1699
+ parent,
1700
+ allocateId
1701
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1702
+ const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1703
+ shape.falsy,
1704
+ statesByValue,
1705
+ safeGlobals,
1706
+ listNames,
1707
+ parent,
1708
+ allocateId
1709
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1710
+ if (!mergeAnalysis(truthyAnalysis) || !mergeAnalysis(falsyAnalysis)) return void 0;
1711
+ for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1712
+ dependencies.add(dependency);
1713
+ }
1714
+ ranges.push({
1715
+ rangeKind: "conditional",
1716
+ before: staticBefore,
1717
+ source: child.expression,
1718
+ test: cloneExpression(shape.test),
1719
+ logical: shape.logical,
1720
+ truthy: shape.truthy,
1721
+ falsy: shape.falsy,
1722
+ truthyBindings: truthyAnalysis.bindings,
1723
+ falsyBindings: falsyAnalysis.bindings
1724
+ });
1725
+ conditionalCount += 1;
1726
+ staticBefore = 0;
1727
+ continue;
1728
+ }
1729
+ }
1730
+ const keyed = analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, true);
1731
+ if (keyed) {
1732
+ if (keyed.events.length > 0) return void 0;
1733
+ const row = types.cloneNode(keyed.row, true);
1734
+ row.openingElement.attributes = row.openingElement.attributes.filter(
1735
+ (attribute) => !types.isJSXAttribute(attribute) || jsxAttributeName(attribute) !== "key"
1736
+ );
1737
+ const rowAnalysis = analyzeRecursiveHostTree(
1738
+ row,
1739
+ statesByValue,
1740
+ safeGlobals,
1741
+ listNames,
1742
+ parent,
1743
+ allocateId
1744
+ );
1745
+ if (!mergeAnalysis(rowAnalysis)) return void 0;
1746
+ for (const dependency of keyed.dependencies) dependencies.add(dependency);
1747
+ ranges.push({
1748
+ rangeKind: "keyed",
1749
+ before: staticBefore,
1750
+ source: keyed.source,
1751
+ collection: keyed.collection,
1752
+ keyCallback: keyed.keyCallback,
1753
+ renderCallback: keyed.renderCallback,
1754
+ row,
1755
+ bindings: rowAnalysis.bindings,
1756
+ syntax: keyed.syntax
1757
+ });
1758
+ keyedCount += 1;
1759
+ staticBefore = 0;
1760
+ continue;
1761
+ }
1762
+ if (!types.isJSXElement(child) || !isHostElement(child)) return void 0;
1763
+ const staticAnalysis = analyzeRecursiveHostTree(
1764
+ child,
1765
+ statesByValue,
1766
+ safeGlobals,
1767
+ listNames,
1768
+ parent,
1769
+ allocateId
1770
+ );
1771
+ if (staticAnalysis.reason) return void 0;
1772
+ for (const binding of staticAnalysis.bindings) {
1773
+ staticBindings.push({
1774
+ ...binding,
1775
+ segment: ranges.length,
1776
+ sibling: staticBefore
1777
+ });
1778
+ }
1779
+ if (!mergeAnalysis(staticAnalysis)) return void 0;
1780
+ staticBefore += 1;
1781
+ }
1782
+ if (conditionalCount === 0 || keyedCount === 0) return void 0;
1783
+ return {
1784
+ ranges,
1785
+ trailing: staticBefore,
1786
+ dependencies: [...dependencies].sort((left, right) => left - right),
1787
+ staticBindings,
1788
+ descriptorBlocks,
1789
+ nestedPlans
1103
1790
  };
1104
1791
  }
1105
1792
  function keyedListBoundary(blockRuntime, plan, source) {
@@ -1124,14 +1811,14 @@ function keyedListBoundary(blockRuntime, plan, source) {
1124
1811
  true
1125
1812
  );
1126
1813
  }
1127
- function hostElementDescriptor(element) {
1814
+ function hostElementDescriptor(element, descriptorBlocks = /* @__PURE__ */ new Map()) {
1128
1815
  const tag = element.openingElement.name;
1129
1816
  const attributes = [];
1130
1817
  const styles = [];
1131
1818
  for (const attribute of element.openingElement.attributes) {
1132
1819
  if (!types.isJSXAttribute(attribute)) continue;
1133
1820
  const name = jsxAttributeName(attribute);
1134
- if (!name || name === "key" || /^on[A-Z]/.test(name)) continue;
1821
+ if (!name || name === "key" || name === "ref" || /^on[A-Z]/.test(name)) continue;
1135
1822
  if (name === "style") {
1136
1823
  if (types.isJSXExpressionContainer(attribute.value) && types.isObjectExpression(attribute.value.expression)) {
1137
1824
  for (const property of attribute.value.expression.properties) {
@@ -1155,24 +1842,93 @@ function hostElementDescriptor(element) {
1155
1842
  ])
1156
1843
  );
1157
1844
  }
1845
+ const block = descriptorBlocks.get(element);
1846
+ const conditionalBySource = /* @__PURE__ */ new Map();
1847
+ const keyedBySource = /* @__PURE__ */ new Map();
1848
+ if (block?.kind === "nested-host-conditional-ranges") {
1849
+ for (const range of block.ranges) conditionalBySource.set(range.source, range);
1850
+ } else if (block?.kind === "nested-host-keyed-ranges") {
1851
+ for (const range of block.ranges) keyedBySource.set(range.source, range);
1852
+ } else if (block?.kind === "nested-host-mixed-ranges") {
1853
+ for (const range of block.ranges) {
1854
+ if (range.rangeKind === "conditional") conditionalBySource.set(range.source, range);
1855
+ else keyedBySource.set(range.source, range);
1856
+ }
1857
+ }
1158
1858
  const children = [];
1159
1859
  for (const child of element.children) {
1160
1860
  if (types.isJSXText(child)) {
1161
1861
  const text = cleanJsxText(child.value);
1162
1862
  if (text) children.push(types.stringLiteral(text));
1163
1863
  } else if (types.isJSXElement(child)) {
1164
- children.push(hostElementDescriptor(child));
1864
+ const keyedRange = keyedBySource.get(child);
1865
+ if (keyedRange) {
1866
+ if (block?.kind !== "nested-host-keyed-ranges") {
1867
+ children.push(keyedRangeDescriptorChildren(keyedRange, descriptorBlocks));
1868
+ }
1869
+ } else {
1870
+ children.push(hostElementDescriptor(child, descriptorBlocks));
1871
+ }
1165
1872
  } else if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1166
- children.push(cloneExpression(child.expression));
1873
+ const conditionalRange = conditionalBySource.get(child.expression);
1874
+ const keyedRange = keyedBySource.get(child.expression);
1875
+ if (conditionalRange) {
1876
+ children.push(conditionalRangeDescriptorChild(conditionalRange, descriptorBlocks));
1877
+ } else if (keyedRange) {
1878
+ if (block?.kind !== "nested-host-keyed-ranges") {
1879
+ children.push(keyedRangeDescriptorChildren(keyedRange, descriptorBlocks));
1880
+ }
1881
+ } else {
1882
+ children.push(cloneExpression(child.expression));
1883
+ }
1167
1884
  }
1168
1885
  }
1169
- return types.objectExpression([
1886
+ const properties = [
1170
1887
  types.objectProperty(types.identifier("kind"), types.stringLiteral("element")),
1171
1888
  types.objectProperty(types.identifier("tag"), types.stringLiteral(tag.name)),
1172
1889
  types.objectProperty(types.identifier("attributes"), types.arrayExpression(attributes)),
1173
1890
  types.objectProperty(types.identifier("styles"), types.arrayExpression(styles)),
1174
1891
  types.objectProperty(types.identifier("children"), types.arrayExpression(children))
1175
- ]);
1892
+ ];
1893
+ if (block) {
1894
+ properties.push(
1895
+ types.objectProperty(types.identifier("block"), nestedHostBlockObject(block, descriptorBlocks))
1896
+ );
1897
+ }
1898
+ return types.objectExpression(properties);
1899
+ }
1900
+ function conditionalRangeDescriptorChild(range, descriptorBlocks) {
1901
+ const truthy = range.truthy ? hostElementDescriptor(range.truthy, descriptorBlocks) : types.nullLiteral();
1902
+ if (range.logical) {
1903
+ return types.logicalExpression("&&", cloneExpression(range.test), truthy);
1904
+ }
1905
+ return types.conditionalExpression(
1906
+ cloneExpression(range.test),
1907
+ truthy,
1908
+ range.falsy ? hostElementDescriptor(range.falsy, descriptorBlocks) : types.nullLiteral()
1909
+ );
1910
+ }
1911
+ function keyedRangeDescriptorChildren(range, descriptorBlocks) {
1912
+ const parameters = range.renderCallback.params.map(
1913
+ (parameter) => types.cloneNode(parameter, true)
1914
+ );
1915
+ const callback = types.arrowFunctionExpression(
1916
+ parameters,
1917
+ hostElementDescriptor(range.row, descriptorBlocks)
1918
+ );
1919
+ const collection = cloneExpression(range.collection);
1920
+ if (range.syntax === "map") {
1921
+ return types.callExpression(types.memberExpression(collection, types.identifier("map")), [callback]);
1922
+ }
1923
+ return types.callExpression(
1924
+ types.memberExpression(
1925
+ types.callExpression(types.memberExpression(types.identifier("Array"), types.identifier("from")), [
1926
+ types.logicalExpression("||", collection, types.arrayExpression([]))
1927
+ ]),
1928
+ types.identifier("map")
1929
+ ),
1930
+ [callback]
1931
+ );
1176
1932
  }
1177
1933
  function keyedRowBindingObject(binding, parameters) {
1178
1934
  const properties = [
@@ -1180,6 +1936,12 @@ function keyedRowBindingObject(binding, parameters) {
1180
1936
  types.objectProperty(
1181
1937
  types.identifier("path"),
1182
1938
  types.arrayExpression(binding.path.map((part) => types.numericLiteral(part)))
1939
+ ),
1940
+ types.objectProperty(
1941
+ types.identifier("dependencies"),
1942
+ types.arrayExpression(
1943
+ (binding.dependencies || []).map((dependency) => types.numericLiteral(dependency))
1944
+ )
1183
1945
  )
1184
1946
  ];
1185
1947
  if (binding.name) {
@@ -1196,6 +1958,16 @@ function keyedRowBindingObject(binding, parameters) {
1196
1958
  );
1197
1959
  return types.objectExpression(properties);
1198
1960
  }
1961
+ function staticRangeBindingObject(binding) {
1962
+ const value = keyedRowBindingObject(binding, []);
1963
+ value.properties.splice(
1964
+ 1,
1965
+ 0,
1966
+ types.objectProperty(types.identifier("segment"), types.numericLiteral(binding.segment)),
1967
+ types.objectProperty(types.identifier("sibling"), types.numericLiteral(binding.sibling))
1968
+ );
1969
+ return value;
1970
+ }
1199
1971
  function uniqueLocalIdentifier(base, nodes) {
1200
1972
  const names = /* @__PURE__ */ new Set();
1201
1973
  for (const node of nodes) {
@@ -1312,6 +2084,10 @@ function keyedRowEventObject(event, renderParameters) {
1312
2084
  const parameters = [types.cloneNode(renderParameters[0], true), index, syntheticEvent];
1313
2085
  return types.objectExpression([
1314
2086
  types.objectProperty(types.identifier("name"), types.stringLiteral(event.name)),
2087
+ types.objectProperty(
2088
+ types.identifier("path"),
2089
+ types.arrayExpression(event.path.map((part) => types.numericLiteral(part)))
2090
+ ),
1315
2091
  types.objectProperty(
1316
2092
  types.identifier("invoke"),
1317
2093
  types.arrowFunctionExpression(
@@ -1381,6 +2157,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1381
2157
  plan.source,
1382
2158
  ...plan.conditionals.map((conditional) => conditional.test)
1383
2159
  ]);
2160
+ const delegatedEvents = canDelegateKeyedRowEvents(plan);
1384
2161
  const renderSource = keyedRowsRenderSource(plan, eventFactory, conditionalFactory);
1385
2162
  const renderFactoryParameters = plan.conditionals.length > 0 ? [types.cloneNode(eventFactory), types.cloneNode(conditionalFactory)] : plan.events.length > 0 ? [types.cloneNode(eventFactory)] : [];
1386
2163
  return types.jsxElement(
@@ -1398,6 +2175,14 @@ function keyedRowsBoundary(blockRuntime, plan) {
1398
2175
  types.jsxIdentifier("items"),
1399
2176
  types.jsxExpressionContainer(types.arrowFunctionExpression([], cloneExpression(plan.collection)))
1400
2177
  ),
2178
+ types.jsxAttribute(
2179
+ types.jsxIdentifier("structureDependencies"),
2180
+ types.jsxExpressionContainer(
2181
+ types.arrayExpression(
2182
+ plan.structureDependencies.map((dependency) => types.numericLiteral(dependency))
2183
+ )
2184
+ )
2185
+ ),
1401
2186
  types.jsxAttribute(
1402
2187
  types.jsxIdentifier("rowKey"),
1403
2188
  types.jsxExpressionContainer(types.cloneNode(plan.keyCallback, true))
@@ -1407,7 +2192,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1407
2192
  types.jsxExpressionContainer(
1408
2193
  types.arrowFunctionExpression(
1409
2194
  parameters.map((parameter) => types.cloneNode(parameter, true)),
1410
- hostElementDescriptor(plan.row)
2195
+ hostElementDescriptor(plan.row, plan.descriptorBlocks)
1411
2196
  )
1412
2197
  )
1413
2198
  ),
@@ -1427,7 +2212,13 @@ function keyedRowsBoundary(blockRuntime, plan) {
1427
2212
  plan.events.map((event) => keyedRowEventObject(event, parameters))
1428
2213
  )
1429
2214
  )
1430
- )
2215
+ ),
2216
+ ...delegatedEvents ? [
2217
+ types.jsxAttribute(
2218
+ types.jsxIdentifier("delegateEvents"),
2219
+ types.jsxExpressionContainer(types.booleanLiteral(true))
2220
+ )
2221
+ ] : []
1431
2222
  ] : [],
1432
2223
  ...plan.conditionals.length > 0 ? [
1433
2224
  types.jsxAttribute(
@@ -1440,6 +2231,12 @@ function keyedRowsBoundary(blockRuntime, plan) {
1440
2231
  )
1441
2232
  )
1442
2233
  )
2234
+ ] : [],
2235
+ ...plan.descriptorBlocks?.size ? [
2236
+ types.jsxAttribute(
2237
+ types.jsxIdentifier("hostBlocks"),
2238
+ types.jsxExpressionContainer(types.booleanLiteral(true))
2239
+ )
1443
2240
  ] : []
1444
2241
  ],
1445
2242
  true
@@ -1449,7 +2246,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1449
2246
  true
1450
2247
  );
1451
2248
  }
1452
- function keyedRangeObject(range) {
2249
+ function keyedRangeObject(range, descriptorBlocks = /* @__PURE__ */ new Map()) {
1453
2250
  const parameters = range.renderCallback.params.map(
1454
2251
  (parameter) => types.cloneNode(parameter, true)
1455
2252
  );
@@ -1464,7 +2261,7 @@ function keyedRangeObject(range) {
1464
2261
  types.identifier("create"),
1465
2262
  types.arrowFunctionExpression(
1466
2263
  parameters.map((parameter) => types.cloneNode(parameter, true)),
1467
- hostElementDescriptor(range.row)
2264
+ hostElementDescriptor(range.row, descriptorBlocks)
1468
2265
  )
1469
2266
  ),
1470
2267
  types.objectProperty(
@@ -1508,6 +2305,12 @@ function keyedRangesBoundary(blockRuntime, plan) {
1508
2305
  types.jsxAttribute(
1509
2306
  types.jsxIdentifier("trailing"),
1510
2307
  types.jsxExpressionContainer(types.numericLiteral(plan.trailing))
2308
+ ),
2309
+ types.jsxAttribute(
2310
+ types.jsxIdentifier("bindings"),
2311
+ types.jsxExpressionContainer(
2312
+ types.arrayExpression(plan.staticBindings.map(staticRangeBindingObject))
2313
+ )
1511
2314
  )
1512
2315
  ],
1513
2316
  true
@@ -1517,11 +2320,11 @@ function keyedRangesBoundary(blockRuntime, plan) {
1517
2320
  true
1518
2321
  );
1519
2322
  }
1520
- function hostConditionalBranchObject(branch, bindings) {
2323
+ function hostConditionalBranchObject(branch, bindings, descriptorBlocks = /* @__PURE__ */ new Map()) {
1521
2324
  return types.objectExpression([
1522
2325
  types.objectProperty(
1523
2326
  types.identifier("create"),
1524
- types.arrowFunctionExpression([], hostElementDescriptor(branch))
2327
+ types.arrowFunctionExpression([], hostElementDescriptor(branch, descriptorBlocks))
1525
2328
  ),
1526
2329
  types.objectProperty(
1527
2330
  types.identifier("bindings"),
@@ -1550,7 +2353,9 @@ function hostConditionalBoundary(blockRuntime, plan) {
1550
2353
  attributes.push(
1551
2354
  types.jsxAttribute(
1552
2355
  types.jsxIdentifier("truthy"),
1553
- types.jsxExpressionContainer(hostConditionalBranchObject(plan.truthy, plan.truthyBindings))
2356
+ types.jsxExpressionContainer(
2357
+ hostConditionalBranchObject(plan.truthy, plan.truthyBindings, plan.descriptorBlocks)
2358
+ )
1554
2359
  )
1555
2360
  );
1556
2361
  }
@@ -1558,13 +2363,15 @@ function hostConditionalBoundary(blockRuntime, plan) {
1558
2363
  attributes.push(
1559
2364
  types.jsxAttribute(
1560
2365
  types.jsxIdentifier("falsy"),
1561
- types.jsxExpressionContainer(hostConditionalBranchObject(plan.falsy, plan.falsyBindings))
2366
+ types.jsxExpressionContainer(
2367
+ hostConditionalBranchObject(plan.falsy, plan.falsyBindings, plan.descriptorBlocks)
2368
+ )
1562
2369
  )
1563
2370
  );
1564
2371
  }
1565
2372
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
1566
2373
  }
1567
- function conditionalRangeObject(range) {
2374
+ function conditionalRangeObject(range, descriptorBlocks = /* @__PURE__ */ new Map()) {
1568
2375
  const properties = [
1569
2376
  types.objectProperty(types.identifier("before"), types.numericLiteral(range.before)),
1570
2377
  types.objectProperty(
@@ -1579,7 +2386,7 @@ function conditionalRangeObject(range) {
1579
2386
  properties.push(
1580
2387
  types.objectProperty(
1581
2388
  types.identifier("truthy"),
1582
- hostConditionalBranchObject(range.truthy, range.truthyBindings)
2389
+ hostConditionalBranchObject(range.truthy, range.truthyBindings, descriptorBlocks)
1583
2390
  )
1584
2391
  );
1585
2392
  }
@@ -1587,12 +2394,78 @@ function conditionalRangeObject(range) {
1587
2394
  properties.push(
1588
2395
  types.objectProperty(
1589
2396
  types.identifier("falsy"),
1590
- hostConditionalBranchObject(range.falsy, range.falsyBindings)
2397
+ hostConditionalBranchObject(range.falsy, range.falsyBindings, descriptorBlocks)
1591
2398
  )
1592
2399
  );
1593
2400
  }
1594
2401
  return types.objectExpression(properties);
1595
2402
  }
2403
+ function nestedHostBlockObject(plan, descriptorBlocks) {
2404
+ const kind = plan.kind === "nested-host-conditional-ranges" ? "conditional-ranges" : plan.kind === "nested-host-keyed-ranges" ? "keyed-ranges" : "mixed-ranges";
2405
+ return types.objectExpression([
2406
+ types.objectProperty(types.identifier("kind"), types.stringLiteral(kind)),
2407
+ types.objectProperty(types.identifier("id"), types.numericLiteral(plan.id)),
2408
+ types.objectProperty(
2409
+ types.identifier("ranges"),
2410
+ types.arrayExpression(
2411
+ plan.kind === "nested-host-conditional-ranges" ? plan.ranges.map((range) => conditionalRangeObject(range, descriptorBlocks)) : plan.kind === "nested-host-keyed-ranges" ? plan.ranges.map((range) => keyedRangeObject(range, descriptorBlocks)) : plan.ranges.map((range) => mixedRangeObject(range, descriptorBlocks))
2412
+ )
2413
+ ),
2414
+ types.objectProperty(types.identifier("trailing"), types.numericLiteral(plan.trailing)),
2415
+ types.objectProperty(
2416
+ types.identifier("bindings"),
2417
+ types.arrayExpression(plan.staticBindings.map(staticRangeBindingObject))
2418
+ ),
2419
+ ...plan.kind === "nested-host-keyed-ranges" ? [types.objectProperty(types.identifier("staticChildrenOnly"), types.booleanLiteral(true))] : []
2420
+ ]);
2421
+ }
2422
+ function mixedRangeObject(range, descriptorBlocks) {
2423
+ const value = range.rangeKind === "conditional" ? conditionalRangeObject(range, descriptorBlocks) : keyedRangeObject(range, descriptorBlocks);
2424
+ value.properties.unshift(
2425
+ types.objectProperty(types.identifier("kind"), types.stringLiteral(range.rangeKind))
2426
+ );
2427
+ return value;
2428
+ }
2429
+ function mixedRangesBoundary(blockRuntime, plan) {
2430
+ const name = types.jsxMemberExpression(
2431
+ types.jsxIdentifier(blockRuntime.name),
2432
+ types.jsxIdentifier("MixedRanges")
2433
+ );
2434
+ const source = types.cloneNode(plan.source, true);
2435
+ const rootRefIndex = source.openingElement.attributes.findIndex(
2436
+ (attribute) => types.isJSXAttribute(attribute) && jsxAttributeName(attribute) === "ref"
2437
+ );
2438
+ const rootRef = rootRefIndex >= 0 ? source.openingElement.attributes.splice(rootRefIndex, 1)[0] : void 0;
2439
+ const nestedPlan = {
2440
+ kind: "nested-host-mixed-ranges",
2441
+ id: plan.id,
2442
+ parent: plan.parent,
2443
+ dependencies: plan.dependencies,
2444
+ source: plan.source,
2445
+ ranges: plan.ranges,
2446
+ trailing: plan.trailing,
2447
+ staticBindings: plan.staticBindings
2448
+ };
2449
+ const descriptorBlocks = new Map(plan.descriptorBlocks);
2450
+ descriptorBlocks.set(plan.source, nestedPlan);
2451
+ const attributes = [
2452
+ types.jsxAttribute(types.jsxIdentifier("id"), types.jsxExpressionContainer(types.numericLiteral(plan.id))),
2453
+ types.jsxAttribute(
2454
+ types.jsxIdentifier("render"),
2455
+ types.jsxExpressionContainer(types.arrowFunctionExpression([], source))
2456
+ ),
2457
+ types.jsxAttribute(
2458
+ types.jsxIdentifier("create"),
2459
+ types.jsxExpressionContainer(
2460
+ types.arrowFunctionExpression([], hostElementDescriptor(plan.source, descriptorBlocks))
2461
+ )
2462
+ )
2463
+ ];
2464
+ if (rootRef?.value) {
2465
+ attributes.push(types.jsxAttribute(types.jsxIdentifier("rootRef"), types.cloneNode(rootRef.value, true)));
2466
+ }
2467
+ return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
2468
+ }
1596
2469
  function conditionalRangesBoundary(blockRuntime, plan) {
1597
2470
  const name = types.jsxMemberExpression(
1598
2471
  types.jsxIdentifier(blockRuntime.name),
@@ -1612,12 +2485,20 @@ function conditionalRangesBoundary(blockRuntime, plan) {
1612
2485
  types.jsxAttribute(
1613
2486
  types.jsxIdentifier("ranges"),
1614
2487
  types.jsxExpressionContainer(
1615
- types.arrayExpression(plan.ranges.map((range) => conditionalRangeObject(range)))
2488
+ types.arrayExpression(
2489
+ plan.ranges.map((range) => conditionalRangeObject(range, plan.descriptorBlocks))
2490
+ )
1616
2491
  )
1617
2492
  ),
1618
2493
  types.jsxAttribute(
1619
2494
  types.jsxIdentifier("trailing"),
1620
2495
  types.jsxExpressionContainer(types.numericLiteral(plan.trailing))
2496
+ ),
2497
+ types.jsxAttribute(
2498
+ types.jsxIdentifier("bindings"),
2499
+ types.jsxExpressionContainer(
2500
+ types.arrayExpression(plan.staticBindings.map(staticRangeBindingObject))
2501
+ )
1621
2502
  )
1622
2503
  ];
1623
2504
  if (rootRef?.value) {
@@ -1790,6 +2671,41 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1790
2671
  }
1791
2672
  if (types.isJSXElement(child)) {
1792
2673
  if (isHostElement(child)) {
2674
+ const mixedRangeStart = nextId;
2675
+ const mixedRangeId = nextId++;
2676
+ const mixedRanges = insideConditional ? void 0 : analyzeMixedRangesContainer(
2677
+ child,
2678
+ statesByValue,
2679
+ safeGlobals,
2680
+ listNames,
2681
+ mixedRangeId,
2682
+ () => nextId++
2683
+ );
2684
+ if (mixedRanges) {
2685
+ plans.push({
2686
+ kind: "mixed-ranges",
2687
+ id: mixedRangeId,
2688
+ parent,
2689
+ dependencies: mixedRanges.dependencies,
2690
+ source: child,
2691
+ ranges: mixedRanges.ranges,
2692
+ trailing: mixedRanges.trailing,
2693
+ staticBindings: mixedRanges.staticBindings,
2694
+ descriptorBlocks: mixedRanges.descriptorBlocks
2695
+ });
2696
+ plans.push(...mixedRanges.nestedPlans);
2697
+ for (const range of mixedRanges.ranges) {
2698
+ if (range.rangeKind === "conditional") {
2699
+ conditionalExpressions.add(range.source);
2700
+ } else if (types.isJSXElement(range.source)) {
2701
+ ownedElements.add(range.source);
2702
+ } else {
2703
+ keyedExpressions.add(range.source);
2704
+ }
2705
+ }
2706
+ continue;
2707
+ }
2708
+ nextId = mixedRangeStart;
1793
2709
  const keyedRanges = insideConditional ? void 0 : analyzeKeyedRangesContainer(child, statesByValue, safeGlobals, listNames);
1794
2710
  if (keyedRanges) {
1795
2711
  plans.push({
@@ -1799,7 +2715,8 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1799
2715
  dependencies: keyedRanges.dependencies,
1800
2716
  source: child,
1801
2717
  ranges: keyedRanges.ranges,
1802
- trailing: keyedRanges.trailing
2718
+ trailing: keyedRanges.trailing,
2719
+ staticBindings: keyedRanges.staticBindings
1803
2720
  });
1804
2721
  for (const range of keyedRanges.ranges) {
1805
2722
  if (types.isJSXElement(range.source)) ownedElements.add(range.source);
@@ -1807,58 +2724,102 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1807
2724
  }
1808
2725
  continue;
1809
2726
  }
1810
- const conditionalRanges = insideConditional ? void 0 : analyzeConditionalRangesContainer(child, statesByValue, safeGlobals);
2727
+ const conditionalRangeStart = nextId;
2728
+ const conditionalRangeId = nextId++;
2729
+ const conditionalRanges = insideConditional ? void 0 : analyzeConditionalRangesContainer(
2730
+ child,
2731
+ statesByValue,
2732
+ safeGlobals,
2733
+ false,
2734
+ listNames,
2735
+ conditionalRangeId,
2736
+ () => nextId++
2737
+ );
1811
2738
  if (conditionalRanges) {
1812
2739
  plans.push({
1813
2740
  kind: "conditional-ranges",
1814
- id: nextId++,
2741
+ id: conditionalRangeId,
1815
2742
  parent,
1816
2743
  dependencies: conditionalRanges.dependencies,
1817
2744
  source: child,
1818
2745
  ranges: conditionalRanges.ranges,
1819
- trailing: conditionalRanges.trailing
2746
+ trailing: conditionalRanges.trailing,
2747
+ staticBindings: conditionalRanges.staticBindings,
2748
+ descriptorBlocks: conditionalRanges.descriptorBlocks
1820
2749
  });
2750
+ plans.push(...conditionalRanges.nestedPlans || []);
1821
2751
  for (const range of conditionalRanges.ranges) {
1822
2752
  conditionalExpressions.add(range.source);
1823
2753
  }
1824
2754
  continue;
1825
2755
  }
2756
+ nextId = conditionalRangeStart;
2757
+ const hostConditionalStart = nextId;
2758
+ const hostConditionalId = nextId++;
1826
2759
  const hostConditional = analyzeHostConditionalContainer(
1827
2760
  child,
1828
2761
  statesByValue,
1829
- safeGlobals
2762
+ safeGlobals,
2763
+ listNames,
2764
+ hostConditionalId,
2765
+ () => nextId++
1830
2766
  );
1831
2767
  if (hostConditional) {
1832
2768
  plans.push({
1833
2769
  kind: "host-conditional",
1834
- id: nextId++,
2770
+ id: hostConditionalId,
1835
2771
  parent,
1836
2772
  source: child,
1837
2773
  ...hostConditional
1838
2774
  });
2775
+ plans.push(...hostConditional.nestedPlans || []);
1839
2776
  ownedElements.add(child);
1840
2777
  continue;
1841
2778
  }
2779
+ nextId = hostConditionalStart;
2780
+ const keyedRowsStart = nextId;
2781
+ const keyedRowsId = nextId++;
1842
2782
  const keyedRows = analyzeKeyedRowsContainer(child, statesByValue, safeGlobals, listNames);
1843
2783
  if (keyedRows) {
2784
+ const nestedStart = nextId;
2785
+ const hostBlocks = analyzeCompilerOwnedKeyedRowHostBlocks(
2786
+ keyedRows,
2787
+ statesByValue,
2788
+ safeGlobals,
2789
+ listNames,
2790
+ keyedRowsId,
2791
+ () => nextId++
2792
+ );
2793
+ if (!hostBlocks) nextId = nestedStart;
2794
+ if (keyedRows.rowReason && !hostBlocks) {
2795
+ nextId = keyedRowsStart;
2796
+ const nested2 = visitHost(child, parent, insideConditional);
2797
+ if (nested2.reason) return { dependencies, reason: nested2.reason };
2798
+ mergeDependencies(dependencies, nested2.dependencies);
2799
+ continue;
2800
+ }
1844
2801
  plans.push({
1845
2802
  kind: "keyed-rows",
1846
- id: nextId++,
2803
+ id: keyedRowsId,
1847
2804
  parent,
1848
2805
  dependencies: keyedRows.dependencies,
1849
2806
  source: child,
1850
2807
  collection: keyedRows.collection,
2808
+ structureDependencies: keyedRows.structureDependencies,
1851
2809
  keyCallback: keyedRows.keyCallback,
1852
2810
  renderCallback: keyedRows.renderCallback,
1853
- row: keyedRows.row,
1854
- bindings: keyedRows.bindings,
2811
+ row: hostBlocks?.row || keyedRows.row,
2812
+ bindings: hostBlocks?.bindings || keyedRows.bindings,
1855
2813
  events: keyedRows.events,
1856
- conditionals: keyedRows.conditionals,
2814
+ conditionals: hostBlocks ? [] : keyedRows.conditionals,
2815
+ descriptorBlocks: hostBlocks?.descriptorBlocks,
1857
2816
  syntax: keyedRows.syntax
1858
2817
  });
2818
+ plans.push(...hostBlocks?.nestedPlans || []);
1859
2819
  ownedElements.add(child);
1860
2820
  continue;
1861
2821
  }
2822
+ nextId = keyedRowsStart;
1862
2823
  const nested = visitHost(child, parent, insideConditional);
1863
2824
  if (nested.reason) return { dependencies, reason: nested.reason };
1864
2825
  mergeDependencies(dependencies, nested.dependencies);
@@ -1955,6 +2916,46 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1955
2916
  }
1956
2917
  return { dependencies };
1957
2918
  };
2919
+ const rootMixedRangeStart = nextId;
2920
+ const rootMixedRangeId = nextId++;
2921
+ const rootMixedRanges = analyzeMixedRangesContainer(
2922
+ root,
2923
+ statesByValue,
2924
+ safeGlobals,
2925
+ listNames,
2926
+ rootMixedRangeId,
2927
+ () => nextId++
2928
+ );
2929
+ if (rootMixedRanges) {
2930
+ plans.push({
2931
+ kind: "mixed-ranges",
2932
+ id: rootMixedRangeId,
2933
+ dependencies: rootMixedRanges.dependencies,
2934
+ source: root,
2935
+ ranges: rootMixedRanges.ranges,
2936
+ trailing: rootMixedRanges.trailing,
2937
+ staticBindings: rootMixedRanges.staticBindings,
2938
+ descriptorBlocks: rootMixedRanges.descriptorBlocks
2939
+ });
2940
+ plans.push(...rootMixedRanges.nestedPlans);
2941
+ for (const range of rootMixedRanges.ranges) {
2942
+ if (range.rangeKind === "conditional") {
2943
+ conditionalExpressions.add(range.source);
2944
+ } else if (types.isJSXElement(range.source)) {
2945
+ ownedElements.add(range.source);
2946
+ } else {
2947
+ keyedExpressions.add(range.source);
2948
+ }
2949
+ }
2950
+ return {
2951
+ plans,
2952
+ componentElements,
2953
+ conditionalExpressions,
2954
+ ownedElements,
2955
+ keyedExpressions
2956
+ };
2957
+ }
2958
+ nextId = rootMixedRangeStart;
1958
2959
  const rootRanges = analyzeKeyedRangesContainer(root, statesByValue, safeGlobals, listNames, true);
1959
2960
  if (rootRanges) {
1960
2961
  plans.push({
@@ -1963,7 +2964,8 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1963
2964
  dependencies: rootRanges.dependencies,
1964
2965
  source: root,
1965
2966
  ranges: rootRanges.ranges,
1966
- trailing: rootRanges.trailing
2967
+ trailing: rootRanges.trailing,
2968
+ staticBindings: rootRanges.staticBindings
1967
2969
  });
1968
2970
  for (const range of rootRanges.ranges) {
1969
2971
  if (types.isJSXElement(range.source)) ownedElements.add(range.source);
@@ -1977,21 +2979,29 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1977
2979
  keyedExpressions
1978
2980
  };
1979
2981
  }
2982
+ const rootConditionalRangeStart = nextId;
2983
+ const rootConditionalRangeId = nextId++;
1980
2984
  const rootConditionalRanges = analyzeConditionalRangesContainer(
1981
2985
  root,
1982
2986
  statesByValue,
1983
2987
  safeGlobals,
1984
- true
2988
+ true,
2989
+ listNames,
2990
+ rootConditionalRangeId,
2991
+ () => nextId++
1985
2992
  );
1986
2993
  if (rootConditionalRanges) {
1987
2994
  plans.push({
1988
2995
  kind: "conditional-ranges",
1989
- id: nextId++,
2996
+ id: rootConditionalRangeId,
1990
2997
  dependencies: rootConditionalRanges.dependencies,
1991
2998
  source: root,
1992
2999
  ranges: rootConditionalRanges.ranges,
1993
- trailing: rootConditionalRanges.trailing
3000
+ trailing: rootConditionalRanges.trailing,
3001
+ staticBindings: rootConditionalRanges.staticBindings,
3002
+ descriptorBlocks: rootConditionalRanges.descriptorBlocks
1994
3003
  });
3004
+ plans.push(...rootConditionalRanges.nestedPlans || []);
1995
3005
  for (const range of rootConditionalRanges.ranges) {
1996
3006
  conditionalExpressions.add(range.source);
1997
3007
  }
@@ -2003,6 +3013,7 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
2003
3013
  keyedExpressions
2004
3014
  };
2005
3015
  }
3016
+ nextId = rootConditionalRangeStart;
2006
3017
  const result = visitHost(root, void 0, false);
2007
3018
  if (result.reason) return { reason: result.reason };
2008
3019
  return {
@@ -2040,6 +3051,9 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
2040
3051
  plans.map((plan) => [plan.source, plan])
2041
3052
  );
2042
3053
  const rootPlan = planBySource.get(root);
3054
+ if (rootPlan?.kind === "mixed-ranges") {
3055
+ return mixedRangesBoundary(blockRuntime, rootPlan);
3056
+ }
2043
3057
  if (rootPlan?.kind === "keyed-ranges") {
2044
3058
  return keyedRangesBoundary(blockRuntime, rootPlan);
2045
3059
  }
@@ -2053,6 +3067,9 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
2053
3067
  if (plan2?.kind === "host-conditional") {
2054
3068
  return hostConditionalBoundary(blockRuntime, plan2);
2055
3069
  }
3070
+ if (plan2?.kind === "mixed-ranges") {
3071
+ return mixedRangesBoundary(blockRuntime, plan2);
3072
+ }
2056
3073
  if (plan2?.kind === "keyed-rows") {
2057
3074
  return keyedRowsBoundary(blockRuntime, plan2);
2058
3075
  }
@@ -2102,6 +3119,31 @@ function assignStableBindingTargets(bindings) {
2102
3119
  binding.target = target;
2103
3120
  }
2104
3121
  }
3122
+ function markShortCircuitBindings(bindings) {
3123
+ for (const binding of bindings) {
3124
+ if (binding.kind === "block" || binding.dependencies.length < 2) continue;
3125
+ let shortCircuits = false;
3126
+ traverse(expressionFile(types.cloneNode(binding.value, true)), {
3127
+ ConditionalExpression(path) {
3128
+ shortCircuits = true;
3129
+ path.stop();
3130
+ },
3131
+ LogicalExpression(path) {
3132
+ shortCircuits = true;
3133
+ path.stop();
3134
+ },
3135
+ OptionalCallExpression(path) {
3136
+ shortCircuits = true;
3137
+ path.stop();
3138
+ },
3139
+ OptionalMemberExpression(path) {
3140
+ shortCircuits = true;
3141
+ path.stop();
3142
+ }
3143
+ });
3144
+ if (shortCircuits) binding.tracking = "dynamic";
3145
+ }
3146
+ }
2105
3147
  function lowerStableBindingTargets(root, bindings, blockRuntime, ownedElements) {
2106
3148
  const targetByPath = /* @__PURE__ */ new Map();
2107
3149
  for (const binding of bindings) {
@@ -2371,6 +3413,9 @@ function bindingObject(binding, propsParameter, stateParameter, statesByValue, s
2371
3413
  types.objectProperty(types.identifier("target"), types.numericLiteral(binding.target))
2372
3414
  );
2373
3415
  }
3416
+ if (binding.tracking) {
3417
+ properties.push(types.objectProperty(types.identifier("tracking"), types.stringLiteral(binding.tracking)));
3418
+ }
2374
3419
  if (binding.name)
2375
3420
  properties.push(types.objectProperty(types.identifier("name"), types.stringLiteral(binding.name)));
2376
3421
  properties.push(
@@ -2389,7 +3434,7 @@ function wrapperElement(definitionIdentifier, props) {
2389
3434
  const attributes = props ? [types.jsxSpreadAttribute(types.cloneNode(props))] : [];
2390
3435
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
2391
3436
  }
2392
- function compileCandidate(candidate, createComponentIdentifier, useStateNames, reactNames, listNames, moduleId) {
3437
+ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureIdentifiers, usedRuntimeFeatures, useStateNames, reactNames, listNames, moduleId, reactivity) {
2393
3438
  const { path, name, statementPath } = candidate;
2394
3439
  if (path.node.async || path.node.generator)
2395
3440
  return "async and generator components are not supported";
@@ -2506,8 +3551,34 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2506
3551
  );
2507
3552
  const setterReason = validateSetterUsage(expandedRoot, statesBySetter);
2508
3553
  if (setterReason) return setterReason;
3554
+ const referencedPropNames = collectDestructuredPropNames(
3555
+ expandedRoot,
3556
+ propsPlan.destructuredNames,
3557
+ propsPlan.definitionParameter
3558
+ );
3559
+ let propBindings = referencedPropNames.has("children") ? [] : [...propsPlan.destructuredNames].filter((propName) => referencedPropNames.has(propName)).map(
3560
+ (localName, propIndex) => ({
3561
+ localName,
3562
+ valueName: path.scope.generateUidIdentifier(`farmProp${propIndex}`).name,
3563
+ index: states.length + propIndex
3564
+ })
3565
+ );
3566
+ const propBindingsByName = new Map(propBindings.map((binding) => [binding.localName, binding]));
3567
+ let reactiveByValue = new Map(statesByValue);
3568
+ for (const binding of propBindings) {
3569
+ reactiveByValue.set(binding.valueName, {
3570
+ valueName: binding.valueName,
3571
+ setterName: "",
3572
+ index: binding.index
3573
+ });
3574
+ }
3575
+ let expandedReactiveRoot = rewriteTrackedPropAccess(
3576
+ expandedRoot,
3577
+ propsPlan.definitionParameter,
3578
+ propBindingsByName
3579
+ );
2509
3580
  const referencedComponentNames = /* @__PURE__ */ new Set();
2510
- traverse(expressionFile(types.cloneNode(expandedRoot, true)), {
3581
+ traverse(expressionFile(types.cloneNode(expandedReactiveRoot, true)), {
2511
3582
  JSXOpeningElement(componentPath) {
2512
3583
  const componentName = componentPath.node.name;
2513
3584
  if (types.isJSXIdentifier(componentName) && isComponentName(componentName.name)) {
@@ -2524,32 +3595,55 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2524
3595
  );
2525
3596
  })
2526
3597
  );
2527
- const blockAnalysis = analyzeComposableBlocks(
2528
- expandedRoot,
2529
- statesByValue,
3598
+ let blockAnalysis = analyzeComposableBlocks(
3599
+ expandedReactiveRoot,
3600
+ reactiveByValue,
2530
3601
  safeGlobals,
2531
3602
  listNames,
2532
3603
  allowedComponentNames
2533
3604
  );
2534
- if (blockAnalysis.reason) return blockAnalysis.reason;
2535
- const blockPlans = blockAnalysis.plans || [];
2536
- const analysis = analyzeHostTree(
2537
- expandedRoot,
2538
- statesByValue,
3605
+ let analysis = analyzeHostTree(
3606
+ expandedReactiveRoot,
3607
+ reactiveByValue,
2539
3608
  safeGlobals,
2540
3609
  blockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
2541
3610
  blockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
2542
3611
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
2543
3612
  blockAnalysis.componentElements || /* @__PURE__ */ new Set()
2544
3613
  );
3614
+ if (propBindings.length > 0 && (blockAnalysis.reason || analysis.reason)) {
3615
+ propBindings = [];
3616
+ reactiveByValue = new Map(statesByValue);
3617
+ expandedReactiveRoot = types.cloneNode(expandedRoot, true);
3618
+ blockAnalysis = analyzeComposableBlocks(
3619
+ expandedReactiveRoot,
3620
+ reactiveByValue,
3621
+ safeGlobals,
3622
+ listNames,
3623
+ allowedComponentNames
3624
+ );
3625
+ analysis = analyzeHostTree(
3626
+ expandedReactiveRoot,
3627
+ reactiveByValue,
3628
+ safeGlobals,
3629
+ blockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
3630
+ blockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
3631
+ blockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
3632
+ blockAnalysis.componentElements || /* @__PURE__ */ new Set()
3633
+ );
3634
+ }
3635
+ if (blockAnalysis.reason) return blockAnalysis.reason;
2545
3636
  if (analysis.reason) return analysis.reason;
3637
+ const blockPlans = blockAnalysis.plans || [];
3638
+ const runtimeFeatures = runtimeFeaturesForPlans(blockPlans);
3639
+ markShortCircuitBindings(analysis.bindings || []);
2546
3640
  assignStableBindingTargets(analysis.bindings || []);
2547
3641
  const stateParameter = path.scope.generateUidIdentifier("farmState");
2548
3642
  const blockParameter = path.scope.generateUidIdentifier("farmBlocks");
2549
3643
  const definitionIdentifier = path.scope.generateUidIdentifier(`${name}Compiled`);
2550
3644
  const propsParameter = propsPlan.definitionParameter;
2551
3645
  const rootWithTargets = lowerStableBindingTargets(
2552
- expandedRoot,
3646
+ expandedReactiveRoot,
2553
3647
  analysis.bindings || [],
2554
3648
  blockParameter,
2555
3649
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set()
@@ -2563,12 +3657,13 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2563
3657
  const rewrittenRoot = rewriteStateAccess(
2564
3658
  rootWithBlocks,
2565
3659
  stateParameter,
2566
- statesByValue,
3660
+ reactiveByValue,
2567
3661
  statesBySetter
2568
3662
  );
2569
3663
  const definition = types.callExpression(types.cloneNode(createComponentIdentifier), [
2570
3664
  types.objectExpression([
2571
3665
  types.objectProperty(types.identifier("displayName"), types.stringLiteral(name)),
3666
+ types.objectProperty(types.identifier("reactivity"), types.stringLiteral(reactivity)),
2572
3667
  types.spreadElement(
2573
3668
  types.conditionalExpression(
2574
3669
  types.memberExpression(
@@ -2579,7 +3674,9 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2579
3674
  types.objectProperty(types.identifier("hmrId"), types.stringLiteral(`${moduleId}#${name}`)),
2580
3675
  types.objectProperty(
2581
3676
  types.identifier("stateSignature"),
2582
- types.stringLiteral(String(states.length))
3677
+ types.stringLiteral(
3678
+ propBindings.length === 0 ? String(states.length) : `${states.length}:${propBindings.map((binding) => binding.localName).join(",")}`
3679
+ )
2583
3680
  )
2584
3681
  ]),
2585
3682
  types.objectExpression([])
@@ -2602,6 +3699,22 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2602
3699
  )
2603
3700
  )
2604
3701
  ),
3702
+ ...propBindings.length > 0 ? [
3703
+ types.objectProperty(
3704
+ types.identifier("readProps"),
3705
+ types.arrowFunctionExpression(
3706
+ [types.cloneNode(propsParameter)],
3707
+ types.arrayExpression(
3708
+ propBindings.map(
3709
+ (binding) => types.memberExpression(
3710
+ types.cloneNode(propsParameter),
3711
+ types.identifier(binding.localName)
3712
+ )
3713
+ )
3714
+ )
3715
+ )
3716
+ )
3717
+ ] : [],
2605
3718
  types.objectProperty(
2606
3719
  types.identifier("render"),
2607
3720
  types.arrowFunctionExpression(
@@ -2621,11 +3734,14 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2621
3734
  dependencies: block.dependencies
2622
3735
  }))
2623
3736
  ].map(
2624
- (binding) => bindingObject(binding, propsParameter, stateParameter, statesByValue, statesBySetter)
3737
+ (binding) => bindingObject(binding, propsParameter, stateParameter, reactiveByValue, statesBySetter)
2625
3738
  )
2626
3739
  )
2627
3740
  )
2628
- ])
3741
+ ]),
3742
+ types.arrayExpression(
3743
+ runtimeFeatures.map((feature) => types.cloneNode(runtimeFeatureIdentifiers.get(feature)))
3744
+ )
2629
3745
  ]);
2630
3746
  path.get("body").replaceWith(
2631
3747
  types.blockStatement([
@@ -2635,6 +3751,7 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2635
3751
  statementPath.insertAfter(
2636
3752
  types.variableDeclaration("const", [types.variableDeclarator(definitionIdentifier, definition)])
2637
3753
  );
3754
+ for (const feature of runtimeFeatures) usedRuntimeFeatures.add(feature);
2638
3755
  return void 0;
2639
3756
  }
2640
3757
  function collectCandidates(programPath) {
@@ -2702,6 +3819,13 @@ async function compileReactModule(code, id, options) {
2702
3819
  }
2703
3820
  const candidates = collectCandidates(programPath);
2704
3821
  const createComponentIdentifier = programPath.scope.generateUidIdentifier("createCompiledComponent");
3822
+ const runtimeFeatureIdentifiers = new Map(
3823
+ Object.entries(COMPILER_RUNTIME_FEATURE_EXPORTS).map(([feature, exportName]) => [
3824
+ feature,
3825
+ programPath.scope.generateUidIdentifier(exportName)
3826
+ ])
3827
+ );
3828
+ const usedRuntimeFeatures = /* @__PURE__ */ new Set();
2705
3829
  for (const candidate of candidates) {
2706
3830
  const functionDirectives = new Set(
2707
3831
  types.isBlockStatement(candidate.path.node.body) ? directiveValues(candidate.path.node.body) : []
@@ -2712,10 +3836,13 @@ async function compileReactModule(code, id, options) {
2712
3836
  const reason = compileCandidate(
2713
3837
  candidate,
2714
3838
  createComponentIdentifier,
3839
+ runtimeFeatureIdentifiers,
3840
+ usedRuntimeFeatures,
2715
3841
  useStateNames,
2716
3842
  reactNames,
2717
3843
  listNames,
2718
- id
3844
+ id,
3845
+ options.reactivity
2719
3846
  );
2720
3847
  if (reason) {
2721
3848
  diagnostics.push({
@@ -2734,7 +3861,13 @@ async function compileReactModule(code, id, options) {
2734
3861
  [
2735
3862
  types.importSpecifier(
2736
3863
  createComponentIdentifier,
2737
- types.identifier("createCompiledComponent")
3864
+ types.identifier("createCompiledComponentWithFeatures")
3865
+ ),
3866
+ ...[...usedRuntimeFeatures].sort().map(
3867
+ (feature) => types.importSpecifier(
3868
+ types.cloneNode(runtimeFeatureIdentifiers.get(feature)),
3869
+ types.identifier(COMPILER_RUNTIME_FEATURE_EXPORTS[feature])
3870
+ )
2738
3871
  )
2739
3872
  ],
2740
3873
  types.stringLiteral("@farm.js/react/compiler-runtime")
@@ -2842,6 +3975,10 @@ function normalizeReactCompilerOptions(value) {
2842
3975
  if (!["fallback", "warn", "error"].includes(onUnsupported)) {
2843
3976
  throw new TypeError(`Unknown unsupported-component behavior: ${String(onUnsupported)}`);
2844
3977
  }
3978
+ const reactivity = options.reactivity || "hybrid";
3979
+ if (reactivity !== "static" && reactivity !== "hybrid") {
3980
+ throw new TypeError(`Unknown React compiler reactivity mode: ${String(reactivity)}`);
3981
+ }
2845
3982
  if (options.report !== void 0 && typeof options.report !== "boolean") {
2846
3983
  throw new TypeError("The React compiler report option must be a boolean.");
2847
3984
  }
@@ -2852,7 +3989,7 @@ function normalizeReactCompilerOptions(value) {
2852
3989
  }
2853
3990
  const reportFile = normalizeCompilerReportFile(options.reportFile);
2854
3991
  const report = options.report === true || options.reportFile !== void 0;
2855
- return { mode, directive, onUnsupported, report, reportFile };
3992
+ return { mode, directive, onUnsupported, reactivity, report, reportFile };
2856
3993
  }
2857
3994
 
2858
3995
  // src/vite.ts
@@ -2868,7 +4005,7 @@ function readCompilerOptions(rendererOptions) {
2868
4005
  );
2869
4006
  }
2870
4007
  const normalized = compiler;
2871
- if ((normalized.mode === "infer" || normalized.mode === "annotation") && typeof normalized.directive === "string" && (normalized.onUnsupported === "fallback" || normalized.onUnsupported === "warn" || normalized.onUnsupported === "error")) {
4008
+ if ((normalized.mode === "infer" || normalized.mode === "annotation") && typeof normalized.directive === "string" && (normalized.reactivity === "static" || normalized.reactivity === "hybrid") && (normalized.onUnsupported === "fallback" || normalized.onUnsupported === "warn" || normalized.onUnsupported === "error")) {
2872
4009
  return normalized;
2873
4010
  }
2874
4011
  return normalizeReactCompilerOptions(compiler);