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

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,879 @@ 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 rewriteKeyedArrayPositionHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
847
+ if (hintedStateIndices.size === 0) {
848
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
849
+ }
850
+ const file = expressionFile(types.cloneNode(root, true));
851
+ const stateIndices = /* @__PURE__ */ new Set();
852
+ let count = 0;
853
+ traverse(file, {
854
+ CallExpression(path) {
855
+ const callee = path.get("callee");
856
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
857
+ const state = statesBySetter.get(callee.node.name);
858
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
859
+ return;
860
+ }
861
+ const updater = path.node.arguments[0];
862
+ 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)) {
863
+ return;
864
+ }
865
+ const methodName = updater.body.callee.property.name;
866
+ const args = updater.body.arguments;
867
+ const kind = methodName === "with" && args.length === 2 ? "replace" : methodName === "toSpliced" && args.length === 3 && types.isNumericLiteral(args[1], { value: 0 }) ? "insert" : void 0;
868
+ if (!kind || !types.isExpression(args[0]) || !types.isExpression(args.at(-1))) return;
869
+ const position = staticSliceIndex(args[0]);
870
+ const item = args.at(-1);
871
+ if (position === void 0 || !types.isExpression(item) || validateDerivedExpression(item, safeGlobals) !== void 0) {
872
+ return;
873
+ }
874
+ const previous = types.cloneNode(updater.params[0]);
875
+ const method = path.scope.generateUidIdentifier(
876
+ methodName === "with" ? "farmWith" : "farmToSpliced"
877
+ );
878
+ path.node.arguments[0] = types.arrowFunctionExpression(
879
+ [types.cloneNode(previous)],
880
+ types.blockStatement([
881
+ types.variableDeclaration("const", [
882
+ types.variableDeclarator(
883
+ types.cloneNode(method),
884
+ types.memberExpression(types.cloneNode(previous), types.identifier(methodName))
885
+ )
886
+ ]),
887
+ types.returnStatement(
888
+ types.callExpression(types.cloneNode(helperIdentifier), [
889
+ types.cloneNode(previous),
890
+ types.cloneNode(method),
891
+ types.stringLiteral(kind),
892
+ ...args.map((argument) => types.cloneNode(argument, true))
893
+ ])
894
+ )
895
+ ])
896
+ );
897
+ count += 1;
898
+ stateIndices.add(state.index);
899
+ path.skip();
900
+ }
901
+ });
902
+ return {
903
+ root: file.program.body[0].expression,
904
+ count,
905
+ stateIndices
906
+ };
907
+ }
908
+ function rewriteKeyedArrayReorderHints(root, hintedStateIndices, statesBySetter, helperIdentifier) {
909
+ if (hintedStateIndices.size === 0) {
910
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
911
+ }
912
+ const file = expressionFile(types.cloneNode(root, true));
913
+ const stateIndices = /* @__PURE__ */ new Set();
914
+ let count = 0;
915
+ traverse(file, {
916
+ CallExpression(path) {
917
+ const callee = path.get("callee");
918
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
919
+ const state = statesBySetter.get(callee.node.name);
920
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
921
+ return;
922
+ }
923
+ const updater = path.node.arguments[0];
924
+ 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" })) {
925
+ return;
926
+ }
927
+ const previous = types.cloneNode(updater.params[0]);
928
+ const method = path.scope.generateUidIdentifier("farmToReversed");
929
+ path.node.arguments[0] = types.arrowFunctionExpression(
930
+ [types.cloneNode(previous)],
931
+ types.blockStatement([
932
+ types.variableDeclaration("const", [
933
+ types.variableDeclarator(
934
+ types.cloneNode(method),
935
+ types.memberExpression(types.cloneNode(previous), types.identifier("toReversed"))
936
+ )
937
+ ]),
938
+ types.returnStatement(
939
+ types.callExpression(types.cloneNode(helperIdentifier), [
940
+ types.cloneNode(previous),
941
+ types.cloneNode(method)
942
+ ])
943
+ )
944
+ ])
945
+ );
946
+ count += 1;
947
+ stateIndices.add(state.index);
948
+ path.skip();
949
+ }
950
+ });
951
+ return {
952
+ root: file.program.body[0].expression,
953
+ count,
954
+ stateIndices
955
+ };
956
+ }
957
+ function rewriteKeyedArraySliceHints(root, hintedStateIndices, statesBySetter, helperIdentifier) {
958
+ if (hintedStateIndices.size === 0) {
959
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
960
+ }
961
+ const file = expressionFile(types.cloneNode(root, true));
962
+ const stateIndices = /* @__PURE__ */ new Set();
963
+ let count = 0;
964
+ traverse(file, {
965
+ CallExpression(path) {
966
+ const callee = path.get("callee");
967
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
968
+ const state = statesBySetter.get(callee.node.name);
969
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
970
+ return;
971
+ }
972
+ const updater = path.node.arguments[0];
973
+ 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" })) {
974
+ return;
975
+ }
976
+ const indexes = [];
977
+ for (const argument of updater.body.arguments) {
978
+ if (!types.isExpression(argument)) return;
979
+ const value = staticSliceIndex(argument);
980
+ if (value === void 0) return;
981
+ indexes.push(value);
982
+ }
983
+ if (indexes.length === 1 && indexes[0] === 0) return;
984
+ const previous = types.cloneNode(updater.params[0]);
985
+ const sliceMethod = path.scope.generateUidIdentifier("farmSlice");
986
+ path.node.arguments[0] = types.arrowFunctionExpression(
987
+ [types.cloneNode(previous)],
988
+ types.blockStatement([
989
+ types.variableDeclaration("const", [
990
+ types.variableDeclarator(
991
+ types.cloneNode(sliceMethod),
992
+ types.memberExpression(types.cloneNode(previous), types.identifier("slice"))
993
+ )
994
+ ]),
995
+ types.returnStatement(
996
+ types.callExpression(types.cloneNode(helperIdentifier), [
997
+ types.cloneNode(previous),
998
+ types.cloneNode(sliceMethod),
999
+ ...indexes.map((value) => types.numericLiteral(value))
1000
+ ])
1001
+ )
1002
+ ])
1003
+ );
1004
+ count += 1;
1005
+ stateIndices.add(state.index);
1006
+ path.skip();
1007
+ }
1008
+ });
1009
+ return {
1010
+ root: file.program.body[0].expression,
1011
+ count,
1012
+ stateIndices
1013
+ };
1014
+ }
1015
+ function rewriteKeyedArrayFilterHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
1016
+ if (hintedStateIndices.size === 0) {
1017
+ return { root: types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
1018
+ }
1019
+ const file = expressionFile(types.cloneNode(root, true));
1020
+ const stateIndices = /* @__PURE__ */ new Set();
1021
+ let count = 0;
1022
+ traverse(file, {
1023
+ CallExpression(path) {
1024
+ const callee = path.get("callee");
1025
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
1026
+ const state = statesBySetter.get(callee.node.name);
1027
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
1028
+ return;
1029
+ }
1030
+ const updater = path.node.arguments[0];
1031
+ 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, {
1032
+ name: updater.params[0].name
1033
+ }) || !types.isIdentifier(updater.body.callee.property, { name: "filter" })) {
1034
+ return;
1035
+ }
1036
+ const predicate = updater.body.arguments[0];
1037
+ 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)) {
1038
+ return;
1039
+ }
1040
+ const previous = types.cloneNode(updater.params[0]);
1041
+ const filterMethod = path.scope.generateUidIdentifier("farmFilter");
1042
+ path.node.arguments[0] = types.arrowFunctionExpression(
1043
+ [types.cloneNode(previous)],
1044
+ types.blockStatement([
1045
+ types.variableDeclaration("const", [
1046
+ types.variableDeclarator(
1047
+ types.cloneNode(filterMethod),
1048
+ types.memberExpression(types.cloneNode(previous), types.identifier("filter"))
1049
+ )
1050
+ ]),
1051
+ types.returnStatement(
1052
+ types.callExpression(types.cloneNode(helperIdentifier), [
1053
+ types.cloneNode(previous),
1054
+ types.cloneNode(filterMethod),
1055
+ types.cloneNode(predicate, true)
1056
+ ])
1057
+ )
1058
+ ])
1059
+ );
1060
+ count += 1;
1061
+ stateIndices.add(state.index);
1062
+ path.skip();
1063
+ }
1064
+ });
1065
+ return {
1066
+ root: file.program.body[0].expression,
1067
+ count,
1068
+ stateIndices
1069
+ };
1070
+ }
1071
+ function collectionConstructorName(kind) {
1072
+ return kind === "set" ? "Set" : "Map";
1073
+ }
1074
+ function isCollectionConstruction(expression, kind, previous) {
1075
+ if (!types.isNewExpression(expression) || !types.isIdentifier(expression.callee, {
1076
+ name: collectionConstructorName(kind)
1077
+ })) {
1078
+ return false;
1079
+ }
1080
+ if (!previous) return true;
1081
+ return expression.arguments.length === 1 && types.isIdentifier(expression.arguments[0], { name: previous.name });
1082
+ }
1083
+ function collectionStateInitializerIsOwned(initialValue, kind, constructorIsGlobal) {
1084
+ if (!initialValue || !constructorIsGlobal) return false;
1085
+ if (isCollectionConstruction(initialValue, kind)) return true;
1086
+ if (!types.isArrowFunctionExpression(initialValue) && !types.isFunctionExpression(initialValue)) {
1087
+ return false;
1088
+ }
1089
+ if (types.isBlockStatement(initialValue.body) && initialValue.body.body.length !== 1) return false;
1090
+ const value = returnedExpression(initialValue);
1091
+ return Boolean(value && isCollectionConstruction(value, kind));
1092
+ }
1093
+ function collectionStateHasOnlyOwnedReads(root, state, kind) {
1094
+ const file = expressionFile(types.cloneNode(root, true));
1095
+ let valid = true;
1096
+ const readMethods = kind === "set" ? /* @__PURE__ */ new Set(["has"]) : /* @__PURE__ */ new Set(["get", "has"]);
1097
+ traverse(file, {
1098
+ ReferencedIdentifier(path) {
1099
+ if (!valid || !types.isIdentifier(path.node) || path.node.name !== state.valueName || path.scope.hasBinding(path.node.name)) {
1100
+ return;
1101
+ }
1102
+ const parent = path.parentPath;
1103
+ if (parent.isNewExpression() && types.isIdentifier(parent.node.callee, {
1104
+ name: collectionConstructorName(kind)
1105
+ }) && parent.node.arguments.length === 1 && parent.node.arguments[0] === path.node) {
1106
+ return;
1107
+ }
1108
+ if (!parent.isMemberExpression() || parent.node.object !== path.node || parent.node.computed || !types.isIdentifier(parent.node.property)) {
1109
+ valid = false;
1110
+ path.stop();
1111
+ return;
1112
+ }
1113
+ const property = parent.node.property.name;
1114
+ if (property === "size") return;
1115
+ const call = parent.parentPath;
1116
+ if (!readMethods.has(property) || !call?.isCallExpression() || call.node.callee !== parent.node) {
1117
+ valid = false;
1118
+ path.stop();
1119
+ }
1120
+ }
1121
+ });
1122
+ return valid;
1123
+ }
1124
+ function collectionMutationOperation(kind, method) {
1125
+ if (kind === "set" && method === "add") return "set-add";
1126
+ if (kind === "set" && method === "delete") return "set-delete";
1127
+ if (kind === "map" && method === "set") return "map-set";
1128
+ if (kind === "map" && method === "delete") return "map-delete";
1129
+ return void 0;
1130
+ }
1131
+ function collectionMutationArgumentsAreValid(operation, args) {
1132
+ if (!operation || args.some((argument) => !types.isExpression(argument))) return false;
1133
+ return operation === "map-set" ? args.length === 2 : args.length === 1;
1134
+ }
1135
+ function prepareKeyedCollectionUpdater(updater, kind, updateHelper, mutationHelper) {
1136
+ if (updater.async || updater.generator || updater.params.length !== 1 || !types.isIdentifier(updater.params[0])) {
1137
+ return void 0;
1138
+ }
1139
+ const previous = updater.params[0];
1140
+ if (types.isExpression(updater.body)) {
1141
+ if (!types.isCallExpression(updater.body) || !types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !types.isIdentifier(updater.body.callee.property)) {
1142
+ return void 0;
1143
+ }
1144
+ const operation = collectionMutationOperation(kind, updater.body.callee.property.name);
1145
+ if (operation !== "set-add" && operation !== "map-set" || !collectionMutationArgumentsAreValid(operation, updater.body.arguments) || !isCollectionConstruction(updater.body.callee.object, kind, previous)) {
1146
+ return void 0;
1147
+ }
1148
+ const collection = uniqueLocalIdentifier("_farmCollection", [updater]);
1149
+ const body = types.blockStatement([
1150
+ types.variableDeclaration("const", [
1151
+ types.variableDeclarator(
1152
+ types.cloneNode(collection),
1153
+ types.cloneNode(updater.body.callee.object, true)
1154
+ )
1155
+ ]),
1156
+ types.returnStatement(
1157
+ types.callExpression(types.cloneNode(updateHelper), [
1158
+ types.cloneNode(previous),
1159
+ types.callExpression(types.cloneNode(mutationHelper), [
1160
+ types.cloneNode(collection),
1161
+ types.memberExpression(types.cloneNode(collection), types.cloneNode(updater.body.callee.property)),
1162
+ types.stringLiteral(operation),
1163
+ ...updater.body.arguments.map(
1164
+ (argument) => types.cloneNode(argument, true)
1165
+ )
1166
+ ]),
1167
+ types.stringLiteral(kind)
1168
+ ])
1169
+ )
1170
+ ]);
1171
+ return {
1172
+ updater: types.arrowFunctionExpression([types.cloneNode(previous)], body),
1173
+ mutations: 1
1174
+ };
1175
+ }
1176
+ const cloned = types.cloneNode(updater, true);
1177
+ const file = expressionFile(cloned);
1178
+ let functionPath;
1179
+ traverse(file, {
1180
+ Function(path) {
1181
+ if (functionPath) return;
1182
+ if (path.isArrowFunctionExpression() || path.isFunctionExpression()) {
1183
+ functionPath = path;
1184
+ }
1185
+ }
1186
+ });
1187
+ if (!functionPath || !types.isBlockStatement(functionPath.node.body)) return void 0;
1188
+ const clonedPrevious = functionPath.node.params[0];
1189
+ if (!types.isIdentifier(clonedPrevious)) return void 0;
1190
+ let collectionDeclaration;
1191
+ const updaterBody = functionPath.get("body");
1192
+ if (!updaterBody.isBlockStatement()) return void 0;
1193
+ for (const statement of updaterBody.get("body")) {
1194
+ if (!statement.isVariableDeclaration({ kind: "const" })) continue;
1195
+ for (const declaration of statement.get("declarations")) {
1196
+ if (declaration.isVariableDeclarator() && types.isIdentifier(declaration.node.id) && types.isExpression(declaration.node.init) && isCollectionConstruction(declaration.node.init, kind, clonedPrevious)) {
1197
+ if (collectionDeclaration) return void 0;
1198
+ collectionDeclaration = declaration;
1199
+ }
1200
+ }
1201
+ }
1202
+ if (!collectionDeclaration || !types.isIdentifier(collectionDeclaration.node.id)) return void 0;
1203
+ if (collectionDeclaration.scope.getBinding(collectionConstructorName(kind))) return void 0;
1204
+ const collectionName = collectionDeclaration.node.id.name;
1205
+ const collectionBinding = collectionDeclaration.scope.getBinding(collectionName);
1206
+ const previousBinding = functionPath.scope.getBinding(clonedPrevious.name);
1207
+ if (!collectionBinding || !previousBinding) return void 0;
1208
+ let valid = true;
1209
+ let mutations = 0;
1210
+ traverse(file, {
1211
+ ReferencedIdentifier(path) {
1212
+ if (!valid) return;
1213
+ const binding = path.scope.getBinding(path.node.name);
1214
+ const belongsToUpdater = path.getFunctionParent()?.node === functionPath?.node;
1215
+ if (binding === collectionBinding) {
1216
+ if (!belongsToUpdater) {
1217
+ valid = false;
1218
+ path.stop();
1219
+ return;
1220
+ }
1221
+ const parent = path.parentPath;
1222
+ if (parent.isReturnStatement() && parent.node.argument === path.node) return;
1223
+ if (!parent.isMemberExpression() || parent.node.object !== path.node || parent.node.computed || !types.isIdentifier(parent.node.property)) {
1224
+ valid = false;
1225
+ path.stop();
1226
+ return;
1227
+ }
1228
+ const method = parent.node.property.name;
1229
+ if (method === "size") return;
1230
+ const call = parent.parentPath;
1231
+ if (!call?.isCallExpression() || call.node.callee !== parent.node) {
1232
+ valid = false;
1233
+ path.stop();
1234
+ return;
1235
+ }
1236
+ const operation = collectionMutationOperation(kind, method);
1237
+ const read = kind === "set" ? method === "has" : method === "get" || method === "has";
1238
+ if (read) return;
1239
+ if (!collectionMutationArgumentsAreValid(operation, call.node.arguments) || call.parentPath.isMemberExpression() && call.parentPath.node.object === call.node) {
1240
+ valid = false;
1241
+ path.stop();
1242
+ return;
1243
+ }
1244
+ mutations += 1;
1245
+ return;
1246
+ }
1247
+ if (binding === previousBinding) {
1248
+ if (!belongsToUpdater) {
1249
+ valid = false;
1250
+ path.stop();
1251
+ return;
1252
+ }
1253
+ const parent = path.parentPath;
1254
+ if (parent.isReturnStatement() && parent.node.argument === path.node) return;
1255
+ if (parent.isNewExpression() && parent.node.arguments.length === 1 && parent.node.arguments[0] === path.node && types.isIdentifier(parent.node.callee, {
1256
+ name: collectionConstructorName(kind)
1257
+ })) {
1258
+ return;
1259
+ }
1260
+ if (parent.isMemberExpression() && parent.node.object === path.node && !parent.node.computed && types.isIdentifier(parent.node.property)) {
1261
+ const method = parent.node.property.name;
1262
+ const call = parent.parentPath;
1263
+ if (method === "size" || (kind === "set" ? method === "has" : method === "get" || method === "has") && call?.isCallExpression() && call.node.callee === parent.node) {
1264
+ return;
1265
+ }
1266
+ }
1267
+ valid = false;
1268
+ path.stop();
1269
+ }
1270
+ },
1271
+ ReturnStatement(path) {
1272
+ if (!valid || path.getFunctionParent()?.node !== functionPath?.node || !path.node.argument) {
1273
+ return;
1274
+ }
1275
+ const value = path.node.argument;
1276
+ const returnsOwnedCollection = types.isIdentifier(value) && (path.scope.getBinding(value.name) === collectionBinding || path.scope.getBinding(value.name) === previousBinding) || isCollectionConstruction(value, kind);
1277
+ if (!returnsOwnedCollection) {
1278
+ valid = false;
1279
+ path.stop();
1280
+ }
1281
+ }
1282
+ });
1283
+ if (!valid || mutations === 0) return void 0;
1284
+ traverse(file, {
1285
+ CallExpression(path) {
1286
+ if (path.getFunctionParent()?.node !== functionPath?.node) return;
1287
+ const callee = path.node.callee;
1288
+ if (!types.isMemberExpression(callee) || callee.computed || !types.isIdentifier(callee.object, { name: collectionName }) || path.scope.getBinding(collectionName) !== collectionBinding || !types.isIdentifier(callee.property)) {
1289
+ return;
1290
+ }
1291
+ const operation = collectionMutationOperation(kind, callee.property.name);
1292
+ if (!collectionMutationArgumentsAreValid(operation, path.node.arguments)) return;
1293
+ path.replaceWith(
1294
+ types.callExpression(types.cloneNode(mutationHelper), [
1295
+ types.cloneNode(callee.object),
1296
+ types.memberExpression(types.cloneNode(callee.object), types.cloneNode(callee.property)),
1297
+ types.stringLiteral(operation),
1298
+ ...path.node.arguments.map((argument) => types.cloneNode(argument, true))
1299
+ ])
1300
+ );
1301
+ path.skip();
1302
+ },
1303
+ ReturnStatement(path) {
1304
+ if (path.getFunctionParent()?.node !== functionPath?.node || !path.node.argument) return;
1305
+ path.node.argument = types.callExpression(types.cloneNode(updateHelper), [
1306
+ types.cloneNode(clonedPrevious),
1307
+ types.cloneNode(path.node.argument, true),
1308
+ types.stringLiteral(kind)
1309
+ ]);
1310
+ }
1311
+ });
1312
+ const prepared = file.program.body[0].expression;
1313
+ return types.isArrowFunctionExpression(prepared) || types.isFunctionExpression(prepared) ? { updater: prepared, mutations } : void 0;
1314
+ }
1315
+ function rewriteKeyedCollectionUpdateHints(root, targetKinds, states, statesBySetter, updateHelper, mutationHelper, globalCollections) {
1316
+ if (targetKinds.size === 0) return { root: types.cloneNode(root, true), count: 0 };
1317
+ const file = expressionFile(types.cloneNode(root, true));
1318
+ let count = 0;
1319
+ for (const [stateIndex, kind] of targetKinds) {
1320
+ const state = states[stateIndex];
1321
+ if (!state || !collectionStateInitializerIsOwned(
1322
+ state.initialValue,
1323
+ kind,
1324
+ globalCollections.has(collectionConstructorName(kind))
1325
+ ) || !collectionStateHasOnlyOwnedReads(root, state, kind)) {
1326
+ continue;
1327
+ }
1328
+ const replacements = /* @__PURE__ */ new Map();
1329
+ let valid = true;
1330
+ traverse(file, {
1331
+ CallExpression(path) {
1332
+ if (!valid) return;
1333
+ const callee = path.get("callee");
1334
+ if (!callee.isIdentifier({ name: state.setterName }) || callee.scope.hasBinding(callee.node.name) || statesBySetter.get(callee.node.name)?.index !== stateIndex) {
1335
+ return;
1336
+ }
1337
+ if (path.node.arguments.length !== 1 || !types.isExpression(path.node.arguments[0])) {
1338
+ valid = false;
1339
+ path.stop();
1340
+ return;
1341
+ }
1342
+ const update = path.node.arguments[0];
1343
+ if (path.scope.getBinding(collectionConstructorName(kind))) {
1344
+ valid = false;
1345
+ path.stop();
1346
+ return;
1347
+ }
1348
+ if (isCollectionConstruction(update, kind)) return;
1349
+ if (!types.isArrowFunctionExpression(update) && !types.isFunctionExpression(update)) {
1350
+ valid = false;
1351
+ path.stop();
1352
+ return;
1353
+ }
1354
+ const prepared = prepareKeyedCollectionUpdater(update, kind, updateHelper, mutationHelper);
1355
+ if (!prepared) {
1356
+ valid = false;
1357
+ path.stop();
1358
+ return;
1359
+ }
1360
+ replacements.set(path.node, prepared);
1361
+ }
1362
+ });
1363
+ if (!valid || replacements.size === 0) continue;
1364
+ traverse(file, {
1365
+ CallExpression(path) {
1366
+ const prepared = replacements.get(path.node);
1367
+ if (!prepared) return;
1368
+ path.node.arguments[0] = types.cloneNode(prepared.updater, true);
1369
+ count += prepared.mutations;
1370
+ path.skip();
1371
+ }
1372
+ });
1373
+ }
1374
+ return {
1375
+ root: file.program.body[0].expression,
1376
+ count
1377
+ };
1378
+ }
301
1379
  function rewriteStateAccess(expression, stateParameter, statesByValue, statesBySetter) {
302
1380
  const file = expressionFile(cloneExpression(expression));
303
1381
  traverse(file, {
@@ -548,21 +1626,28 @@ function analyzeMapList(expression, statesByValue, safeGlobals) {
548
1626
  const collection = callee.object;
549
1627
  const collectionUnsupported = validateCollectionPipeline(collection, safeGlobals);
550
1628
  if (collectionUnsupported) {
551
- return { reason: `keyed list collection cannot use ${collectionUnsupported}` };
1629
+ return {
1630
+ reason: `keyed list collection cannot use ${collectionUnsupported}`
1631
+ };
552
1632
  }
553
1633
  if (expression.arguments.length !== 1 || !types.isArrowFunctionExpression(expression.arguments[0]) && !types.isFunctionExpression(expression.arguments[0])) {
554
1634
  return { reason: "keyed list map must use one inline render callback" };
555
1635
  }
556
1636
  const callback = expression.arguments[0];
557
1637
  if (containsDirectHookCall(callback)) {
558
- return { reason: "Hooks cannot be called directly inside a keyed list callback" };
1638
+ return {
1639
+ reason: "Hooks cannot be called directly inside a keyed list callback"
1640
+ };
559
1641
  }
560
1642
  const row = returnedExpression(callback);
561
1643
  if (!row || !types.isJSXElement(row)) {
562
1644
  return { reason: "keyed list map callbacks must return one React element" };
563
1645
  }
564
1646
  const key = jsxKeyExpression(row);
565
- if (!key) return { reason: "automatically compiled lists require an explicit item key" };
1647
+ if (!key)
1648
+ return {
1649
+ reason: "automatically compiled lists require an explicit item key"
1650
+ };
566
1651
  const keyReason = validateKeyFunction(callback, key, safeGlobals);
567
1652
  if (keyReason) return { reason: keyReason };
568
1653
  return { dependencies: collectStateDependencies(expression, statesByValue) };
@@ -577,7 +1662,9 @@ function listAttributeExpression(element, name) {
577
1662
  }
578
1663
  function analyzePublicList(element, statesByValue, safeGlobals) {
579
1664
  if (element.openingElement.attributes.some((attribute) => types.isJSXSpreadAttribute(attribute))) {
580
- return { reason: "compiled List boundaries do not support JSX attribute spreads" };
1665
+ return {
1666
+ reason: "compiled List boundaries do not support JSX attribute spreads"
1667
+ };
581
1668
  }
582
1669
  const each = listAttributeExpression(element, "each");
583
1670
  const by = listAttributeExpression(element, "by");
@@ -595,7 +1682,9 @@ function analyzePublicList(element, statesByValue, safeGlobals) {
595
1682
  if (keyReason) return { reason: keyReason };
596
1683
  const children = meaningfulJsxChildren(element);
597
1684
  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" };
1685
+ return {
1686
+ reason: "compiled List children must use one inline render function"
1687
+ };
599
1688
  }
600
1689
  const render = children[0].expression;
601
1690
  if (!returnedExpression(render)) {
@@ -709,13 +1798,20 @@ function stabilizeDeferredEventCurrentTargets(root, compilerSetters) {
709
1798
  const expression = file.program.body[0].expression;
710
1799
  return types.isJSXElement(expression) ? expression : types.cloneNode(root, true);
711
1800
  }
712
- function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters) {
1801
+ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters, keyedTargetReactiveByValue) {
713
1802
  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" };
1803
+ return {
1804
+ reason: "compiled keyed rows require item and optional index identifiers"
1805
+ };
715
1806
  }
716
1807
  const bindings = [];
717
1808
  const events = [];
718
1809
  const conditionals = [];
1810
+ const validateBindingExpression = (expression) => validateDerivedExpression(
1811
+ expression,
1812
+ safeGlobals,
1813
+ keyedTargetReactiveByValue ? (call) => isPotentialKeyedMembershipCall(call, keyedTargetReactiveByValue) || isPotentialKeyedMapLookupCall(call, keyedTargetReactiveByValue) : void 0
1814
+ );
719
1815
  const visit = (element, path) => {
720
1816
  const tag = element.openingElement.name;
721
1817
  if (!types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
@@ -794,7 +1890,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
794
1890
  if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
795
1891
  return "compiled keyed row style names must use camelCase or a CSS custom property";
796
1892
  }
797
- const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
1893
+ const unsupported2 = validateBindingExpression(property.value);
798
1894
  if (unsupported2) {
799
1895
  return `compiled keyed row style ${propertyName} cannot use ${unsupported2}`;
800
1896
  }
@@ -808,7 +1904,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
808
1904
  continue;
809
1905
  }
810
1906
  if (name === "children") return "compiled keyed row children props are not supported yet";
811
- const unsupported = validateDerivedExpression(expression, safeGlobals);
1907
+ const unsupported = validateBindingExpression(expression);
812
1908
  if (unsupported) return `compiled keyed row ${name} cannot use ${unsupported}`;
813
1909
  bindings.push({
814
1910
  kind: "attribute",
@@ -858,7 +1954,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
858
1954
  (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)
859
1955
  );
860
1956
  for (const child of expressions) {
861
- const unsupported = validateDerivedExpression(child.expression, safeGlobals);
1957
+ const unsupported = validateBindingExpression(child.expression);
862
1958
  if (unsupported) return `compiled keyed row text cannot use ${unsupported}`;
863
1959
  }
864
1960
  if (nestedElements.length > 0 && expressions.length > 0) {
@@ -874,7 +1970,11 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
874
1970
  parts.push(cloneExpression(child.expression));
875
1971
  }
876
1972
  }
877
- bindings.push({ kind: "text", path: [...path], value: types.arrayExpression(parts) });
1973
+ bindings.push({
1974
+ kind: "text",
1975
+ path: [...path],
1976
+ value: types.arrayExpression(parts)
1977
+ });
878
1978
  }
879
1979
  let elementIndex = 0;
880
1980
  for (const child of element.children) {
@@ -888,7 +1988,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
888
1988
  const reason = visit(row, []);
889
1989
  return reason ? { reason } : { bindings, events, conditionals };
890
1990
  }
891
- function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false) {
1991
+ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false, allowMembershipTargets = false) {
892
1992
  let collection;
893
1993
  let keyCallback;
894
1994
  let renderCallback;
@@ -941,7 +2041,8 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
941
2041
  row,
942
2042
  renderCallback,
943
2043
  safeGlobals,
944
- new Set([...statesByValue.values()].map((state) => state.setterName))
2044
+ new Set([...statesByValue.values()].map((state) => state.setterName)),
2045
+ allowMembershipTargets ? statesByValue : void 0
945
2046
  );
946
2047
  if (rowAnalysis.reason && !acceptUnsupportedRow) return void 0;
947
2048
  const keyExpression = returnedExpression(keyCallback);
@@ -954,6 +2055,7 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
954
2055
  return {
955
2056
  source,
956
2057
  collection,
2058
+ collectionDependency: types.isIdentifier(collection) ? statesByValue.get(collection.name)?.index : void 0,
957
2059
  structureDependencies: [...structureDependencies].sort((left, right) => left - right),
958
2060
  keyCallback,
959
2061
  renderCallback,
@@ -982,7 +2084,74 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
982
2084
  }
983
2085
  const children = meaningfulJsxChildren(container);
984
2086
  if (children.length !== 1) return void 0;
985
- return analyzeKeyedRowChild(children[0], statesByValue, safeGlobals, listNames, true);
2087
+ const shape = analyzeKeyedRowChild(
2088
+ children[0],
2089
+ statesByValue,
2090
+ safeGlobals,
2091
+ listNames,
2092
+ true,
2093
+ true
2094
+ );
2095
+ if (!shape || shape.rowReason) return shape;
2096
+ const needsMembershipProof = (binding) => containsPotentialKeyedMembershipCall(binding.value, statesByValue);
2097
+ const needsMapLookupProof = (binding) => containsPotentialKeyedMapLookupCall(binding.value, statesByValue);
2098
+ if (shape.conditionals.length > 0 || shape.events.length > 0 && !canDelegateKeyedRowEvents({
2099
+ conditionals: shape.conditionals,
2100
+ events: shape.events,
2101
+ source: container
2102
+ })) {
2103
+ return shape.bindings.some(
2104
+ (binding) => needsMembershipProof(binding) || needsMapLookupProof(binding)
2105
+ ) ? {
2106
+ ...shape,
2107
+ rowReason: "keyed collection targets require compiler-owned rows without React-owned branches or events"
2108
+ } : shape;
2109
+ }
2110
+ const structureDependencies = new Set(shape.structureDependencies);
2111
+ const bindings = shape.bindings.map((binding) => ({
2112
+ ...binding,
2113
+ identityTarget: keyedIdentityTarget(
2114
+ binding,
2115
+ shape.keyCallback,
2116
+ shape.renderCallback,
2117
+ statesByValue,
2118
+ structureDependencies
2119
+ ),
2120
+ membershipTarget: keyedMembershipTarget(
2121
+ binding,
2122
+ shape.keyCallback,
2123
+ shape.renderCallback,
2124
+ statesByValue,
2125
+ structureDependencies
2126
+ ),
2127
+ mapLookupTarget: keyedMapLookupTarget(
2128
+ binding,
2129
+ shape.keyCallback,
2130
+ shape.renderCallback,
2131
+ statesByValue,
2132
+ structureDependencies
2133
+ )
2134
+ }));
2135
+ if (bindings.some(
2136
+ (binding, index) => needsMembershipProof(shape.bindings[index]) && !binding.membershipTarget
2137
+ )) {
2138
+ return {
2139
+ ...shape,
2140
+ rowReason: "keyed membership targets must call one local Set state with the exact row key"
2141
+ };
2142
+ }
2143
+ if (bindings.some(
2144
+ (binding, index) => needsMapLookupProof(shape.bindings[index]) && !binding.mapLookupTarget
2145
+ )) {
2146
+ return {
2147
+ ...shape,
2148
+ rowReason: "keyed Map lookup targets must call one local Map state with the exact row key"
2149
+ };
2150
+ }
2151
+ return {
2152
+ ...shape,
2153
+ bindings
2154
+ };
986
2155
  }
987
2156
  function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
988
2157
  if (container.openingElement.attributes.some(
@@ -1138,7 +2307,11 @@ function analyzeHostConditionalTree(branch, safeGlobals, allowStaticEvents = fal
1138
2307
  parts.push(cloneExpression(child.expression));
1139
2308
  }
1140
2309
  }
1141
- bindings.push({ kind: "text", path: [...path], value: types.arrayExpression(parts) });
2310
+ bindings.push({
2311
+ kind: "text",
2312
+ path: [...path],
2313
+ value: types.arrayExpression(parts)
2314
+ });
1142
2315
  }
1143
2316
  let elementIndex = 0;
1144
2317
  for (const child of element.children) {
@@ -1310,7 +2483,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1310
2483
  listNames,
1311
2484
  id,
1312
2485
  allocateId
1313
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2486
+ ) : {
2487
+ bindings: [],
2488
+ plans: [],
2489
+ blocks: /* @__PURE__ */ new Map()
2490
+ };
1314
2491
  const falsy = shape.falsy ? analyzeRecursiveHostTree(
1315
2492
  shape.falsy,
1316
2493
  statesByValue,
@@ -1318,7 +2495,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1318
2495
  listNames,
1319
2496
  id,
1320
2497
  allocateId
1321
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2498
+ ) : {
2499
+ bindings: [],
2500
+ plans: [],
2501
+ blocks: /* @__PURE__ */ new Map()
2502
+ };
1322
2503
  if (truthy.reason) return truthy.reason;
1323
2504
  if (falsy.reason) return falsy.reason;
1324
2505
  plans.push(...truthy.plans, ...falsy.plans);
@@ -1476,7 +2657,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1476
2657
  parts.push(cloneExpression(child.expression));
1477
2658
  }
1478
2659
  }
1479
- bindings.push({ kind: "text", path: [...path], value: types.arrayExpression(parts) });
2660
+ bindings.push({
2661
+ kind: "text",
2662
+ path: [...path],
2663
+ value: types.arrayExpression(parts)
2664
+ });
1480
2665
  }
1481
2666
  let elementIndex = 0;
1482
2667
  for (const child of element.children) {
@@ -1543,7 +2728,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1543
2728
  listNames,
1544
2729
  parent,
1545
2730
  allocateId
1546
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2731
+ ) : {
2732
+ bindings: [],
2733
+ plans: [],
2734
+ blocks: /* @__PURE__ */ new Map()
2735
+ };
1547
2736
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1548
2737
  shape.falsy,
1549
2738
  statesByValue,
@@ -1551,7 +2740,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1551
2740
  listNames,
1552
2741
  parent,
1553
2742
  allocateId
1554
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2743
+ ) : {
2744
+ bindings: [],
2745
+ plans: [],
2746
+ blocks: /* @__PURE__ */ new Map()
2747
+ };
1555
2748
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1556
2749
  const dependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1557
2750
  for (const binding of [...truthyAnalysis.bindings || [], ...falsyAnalysis.bindings || []]) {
@@ -1597,7 +2790,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1597
2790
  listNames,
1598
2791
  parent,
1599
2792
  allocateId
1600
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2793
+ ) : {
2794
+ bindings: [],
2795
+ plans: [],
2796
+ blocks: /* @__PURE__ */ new Map()
2797
+ };
1601
2798
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1602
2799
  shape.falsy,
1603
2800
  statesByValue,
@@ -1605,7 +2802,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1605
2802
  listNames,
1606
2803
  parent,
1607
2804
  allocateId
1608
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2805
+ ) : {
2806
+ bindings: [],
2807
+ plans: [],
2808
+ blocks: /* @__PURE__ */ new Map()
2809
+ };
1609
2810
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1610
2811
  nestedPlans.push(...truthyAnalysis.plans, ...falsyAnalysis.plans);
1611
2812
  for (const [element, plan] of truthyAnalysis.blocks) descriptorBlocks.set(element, plan);
@@ -1698,7 +2899,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1698
2899
  listNames,
1699
2900
  parent,
1700
2901
  allocateId
1701
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2902
+ ) : {
2903
+ bindings: [],
2904
+ plans: [],
2905
+ blocks: /* @__PURE__ */ new Map()
2906
+ };
1702
2907
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1703
2908
  shape.falsy,
1704
2909
  statesByValue,
@@ -1706,7 +2911,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1706
2911
  listNames,
1707
2912
  parent,
1708
2913
  allocateId
1709
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2914
+ ) : {
2915
+ bindings: [],
2916
+ plans: [],
2917
+ blocks: /* @__PURE__ */ new Map()
2918
+ };
1710
2919
  if (!mergeAnalysis(truthyAnalysis) || !mergeAnalysis(falsyAnalysis)) return void 0;
1711
2920
  for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1712
2921
  dependencies.add(dependency);
@@ -1947,6 +3156,57 @@ function keyedRowBindingObject(binding, parameters) {
1947
3156
  if (binding.name) {
1948
3157
  properties.push(types.objectProperty(types.identifier("name"), types.stringLiteral(binding.name)));
1949
3158
  }
3159
+ if (binding.identityTarget) {
3160
+ properties.push(
3161
+ types.objectProperty(
3162
+ types.identifier("identityTarget"),
3163
+ types.objectExpression([
3164
+ types.objectProperty(
3165
+ types.identifier("dependency"),
3166
+ types.numericLiteral(binding.identityTarget.dependency)
3167
+ ),
3168
+ types.objectProperty(
3169
+ types.identifier("read"),
3170
+ types.arrowFunctionExpression([], cloneExpression(binding.identityTarget.value))
3171
+ )
3172
+ ])
3173
+ )
3174
+ );
3175
+ }
3176
+ if (binding.membershipTarget) {
3177
+ properties.push(
3178
+ types.objectProperty(
3179
+ types.identifier("membershipTarget"),
3180
+ types.objectExpression([
3181
+ types.objectProperty(
3182
+ types.identifier("dependency"),
3183
+ types.numericLiteral(binding.membershipTarget.dependency)
3184
+ ),
3185
+ types.objectProperty(
3186
+ types.identifier("read"),
3187
+ types.arrowFunctionExpression([], cloneExpression(binding.membershipTarget.value))
3188
+ )
3189
+ ])
3190
+ )
3191
+ );
3192
+ }
3193
+ if (binding.mapLookupTarget) {
3194
+ properties.push(
3195
+ types.objectProperty(
3196
+ types.identifier("mapLookupTarget"),
3197
+ types.objectExpression([
3198
+ types.objectProperty(
3199
+ types.identifier("dependency"),
3200
+ types.numericLiteral(binding.mapLookupTarget.dependency)
3201
+ ),
3202
+ types.objectProperty(
3203
+ types.identifier("read"),
3204
+ types.arrowFunctionExpression([], cloneExpression(binding.mapLookupTarget.value))
3205
+ )
3206
+ ])
3207
+ )
3208
+ );
3209
+ }
1950
3210
  properties.push(
1951
3211
  types.objectProperty(
1952
3212
  types.identifier("read"),
@@ -2141,7 +3401,7 @@ function keyedRowConditionalObject(conditional, parameters) {
2141
3401
  }
2142
3402
  return types.objectExpression(properties);
2143
3403
  }
2144
- function keyedRowsBoundary(blockRuntime, plan) {
3404
+ function keyedRowsBoundary(blockRuntime, plan, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
2145
3405
  const name = types.jsxMemberExpression(
2146
3406
  types.jsxIdentifier(blockRuntime.name),
2147
3407
  types.jsxIdentifier("KeyedRows")
@@ -2175,6 +3435,16 @@ function keyedRowsBoundary(blockRuntime, plan) {
2175
3435
  types.jsxIdentifier("items"),
2176
3436
  types.jsxExpressionContainer(types.arrowFunctionExpression([], cloneExpression(plan.collection)))
2177
3437
  ),
3438
+ ...keyedMapUpdateHints ? [
3439
+ types.jsxAttribute(
3440
+ types.jsxIdentifier("dependencies"),
3441
+ types.jsxExpressionContainer(
3442
+ types.arrayExpression(
3443
+ plan.dependencies.map((dependency) => types.numericLiteral(dependency))
3444
+ )
3445
+ )
3446
+ )
3447
+ ] : [],
2178
3448
  types.jsxAttribute(
2179
3449
  types.jsxIdentifier("structureDependencies"),
2180
3450
  types.jsxExpressionContainer(
@@ -2183,6 +3453,36 @@ function keyedRowsBoundary(blockRuntime, plan) {
2183
3453
  )
2184
3454
  )
2185
3455
  ),
3456
+ ...!keyedMapUpdateHints || plan.collectionDependency === void 0 ? [] : [
3457
+ types.jsxAttribute(
3458
+ types.jsxIdentifier("collectionDependency"),
3459
+ types.jsxExpressionContainer(types.numericLiteral(plan.collectionDependency))
3460
+ )
3461
+ ],
3462
+ ...plan.collectionDependency !== void 0 && keyedArrayFilterHintedStateIndices.has(plan.collectionDependency) ? [
3463
+ types.jsxAttribute(
3464
+ types.jsxIdentifier("filterIndexIndependent"),
3465
+ types.jsxExpressionContainer(types.booleanLiteral(true))
3466
+ )
3467
+ ] : [],
3468
+ ...plan.collectionDependency !== void 0 && keyedArrayPrependHintedStateIndices.has(plan.collectionDependency) ? [
3469
+ types.jsxAttribute(
3470
+ types.jsxIdentifier("prependIndexIndependent"),
3471
+ types.jsxExpressionContainer(types.booleanLiteral(true))
3472
+ )
3473
+ ] : [],
3474
+ ...plan.collectionDependency !== void 0 && keyedArrayPositionHintedStateIndices.has(plan.collectionDependency) ? [
3475
+ types.jsxAttribute(
3476
+ types.jsxIdentifier("positionIndexIndependent"),
3477
+ types.jsxExpressionContainer(types.booleanLiteral(true))
3478
+ )
3479
+ ] : [],
3480
+ ...plan.collectionDependency !== void 0 && keyedArrayReorderHintedStateIndices.has(plan.collectionDependency) ? [
3481
+ types.jsxAttribute(
3482
+ types.jsxIdentifier("reorderIndexIndependent"),
3483
+ types.jsxExpressionContainer(types.booleanLiteral(true))
3484
+ )
3485
+ ] : [],
2186
3486
  types.jsxAttribute(
2187
3487
  types.jsxIdentifier("rowKey"),
2188
3488
  types.jsxExpressionContainer(types.cloneNode(plan.keyCallback, true))
@@ -2509,7 +3809,9 @@ function conditionalRangesBoundary(blockRuntime, plan) {
2509
3809
  function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allowedComponentNames) {
2510
3810
  const name = element.openingElement.name;
2511
3811
  if (!types.isJSXIdentifier(name) || !isComponentName(name.name)) {
2512
- return { reason: "component islands require a direct component identifier" };
3812
+ return {
3813
+ reason: "component islands require a direct component identifier"
3814
+ };
2513
3815
  }
2514
3816
  if (!allowedComponentNames.has(name.name)) {
2515
3817
  return {
@@ -2517,19 +3819,27 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2517
3819
  };
2518
3820
  }
2519
3821
  if (meaningfulJsxChildren(element).length > 0) {
2520
- return { reason: `component island ${name.name} does not support children yet` };
3822
+ return {
3823
+ reason: `component island ${name.name} does not support children yet`
3824
+ };
2521
3825
  }
2522
3826
  const dependencies = /* @__PURE__ */ new Set();
2523
3827
  for (const attribute of element.openingElement.attributes) {
2524
3828
  if (types.isJSXSpreadAttribute(attribute)) {
2525
- return { reason: `component island ${name.name} does not support JSX attribute spreads` };
3829
+ return {
3830
+ reason: `component island ${name.name} does not support JSX attribute spreads`
3831
+ };
2526
3832
  }
2527
3833
  const attributeName = jsxAttributeName(attribute);
2528
3834
  if (!attributeName) {
2529
- return { reason: `component island ${name.name} does not support namespaced JSX attributes` };
3835
+ return {
3836
+ reason: `component island ${name.name} does not support namespaced JSX attributes`
3837
+ };
2530
3838
  }
2531
3839
  if (attributeName === "ref" || attributeName === "key" || attributeName === "children") {
2532
- return { reason: `component island ${name.name} does not support ${attributeName} yet` };
3840
+ return {
3841
+ reason: `component island ${name.name} does not support ${attributeName} yet`
3842
+ };
2533
3843
  }
2534
3844
  if (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression)) {
2535
3845
  continue;
@@ -2548,7 +3858,9 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2548
3858
  (dependency) => dependencies.add(dependency)
2549
3859
  );
2550
3860
  }
2551
- return { dependencies: [...dependencies].sort((left, right) => left - right) };
3861
+ return {
3862
+ dependencies: [...dependencies].sort((left, right) => left - right)
3863
+ };
2552
3864
  }
2553
3865
  function componentIslandBoundary(blockRuntime, plan, source) {
2554
3866
  const name = types.jsxMemberExpression(
@@ -2805,6 +4117,7 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
2805
4117
  dependencies: keyedRows.dependencies,
2806
4118
  source: child,
2807
4119
  collection: keyedRows.collection,
4120
+ collectionDependency: keyedRows.collectionDependency,
2808
4121
  structureDependencies: keyedRows.structureDependencies,
2809
4122
  keyCallback: keyedRows.keyCallback,
2810
4123
  renderCallback: keyedRows.renderCallback,
@@ -3046,7 +4359,7 @@ function conditionalBlockBoundary(blockRuntime, plan, source) {
3046
4359
  true
3047
4360
  );
3048
4361
  }
3049
- function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
4362
+ function lowerComposableBlocks(root, plans, blockRuntime, listNames, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
3050
4363
  const planBySource = new Map(
3051
4364
  plans.map((plan) => [plan.source, plan])
3052
4365
  );
@@ -3071,7 +4384,15 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
3071
4384
  return mixedRangesBoundary(blockRuntime, plan2);
3072
4385
  }
3073
4386
  if (plan2?.kind === "keyed-rows") {
3074
- return keyedRowsBoundary(blockRuntime, plan2);
4387
+ return keyedRowsBoundary(
4388
+ blockRuntime,
4389
+ plan2,
4390
+ keyedMapUpdateHints,
4391
+ keyedArrayFilterHintedStateIndices,
4392
+ keyedArrayPrependHintedStateIndices,
4393
+ keyedArrayPositionHintedStateIndices,
4394
+ keyedArrayReorderHintedStateIndices
4395
+ );
3075
4396
  }
3076
4397
  if (plan2?.kind === "keyed-ranges") {
3077
4398
  return keyedRangesBoundary(blockRuntime, plan2);
@@ -3434,7 +4755,7 @@ function wrapperElement(definitionIdentifier, props) {
3434
4755
  const attributes = props ? [types.jsxSpreadAttribute(types.cloneNode(props))] : [];
3435
4756
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
3436
4757
  }
3437
- function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureIdentifiers, usedRuntimeFeatures, useStateNames, reactNames, listNames, moduleId, reactivity) {
4758
+ function compileCandidate(candidate, createComponentIdentifier, keyedMapUpdateIdentifier, keyedArrayAppendIdentifier, keyedArrayFilterIdentifier, keyedArrayPrependIdentifier, keyedArrayPositionIdentifier, keyedArrayReorderIdentifier, keyedArrayRollingWindowIdentifier, keyedArraySliceIdentifier, keyedCollectionUpdateIdentifier, keyedCollectionMutationIdentifier, runtimeFeatureIdentifiers, usedRuntimeFeatures, optimizationCounts, useStateNames, reactNames, listNames, moduleId, reactivity) {
3438
4759
  const { path, name, statementPath } = candidate;
3439
4760
  if (path.node.async || path.node.generator)
3440
4761
  return "async and generator components are not supported";
@@ -3446,6 +4767,7 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3446
4767
  const safeGlobals = new Set(
3447
4768
  [...SAFE_GLOBAL_CALLS, "Math"].filter((name2) => !path.scope.getBinding(name2))
3448
4769
  );
4770
+ const globalCollections = new Set(["Set", "Map"].filter((name2) => !path.scope.getBinding(name2)));
3449
4771
  const states = [];
3450
4772
  const locals = [];
3451
4773
  let returned;
@@ -3493,9 +4815,17 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3493
4815
  if (declaration.init.async || declaration.init.generator) {
3494
4816
  return `event handler ${declaration.id.name} must be synchronous`;
3495
4817
  }
3496
- locals.push({ kind: "handler", name: declaration.id.name, value: declaration.init });
4818
+ locals.push({
4819
+ kind: "handler",
4820
+ name: declaration.id.name,
4821
+ value: declaration.init
4822
+ });
3497
4823
  } else {
3498
- locals.push({ kind: "derived", name: declaration.id.name, value: declaration.init });
4824
+ locals.push({
4825
+ kind: "derived",
4826
+ name: declaration.id.name,
4827
+ value: declaration.init
4828
+ });
3499
4829
  }
3500
4830
  }
3501
4831
  if (states.length === 0) return "no local useState binding was found";
@@ -3634,8 +4964,378 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3634
4964
  }
3635
4965
  if (blockAnalysis.reason) return blockAnalysis.reason;
3636
4966
  if (analysis.reason) return analysis.reason;
4967
+ const hintedStateIndices = new Set(
4968
+ (blockAnalysis.plans || []).filter(
4969
+ (plan) => plan.kind === "keyed-rows" && plan.collectionDependency !== void 0
4970
+ ).map((plan) => plan.collectionDependency)
4971
+ );
4972
+ const hintedRoot = rewriteKeyedMapUpdateHints(
4973
+ expandedReactiveRoot,
4974
+ hintedStateIndices,
4975
+ statesBySetter,
4976
+ keyedMapUpdateIdentifier,
4977
+ safeGlobals
4978
+ );
4979
+ let appliedKeyedMapUpdateHints = 0;
4980
+ if (hintedRoot.count > 0) {
4981
+ const hintedBlockAnalysis = analyzeComposableBlocks(
4982
+ hintedRoot.root,
4983
+ reactiveByValue,
4984
+ safeGlobals,
4985
+ listNames,
4986
+ allowedComponentNames
4987
+ );
4988
+ const hintedAnalysis = analyzeHostTree(
4989
+ hintedRoot.root,
4990
+ reactiveByValue,
4991
+ safeGlobals,
4992
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
4993
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
4994
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
4995
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
4996
+ );
4997
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
4998
+ expandedReactiveRoot = hintedRoot.root;
4999
+ blockAnalysis = hintedBlockAnalysis;
5000
+ analysis = hintedAnalysis;
5001
+ appliedKeyedMapUpdateHints = hintedRoot.count;
5002
+ optimizationCounts.keyedMapUpdateHints += hintedRoot.count;
5003
+ }
5004
+ }
5005
+ const appendHintedStateIndices = new Set(hintedStateIndices);
5006
+ for (const plan of blockAnalysis.plans || []) {
5007
+ if (plan.kind !== "keyed-rows" || plan.collectionDependency === void 0) continue;
5008
+ const keyExpression = returnedExpression(plan.keyCallback);
5009
+ if (!keyExpression || collectStateDependencies(keyExpression, reactiveByValue).includes(plan.collectionDependency)) {
5010
+ appendHintedStateIndices.delete(plan.collectionDependency);
5011
+ }
5012
+ }
5013
+ const appendHintedRoot = rewriteKeyedArrayAppendHints(
5014
+ expandedReactiveRoot,
5015
+ appendHintedStateIndices,
5016
+ statesBySetter,
5017
+ keyedArrayAppendIdentifier,
5018
+ safeGlobals
5019
+ );
5020
+ let appliedKeyedArrayAppendHints = 0;
5021
+ if (appendHintedRoot.count > 0) {
5022
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5023
+ appendHintedRoot.root,
5024
+ reactiveByValue,
5025
+ safeGlobals,
5026
+ listNames,
5027
+ allowedComponentNames
5028
+ );
5029
+ const hintedAnalysis = analyzeHostTree(
5030
+ appendHintedRoot.root,
5031
+ reactiveByValue,
5032
+ safeGlobals,
5033
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5034
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5035
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5036
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5037
+ );
5038
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5039
+ expandedReactiveRoot = appendHintedRoot.root;
5040
+ blockAnalysis = hintedBlockAnalysis;
5041
+ analysis = hintedAnalysis;
5042
+ appliedKeyedArrayAppendHints = appendHintedRoot.count;
5043
+ optimizationCounts.keyedArrayAppendHints += appendHintedRoot.count;
5044
+ }
5045
+ }
5046
+ const shiftedIndexIndependentStateIndices = new Set(hintedStateIndices);
5047
+ for (const plan of blockAnalysis.plans || []) {
5048
+ if (plan.kind !== "keyed-rows" || plan.collectionDependency === void 0) continue;
5049
+ const keyExpression = returnedExpression(plan.keyCallback);
5050
+ if (plan.renderCallback.params.length > 1 || !keyExpression || collectStateDependencies(keyExpression, reactiveByValue).includes(plan.collectionDependency)) {
5051
+ shiftedIndexIndependentStateIndices.delete(plan.collectionDependency);
5052
+ }
5053
+ }
5054
+ const prependHintedRoot = rewriteKeyedArrayPrependHints(
5055
+ expandedReactiveRoot,
5056
+ shiftedIndexIndependentStateIndices,
5057
+ statesBySetter,
5058
+ keyedArrayPrependIdentifier,
5059
+ safeGlobals
5060
+ );
5061
+ let appliedKeyedArrayPrependHints = 0;
5062
+ let appliedPrependHintedStateIndices = /* @__PURE__ */ new Set();
5063
+ if (prependHintedRoot.count > 0) {
5064
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5065
+ prependHintedRoot.root,
5066
+ reactiveByValue,
5067
+ safeGlobals,
5068
+ listNames,
5069
+ allowedComponentNames
5070
+ );
5071
+ const hintedAnalysis = analyzeHostTree(
5072
+ prependHintedRoot.root,
5073
+ reactiveByValue,
5074
+ safeGlobals,
5075
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5076
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5077
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5078
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5079
+ );
5080
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5081
+ expandedReactiveRoot = prependHintedRoot.root;
5082
+ blockAnalysis = hintedBlockAnalysis;
5083
+ analysis = hintedAnalysis;
5084
+ appliedKeyedArrayPrependHints = prependHintedRoot.count;
5085
+ appliedPrependHintedStateIndices = prependHintedRoot.stateIndices;
5086
+ optimizationCounts.keyedArrayPrependHints += prependHintedRoot.count;
5087
+ }
5088
+ }
5089
+ const rollingWindowHintedRoot = rewriteKeyedArrayRollingWindowHints(
5090
+ expandedReactiveRoot,
5091
+ shiftedIndexIndependentStateIndices,
5092
+ statesBySetter,
5093
+ keyedArraySliceIdentifier,
5094
+ keyedArrayRollingWindowIdentifier,
5095
+ safeGlobals
5096
+ );
5097
+ let appliedKeyedArrayRollingWindowHints = 0;
5098
+ let appliedRollingWindowHintedStateIndices = /* @__PURE__ */ new Set();
5099
+ if (rollingWindowHintedRoot.count > 0) {
5100
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5101
+ rollingWindowHintedRoot.root,
5102
+ reactiveByValue,
5103
+ safeGlobals,
5104
+ listNames,
5105
+ allowedComponentNames
5106
+ );
5107
+ const hintedAnalysis = analyzeHostTree(
5108
+ rollingWindowHintedRoot.root,
5109
+ reactiveByValue,
5110
+ safeGlobals,
5111
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5112
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5113
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5114
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5115
+ );
5116
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5117
+ expandedReactiveRoot = rollingWindowHintedRoot.root;
5118
+ blockAnalysis = hintedBlockAnalysis;
5119
+ analysis = hintedAnalysis;
5120
+ appliedKeyedArrayRollingWindowHints = rollingWindowHintedRoot.count;
5121
+ appliedRollingWindowHintedStateIndices = rollingWindowHintedRoot.stateIndices;
5122
+ optimizationCounts.keyedArrayRollingWindowHints += rollingWindowHintedRoot.count;
5123
+ }
5124
+ }
5125
+ const positionHintedRoot = rewriteKeyedArrayPositionHints(
5126
+ expandedReactiveRoot,
5127
+ shiftedIndexIndependentStateIndices,
5128
+ statesBySetter,
5129
+ keyedArrayPositionIdentifier,
5130
+ safeGlobals
5131
+ );
5132
+ let appliedKeyedArrayPositionHints = 0;
5133
+ let appliedPositionHintedStateIndices = /* @__PURE__ */ new Set();
5134
+ if (positionHintedRoot.count > 0) {
5135
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5136
+ positionHintedRoot.root,
5137
+ reactiveByValue,
5138
+ safeGlobals,
5139
+ listNames,
5140
+ allowedComponentNames
5141
+ );
5142
+ const hintedAnalysis = analyzeHostTree(
5143
+ positionHintedRoot.root,
5144
+ reactiveByValue,
5145
+ safeGlobals,
5146
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5147
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5148
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5149
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5150
+ );
5151
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5152
+ expandedReactiveRoot = positionHintedRoot.root;
5153
+ blockAnalysis = hintedBlockAnalysis;
5154
+ analysis = hintedAnalysis;
5155
+ appliedKeyedArrayPositionHints = positionHintedRoot.count;
5156
+ appliedPositionHintedStateIndices = positionHintedRoot.stateIndices;
5157
+ optimizationCounts.keyedArrayPositionHints += positionHintedRoot.count;
5158
+ }
5159
+ }
5160
+ const reorderHintedRoot = rewriteKeyedArrayReorderHints(
5161
+ expandedReactiveRoot,
5162
+ shiftedIndexIndependentStateIndices,
5163
+ statesBySetter,
5164
+ keyedArrayReorderIdentifier
5165
+ );
5166
+ let appliedKeyedArrayReorderHints = 0;
5167
+ let appliedReorderHintedStateIndices = /* @__PURE__ */ new Set();
5168
+ if (reorderHintedRoot.count > 0) {
5169
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5170
+ reorderHintedRoot.root,
5171
+ reactiveByValue,
5172
+ safeGlobals,
5173
+ listNames,
5174
+ allowedComponentNames
5175
+ );
5176
+ const hintedAnalysis = analyzeHostTree(
5177
+ reorderHintedRoot.root,
5178
+ reactiveByValue,
5179
+ safeGlobals,
5180
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5181
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5182
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5183
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5184
+ );
5185
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5186
+ expandedReactiveRoot = reorderHintedRoot.root;
5187
+ blockAnalysis = hintedBlockAnalysis;
5188
+ analysis = hintedAnalysis;
5189
+ appliedKeyedArrayReorderHints = reorderHintedRoot.count;
5190
+ appliedReorderHintedStateIndices = reorderHintedRoot.stateIndices;
5191
+ optimizationCounts.keyedArrayReorderHints += reorderHintedRoot.count;
5192
+ }
5193
+ }
5194
+ const sliceHintedRoot = rewriteKeyedArraySliceHints(
5195
+ expandedReactiveRoot,
5196
+ shiftedIndexIndependentStateIndices,
5197
+ statesBySetter,
5198
+ keyedArraySliceIdentifier
5199
+ );
5200
+ let appliedKeyedArraySliceHints = 0;
5201
+ let appliedSliceHintedStateIndices = /* @__PURE__ */ new Set();
5202
+ if (sliceHintedRoot.count > 0) {
5203
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5204
+ sliceHintedRoot.root,
5205
+ reactiveByValue,
5206
+ safeGlobals,
5207
+ listNames,
5208
+ allowedComponentNames
5209
+ );
5210
+ const hintedAnalysis = analyzeHostTree(
5211
+ sliceHintedRoot.root,
5212
+ reactiveByValue,
5213
+ safeGlobals,
5214
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5215
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5216
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5217
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5218
+ );
5219
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5220
+ expandedReactiveRoot = sliceHintedRoot.root;
5221
+ blockAnalysis = hintedBlockAnalysis;
5222
+ analysis = hintedAnalysis;
5223
+ appliedKeyedArraySliceHints = sliceHintedRoot.count;
5224
+ appliedSliceHintedStateIndices = sliceHintedRoot.stateIndices;
5225
+ optimizationCounts.keyedArraySliceHints += sliceHintedRoot.count;
5226
+ }
5227
+ }
5228
+ const filterHintedRoot = rewriteKeyedArrayFilterHints(
5229
+ expandedReactiveRoot,
5230
+ shiftedIndexIndependentStateIndices,
5231
+ statesBySetter,
5232
+ keyedArrayFilterIdentifier,
5233
+ safeGlobals
5234
+ );
5235
+ let appliedKeyedArrayFilterHints = 0;
5236
+ let appliedFilterHintedStateIndices = /* @__PURE__ */ new Set();
5237
+ if (filterHintedRoot.count > 0) {
5238
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5239
+ filterHintedRoot.root,
5240
+ reactiveByValue,
5241
+ safeGlobals,
5242
+ listNames,
5243
+ allowedComponentNames
5244
+ );
5245
+ const hintedAnalysis = analyzeHostTree(
5246
+ filterHintedRoot.root,
5247
+ reactiveByValue,
5248
+ safeGlobals,
5249
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5250
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5251
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5252
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5253
+ );
5254
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5255
+ expandedReactiveRoot = filterHintedRoot.root;
5256
+ blockAnalysis = hintedBlockAnalysis;
5257
+ analysis = hintedAnalysis;
5258
+ appliedKeyedArrayFilterHints = filterHintedRoot.count;
5259
+ appliedFilterHintedStateIndices = filterHintedRoot.stateIndices;
5260
+ optimizationCounts.keyedArrayFilterHints += filterHintedRoot.count;
5261
+ }
5262
+ }
5263
+ const keyedCollectionTargetKinds = /* @__PURE__ */ new Map();
5264
+ const conflictingKeyedCollectionTargets = /* @__PURE__ */ new Set();
5265
+ for (const plan of blockAnalysis.plans || []) {
5266
+ if (plan.kind !== "keyed-rows") continue;
5267
+ for (const binding of plan.bindings) {
5268
+ const target = binding.membershipTarget || binding.mapLookupTarget;
5269
+ if (!target) continue;
5270
+ const kind = binding.membershipTarget ? "set" : "map";
5271
+ const previousKind = keyedCollectionTargetKinds.get(target.dependency);
5272
+ if (previousKind && previousKind !== kind) {
5273
+ conflictingKeyedCollectionTargets.add(target.dependency);
5274
+ } else {
5275
+ keyedCollectionTargetKinds.set(target.dependency, kind);
5276
+ }
5277
+ }
5278
+ }
5279
+ for (const dependency of conflictingKeyedCollectionTargets) {
5280
+ keyedCollectionTargetKinds.delete(dependency);
5281
+ }
5282
+ const keyedCollectionHintedRoot = rewriteKeyedCollectionUpdateHints(
5283
+ expandedReactiveRoot,
5284
+ keyedCollectionTargetKinds,
5285
+ states,
5286
+ statesBySetter,
5287
+ keyedCollectionUpdateIdentifier,
5288
+ keyedCollectionMutationIdentifier,
5289
+ globalCollections
5290
+ );
5291
+ if (keyedCollectionHintedRoot.count > 0) {
5292
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5293
+ keyedCollectionHintedRoot.root,
5294
+ reactiveByValue,
5295
+ safeGlobals,
5296
+ listNames,
5297
+ allowedComponentNames
5298
+ );
5299
+ const hintedAnalysis = analyzeHostTree(
5300
+ keyedCollectionHintedRoot.root,
5301
+ reactiveByValue,
5302
+ safeGlobals,
5303
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5304
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5305
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5306
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5307
+ );
5308
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5309
+ expandedReactiveRoot = keyedCollectionHintedRoot.root;
5310
+ blockAnalysis = hintedBlockAnalysis;
5311
+ analysis = hintedAnalysis;
5312
+ optimizationCounts.keyedCollectionUpdateHints += keyedCollectionHintedRoot.count;
5313
+ }
5314
+ }
3637
5315
  const blockPlans = blockAnalysis.plans || [];
3638
- const runtimeFeatures = runtimeFeaturesForPlans(blockPlans);
5316
+ optimizationCounts.keyedIdentityTargets += blockPlans.reduce(
5317
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.identityTarget !== void 0).length : 0),
5318
+ 0
5319
+ );
5320
+ optimizationCounts.keyedMembershipTargets += blockPlans.reduce(
5321
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.membershipTarget !== void 0).length : 0),
5322
+ 0
5323
+ );
5324
+ optimizationCounts.keyedMapLookupTargets += blockPlans.reduce(
5325
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.mapLookupTarget !== void 0).length : 0),
5326
+ 0
5327
+ );
5328
+ const hasKeyedUpdateHints = appliedKeyedMapUpdateHints > 0 || appliedKeyedArrayAppendHints > 0 || appliedKeyedArrayFilterHints > 0 || appliedKeyedArrayPrependHints > 0 || appliedKeyedArrayPositionHints > 0 || appliedKeyedArrayReorderHints > 0 || appliedKeyedArrayRollingWindowHints > 0 || appliedKeyedArraySliceHints > 0;
5329
+ const hasKeyedArrayRemovalHints = appliedKeyedArrayFilterHints > 0 || appliedKeyedArrayRollingWindowHints > 0 || appliedKeyedArraySliceHints > 0;
5330
+ const runtimeFeatures = runtimeFeaturesForPlans(
5331
+ blockPlans,
5332
+ hasKeyedUpdateHints,
5333
+ hasKeyedArrayRemovalHints,
5334
+ appliedKeyedArrayPrependHints > 0,
5335
+ appliedKeyedArrayPositionHints > 0,
5336
+ appliedKeyedArrayReorderHints > 0,
5337
+ appliedKeyedArrayRollingWindowHints > 0
5338
+ );
3639
5339
  markShortCircuitBindings(analysis.bindings || []);
3640
5340
  assignStableBindingTargets(analysis.bindings || []);
3641
5341
  const stateParameter = path.scope.generateUidIdentifier("farmState");
@@ -3648,11 +5348,21 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3648
5348
  blockParameter,
3649
5349
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set()
3650
5350
  );
5351
+ const appliedRemovalHintedStateIndices = /* @__PURE__ */ new Set([
5352
+ ...appliedFilterHintedStateIndices,
5353
+ ...appliedRollingWindowHintedStateIndices,
5354
+ ...appliedSliceHintedStateIndices
5355
+ ]);
3651
5356
  const rootWithBlocks = lowerComposableBlocks(
3652
5357
  rootWithTargets,
3653
5358
  blockPlans,
3654
5359
  blockParameter,
3655
- listNames
5360
+ listNames,
5361
+ hasKeyedUpdateHints,
5362
+ appliedRemovalHintedStateIndices,
5363
+ appliedPrependHintedStateIndices,
5364
+ appliedPositionHintedStateIndices,
5365
+ appliedReorderHintedStateIndices
3656
5366
  );
3657
5367
  const rewrittenRoot = rewriteStateAccess(
3658
5368
  rootWithBlocks,
@@ -3790,6 +5500,20 @@ function collectCandidates(programPath) {
3790
5500
  async function compileReactModule(code, id, options) {
3791
5501
  const compiled = [];
3792
5502
  const diagnostics = [];
5503
+ const optimizationCounts = {
5504
+ keyedArrayAppendHints: 0,
5505
+ keyedArrayFilterHints: 0,
5506
+ keyedArrayPrependHints: 0,
5507
+ keyedArrayPositionHints: 0,
5508
+ keyedArrayReorderHints: 0,
5509
+ keyedArrayRollingWindowHints: 0,
5510
+ keyedArraySliceHints: 0,
5511
+ keyedCollectionUpdateHints: 0,
5512
+ keyedIdentityTargets: 0,
5513
+ keyedMapLookupTargets: 0,
5514
+ keyedMembershipTargets: 0,
5515
+ keyedMapUpdateHints: 0
5516
+ };
3793
5517
  const plugin = () => ({
3794
5518
  name: "farm-react-aot",
3795
5519
  visitor: {
@@ -3819,6 +5543,36 @@ async function compileReactModule(code, id, options) {
3819
5543
  }
3820
5544
  const candidates = collectCandidates(programPath);
3821
5545
  const createComponentIdentifier = programPath.scope.generateUidIdentifier("createCompiledComponent");
5546
+ const keyedMapUpdateIdentifier = programPath.scope.generateUidIdentifier(
5547
+ "createCompilerKeyedMapUpdate"
5548
+ );
5549
+ const keyedArrayAppendIdentifier = programPath.scope.generateUidIdentifier(
5550
+ "createCompilerKeyedArrayAppend"
5551
+ );
5552
+ const keyedArrayFilterIdentifier = programPath.scope.generateUidIdentifier(
5553
+ "createCompilerKeyedArrayFilter"
5554
+ );
5555
+ const keyedArrayPrependIdentifier = programPath.scope.generateUidIdentifier(
5556
+ "createCompilerKeyedArrayPrepend"
5557
+ );
5558
+ const keyedArrayPositionIdentifier = programPath.scope.generateUidIdentifier(
5559
+ "createCompilerKeyedArrayPositionUpdate"
5560
+ );
5561
+ const keyedArrayReorderIdentifier = programPath.scope.generateUidIdentifier(
5562
+ "createCompilerKeyedArrayReorder"
5563
+ );
5564
+ const keyedArrayRollingWindowIdentifier = programPath.scope.generateUidIdentifier(
5565
+ "createCompilerKeyedArrayRollingWindow"
5566
+ );
5567
+ const keyedArraySliceIdentifier = programPath.scope.generateUidIdentifier(
5568
+ "createCompilerKeyedArraySlice"
5569
+ );
5570
+ const keyedCollectionUpdateIdentifier = programPath.scope.generateUidIdentifier(
5571
+ "createCompilerKeyedCollectionUpdate"
5572
+ );
5573
+ const keyedCollectionMutationIdentifier = programPath.scope.generateUidIdentifier(
5574
+ "applyCompilerKeyedCollectionMutation"
5575
+ );
3822
5576
  const runtimeFeatureIdentifiers = new Map(
3823
5577
  Object.entries(COMPILER_RUNTIME_FEATURE_EXPORTS).map(([feature, exportName]) => [
3824
5578
  feature,
@@ -3836,8 +5590,19 @@ async function compileReactModule(code, id, options) {
3836
5590
  const reason = compileCandidate(
3837
5591
  candidate,
3838
5592
  createComponentIdentifier,
5593
+ keyedMapUpdateIdentifier,
5594
+ keyedArrayAppendIdentifier,
5595
+ keyedArrayFilterIdentifier,
5596
+ keyedArrayPrependIdentifier,
5597
+ keyedArrayPositionIdentifier,
5598
+ keyedArrayReorderIdentifier,
5599
+ keyedArrayRollingWindowIdentifier,
5600
+ keyedArraySliceIdentifier,
5601
+ keyedCollectionUpdateIdentifier,
5602
+ keyedCollectionMutationIdentifier,
3839
5603
  runtimeFeatureIdentifiers,
3840
5604
  usedRuntimeFeatures,
5605
+ optimizationCounts,
3841
5606
  useStateNames,
3842
5607
  reactNames,
3843
5608
  listNames,
@@ -3863,6 +5628,64 @@ async function compileReactModule(code, id, options) {
3863
5628
  createComponentIdentifier,
3864
5629
  types.identifier("createCompiledComponentWithFeatures")
3865
5630
  ),
5631
+ ...optimizationCounts.keyedMapUpdateHints > 0 ? [
5632
+ types.importSpecifier(
5633
+ keyedMapUpdateIdentifier,
5634
+ types.identifier("createCompilerKeyedMapUpdate")
5635
+ )
5636
+ ] : [],
5637
+ ...optimizationCounts.keyedArrayAppendHints > 0 ? [
5638
+ types.importSpecifier(
5639
+ keyedArrayAppendIdentifier,
5640
+ types.identifier("createCompilerKeyedArrayAppend")
5641
+ )
5642
+ ] : [],
5643
+ ...optimizationCounts.keyedArrayFilterHints > 0 ? [
5644
+ types.importSpecifier(
5645
+ keyedArrayFilterIdentifier,
5646
+ types.identifier("createCompilerKeyedArrayFilter")
5647
+ )
5648
+ ] : [],
5649
+ ...optimizationCounts.keyedArrayPrependHints > 0 ? [
5650
+ types.importSpecifier(
5651
+ keyedArrayPrependIdentifier,
5652
+ types.identifier("createCompilerKeyedArrayPrepend")
5653
+ )
5654
+ ] : [],
5655
+ ...optimizationCounts.keyedArrayPositionHints > 0 ? [
5656
+ types.importSpecifier(
5657
+ keyedArrayPositionIdentifier,
5658
+ types.identifier("createCompilerKeyedArrayPositionUpdate")
5659
+ )
5660
+ ] : [],
5661
+ ...optimizationCounts.keyedArrayReorderHints > 0 ? [
5662
+ types.importSpecifier(
5663
+ keyedArrayReorderIdentifier,
5664
+ types.identifier("createCompilerKeyedArrayReorder")
5665
+ )
5666
+ ] : [],
5667
+ ...optimizationCounts.keyedArrayRollingWindowHints > 0 ? [
5668
+ types.importSpecifier(
5669
+ keyedArrayRollingWindowIdentifier,
5670
+ types.identifier("createCompilerKeyedArrayRollingWindow")
5671
+ )
5672
+ ] : [],
5673
+ ...optimizationCounts.keyedArraySliceHints > 0 || optimizationCounts.keyedArrayRollingWindowHints > 0 ? [
5674
+ types.importSpecifier(
5675
+ keyedArraySliceIdentifier,
5676
+ types.identifier("createCompilerKeyedArraySlice")
5677
+ )
5678
+ ] : [],
5679
+ ...optimizationCounts.keyedCollectionUpdateHints > 0 ? [
5680
+ types.importSpecifier(
5681
+ keyedCollectionUpdateIdentifier,
5682
+ types.identifier("createCompilerKeyedCollectionUpdate")
5683
+ ),
5684
+ types.importSpecifier(
5685
+ keyedCollectionMutationIdentifier,
5686
+ types.identifier("applyCompilerKeyedCollectionMutation")
5687
+ )
5688
+ ] : [],
3866
5689
  ...[...usedRuntimeFeatures].sort().map(
3867
5690
  (feature) => types.importSpecifier(
3868
5691
  types.cloneNode(runtimeFeatureIdentifiers.get(feature)),
@@ -3899,7 +5722,8 @@ async function compileReactModule(code, id, options) {
3899
5722
  code: result?.code || code,
3900
5723
  map: result?.map || null,
3901
5724
  compiled,
3902
- diagnostics
5725
+ diagnostics,
5726
+ optimizations: optimizationCounts
3903
5727
  };
3904
5728
  }
3905
5729
  function projectRelativeId(projectRoot, id) {
@@ -3910,10 +5734,34 @@ function createReactCompilerReport(projectRoot, observations) {
3910
5734
  let componentsConsidered = 0;
3911
5735
  let compiled = 0;
3912
5736
  let fallback = 0;
5737
+ let keyedArrayAppendHints = 0;
5738
+ let keyedArrayFilterHints = 0;
5739
+ let keyedArrayPrependHints = 0;
5740
+ let keyedArrayPositionHints = 0;
5741
+ let keyedArrayReorderHints = 0;
5742
+ let keyedArrayRollingWindowHints = 0;
5743
+ let keyedArraySliceHints = 0;
5744
+ let keyedCollectionUpdateHints = 0;
5745
+ let keyedIdentityTargets = 0;
5746
+ let keyedMapLookupTargets = 0;
5747
+ let keyedMembershipTargets = 0;
5748
+ let keyedMapUpdateHints = 0;
3913
5749
  const modules = [...observations].map(({ id, result }) => {
3914
5750
  componentsConsidered += result.compiled.length + result.diagnostics.length;
3915
5751
  compiled += result.compiled.length;
3916
5752
  fallback += result.diagnostics.length;
5753
+ keyedArrayAppendHints += result.optimizations.keyedArrayAppendHints || 0;
5754
+ keyedArrayFilterHints += result.optimizations.keyedArrayFilterHints || 0;
5755
+ keyedArrayPrependHints += result.optimizations.keyedArrayPrependHints || 0;
5756
+ keyedArrayPositionHints += result.optimizations.keyedArrayPositionHints || 0;
5757
+ keyedArrayReorderHints += result.optimizations.keyedArrayReorderHints || 0;
5758
+ keyedArrayRollingWindowHints += result.optimizations.keyedArrayRollingWindowHints || 0;
5759
+ keyedArraySliceHints += result.optimizations.keyedArraySliceHints || 0;
5760
+ keyedCollectionUpdateHints += result.optimizations.keyedCollectionUpdateHints || 0;
5761
+ keyedIdentityTargets += result.optimizations.keyedIdentityTargets || 0;
5762
+ keyedMapLookupTargets += result.optimizations.keyedMapLookupTargets || 0;
5763
+ keyedMembershipTargets += result.optimizations.keyedMembershipTargets || 0;
5764
+ keyedMapUpdateHints += result.optimizations.keyedMapUpdateHints;
3917
5765
  const moduleId = projectRelativeId(projectRoot, id);
3918
5766
  const fallbacks = result.diagnostics.map((diagnostic) => {
3919
5767
  reasonCounts.set(diagnostic.reason, (reasonCounts.get(diagnostic.reason) || 0) + 1);
@@ -3922,6 +5770,7 @@ function createReactCompilerReport(projectRoot, observations) {
3922
5770
  return {
3923
5771
  id: moduleId,
3924
5772
  compiled: [...result.compiled].sort(),
5773
+ optimizations: result.optimizations,
3925
5774
  fallbacks
3926
5775
  };
3927
5776
  }).sort((left, right) => left.id.localeCompare(right.id));
@@ -3932,7 +5781,19 @@ function createReactCompilerReport(projectRoot, observations) {
3932
5781
  modules: modules.length,
3933
5782
  componentsConsidered,
3934
5783
  compiled,
3935
- fallback
5784
+ fallback,
5785
+ keyedArrayAppendHints,
5786
+ keyedArrayFilterHints,
5787
+ keyedArrayPrependHints,
5788
+ keyedArrayPositionHints,
5789
+ keyedArrayReorderHints,
5790
+ keyedArrayRollingWindowHints,
5791
+ keyedArraySliceHints,
5792
+ keyedCollectionUpdateHints,
5793
+ keyedIdentityTargets,
5794
+ keyedMapLookupTargets,
5795
+ keyedMembershipTargets,
5796
+ keyedMapUpdateHints
3936
5797
  },
3937
5798
  fallbackReasons,
3938
5799
  modules