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

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,62 @@ 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
+ }
7
63
  var SAFE_GLOBAL_CALLS = /* @__PURE__ */ new Set(["Boolean", "Number", "String"]);
8
64
  var SAFE_MATH_CALLS = /* @__PURE__ */ new Set(["abs", "ceil", "floor", "max", "min", "round", "sign", "trunc"]);
9
65
  function isComponentName(name) {
@@ -88,6 +144,37 @@ function rewriteDestructuredPropAccess(expression, names, propsParameter) {
88
144
  });
89
145
  return file.program.body[0].expression;
90
146
  }
147
+ function collectDestructuredPropNames(expression, names, propsParameter) {
148
+ const referenced = /* @__PURE__ */ new Set();
149
+ if (names.size === 0) return referenced;
150
+ traverse(expressionFile(cloneExpression(expression)), {
151
+ MemberExpression(path) {
152
+ const { object, property, computed } = path.node;
153
+ if (computed || !types.isIdentifier(object, { name: propsParameter.name }) || !types.isIdentifier(property) || !names.has(property.name)) {
154
+ return;
155
+ }
156
+ referenced.add(property.name);
157
+ }
158
+ });
159
+ return referenced;
160
+ }
161
+ function rewriteTrackedPropAccess(expression, propsParameter, bindingsByName) {
162
+ if (bindingsByName.size === 0) return cloneExpression(expression);
163
+ const file = expressionFile(cloneExpression(expression));
164
+ traverse(file, {
165
+ MemberExpression(path) {
166
+ const { object, property, computed } = path.node;
167
+ if (computed || !types.isIdentifier(object, { name: propsParameter.name }) || !types.isIdentifier(property)) {
168
+ return;
169
+ }
170
+ const binding = bindingsByName.get(property.name);
171
+ if (!binding) return;
172
+ path.replaceWith(types.identifier(binding.valueName));
173
+ path.skip();
174
+ }
175
+ });
176
+ return file.program.body[0].expression;
177
+ }
91
178
  function rewriteHandlerAccess(root, handlersByName) {
92
179
  if (handlersByName.size === 0) return { root: types.cloneNode(root, true) };
93
180
  const file = expressionFile(types.cloneNode(root, true));
@@ -767,7 +854,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
767
854
  const reason = visit(row, []);
768
855
  return reason ? { reason } : { bindings, events, conditionals };
769
856
  }
770
- function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames) {
857
+ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false) {
771
858
  let collection;
772
859
  let keyCallback;
773
860
  let renderCallback;
@@ -822,17 +909,29 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames) {
822
909
  safeGlobals,
823
910
  new Set([...statesByValue.values()].map((state) => state.setterName))
824
911
  );
825
- if (rowAnalysis.reason) return void 0;
912
+ if (rowAnalysis.reason && !acceptUnsupportedRow) return void 0;
913
+ const keyExpression = returnedExpression(keyCallback);
914
+ const structureDependencies = new Set(collectStateDependencies(collection, statesByValue));
915
+ if (keyExpression) {
916
+ for (const dependency of collectStateDependencies(keyExpression, statesByValue)) {
917
+ structureDependencies.add(dependency);
918
+ }
919
+ }
826
920
  return {
827
921
  source,
828
922
  collection,
923
+ structureDependencies: [...structureDependencies].sort((left, right) => left - right),
829
924
  keyCallback,
830
925
  renderCallback,
831
926
  row,
832
927
  dependencies,
833
- bindings: rowAnalysis.bindings || [],
928
+ bindings: (rowAnalysis.bindings || []).map((binding) => ({
929
+ ...binding,
930
+ dependencies: collectStateDependencies(binding.value, statesByValue)
931
+ })),
834
932
  events: rowAnalysis.events || [],
835
933
  conditionals: rowAnalysis.conditionals || [],
934
+ rowReason: rowAnalysis.reason,
836
935
  syntax
837
936
  };
838
937
  }
@@ -849,22 +948,7 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
849
948
  }
850
949
  const children = meaningfulJsxChildren(container);
851
950
  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;
951
+ return analyzeKeyedRowChild(children[0], statesByValue, safeGlobals, listNames, true);
868
952
  }
869
953
  function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
870
954
  if (container.openingElement.attributes.some(
@@ -876,6 +960,7 @@ function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, list
876
960
  if (children.length < (allowSingleRange ? 1 : 2)) return void 0;
877
961
  const ranges = [];
878
962
  const dependencies = /* @__PURE__ */ new Set();
963
+ const staticBindings = [];
879
964
  let staticBefore = 0;
880
965
  for (const child of children) {
881
966
  const range = analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames);
@@ -896,18 +981,31 @@ function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, list
896
981
  continue;
897
982
  }
898
983
  if (!types.isJSXElement(child) || !isHostElement(child)) return void 0;
899
- if (!isStaticRangeSibling(child, statesByValue, safeGlobals)) return void 0;
984
+ const analysis = analyzeHostConditionalTree(child, safeGlobals, true, statesByValue);
985
+ if (analysis.reason) return void 0;
986
+ for (const binding of analysis.bindings || []) {
987
+ staticBindings.push({
988
+ ...binding,
989
+ segment: ranges.length,
990
+ sibling: staticBefore
991
+ });
992
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
993
+ dependencies.add(dependency);
994
+ }
995
+ }
900
996
  staticBefore += 1;
901
997
  }
902
998
  if (ranges.length === 0) return void 0;
903
999
  return {
904
1000
  ranges,
905
1001
  trailing: staticBefore,
906
- dependencies: [...dependencies].sort((left, right) => left - right)
1002
+ dependencies: [...dependencies].sort((left, right) => left - right),
1003
+ staticBindings
907
1004
  };
908
1005
  }
909
- function analyzeHostConditionalTree(branch, safeGlobals) {
1006
+ function analyzeHostConditionalTree(branch, safeGlobals, allowStaticEvents = false, statefulOnly) {
910
1007
  const bindings = [];
1008
+ const needsStaticBinding = (expression) => !statefulOnly || collectStateDependencies(expression, statefulOnly).length > 0;
911
1009
  const visit = (element, path) => {
912
1010
  const tag = element.openingElement.name;
913
1011
  if (!types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
@@ -916,20 +1014,26 @@ function analyzeHostConditionalTree(branch, safeGlobals) {
916
1014
  if (tag.name === "svg") return "compiler-owned conditionals do not support SVG yet";
917
1015
  for (const attribute of element.openingElement.attributes) {
918
1016
  if (types.isJSXSpreadAttribute(attribute)) {
1017
+ if (statefulOnly && !needsStaticBinding(attribute.argument)) continue;
919
1018
  return "compiler-owned conditionals do not support JSX attribute spreads";
920
1019
  }
921
1020
  const name = jsxAttributeName(attribute);
922
1021
  if (!name) return "compiler-owned conditionals do not support namespaced JSX attributes";
923
1022
  if (name === "ref" || name === "dangerouslySetInnerHTML" || name === "key") {
1023
+ if (statefulOnly && (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression) || !needsStaticBinding(attribute.value.expression))) {
1024
+ continue;
1025
+ }
924
1026
  return `compiler-owned conditional ${name} requires React ownership`;
925
1027
  }
926
1028
  if (/^on[A-Z]/.test(name)) {
1029
+ if (allowStaticEvents) continue;
927
1030
  return "compiler-owned conditional events require React ownership";
928
1031
  }
929
1032
  if (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression)) {
930
1033
  continue;
931
1034
  }
932
1035
  const expression = attribute.value.expression;
1036
+ if (!needsStaticBinding(expression)) continue;
933
1037
  if (name === "style") {
934
1038
  if (!types.isObjectExpression(expression)) {
935
1039
  return "compiler-owned conditional styles must use one inline object literal";
@@ -942,6 +1046,7 @@ function analyzeHostConditionalTree(branch, safeGlobals) {
942
1046
  if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
943
1047
  return "compiler-owned conditional style names must use camelCase or a CSS custom property";
944
1048
  }
1049
+ if (!needsStaticBinding(property.value)) continue;
945
1050
  const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
946
1051
  if (unsupported2) {
947
1052
  return `compiler-owned conditional style ${propertyName} cannot use ${unsupported2}`;
@@ -976,14 +1081,20 @@ function analyzeHostConditionalTree(branch, safeGlobals) {
976
1081
  const expressions = element.children.filter(
977
1082
  (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)
978
1083
  );
979
- for (const child of expressions) {
980
- const unsupported = validateDerivedExpression(child.expression, safeGlobals);
981
- if (unsupported) return `compiler-owned conditional text cannot use ${unsupported}`;
1084
+ const boundExpressions = statefulOnly ? expressions.filter((child) => needsStaticBinding(child.expression)) : expressions;
1085
+ if (boundExpressions.length > 0) {
1086
+ for (const child of expressions) {
1087
+ const unsupported = validateDerivedExpression(
1088
+ child.expression,
1089
+ safeGlobals
1090
+ );
1091
+ if (unsupported) return `compiler-owned conditional text cannot use ${unsupported}`;
1092
+ }
982
1093
  }
983
- if (nestedElements.length > 0 && expressions.length > 0) {
1094
+ if (nestedElements.length > 0 && boundExpressions.length > 0) {
984
1095
  return "compiler-owned conditionals do not support dynamic text beside nested elements yet";
985
1096
  }
986
- if (nestedElements.length === 0 && expressions.length > 0) {
1097
+ if (nestedElements.length === 0 && boundExpressions.length > 0) {
987
1098
  const parts = [];
988
1099
  for (const child of element.children) {
989
1100
  if (types.isJSXText(child)) {
@@ -1007,7 +1118,369 @@ function analyzeHostConditionalTree(branch, safeGlobals) {
1007
1118
  const reason = visit(branch, []);
1008
1119
  return reason ? { reason } : { bindings };
1009
1120
  }
1010
- function analyzeHostConditionalContainer(container, statesByValue, safeGlobals) {
1121
+ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames, parent, allocateId) {
1122
+ const bindings = [];
1123
+ const plans = [];
1124
+ const blocks = /* @__PURE__ */ new Map();
1125
+ const visit = (element, path, owner) => {
1126
+ const tag = element.openingElement.name;
1127
+ if (!types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
1128
+ return "recursive compiler-owned blocks support host elements only";
1129
+ }
1130
+ if (tag.name === "svg") return "recursive compiler-owned blocks do not support SVG yet";
1131
+ for (const attribute of element.openingElement.attributes) {
1132
+ if (types.isJSXSpreadAttribute(attribute)) {
1133
+ return "recursive compiler-owned blocks do not support JSX attribute spreads";
1134
+ }
1135
+ const name = jsxAttributeName(attribute);
1136
+ if (!name) return "recursive compiler-owned blocks do not support namespaced attributes";
1137
+ if (name === "ref" || name === "dangerouslySetInnerHTML" || name === "key") {
1138
+ return `recursive compiler-owned block ${name} requires React ownership`;
1139
+ }
1140
+ if (/^on[A-Z]/.test(name)) {
1141
+ return "recursive compiler-owned block events require React ownership";
1142
+ }
1143
+ if (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression)) {
1144
+ continue;
1145
+ }
1146
+ const expression = attribute.value.expression;
1147
+ if (name === "style") {
1148
+ if (!types.isObjectExpression(expression)) {
1149
+ return "recursive compiler-owned styles must use one inline object literal";
1150
+ }
1151
+ for (const property of expression.properties) {
1152
+ if (!types.isObjectProperty(property) || property.computed || !types.isExpression(property.value)) {
1153
+ return "recursive compiler-owned styles do not support spreads, methods, or computed properties";
1154
+ }
1155
+ const propertyName = types.isIdentifier(property.key) ? property.key.name : types.isStringLiteral(property.key) ? property.key.value : void 0;
1156
+ if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
1157
+ return "recursive compiler-owned style names must use camelCase or a CSS custom property";
1158
+ }
1159
+ const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
1160
+ if (unsupported2) {
1161
+ return `recursive compiler-owned style ${propertyName} cannot use ${unsupported2}`;
1162
+ }
1163
+ bindings.push({
1164
+ kind: "style",
1165
+ path: [...path],
1166
+ name: propertyName,
1167
+ value: cloneExpression(property.value)
1168
+ });
1169
+ }
1170
+ continue;
1171
+ }
1172
+ if (name === "children") {
1173
+ return "recursive compiler-owned children props are not supported yet";
1174
+ }
1175
+ const unsupported = validateDerivedExpression(expression, safeGlobals);
1176
+ if (unsupported) return `recursive compiler-owned ${name} cannot use ${unsupported}`;
1177
+ bindings.push({
1178
+ kind: "attribute",
1179
+ path: [...path],
1180
+ name,
1181
+ value: cloneExpression(expression)
1182
+ });
1183
+ }
1184
+ const children = meaningfulJsxChildren(element);
1185
+ const hasConditionalRange = children.some(
1186
+ (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression) && conditionalBlockShape(child.expression) !== null
1187
+ );
1188
+ const hasKeyedRange = children.some(
1189
+ (child) => analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, true) !== void 0
1190
+ );
1191
+ if (hasConditionalRange && hasKeyedRange) {
1192
+ const id = allocateId();
1193
+ const mixed = analyzeMixedRangesContainer(
1194
+ element,
1195
+ statesByValue,
1196
+ safeGlobals,
1197
+ listNames,
1198
+ id,
1199
+ allocateId
1200
+ );
1201
+ if (mixed) {
1202
+ plans.push(...mixed.nestedPlans);
1203
+ for (const [nestedElement, plan2] of mixed.descriptorBlocks) {
1204
+ blocks.set(nestedElement, plan2);
1205
+ }
1206
+ const plan = {
1207
+ kind: "nested-host-mixed-ranges",
1208
+ id,
1209
+ parent: owner,
1210
+ dependencies: mixed.dependencies,
1211
+ source: element,
1212
+ ranges: mixed.ranges,
1213
+ trailing: mixed.trailing,
1214
+ staticBindings: mixed.staticBindings
1215
+ };
1216
+ plans.push(plan);
1217
+ blocks.set(element, plan);
1218
+ return void 0;
1219
+ }
1220
+ }
1221
+ const conditionalChildren = /* @__PURE__ */ new Map();
1222
+ let conditionalContainer = children.length > 0;
1223
+ for (const child of children) {
1224
+ if (types.isJSXElement(child) && isHostElement(child)) continue;
1225
+ if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1226
+ const shape = conditionalBlockShape(child.expression);
1227
+ if (shape && !validateDerivedExpression(shape.test, safeGlobals)) {
1228
+ conditionalChildren.set(child.expression, shape);
1229
+ continue;
1230
+ }
1231
+ }
1232
+ conditionalContainer = false;
1233
+ break;
1234
+ }
1235
+ if (conditionalContainer && conditionalChildren.size > 0) {
1236
+ const id = allocateId();
1237
+ const ranges = [];
1238
+ const staticBindings = [];
1239
+ const dependencies = /* @__PURE__ */ new Set();
1240
+ let staticBefore2 = 0;
1241
+ for (const child of children) {
1242
+ if (types.isJSXElement(child)) {
1243
+ const nested = analyzeRecursiveHostTree(
1244
+ child,
1245
+ statesByValue,
1246
+ safeGlobals,
1247
+ listNames,
1248
+ owner,
1249
+ allocateId
1250
+ );
1251
+ if (nested.reason) return nested.reason;
1252
+ plans.push(...nested.plans);
1253
+ for (const [nestedElement, plan2] of nested.blocks) blocks.set(nestedElement, plan2);
1254
+ for (const binding of nested.bindings) {
1255
+ staticBindings.push({
1256
+ ...binding,
1257
+ segment: ranges.length,
1258
+ sibling: staticBefore2
1259
+ });
1260
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1261
+ dependencies.add(dependency);
1262
+ }
1263
+ }
1264
+ staticBefore2 += 1;
1265
+ continue;
1266
+ }
1267
+ if (!types.isJSXExpressionContainer(child) || types.isJSXEmptyExpression(child.expression)) {
1268
+ return "recursive conditional ranges contain an unsupported child";
1269
+ }
1270
+ const expression = child.expression;
1271
+ const shape = conditionalChildren.get(expression);
1272
+ const truthy = shape.truthy ? analyzeRecursiveHostTree(
1273
+ shape.truthy,
1274
+ statesByValue,
1275
+ safeGlobals,
1276
+ listNames,
1277
+ id,
1278
+ allocateId
1279
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1280
+ const falsy = shape.falsy ? analyzeRecursiveHostTree(
1281
+ shape.falsy,
1282
+ statesByValue,
1283
+ safeGlobals,
1284
+ listNames,
1285
+ id,
1286
+ allocateId
1287
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1288
+ if (truthy.reason) return truthy.reason;
1289
+ if (falsy.reason) return falsy.reason;
1290
+ plans.push(...truthy.plans, ...falsy.plans);
1291
+ for (const [nestedElement, plan2] of truthy.blocks) blocks.set(nestedElement, plan2);
1292
+ for (const [nestedElement, plan2] of falsy.blocks) blocks.set(nestedElement, plan2);
1293
+ for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1294
+ dependencies.add(dependency);
1295
+ }
1296
+ for (const binding of [...truthy.bindings, ...falsy.bindings]) {
1297
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1298
+ dependencies.add(dependency);
1299
+ }
1300
+ }
1301
+ ranges.push({
1302
+ before: staticBefore2,
1303
+ source: expression,
1304
+ test: cloneExpression(shape.test),
1305
+ logical: shape.logical,
1306
+ truthy: shape.truthy,
1307
+ falsy: shape.falsy,
1308
+ truthyBindings: truthy.bindings,
1309
+ falsyBindings: falsy.bindings
1310
+ });
1311
+ staticBefore2 = 0;
1312
+ }
1313
+ const plan = {
1314
+ kind: "nested-host-conditional-ranges",
1315
+ id,
1316
+ parent: owner,
1317
+ dependencies: [...dependencies].sort((left, right) => left - right),
1318
+ source: element,
1319
+ ranges,
1320
+ trailing: staticBefore2,
1321
+ staticBindings
1322
+ };
1323
+ plans.push(plan);
1324
+ blocks.set(element, plan);
1325
+ return void 0;
1326
+ }
1327
+ const keyedRows = [];
1328
+ let keyedContainer = children.length > 0;
1329
+ let staticBefore = 0;
1330
+ const staticChildren = [];
1331
+ for (const child of children) {
1332
+ const range = analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, true);
1333
+ if (range) {
1334
+ keyedRows.push({ before: staticBefore, range });
1335
+ staticBefore = 0;
1336
+ } else if (types.isJSXElement(child) && isHostElement(child)) {
1337
+ staticChildren.push({
1338
+ element: child,
1339
+ segment: keyedRows.length,
1340
+ sibling: staticBefore
1341
+ });
1342
+ staticBefore += 1;
1343
+ } else {
1344
+ keyedContainer = false;
1345
+ break;
1346
+ }
1347
+ }
1348
+ if (keyedContainer && keyedRows.length > 0) {
1349
+ const id = allocateId();
1350
+ const keyedRanges = [];
1351
+ const staticBindings = [];
1352
+ const keyedDependencies = /* @__PURE__ */ new Set();
1353
+ for (const { before, range } of keyedRows) {
1354
+ if (range.events.length > 0) {
1355
+ return "recursive compiler-owned keyed rows cannot contain events";
1356
+ }
1357
+ const row = types.cloneNode(range.row, true);
1358
+ row.openingElement.attributes = row.openingElement.attributes.filter(
1359
+ (attribute) => !types.isJSXAttribute(attribute) || jsxAttributeName(attribute) !== "key"
1360
+ );
1361
+ const rowAnalysis = analyzeRecursiveHostTree(
1362
+ row,
1363
+ statesByValue,
1364
+ safeGlobals,
1365
+ listNames,
1366
+ id,
1367
+ allocateId
1368
+ );
1369
+ if (rowAnalysis.reason) return rowAnalysis.reason;
1370
+ plans.push(...rowAnalysis.plans);
1371
+ for (const [nestedElement, nestedPlan] of rowAnalysis.blocks) {
1372
+ blocks.set(nestedElement, nestedPlan);
1373
+ }
1374
+ keyedRanges.push({
1375
+ before,
1376
+ source: range.source,
1377
+ collection: range.collection,
1378
+ keyCallback: range.keyCallback,
1379
+ renderCallback: range.renderCallback,
1380
+ row,
1381
+ bindings: rowAnalysis.bindings,
1382
+ syntax: range.syntax
1383
+ });
1384
+ for (const dependency of range.dependencies) keyedDependencies.add(dependency);
1385
+ }
1386
+ for (const { element: child, segment, sibling } of staticChildren) {
1387
+ const nested = analyzeRecursiveHostTree(
1388
+ child,
1389
+ statesByValue,
1390
+ safeGlobals,
1391
+ listNames,
1392
+ owner,
1393
+ allocateId
1394
+ );
1395
+ if (nested.reason) return nested.reason;
1396
+ plans.push(...nested.plans);
1397
+ for (const [nestedElement, plan2] of nested.blocks) blocks.set(nestedElement, plan2);
1398
+ for (const binding of nested.bindings) {
1399
+ staticBindings.push({ ...binding, segment, sibling });
1400
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1401
+ keyedDependencies.add(dependency);
1402
+ }
1403
+ }
1404
+ }
1405
+ const plan = {
1406
+ kind: "nested-host-keyed-ranges",
1407
+ id,
1408
+ parent: owner,
1409
+ dependencies: [...keyedDependencies].sort((left, right) => left - right),
1410
+ source: element,
1411
+ ranges: keyedRanges,
1412
+ trailing: staticBefore,
1413
+ staticBindings
1414
+ };
1415
+ plans.push(plan);
1416
+ blocks.set(element, plan);
1417
+ return void 0;
1418
+ }
1419
+ if (element.children.some((child) => types.isJSXFragment(child))) {
1420
+ return "recursive compiler-owned blocks do not support fragments yet";
1421
+ }
1422
+ const nestedElements = element.children.filter(
1423
+ (child) => types.isJSXElement(child)
1424
+ );
1425
+ const expressions = element.children.filter(
1426
+ (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)
1427
+ );
1428
+ for (const child of expressions) {
1429
+ const unsupported = validateDerivedExpression(child.expression, safeGlobals);
1430
+ if (unsupported) return `recursive compiler-owned text cannot use ${unsupported}`;
1431
+ }
1432
+ if (nestedElements.length > 0 && expressions.length > 0) {
1433
+ return "recursive compiler-owned blocks do not support dynamic text beside nested elements";
1434
+ }
1435
+ if (nestedElements.length === 0 && expressions.length > 0) {
1436
+ const parts = [];
1437
+ for (const child of element.children) {
1438
+ if (types.isJSXText(child)) {
1439
+ const text = cleanJsxText(child.value);
1440
+ if (text) parts.push(types.stringLiteral(text));
1441
+ } else if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1442
+ parts.push(cloneExpression(child.expression));
1443
+ }
1444
+ }
1445
+ bindings.push({ kind: "text", path: [...path], value: types.arrayExpression(parts) });
1446
+ }
1447
+ let elementIndex = 0;
1448
+ for (const child of element.children) {
1449
+ if (!types.isJSXElement(child)) continue;
1450
+ const reason2 = visit(child, [...path, elementIndex], owner);
1451
+ if (reason2) return reason2;
1452
+ elementIndex += 1;
1453
+ }
1454
+ return void 0;
1455
+ };
1456
+ const reason = visit(branch, [], parent);
1457
+ return { bindings, plans, blocks, reason };
1458
+ }
1459
+ function analyzeCompilerOwnedKeyedRowHostBlocks(shape, statesByValue, safeGlobals, listNames, parent, allocateId) {
1460
+ if (shape.events.length > 0) return void 0;
1461
+ const row = types.cloneNode(shape.row, true);
1462
+ row.openingElement.attributes = row.openingElement.attributes.filter(
1463
+ (attribute) => !types.isJSXAttribute(attribute) || jsxAttributeName(attribute) !== "key"
1464
+ );
1465
+ const analysis = analyzeRecursiveHostTree(
1466
+ row,
1467
+ statesByValue,
1468
+ safeGlobals,
1469
+ listNames,
1470
+ parent,
1471
+ allocateId
1472
+ );
1473
+ if (analysis.reason || analysis.plans.length === 0) {
1474
+ return void 0;
1475
+ }
1476
+ return {
1477
+ row,
1478
+ bindings: analysis.bindings,
1479
+ descriptorBlocks: analysis.blocks,
1480
+ nestedPlans: analysis.plans
1481
+ };
1482
+ }
1483
+ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals, listNames, parent, allocateId) {
1011
1484
  const containerName = container.openingElement.name;
1012
1485
  if (!types.isJSXIdentifier(containerName) || !/^[a-z]/.test(containerName.name) || containerName.name === "svg") {
1013
1486
  return void 0;
@@ -1029,8 +1502,22 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals)
1029
1502
  }
1030
1503
  const shape = conditionalBlockShape(children[0].expression);
1031
1504
  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: [] };
1505
+ const truthyAnalysis = shape.truthy ? analyzeRecursiveHostTree(
1506
+ shape.truthy,
1507
+ statesByValue,
1508
+ safeGlobals,
1509
+ listNames,
1510
+ parent,
1511
+ allocateId
1512
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1513
+ const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1514
+ shape.falsy,
1515
+ statesByValue,
1516
+ safeGlobals,
1517
+ listNames,
1518
+ parent,
1519
+ allocateId
1520
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1034
1521
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1035
1522
  const dependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1036
1523
  for (const binding of [...truthyAnalysis.bindings || [], ...falsyAnalysis.bindings || []]) {
@@ -1045,10 +1532,12 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals)
1045
1532
  truthy: shape.truthy,
1046
1533
  falsy: shape.falsy,
1047
1534
  truthyBindings: truthyAnalysis.bindings || [],
1048
- falsyBindings: falsyAnalysis.bindings || []
1535
+ falsyBindings: falsyAnalysis.bindings || [],
1536
+ descriptorBlocks: new Map([...truthyAnalysis.blocks, ...falsyAnalysis.blocks]),
1537
+ nestedPlans: [...truthyAnalysis.plans, ...falsyAnalysis.plans]
1049
1538
  };
1050
1539
  }
1051
- function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals, allowSingleRange = false) {
1540
+ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals, allowSingleRange, listNames, parent, allocateId) {
1052
1541
  const containerName = container.openingElement.name;
1053
1542
  if (!types.isJSXIdentifier(containerName) || !/^[a-z]/.test(containerName.name) || containerName.name === "svg" || container.openingElement.attributes.some(
1054
1543
  (attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
@@ -1059,14 +1548,34 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1059
1548
  if (children.length < (allowSingleRange ? 1 : 2)) return void 0;
1060
1549
  const ranges = [];
1061
1550
  const dependencies = /* @__PURE__ */ new Set();
1551
+ const descriptorBlocks = /* @__PURE__ */ new Map();
1552
+ const nestedPlans = [];
1553
+ const staticBindings = [];
1062
1554
  let staticBefore = 0;
1063
1555
  for (const child of children) {
1064
1556
  if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1065
1557
  const shape = conditionalBlockShape(child.expression);
1066
1558
  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: [] };
1559
+ const truthyAnalysis = shape.truthy ? analyzeRecursiveHostTree(
1560
+ shape.truthy,
1561
+ statesByValue,
1562
+ safeGlobals,
1563
+ listNames,
1564
+ parent,
1565
+ allocateId
1566
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1567
+ const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1568
+ shape.falsy,
1569
+ statesByValue,
1570
+ safeGlobals,
1571
+ listNames,
1572
+ parent,
1573
+ allocateId
1574
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1069
1575
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1576
+ nestedPlans.push(...truthyAnalysis.plans, ...falsyAnalysis.plans);
1577
+ for (const [element, plan] of truthyAnalysis.blocks) descriptorBlocks.set(element, plan);
1578
+ for (const [element, plan] of falsyAnalysis.blocks) descriptorBlocks.set(element, plan);
1070
1579
  const rangeDependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1071
1580
  for (const binding of [
1072
1581
  ...truthyAnalysis.bindings || [],
@@ -1092,14 +1601,158 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1092
1601
  }
1093
1602
  }
1094
1603
  if (!types.isJSXElement(child) || !isHostElement(child)) return void 0;
1095
- if (!isStaticRangeSibling(child, statesByValue, safeGlobals)) return void 0;
1604
+ const staticAnalysis = analyzeHostConditionalTree(child, safeGlobals, true, statesByValue);
1605
+ if (staticAnalysis.reason) return void 0;
1606
+ for (const binding of staticAnalysis.bindings || []) {
1607
+ staticBindings.push({
1608
+ ...binding,
1609
+ segment: ranges.length,
1610
+ sibling: staticBefore
1611
+ });
1612
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1613
+ dependencies.add(dependency);
1614
+ }
1615
+ }
1096
1616
  staticBefore += 1;
1097
1617
  }
1098
1618
  if (ranges.length === 0) return void 0;
1099
1619
  return {
1100
1620
  ranges,
1101
1621
  trailing: staticBefore,
1102
- dependencies: [...dependencies].sort((left, right) => left - right)
1622
+ dependencies: [...dependencies].sort((left, right) => left - right),
1623
+ staticBindings,
1624
+ descriptorBlocks,
1625
+ nestedPlans
1626
+ };
1627
+ }
1628
+ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, listNames, parent, allocateId) {
1629
+ const containerName = container.openingElement.name;
1630
+ if (!types.isJSXIdentifier(containerName) || !/^[a-z]/.test(containerName.name) || containerName.name === "svg" || container.openingElement.attributes.some(
1631
+ (attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
1632
+ )) {
1633
+ return void 0;
1634
+ }
1635
+ const children = meaningfulJsxChildren(container);
1636
+ if (children.length < 2) return void 0;
1637
+ const ranges = [];
1638
+ const dependencies = /* @__PURE__ */ new Set();
1639
+ const descriptorBlocks = /* @__PURE__ */ new Map();
1640
+ const nestedPlans = [];
1641
+ const staticBindings = [];
1642
+ let conditionalCount = 0;
1643
+ let keyedCount = 0;
1644
+ let staticBefore = 0;
1645
+ const mergeAnalysis = (analysis) => {
1646
+ if (analysis.reason) return false;
1647
+ nestedPlans.push(...analysis.plans);
1648
+ for (const [element, plan] of analysis.blocks) descriptorBlocks.set(element, plan);
1649
+ for (const binding of analysis.bindings) {
1650
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1651
+ dependencies.add(dependency);
1652
+ }
1653
+ }
1654
+ return true;
1655
+ };
1656
+ for (const child of children) {
1657
+ if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1658
+ const shape = conditionalBlockShape(child.expression);
1659
+ if (shape && !validateDerivedExpression(shape.test, safeGlobals)) {
1660
+ const truthyAnalysis = shape.truthy ? analyzeRecursiveHostTree(
1661
+ shape.truthy,
1662
+ statesByValue,
1663
+ safeGlobals,
1664
+ listNames,
1665
+ parent,
1666
+ allocateId
1667
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1668
+ const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1669
+ shape.falsy,
1670
+ statesByValue,
1671
+ safeGlobals,
1672
+ listNames,
1673
+ parent,
1674
+ allocateId
1675
+ ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
1676
+ if (!mergeAnalysis(truthyAnalysis) || !mergeAnalysis(falsyAnalysis)) return void 0;
1677
+ for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1678
+ dependencies.add(dependency);
1679
+ }
1680
+ ranges.push({
1681
+ rangeKind: "conditional",
1682
+ before: staticBefore,
1683
+ source: child.expression,
1684
+ test: cloneExpression(shape.test),
1685
+ logical: shape.logical,
1686
+ truthy: shape.truthy,
1687
+ falsy: shape.falsy,
1688
+ truthyBindings: truthyAnalysis.bindings,
1689
+ falsyBindings: falsyAnalysis.bindings
1690
+ });
1691
+ conditionalCount += 1;
1692
+ staticBefore = 0;
1693
+ continue;
1694
+ }
1695
+ }
1696
+ const keyed = analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, true);
1697
+ if (keyed) {
1698
+ if (keyed.events.length > 0) return void 0;
1699
+ const row = types.cloneNode(keyed.row, true);
1700
+ row.openingElement.attributes = row.openingElement.attributes.filter(
1701
+ (attribute) => !types.isJSXAttribute(attribute) || jsxAttributeName(attribute) !== "key"
1702
+ );
1703
+ const rowAnalysis = analyzeRecursiveHostTree(
1704
+ row,
1705
+ statesByValue,
1706
+ safeGlobals,
1707
+ listNames,
1708
+ parent,
1709
+ allocateId
1710
+ );
1711
+ if (!mergeAnalysis(rowAnalysis)) return void 0;
1712
+ for (const dependency of keyed.dependencies) dependencies.add(dependency);
1713
+ ranges.push({
1714
+ rangeKind: "keyed",
1715
+ before: staticBefore,
1716
+ source: keyed.source,
1717
+ collection: keyed.collection,
1718
+ keyCallback: keyed.keyCallback,
1719
+ renderCallback: keyed.renderCallback,
1720
+ row,
1721
+ bindings: rowAnalysis.bindings,
1722
+ syntax: keyed.syntax
1723
+ });
1724
+ keyedCount += 1;
1725
+ staticBefore = 0;
1726
+ continue;
1727
+ }
1728
+ if (!types.isJSXElement(child) || !isHostElement(child)) return void 0;
1729
+ const staticAnalysis = analyzeRecursiveHostTree(
1730
+ child,
1731
+ statesByValue,
1732
+ safeGlobals,
1733
+ listNames,
1734
+ parent,
1735
+ allocateId
1736
+ );
1737
+ if (staticAnalysis.reason) return void 0;
1738
+ for (const binding of staticAnalysis.bindings) {
1739
+ staticBindings.push({
1740
+ ...binding,
1741
+ segment: ranges.length,
1742
+ sibling: staticBefore
1743
+ });
1744
+ }
1745
+ if (!mergeAnalysis(staticAnalysis)) return void 0;
1746
+ staticBefore += 1;
1747
+ }
1748
+ if (conditionalCount === 0 || keyedCount === 0) return void 0;
1749
+ return {
1750
+ ranges,
1751
+ trailing: staticBefore,
1752
+ dependencies: [...dependencies].sort((left, right) => left - right),
1753
+ staticBindings,
1754
+ descriptorBlocks,
1755
+ nestedPlans
1103
1756
  };
1104
1757
  }
1105
1758
  function keyedListBoundary(blockRuntime, plan, source) {
@@ -1124,14 +1777,14 @@ function keyedListBoundary(blockRuntime, plan, source) {
1124
1777
  true
1125
1778
  );
1126
1779
  }
1127
- function hostElementDescriptor(element) {
1780
+ function hostElementDescriptor(element, descriptorBlocks = /* @__PURE__ */ new Map()) {
1128
1781
  const tag = element.openingElement.name;
1129
1782
  const attributes = [];
1130
1783
  const styles = [];
1131
1784
  for (const attribute of element.openingElement.attributes) {
1132
1785
  if (!types.isJSXAttribute(attribute)) continue;
1133
1786
  const name = jsxAttributeName(attribute);
1134
- if (!name || name === "key" || /^on[A-Z]/.test(name)) continue;
1787
+ if (!name || name === "key" || name === "ref" || /^on[A-Z]/.test(name)) continue;
1135
1788
  if (name === "style") {
1136
1789
  if (types.isJSXExpressionContainer(attribute.value) && types.isObjectExpression(attribute.value.expression)) {
1137
1790
  for (const property of attribute.value.expression.properties) {
@@ -1155,24 +1808,93 @@ function hostElementDescriptor(element) {
1155
1808
  ])
1156
1809
  );
1157
1810
  }
1811
+ const block = descriptorBlocks.get(element);
1812
+ const conditionalBySource = /* @__PURE__ */ new Map();
1813
+ const keyedBySource = /* @__PURE__ */ new Map();
1814
+ if (block?.kind === "nested-host-conditional-ranges") {
1815
+ for (const range of block.ranges) conditionalBySource.set(range.source, range);
1816
+ } else if (block?.kind === "nested-host-keyed-ranges") {
1817
+ for (const range of block.ranges) keyedBySource.set(range.source, range);
1818
+ } else if (block?.kind === "nested-host-mixed-ranges") {
1819
+ for (const range of block.ranges) {
1820
+ if (range.rangeKind === "conditional") conditionalBySource.set(range.source, range);
1821
+ else keyedBySource.set(range.source, range);
1822
+ }
1823
+ }
1158
1824
  const children = [];
1159
1825
  for (const child of element.children) {
1160
1826
  if (types.isJSXText(child)) {
1161
1827
  const text = cleanJsxText(child.value);
1162
1828
  if (text) children.push(types.stringLiteral(text));
1163
1829
  } else if (types.isJSXElement(child)) {
1164
- children.push(hostElementDescriptor(child));
1830
+ const keyedRange = keyedBySource.get(child);
1831
+ if (keyedRange) {
1832
+ if (block?.kind !== "nested-host-keyed-ranges") {
1833
+ children.push(keyedRangeDescriptorChildren(keyedRange, descriptorBlocks));
1834
+ }
1835
+ } else {
1836
+ children.push(hostElementDescriptor(child, descriptorBlocks));
1837
+ }
1165
1838
  } else if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1166
- children.push(cloneExpression(child.expression));
1839
+ const conditionalRange = conditionalBySource.get(child.expression);
1840
+ const keyedRange = keyedBySource.get(child.expression);
1841
+ if (conditionalRange) {
1842
+ children.push(conditionalRangeDescriptorChild(conditionalRange, descriptorBlocks));
1843
+ } else if (keyedRange) {
1844
+ if (block?.kind !== "nested-host-keyed-ranges") {
1845
+ children.push(keyedRangeDescriptorChildren(keyedRange, descriptorBlocks));
1846
+ }
1847
+ } else {
1848
+ children.push(cloneExpression(child.expression));
1849
+ }
1167
1850
  }
1168
1851
  }
1169
- return types.objectExpression([
1852
+ const properties = [
1170
1853
  types.objectProperty(types.identifier("kind"), types.stringLiteral("element")),
1171
1854
  types.objectProperty(types.identifier("tag"), types.stringLiteral(tag.name)),
1172
1855
  types.objectProperty(types.identifier("attributes"), types.arrayExpression(attributes)),
1173
1856
  types.objectProperty(types.identifier("styles"), types.arrayExpression(styles)),
1174
1857
  types.objectProperty(types.identifier("children"), types.arrayExpression(children))
1175
- ]);
1858
+ ];
1859
+ if (block) {
1860
+ properties.push(
1861
+ types.objectProperty(types.identifier("block"), nestedHostBlockObject(block, descriptorBlocks))
1862
+ );
1863
+ }
1864
+ return types.objectExpression(properties);
1865
+ }
1866
+ function conditionalRangeDescriptorChild(range, descriptorBlocks) {
1867
+ const truthy = range.truthy ? hostElementDescriptor(range.truthy, descriptorBlocks) : types.nullLiteral();
1868
+ if (range.logical) {
1869
+ return types.logicalExpression("&&", cloneExpression(range.test), truthy);
1870
+ }
1871
+ return types.conditionalExpression(
1872
+ cloneExpression(range.test),
1873
+ truthy,
1874
+ range.falsy ? hostElementDescriptor(range.falsy, descriptorBlocks) : types.nullLiteral()
1875
+ );
1876
+ }
1877
+ function keyedRangeDescriptorChildren(range, descriptorBlocks) {
1878
+ const parameters = range.renderCallback.params.map(
1879
+ (parameter) => types.cloneNode(parameter, true)
1880
+ );
1881
+ const callback = types.arrowFunctionExpression(
1882
+ parameters,
1883
+ hostElementDescriptor(range.row, descriptorBlocks)
1884
+ );
1885
+ const collection = cloneExpression(range.collection);
1886
+ if (range.syntax === "map") {
1887
+ return types.callExpression(types.memberExpression(collection, types.identifier("map")), [callback]);
1888
+ }
1889
+ return types.callExpression(
1890
+ types.memberExpression(
1891
+ types.callExpression(types.memberExpression(types.identifier("Array"), types.identifier("from")), [
1892
+ types.logicalExpression("||", collection, types.arrayExpression([]))
1893
+ ]),
1894
+ types.identifier("map")
1895
+ ),
1896
+ [callback]
1897
+ );
1176
1898
  }
1177
1899
  function keyedRowBindingObject(binding, parameters) {
1178
1900
  const properties = [
@@ -1180,6 +1902,12 @@ function keyedRowBindingObject(binding, parameters) {
1180
1902
  types.objectProperty(
1181
1903
  types.identifier("path"),
1182
1904
  types.arrayExpression(binding.path.map((part) => types.numericLiteral(part)))
1905
+ ),
1906
+ types.objectProperty(
1907
+ types.identifier("dependencies"),
1908
+ types.arrayExpression(
1909
+ (binding.dependencies || []).map((dependency) => types.numericLiteral(dependency))
1910
+ )
1183
1911
  )
1184
1912
  ];
1185
1913
  if (binding.name) {
@@ -1196,6 +1924,16 @@ function keyedRowBindingObject(binding, parameters) {
1196
1924
  );
1197
1925
  return types.objectExpression(properties);
1198
1926
  }
1927
+ function staticRangeBindingObject(binding) {
1928
+ const value = keyedRowBindingObject(binding, []);
1929
+ value.properties.splice(
1930
+ 1,
1931
+ 0,
1932
+ types.objectProperty(types.identifier("segment"), types.numericLiteral(binding.segment)),
1933
+ types.objectProperty(types.identifier("sibling"), types.numericLiteral(binding.sibling))
1934
+ );
1935
+ return value;
1936
+ }
1199
1937
  function uniqueLocalIdentifier(base, nodes) {
1200
1938
  const names = /* @__PURE__ */ new Set();
1201
1939
  for (const node of nodes) {
@@ -1312,6 +2050,10 @@ function keyedRowEventObject(event, renderParameters) {
1312
2050
  const parameters = [types.cloneNode(renderParameters[0], true), index, syntheticEvent];
1313
2051
  return types.objectExpression([
1314
2052
  types.objectProperty(types.identifier("name"), types.stringLiteral(event.name)),
2053
+ types.objectProperty(
2054
+ types.identifier("path"),
2055
+ types.arrayExpression(event.path.map((part) => types.numericLiteral(part)))
2056
+ ),
1315
2057
  types.objectProperty(
1316
2058
  types.identifier("invoke"),
1317
2059
  types.arrowFunctionExpression(
@@ -1381,6 +2123,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1381
2123
  plan.source,
1382
2124
  ...plan.conditionals.map((conditional) => conditional.test)
1383
2125
  ]);
2126
+ const delegatedEvents = canDelegateKeyedRowEvents(plan);
1384
2127
  const renderSource = keyedRowsRenderSource(plan, eventFactory, conditionalFactory);
1385
2128
  const renderFactoryParameters = plan.conditionals.length > 0 ? [types.cloneNode(eventFactory), types.cloneNode(conditionalFactory)] : plan.events.length > 0 ? [types.cloneNode(eventFactory)] : [];
1386
2129
  return types.jsxElement(
@@ -1398,6 +2141,14 @@ function keyedRowsBoundary(blockRuntime, plan) {
1398
2141
  types.jsxIdentifier("items"),
1399
2142
  types.jsxExpressionContainer(types.arrowFunctionExpression([], cloneExpression(plan.collection)))
1400
2143
  ),
2144
+ types.jsxAttribute(
2145
+ types.jsxIdentifier("structureDependencies"),
2146
+ types.jsxExpressionContainer(
2147
+ types.arrayExpression(
2148
+ plan.structureDependencies.map((dependency) => types.numericLiteral(dependency))
2149
+ )
2150
+ )
2151
+ ),
1401
2152
  types.jsxAttribute(
1402
2153
  types.jsxIdentifier("rowKey"),
1403
2154
  types.jsxExpressionContainer(types.cloneNode(plan.keyCallback, true))
@@ -1407,7 +2158,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1407
2158
  types.jsxExpressionContainer(
1408
2159
  types.arrowFunctionExpression(
1409
2160
  parameters.map((parameter) => types.cloneNode(parameter, true)),
1410
- hostElementDescriptor(plan.row)
2161
+ hostElementDescriptor(plan.row, plan.descriptorBlocks)
1411
2162
  )
1412
2163
  )
1413
2164
  ),
@@ -1427,7 +2178,13 @@ function keyedRowsBoundary(blockRuntime, plan) {
1427
2178
  plan.events.map((event) => keyedRowEventObject(event, parameters))
1428
2179
  )
1429
2180
  )
1430
- )
2181
+ ),
2182
+ ...delegatedEvents ? [
2183
+ types.jsxAttribute(
2184
+ types.jsxIdentifier("delegateEvents"),
2185
+ types.jsxExpressionContainer(types.booleanLiteral(true))
2186
+ )
2187
+ ] : []
1431
2188
  ] : [],
1432
2189
  ...plan.conditionals.length > 0 ? [
1433
2190
  types.jsxAttribute(
@@ -1440,6 +2197,12 @@ function keyedRowsBoundary(blockRuntime, plan) {
1440
2197
  )
1441
2198
  )
1442
2199
  )
2200
+ ] : [],
2201
+ ...plan.descriptorBlocks?.size ? [
2202
+ types.jsxAttribute(
2203
+ types.jsxIdentifier("hostBlocks"),
2204
+ types.jsxExpressionContainer(types.booleanLiteral(true))
2205
+ )
1443
2206
  ] : []
1444
2207
  ],
1445
2208
  true
@@ -1449,7 +2212,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1449
2212
  true
1450
2213
  );
1451
2214
  }
1452
- function keyedRangeObject(range) {
2215
+ function keyedRangeObject(range, descriptorBlocks = /* @__PURE__ */ new Map()) {
1453
2216
  const parameters = range.renderCallback.params.map(
1454
2217
  (parameter) => types.cloneNode(parameter, true)
1455
2218
  );
@@ -1464,7 +2227,7 @@ function keyedRangeObject(range) {
1464
2227
  types.identifier("create"),
1465
2228
  types.arrowFunctionExpression(
1466
2229
  parameters.map((parameter) => types.cloneNode(parameter, true)),
1467
- hostElementDescriptor(range.row)
2230
+ hostElementDescriptor(range.row, descriptorBlocks)
1468
2231
  )
1469
2232
  ),
1470
2233
  types.objectProperty(
@@ -1508,6 +2271,12 @@ function keyedRangesBoundary(blockRuntime, plan) {
1508
2271
  types.jsxAttribute(
1509
2272
  types.jsxIdentifier("trailing"),
1510
2273
  types.jsxExpressionContainer(types.numericLiteral(plan.trailing))
2274
+ ),
2275
+ types.jsxAttribute(
2276
+ types.jsxIdentifier("bindings"),
2277
+ types.jsxExpressionContainer(
2278
+ types.arrayExpression(plan.staticBindings.map(staticRangeBindingObject))
2279
+ )
1511
2280
  )
1512
2281
  ],
1513
2282
  true
@@ -1517,11 +2286,11 @@ function keyedRangesBoundary(blockRuntime, plan) {
1517
2286
  true
1518
2287
  );
1519
2288
  }
1520
- function hostConditionalBranchObject(branch, bindings) {
2289
+ function hostConditionalBranchObject(branch, bindings, descriptorBlocks = /* @__PURE__ */ new Map()) {
1521
2290
  return types.objectExpression([
1522
2291
  types.objectProperty(
1523
2292
  types.identifier("create"),
1524
- types.arrowFunctionExpression([], hostElementDescriptor(branch))
2293
+ types.arrowFunctionExpression([], hostElementDescriptor(branch, descriptorBlocks))
1525
2294
  ),
1526
2295
  types.objectProperty(
1527
2296
  types.identifier("bindings"),
@@ -1550,7 +2319,9 @@ function hostConditionalBoundary(blockRuntime, plan) {
1550
2319
  attributes.push(
1551
2320
  types.jsxAttribute(
1552
2321
  types.jsxIdentifier("truthy"),
1553
- types.jsxExpressionContainer(hostConditionalBranchObject(plan.truthy, plan.truthyBindings))
2322
+ types.jsxExpressionContainer(
2323
+ hostConditionalBranchObject(plan.truthy, plan.truthyBindings, plan.descriptorBlocks)
2324
+ )
1554
2325
  )
1555
2326
  );
1556
2327
  }
@@ -1558,13 +2329,15 @@ function hostConditionalBoundary(blockRuntime, plan) {
1558
2329
  attributes.push(
1559
2330
  types.jsxAttribute(
1560
2331
  types.jsxIdentifier("falsy"),
1561
- types.jsxExpressionContainer(hostConditionalBranchObject(plan.falsy, plan.falsyBindings))
2332
+ types.jsxExpressionContainer(
2333
+ hostConditionalBranchObject(plan.falsy, plan.falsyBindings, plan.descriptorBlocks)
2334
+ )
1562
2335
  )
1563
2336
  );
1564
2337
  }
1565
2338
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
1566
2339
  }
1567
- function conditionalRangeObject(range) {
2340
+ function conditionalRangeObject(range, descriptorBlocks = /* @__PURE__ */ new Map()) {
1568
2341
  const properties = [
1569
2342
  types.objectProperty(types.identifier("before"), types.numericLiteral(range.before)),
1570
2343
  types.objectProperty(
@@ -1579,7 +2352,7 @@ function conditionalRangeObject(range) {
1579
2352
  properties.push(
1580
2353
  types.objectProperty(
1581
2354
  types.identifier("truthy"),
1582
- hostConditionalBranchObject(range.truthy, range.truthyBindings)
2355
+ hostConditionalBranchObject(range.truthy, range.truthyBindings, descriptorBlocks)
1583
2356
  )
1584
2357
  );
1585
2358
  }
@@ -1587,12 +2360,78 @@ function conditionalRangeObject(range) {
1587
2360
  properties.push(
1588
2361
  types.objectProperty(
1589
2362
  types.identifier("falsy"),
1590
- hostConditionalBranchObject(range.falsy, range.falsyBindings)
2363
+ hostConditionalBranchObject(range.falsy, range.falsyBindings, descriptorBlocks)
1591
2364
  )
1592
2365
  );
1593
2366
  }
1594
2367
  return types.objectExpression(properties);
1595
2368
  }
2369
+ function nestedHostBlockObject(plan, descriptorBlocks) {
2370
+ const kind = plan.kind === "nested-host-conditional-ranges" ? "conditional-ranges" : plan.kind === "nested-host-keyed-ranges" ? "keyed-ranges" : "mixed-ranges";
2371
+ return types.objectExpression([
2372
+ types.objectProperty(types.identifier("kind"), types.stringLiteral(kind)),
2373
+ types.objectProperty(types.identifier("id"), types.numericLiteral(plan.id)),
2374
+ types.objectProperty(
2375
+ types.identifier("ranges"),
2376
+ types.arrayExpression(
2377
+ 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))
2378
+ )
2379
+ ),
2380
+ types.objectProperty(types.identifier("trailing"), types.numericLiteral(plan.trailing)),
2381
+ types.objectProperty(
2382
+ types.identifier("bindings"),
2383
+ types.arrayExpression(plan.staticBindings.map(staticRangeBindingObject))
2384
+ ),
2385
+ ...plan.kind === "nested-host-keyed-ranges" ? [types.objectProperty(types.identifier("staticChildrenOnly"), types.booleanLiteral(true))] : []
2386
+ ]);
2387
+ }
2388
+ function mixedRangeObject(range, descriptorBlocks) {
2389
+ const value = range.rangeKind === "conditional" ? conditionalRangeObject(range, descriptorBlocks) : keyedRangeObject(range, descriptorBlocks);
2390
+ value.properties.unshift(
2391
+ types.objectProperty(types.identifier("kind"), types.stringLiteral(range.rangeKind))
2392
+ );
2393
+ return value;
2394
+ }
2395
+ function mixedRangesBoundary(blockRuntime, plan) {
2396
+ const name = types.jsxMemberExpression(
2397
+ types.jsxIdentifier(blockRuntime.name),
2398
+ types.jsxIdentifier("MixedRanges")
2399
+ );
2400
+ const source = types.cloneNode(plan.source, true);
2401
+ const rootRefIndex = source.openingElement.attributes.findIndex(
2402
+ (attribute) => types.isJSXAttribute(attribute) && jsxAttributeName(attribute) === "ref"
2403
+ );
2404
+ const rootRef = rootRefIndex >= 0 ? source.openingElement.attributes.splice(rootRefIndex, 1)[0] : void 0;
2405
+ const nestedPlan = {
2406
+ kind: "nested-host-mixed-ranges",
2407
+ id: plan.id,
2408
+ parent: plan.parent,
2409
+ dependencies: plan.dependencies,
2410
+ source: plan.source,
2411
+ ranges: plan.ranges,
2412
+ trailing: plan.trailing,
2413
+ staticBindings: plan.staticBindings
2414
+ };
2415
+ const descriptorBlocks = new Map(plan.descriptorBlocks);
2416
+ descriptorBlocks.set(plan.source, nestedPlan);
2417
+ const attributes = [
2418
+ types.jsxAttribute(types.jsxIdentifier("id"), types.jsxExpressionContainer(types.numericLiteral(plan.id))),
2419
+ types.jsxAttribute(
2420
+ types.jsxIdentifier("render"),
2421
+ types.jsxExpressionContainer(types.arrowFunctionExpression([], source))
2422
+ ),
2423
+ types.jsxAttribute(
2424
+ types.jsxIdentifier("create"),
2425
+ types.jsxExpressionContainer(
2426
+ types.arrowFunctionExpression([], hostElementDescriptor(plan.source, descriptorBlocks))
2427
+ )
2428
+ )
2429
+ ];
2430
+ if (rootRef?.value) {
2431
+ attributes.push(types.jsxAttribute(types.jsxIdentifier("rootRef"), types.cloneNode(rootRef.value, true)));
2432
+ }
2433
+ return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
2434
+ }
1596
2435
  function conditionalRangesBoundary(blockRuntime, plan) {
1597
2436
  const name = types.jsxMemberExpression(
1598
2437
  types.jsxIdentifier(blockRuntime.name),
@@ -1612,12 +2451,20 @@ function conditionalRangesBoundary(blockRuntime, plan) {
1612
2451
  types.jsxAttribute(
1613
2452
  types.jsxIdentifier("ranges"),
1614
2453
  types.jsxExpressionContainer(
1615
- types.arrayExpression(plan.ranges.map((range) => conditionalRangeObject(range)))
2454
+ types.arrayExpression(
2455
+ plan.ranges.map((range) => conditionalRangeObject(range, plan.descriptorBlocks))
2456
+ )
1616
2457
  )
1617
2458
  ),
1618
2459
  types.jsxAttribute(
1619
2460
  types.jsxIdentifier("trailing"),
1620
2461
  types.jsxExpressionContainer(types.numericLiteral(plan.trailing))
2462
+ ),
2463
+ types.jsxAttribute(
2464
+ types.jsxIdentifier("bindings"),
2465
+ types.jsxExpressionContainer(
2466
+ types.arrayExpression(plan.staticBindings.map(staticRangeBindingObject))
2467
+ )
1621
2468
  )
1622
2469
  ];
1623
2470
  if (rootRef?.value) {
@@ -1790,6 +2637,41 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1790
2637
  }
1791
2638
  if (types.isJSXElement(child)) {
1792
2639
  if (isHostElement(child)) {
2640
+ const mixedRangeStart = nextId;
2641
+ const mixedRangeId = nextId++;
2642
+ const mixedRanges = insideConditional ? void 0 : analyzeMixedRangesContainer(
2643
+ child,
2644
+ statesByValue,
2645
+ safeGlobals,
2646
+ listNames,
2647
+ mixedRangeId,
2648
+ () => nextId++
2649
+ );
2650
+ if (mixedRanges) {
2651
+ plans.push({
2652
+ kind: "mixed-ranges",
2653
+ id: mixedRangeId,
2654
+ parent,
2655
+ dependencies: mixedRanges.dependencies,
2656
+ source: child,
2657
+ ranges: mixedRanges.ranges,
2658
+ trailing: mixedRanges.trailing,
2659
+ staticBindings: mixedRanges.staticBindings,
2660
+ descriptorBlocks: mixedRanges.descriptorBlocks
2661
+ });
2662
+ plans.push(...mixedRanges.nestedPlans);
2663
+ for (const range of mixedRanges.ranges) {
2664
+ if (range.rangeKind === "conditional") {
2665
+ conditionalExpressions.add(range.source);
2666
+ } else if (types.isJSXElement(range.source)) {
2667
+ ownedElements.add(range.source);
2668
+ } else {
2669
+ keyedExpressions.add(range.source);
2670
+ }
2671
+ }
2672
+ continue;
2673
+ }
2674
+ nextId = mixedRangeStart;
1793
2675
  const keyedRanges = insideConditional ? void 0 : analyzeKeyedRangesContainer(child, statesByValue, safeGlobals, listNames);
1794
2676
  if (keyedRanges) {
1795
2677
  plans.push({
@@ -1799,7 +2681,8 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1799
2681
  dependencies: keyedRanges.dependencies,
1800
2682
  source: child,
1801
2683
  ranges: keyedRanges.ranges,
1802
- trailing: keyedRanges.trailing
2684
+ trailing: keyedRanges.trailing,
2685
+ staticBindings: keyedRanges.staticBindings
1803
2686
  });
1804
2687
  for (const range of keyedRanges.ranges) {
1805
2688
  if (types.isJSXElement(range.source)) ownedElements.add(range.source);
@@ -1807,58 +2690,102 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1807
2690
  }
1808
2691
  continue;
1809
2692
  }
1810
- const conditionalRanges = insideConditional ? void 0 : analyzeConditionalRangesContainer(child, statesByValue, safeGlobals);
2693
+ const conditionalRangeStart = nextId;
2694
+ const conditionalRangeId = nextId++;
2695
+ const conditionalRanges = insideConditional ? void 0 : analyzeConditionalRangesContainer(
2696
+ child,
2697
+ statesByValue,
2698
+ safeGlobals,
2699
+ false,
2700
+ listNames,
2701
+ conditionalRangeId,
2702
+ () => nextId++
2703
+ );
1811
2704
  if (conditionalRanges) {
1812
2705
  plans.push({
1813
2706
  kind: "conditional-ranges",
1814
- id: nextId++,
2707
+ id: conditionalRangeId,
1815
2708
  parent,
1816
2709
  dependencies: conditionalRanges.dependencies,
1817
2710
  source: child,
1818
2711
  ranges: conditionalRanges.ranges,
1819
- trailing: conditionalRanges.trailing
2712
+ trailing: conditionalRanges.trailing,
2713
+ staticBindings: conditionalRanges.staticBindings,
2714
+ descriptorBlocks: conditionalRanges.descriptorBlocks
1820
2715
  });
2716
+ plans.push(...conditionalRanges.nestedPlans || []);
1821
2717
  for (const range of conditionalRanges.ranges) {
1822
2718
  conditionalExpressions.add(range.source);
1823
2719
  }
1824
2720
  continue;
1825
2721
  }
2722
+ nextId = conditionalRangeStart;
2723
+ const hostConditionalStart = nextId;
2724
+ const hostConditionalId = nextId++;
1826
2725
  const hostConditional = analyzeHostConditionalContainer(
1827
2726
  child,
1828
2727
  statesByValue,
1829
- safeGlobals
2728
+ safeGlobals,
2729
+ listNames,
2730
+ hostConditionalId,
2731
+ () => nextId++
1830
2732
  );
1831
2733
  if (hostConditional) {
1832
2734
  plans.push({
1833
2735
  kind: "host-conditional",
1834
- id: nextId++,
2736
+ id: hostConditionalId,
1835
2737
  parent,
1836
2738
  source: child,
1837
2739
  ...hostConditional
1838
2740
  });
2741
+ plans.push(...hostConditional.nestedPlans || []);
1839
2742
  ownedElements.add(child);
1840
2743
  continue;
1841
2744
  }
2745
+ nextId = hostConditionalStart;
2746
+ const keyedRowsStart = nextId;
2747
+ const keyedRowsId = nextId++;
1842
2748
  const keyedRows = analyzeKeyedRowsContainer(child, statesByValue, safeGlobals, listNames);
1843
2749
  if (keyedRows) {
2750
+ const nestedStart = nextId;
2751
+ const hostBlocks = analyzeCompilerOwnedKeyedRowHostBlocks(
2752
+ keyedRows,
2753
+ statesByValue,
2754
+ safeGlobals,
2755
+ listNames,
2756
+ keyedRowsId,
2757
+ () => nextId++
2758
+ );
2759
+ if (!hostBlocks) nextId = nestedStart;
2760
+ if (keyedRows.rowReason && !hostBlocks) {
2761
+ nextId = keyedRowsStart;
2762
+ const nested2 = visitHost(child, parent, insideConditional);
2763
+ if (nested2.reason) return { dependencies, reason: nested2.reason };
2764
+ mergeDependencies(dependencies, nested2.dependencies);
2765
+ continue;
2766
+ }
1844
2767
  plans.push({
1845
2768
  kind: "keyed-rows",
1846
- id: nextId++,
2769
+ id: keyedRowsId,
1847
2770
  parent,
1848
2771
  dependencies: keyedRows.dependencies,
1849
2772
  source: child,
1850
2773
  collection: keyedRows.collection,
2774
+ structureDependencies: keyedRows.structureDependencies,
1851
2775
  keyCallback: keyedRows.keyCallback,
1852
2776
  renderCallback: keyedRows.renderCallback,
1853
- row: keyedRows.row,
1854
- bindings: keyedRows.bindings,
2777
+ row: hostBlocks?.row || keyedRows.row,
2778
+ bindings: hostBlocks?.bindings || keyedRows.bindings,
1855
2779
  events: keyedRows.events,
1856
- conditionals: keyedRows.conditionals,
2780
+ conditionals: hostBlocks ? [] : keyedRows.conditionals,
2781
+ descriptorBlocks: hostBlocks?.descriptorBlocks,
1857
2782
  syntax: keyedRows.syntax
1858
2783
  });
2784
+ plans.push(...hostBlocks?.nestedPlans || []);
1859
2785
  ownedElements.add(child);
1860
2786
  continue;
1861
2787
  }
2788
+ nextId = keyedRowsStart;
1862
2789
  const nested = visitHost(child, parent, insideConditional);
1863
2790
  if (nested.reason) return { dependencies, reason: nested.reason };
1864
2791
  mergeDependencies(dependencies, nested.dependencies);
@@ -1955,6 +2882,46 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1955
2882
  }
1956
2883
  return { dependencies };
1957
2884
  };
2885
+ const rootMixedRangeStart = nextId;
2886
+ const rootMixedRangeId = nextId++;
2887
+ const rootMixedRanges = analyzeMixedRangesContainer(
2888
+ root,
2889
+ statesByValue,
2890
+ safeGlobals,
2891
+ listNames,
2892
+ rootMixedRangeId,
2893
+ () => nextId++
2894
+ );
2895
+ if (rootMixedRanges) {
2896
+ plans.push({
2897
+ kind: "mixed-ranges",
2898
+ id: rootMixedRangeId,
2899
+ dependencies: rootMixedRanges.dependencies,
2900
+ source: root,
2901
+ ranges: rootMixedRanges.ranges,
2902
+ trailing: rootMixedRanges.trailing,
2903
+ staticBindings: rootMixedRanges.staticBindings,
2904
+ descriptorBlocks: rootMixedRanges.descriptorBlocks
2905
+ });
2906
+ plans.push(...rootMixedRanges.nestedPlans);
2907
+ for (const range of rootMixedRanges.ranges) {
2908
+ if (range.rangeKind === "conditional") {
2909
+ conditionalExpressions.add(range.source);
2910
+ } else if (types.isJSXElement(range.source)) {
2911
+ ownedElements.add(range.source);
2912
+ } else {
2913
+ keyedExpressions.add(range.source);
2914
+ }
2915
+ }
2916
+ return {
2917
+ plans,
2918
+ componentElements,
2919
+ conditionalExpressions,
2920
+ ownedElements,
2921
+ keyedExpressions
2922
+ };
2923
+ }
2924
+ nextId = rootMixedRangeStart;
1958
2925
  const rootRanges = analyzeKeyedRangesContainer(root, statesByValue, safeGlobals, listNames, true);
1959
2926
  if (rootRanges) {
1960
2927
  plans.push({
@@ -1963,7 +2930,8 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1963
2930
  dependencies: rootRanges.dependencies,
1964
2931
  source: root,
1965
2932
  ranges: rootRanges.ranges,
1966
- trailing: rootRanges.trailing
2933
+ trailing: rootRanges.trailing,
2934
+ staticBindings: rootRanges.staticBindings
1967
2935
  });
1968
2936
  for (const range of rootRanges.ranges) {
1969
2937
  if (types.isJSXElement(range.source)) ownedElements.add(range.source);
@@ -1977,21 +2945,29 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1977
2945
  keyedExpressions
1978
2946
  };
1979
2947
  }
2948
+ const rootConditionalRangeStart = nextId;
2949
+ const rootConditionalRangeId = nextId++;
1980
2950
  const rootConditionalRanges = analyzeConditionalRangesContainer(
1981
2951
  root,
1982
2952
  statesByValue,
1983
2953
  safeGlobals,
1984
- true
2954
+ true,
2955
+ listNames,
2956
+ rootConditionalRangeId,
2957
+ () => nextId++
1985
2958
  );
1986
2959
  if (rootConditionalRanges) {
1987
2960
  plans.push({
1988
2961
  kind: "conditional-ranges",
1989
- id: nextId++,
2962
+ id: rootConditionalRangeId,
1990
2963
  dependencies: rootConditionalRanges.dependencies,
1991
2964
  source: root,
1992
2965
  ranges: rootConditionalRanges.ranges,
1993
- trailing: rootConditionalRanges.trailing
2966
+ trailing: rootConditionalRanges.trailing,
2967
+ staticBindings: rootConditionalRanges.staticBindings,
2968
+ descriptorBlocks: rootConditionalRanges.descriptorBlocks
1994
2969
  });
2970
+ plans.push(...rootConditionalRanges.nestedPlans || []);
1995
2971
  for (const range of rootConditionalRanges.ranges) {
1996
2972
  conditionalExpressions.add(range.source);
1997
2973
  }
@@ -2003,6 +2979,7 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
2003
2979
  keyedExpressions
2004
2980
  };
2005
2981
  }
2982
+ nextId = rootConditionalRangeStart;
2006
2983
  const result = visitHost(root, void 0, false);
2007
2984
  if (result.reason) return { reason: result.reason };
2008
2985
  return {
@@ -2040,6 +3017,9 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
2040
3017
  plans.map((plan) => [plan.source, plan])
2041
3018
  );
2042
3019
  const rootPlan = planBySource.get(root);
3020
+ if (rootPlan?.kind === "mixed-ranges") {
3021
+ return mixedRangesBoundary(blockRuntime, rootPlan);
3022
+ }
2043
3023
  if (rootPlan?.kind === "keyed-ranges") {
2044
3024
  return keyedRangesBoundary(blockRuntime, rootPlan);
2045
3025
  }
@@ -2053,6 +3033,9 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
2053
3033
  if (plan2?.kind === "host-conditional") {
2054
3034
  return hostConditionalBoundary(blockRuntime, plan2);
2055
3035
  }
3036
+ if (plan2?.kind === "mixed-ranges") {
3037
+ return mixedRangesBoundary(blockRuntime, plan2);
3038
+ }
2056
3039
  if (plan2?.kind === "keyed-rows") {
2057
3040
  return keyedRowsBoundary(blockRuntime, plan2);
2058
3041
  }
@@ -2102,6 +3085,31 @@ function assignStableBindingTargets(bindings) {
2102
3085
  binding.target = target;
2103
3086
  }
2104
3087
  }
3088
+ function markShortCircuitBindings(bindings) {
3089
+ for (const binding of bindings) {
3090
+ if (binding.kind === "block" || binding.dependencies.length < 2) continue;
3091
+ let shortCircuits = false;
3092
+ traverse(expressionFile(types.cloneNode(binding.value, true)), {
3093
+ ConditionalExpression(path) {
3094
+ shortCircuits = true;
3095
+ path.stop();
3096
+ },
3097
+ LogicalExpression(path) {
3098
+ shortCircuits = true;
3099
+ path.stop();
3100
+ },
3101
+ OptionalCallExpression(path) {
3102
+ shortCircuits = true;
3103
+ path.stop();
3104
+ },
3105
+ OptionalMemberExpression(path) {
3106
+ shortCircuits = true;
3107
+ path.stop();
3108
+ }
3109
+ });
3110
+ if (shortCircuits) binding.tracking = "dynamic";
3111
+ }
3112
+ }
2105
3113
  function lowerStableBindingTargets(root, bindings, blockRuntime, ownedElements) {
2106
3114
  const targetByPath = /* @__PURE__ */ new Map();
2107
3115
  for (const binding of bindings) {
@@ -2371,6 +3379,9 @@ function bindingObject(binding, propsParameter, stateParameter, statesByValue, s
2371
3379
  types.objectProperty(types.identifier("target"), types.numericLiteral(binding.target))
2372
3380
  );
2373
3381
  }
3382
+ if (binding.tracking) {
3383
+ properties.push(types.objectProperty(types.identifier("tracking"), types.stringLiteral(binding.tracking)));
3384
+ }
2374
3385
  if (binding.name)
2375
3386
  properties.push(types.objectProperty(types.identifier("name"), types.stringLiteral(binding.name)));
2376
3387
  properties.push(
@@ -2389,7 +3400,7 @@ function wrapperElement(definitionIdentifier, props) {
2389
3400
  const attributes = props ? [types.jsxSpreadAttribute(types.cloneNode(props))] : [];
2390
3401
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
2391
3402
  }
2392
- function compileCandidate(candidate, createComponentIdentifier, useStateNames, reactNames, listNames, moduleId) {
3403
+ function compileCandidate(candidate, createComponentIdentifier, useStateNames, reactNames, listNames, moduleId, reactivity) {
2393
3404
  const { path, name, statementPath } = candidate;
2394
3405
  if (path.node.async || path.node.generator)
2395
3406
  return "async and generator components are not supported";
@@ -2506,8 +3517,34 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2506
3517
  );
2507
3518
  const setterReason = validateSetterUsage(expandedRoot, statesBySetter);
2508
3519
  if (setterReason) return setterReason;
3520
+ const referencedPropNames = collectDestructuredPropNames(
3521
+ expandedRoot,
3522
+ propsPlan.destructuredNames,
3523
+ propsPlan.definitionParameter
3524
+ );
3525
+ let propBindings = referencedPropNames.has("children") ? [] : [...propsPlan.destructuredNames].filter((propName) => referencedPropNames.has(propName)).map(
3526
+ (localName, propIndex) => ({
3527
+ localName,
3528
+ valueName: path.scope.generateUidIdentifier(`farmProp${propIndex}`).name,
3529
+ index: states.length + propIndex
3530
+ })
3531
+ );
3532
+ const propBindingsByName = new Map(propBindings.map((binding) => [binding.localName, binding]));
3533
+ let reactiveByValue = new Map(statesByValue);
3534
+ for (const binding of propBindings) {
3535
+ reactiveByValue.set(binding.valueName, {
3536
+ valueName: binding.valueName,
3537
+ setterName: "",
3538
+ index: binding.index
3539
+ });
3540
+ }
3541
+ let expandedReactiveRoot = rewriteTrackedPropAccess(
3542
+ expandedRoot,
3543
+ propsPlan.definitionParameter,
3544
+ propBindingsByName
3545
+ );
2509
3546
  const referencedComponentNames = /* @__PURE__ */ new Set();
2510
- traverse(expressionFile(types.cloneNode(expandedRoot, true)), {
3547
+ traverse(expressionFile(types.cloneNode(expandedReactiveRoot, true)), {
2511
3548
  JSXOpeningElement(componentPath) {
2512
3549
  const componentName = componentPath.node.name;
2513
3550
  if (types.isJSXIdentifier(componentName) && isComponentName(componentName.name)) {
@@ -2524,32 +3561,54 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2524
3561
  );
2525
3562
  })
2526
3563
  );
2527
- const blockAnalysis = analyzeComposableBlocks(
2528
- expandedRoot,
2529
- statesByValue,
3564
+ let blockAnalysis = analyzeComposableBlocks(
3565
+ expandedReactiveRoot,
3566
+ reactiveByValue,
2530
3567
  safeGlobals,
2531
3568
  listNames,
2532
3569
  allowedComponentNames
2533
3570
  );
2534
- if (blockAnalysis.reason) return blockAnalysis.reason;
2535
- const blockPlans = blockAnalysis.plans || [];
2536
- const analysis = analyzeHostTree(
2537
- expandedRoot,
2538
- statesByValue,
3571
+ let analysis = analyzeHostTree(
3572
+ expandedReactiveRoot,
3573
+ reactiveByValue,
2539
3574
  safeGlobals,
2540
3575
  blockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
2541
3576
  blockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
2542
3577
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
2543
3578
  blockAnalysis.componentElements || /* @__PURE__ */ new Set()
2544
3579
  );
3580
+ if (propBindings.length > 0 && (blockAnalysis.reason || analysis.reason)) {
3581
+ propBindings = [];
3582
+ reactiveByValue = new Map(statesByValue);
3583
+ expandedReactiveRoot = types.cloneNode(expandedRoot, true);
3584
+ blockAnalysis = analyzeComposableBlocks(
3585
+ expandedReactiveRoot,
3586
+ reactiveByValue,
3587
+ safeGlobals,
3588
+ listNames,
3589
+ allowedComponentNames
3590
+ );
3591
+ analysis = analyzeHostTree(
3592
+ expandedReactiveRoot,
3593
+ reactiveByValue,
3594
+ safeGlobals,
3595
+ blockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
3596
+ blockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
3597
+ blockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
3598
+ blockAnalysis.componentElements || /* @__PURE__ */ new Set()
3599
+ );
3600
+ }
3601
+ if (blockAnalysis.reason) return blockAnalysis.reason;
2545
3602
  if (analysis.reason) return analysis.reason;
3603
+ const blockPlans = blockAnalysis.plans || [];
3604
+ markShortCircuitBindings(analysis.bindings || []);
2546
3605
  assignStableBindingTargets(analysis.bindings || []);
2547
3606
  const stateParameter = path.scope.generateUidIdentifier("farmState");
2548
3607
  const blockParameter = path.scope.generateUidIdentifier("farmBlocks");
2549
3608
  const definitionIdentifier = path.scope.generateUidIdentifier(`${name}Compiled`);
2550
3609
  const propsParameter = propsPlan.definitionParameter;
2551
3610
  const rootWithTargets = lowerStableBindingTargets(
2552
- expandedRoot,
3611
+ expandedReactiveRoot,
2553
3612
  analysis.bindings || [],
2554
3613
  blockParameter,
2555
3614
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set()
@@ -2563,12 +3622,13 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2563
3622
  const rewrittenRoot = rewriteStateAccess(
2564
3623
  rootWithBlocks,
2565
3624
  stateParameter,
2566
- statesByValue,
3625
+ reactiveByValue,
2567
3626
  statesBySetter
2568
3627
  );
2569
3628
  const definition = types.callExpression(types.cloneNode(createComponentIdentifier), [
2570
3629
  types.objectExpression([
2571
3630
  types.objectProperty(types.identifier("displayName"), types.stringLiteral(name)),
3631
+ types.objectProperty(types.identifier("reactivity"), types.stringLiteral(reactivity)),
2572
3632
  types.spreadElement(
2573
3633
  types.conditionalExpression(
2574
3634
  types.memberExpression(
@@ -2579,7 +3639,9 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2579
3639
  types.objectProperty(types.identifier("hmrId"), types.stringLiteral(`${moduleId}#${name}`)),
2580
3640
  types.objectProperty(
2581
3641
  types.identifier("stateSignature"),
2582
- types.stringLiteral(String(states.length))
3642
+ types.stringLiteral(
3643
+ propBindings.length === 0 ? String(states.length) : `${states.length}:${propBindings.map((binding) => binding.localName).join(",")}`
3644
+ )
2583
3645
  )
2584
3646
  ]),
2585
3647
  types.objectExpression([])
@@ -2602,6 +3664,22 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2602
3664
  )
2603
3665
  )
2604
3666
  ),
3667
+ ...propBindings.length > 0 ? [
3668
+ types.objectProperty(
3669
+ types.identifier("readProps"),
3670
+ types.arrowFunctionExpression(
3671
+ [types.cloneNode(propsParameter)],
3672
+ types.arrayExpression(
3673
+ propBindings.map(
3674
+ (binding) => types.memberExpression(
3675
+ types.cloneNode(propsParameter),
3676
+ types.identifier(binding.localName)
3677
+ )
3678
+ )
3679
+ )
3680
+ )
3681
+ )
3682
+ ] : [],
2605
3683
  types.objectProperty(
2606
3684
  types.identifier("render"),
2607
3685
  types.arrowFunctionExpression(
@@ -2621,7 +3699,7 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2621
3699
  dependencies: block.dependencies
2622
3700
  }))
2623
3701
  ].map(
2624
- (binding) => bindingObject(binding, propsParameter, stateParameter, statesByValue, statesBySetter)
3702
+ (binding) => bindingObject(binding, propsParameter, stateParameter, reactiveByValue, statesBySetter)
2625
3703
  )
2626
3704
  )
2627
3705
  )
@@ -2715,7 +3793,8 @@ async function compileReactModule(code, id, options) {
2715
3793
  useStateNames,
2716
3794
  reactNames,
2717
3795
  listNames,
2718
- id
3796
+ id,
3797
+ options.reactivity
2719
3798
  );
2720
3799
  if (reason) {
2721
3800
  diagnostics.push({
@@ -2842,6 +3921,10 @@ function normalizeReactCompilerOptions(value) {
2842
3921
  if (!["fallback", "warn", "error"].includes(onUnsupported)) {
2843
3922
  throw new TypeError(`Unknown unsupported-component behavior: ${String(onUnsupported)}`);
2844
3923
  }
3924
+ const reactivity = options.reactivity || "hybrid";
3925
+ if (reactivity !== "static" && reactivity !== "hybrid") {
3926
+ throw new TypeError(`Unknown React compiler reactivity mode: ${String(reactivity)}`);
3927
+ }
2845
3928
  if (options.report !== void 0 && typeof options.report !== "boolean") {
2846
3929
  throw new TypeError("The React compiler report option must be a boolean.");
2847
3930
  }
@@ -2852,7 +3935,7 @@ function normalizeReactCompilerOptions(value) {
2852
3935
  }
2853
3936
  const reportFile = normalizeCompilerReportFile(options.reportFile);
2854
3937
  const report = options.report === true || options.reportFile !== void 0;
2855
- return { mode, directive, onUnsupported, report, reportFile };
3938
+ return { mode, directive, onUnsupported, reactivity, report, reportFile };
2856
3939
  }
2857
3940
 
2858
3941
  // src/vite.ts
@@ -2868,7 +3951,7 @@ function readCompilerOptions(rendererOptions) {
2868
3951
  );
2869
3952
  }
2870
3953
  const normalized = compiler;
2871
- if ((normalized.mode === "infer" || normalized.mode === "annotation") && typeof normalized.directive === "string" && (normalized.onUnsupported === "fallback" || normalized.onUnsupported === "warn" || normalized.onUnsupported === "error")) {
3954
+ 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
3955
  return normalized;
2873
3956
  }
2874
3957
  return normalizeReactCompilerOptions(compiler);