@farm.js/react 0.1.0-beta.2 → 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 isStaticKeyedRangeSibling(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) || !isStaticKeyedRangeSibling(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 (!isStaticKeyedRangeSibling(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 || []]) {
@@ -1040,12 +1527,232 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals)
1040
1527
  }
1041
1528
  return {
1042
1529
  dependencies: [...dependencies].sort((left, right) => left - right),
1043
- test: cloneExpression(shape.test),
1044
- logical: shape.logical,
1045
- truthy: shape.truthy,
1046
- falsy: shape.falsy,
1047
- truthyBindings: truthyAnalysis.bindings || [],
1048
- falsyBindings: falsyAnalysis.bindings || []
1530
+ test: cloneExpression(shape.test),
1531
+ logical: shape.logical,
1532
+ truthy: shape.truthy,
1533
+ falsy: shape.falsy,
1534
+ truthyBindings: truthyAnalysis.bindings || [],
1535
+ falsyBindings: falsyAnalysis.bindings || [],
1536
+ descriptorBlocks: new Map([...truthyAnalysis.blocks, ...falsyAnalysis.blocks]),
1537
+ nestedPlans: [...truthyAnalysis.plans, ...falsyAnalysis.plans]
1538
+ };
1539
+ }
1540
+ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals, allowSingleRange, listNames, parent, allocateId) {
1541
+ const containerName = container.openingElement.name;
1542
+ if (!types.isJSXIdentifier(containerName) || !/^[a-z]/.test(containerName.name) || containerName.name === "svg" || container.openingElement.attributes.some(
1543
+ (attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
1544
+ )) {
1545
+ return void 0;
1546
+ }
1547
+ const children = meaningfulJsxChildren(container);
1548
+ if (children.length < (allowSingleRange ? 1 : 2)) return void 0;
1549
+ const ranges = [];
1550
+ const dependencies = /* @__PURE__ */ new Set();
1551
+ const descriptorBlocks = /* @__PURE__ */ new Map();
1552
+ const nestedPlans = [];
1553
+ const staticBindings = [];
1554
+ let staticBefore = 0;
1555
+ for (const child of children) {
1556
+ if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1557
+ const shape = conditionalBlockShape(child.expression);
1558
+ if (shape && !validateDerivedExpression(shape.test, safeGlobals)) {
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() };
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);
1579
+ const rangeDependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1580
+ for (const binding of [
1581
+ ...truthyAnalysis.bindings || [],
1582
+ ...falsyAnalysis.bindings || []
1583
+ ]) {
1584
+ for (const dependency of collectStateDependencies(binding.value, statesByValue)) {
1585
+ rangeDependencies.add(dependency);
1586
+ }
1587
+ }
1588
+ for (const dependency of rangeDependencies) dependencies.add(dependency);
1589
+ ranges.push({
1590
+ before: staticBefore,
1591
+ source: child.expression,
1592
+ test: cloneExpression(shape.test),
1593
+ logical: shape.logical,
1594
+ truthy: shape.truthy,
1595
+ falsy: shape.falsy,
1596
+ truthyBindings: truthyAnalysis.bindings || [],
1597
+ falsyBindings: falsyAnalysis.bindings || []
1598
+ });
1599
+ staticBefore = 0;
1600
+ continue;
1601
+ }
1602
+ }
1603
+ if (!types.isJSXElement(child) || !isHostElement(child)) 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
+ }
1616
+ staticBefore += 1;
1617
+ }
1618
+ if (ranges.length === 0) return void 0;
1619
+ return {
1620
+ ranges,
1621
+ trailing: staticBefore,
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
1049
1756
  };
1050
1757
  }
1051
1758
  function keyedListBoundary(blockRuntime, plan, source) {
@@ -1070,14 +1777,14 @@ function keyedListBoundary(blockRuntime, plan, source) {
1070
1777
  true
1071
1778
  );
1072
1779
  }
1073
- function hostElementDescriptor(element) {
1780
+ function hostElementDescriptor(element, descriptorBlocks = /* @__PURE__ */ new Map()) {
1074
1781
  const tag = element.openingElement.name;
1075
1782
  const attributes = [];
1076
1783
  const styles = [];
1077
1784
  for (const attribute of element.openingElement.attributes) {
1078
1785
  if (!types.isJSXAttribute(attribute)) continue;
1079
1786
  const name = jsxAttributeName(attribute);
1080
- if (!name || name === "key" || /^on[A-Z]/.test(name)) continue;
1787
+ if (!name || name === "key" || name === "ref" || /^on[A-Z]/.test(name)) continue;
1081
1788
  if (name === "style") {
1082
1789
  if (types.isJSXExpressionContainer(attribute.value) && types.isObjectExpression(attribute.value.expression)) {
1083
1790
  for (const property of attribute.value.expression.properties) {
@@ -1101,24 +1808,93 @@ function hostElementDescriptor(element) {
1101
1808
  ])
1102
1809
  );
1103
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
+ }
1104
1824
  const children = [];
1105
1825
  for (const child of element.children) {
1106
1826
  if (types.isJSXText(child)) {
1107
1827
  const text = cleanJsxText(child.value);
1108
1828
  if (text) children.push(types.stringLiteral(text));
1109
1829
  } else if (types.isJSXElement(child)) {
1110
- 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
+ }
1111
1838
  } else if (types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)) {
1112
- 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
+ }
1113
1850
  }
1114
1851
  }
1115
- return types.objectExpression([
1852
+ const properties = [
1116
1853
  types.objectProperty(types.identifier("kind"), types.stringLiteral("element")),
1117
1854
  types.objectProperty(types.identifier("tag"), types.stringLiteral(tag.name)),
1118
1855
  types.objectProperty(types.identifier("attributes"), types.arrayExpression(attributes)),
1119
1856
  types.objectProperty(types.identifier("styles"), types.arrayExpression(styles)),
1120
1857
  types.objectProperty(types.identifier("children"), types.arrayExpression(children))
1121
- ]);
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
+ );
1122
1898
  }
1123
1899
  function keyedRowBindingObject(binding, parameters) {
1124
1900
  const properties = [
@@ -1126,6 +1902,12 @@ function keyedRowBindingObject(binding, parameters) {
1126
1902
  types.objectProperty(
1127
1903
  types.identifier("path"),
1128
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
+ )
1129
1911
  )
1130
1912
  ];
1131
1913
  if (binding.name) {
@@ -1142,6 +1924,16 @@ function keyedRowBindingObject(binding, parameters) {
1142
1924
  );
1143
1925
  return types.objectExpression(properties);
1144
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
+ }
1145
1937
  function uniqueLocalIdentifier(base, nodes) {
1146
1938
  const names = /* @__PURE__ */ new Set();
1147
1939
  for (const node of nodes) {
@@ -1258,6 +2050,10 @@ function keyedRowEventObject(event, renderParameters) {
1258
2050
  const parameters = [types.cloneNode(renderParameters[0], true), index, syntheticEvent];
1259
2051
  return types.objectExpression([
1260
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
+ ),
1261
2057
  types.objectProperty(
1262
2058
  types.identifier("invoke"),
1263
2059
  types.arrowFunctionExpression(
@@ -1327,6 +2123,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1327
2123
  plan.source,
1328
2124
  ...plan.conditionals.map((conditional) => conditional.test)
1329
2125
  ]);
2126
+ const delegatedEvents = canDelegateKeyedRowEvents(plan);
1330
2127
  const renderSource = keyedRowsRenderSource(plan, eventFactory, conditionalFactory);
1331
2128
  const renderFactoryParameters = plan.conditionals.length > 0 ? [types.cloneNode(eventFactory), types.cloneNode(conditionalFactory)] : plan.events.length > 0 ? [types.cloneNode(eventFactory)] : [];
1332
2129
  return types.jsxElement(
@@ -1344,6 +2141,14 @@ function keyedRowsBoundary(blockRuntime, plan) {
1344
2141
  types.jsxIdentifier("items"),
1345
2142
  types.jsxExpressionContainer(types.arrowFunctionExpression([], cloneExpression(plan.collection)))
1346
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
+ ),
1347
2152
  types.jsxAttribute(
1348
2153
  types.jsxIdentifier("rowKey"),
1349
2154
  types.jsxExpressionContainer(types.cloneNode(plan.keyCallback, true))
@@ -1353,7 +2158,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1353
2158
  types.jsxExpressionContainer(
1354
2159
  types.arrowFunctionExpression(
1355
2160
  parameters.map((parameter) => types.cloneNode(parameter, true)),
1356
- hostElementDescriptor(plan.row)
2161
+ hostElementDescriptor(plan.row, plan.descriptorBlocks)
1357
2162
  )
1358
2163
  )
1359
2164
  ),
@@ -1373,7 +2178,13 @@ function keyedRowsBoundary(blockRuntime, plan) {
1373
2178
  plan.events.map((event) => keyedRowEventObject(event, parameters))
1374
2179
  )
1375
2180
  )
1376
- )
2181
+ ),
2182
+ ...delegatedEvents ? [
2183
+ types.jsxAttribute(
2184
+ types.jsxIdentifier("delegateEvents"),
2185
+ types.jsxExpressionContainer(types.booleanLiteral(true))
2186
+ )
2187
+ ] : []
1377
2188
  ] : [],
1378
2189
  ...plan.conditionals.length > 0 ? [
1379
2190
  types.jsxAttribute(
@@ -1386,6 +2197,12 @@ function keyedRowsBoundary(blockRuntime, plan) {
1386
2197
  )
1387
2198
  )
1388
2199
  )
2200
+ ] : [],
2201
+ ...plan.descriptorBlocks?.size ? [
2202
+ types.jsxAttribute(
2203
+ types.jsxIdentifier("hostBlocks"),
2204
+ types.jsxExpressionContainer(types.booleanLiteral(true))
2205
+ )
1389
2206
  ] : []
1390
2207
  ],
1391
2208
  true
@@ -1395,7 +2212,7 @@ function keyedRowsBoundary(blockRuntime, plan) {
1395
2212
  true
1396
2213
  );
1397
2214
  }
1398
- function keyedRangeObject(range) {
2215
+ function keyedRangeObject(range, descriptorBlocks = /* @__PURE__ */ new Map()) {
1399
2216
  const parameters = range.renderCallback.params.map(
1400
2217
  (parameter) => types.cloneNode(parameter, true)
1401
2218
  );
@@ -1410,7 +2227,7 @@ function keyedRangeObject(range) {
1410
2227
  types.identifier("create"),
1411
2228
  types.arrowFunctionExpression(
1412
2229
  parameters.map((parameter) => types.cloneNode(parameter, true)),
1413
- hostElementDescriptor(range.row)
2230
+ hostElementDescriptor(range.row, descriptorBlocks)
1414
2231
  )
1415
2232
  ),
1416
2233
  types.objectProperty(
@@ -1454,6 +2271,12 @@ function keyedRangesBoundary(blockRuntime, plan) {
1454
2271
  types.jsxAttribute(
1455
2272
  types.jsxIdentifier("trailing"),
1456
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
+ )
1457
2280
  )
1458
2281
  ],
1459
2282
  true
@@ -1463,11 +2286,11 @@ function keyedRangesBoundary(blockRuntime, plan) {
1463
2286
  true
1464
2287
  );
1465
2288
  }
1466
- function hostConditionalBranchObject(branch, bindings) {
2289
+ function hostConditionalBranchObject(branch, bindings, descriptorBlocks = /* @__PURE__ */ new Map()) {
1467
2290
  return types.objectExpression([
1468
2291
  types.objectProperty(
1469
2292
  types.identifier("create"),
1470
- types.arrowFunctionExpression([], hostElementDescriptor(branch))
2293
+ types.arrowFunctionExpression([], hostElementDescriptor(branch, descriptorBlocks))
1471
2294
  ),
1472
2295
  types.objectProperty(
1473
2296
  types.identifier("bindings"),
@@ -1496,7 +2319,9 @@ function hostConditionalBoundary(blockRuntime, plan) {
1496
2319
  attributes.push(
1497
2320
  types.jsxAttribute(
1498
2321
  types.jsxIdentifier("truthy"),
1499
- types.jsxExpressionContainer(hostConditionalBranchObject(plan.truthy, plan.truthyBindings))
2322
+ types.jsxExpressionContainer(
2323
+ hostConditionalBranchObject(plan.truthy, plan.truthyBindings, plan.descriptorBlocks)
2324
+ )
1500
2325
  )
1501
2326
  );
1502
2327
  }
@@ -1504,10 +2329,147 @@ function hostConditionalBoundary(blockRuntime, plan) {
1504
2329
  attributes.push(
1505
2330
  types.jsxAttribute(
1506
2331
  types.jsxIdentifier("falsy"),
1507
- types.jsxExpressionContainer(hostConditionalBranchObject(plan.falsy, plan.falsyBindings))
2332
+ types.jsxExpressionContainer(
2333
+ hostConditionalBranchObject(plan.falsy, plan.falsyBindings, plan.descriptorBlocks)
2334
+ )
2335
+ )
2336
+ );
2337
+ }
2338
+ return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
2339
+ }
2340
+ function conditionalRangeObject(range, descriptorBlocks = /* @__PURE__ */ new Map()) {
2341
+ const properties = [
2342
+ types.objectProperty(types.identifier("before"), types.numericLiteral(range.before)),
2343
+ types.objectProperty(
2344
+ types.identifier("test"),
2345
+ types.arrowFunctionExpression([], cloneExpression(range.test))
2346
+ )
2347
+ ];
2348
+ if (range.logical) {
2349
+ properties.push(types.objectProperty(types.identifier("logical"), types.booleanLiteral(true)));
2350
+ }
2351
+ if (range.truthy) {
2352
+ properties.push(
2353
+ types.objectProperty(
2354
+ types.identifier("truthy"),
2355
+ hostConditionalBranchObject(range.truthy, range.truthyBindings, descriptorBlocks)
2356
+ )
2357
+ );
2358
+ }
2359
+ if (range.falsy) {
2360
+ properties.push(
2361
+ types.objectProperty(
2362
+ types.identifier("falsy"),
2363
+ hostConditionalBranchObject(range.falsy, range.falsyBindings, descriptorBlocks)
1508
2364
  )
1509
2365
  );
1510
2366
  }
2367
+ return types.objectExpression(properties);
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
+ }
2435
+ function conditionalRangesBoundary(blockRuntime, plan) {
2436
+ const name = types.jsxMemberExpression(
2437
+ types.jsxIdentifier(blockRuntime.name),
2438
+ types.jsxIdentifier("ConditionalRanges")
2439
+ );
2440
+ const source = types.cloneNode(plan.source, true);
2441
+ const rootRefIndex = source.openingElement.attributes.findIndex(
2442
+ (attribute) => types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && attribute.name.name === "ref"
2443
+ );
2444
+ const rootRef = rootRefIndex >= 0 ? source.openingElement.attributes.splice(rootRefIndex, 1)[0] : void 0;
2445
+ const attributes = [
2446
+ types.jsxAttribute(types.jsxIdentifier("id"), types.jsxExpressionContainer(types.numericLiteral(plan.id))),
2447
+ types.jsxAttribute(
2448
+ types.jsxIdentifier("render"),
2449
+ types.jsxExpressionContainer(types.arrowFunctionExpression([], source))
2450
+ ),
2451
+ types.jsxAttribute(
2452
+ types.jsxIdentifier("ranges"),
2453
+ types.jsxExpressionContainer(
2454
+ types.arrayExpression(
2455
+ plan.ranges.map((range) => conditionalRangeObject(range, plan.descriptorBlocks))
2456
+ )
2457
+ )
2458
+ ),
2459
+ types.jsxAttribute(
2460
+ types.jsxIdentifier("trailing"),
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
+ )
2468
+ )
2469
+ ];
2470
+ if (rootRef?.value) {
2471
+ attributes.push(types.jsxAttribute(types.jsxIdentifier("rootRef"), types.cloneNode(rootRef.value, true)));
2472
+ }
1511
2473
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
1512
2474
  }
1513
2475
  function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allowedComponentNames) {
@@ -1675,6 +2637,41 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1675
2637
  }
1676
2638
  if (types.isJSXElement(child)) {
1677
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;
1678
2675
  const keyedRanges = insideConditional ? void 0 : analyzeKeyedRangesContainer(child, statesByValue, safeGlobals, listNames);
1679
2676
  if (keyedRanges) {
1680
2677
  plans.push({
@@ -1684,7 +2681,8 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1684
2681
  dependencies: keyedRanges.dependencies,
1685
2682
  source: child,
1686
2683
  ranges: keyedRanges.ranges,
1687
- trailing: keyedRanges.trailing
2684
+ trailing: keyedRanges.trailing,
2685
+ staticBindings: keyedRanges.staticBindings
1688
2686
  });
1689
2687
  for (const range of keyedRanges.ranges) {
1690
2688
  if (types.isJSXElement(range.source)) ownedElements.add(range.source);
@@ -1692,42 +2690,102 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1692
2690
  }
1693
2691
  continue;
1694
2692
  }
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
+ );
2704
+ if (conditionalRanges) {
2705
+ plans.push({
2706
+ kind: "conditional-ranges",
2707
+ id: conditionalRangeId,
2708
+ parent,
2709
+ dependencies: conditionalRanges.dependencies,
2710
+ source: child,
2711
+ ranges: conditionalRanges.ranges,
2712
+ trailing: conditionalRanges.trailing,
2713
+ staticBindings: conditionalRanges.staticBindings,
2714
+ descriptorBlocks: conditionalRanges.descriptorBlocks
2715
+ });
2716
+ plans.push(...conditionalRanges.nestedPlans || []);
2717
+ for (const range of conditionalRanges.ranges) {
2718
+ conditionalExpressions.add(range.source);
2719
+ }
2720
+ continue;
2721
+ }
2722
+ nextId = conditionalRangeStart;
2723
+ const hostConditionalStart = nextId;
2724
+ const hostConditionalId = nextId++;
1695
2725
  const hostConditional = analyzeHostConditionalContainer(
1696
2726
  child,
1697
2727
  statesByValue,
1698
- safeGlobals
2728
+ safeGlobals,
2729
+ listNames,
2730
+ hostConditionalId,
2731
+ () => nextId++
1699
2732
  );
1700
2733
  if (hostConditional) {
1701
2734
  plans.push({
1702
2735
  kind: "host-conditional",
1703
- id: nextId++,
2736
+ id: hostConditionalId,
1704
2737
  parent,
1705
2738
  source: child,
1706
2739
  ...hostConditional
1707
2740
  });
2741
+ plans.push(...hostConditional.nestedPlans || []);
1708
2742
  ownedElements.add(child);
1709
2743
  continue;
1710
2744
  }
2745
+ nextId = hostConditionalStart;
2746
+ const keyedRowsStart = nextId;
2747
+ const keyedRowsId = nextId++;
1711
2748
  const keyedRows = analyzeKeyedRowsContainer(child, statesByValue, safeGlobals, listNames);
1712
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
+ }
1713
2767
  plans.push({
1714
2768
  kind: "keyed-rows",
1715
- id: nextId++,
2769
+ id: keyedRowsId,
1716
2770
  parent,
1717
2771
  dependencies: keyedRows.dependencies,
1718
2772
  source: child,
1719
2773
  collection: keyedRows.collection,
2774
+ structureDependencies: keyedRows.structureDependencies,
1720
2775
  keyCallback: keyedRows.keyCallback,
1721
2776
  renderCallback: keyedRows.renderCallback,
1722
- row: keyedRows.row,
1723
- bindings: keyedRows.bindings,
2777
+ row: hostBlocks?.row || keyedRows.row,
2778
+ bindings: hostBlocks?.bindings || keyedRows.bindings,
1724
2779
  events: keyedRows.events,
1725
- conditionals: keyedRows.conditionals,
2780
+ conditionals: hostBlocks ? [] : keyedRows.conditionals,
2781
+ descriptorBlocks: hostBlocks?.descriptorBlocks,
1726
2782
  syntax: keyedRows.syntax
1727
2783
  });
2784
+ plans.push(...hostBlocks?.nestedPlans || []);
1728
2785
  ownedElements.add(child);
1729
2786
  continue;
1730
2787
  }
2788
+ nextId = keyedRowsStart;
1731
2789
  const nested = visitHost(child, parent, insideConditional);
1732
2790
  if (nested.reason) return { dependencies, reason: nested.reason };
1733
2791
  mergeDependencies(dependencies, nested.dependencies);
@@ -1824,6 +2882,46 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1824
2882
  }
1825
2883
  return { dependencies };
1826
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;
1827
2925
  const rootRanges = analyzeKeyedRangesContainer(root, statesByValue, safeGlobals, listNames, true);
1828
2926
  if (rootRanges) {
1829
2927
  plans.push({
@@ -1832,7 +2930,8 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1832
2930
  dependencies: rootRanges.dependencies,
1833
2931
  source: root,
1834
2932
  ranges: rootRanges.ranges,
1835
- trailing: rootRanges.trailing
2933
+ trailing: rootRanges.trailing,
2934
+ staticBindings: rootRanges.staticBindings
1836
2935
  });
1837
2936
  for (const range of rootRanges.ranges) {
1838
2937
  if (types.isJSXElement(range.source)) ownedElements.add(range.source);
@@ -1846,6 +2945,41 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
1846
2945
  keyedExpressions
1847
2946
  };
1848
2947
  }
2948
+ const rootConditionalRangeStart = nextId;
2949
+ const rootConditionalRangeId = nextId++;
2950
+ const rootConditionalRanges = analyzeConditionalRangesContainer(
2951
+ root,
2952
+ statesByValue,
2953
+ safeGlobals,
2954
+ true,
2955
+ listNames,
2956
+ rootConditionalRangeId,
2957
+ () => nextId++
2958
+ );
2959
+ if (rootConditionalRanges) {
2960
+ plans.push({
2961
+ kind: "conditional-ranges",
2962
+ id: rootConditionalRangeId,
2963
+ dependencies: rootConditionalRanges.dependencies,
2964
+ source: root,
2965
+ ranges: rootConditionalRanges.ranges,
2966
+ trailing: rootConditionalRanges.trailing,
2967
+ staticBindings: rootConditionalRanges.staticBindings,
2968
+ descriptorBlocks: rootConditionalRanges.descriptorBlocks
2969
+ });
2970
+ plans.push(...rootConditionalRanges.nestedPlans || []);
2971
+ for (const range of rootConditionalRanges.ranges) {
2972
+ conditionalExpressions.add(range.source);
2973
+ }
2974
+ return {
2975
+ plans,
2976
+ componentElements,
2977
+ conditionalExpressions,
2978
+ ownedElements,
2979
+ keyedExpressions
2980
+ };
2981
+ }
2982
+ nextId = rootConditionalRangeStart;
1849
2983
  const result = visitHost(root, void 0, false);
1850
2984
  if (result.reason) return { reason: result.reason };
1851
2985
  return {
@@ -1883,9 +3017,15 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
1883
3017
  plans.map((plan) => [plan.source, plan])
1884
3018
  );
1885
3019
  const rootPlan = planBySource.get(root);
3020
+ if (rootPlan?.kind === "mixed-ranges") {
3021
+ return mixedRangesBoundary(blockRuntime, rootPlan);
3022
+ }
1886
3023
  if (rootPlan?.kind === "keyed-ranges") {
1887
3024
  return keyedRangesBoundary(blockRuntime, rootPlan);
1888
3025
  }
3026
+ if (rootPlan?.kind === "conditional-ranges") {
3027
+ return conditionalRangesBoundary(blockRuntime, rootPlan);
3028
+ }
1889
3029
  const visit = (element) => {
1890
3030
  element.children = element.children.map((child) => {
1891
3031
  if (types.isJSXElement(child)) {
@@ -1893,12 +3033,18 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
1893
3033
  if (plan2?.kind === "host-conditional") {
1894
3034
  return hostConditionalBoundary(blockRuntime, plan2);
1895
3035
  }
3036
+ if (plan2?.kind === "mixed-ranges") {
3037
+ return mixedRangesBoundary(blockRuntime, plan2);
3038
+ }
1896
3039
  if (plan2?.kind === "keyed-rows") {
1897
3040
  return keyedRowsBoundary(blockRuntime, plan2);
1898
3041
  }
1899
3042
  if (plan2?.kind === "keyed-ranges") {
1900
3043
  return keyedRangesBoundary(blockRuntime, plan2);
1901
3044
  }
3045
+ if (plan2?.kind === "conditional-ranges") {
3046
+ return conditionalRangesBoundary(blockRuntime, plan2);
3047
+ }
1902
3048
  if (plan2?.kind === "component") {
1903
3049
  return componentIslandBoundary(blockRuntime, plan2, child);
1904
3050
  }
@@ -1939,6 +3085,31 @@ function assignStableBindingTargets(bindings) {
1939
3085
  binding.target = target;
1940
3086
  }
1941
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
+ }
1942
3113
  function lowerStableBindingTargets(root, bindings, blockRuntime, ownedElements) {
1943
3114
  const targetByPath = /* @__PURE__ */ new Map();
1944
3115
  for (const binding of bindings) {
@@ -2208,6 +3379,9 @@ function bindingObject(binding, propsParameter, stateParameter, statesByValue, s
2208
3379
  types.objectProperty(types.identifier("target"), types.numericLiteral(binding.target))
2209
3380
  );
2210
3381
  }
3382
+ if (binding.tracking) {
3383
+ properties.push(types.objectProperty(types.identifier("tracking"), types.stringLiteral(binding.tracking)));
3384
+ }
2211
3385
  if (binding.name)
2212
3386
  properties.push(types.objectProperty(types.identifier("name"), types.stringLiteral(binding.name)));
2213
3387
  properties.push(
@@ -2226,7 +3400,7 @@ function wrapperElement(definitionIdentifier, props) {
2226
3400
  const attributes = props ? [types.jsxSpreadAttribute(types.cloneNode(props))] : [];
2227
3401
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
2228
3402
  }
2229
- function compileCandidate(candidate, createComponentIdentifier, useStateNames, reactNames, listNames, moduleId) {
3403
+ function compileCandidate(candidate, createComponentIdentifier, useStateNames, reactNames, listNames, moduleId, reactivity) {
2230
3404
  const { path, name, statementPath } = candidate;
2231
3405
  if (path.node.async || path.node.generator)
2232
3406
  return "async and generator components are not supported";
@@ -2343,8 +3517,34 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2343
3517
  );
2344
3518
  const setterReason = validateSetterUsage(expandedRoot, statesBySetter);
2345
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
+ );
2346
3546
  const referencedComponentNames = /* @__PURE__ */ new Set();
2347
- traverse(expressionFile(types.cloneNode(expandedRoot, true)), {
3547
+ traverse(expressionFile(types.cloneNode(expandedReactiveRoot, true)), {
2348
3548
  JSXOpeningElement(componentPath) {
2349
3549
  const componentName = componentPath.node.name;
2350
3550
  if (types.isJSXIdentifier(componentName) && isComponentName(componentName.name)) {
@@ -2361,32 +3561,54 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2361
3561
  );
2362
3562
  })
2363
3563
  );
2364
- const blockAnalysis = analyzeComposableBlocks(
2365
- expandedRoot,
2366
- statesByValue,
3564
+ let blockAnalysis = analyzeComposableBlocks(
3565
+ expandedReactiveRoot,
3566
+ reactiveByValue,
2367
3567
  safeGlobals,
2368
3568
  listNames,
2369
3569
  allowedComponentNames
2370
3570
  );
2371
- if (blockAnalysis.reason) return blockAnalysis.reason;
2372
- const blockPlans = blockAnalysis.plans || [];
2373
- const analysis = analyzeHostTree(
2374
- expandedRoot,
2375
- statesByValue,
3571
+ let analysis = analyzeHostTree(
3572
+ expandedReactiveRoot,
3573
+ reactiveByValue,
2376
3574
  safeGlobals,
2377
3575
  blockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
2378
3576
  blockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
2379
3577
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
2380
3578
  blockAnalysis.componentElements || /* @__PURE__ */ new Set()
2381
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;
2382
3602
  if (analysis.reason) return analysis.reason;
3603
+ const blockPlans = blockAnalysis.plans || [];
3604
+ markShortCircuitBindings(analysis.bindings || []);
2383
3605
  assignStableBindingTargets(analysis.bindings || []);
2384
3606
  const stateParameter = path.scope.generateUidIdentifier("farmState");
2385
3607
  const blockParameter = path.scope.generateUidIdentifier("farmBlocks");
2386
3608
  const definitionIdentifier = path.scope.generateUidIdentifier(`${name}Compiled`);
2387
3609
  const propsParameter = propsPlan.definitionParameter;
2388
3610
  const rootWithTargets = lowerStableBindingTargets(
2389
- expandedRoot,
3611
+ expandedReactiveRoot,
2390
3612
  analysis.bindings || [],
2391
3613
  blockParameter,
2392
3614
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set()
@@ -2400,12 +3622,13 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2400
3622
  const rewrittenRoot = rewriteStateAccess(
2401
3623
  rootWithBlocks,
2402
3624
  stateParameter,
2403
- statesByValue,
3625
+ reactiveByValue,
2404
3626
  statesBySetter
2405
3627
  );
2406
3628
  const definition = types.callExpression(types.cloneNode(createComponentIdentifier), [
2407
3629
  types.objectExpression([
2408
3630
  types.objectProperty(types.identifier("displayName"), types.stringLiteral(name)),
3631
+ types.objectProperty(types.identifier("reactivity"), types.stringLiteral(reactivity)),
2409
3632
  types.spreadElement(
2410
3633
  types.conditionalExpression(
2411
3634
  types.memberExpression(
@@ -2416,7 +3639,9 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2416
3639
  types.objectProperty(types.identifier("hmrId"), types.stringLiteral(`${moduleId}#${name}`)),
2417
3640
  types.objectProperty(
2418
3641
  types.identifier("stateSignature"),
2419
- types.stringLiteral(String(states.length))
3642
+ types.stringLiteral(
3643
+ propBindings.length === 0 ? String(states.length) : `${states.length}:${propBindings.map((binding) => binding.localName).join(",")}`
3644
+ )
2420
3645
  )
2421
3646
  ]),
2422
3647
  types.objectExpression([])
@@ -2439,6 +3664,22 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2439
3664
  )
2440
3665
  )
2441
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
+ ] : [],
2442
3683
  types.objectProperty(
2443
3684
  types.identifier("render"),
2444
3685
  types.arrowFunctionExpression(
@@ -2458,7 +3699,7 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
2458
3699
  dependencies: block.dependencies
2459
3700
  }))
2460
3701
  ].map(
2461
- (binding) => bindingObject(binding, propsParameter, stateParameter, statesByValue, statesBySetter)
3702
+ (binding) => bindingObject(binding, propsParameter, stateParameter, reactiveByValue, statesBySetter)
2462
3703
  )
2463
3704
  )
2464
3705
  )
@@ -2552,7 +3793,8 @@ async function compileReactModule(code, id, options) {
2552
3793
  useStateNames,
2553
3794
  reactNames,
2554
3795
  listNames,
2555
- id
3796
+ id,
3797
+ options.reactivity
2556
3798
  );
2557
3799
  if (reason) {
2558
3800
  diagnostics.push({
@@ -2679,6 +3921,10 @@ function normalizeReactCompilerOptions(value) {
2679
3921
  if (!["fallback", "warn", "error"].includes(onUnsupported)) {
2680
3922
  throw new TypeError(`Unknown unsupported-component behavior: ${String(onUnsupported)}`);
2681
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
+ }
2682
3928
  if (options.report !== void 0 && typeof options.report !== "boolean") {
2683
3929
  throw new TypeError("The React compiler report option must be a boolean.");
2684
3930
  }
@@ -2689,7 +3935,7 @@ function normalizeReactCompilerOptions(value) {
2689
3935
  }
2690
3936
  const reportFile = normalizeCompilerReportFile(options.reportFile);
2691
3937
  const report = options.report === true || options.reportFile !== void 0;
2692
- return { mode, directive, onUnsupported, report, reportFile };
3938
+ return { mode, directive, onUnsupported, reactivity, report, reportFile };
2693
3939
  }
2694
3940
 
2695
3941
  // src/vite.ts
@@ -2705,7 +3951,7 @@ function readCompilerOptions(rendererOptions) {
2705
3951
  );
2706
3952
  }
2707
3953
  const normalized = compiler;
2708
- 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")) {
2709
3955
  return normalized;
2710
3956
  }
2711
3957
  return normalizeReactCompilerOptions(compiler);