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

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
@@ -66,14 +66,46 @@ var COMPILER_RUNTIME_FEATURE_EXPORTS = {
66
66
  "conditional-ranges": "conditionalRangesRuntimeFeature",
67
67
  "keyed-list": "keyedListRuntimeFeature",
68
68
  "keyed-rows": "keyedRowsRuntimeFeature",
69
+ "keyed-rows-hinted": "keyedRowsHintedRuntimeFeature",
70
+ "keyed-rows-position-hinted": "keyedRowsPositionHintedRuntimeFeature",
71
+ "keyed-rows-reorder-hinted": "keyedRowsReorderHintedRuntimeFeature",
72
+ "keyed-rows-all-hinted": "keyedRowsAllHintedRuntimeFeature",
73
+ "keyed-rows-every-hinted": "keyedRowsEveryHintedRuntimeFeature",
74
+ "keyed-rows-filter-hinted": "keyedRowsFilterHintedRuntimeFeature",
75
+ "keyed-rows-prepend-hinted": "keyedRowsPrependHintedRuntimeFeature",
76
+ "keyed-rows-filter-prepend-hinted": "keyedRowsFilterPrependHintedRuntimeFeature",
69
77
  "keyed-rows-conditional": "keyedRowsConditionalRuntimeFeature",
78
+ "keyed-rows-conditional-hinted": "keyedRowsConditionalHintedRuntimeFeature",
79
+ "keyed-rows-conditional-position-hinted": "keyedRowsConditionalPositionHintedRuntimeFeature",
80
+ "keyed-rows-conditional-reorder-hinted": "keyedRowsConditionalReorderHintedRuntimeFeature",
81
+ "keyed-rows-conditional-all-hinted": "keyedRowsConditionalAllHintedRuntimeFeature",
82
+ "keyed-rows-conditional-every-hinted": "keyedRowsConditionalEveryHintedRuntimeFeature",
83
+ "keyed-rows-conditional-filter-hinted": "keyedRowsConditionalFilterHintedRuntimeFeature",
84
+ "keyed-rows-conditional-prepend-hinted": "keyedRowsConditionalPrependHintedRuntimeFeature",
85
+ "keyed-rows-conditional-filter-prepend-hinted": "keyedRowsConditionalFilterPrependHintedRuntimeFeature",
70
86
  "keyed-rows-host": "keyedRowsHostRuntimeFeature",
87
+ "keyed-rows-host-hinted": "keyedRowsHostHintedRuntimeFeature",
88
+ "keyed-rows-host-position-hinted": "keyedRowsHostPositionHintedRuntimeFeature",
89
+ "keyed-rows-host-reorder-hinted": "keyedRowsHostReorderHintedRuntimeFeature",
90
+ "keyed-rows-host-all-hinted": "keyedRowsHostAllHintedRuntimeFeature",
91
+ "keyed-rows-host-every-hinted": "keyedRowsHostEveryHintedRuntimeFeature",
92
+ "keyed-rows-host-filter-hinted": "keyedRowsHostFilterHintedRuntimeFeature",
93
+ "keyed-rows-host-prepend-hinted": "keyedRowsHostPrependHintedRuntimeFeature",
94
+ "keyed-rows-host-filter-prepend-hinted": "keyedRowsHostFilterPrependHintedRuntimeFeature",
71
95
  "keyed-rows-complete": "keyedRowsCompleteRuntimeFeature",
96
+ "keyed-rows-complete-hinted": "keyedRowsCompleteHintedRuntimeFeature",
97
+ "keyed-rows-complete-position-hinted": "keyedRowsCompletePositionHintedRuntimeFeature",
98
+ "keyed-rows-complete-reorder-hinted": "keyedRowsCompleteReorderHintedRuntimeFeature",
99
+ "keyed-rows-complete-all-hinted": "keyedRowsCompleteAllHintedRuntimeFeature",
100
+ "keyed-rows-complete-every-hinted": "keyedRowsCompleteEveryHintedRuntimeFeature",
101
+ "keyed-rows-complete-filter-hinted": "keyedRowsCompleteFilterHintedRuntimeFeature",
102
+ "keyed-rows-complete-prepend-hinted": "keyedRowsCompletePrependHintedRuntimeFeature",
103
+ "keyed-rows-complete-filter-prepend-hinted": "keyedRowsCompleteFilterPrependHintedRuntimeFeature",
72
104
  "keyed-ranges": "keyedRangesRuntimeFeature",
73
105
  "mixed-ranges": "mixedRangesRuntimeFeature",
74
106
  component: "componentRuntimeFeature"
75
107
  };
76
- function runtimeFeaturesForPlans(plans) {
108
+ function runtimeFeaturesForPlans(plans, keyedMapUpdateHints, keyedArrayFilterHints, keyedArrayPrependHints, keyedArrayPositionHints, keyedArrayReorderHints, keyedArrayRollingWindowHints) {
77
109
  const features = /* @__PURE__ */ new Set();
78
110
  let keyedRowsHaveConditionals = false;
79
111
  let keyedRowsHaveHostBlocks = false;
@@ -88,9 +120,10 @@ function runtimeFeaturesForPlans(plans) {
88
120
  }
89
121
  }
90
122
  if (plans.some((plan) => plan.kind === "keyed-rows")) {
91
- features.add(
92
- keyedRowsHaveConditionals && keyedRowsHaveHostBlocks ? "keyed-rows-complete" : keyedRowsHaveConditionals ? "keyed-rows-conditional" : keyedRowsHaveHostBlocks ? "keyed-rows-host" : "keyed-rows"
93
- );
123
+ const keyedRowsFeature = keyedRowsHaveConditionals && keyedRowsHaveHostBlocks ? "keyed-rows-complete" : keyedRowsHaveConditionals ? "keyed-rows-conditional" : keyedRowsHaveHostBlocks ? "keyed-rows-host" : "keyed-rows";
124
+ const arrayRangeHints = keyedArrayRollingWindowHints || keyedArrayFilterHints || keyedArrayPrependHints;
125
+ const hintSuffix = keyedArrayPositionHints && (arrayRangeHints || keyedArrayReorderHints) || keyedArrayReorderHints && arrayRangeHints ? "-every-hinted" : keyedArrayRollingWindowHints ? "-all-hinted" : keyedArrayPositionHints ? "-position-hinted" : keyedArrayReorderHints ? "-reorder-hinted" : keyedArrayFilterHints && keyedArrayPrependHints ? "-filter-prepend-hinted" : keyedArrayFilterHints ? "-filter-hinted" : keyedArrayPrependHints ? "-prepend-hinted" : keyedMapUpdateHints ? "-hinted" : "";
126
+ features.add(`${keyedRowsFeature}${hintSuffix}`);
94
127
  }
95
128
  return [...features].sort();
96
129
  }
@@ -139,6 +172,174 @@ function referencesIdentifier(expression, name) {
139
172
  });
140
173
  return referenced;
141
174
  }
175
+ function rewriteFreeIdentifierNames(expression, replacements) {
176
+ if (replacements.size === 0) return cloneExpression(expression);
177
+ const file = expressionFile(cloneExpression(expression));
178
+ traverse(file, {
179
+ ReferencedIdentifier(path) {
180
+ if (path.scope.hasBinding(path.node.name)) return;
181
+ const replacement = replacements.get(path.node.name);
182
+ if (!replacement) return;
183
+ path.replaceWith(types.cloneNode(replacement));
184
+ path.skip();
185
+ }
186
+ });
187
+ return file.program.body[0].expression;
188
+ }
189
+ function keyedIdentityTarget(binding, keyCallback, renderCallback, reactiveByValue, structureDependencies) {
190
+ const dependencies = binding.dependencies || [];
191
+ if (dependencies.length !== 1 || structureDependencies.has(dependencies[0])) return void 0;
192
+ const keyExpression = returnedExpression(keyCallback);
193
+ const keyItem = keyCallback.params[0];
194
+ const rowItem = renderCallback.params[0];
195
+ if (!keyExpression || !types.isIdentifier(keyItem) || !types.isIdentifier(rowItem)) return void 0;
196
+ const replacements = /* @__PURE__ */ new Map([[keyItem.name, rowItem]]);
197
+ const keyIndex = keyCallback.params[1];
198
+ const rowIndex = renderCallback.params[1];
199
+ if (types.isIdentifier(keyIndex) && types.isIdentifier(rowIndex)) {
200
+ replacements.set(keyIndex.name, rowIndex);
201
+ }
202
+ const rowKeyExpression = rewriteFreeIdentifierNames(keyExpression, replacements);
203
+ const file = expressionFile(cloneExpression(binding.value));
204
+ let target;
205
+ let valid = true;
206
+ let comparisons = 0;
207
+ traverse(file, {
208
+ ReferencedIdentifier(path) {
209
+ if (!valid || path.scope.hasBinding(path.node.name)) return;
210
+ const reactive = reactiveByValue.get(path.node.name);
211
+ if (!reactive) return;
212
+ if (reactive.index !== dependencies[0] || target && target !== reactive) {
213
+ valid = false;
214
+ path.stop();
215
+ return;
216
+ }
217
+ const parent = path.parentPath;
218
+ if (!parent.isBinaryExpression() || parent.node.operator !== "===" && parent.node.operator !== "!==") {
219
+ valid = false;
220
+ path.stop();
221
+ return;
222
+ }
223
+ const other = parent.node.left === path.node ? parent.node.right : parent.node.left;
224
+ if (!types.isExpression(other) || !types.isNodesEquivalent(other, rowKeyExpression)) {
225
+ valid = false;
226
+ path.stop();
227
+ return;
228
+ }
229
+ target = reactive;
230
+ comparisons += 1;
231
+ }
232
+ });
233
+ return valid && target && comparisons > 0 ? { dependency: target.index, value: types.identifier(target.valueName) } : void 0;
234
+ }
235
+ function isPotentialKeyedMembershipCall(expression, reactiveByValue) {
236
+ if (expression.arguments.length !== 1 || !types.isExpression(expression.arguments[0]) || !types.isMemberExpression(expression.callee) || expression.callee.computed || !types.isIdentifier(expression.callee.object) || !types.isIdentifier(expression.callee.property, { name: "has" })) {
237
+ return false;
238
+ }
239
+ const reactive = reactiveByValue.get(expression.callee.object.name);
240
+ return Boolean(reactive?.setterName);
241
+ }
242
+ function containsPotentialKeyedMembershipCall(expression, reactiveByValue) {
243
+ let found = false;
244
+ traverse(expressionFile(cloneExpression(expression)), {
245
+ CallExpression(path) {
246
+ if (!isPotentialKeyedMembershipCall(path.node, reactiveByValue)) return;
247
+ found = true;
248
+ path.stop();
249
+ }
250
+ });
251
+ return found;
252
+ }
253
+ function isPotentialKeyedMapLookupCall(expression, reactiveByValue) {
254
+ if (expression.arguments.length !== 1 || !types.isExpression(expression.arguments[0]) || !types.isMemberExpression(expression.callee) || expression.callee.computed || !types.isIdentifier(expression.callee.object) || !types.isIdentifier(expression.callee.property, { name: "get" })) {
255
+ return false;
256
+ }
257
+ const reactive = reactiveByValue.get(expression.callee.object.name);
258
+ return Boolean(reactive?.setterName);
259
+ }
260
+ function containsPotentialKeyedMapLookupCall(expression, reactiveByValue) {
261
+ let found = false;
262
+ traverse(expressionFile(cloneExpression(expression)), {
263
+ CallExpression(path) {
264
+ if (!isPotentialKeyedMapLookupCall(path.node, reactiveByValue)) return;
265
+ found = true;
266
+ path.stop();
267
+ }
268
+ });
269
+ return found;
270
+ }
271
+ function keyedMembershipTarget(binding, keyCallback, renderCallback, reactiveByValue, structureDependencies) {
272
+ const dependencies = binding.dependencies || [];
273
+ if (dependencies.length !== 1 || structureDependencies.has(dependencies[0])) return void 0;
274
+ const keyExpression = returnedExpression(keyCallback);
275
+ const keyItem = keyCallback.params[0];
276
+ const rowItem = renderCallback.params[0];
277
+ if (!keyExpression || !types.isIdentifier(keyItem) || !types.isIdentifier(rowItem)) return void 0;
278
+ const replacements = /* @__PURE__ */ new Map([[keyItem.name, rowItem]]);
279
+ const keyIndex = keyCallback.params[1];
280
+ const rowIndex = renderCallback.params[1];
281
+ if (types.isIdentifier(keyIndex) && types.isIdentifier(rowIndex)) {
282
+ replacements.set(keyIndex.name, rowIndex);
283
+ }
284
+ const rowKeyExpression = rewriteFreeIdentifierNames(keyExpression, replacements);
285
+ const file = expressionFile(cloneExpression(binding.value));
286
+ let target;
287
+ let valid = true;
288
+ let calls = 0;
289
+ traverse(file, {
290
+ ReferencedIdentifier(path) {
291
+ if (!valid || path.scope.hasBinding(path.node.name)) return;
292
+ const reactive = reactiveByValue.get(path.node.name);
293
+ if (!reactive) return;
294
+ const member = path.parentPath;
295
+ const call = member.parentPath;
296
+ if (!reactive.setterName || reactive.index !== dependencies[0] || target && target !== reactive || !member.isMemberExpression() || member.node.object !== path.node || member.node.computed || !types.isIdentifier(member.node.property, { name: "has" }) || !call || !call.isCallExpression() || call.node.callee !== member.node || call.node.arguments.length !== 1 || !types.isExpression(call.node.arguments[0]) || !types.isNodesEquivalent(call.node.arguments[0], rowKeyExpression)) {
297
+ valid = false;
298
+ path.stop();
299
+ return;
300
+ }
301
+ target = reactive;
302
+ calls += 1;
303
+ }
304
+ });
305
+ return valid && target && calls > 0 ? { dependency: target.index, value: types.identifier(target.valueName) } : void 0;
306
+ }
307
+ function keyedMapLookupTarget(binding, keyCallback, renderCallback, reactiveByValue, structureDependencies) {
308
+ const dependencies = binding.dependencies || [];
309
+ if (dependencies.length !== 1 || structureDependencies.has(dependencies[0])) return void 0;
310
+ const keyExpression = returnedExpression(keyCallback);
311
+ const keyItem = keyCallback.params[0];
312
+ const rowItem = renderCallback.params[0];
313
+ if (!keyExpression || !types.isIdentifier(keyItem) || !types.isIdentifier(rowItem)) return void 0;
314
+ const replacements = /* @__PURE__ */ new Map([[keyItem.name, rowItem]]);
315
+ const keyIndex = keyCallback.params[1];
316
+ const rowIndex = renderCallback.params[1];
317
+ if (types.isIdentifier(keyIndex) && types.isIdentifier(rowIndex)) {
318
+ replacements.set(keyIndex.name, rowIndex);
319
+ }
320
+ const rowKeyExpression = rewriteFreeIdentifierNames(keyExpression, replacements);
321
+ const file = expressionFile(cloneExpression(binding.value));
322
+ let target;
323
+ let valid = true;
324
+ let calls = 0;
325
+ traverse(file, {
326
+ ReferencedIdentifier(path) {
327
+ if (!valid || path.scope.hasBinding(path.node.name)) return;
328
+ const reactive = reactiveByValue.get(path.node.name);
329
+ if (!reactive) return;
330
+ const member = path.parentPath;
331
+ const call = member.parentPath;
332
+ if (!reactive.setterName || reactive.index !== dependencies[0] || target && target !== reactive || !member.isMemberExpression() || member.node.object !== path.node || member.node.computed || !types.isIdentifier(member.node.property, { name: "get" }) || !call || !call.isCallExpression() || call.node.callee !== member.node || call.node.arguments.length !== 1 || !types.isExpression(call.node.arguments[0]) || !types.isNodesEquivalent(call.node.arguments[0], rowKeyExpression)) {
333
+ valid = false;
334
+ path.stop();
335
+ return;
336
+ }
337
+ target = reactive;
338
+ calls += 1;
339
+ }
340
+ });
341
+ return valid && target && calls > 0 ? { dependency: target.index, value: types.identifier(target.valueName) } : void 0;
342
+ }
142
343
  function collectReferencedLocals(expression, names) {
143
344
  const references = /* @__PURE__ */ new Set();
144
345
  traverse(expressionFile(cloneExpression(expression)), {
@@ -239,7 +440,9 @@ function rewriteHandlerAccess(root, handlersByName) {
239
440
  path.skip();
240
441
  }
241
442
  });
242
- return reason ? { reason } : { root: file.program.body[0].expression };
443
+ return reason ? { reason } : {
444
+ root: file.program.body[0].expression
445
+ };
243
446
  }
244
447
  function isSafeCompilerCall(expression, safeGlobals) {
245
448
  if (types.isIdentifier(expression.callee)) {
@@ -247,7 +450,7 @@ function isSafeCompilerCall(expression, safeGlobals) {
247
450
  }
248
451
  return types.isMemberExpression(expression.callee) && !expression.callee.computed && types.isIdentifier(expression.callee.object, { name: "Math" }) && safeGlobals.has("Math") && types.isIdentifier(expression.callee.property) && SAFE_MATH_CALLS.has(expression.callee.property.name);
249
452
  }
250
- function validateDerivedExpression(expression, safeGlobals) {
453
+ function validateDerivedExpression(expression, safeGlobals, additionalSafeCall) {
251
454
  let unsupported;
252
455
  const reject = (reason, path) => {
253
456
  unsupported = reason;
@@ -258,7 +461,9 @@ function validateDerivedExpression(expression, safeGlobals) {
258
461
  AssignmentExpression: (path) => reject("assignments", path),
259
462
  AwaitExpression: (path) => reject("await expressions", path),
260
463
  CallExpression(path) {
261
- if (!isSafeCompilerCall(path.node, safeGlobals)) reject("function calls", path);
464
+ if (!isSafeCompilerCall(path.node, safeGlobals) && !additionalSafeCall?.(path.node)) {
465
+ reject("function calls", path);
466
+ }
262
467
  },
263
468
  ClassExpression: (path) => reject("class expressions", path),
264
469
  Function: (path) => reject("function expressions", path),
@@ -298,6 +503,941 @@ function validateSetterUsage(root, statesBySetter) {
298
503
  });
299
504
  return reason;
300
505
  }
506
+ function isUnchangedMapItem(expression, item) {
507
+ return types.isIdentifier(expression, { name: item.name });
508
+ }
509
+ function isSafeKeyedMapReplacement(expression, item, safeGlobals) {
510
+ if (!types.isObjectExpression(expression)) return false;
511
+ let spreadsItem = false;
512
+ for (const property of expression.properties) {
513
+ if (types.isSpreadElement(property)) {
514
+ if (!types.isIdentifier(property.argument, { name: item.name })) return false;
515
+ spreadsItem = true;
516
+ continue;
517
+ }
518
+ if (!types.isObjectProperty(property) || property.computed || !types.isExpression(property.value)) {
519
+ return false;
520
+ }
521
+ if (validateDerivedExpression(property.value, safeGlobals)) return false;
522
+ }
523
+ return spreadsItem;
524
+ }
525
+ function isSafeKeyedMapResult(expression, item, safeGlobals) {
526
+ if (!types.isConditionalExpression(expression)) return false;
527
+ if (validateDerivedExpression(expression.test, safeGlobals)) return false;
528
+ return isUnchangedMapItem(expression.consequent, item) && isSafeKeyedMapReplacement(expression.alternate, item, safeGlobals) || isUnchangedMapItem(expression.alternate, item) && isSafeKeyedMapReplacement(expression.consequent, item, safeGlobals);
529
+ }
530
+ function rewriteKeyedMapUpdateHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
531
+ if (hintedStateIndices.size === 0) return { root: types.cloneNode(root, true), count: 0 };
532
+ const file = expressionFile(types.cloneNode(root, true));
533
+ let count = 0;
534
+ traverse(file, {
535
+ CallExpression(path) {
536
+ const callee = path.get("callee");
537
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
538
+ const state = statesBySetter.get(callee.node.name);
539
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
540
+ return;
541
+ }
542
+ const updater = path.node.arguments[0];
543
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isCallExpression(updater.body) || !types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !types.isIdentifier(updater.body.callee.object, {
544
+ name: updater.params[0].name
545
+ }) || !types.isIdentifier(updater.body.callee.property, { name: "map" })) {
546
+ return;
547
+ }
548
+ const callback = updater.body.arguments[0];
549
+ if (!types.isArrowFunctionExpression(callback) || callback.async || callback.generator || callback.params.length === 0 || callback.params.length > 3 || callback.params.some((parameter) => !types.isIdentifier(parameter)) || !types.isExpression(callback.body)) {
550
+ return;
551
+ }
552
+ const item = callback.params[0];
553
+ if (!isSafeKeyedMapResult(callback.body, item, safeGlobals)) return;
554
+ const changedIndices = path.scope.generateUidIdentifier("farmChangedIndices");
555
+ const mappedItem = path.scope.generateUidIdentifier("farmMappedItem");
556
+ const nextValue = path.scope.generateUidIdentifier("farmNextItems");
557
+ const index = types.isIdentifier(callback.params[1]) ? types.cloneNode(callback.params[1]) : path.scope.generateUidIdentifier("farmMapIndex");
558
+ const wrappedCallback = types.cloneNode(callback, true);
559
+ if (wrappedCallback.params.length === 1) wrappedCallback.params.push(types.cloneNode(index));
560
+ wrappedCallback.body = types.blockStatement([
561
+ types.variableDeclaration("const", [
562
+ types.variableDeclarator(types.cloneNode(mappedItem), types.cloneNode(callback.body, true))
563
+ ]),
564
+ types.ifStatement(
565
+ types.binaryExpression("!==", types.cloneNode(mappedItem), types.cloneNode(item)),
566
+ types.expressionStatement(
567
+ types.callExpression(
568
+ types.memberExpression(types.cloneNode(changedIndices), types.identifier("push")),
569
+ [types.cloneNode(index)]
570
+ )
571
+ )
572
+ ),
573
+ types.returnStatement(types.cloneNode(mappedItem))
574
+ ]);
575
+ const mapCall = types.cloneNode(updater.body, true);
576
+ mapCall.arguments[0] = wrappedCallback;
577
+ const previous = types.cloneNode(updater.params[0]);
578
+ path.node.arguments[0] = types.arrowFunctionExpression(
579
+ [types.cloneNode(previous)],
580
+ types.blockStatement([
581
+ types.variableDeclaration("const", [
582
+ types.variableDeclarator(types.cloneNode(changedIndices), types.arrayExpression([]))
583
+ ]),
584
+ types.variableDeclaration("const", [types.variableDeclarator(types.cloneNode(nextValue), mapCall)]),
585
+ types.returnStatement(
586
+ types.callExpression(types.cloneNode(helperIdentifier), [
587
+ types.cloneNode(previous),
588
+ types.cloneNode(nextValue),
589
+ types.cloneNode(changedIndices)
590
+ ])
591
+ )
592
+ ])
593
+ );
594
+ count += 1;
595
+ path.skip();
596
+ }
597
+ });
598
+ return {
599
+ root: file.program.body[0].expression,
600
+ count
601
+ };
602
+ }
603
+ function rewriteKeyedArrayAppendHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
604
+ if (hintedStateIndices.size === 0) return { root: types.cloneNode(root, true), count: 0 };
605
+ const file = expressionFile(types.cloneNode(root, true));
606
+ let count = 0;
607
+ traverse(file, {
608
+ CallExpression(path) {
609
+ const callee = path.get("callee");
610
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
611
+ const state = statesBySetter.get(callee.node.name);
612
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
613
+ return;
614
+ }
615
+ const updater = path.node.arguments[0];
616
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isArrayExpression(updater.body) || updater.body.elements.length < 2 || updater.body.elements.some((element) => element === null) || !types.isSpreadElement(updater.body.elements[0]) || !types.isIdentifier(updater.body.elements[0].argument, {
617
+ name: updater.params[0].name
618
+ })) {
619
+ return;
620
+ }
621
+ const safeAppendValue = (value) => {
622
+ if (types.isArrayExpression(value)) {
623
+ return value.elements.every(
624
+ (element) => element !== null && !types.isJSXNamespacedName(element) && !types.isArgumentPlaceholder(element) && safeAppendValue(types.isSpreadElement(element) ? element.argument : element)
625
+ );
626
+ }
627
+ if (types.isObjectExpression(value)) {
628
+ return value.properties.every((property) => {
629
+ if (types.isSpreadElement(property)) return safeAppendValue(property.argument);
630
+ if (!types.isObjectProperty(property) || !types.isExpression(property.value)) return false;
631
+ return (!property.computed || types.isExpression(property.key) && safeAppendValue(property.key)) && safeAppendValue(property.value);
632
+ });
633
+ }
634
+ return validateDerivedExpression(value, safeGlobals) === void 0;
635
+ };
636
+ if (updater.body.elements.slice(1).some((element) => {
637
+ if (!element || types.isJSXNamespacedName(element) || types.isArgumentPlaceholder(element)) {
638
+ return true;
639
+ }
640
+ return !safeAppendValue(types.isSpreadElement(element) ? element.argument : element);
641
+ })) {
642
+ return;
643
+ }
644
+ const previous = types.cloneNode(updater.params[0]);
645
+ const nextValue = path.scope.generateUidIdentifier("farmNextItems");
646
+ path.node.arguments[0] = types.arrowFunctionExpression(
647
+ [types.cloneNode(previous)],
648
+ types.blockStatement([
649
+ types.variableDeclaration("const", [
650
+ types.variableDeclarator(types.cloneNode(nextValue), types.cloneNode(updater.body, true))
651
+ ]),
652
+ types.returnStatement(
653
+ types.callExpression(types.cloneNode(helperIdentifier), [
654
+ types.cloneNode(previous),
655
+ types.cloneNode(nextValue)
656
+ ])
657
+ )
658
+ ])
659
+ );
660
+ count += 1;
661
+ path.skip();
662
+ }
663
+ });
664
+ return {
665
+ root: file.program.body[0].expression,
666
+ count
667
+ };
668
+ }
669
+ function rewriteKeyedArrayPrependHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
670
+ if (hintedStateIndices.size === 0) {
671
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
672
+ }
673
+ const file = expressionFile(types.cloneNode(root, true));
674
+ const stateIndices = /* @__PURE__ */ new Set();
675
+ let count = 0;
676
+ traverse(file, {
677
+ CallExpression(path) {
678
+ const callee = path.get("callee");
679
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
680
+ const state = statesBySetter.get(callee.node.name);
681
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
682
+ return;
683
+ }
684
+ const updater = path.node.arguments[0];
685
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isArrayExpression(updater.body) || updater.body.elements.length < 2 || updater.body.elements.some((element) => element === null)) {
686
+ return;
687
+ }
688
+ const suffix = updater.body.elements[updater.body.elements.length - 1];
689
+ if (!types.isSpreadElement(suffix) || !types.isIdentifier(suffix.argument, { name: updater.params[0].name })) {
690
+ return;
691
+ }
692
+ const safePrependValue = (value) => {
693
+ if (types.isArrayExpression(value)) {
694
+ return value.elements.every(
695
+ (element) => element !== null && !types.isJSXNamespacedName(element) && !types.isArgumentPlaceholder(element) && safePrependValue(types.isSpreadElement(element) ? element.argument : element)
696
+ );
697
+ }
698
+ if (types.isObjectExpression(value)) {
699
+ return value.properties.every((property) => {
700
+ if (types.isSpreadElement(property)) return safePrependValue(property.argument);
701
+ if (!types.isObjectProperty(property) || !types.isExpression(property.value)) return false;
702
+ return (!property.computed || types.isExpression(property.key) && safePrependValue(property.key)) && safePrependValue(property.value);
703
+ });
704
+ }
705
+ return validateDerivedExpression(value, safeGlobals) === void 0;
706
+ };
707
+ if (updater.body.elements.slice(0, -1).some((element) => {
708
+ if (!element || types.isJSXNamespacedName(element) || types.isArgumentPlaceholder(element)) {
709
+ return true;
710
+ }
711
+ return !safePrependValue(types.isSpreadElement(element) ? element.argument : element);
712
+ })) {
713
+ return;
714
+ }
715
+ const previous = types.cloneNode(updater.params[0]);
716
+ const nextValue = path.scope.generateUidIdentifier("farmNextItems");
717
+ path.node.arguments[0] = types.arrowFunctionExpression(
718
+ [types.cloneNode(previous)],
719
+ types.blockStatement([
720
+ types.variableDeclaration("const", [
721
+ types.variableDeclarator(types.cloneNode(nextValue), types.cloneNode(updater.body, true))
722
+ ]),
723
+ types.returnStatement(
724
+ types.callExpression(types.cloneNode(helperIdentifier), [
725
+ types.cloneNode(previous),
726
+ types.cloneNode(nextValue)
727
+ ])
728
+ )
729
+ ])
730
+ );
731
+ count += 1;
732
+ stateIndices.add(state.index);
733
+ path.skip();
734
+ }
735
+ });
736
+ return {
737
+ root: file.program.body[0].expression,
738
+ count,
739
+ stateIndices
740
+ };
741
+ }
742
+ function rewriteKeyedArrayRollingWindowHints(root, hintedStateIndices, statesBySetter, sliceHelperIdentifier, rollingWindowHelperIdentifier, safeGlobals) {
743
+ if (hintedStateIndices.size === 0) {
744
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
745
+ }
746
+ const file = expressionFile(types.cloneNode(root, true));
747
+ const stateIndices = /* @__PURE__ */ new Set();
748
+ let count = 0;
749
+ traverse(file, {
750
+ CallExpression(path) {
751
+ const callee = path.get("callee");
752
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
753
+ const state = statesBySetter.get(callee.node.name);
754
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
755
+ return;
756
+ }
757
+ const updater = path.node.arguments[0];
758
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isArrayExpression(updater.body) || updater.body.elements.length < 2 || updater.body.elements.some((element) => element === null)) {
759
+ return;
760
+ }
761
+ const retainedSpread = updater.body.elements[0];
762
+ if (!types.isSpreadElement(retainedSpread) || !types.isCallExpression(retainedSpread.argument) || retainedSpread.argument.arguments.length !== 1 || !types.isMemberExpression(retainedSpread.argument.callee) || retainedSpread.argument.callee.computed || !types.isIdentifier(retainedSpread.argument.callee.object, {
763
+ name: updater.params[0].name
764
+ }) || !types.isIdentifier(retainedSpread.argument.callee.property, { name: "slice" }) || !types.isExpression(retainedSpread.argument.arguments[0])) {
765
+ return;
766
+ }
767
+ const retainedStart = staticSliceIndex(retainedSpread.argument.arguments[0]);
768
+ if (retainedStart === void 0 || retainedStart === 0) return;
769
+ const safeIncomingValue = (value) => {
770
+ if (types.isArrayExpression(value)) {
771
+ return value.elements.every(
772
+ (element) => element !== null && !types.isJSXNamespacedName(element) && !types.isArgumentPlaceholder(element) && safeIncomingValue(types.isSpreadElement(element) ? element.argument : element)
773
+ );
774
+ }
775
+ if (types.isObjectExpression(value)) {
776
+ return value.properties.every((property) => {
777
+ if (types.isSpreadElement(property)) return safeIncomingValue(property.argument);
778
+ if (!types.isObjectProperty(property) || !types.isExpression(property.value)) return false;
779
+ return (!property.computed || types.isExpression(property.key) && safeIncomingValue(property.key)) && safeIncomingValue(property.value);
780
+ });
781
+ }
782
+ return validateDerivedExpression(value, safeGlobals) === void 0;
783
+ };
784
+ if (updater.body.elements.slice(1).some((element) => {
785
+ if (!element || types.isJSXNamespacedName(element) || types.isArgumentPlaceholder(element)) {
786
+ return true;
787
+ }
788
+ return !safeIncomingValue(types.isSpreadElement(element) ? element.argument : element);
789
+ })) {
790
+ return;
791
+ }
792
+ const previous = types.cloneNode(updater.params[0]);
793
+ const sliceMethod = path.scope.generateUidIdentifier("farmSlice");
794
+ const retained = path.scope.generateUidIdentifier("farmRetainedItems");
795
+ const nextValue = path.scope.generateUidIdentifier("farmNextItems");
796
+ const nextElements = types.cloneNode(updater.body, true).elements;
797
+ nextElements[0] = types.spreadElement(types.cloneNode(retained));
798
+ path.node.arguments[0] = types.arrowFunctionExpression(
799
+ [types.cloneNode(previous)],
800
+ types.blockStatement([
801
+ types.variableDeclaration("const", [
802
+ types.variableDeclarator(
803
+ types.cloneNode(sliceMethod),
804
+ types.memberExpression(types.cloneNode(previous), types.identifier("slice"))
805
+ ),
806
+ types.variableDeclarator(
807
+ types.cloneNode(retained),
808
+ types.callExpression(types.cloneNode(sliceHelperIdentifier), [
809
+ types.cloneNode(previous),
810
+ types.cloneNode(sliceMethod),
811
+ types.numericLiteral(retainedStart)
812
+ ])
813
+ ),
814
+ types.variableDeclarator(types.cloneNode(nextValue), types.arrayExpression(nextElements))
815
+ ]),
816
+ types.returnStatement(
817
+ types.callExpression(types.cloneNode(rollingWindowHelperIdentifier), [
818
+ types.cloneNode(previous),
819
+ types.cloneNode(retained),
820
+ types.cloneNode(nextValue)
821
+ ])
822
+ )
823
+ ])
824
+ );
825
+ count += 1;
826
+ stateIndices.add(state.index);
827
+ path.skip();
828
+ }
829
+ });
830
+ return {
831
+ root: file.program.body[0].expression,
832
+ count,
833
+ stateIndices
834
+ };
835
+ }
836
+ function staticSliceIndex(expression) {
837
+ if (types.isNumericLiteral(expression) && Number.isSafeInteger(expression.value)) {
838
+ return expression.value;
839
+ }
840
+ if (types.isUnaryExpression(expression) && (expression.operator === "+" || expression.operator === "-") && types.isNumericLiteral(expression.argument)) {
841
+ const value = expression.operator === "-" ? -expression.argument.value : expression.argument.value;
842
+ return Number.isSafeInteger(value) ? value : void 0;
843
+ }
844
+ return void 0;
845
+ }
846
+ function validateKeyedArrayPositionExpression(expression, safeGlobals) {
847
+ const isNumericLiteral = types.isNumericLiteral(expression) || types.isUnaryExpression(expression) && (expression.operator === "+" || expression.operator === "-") && types.isNumericLiteral(expression.argument);
848
+ if (isNumericLiteral && staticSliceIndex(expression) === void 0) {
849
+ return "non-safe-integer position literals";
850
+ }
851
+ return validateDerivedExpression(expression, safeGlobals);
852
+ }
853
+ function rewriteKeyedArrayPositionHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
854
+ if (hintedStateIndices.size === 0) {
855
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
856
+ }
857
+ const file = expressionFile(types.cloneNode(root, true));
858
+ const stateIndices = /* @__PURE__ */ new Set();
859
+ let count = 0;
860
+ traverse(file, {
861
+ CallExpression(path) {
862
+ const callee = path.get("callee");
863
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
864
+ const state = statesBySetter.get(callee.node.name);
865
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
866
+ return;
867
+ }
868
+ const updater = path.node.arguments[0];
869
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isCallExpression(updater.body) || !types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !types.isIdentifier(updater.body.callee.object, { name: updater.params[0].name }) || !types.isIdentifier(updater.body.callee.property)) {
870
+ return;
871
+ }
872
+ const methodName = updater.body.callee.property.name;
873
+ const args = updater.body.arguments;
874
+ const kind = methodName === "with" && args.length === 2 ? "replace" : methodName === "toSpliced" && args.length === 3 && types.isNumericLiteral(args[1], { value: 0 }) ? "insert" : methodName === "toSpliced" && args.length === 2 && types.isNumericLiteral(args[1], { value: 1 }) ? "remove" : void 0;
875
+ if (!kind || !types.isExpression(args[0])) return;
876
+ const position = args[0];
877
+ const item = kind === "remove" ? void 0 : args.at(-1);
878
+ if (validateKeyedArrayPositionExpression(position, safeGlobals) !== void 0 || item !== void 0 && (!types.isExpression(item) || validateDerivedExpression(item, safeGlobals) !== void 0)) {
879
+ return;
880
+ }
881
+ const previous = types.cloneNode(updater.params[0]);
882
+ const method = path.scope.generateUidIdentifier(
883
+ methodName === "with" ? "farmWith" : "farmToSpliced"
884
+ );
885
+ path.node.arguments[0] = types.arrowFunctionExpression(
886
+ [types.cloneNode(previous)],
887
+ types.blockStatement([
888
+ types.variableDeclaration("const", [
889
+ types.variableDeclarator(
890
+ types.cloneNode(method),
891
+ types.memberExpression(types.cloneNode(previous), types.identifier(methodName))
892
+ )
893
+ ]),
894
+ types.returnStatement(
895
+ types.callExpression(types.cloneNode(helperIdentifier), [
896
+ types.cloneNode(previous),
897
+ types.cloneNode(method),
898
+ types.stringLiteral(kind),
899
+ ...args.map((argument) => types.cloneNode(argument, true))
900
+ ])
901
+ )
902
+ ])
903
+ );
904
+ count += 1;
905
+ stateIndices.add(state.index);
906
+ path.skip();
907
+ }
908
+ });
909
+ return {
910
+ root: file.program.body[0].expression,
911
+ count,
912
+ stateIndices
913
+ };
914
+ }
915
+ function rewriteKeyedArrayReorderHints(root, hintedStateIndices, statesBySetter, helperIdentifier) {
916
+ if (hintedStateIndices.size === 0) {
917
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
918
+ }
919
+ const file = expressionFile(types.cloneNode(root, true));
920
+ const stateIndices = /* @__PURE__ */ new Set();
921
+ let count = 0;
922
+ traverse(file, {
923
+ CallExpression(path) {
924
+ const callee = path.get("callee");
925
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
926
+ const state = statesBySetter.get(callee.node.name);
927
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
928
+ return;
929
+ }
930
+ const updater = path.node.arguments[0];
931
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isCallExpression(updater.body) || updater.body.arguments.length !== 0 || !types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !types.isIdentifier(updater.body.callee.object, { name: updater.params[0].name }) || !types.isIdentifier(updater.body.callee.property, { name: "toReversed" })) {
932
+ return;
933
+ }
934
+ const previous = types.cloneNode(updater.params[0]);
935
+ const method = path.scope.generateUidIdentifier("farmToReversed");
936
+ path.node.arguments[0] = types.arrowFunctionExpression(
937
+ [types.cloneNode(previous)],
938
+ types.blockStatement([
939
+ types.variableDeclaration("const", [
940
+ types.variableDeclarator(
941
+ types.cloneNode(method),
942
+ types.memberExpression(types.cloneNode(previous), types.identifier("toReversed"))
943
+ )
944
+ ]),
945
+ types.returnStatement(
946
+ types.callExpression(types.cloneNode(helperIdentifier), [
947
+ types.cloneNode(previous),
948
+ types.cloneNode(method)
949
+ ])
950
+ )
951
+ ])
952
+ );
953
+ count += 1;
954
+ stateIndices.add(state.index);
955
+ path.skip();
956
+ }
957
+ });
958
+ return {
959
+ root: file.program.body[0].expression,
960
+ count,
961
+ stateIndices
962
+ };
963
+ }
964
+ function rewriteKeyedArraySortHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
965
+ if (hintedStateIndices.size === 0) {
966
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
967
+ }
968
+ const file = expressionFile(types.cloneNode(root, true));
969
+ const stateIndices = /* @__PURE__ */ new Set();
970
+ let count = 0;
971
+ traverse(file, {
972
+ CallExpression(path) {
973
+ const callee = path.get("callee");
974
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
975
+ const state = statesBySetter.get(callee.node.name);
976
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
977
+ return;
978
+ }
979
+ const updater = path.node.arguments[0];
980
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isCallExpression(updater.body) || updater.body.arguments.length > 1 || !types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !types.isIdentifier(updater.body.callee.object, { name: updater.params[0].name }) || !types.isIdentifier(updater.body.callee.property, { name: "toSorted" })) {
981
+ return;
982
+ }
983
+ const comparator = updater.body.arguments[0];
984
+ if (comparator && !types.isArrowFunctionExpression(comparator) && !types.isFunctionExpression(comparator)) {
985
+ return;
986
+ }
987
+ if (comparator && validateCollectionCallback(comparator, "toSorted", safeGlobals)) return;
988
+ const previous = types.cloneNode(updater.params[0]);
989
+ const method = path.scope.generateUidIdentifier("farmToSorted");
990
+ path.node.arguments[0] = types.arrowFunctionExpression(
991
+ [types.cloneNode(previous)],
992
+ types.blockStatement([
993
+ types.variableDeclaration("const", [
994
+ types.variableDeclarator(
995
+ types.cloneNode(method),
996
+ types.memberExpression(types.cloneNode(previous), types.identifier("toSorted"))
997
+ )
998
+ ]),
999
+ types.returnStatement(
1000
+ types.callExpression(types.cloneNode(helperIdentifier), [
1001
+ types.cloneNode(previous),
1002
+ types.cloneNode(method),
1003
+ ...comparator ? [types.cloneNode(comparator, true)] : []
1004
+ ])
1005
+ )
1006
+ ])
1007
+ );
1008
+ count += 1;
1009
+ stateIndices.add(state.index);
1010
+ path.skip();
1011
+ }
1012
+ });
1013
+ return {
1014
+ root: file.program.body[0].expression,
1015
+ count,
1016
+ stateIndices
1017
+ };
1018
+ }
1019
+ function rewriteKeyedArraySliceHints(root, hintedStateIndices, statesBySetter, helperIdentifier) {
1020
+ if (hintedStateIndices.size === 0) {
1021
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
1022
+ }
1023
+ const file = expressionFile(types.cloneNode(root, true));
1024
+ const stateIndices = /* @__PURE__ */ new Set();
1025
+ let count = 0;
1026
+ traverse(file, {
1027
+ CallExpression(path) {
1028
+ const callee = path.get("callee");
1029
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
1030
+ const state = statesBySetter.get(callee.node.name);
1031
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
1032
+ return;
1033
+ }
1034
+ const updater = path.node.arguments[0];
1035
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isCallExpression(updater.body) || updater.body.arguments.length < 1 || updater.body.arguments.length > 2 || !types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !types.isIdentifier(updater.body.callee.object, { name: updater.params[0].name }) || !types.isIdentifier(updater.body.callee.property, { name: "slice" })) {
1036
+ return;
1037
+ }
1038
+ const indexes = [];
1039
+ for (const argument of updater.body.arguments) {
1040
+ if (!types.isExpression(argument)) return;
1041
+ const value = staticSliceIndex(argument);
1042
+ if (value === void 0) return;
1043
+ indexes.push(value);
1044
+ }
1045
+ if (indexes.length === 1 && indexes[0] === 0) return;
1046
+ const previous = types.cloneNode(updater.params[0]);
1047
+ const sliceMethod = path.scope.generateUidIdentifier("farmSlice");
1048
+ path.node.arguments[0] = types.arrowFunctionExpression(
1049
+ [types.cloneNode(previous)],
1050
+ types.blockStatement([
1051
+ types.variableDeclaration("const", [
1052
+ types.variableDeclarator(
1053
+ types.cloneNode(sliceMethod),
1054
+ types.memberExpression(types.cloneNode(previous), types.identifier("slice"))
1055
+ )
1056
+ ]),
1057
+ types.returnStatement(
1058
+ types.callExpression(types.cloneNode(helperIdentifier), [
1059
+ types.cloneNode(previous),
1060
+ types.cloneNode(sliceMethod),
1061
+ ...indexes.map((value) => types.numericLiteral(value))
1062
+ ])
1063
+ )
1064
+ ])
1065
+ );
1066
+ count += 1;
1067
+ stateIndices.add(state.index);
1068
+ path.skip();
1069
+ }
1070
+ });
1071
+ return {
1072
+ root: file.program.body[0].expression,
1073
+ count,
1074
+ stateIndices
1075
+ };
1076
+ }
1077
+ function rewriteKeyedArrayFilterHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
1078
+ if (hintedStateIndices.size === 0) {
1079
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
1080
+ }
1081
+ const file = expressionFile(types.cloneNode(root, true));
1082
+ const stateIndices = /* @__PURE__ */ new Set();
1083
+ let count = 0;
1084
+ traverse(file, {
1085
+ CallExpression(path) {
1086
+ const callee = path.get("callee");
1087
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
1088
+ const state = statesBySetter.get(callee.node.name);
1089
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
1090
+ return;
1091
+ }
1092
+ const updater = path.node.arguments[0];
1093
+ if (!types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0]) || !types.isCallExpression(updater.body) || updater.body.arguments.length !== 1 || !types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !types.isIdentifier(updater.body.callee.object, {
1094
+ name: updater.params[0].name
1095
+ }) || !types.isIdentifier(updater.body.callee.property, { name: "filter" })) {
1096
+ return;
1097
+ }
1098
+ const predicate = updater.body.arguments[0];
1099
+ if (!types.isArrowFunctionExpression(predicate) || predicate.async || predicate.generator || predicate.params.length !== 1 || !types.isIdentifier(predicate.params[0]) || !types.isExpression(predicate.body) || validateDerivedExpression(predicate.body, safeGlobals)) {
1100
+ return;
1101
+ }
1102
+ const previous = types.cloneNode(updater.params[0]);
1103
+ const filterMethod = path.scope.generateUidIdentifier("farmFilter");
1104
+ path.node.arguments[0] = types.arrowFunctionExpression(
1105
+ [types.cloneNode(previous)],
1106
+ types.blockStatement([
1107
+ types.variableDeclaration("const", [
1108
+ types.variableDeclarator(
1109
+ types.cloneNode(filterMethod),
1110
+ types.memberExpression(types.cloneNode(previous), types.identifier("filter"))
1111
+ )
1112
+ ]),
1113
+ types.returnStatement(
1114
+ types.callExpression(types.cloneNode(helperIdentifier), [
1115
+ types.cloneNode(previous),
1116
+ types.cloneNode(filterMethod),
1117
+ types.cloneNode(predicate, true)
1118
+ ])
1119
+ )
1120
+ ])
1121
+ );
1122
+ count += 1;
1123
+ stateIndices.add(state.index);
1124
+ path.skip();
1125
+ }
1126
+ });
1127
+ return {
1128
+ root: file.program.body[0].expression,
1129
+ count,
1130
+ stateIndices
1131
+ };
1132
+ }
1133
+ function collectionConstructorName(kind) {
1134
+ return kind === "set" ? "Set" : "Map";
1135
+ }
1136
+ function isCollectionConstruction(expression, kind, previous) {
1137
+ if (!types.isNewExpression(expression) || !types.isIdentifier(expression.callee, {
1138
+ name: collectionConstructorName(kind)
1139
+ })) {
1140
+ return false;
1141
+ }
1142
+ if (!previous) return true;
1143
+ return expression.arguments.length === 1 && types.isIdentifier(expression.arguments[0], { name: previous.name });
1144
+ }
1145
+ function collectionStateInitializerIsOwned(initialValue, kind, constructorIsGlobal) {
1146
+ if (!initialValue || !constructorIsGlobal) return false;
1147
+ if (isCollectionConstruction(initialValue, kind)) return true;
1148
+ if (!types.isArrowFunctionExpression(initialValue) && !types.isFunctionExpression(initialValue)) {
1149
+ return false;
1150
+ }
1151
+ if (types.isBlockStatement(initialValue.body) && initialValue.body.body.length !== 1) return false;
1152
+ const value = returnedExpression(initialValue);
1153
+ return Boolean(value && isCollectionConstruction(value, kind));
1154
+ }
1155
+ function collectionStateHasOnlyOwnedReads(root, state, kind) {
1156
+ const file = expressionFile(types.cloneNode(root, true));
1157
+ let valid = true;
1158
+ const readMethods = kind === "set" ? /* @__PURE__ */ new Set(["has"]) : /* @__PURE__ */ new Set(["get", "has"]);
1159
+ traverse(file, {
1160
+ ReferencedIdentifier(path) {
1161
+ if (!valid || !types.isIdentifier(path.node) || path.node.name !== state.valueName || path.scope.hasBinding(path.node.name)) {
1162
+ return;
1163
+ }
1164
+ const parent = path.parentPath;
1165
+ if (parent.isNewExpression() && types.isIdentifier(parent.node.callee, {
1166
+ name: collectionConstructorName(kind)
1167
+ }) && parent.node.arguments.length === 1 && parent.node.arguments[0] === path.node) {
1168
+ return;
1169
+ }
1170
+ if (!parent.isMemberExpression() || parent.node.object !== path.node || parent.node.computed || !types.isIdentifier(parent.node.property)) {
1171
+ valid = false;
1172
+ path.stop();
1173
+ return;
1174
+ }
1175
+ const property = parent.node.property.name;
1176
+ if (property === "size") return;
1177
+ const call = parent.parentPath;
1178
+ if (!readMethods.has(property) || !call?.isCallExpression() || call.node.callee !== parent.node) {
1179
+ valid = false;
1180
+ path.stop();
1181
+ }
1182
+ }
1183
+ });
1184
+ return valid;
1185
+ }
1186
+ function collectionMutationOperation(kind, method) {
1187
+ if (kind === "set" && method === "add") return "set-add";
1188
+ if (kind === "set" && method === "delete") return "set-delete";
1189
+ if (kind === "map" && method === "set") return "map-set";
1190
+ if (kind === "map" && method === "delete") return "map-delete";
1191
+ return void 0;
1192
+ }
1193
+ function collectionMutationArgumentsAreValid(operation, args) {
1194
+ if (!operation || args.some((argument) => !types.isExpression(argument))) return false;
1195
+ return operation === "map-set" ? args.length === 2 : args.length === 1;
1196
+ }
1197
+ function prepareKeyedCollectionUpdater(updater, kind, updateHelper, mutationHelper) {
1198
+ if (updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0])) {
1199
+ return void 0;
1200
+ }
1201
+ const previous = updater.params[0];
1202
+ if (types.isExpression(updater.body)) {
1203
+ if (!types.isCallExpression(updater.body) || !types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !types.isIdentifier(updater.body.callee.property)) {
1204
+ return void 0;
1205
+ }
1206
+ const operation = collectionMutationOperation(kind, updater.body.callee.property.name);
1207
+ if (operation !== "set-add" && operation !== "map-set" || !collectionMutationArgumentsAreValid(operation, updater.body.arguments) || !isCollectionConstruction(updater.body.callee.object, kind, previous)) {
1208
+ return void 0;
1209
+ }
1210
+ const collection = uniqueLocalIdentifier("_farmCollection", [updater]);
1211
+ const body = types.blockStatement([
1212
+ types.variableDeclaration("const", [
1213
+ types.variableDeclarator(
1214
+ types.cloneNode(collection),
1215
+ types.cloneNode(updater.body.callee.object, true)
1216
+ )
1217
+ ]),
1218
+ types.returnStatement(
1219
+ types.callExpression(types.cloneNode(updateHelper), [
1220
+ types.cloneNode(previous),
1221
+ types.callExpression(types.cloneNode(mutationHelper), [
1222
+ types.cloneNode(collection),
1223
+ types.memberExpression(types.cloneNode(collection), types.cloneNode(updater.body.callee.property)),
1224
+ types.stringLiteral(operation),
1225
+ ...updater.body.arguments.map(
1226
+ (argument) => types.cloneNode(argument, true)
1227
+ )
1228
+ ]),
1229
+ types.stringLiteral(kind)
1230
+ ])
1231
+ )
1232
+ ]);
1233
+ return {
1234
+ updater: types.arrowFunctionExpression([types.cloneNode(previous)], body),
1235
+ mutations: 1
1236
+ };
1237
+ }
1238
+ const cloned = types.cloneNode(updater, true);
1239
+ const file = expressionFile(cloned);
1240
+ let functionPath;
1241
+ traverse(file, {
1242
+ Function(path) {
1243
+ if (functionPath) return;
1244
+ if (path.isArrowFunctionExpression() || path.isFunctionExpression()) {
1245
+ functionPath = path;
1246
+ }
1247
+ }
1248
+ });
1249
+ if (!functionPath || !types.isBlockStatement(functionPath.node.body)) return void 0;
1250
+ const clonedPrevious = functionPath.node.params[0];
1251
+ if (!types.isIdentifier(clonedPrevious)) return void 0;
1252
+ let collectionDeclaration;
1253
+ const updaterBody = functionPath.get("body");
1254
+ if (!updaterBody.isBlockStatement()) return void 0;
1255
+ for (const statement of updaterBody.get("body")) {
1256
+ if (!statement.isVariableDeclaration({ kind: "const" })) continue;
1257
+ for (const declaration of statement.get("declarations")) {
1258
+ if (declaration.isVariableDeclarator() && types.isIdentifier(declaration.node.id) && types.isExpression(declaration.node.init) && isCollectionConstruction(declaration.node.init, kind, clonedPrevious)) {
1259
+ if (collectionDeclaration) return void 0;
1260
+ collectionDeclaration = declaration;
1261
+ }
1262
+ }
1263
+ }
1264
+ if (!collectionDeclaration || !types.isIdentifier(collectionDeclaration.node.id)) return void 0;
1265
+ if (collectionDeclaration.scope.getBinding(collectionConstructorName(kind))) return void 0;
1266
+ const collectionName = collectionDeclaration.node.id.name;
1267
+ const collectionBinding = collectionDeclaration.scope.getBinding(collectionName);
1268
+ const previousBinding = functionPath.scope.getBinding(clonedPrevious.name);
1269
+ if (!collectionBinding || !previousBinding) return void 0;
1270
+ let valid = true;
1271
+ let mutations = 0;
1272
+ traverse(file, {
1273
+ ReferencedIdentifier(path) {
1274
+ if (!valid) return;
1275
+ const binding = path.scope.getBinding(path.node.name);
1276
+ const belongsToUpdater = path.getFunctionParent()?.node === functionPath?.node;
1277
+ if (binding === collectionBinding) {
1278
+ if (!belongsToUpdater) {
1279
+ valid = false;
1280
+ path.stop();
1281
+ return;
1282
+ }
1283
+ const parent = path.parentPath;
1284
+ if (parent.isReturnStatement() && parent.node.argument === path.node) return;
1285
+ if (!parent.isMemberExpression() || parent.node.object !== path.node || parent.node.computed || !types.isIdentifier(parent.node.property)) {
1286
+ valid = false;
1287
+ path.stop();
1288
+ return;
1289
+ }
1290
+ const method = parent.node.property.name;
1291
+ if (method === "size") return;
1292
+ const call = parent.parentPath;
1293
+ if (!call?.isCallExpression() || call.node.callee !== parent.node) {
1294
+ valid = false;
1295
+ path.stop();
1296
+ return;
1297
+ }
1298
+ const operation = collectionMutationOperation(kind, method);
1299
+ const read = kind === "set" ? method === "has" : method === "get" || method === "has";
1300
+ if (read) return;
1301
+ if (!collectionMutationArgumentsAreValid(operation, call.node.arguments) || call.parentPath.isMemberExpression() && call.parentPath.node.object === call.node) {
1302
+ valid = false;
1303
+ path.stop();
1304
+ return;
1305
+ }
1306
+ mutations += 1;
1307
+ return;
1308
+ }
1309
+ if (binding === previousBinding) {
1310
+ if (!belongsToUpdater) {
1311
+ valid = false;
1312
+ path.stop();
1313
+ return;
1314
+ }
1315
+ const parent = path.parentPath;
1316
+ if (parent.isReturnStatement() && parent.node.argument === path.node) return;
1317
+ if (parent.isNewExpression() && parent.node.arguments.length === 1 && parent.node.arguments[0] === path.node && types.isIdentifier(parent.node.callee, {
1318
+ name: collectionConstructorName(kind)
1319
+ })) {
1320
+ return;
1321
+ }
1322
+ if (parent.isMemberExpression() && parent.node.object === path.node && !parent.node.computed && types.isIdentifier(parent.node.property)) {
1323
+ const method = parent.node.property.name;
1324
+ const call = parent.parentPath;
1325
+ if (method === "size" || (kind === "set" ? method === "has" : method === "get" || method === "has") && call?.isCallExpression() && call.node.callee === parent.node) {
1326
+ return;
1327
+ }
1328
+ }
1329
+ valid = false;
1330
+ path.stop();
1331
+ }
1332
+ },
1333
+ ReturnStatement(path) {
1334
+ if (!valid || path.getFunctionParent()?.node !== functionPath?.node || !path.node.argument) {
1335
+ return;
1336
+ }
1337
+ const value = path.node.argument;
1338
+ const returnsOwnedCollection = types.isIdentifier(value) && (path.scope.getBinding(value.name) === collectionBinding || path.scope.getBinding(value.name) === previousBinding) || isCollectionConstruction(value, kind);
1339
+ if (!returnsOwnedCollection) {
1340
+ valid = false;
1341
+ path.stop();
1342
+ }
1343
+ }
1344
+ });
1345
+ if (!valid || mutations === 0) return void 0;
1346
+ traverse(file, {
1347
+ CallExpression(path) {
1348
+ if (path.getFunctionParent()?.node !== functionPath?.node) return;
1349
+ const callee = path.node.callee;
1350
+ if (!types.isMemberExpression(callee) || callee.computed || !types.isIdentifier(callee.object, { name: collectionName }) || path.scope.getBinding(collectionName) !== collectionBinding || !types.isIdentifier(callee.property)) {
1351
+ return;
1352
+ }
1353
+ const operation = collectionMutationOperation(kind, callee.property.name);
1354
+ if (!collectionMutationArgumentsAreValid(operation, path.node.arguments)) return;
1355
+ path.replaceWith(
1356
+ types.callExpression(types.cloneNode(mutationHelper), [
1357
+ types.cloneNode(callee.object),
1358
+ types.memberExpression(types.cloneNode(callee.object), types.cloneNode(callee.property)),
1359
+ types.stringLiteral(operation),
1360
+ ...path.node.arguments.map((argument) => types.cloneNode(argument, true))
1361
+ ])
1362
+ );
1363
+ path.skip();
1364
+ },
1365
+ ReturnStatement(path) {
1366
+ if (path.getFunctionParent()?.node !== functionPath?.node || !path.node.argument) return;
1367
+ path.node.argument = types.callExpression(types.cloneNode(updateHelper), [
1368
+ types.cloneNode(clonedPrevious),
1369
+ types.cloneNode(path.node.argument, true),
1370
+ types.stringLiteral(kind)
1371
+ ]);
1372
+ }
1373
+ });
1374
+ const prepared = file.program.body[0].expression;
1375
+ return types.isArrowFunctionExpression(prepared) || types.isFunctionExpression(prepared) ? { updater: prepared, mutations } : void 0;
1376
+ }
1377
+ function rewriteKeyedCollectionUpdateHints(root, targetKinds, states, statesBySetter, updateHelper, mutationHelper, globalCollections) {
1378
+ if (targetKinds.size === 0) return { root: types.cloneNode(root, true), count: 0 };
1379
+ const file = expressionFile(types.cloneNode(root, true));
1380
+ let count = 0;
1381
+ for (const [stateIndex, kind] of targetKinds) {
1382
+ const state = states[stateIndex];
1383
+ if (!state || !collectionStateInitializerIsOwned(
1384
+ state.initialValue,
1385
+ kind,
1386
+ globalCollections.has(collectionConstructorName(kind))
1387
+ ) || !collectionStateHasOnlyOwnedReads(root, state, kind)) {
1388
+ continue;
1389
+ }
1390
+ const replacements = /* @__PURE__ */ new Map();
1391
+ let valid = true;
1392
+ traverse(file, {
1393
+ CallExpression(path) {
1394
+ if (!valid) return;
1395
+ const callee = path.get("callee");
1396
+ if (!callee.isIdentifier({ name: state.setterName }) || callee.scope.hasBinding(callee.node.name) || statesBySetter.get(callee.node.name)?.index !== stateIndex) {
1397
+ return;
1398
+ }
1399
+ if (path.node.arguments.length !== 1 || !types.isExpression(path.node.arguments[0])) {
1400
+ valid = false;
1401
+ path.stop();
1402
+ return;
1403
+ }
1404
+ const update = path.node.arguments[0];
1405
+ if (path.scope.getBinding(collectionConstructorName(kind))) {
1406
+ valid = false;
1407
+ path.stop();
1408
+ return;
1409
+ }
1410
+ if (isCollectionConstruction(update, kind)) return;
1411
+ if (!types.isArrowFunctionExpression(update) && !types.isFunctionExpression(update)) {
1412
+ valid = false;
1413
+ path.stop();
1414
+ return;
1415
+ }
1416
+ const prepared = prepareKeyedCollectionUpdater(update, kind, updateHelper, mutationHelper);
1417
+ if (!prepared) {
1418
+ valid = false;
1419
+ path.stop();
1420
+ return;
1421
+ }
1422
+ replacements.set(path.node, prepared);
1423
+ }
1424
+ });
1425
+ if (!valid || replacements.size === 0) continue;
1426
+ traverse(file, {
1427
+ CallExpression(path) {
1428
+ const prepared = replacements.get(path.node);
1429
+ if (!prepared) return;
1430
+ path.node.arguments[0] = types.cloneNode(prepared.updater, true);
1431
+ count += prepared.mutations;
1432
+ path.skip();
1433
+ }
1434
+ });
1435
+ }
1436
+ return {
1437
+ root: file.program.body[0].expression,
1438
+ count
1439
+ };
1440
+ }
301
1441
  function rewriteStateAccess(expression, stateParameter, statesByValue, statesBySetter) {
302
1442
  const file = expressionFile(cloneExpression(expression));
303
1443
  traverse(file, {
@@ -548,21 +1688,28 @@ function analyzeMapList(expression, statesByValue, safeGlobals) {
548
1688
  const collection = callee.object;
549
1689
  const collectionUnsupported = validateCollectionPipeline(collection, safeGlobals);
550
1690
  if (collectionUnsupported) {
551
- return { reason: `keyed list collection cannot use ${collectionUnsupported}` };
1691
+ return {
1692
+ reason: `keyed list collection cannot use ${collectionUnsupported}`
1693
+ };
552
1694
  }
553
1695
  if (expression.arguments.length !== 1 || !types.isArrowFunctionExpression(expression.arguments[0]) && !types.isFunctionExpression(expression.arguments[0])) {
554
1696
  return { reason: "keyed list map must use one inline render callback" };
555
1697
  }
556
1698
  const callback = expression.arguments[0];
557
1699
  if (containsDirectHookCall(callback)) {
558
- return { reason: "Hooks cannot be called directly inside a keyed list callback" };
1700
+ return {
1701
+ reason: "Hooks cannot be called directly inside a keyed list callback"
1702
+ };
559
1703
  }
560
1704
  const row = returnedExpression(callback);
561
1705
  if (!row || !types.isJSXElement(row)) {
562
1706
  return { reason: "keyed list map callbacks must return one React element" };
563
1707
  }
564
1708
  const key = jsxKeyExpression(row);
565
- if (!key) return { reason: "automatically compiled lists require an explicit item key" };
1709
+ if (!key)
1710
+ return {
1711
+ reason: "automatically compiled lists require an explicit item key"
1712
+ };
566
1713
  const keyReason = validateKeyFunction(callback, key, safeGlobals);
567
1714
  if (keyReason) return { reason: keyReason };
568
1715
  return { dependencies: collectStateDependencies(expression, statesByValue) };
@@ -577,7 +1724,9 @@ function listAttributeExpression(element, name) {
577
1724
  }
578
1725
  function analyzePublicList(element, statesByValue, safeGlobals) {
579
1726
  if (element.openingElement.attributes.some((attribute) => types.isJSXSpreadAttribute(attribute))) {
580
- return { reason: "compiled List boundaries do not support JSX attribute spreads" };
1727
+ return {
1728
+ reason: "compiled List boundaries do not support JSX attribute spreads"
1729
+ };
581
1730
  }
582
1731
  const each = listAttributeExpression(element, "each");
583
1732
  const by = listAttributeExpression(element, "by");
@@ -595,7 +1744,9 @@ function analyzePublicList(element, statesByValue, safeGlobals) {
595
1744
  if (keyReason) return { reason: keyReason };
596
1745
  const children = meaningfulJsxChildren(element);
597
1746
  if (children.length !== 1 || !types.isJSXExpressionContainer(children[0]) || types.isJSXEmptyExpression(children[0].expression) || !types.isArrowFunctionExpression(children[0].expression) && !types.isFunctionExpression(children[0].expression)) {
598
- return { reason: "compiled List children must use one inline render function" };
1747
+ return {
1748
+ reason: "compiled List children must use one inline render function"
1749
+ };
599
1750
  }
600
1751
  const render = children[0].expression;
601
1752
  if (!returnedExpression(render)) {
@@ -709,13 +1860,20 @@ function stabilizeDeferredEventCurrentTargets(root, compilerSetters) {
709
1860
  const expression = file.program.body[0].expression;
710
1861
  return types.isJSXElement(expression) ? expression : types.cloneNode(root, true);
711
1862
  }
712
- function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters) {
1863
+ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters, keyedTargetReactiveByValue) {
713
1864
  if (renderCallback.async || renderCallback.generator || renderCallback.params.length < 1 || renderCallback.params.length > 2 || !types.isIdentifier(renderCallback.params[0]) || renderCallback.params[1] !== void 0 && !types.isIdentifier(renderCallback.params[1])) {
714
- return { reason: "compiled keyed rows require item and optional index identifiers" };
1865
+ return {
1866
+ reason: "compiled keyed rows require item and optional index identifiers"
1867
+ };
715
1868
  }
716
1869
  const bindings = [];
717
1870
  const events = [];
718
1871
  const conditionals = [];
1872
+ const validateBindingExpression = (expression) => validateDerivedExpression(
1873
+ expression,
1874
+ safeGlobals,
1875
+ keyedTargetReactiveByValue ? (call) => isPotentialKeyedMembershipCall(call, keyedTargetReactiveByValue) || isPotentialKeyedMapLookupCall(call, keyedTargetReactiveByValue) : void 0
1876
+ );
719
1877
  const visit = (element, path) => {
720
1878
  const tag = element.openingElement.name;
721
1879
  if (!types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
@@ -794,7 +1952,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
794
1952
  if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
795
1953
  return "compiled keyed row style names must use camelCase or a CSS custom property";
796
1954
  }
797
- const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
1955
+ const unsupported2 = validateBindingExpression(property.value);
798
1956
  if (unsupported2) {
799
1957
  return `compiled keyed row style ${propertyName} cannot use ${unsupported2}`;
800
1958
  }
@@ -808,7 +1966,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
808
1966
  continue;
809
1967
  }
810
1968
  if (name === "children") return "compiled keyed row children props are not supported yet";
811
- const unsupported = validateDerivedExpression(expression, safeGlobals);
1969
+ const unsupported = validateBindingExpression(expression);
812
1970
  if (unsupported) return `compiled keyed row ${name} cannot use ${unsupported}`;
813
1971
  bindings.push({
814
1972
  kind: "attribute",
@@ -858,7 +2016,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
858
2016
  (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)
859
2017
  );
860
2018
  for (const child of expressions) {
861
- const unsupported = validateDerivedExpression(child.expression, safeGlobals);
2019
+ const unsupported = validateBindingExpression(child.expression);
862
2020
  if (unsupported) return `compiled keyed row text cannot use ${unsupported}`;
863
2021
  }
864
2022
  if (nestedElements.length > 0 && expressions.length > 0) {
@@ -874,7 +2032,11 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
874
2032
  parts.push(cloneExpression(child.expression));
875
2033
  }
876
2034
  }
877
- bindings.push({ kind: "text", path: [...path], value: types.arrayExpression(parts) });
2035
+ bindings.push({
2036
+ kind: "text",
2037
+ path: [...path],
2038
+ value: types.arrayExpression(parts)
2039
+ });
878
2040
  }
879
2041
  let elementIndex = 0;
880
2042
  for (const child of element.children) {
@@ -888,7 +2050,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
888
2050
  const reason = visit(row, []);
889
2051
  return reason ? { reason } : { bindings, events, conditionals };
890
2052
  }
891
- function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false) {
2053
+ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false, allowMembershipTargets = false) {
892
2054
  let collection;
893
2055
  let keyCallback;
894
2056
  let renderCallback;
@@ -941,7 +2103,8 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
941
2103
  row,
942
2104
  renderCallback,
943
2105
  safeGlobals,
944
- new Set([...statesByValue.values()].map((state) => state.setterName))
2106
+ new Set([...statesByValue.values()].map((state) => state.setterName)),
2107
+ allowMembershipTargets ? statesByValue : void 0
945
2108
  );
946
2109
  if (rowAnalysis.reason && !acceptUnsupportedRow) return void 0;
947
2110
  const keyExpression = returnedExpression(keyCallback);
@@ -954,6 +2117,7 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
954
2117
  return {
955
2118
  source,
956
2119
  collection,
2120
+ collectionDependency: types.isIdentifier(collection) ? statesByValue.get(collection.name)?.index : void 0,
957
2121
  structureDependencies: [...structureDependencies].sort((left, right) => left - right),
958
2122
  keyCallback,
959
2123
  renderCallback,
@@ -973,16 +2137,83 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
973
2137
  if (container.openingElement.attributes.some(
974
2138
  (attribute) => types.isJSXSpreadAttribute(attribute) || types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && (attribute.name.name === "ref" || attribute.name.name === "dangerouslySetInnerHTML")
975
2139
  )) {
976
- return void 0;
2140
+ return void 0;
2141
+ }
2142
+ for (const attribute of container.openingElement.attributes) {
2143
+ if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && types.isJSXExpressionContainer(attribute.value) && !types.isJSXEmptyExpression(attribute.value.expression) && !/^on[A-Z]/.test(attribute.name.name) && collectStateDependencies(attribute.value.expression, statesByValue).length > 0) {
2144
+ return void 0;
2145
+ }
2146
+ }
2147
+ const children = meaningfulJsxChildren(container);
2148
+ if (children.length !== 1) return void 0;
2149
+ const shape = analyzeKeyedRowChild(
2150
+ children[0],
2151
+ statesByValue,
2152
+ safeGlobals,
2153
+ listNames,
2154
+ true,
2155
+ true
2156
+ );
2157
+ if (!shape || shape.rowReason) return shape;
2158
+ const needsMembershipProof = (binding) => containsPotentialKeyedMembershipCall(binding.value, statesByValue);
2159
+ const needsMapLookupProof = (binding) => containsPotentialKeyedMapLookupCall(binding.value, statesByValue);
2160
+ if (shape.conditionals.length > 0 || shape.events.length > 0 && !canDelegateKeyedRowEvents({
2161
+ conditionals: shape.conditionals,
2162
+ events: shape.events,
2163
+ source: container
2164
+ })) {
2165
+ return shape.bindings.some(
2166
+ (binding) => needsMembershipProof(binding) || needsMapLookupProof(binding)
2167
+ ) ? {
2168
+ ...shape,
2169
+ rowReason: "keyed collection targets require compiler-owned rows without React-owned branches or events"
2170
+ } : shape;
2171
+ }
2172
+ const structureDependencies = new Set(shape.structureDependencies);
2173
+ const bindings = shape.bindings.map((binding) => ({
2174
+ ...binding,
2175
+ identityTarget: keyedIdentityTarget(
2176
+ binding,
2177
+ shape.keyCallback,
2178
+ shape.renderCallback,
2179
+ statesByValue,
2180
+ structureDependencies
2181
+ ),
2182
+ membershipTarget: keyedMembershipTarget(
2183
+ binding,
2184
+ shape.keyCallback,
2185
+ shape.renderCallback,
2186
+ statesByValue,
2187
+ structureDependencies
2188
+ ),
2189
+ mapLookupTarget: keyedMapLookupTarget(
2190
+ binding,
2191
+ shape.keyCallback,
2192
+ shape.renderCallback,
2193
+ statesByValue,
2194
+ structureDependencies
2195
+ )
2196
+ }));
2197
+ if (bindings.some(
2198
+ (binding, index) => needsMembershipProof(shape.bindings[index]) && !binding.membershipTarget
2199
+ )) {
2200
+ return {
2201
+ ...shape,
2202
+ rowReason: "keyed membership targets must call one local Set state with the exact row key"
2203
+ };
977
2204
  }
978
- for (const attribute of container.openingElement.attributes) {
979
- if (types.isJSXAttribute(attribute) && types.isJSXIdentifier(attribute.name) && types.isJSXExpressionContainer(attribute.value) && !types.isJSXEmptyExpression(attribute.value.expression) && !/^on[A-Z]/.test(attribute.name.name) && collectStateDependencies(attribute.value.expression, statesByValue).length > 0) {
980
- return void 0;
981
- }
2205
+ if (bindings.some(
2206
+ (binding, index) => needsMapLookupProof(shape.bindings[index]) && !binding.mapLookupTarget
2207
+ )) {
2208
+ return {
2209
+ ...shape,
2210
+ rowReason: "keyed Map lookup targets must call one local Map state with the exact row key"
2211
+ };
982
2212
  }
983
- const children = meaningfulJsxChildren(container);
984
- if (children.length !== 1) return void 0;
985
- return analyzeKeyedRowChild(children[0], statesByValue, safeGlobals, listNames, true);
2213
+ return {
2214
+ ...shape,
2215
+ bindings
2216
+ };
986
2217
  }
987
2218
  function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
988
2219
  if (container.openingElement.attributes.some(
@@ -1138,7 +2369,11 @@ function analyzeHostConditionalTree(branch, safeGlobals, allowStaticEvents = fal
1138
2369
  parts.push(cloneExpression(child.expression));
1139
2370
  }
1140
2371
  }
1141
- bindings.push({ kind: "text", path: [...path], value: types.arrayExpression(parts) });
2372
+ bindings.push({
2373
+ kind: "text",
2374
+ path: [...path],
2375
+ value: types.arrayExpression(parts)
2376
+ });
1142
2377
  }
1143
2378
  let elementIndex = 0;
1144
2379
  for (const child of element.children) {
@@ -1310,7 +2545,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1310
2545
  listNames,
1311
2546
  id,
1312
2547
  allocateId
1313
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2548
+ ) : {
2549
+ bindings: [],
2550
+ plans: [],
2551
+ blocks: /* @__PURE__ */ new Map()
2552
+ };
1314
2553
  const falsy = shape.falsy ? analyzeRecursiveHostTree(
1315
2554
  shape.falsy,
1316
2555
  statesByValue,
@@ -1318,7 +2557,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1318
2557
  listNames,
1319
2558
  id,
1320
2559
  allocateId
1321
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2560
+ ) : {
2561
+ bindings: [],
2562
+ plans: [],
2563
+ blocks: /* @__PURE__ */ new Map()
2564
+ };
1322
2565
  if (truthy.reason) return truthy.reason;
1323
2566
  if (falsy.reason) return falsy.reason;
1324
2567
  plans.push(...truthy.plans, ...falsy.plans);
@@ -1476,7 +2719,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1476
2719
  parts.push(cloneExpression(child.expression));
1477
2720
  }
1478
2721
  }
1479
- bindings.push({ kind: "text", path: [...path], value: types.arrayExpression(parts) });
2722
+ bindings.push({
2723
+ kind: "text",
2724
+ path: [...path],
2725
+ value: types.arrayExpression(parts)
2726
+ });
1480
2727
  }
1481
2728
  let elementIndex = 0;
1482
2729
  for (const child of element.children) {
@@ -1543,7 +2790,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1543
2790
  listNames,
1544
2791
  parent,
1545
2792
  allocateId
1546
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2793
+ ) : {
2794
+ bindings: [],
2795
+ plans: [],
2796
+ blocks: /* @__PURE__ */ new Map()
2797
+ };
1547
2798
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1548
2799
  shape.falsy,
1549
2800
  statesByValue,
@@ -1551,7 +2802,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1551
2802
  listNames,
1552
2803
  parent,
1553
2804
  allocateId
1554
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2805
+ ) : {
2806
+ bindings: [],
2807
+ plans: [],
2808
+ blocks: /* @__PURE__ */ new Map()
2809
+ };
1555
2810
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1556
2811
  const dependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1557
2812
  for (const binding of [...truthyAnalysis.bindings || [], ...falsyAnalysis.bindings || []]) {
@@ -1597,7 +2852,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1597
2852
  listNames,
1598
2853
  parent,
1599
2854
  allocateId
1600
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2855
+ ) : {
2856
+ bindings: [],
2857
+ plans: [],
2858
+ blocks: /* @__PURE__ */ new Map()
2859
+ };
1601
2860
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1602
2861
  shape.falsy,
1603
2862
  statesByValue,
@@ -1605,7 +2864,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1605
2864
  listNames,
1606
2865
  parent,
1607
2866
  allocateId
1608
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2867
+ ) : {
2868
+ bindings: [],
2869
+ plans: [],
2870
+ blocks: /* @__PURE__ */ new Map()
2871
+ };
1609
2872
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1610
2873
  nestedPlans.push(...truthyAnalysis.plans, ...falsyAnalysis.plans);
1611
2874
  for (const [element, plan] of truthyAnalysis.blocks) descriptorBlocks.set(element, plan);
@@ -1698,7 +2961,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1698
2961
  listNames,
1699
2962
  parent,
1700
2963
  allocateId
1701
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2964
+ ) : {
2965
+ bindings: [],
2966
+ plans: [],
2967
+ blocks: /* @__PURE__ */ new Map()
2968
+ };
1702
2969
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1703
2970
  shape.falsy,
1704
2971
  statesByValue,
@@ -1706,7 +2973,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1706
2973
  listNames,
1707
2974
  parent,
1708
2975
  allocateId
1709
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2976
+ ) : {
2977
+ bindings: [],
2978
+ plans: [],
2979
+ blocks: /* @__PURE__ */ new Map()
2980
+ };
1710
2981
  if (!mergeAnalysis(truthyAnalysis) || !mergeAnalysis(falsyAnalysis)) return void 0;
1711
2982
  for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1712
2983
  dependencies.add(dependency);
@@ -1947,6 +3218,57 @@ function keyedRowBindingObject(binding, parameters) {
1947
3218
  if (binding.name) {
1948
3219
  properties.push(types.objectProperty(types.identifier("name"), types.stringLiteral(binding.name)));
1949
3220
  }
3221
+ if (binding.identityTarget) {
3222
+ properties.push(
3223
+ types.objectProperty(
3224
+ types.identifier("identityTarget"),
3225
+ types.objectExpression([
3226
+ types.objectProperty(
3227
+ types.identifier("dependency"),
3228
+ types.numericLiteral(binding.identityTarget.dependency)
3229
+ ),
3230
+ types.objectProperty(
3231
+ types.identifier("read"),
3232
+ types.arrowFunctionExpression([], cloneExpression(binding.identityTarget.value))
3233
+ )
3234
+ ])
3235
+ )
3236
+ );
3237
+ }
3238
+ if (binding.membershipTarget) {
3239
+ properties.push(
3240
+ types.objectProperty(
3241
+ types.identifier("membershipTarget"),
3242
+ types.objectExpression([
3243
+ types.objectProperty(
3244
+ types.identifier("dependency"),
3245
+ types.numericLiteral(binding.membershipTarget.dependency)
3246
+ ),
3247
+ types.objectProperty(
3248
+ types.identifier("read"),
3249
+ types.arrowFunctionExpression([], cloneExpression(binding.membershipTarget.value))
3250
+ )
3251
+ ])
3252
+ )
3253
+ );
3254
+ }
3255
+ if (binding.mapLookupTarget) {
3256
+ properties.push(
3257
+ types.objectProperty(
3258
+ types.identifier("mapLookupTarget"),
3259
+ types.objectExpression([
3260
+ types.objectProperty(
3261
+ types.identifier("dependency"),
3262
+ types.numericLiteral(binding.mapLookupTarget.dependency)
3263
+ ),
3264
+ types.objectProperty(
3265
+ types.identifier("read"),
3266
+ types.arrowFunctionExpression([], cloneExpression(binding.mapLookupTarget.value))
3267
+ )
3268
+ ])
3269
+ )
3270
+ );
3271
+ }
1950
3272
  properties.push(
1951
3273
  types.objectProperty(
1952
3274
  types.identifier("read"),
@@ -2141,7 +3463,7 @@ function keyedRowConditionalObject(conditional, parameters) {
2141
3463
  }
2142
3464
  return types.objectExpression(properties);
2143
3465
  }
2144
- function keyedRowsBoundary(blockRuntime, plan) {
3466
+ function keyedRowsBoundary(blockRuntime, plan, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
2145
3467
  const name = types.jsxMemberExpression(
2146
3468
  types.jsxIdentifier(blockRuntime.name),
2147
3469
  types.jsxIdentifier("KeyedRows")
@@ -2175,6 +3497,16 @@ function keyedRowsBoundary(blockRuntime, plan) {
2175
3497
  types.jsxIdentifier("items"),
2176
3498
  types.jsxExpressionContainer(types.arrowFunctionExpression([], cloneExpression(plan.collection)))
2177
3499
  ),
3500
+ ...keyedMapUpdateHints ? [
3501
+ types.jsxAttribute(
3502
+ types.jsxIdentifier("dependencies"),
3503
+ types.jsxExpressionContainer(
3504
+ types.arrayExpression(
3505
+ plan.dependencies.map((dependency) => types.numericLiteral(dependency))
3506
+ )
3507
+ )
3508
+ )
3509
+ ] : [],
2178
3510
  types.jsxAttribute(
2179
3511
  types.jsxIdentifier("structureDependencies"),
2180
3512
  types.jsxExpressionContainer(
@@ -2183,6 +3515,36 @@ function keyedRowsBoundary(blockRuntime, plan) {
2183
3515
  )
2184
3516
  )
2185
3517
  ),
3518
+ ...!keyedMapUpdateHints || plan.collectionDependency === void 0 ? [] : [
3519
+ types.jsxAttribute(
3520
+ types.jsxIdentifier("collectionDependency"),
3521
+ types.jsxExpressionContainer(types.numericLiteral(plan.collectionDependency))
3522
+ )
3523
+ ],
3524
+ ...plan.collectionDependency !== void 0 && keyedArrayFilterHintedStateIndices.has(plan.collectionDependency) ? [
3525
+ types.jsxAttribute(
3526
+ types.jsxIdentifier("filterIndexIndependent"),
3527
+ types.jsxExpressionContainer(types.booleanLiteral(true))
3528
+ )
3529
+ ] : [],
3530
+ ...plan.collectionDependency !== void 0 && keyedArrayPrependHintedStateIndices.has(plan.collectionDependency) ? [
3531
+ types.jsxAttribute(
3532
+ types.jsxIdentifier("prependIndexIndependent"),
3533
+ types.jsxExpressionContainer(types.booleanLiteral(true))
3534
+ )
3535
+ ] : [],
3536
+ ...plan.collectionDependency !== void 0 && keyedArrayPositionHintedStateIndices.has(plan.collectionDependency) ? [
3537
+ types.jsxAttribute(
3538
+ types.jsxIdentifier("positionIndexIndependent"),
3539
+ types.jsxExpressionContainer(types.booleanLiteral(true))
3540
+ )
3541
+ ] : [],
3542
+ ...plan.collectionDependency !== void 0 && keyedArrayReorderHintedStateIndices.has(plan.collectionDependency) ? [
3543
+ types.jsxAttribute(
3544
+ types.jsxIdentifier("reorderIndexIndependent"),
3545
+ types.jsxExpressionContainer(types.booleanLiteral(true))
3546
+ )
3547
+ ] : [],
2186
3548
  types.jsxAttribute(
2187
3549
  types.jsxIdentifier("rowKey"),
2188
3550
  types.jsxExpressionContainer(types.cloneNode(plan.keyCallback, true))
@@ -2509,7 +3871,9 @@ function conditionalRangesBoundary(blockRuntime, plan) {
2509
3871
  function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allowedComponentNames) {
2510
3872
  const name = element.openingElement.name;
2511
3873
  if (!types.isJSXIdentifier(name) || !isComponentName(name.name)) {
2512
- return { reason: "component islands require a direct component identifier" };
3874
+ return {
3875
+ reason: "component islands require a direct component identifier"
3876
+ };
2513
3877
  }
2514
3878
  if (!allowedComponentNames.has(name.name)) {
2515
3879
  return {
@@ -2517,19 +3881,27 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2517
3881
  };
2518
3882
  }
2519
3883
  if (meaningfulJsxChildren(element).length > 0) {
2520
- return { reason: `component island ${name.name} does not support children yet` };
3884
+ return {
3885
+ reason: `component island ${name.name} does not support children yet`
3886
+ };
2521
3887
  }
2522
3888
  const dependencies = /* @__PURE__ */ new Set();
2523
3889
  for (const attribute of element.openingElement.attributes) {
2524
3890
  if (types.isJSXSpreadAttribute(attribute)) {
2525
- return { reason: `component island ${name.name} does not support JSX attribute spreads` };
3891
+ return {
3892
+ reason: `component island ${name.name} does not support JSX attribute spreads`
3893
+ };
2526
3894
  }
2527
3895
  const attributeName = jsxAttributeName(attribute);
2528
3896
  if (!attributeName) {
2529
- return { reason: `component island ${name.name} does not support namespaced JSX attributes` };
3897
+ return {
3898
+ reason: `component island ${name.name} does not support namespaced JSX attributes`
3899
+ };
2530
3900
  }
2531
3901
  if (attributeName === "ref" || attributeName === "key" || attributeName === "children") {
2532
- return { reason: `component island ${name.name} does not support ${attributeName} yet` };
3902
+ return {
3903
+ reason: `component island ${name.name} does not support ${attributeName} yet`
3904
+ };
2533
3905
  }
2534
3906
  if (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression)) {
2535
3907
  continue;
@@ -2548,7 +3920,9 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2548
3920
  (dependency) => dependencies.add(dependency)
2549
3921
  );
2550
3922
  }
2551
- return { dependencies: [...dependencies].sort((left, right) => left - right) };
3923
+ return {
3924
+ dependencies: [...dependencies].sort((left, right) => left - right)
3925
+ };
2552
3926
  }
2553
3927
  function componentIslandBoundary(blockRuntime, plan, source) {
2554
3928
  const name = types.jsxMemberExpression(
@@ -2805,6 +4179,7 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
2805
4179
  dependencies: keyedRows.dependencies,
2806
4180
  source: child,
2807
4181
  collection: keyedRows.collection,
4182
+ collectionDependency: keyedRows.collectionDependency,
2808
4183
  structureDependencies: keyedRows.structureDependencies,
2809
4184
  keyCallback: keyedRows.keyCallback,
2810
4185
  renderCallback: keyedRows.renderCallback,
@@ -3046,7 +4421,7 @@ function conditionalBlockBoundary(blockRuntime, plan, source) {
3046
4421
  true
3047
4422
  );
3048
4423
  }
3049
- function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
4424
+ function lowerComposableBlocks(root, plans, blockRuntime, listNames, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
3050
4425
  const planBySource = new Map(
3051
4426
  plans.map((plan) => [plan.source, plan])
3052
4427
  );
@@ -3071,7 +4446,15 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
3071
4446
  return mixedRangesBoundary(blockRuntime, plan2);
3072
4447
  }
3073
4448
  if (plan2?.kind === "keyed-rows") {
3074
- return keyedRowsBoundary(blockRuntime, plan2);
4449
+ return keyedRowsBoundary(
4450
+ blockRuntime,
4451
+ plan2,
4452
+ keyedMapUpdateHints,
4453
+ keyedArrayFilterHintedStateIndices,
4454
+ keyedArrayPrependHintedStateIndices,
4455
+ keyedArrayPositionHintedStateIndices,
4456
+ keyedArrayReorderHintedStateIndices
4457
+ );
3075
4458
  }
3076
4459
  if (plan2?.kind === "keyed-ranges") {
3077
4460
  return keyedRangesBoundary(blockRuntime, plan2);
@@ -3434,7 +4817,7 @@ function wrapperElement(definitionIdentifier, props) {
3434
4817
  const attributes = props ? [types.jsxSpreadAttribute(types.cloneNode(props))] : [];
3435
4818
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
3436
4819
  }
3437
- function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureIdentifiers, usedRuntimeFeatures, useStateNames, reactNames, listNames, moduleId, reactivity) {
4820
+ function compileCandidate(candidate, createComponentIdentifier, keyedMapUpdateIdentifier, keyedArrayAppendIdentifier, keyedArrayFilterIdentifier, keyedArrayPrependIdentifier, keyedArrayPositionIdentifier, keyedArrayReorderIdentifier, keyedArraySortIdentifier, keyedArrayRollingWindowIdentifier, keyedArraySliceIdentifier, keyedCollectionUpdateIdentifier, keyedCollectionMutationIdentifier, runtimeFeatureIdentifiers, usedRuntimeFeatures, optimizationCounts, useStateNames, reactNames, listNames, moduleId, reactivity) {
3438
4821
  const { path, name, statementPath } = candidate;
3439
4822
  if (path.node.async || path.node.generator)
3440
4823
  return "async and generator components are not supported";
@@ -3446,6 +4829,7 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3446
4829
  const safeGlobals = new Set(
3447
4830
  [...SAFE_GLOBAL_CALLS, "Math"].filter((name2) => !path.scope.getBinding(name2))
3448
4831
  );
4832
+ const globalCollections = new Set(["Set", "Map"].filter((name2) => !path.scope.getBinding(name2)));
3449
4833
  const states = [];
3450
4834
  const locals = [];
3451
4835
  let returned;
@@ -3493,9 +4877,17 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3493
4877
  if (declaration.init.async || declaration.init.generator) {
3494
4878
  return `event handler ${declaration.id.name} must be synchronous`;
3495
4879
  }
3496
- locals.push({ kind: "handler", name: declaration.id.name, value: declaration.init });
4880
+ locals.push({
4881
+ kind: "handler",
4882
+ name: declaration.id.name,
4883
+ value: declaration.init
4884
+ });
3497
4885
  } else {
3498
- locals.push({ kind: "derived", name: declaration.id.name, value: declaration.init });
4886
+ locals.push({
4887
+ kind: "derived",
4888
+ name: declaration.id.name,
4889
+ value: declaration.init
4890
+ });
3499
4891
  }
3500
4892
  }
3501
4893
  if (states.length === 0) return "no local useState binding was found";
@@ -3634,8 +5026,413 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3634
5026
  }
3635
5027
  if (blockAnalysis.reason) return blockAnalysis.reason;
3636
5028
  if (analysis.reason) return analysis.reason;
5029
+ const hintedStateIndices = new Set(
5030
+ (blockAnalysis.plans || []).filter(
5031
+ (plan) => plan.kind === "keyed-rows" && plan.collectionDependency !== void 0
5032
+ ).map((plan) => plan.collectionDependency)
5033
+ );
5034
+ const hintedRoot = rewriteKeyedMapUpdateHints(
5035
+ expandedReactiveRoot,
5036
+ hintedStateIndices,
5037
+ statesBySetter,
5038
+ keyedMapUpdateIdentifier,
5039
+ safeGlobals
5040
+ );
5041
+ let appliedKeyedMapUpdateHints = 0;
5042
+ if (hintedRoot.count > 0) {
5043
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5044
+ hintedRoot.root,
5045
+ reactiveByValue,
5046
+ safeGlobals,
5047
+ listNames,
5048
+ allowedComponentNames
5049
+ );
5050
+ const hintedAnalysis = analyzeHostTree(
5051
+ hintedRoot.root,
5052
+ reactiveByValue,
5053
+ safeGlobals,
5054
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5055
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5056
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5057
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5058
+ );
5059
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5060
+ expandedReactiveRoot = hintedRoot.root;
5061
+ blockAnalysis = hintedBlockAnalysis;
5062
+ analysis = hintedAnalysis;
5063
+ appliedKeyedMapUpdateHints = hintedRoot.count;
5064
+ optimizationCounts.keyedMapUpdateHints += hintedRoot.count;
5065
+ }
5066
+ }
5067
+ const appendHintedStateIndices = new Set(hintedStateIndices);
5068
+ for (const plan of blockAnalysis.plans || []) {
5069
+ if (plan.kind !== "keyed-rows" || plan.collectionDependency === void 0) continue;
5070
+ const keyExpression = returnedExpression(plan.keyCallback);
5071
+ if (!keyExpression || collectStateDependencies(keyExpression, reactiveByValue).includes(plan.collectionDependency)) {
5072
+ appendHintedStateIndices.delete(plan.collectionDependency);
5073
+ }
5074
+ }
5075
+ const appendHintedRoot = rewriteKeyedArrayAppendHints(
5076
+ expandedReactiveRoot,
5077
+ appendHintedStateIndices,
5078
+ statesBySetter,
5079
+ keyedArrayAppendIdentifier,
5080
+ safeGlobals
5081
+ );
5082
+ let appliedKeyedArrayAppendHints = 0;
5083
+ if (appendHintedRoot.count > 0) {
5084
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5085
+ appendHintedRoot.root,
5086
+ reactiveByValue,
5087
+ safeGlobals,
5088
+ listNames,
5089
+ allowedComponentNames
5090
+ );
5091
+ const hintedAnalysis = analyzeHostTree(
5092
+ appendHintedRoot.root,
5093
+ reactiveByValue,
5094
+ safeGlobals,
5095
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5096
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5097
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5098
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5099
+ );
5100
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5101
+ expandedReactiveRoot = appendHintedRoot.root;
5102
+ blockAnalysis = hintedBlockAnalysis;
5103
+ analysis = hintedAnalysis;
5104
+ appliedKeyedArrayAppendHints = appendHintedRoot.count;
5105
+ optimizationCounts.keyedArrayAppendHints += appendHintedRoot.count;
5106
+ }
5107
+ }
5108
+ const shiftedIndexIndependentStateIndices = new Set(hintedStateIndices);
5109
+ for (const plan of blockAnalysis.plans || []) {
5110
+ if (plan.kind !== "keyed-rows" || plan.collectionDependency === void 0) continue;
5111
+ const keyExpression = returnedExpression(plan.keyCallback);
5112
+ if (plan.renderCallback.params.length > 1 || !keyExpression || collectStateDependencies(keyExpression, reactiveByValue).includes(plan.collectionDependency)) {
5113
+ shiftedIndexIndependentStateIndices.delete(plan.collectionDependency);
5114
+ }
5115
+ }
5116
+ const prependHintedRoot = rewriteKeyedArrayPrependHints(
5117
+ expandedReactiveRoot,
5118
+ shiftedIndexIndependentStateIndices,
5119
+ statesBySetter,
5120
+ keyedArrayPrependIdentifier,
5121
+ safeGlobals
5122
+ );
5123
+ let appliedKeyedArrayPrependHints = 0;
5124
+ let appliedPrependHintedStateIndices = /* @__PURE__ */ new Set();
5125
+ if (prependHintedRoot.count > 0) {
5126
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5127
+ prependHintedRoot.root,
5128
+ reactiveByValue,
5129
+ safeGlobals,
5130
+ listNames,
5131
+ allowedComponentNames
5132
+ );
5133
+ const hintedAnalysis = analyzeHostTree(
5134
+ prependHintedRoot.root,
5135
+ reactiveByValue,
5136
+ safeGlobals,
5137
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5138
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5139
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5140
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5141
+ );
5142
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5143
+ expandedReactiveRoot = prependHintedRoot.root;
5144
+ blockAnalysis = hintedBlockAnalysis;
5145
+ analysis = hintedAnalysis;
5146
+ appliedKeyedArrayPrependHints = prependHintedRoot.count;
5147
+ appliedPrependHintedStateIndices = prependHintedRoot.stateIndices;
5148
+ optimizationCounts.keyedArrayPrependHints += prependHintedRoot.count;
5149
+ }
5150
+ }
5151
+ const rollingWindowHintedRoot = rewriteKeyedArrayRollingWindowHints(
5152
+ expandedReactiveRoot,
5153
+ shiftedIndexIndependentStateIndices,
5154
+ statesBySetter,
5155
+ keyedArraySliceIdentifier,
5156
+ keyedArrayRollingWindowIdentifier,
5157
+ safeGlobals
5158
+ );
5159
+ let appliedKeyedArrayRollingWindowHints = 0;
5160
+ let appliedRollingWindowHintedStateIndices = /* @__PURE__ */ new Set();
5161
+ if (rollingWindowHintedRoot.count > 0) {
5162
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5163
+ rollingWindowHintedRoot.root,
5164
+ reactiveByValue,
5165
+ safeGlobals,
5166
+ listNames,
5167
+ allowedComponentNames
5168
+ );
5169
+ const hintedAnalysis = analyzeHostTree(
5170
+ rollingWindowHintedRoot.root,
5171
+ reactiveByValue,
5172
+ safeGlobals,
5173
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5174
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5175
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5176
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5177
+ );
5178
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5179
+ expandedReactiveRoot = rollingWindowHintedRoot.root;
5180
+ blockAnalysis = hintedBlockAnalysis;
5181
+ analysis = hintedAnalysis;
5182
+ appliedKeyedArrayRollingWindowHints = rollingWindowHintedRoot.count;
5183
+ appliedRollingWindowHintedStateIndices = rollingWindowHintedRoot.stateIndices;
5184
+ optimizationCounts.keyedArrayRollingWindowHints += rollingWindowHintedRoot.count;
5185
+ }
5186
+ }
5187
+ const positionHintedRoot = rewriteKeyedArrayPositionHints(
5188
+ expandedReactiveRoot,
5189
+ shiftedIndexIndependentStateIndices,
5190
+ statesBySetter,
5191
+ keyedArrayPositionIdentifier,
5192
+ safeGlobals
5193
+ );
5194
+ let appliedKeyedArrayPositionHints = 0;
5195
+ let appliedPositionHintedStateIndices = /* @__PURE__ */ new Set();
5196
+ if (positionHintedRoot.count > 0) {
5197
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5198
+ positionHintedRoot.root,
5199
+ reactiveByValue,
5200
+ safeGlobals,
5201
+ listNames,
5202
+ allowedComponentNames
5203
+ );
5204
+ const hintedAnalysis = analyzeHostTree(
5205
+ positionHintedRoot.root,
5206
+ reactiveByValue,
5207
+ safeGlobals,
5208
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5209
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5210
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5211
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5212
+ );
5213
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5214
+ expandedReactiveRoot = positionHintedRoot.root;
5215
+ blockAnalysis = hintedBlockAnalysis;
5216
+ analysis = hintedAnalysis;
5217
+ appliedKeyedArrayPositionHints = positionHintedRoot.count;
5218
+ appliedPositionHintedStateIndices = positionHintedRoot.stateIndices;
5219
+ optimizationCounts.keyedArrayPositionHints += positionHintedRoot.count;
5220
+ }
5221
+ }
5222
+ const reorderHintedRoot = rewriteKeyedArrayReorderHints(
5223
+ expandedReactiveRoot,
5224
+ shiftedIndexIndependentStateIndices,
5225
+ statesBySetter,
5226
+ keyedArrayReorderIdentifier
5227
+ );
5228
+ let appliedKeyedArrayReorderHints = 0;
5229
+ let appliedReorderHintedStateIndices = /* @__PURE__ */ new Set();
5230
+ if (reorderHintedRoot.count > 0) {
5231
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5232
+ reorderHintedRoot.root,
5233
+ reactiveByValue,
5234
+ safeGlobals,
5235
+ listNames,
5236
+ allowedComponentNames
5237
+ );
5238
+ const hintedAnalysis = analyzeHostTree(
5239
+ reorderHintedRoot.root,
5240
+ reactiveByValue,
5241
+ safeGlobals,
5242
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5243
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5244
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5245
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5246
+ );
5247
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5248
+ expandedReactiveRoot = reorderHintedRoot.root;
5249
+ blockAnalysis = hintedBlockAnalysis;
5250
+ analysis = hintedAnalysis;
5251
+ appliedKeyedArrayReorderHints = reorderHintedRoot.count;
5252
+ appliedReorderHintedStateIndices = reorderHintedRoot.stateIndices;
5253
+ optimizationCounts.keyedArrayReorderHints += reorderHintedRoot.count;
5254
+ }
5255
+ }
5256
+ const sortHintedRoot = rewriteKeyedArraySortHints(
5257
+ expandedReactiveRoot,
5258
+ shiftedIndexIndependentStateIndices,
5259
+ statesBySetter,
5260
+ keyedArraySortIdentifier,
5261
+ safeGlobals
5262
+ );
5263
+ let appliedKeyedArraySortHints = 0;
5264
+ let appliedSortHintedStateIndices = /* @__PURE__ */ new Set();
5265
+ if (sortHintedRoot.count > 0) {
5266
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5267
+ sortHintedRoot.root,
5268
+ reactiveByValue,
5269
+ safeGlobals,
5270
+ listNames,
5271
+ allowedComponentNames
5272
+ );
5273
+ const hintedAnalysis = analyzeHostTree(
5274
+ sortHintedRoot.root,
5275
+ reactiveByValue,
5276
+ safeGlobals,
5277
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5278
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5279
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5280
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5281
+ );
5282
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5283
+ expandedReactiveRoot = sortHintedRoot.root;
5284
+ blockAnalysis = hintedBlockAnalysis;
5285
+ analysis = hintedAnalysis;
5286
+ appliedKeyedArraySortHints = sortHintedRoot.count;
5287
+ appliedSortHintedStateIndices = sortHintedRoot.stateIndices;
5288
+ optimizationCounts.keyedArraySortHints += sortHintedRoot.count;
5289
+ }
5290
+ }
5291
+ const sliceHintedRoot = rewriteKeyedArraySliceHints(
5292
+ expandedReactiveRoot,
5293
+ shiftedIndexIndependentStateIndices,
5294
+ statesBySetter,
5295
+ keyedArraySliceIdentifier
5296
+ );
5297
+ let appliedKeyedArraySliceHints = 0;
5298
+ let appliedSliceHintedStateIndices = /* @__PURE__ */ new Set();
5299
+ if (sliceHintedRoot.count > 0) {
5300
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5301
+ sliceHintedRoot.root,
5302
+ reactiveByValue,
5303
+ safeGlobals,
5304
+ listNames,
5305
+ allowedComponentNames
5306
+ );
5307
+ const hintedAnalysis = analyzeHostTree(
5308
+ sliceHintedRoot.root,
5309
+ reactiveByValue,
5310
+ safeGlobals,
5311
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5312
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5313
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5314
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5315
+ );
5316
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5317
+ expandedReactiveRoot = sliceHintedRoot.root;
5318
+ blockAnalysis = hintedBlockAnalysis;
5319
+ analysis = hintedAnalysis;
5320
+ appliedKeyedArraySliceHints = sliceHintedRoot.count;
5321
+ appliedSliceHintedStateIndices = sliceHintedRoot.stateIndices;
5322
+ optimizationCounts.keyedArraySliceHints += sliceHintedRoot.count;
5323
+ }
5324
+ }
5325
+ const filterHintedRoot = rewriteKeyedArrayFilterHints(
5326
+ expandedReactiveRoot,
5327
+ shiftedIndexIndependentStateIndices,
5328
+ statesBySetter,
5329
+ keyedArrayFilterIdentifier,
5330
+ safeGlobals
5331
+ );
5332
+ let appliedKeyedArrayFilterHints = 0;
5333
+ let appliedFilterHintedStateIndices = /* @__PURE__ */ new Set();
5334
+ if (filterHintedRoot.count > 0) {
5335
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5336
+ filterHintedRoot.root,
5337
+ reactiveByValue,
5338
+ safeGlobals,
5339
+ listNames,
5340
+ allowedComponentNames
5341
+ );
5342
+ const hintedAnalysis = analyzeHostTree(
5343
+ filterHintedRoot.root,
5344
+ reactiveByValue,
5345
+ safeGlobals,
5346
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5347
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5348
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5349
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5350
+ );
5351
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5352
+ expandedReactiveRoot = filterHintedRoot.root;
5353
+ blockAnalysis = hintedBlockAnalysis;
5354
+ analysis = hintedAnalysis;
5355
+ appliedKeyedArrayFilterHints = filterHintedRoot.count;
5356
+ appliedFilterHintedStateIndices = filterHintedRoot.stateIndices;
5357
+ optimizationCounts.keyedArrayFilterHints += filterHintedRoot.count;
5358
+ }
5359
+ }
5360
+ const keyedCollectionTargetKinds = /* @__PURE__ */ new Map();
5361
+ const conflictingKeyedCollectionTargets = /* @__PURE__ */ new Set();
5362
+ for (const plan of blockAnalysis.plans || []) {
5363
+ if (plan.kind !== "keyed-rows") continue;
5364
+ for (const binding of plan.bindings) {
5365
+ const target = binding.membershipTarget || binding.mapLookupTarget;
5366
+ if (!target) continue;
5367
+ const kind = binding.membershipTarget ? "set" : "map";
5368
+ const previousKind = keyedCollectionTargetKinds.get(target.dependency);
5369
+ if (previousKind && previousKind !== kind) {
5370
+ conflictingKeyedCollectionTargets.add(target.dependency);
5371
+ } else {
5372
+ keyedCollectionTargetKinds.set(target.dependency, kind);
5373
+ }
5374
+ }
5375
+ }
5376
+ for (const dependency of conflictingKeyedCollectionTargets) {
5377
+ keyedCollectionTargetKinds.delete(dependency);
5378
+ }
5379
+ const keyedCollectionHintedRoot = rewriteKeyedCollectionUpdateHints(
5380
+ expandedReactiveRoot,
5381
+ keyedCollectionTargetKinds,
5382
+ states,
5383
+ statesBySetter,
5384
+ keyedCollectionUpdateIdentifier,
5385
+ keyedCollectionMutationIdentifier,
5386
+ globalCollections
5387
+ );
5388
+ if (keyedCollectionHintedRoot.count > 0) {
5389
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5390
+ keyedCollectionHintedRoot.root,
5391
+ reactiveByValue,
5392
+ safeGlobals,
5393
+ listNames,
5394
+ allowedComponentNames
5395
+ );
5396
+ const hintedAnalysis = analyzeHostTree(
5397
+ keyedCollectionHintedRoot.root,
5398
+ reactiveByValue,
5399
+ safeGlobals,
5400
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5401
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5402
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5403
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5404
+ );
5405
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5406
+ expandedReactiveRoot = keyedCollectionHintedRoot.root;
5407
+ blockAnalysis = hintedBlockAnalysis;
5408
+ analysis = hintedAnalysis;
5409
+ optimizationCounts.keyedCollectionUpdateHints += keyedCollectionHintedRoot.count;
5410
+ }
5411
+ }
3637
5412
  const blockPlans = blockAnalysis.plans || [];
3638
- const runtimeFeatures = runtimeFeaturesForPlans(blockPlans);
5413
+ optimizationCounts.keyedIdentityTargets += blockPlans.reduce(
5414
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.identityTarget !== void 0).length : 0),
5415
+ 0
5416
+ );
5417
+ optimizationCounts.keyedMembershipTargets += blockPlans.reduce(
5418
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.membershipTarget !== void 0).length : 0),
5419
+ 0
5420
+ );
5421
+ optimizationCounts.keyedMapLookupTargets += blockPlans.reduce(
5422
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.mapLookupTarget !== void 0).length : 0),
5423
+ 0
5424
+ );
5425
+ const hasKeyedUpdateHints = appliedKeyedMapUpdateHints > 0 || appliedKeyedArrayAppendHints > 0 || appliedKeyedArrayFilterHints > 0 || appliedKeyedArrayPrependHints > 0 || appliedKeyedArrayPositionHints > 0 || appliedKeyedArrayReorderHints > 0 || appliedKeyedArraySortHints > 0 || appliedKeyedArrayRollingWindowHints > 0 || appliedKeyedArraySliceHints > 0;
5426
+ const hasKeyedArrayRemovalHints = appliedKeyedArrayFilterHints > 0 || appliedKeyedArrayRollingWindowHints > 0 || appliedKeyedArraySliceHints > 0;
5427
+ const runtimeFeatures = runtimeFeaturesForPlans(
5428
+ blockPlans,
5429
+ hasKeyedUpdateHints,
5430
+ hasKeyedArrayRemovalHints,
5431
+ appliedKeyedArrayPrependHints > 0,
5432
+ appliedKeyedArrayPositionHints > 0,
5433
+ appliedKeyedArrayReorderHints > 0 || appliedKeyedArraySortHints > 0,
5434
+ appliedKeyedArrayRollingWindowHints > 0
5435
+ );
3639
5436
  markShortCircuitBindings(analysis.bindings || []);
3640
5437
  assignStableBindingTargets(analysis.bindings || []);
3641
5438
  const stateParameter = path.scope.generateUidIdentifier("farmState");
@@ -3648,11 +5445,21 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3648
5445
  blockParameter,
3649
5446
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set()
3650
5447
  );
5448
+ const appliedRemovalHintedStateIndices = /* @__PURE__ */ new Set([
5449
+ ...appliedFilterHintedStateIndices,
5450
+ ...appliedRollingWindowHintedStateIndices,
5451
+ ...appliedSliceHintedStateIndices
5452
+ ]);
3651
5453
  const rootWithBlocks = lowerComposableBlocks(
3652
5454
  rootWithTargets,
3653
5455
  blockPlans,
3654
5456
  blockParameter,
3655
- listNames
5457
+ listNames,
5458
+ hasKeyedUpdateHints,
5459
+ appliedRemovalHintedStateIndices,
5460
+ appliedPrependHintedStateIndices,
5461
+ appliedPositionHintedStateIndices,
5462
+ /* @__PURE__ */ new Set([...appliedReorderHintedStateIndices, ...appliedSortHintedStateIndices])
3656
5463
  );
3657
5464
  const rewrittenRoot = rewriteStateAccess(
3658
5465
  rootWithBlocks,
@@ -3790,6 +5597,21 @@ function collectCandidates(programPath) {
3790
5597
  async function compileReactModule(code, id, options) {
3791
5598
  const compiled = [];
3792
5599
  const diagnostics = [];
5600
+ const optimizationCounts = {
5601
+ keyedArrayAppendHints: 0,
5602
+ keyedArrayFilterHints: 0,
5603
+ keyedArrayPrependHints: 0,
5604
+ keyedArrayPositionHints: 0,
5605
+ keyedArrayReorderHints: 0,
5606
+ keyedArraySortHints: 0,
5607
+ keyedArrayRollingWindowHints: 0,
5608
+ keyedArraySliceHints: 0,
5609
+ keyedCollectionUpdateHints: 0,
5610
+ keyedIdentityTargets: 0,
5611
+ keyedMapLookupTargets: 0,
5612
+ keyedMembershipTargets: 0,
5613
+ keyedMapUpdateHints: 0
5614
+ };
3793
5615
  const plugin = () => ({
3794
5616
  name: "farm-react-aot",
3795
5617
  visitor: {
@@ -3819,6 +5641,39 @@ async function compileReactModule(code, id, options) {
3819
5641
  }
3820
5642
  const candidates = collectCandidates(programPath);
3821
5643
  const createComponentIdentifier = programPath.scope.generateUidIdentifier("createCompiledComponent");
5644
+ const keyedMapUpdateIdentifier = programPath.scope.generateUidIdentifier(
5645
+ "createCompilerKeyedMapUpdate"
5646
+ );
5647
+ const keyedArrayAppendIdentifier = programPath.scope.generateUidIdentifier(
5648
+ "createCompilerKeyedArrayAppend"
5649
+ );
5650
+ const keyedArrayFilterIdentifier = programPath.scope.generateUidIdentifier(
5651
+ "createCompilerKeyedArrayFilter"
5652
+ );
5653
+ const keyedArrayPrependIdentifier = programPath.scope.generateUidIdentifier(
5654
+ "createCompilerKeyedArrayPrepend"
5655
+ );
5656
+ const keyedArrayPositionIdentifier = programPath.scope.generateUidIdentifier(
5657
+ "createCompilerKeyedArrayPositionUpdate"
5658
+ );
5659
+ const keyedArrayReorderIdentifier = programPath.scope.generateUidIdentifier(
5660
+ "createCompilerKeyedArrayReorder"
5661
+ );
5662
+ const keyedArraySortIdentifier = programPath.scope.generateUidIdentifier(
5663
+ "createCompilerKeyedArraySort"
5664
+ );
5665
+ const keyedArrayRollingWindowIdentifier = programPath.scope.generateUidIdentifier(
5666
+ "createCompilerKeyedArrayRollingWindow"
5667
+ );
5668
+ const keyedArraySliceIdentifier = programPath.scope.generateUidIdentifier(
5669
+ "createCompilerKeyedArraySlice"
5670
+ );
5671
+ const keyedCollectionUpdateIdentifier = programPath.scope.generateUidIdentifier(
5672
+ "createCompilerKeyedCollectionUpdate"
5673
+ );
5674
+ const keyedCollectionMutationIdentifier = programPath.scope.generateUidIdentifier(
5675
+ "applyCompilerKeyedCollectionMutation"
5676
+ );
3822
5677
  const runtimeFeatureIdentifiers = new Map(
3823
5678
  Object.entries(COMPILER_RUNTIME_FEATURE_EXPORTS).map(([feature, exportName]) => [
3824
5679
  feature,
@@ -3836,8 +5691,20 @@ async function compileReactModule(code, id, options) {
3836
5691
  const reason = compileCandidate(
3837
5692
  candidate,
3838
5693
  createComponentIdentifier,
5694
+ keyedMapUpdateIdentifier,
5695
+ keyedArrayAppendIdentifier,
5696
+ keyedArrayFilterIdentifier,
5697
+ keyedArrayPrependIdentifier,
5698
+ keyedArrayPositionIdentifier,
5699
+ keyedArrayReorderIdentifier,
5700
+ keyedArraySortIdentifier,
5701
+ keyedArrayRollingWindowIdentifier,
5702
+ keyedArraySliceIdentifier,
5703
+ keyedCollectionUpdateIdentifier,
5704
+ keyedCollectionMutationIdentifier,
3839
5705
  runtimeFeatureIdentifiers,
3840
5706
  usedRuntimeFeatures,
5707
+ optimizationCounts,
3841
5708
  useStateNames,
3842
5709
  reactNames,
3843
5710
  listNames,
@@ -3863,6 +5730,70 @@ async function compileReactModule(code, id, options) {
3863
5730
  createComponentIdentifier,
3864
5731
  types.identifier("createCompiledComponentWithFeatures")
3865
5732
  ),
5733
+ ...optimizationCounts.keyedMapUpdateHints > 0 ? [
5734
+ types.importSpecifier(
5735
+ keyedMapUpdateIdentifier,
5736
+ types.identifier("createCompilerKeyedMapUpdate")
5737
+ )
5738
+ ] : [],
5739
+ ...optimizationCounts.keyedArrayAppendHints > 0 ? [
5740
+ types.importSpecifier(
5741
+ keyedArrayAppendIdentifier,
5742
+ types.identifier("createCompilerKeyedArrayAppend")
5743
+ )
5744
+ ] : [],
5745
+ ...optimizationCounts.keyedArrayFilterHints > 0 ? [
5746
+ types.importSpecifier(
5747
+ keyedArrayFilterIdentifier,
5748
+ types.identifier("createCompilerKeyedArrayFilter")
5749
+ )
5750
+ ] : [],
5751
+ ...optimizationCounts.keyedArrayPrependHints > 0 ? [
5752
+ types.importSpecifier(
5753
+ keyedArrayPrependIdentifier,
5754
+ types.identifier("createCompilerKeyedArrayPrepend")
5755
+ )
5756
+ ] : [],
5757
+ ...optimizationCounts.keyedArrayPositionHints > 0 ? [
5758
+ types.importSpecifier(
5759
+ keyedArrayPositionIdentifier,
5760
+ types.identifier("createCompilerKeyedArrayPositionUpdate")
5761
+ )
5762
+ ] : [],
5763
+ ...optimizationCounts.keyedArrayReorderHints > 0 ? [
5764
+ types.importSpecifier(
5765
+ keyedArrayReorderIdentifier,
5766
+ types.identifier("createCompilerKeyedArrayReorder")
5767
+ )
5768
+ ] : [],
5769
+ ...optimizationCounts.keyedArraySortHints > 0 ? [
5770
+ types.importSpecifier(
5771
+ keyedArraySortIdentifier,
5772
+ types.identifier("createCompilerKeyedArraySort")
5773
+ )
5774
+ ] : [],
5775
+ ...optimizationCounts.keyedArrayRollingWindowHints > 0 ? [
5776
+ types.importSpecifier(
5777
+ keyedArrayRollingWindowIdentifier,
5778
+ types.identifier("createCompilerKeyedArrayRollingWindow")
5779
+ )
5780
+ ] : [],
5781
+ ...optimizationCounts.keyedArraySliceHints > 0 || optimizationCounts.keyedArrayRollingWindowHints > 0 ? [
5782
+ types.importSpecifier(
5783
+ keyedArraySliceIdentifier,
5784
+ types.identifier("createCompilerKeyedArraySlice")
5785
+ )
5786
+ ] : [],
5787
+ ...optimizationCounts.keyedCollectionUpdateHints > 0 ? [
5788
+ types.importSpecifier(
5789
+ keyedCollectionUpdateIdentifier,
5790
+ types.identifier("createCompilerKeyedCollectionUpdate")
5791
+ ),
5792
+ types.importSpecifier(
5793
+ keyedCollectionMutationIdentifier,
5794
+ types.identifier("applyCompilerKeyedCollectionMutation")
5795
+ )
5796
+ ] : [],
3866
5797
  ...[...usedRuntimeFeatures].sort().map(
3867
5798
  (feature) => types.importSpecifier(
3868
5799
  types.cloneNode(runtimeFeatureIdentifiers.get(feature)),
@@ -3899,7 +5830,8 @@ async function compileReactModule(code, id, options) {
3899
5830
  code: result?.code || code,
3900
5831
  map: result?.map || null,
3901
5832
  compiled,
3902
- diagnostics
5833
+ diagnostics,
5834
+ optimizations: optimizationCounts
3903
5835
  };
3904
5836
  }
3905
5837
  function projectRelativeId(projectRoot, id) {
@@ -3910,10 +5842,36 @@ function createReactCompilerReport(projectRoot, observations) {
3910
5842
  let componentsConsidered = 0;
3911
5843
  let compiled = 0;
3912
5844
  let fallback = 0;
5845
+ let keyedArrayAppendHints = 0;
5846
+ let keyedArrayFilterHints = 0;
5847
+ let keyedArrayPrependHints = 0;
5848
+ let keyedArrayPositionHints = 0;
5849
+ let keyedArrayReorderHints = 0;
5850
+ let keyedArraySortHints = 0;
5851
+ let keyedArrayRollingWindowHints = 0;
5852
+ let keyedArraySliceHints = 0;
5853
+ let keyedCollectionUpdateHints = 0;
5854
+ let keyedIdentityTargets = 0;
5855
+ let keyedMapLookupTargets = 0;
5856
+ let keyedMembershipTargets = 0;
5857
+ let keyedMapUpdateHints = 0;
3913
5858
  const modules = [...observations].map(({ id, result }) => {
3914
5859
  componentsConsidered += result.compiled.length + result.diagnostics.length;
3915
5860
  compiled += result.compiled.length;
3916
5861
  fallback += result.diagnostics.length;
5862
+ keyedArrayAppendHints += result.optimizations.keyedArrayAppendHints || 0;
5863
+ keyedArrayFilterHints += result.optimizations.keyedArrayFilterHints || 0;
5864
+ keyedArrayPrependHints += result.optimizations.keyedArrayPrependHints || 0;
5865
+ keyedArrayPositionHints += result.optimizations.keyedArrayPositionHints || 0;
5866
+ keyedArrayReorderHints += result.optimizations.keyedArrayReorderHints || 0;
5867
+ keyedArraySortHints += result.optimizations.keyedArraySortHints || 0;
5868
+ keyedArrayRollingWindowHints += result.optimizations.keyedArrayRollingWindowHints || 0;
5869
+ keyedArraySliceHints += result.optimizations.keyedArraySliceHints || 0;
5870
+ keyedCollectionUpdateHints += result.optimizations.keyedCollectionUpdateHints || 0;
5871
+ keyedIdentityTargets += result.optimizations.keyedIdentityTargets || 0;
5872
+ keyedMapLookupTargets += result.optimizations.keyedMapLookupTargets || 0;
5873
+ keyedMembershipTargets += result.optimizations.keyedMembershipTargets || 0;
5874
+ keyedMapUpdateHints += result.optimizations.keyedMapUpdateHints;
3917
5875
  const moduleId = projectRelativeId(projectRoot, id);
3918
5876
  const fallbacks = result.diagnostics.map((diagnostic) => {
3919
5877
  reasonCounts.set(diagnostic.reason, (reasonCounts.get(diagnostic.reason) || 0) + 1);
@@ -3922,6 +5880,7 @@ function createReactCompilerReport(projectRoot, observations) {
3922
5880
  return {
3923
5881
  id: moduleId,
3924
5882
  compiled: [...result.compiled].sort(),
5883
+ optimizations: result.optimizations,
3925
5884
  fallbacks
3926
5885
  };
3927
5886
  }).sort((left, right) => left.id.localeCompare(right.id));
@@ -3932,7 +5891,20 @@ function createReactCompilerReport(projectRoot, observations) {
3932
5891
  modules: modules.length,
3933
5892
  componentsConsidered,
3934
5893
  compiled,
3935
- fallback
5894
+ fallback,
5895
+ keyedArrayAppendHints,
5896
+ keyedArrayFilterHints,
5897
+ keyedArrayPrependHints,
5898
+ keyedArrayPositionHints,
5899
+ keyedArrayReorderHints,
5900
+ keyedArraySortHints,
5901
+ keyedArrayRollingWindowHints,
5902
+ keyedArraySliceHints,
5903
+ keyedCollectionUpdateHints,
5904
+ keyedIdentityTargets,
5905
+ keyedMapLookupTargets,
5906
+ keyedMembershipTargets,
5907
+ keyedMapUpdateHints
3936
5908
  },
3937
5909
  fallbackReasons,
3938
5910
  modules