@farm.js/react 0.1.0-beta.4 → 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
@@ -60,6 +60,73 @@ function canDelegateKeyedRowEvents(plan) {
60
60
  (event) => DELEGATABLE_KEYED_ROW_EVENTS.has(event.name.replace(/Capture$/, ""))
61
61
  );
62
62
  }
63
+ var COMPILER_RUNTIME_FEATURE_EXPORTS = {
64
+ conditional: "conditionalRuntimeFeature",
65
+ "host-conditional": "hostConditionalRuntimeFeature",
66
+ "conditional-ranges": "conditionalRangesRuntimeFeature",
67
+ "keyed-list": "keyedListRuntimeFeature",
68
+ "keyed-rows": "keyedRowsRuntimeFeature",
69
+ "keyed-rows-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",
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",
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",
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",
104
+ "keyed-ranges": "keyedRangesRuntimeFeature",
105
+ "mixed-ranges": "mixedRangesRuntimeFeature",
106
+ component: "componentRuntimeFeature"
107
+ };
108
+ function runtimeFeaturesForPlans(plans, keyedMapUpdateHints, keyedArrayFilterHints, keyedArrayPrependHints, keyedArrayPositionHints, keyedArrayReorderHints, keyedArrayRollingWindowHints) {
109
+ const features = /* @__PURE__ */ new Set();
110
+ let keyedRowsHaveConditionals = false;
111
+ let keyedRowsHaveHostBlocks = false;
112
+ for (const plan of plans) {
113
+ if (plan.kind === "keyed-rows") {
114
+ keyedRowsHaveConditionals || (keyedRowsHaveConditionals = plan.conditionals.length > 0);
115
+ keyedRowsHaveHostBlocks || (keyedRowsHaveHostBlocks = Boolean(plan.descriptorBlocks?.size));
116
+ continue;
117
+ }
118
+ if (plan.kind in COMPILER_RUNTIME_FEATURE_EXPORTS) {
119
+ features.add(plan.kind);
120
+ }
121
+ }
122
+ if (plans.some((plan) => plan.kind === "keyed-rows")) {
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}`);
127
+ }
128
+ return [...features].sort();
129
+ }
63
130
  var SAFE_GLOBAL_CALLS = /* @__PURE__ */ new Set(["Boolean", "Number", "String"]);
64
131
  var SAFE_MATH_CALLS = /* @__PURE__ */ new Set(["abs", "ceil", "floor", "max", "min", "round", "sign", "trunc"]);
65
132
  function isComponentName(name) {
@@ -105,6 +172,174 @@ function referencesIdentifier(expression, name) {
105
172
  });
106
173
  return referenced;
107
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
+ }
108
343
  function collectReferencedLocals(expression, names) {
109
344
  const references = /* @__PURE__ */ new Set();
110
345
  traverse(expressionFile(cloneExpression(expression)), {
@@ -205,7 +440,9 @@ function rewriteHandlerAccess(root, handlersByName) {
205
440
  path.skip();
206
441
  }
207
442
  });
208
- return reason ? { reason } : { root: file.program.body[0].expression };
443
+ return reason ? { reason } : {
444
+ root: file.program.body[0].expression
445
+ };
209
446
  }
210
447
  function isSafeCompilerCall(expression, safeGlobals) {
211
448
  if (types.isIdentifier(expression.callee)) {
@@ -213,7 +450,7 @@ function isSafeCompilerCall(expression, safeGlobals) {
213
450
  }
214
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);
215
452
  }
216
- function validateDerivedExpression(expression, safeGlobals) {
453
+ function validateDerivedExpression(expression, safeGlobals, additionalSafeCall) {
217
454
  let unsupported;
218
455
  const reject = (reason, path) => {
219
456
  unsupported = reason;
@@ -224,7 +461,9 @@ function validateDerivedExpression(expression, safeGlobals) {
224
461
  AssignmentExpression: (path) => reject("assignments", path),
225
462
  AwaitExpression: (path) => reject("await expressions", path),
226
463
  CallExpression(path) {
227
- if (!isSafeCompilerCall(path.node, safeGlobals)) reject("function calls", path);
464
+ if (!isSafeCompilerCall(path.node, safeGlobals) && !additionalSafeCall?.(path.node)) {
465
+ reject("function calls", path);
466
+ }
228
467
  },
229
468
  ClassExpression: (path) => reject("class expressions", path),
230
469
  Function: (path) => reject("function expressions", path),
@@ -264,6 +503,879 @@ function validateSetterUsage(root, statesBySetter) {
264
503
  });
265
504
  return reason;
266
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
+ }
267
1379
  function rewriteStateAccess(expression, stateParameter, statesByValue, statesBySetter) {
268
1380
  const file = expressionFile(cloneExpression(expression));
269
1381
  traverse(file, {
@@ -514,21 +1626,28 @@ function analyzeMapList(expression, statesByValue, safeGlobals) {
514
1626
  const collection = callee.object;
515
1627
  const collectionUnsupported = validateCollectionPipeline(collection, safeGlobals);
516
1628
  if (collectionUnsupported) {
517
- return { reason: `keyed list collection cannot use ${collectionUnsupported}` };
1629
+ return {
1630
+ reason: `keyed list collection cannot use ${collectionUnsupported}`
1631
+ };
518
1632
  }
519
1633
  if (expression.arguments.length !== 1 || !types.isArrowFunctionExpression(expression.arguments[0]) && !types.isFunctionExpression(expression.arguments[0])) {
520
1634
  return { reason: "keyed list map must use one inline render callback" };
521
1635
  }
522
1636
  const callback = expression.arguments[0];
523
1637
  if (containsDirectHookCall(callback)) {
524
- 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
+ };
525
1641
  }
526
1642
  const row = returnedExpression(callback);
527
1643
  if (!row || !types.isJSXElement(row)) {
528
1644
  return { reason: "keyed list map callbacks must return one React element" };
529
1645
  }
530
1646
  const key = jsxKeyExpression(row);
531
- 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
+ };
532
1651
  const keyReason = validateKeyFunction(callback, key, safeGlobals);
533
1652
  if (keyReason) return { reason: keyReason };
534
1653
  return { dependencies: collectStateDependencies(expression, statesByValue) };
@@ -543,7 +1662,9 @@ function listAttributeExpression(element, name) {
543
1662
  }
544
1663
  function analyzePublicList(element, statesByValue, safeGlobals) {
545
1664
  if (element.openingElement.attributes.some((attribute) => types.isJSXSpreadAttribute(attribute))) {
546
- 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
+ };
547
1668
  }
548
1669
  const each = listAttributeExpression(element, "each");
549
1670
  const by = listAttributeExpression(element, "by");
@@ -561,7 +1682,9 @@ function analyzePublicList(element, statesByValue, safeGlobals) {
561
1682
  if (keyReason) return { reason: keyReason };
562
1683
  const children = meaningfulJsxChildren(element);
563
1684
  if (children.length !== 1 || !types.isJSXExpressionContainer(children[0]) || types.isJSXEmptyExpression(children[0].expression) || !types.isArrowFunctionExpression(children[0].expression) && !types.isFunctionExpression(children[0].expression)) {
564
- 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
+ };
565
1688
  }
566
1689
  const render = children[0].expression;
567
1690
  if (!returnedExpression(render)) {
@@ -675,13 +1798,20 @@ function stabilizeDeferredEventCurrentTargets(root, compilerSetters) {
675
1798
  const expression = file.program.body[0].expression;
676
1799
  return types.isJSXElement(expression) ? expression : types.cloneNode(root, true);
677
1800
  }
678
- function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters) {
1801
+ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters, keyedTargetReactiveByValue) {
679
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])) {
680
- 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
+ };
681
1806
  }
682
1807
  const bindings = [];
683
1808
  const events = [];
684
1809
  const conditionals = [];
1810
+ const validateBindingExpression = (expression) => validateDerivedExpression(
1811
+ expression,
1812
+ safeGlobals,
1813
+ keyedTargetReactiveByValue ? (call) => isPotentialKeyedMembershipCall(call, keyedTargetReactiveByValue) || isPotentialKeyedMapLookupCall(call, keyedTargetReactiveByValue) : void 0
1814
+ );
685
1815
  const visit = (element, path) => {
686
1816
  const tag = element.openingElement.name;
687
1817
  if (!types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
@@ -760,7 +1890,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
760
1890
  if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
761
1891
  return "compiled keyed row style names must use camelCase or a CSS custom property";
762
1892
  }
763
- const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
1893
+ const unsupported2 = validateBindingExpression(property.value);
764
1894
  if (unsupported2) {
765
1895
  return `compiled keyed row style ${propertyName} cannot use ${unsupported2}`;
766
1896
  }
@@ -774,7 +1904,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
774
1904
  continue;
775
1905
  }
776
1906
  if (name === "children") return "compiled keyed row children props are not supported yet";
777
- const unsupported = validateDerivedExpression(expression, safeGlobals);
1907
+ const unsupported = validateBindingExpression(expression);
778
1908
  if (unsupported) return `compiled keyed row ${name} cannot use ${unsupported}`;
779
1909
  bindings.push({
780
1910
  kind: "attribute",
@@ -824,7 +1954,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
824
1954
  (child) => types.isJSXExpressionContainer(child) && !types.isJSXEmptyExpression(child.expression)
825
1955
  );
826
1956
  for (const child of expressions) {
827
- const unsupported = validateDerivedExpression(child.expression, safeGlobals);
1957
+ const unsupported = validateBindingExpression(child.expression);
828
1958
  if (unsupported) return `compiled keyed row text cannot use ${unsupported}`;
829
1959
  }
830
1960
  if (nestedElements.length > 0 && expressions.length > 0) {
@@ -840,7 +1970,11 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
840
1970
  parts.push(cloneExpression(child.expression));
841
1971
  }
842
1972
  }
843
- 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
+ });
844
1978
  }
845
1979
  let elementIndex = 0;
846
1980
  for (const child of element.children) {
@@ -854,7 +1988,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
854
1988
  const reason = visit(row, []);
855
1989
  return reason ? { reason } : { bindings, events, conditionals };
856
1990
  }
857
- function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false) {
1991
+ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false, allowMembershipTargets = false) {
858
1992
  let collection;
859
1993
  let keyCallback;
860
1994
  let renderCallback;
@@ -907,7 +2041,8 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
907
2041
  row,
908
2042
  renderCallback,
909
2043
  safeGlobals,
910
- new Set([...statesByValue.values()].map((state) => state.setterName))
2044
+ new Set([...statesByValue.values()].map((state) => state.setterName)),
2045
+ allowMembershipTargets ? statesByValue : void 0
911
2046
  );
912
2047
  if (rowAnalysis.reason && !acceptUnsupportedRow) return void 0;
913
2048
  const keyExpression = returnedExpression(keyCallback);
@@ -920,6 +2055,7 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
920
2055
  return {
921
2056
  source,
922
2057
  collection,
2058
+ collectionDependency: types.isIdentifier(collection) ? statesByValue.get(collection.name)?.index : void 0,
923
2059
  structureDependencies: [...structureDependencies].sort((left, right) => left - right),
924
2060
  keyCallback,
925
2061
  renderCallback,
@@ -948,7 +2084,74 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
948
2084
  }
949
2085
  const children = meaningfulJsxChildren(container);
950
2086
  if (children.length !== 1) return void 0;
951
- 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
+ };
952
2155
  }
953
2156
  function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
954
2157
  if (container.openingElement.attributes.some(
@@ -1104,7 +2307,11 @@ function analyzeHostConditionalTree(branch, safeGlobals, allowStaticEvents = fal
1104
2307
  parts.push(cloneExpression(child.expression));
1105
2308
  }
1106
2309
  }
1107
- 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
+ });
1108
2315
  }
1109
2316
  let elementIndex = 0;
1110
2317
  for (const child of element.children) {
@@ -1276,7 +2483,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1276
2483
  listNames,
1277
2484
  id,
1278
2485
  allocateId
1279
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2486
+ ) : {
2487
+ bindings: [],
2488
+ plans: [],
2489
+ blocks: /* @__PURE__ */ new Map()
2490
+ };
1280
2491
  const falsy = shape.falsy ? analyzeRecursiveHostTree(
1281
2492
  shape.falsy,
1282
2493
  statesByValue,
@@ -1284,7 +2495,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1284
2495
  listNames,
1285
2496
  id,
1286
2497
  allocateId
1287
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2498
+ ) : {
2499
+ bindings: [],
2500
+ plans: [],
2501
+ blocks: /* @__PURE__ */ new Map()
2502
+ };
1288
2503
  if (truthy.reason) return truthy.reason;
1289
2504
  if (falsy.reason) return falsy.reason;
1290
2505
  plans.push(...truthy.plans, ...falsy.plans);
@@ -1442,7 +2657,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1442
2657
  parts.push(cloneExpression(child.expression));
1443
2658
  }
1444
2659
  }
1445
- 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
+ });
1446
2665
  }
1447
2666
  let elementIndex = 0;
1448
2667
  for (const child of element.children) {
@@ -1509,7 +2728,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1509
2728
  listNames,
1510
2729
  parent,
1511
2730
  allocateId
1512
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2731
+ ) : {
2732
+ bindings: [],
2733
+ plans: [],
2734
+ blocks: /* @__PURE__ */ new Map()
2735
+ };
1513
2736
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1514
2737
  shape.falsy,
1515
2738
  statesByValue,
@@ -1517,7 +2740,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1517
2740
  listNames,
1518
2741
  parent,
1519
2742
  allocateId
1520
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2743
+ ) : {
2744
+ bindings: [],
2745
+ plans: [],
2746
+ blocks: /* @__PURE__ */ new Map()
2747
+ };
1521
2748
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1522
2749
  const dependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1523
2750
  for (const binding of [...truthyAnalysis.bindings || [], ...falsyAnalysis.bindings || []]) {
@@ -1563,7 +2790,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1563
2790
  listNames,
1564
2791
  parent,
1565
2792
  allocateId
1566
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2793
+ ) : {
2794
+ bindings: [],
2795
+ plans: [],
2796
+ blocks: /* @__PURE__ */ new Map()
2797
+ };
1567
2798
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1568
2799
  shape.falsy,
1569
2800
  statesByValue,
@@ -1571,7 +2802,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1571
2802
  listNames,
1572
2803
  parent,
1573
2804
  allocateId
1574
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2805
+ ) : {
2806
+ bindings: [],
2807
+ plans: [],
2808
+ blocks: /* @__PURE__ */ new Map()
2809
+ };
1575
2810
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1576
2811
  nestedPlans.push(...truthyAnalysis.plans, ...falsyAnalysis.plans);
1577
2812
  for (const [element, plan] of truthyAnalysis.blocks) descriptorBlocks.set(element, plan);
@@ -1664,7 +2899,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1664
2899
  listNames,
1665
2900
  parent,
1666
2901
  allocateId
1667
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2902
+ ) : {
2903
+ bindings: [],
2904
+ plans: [],
2905
+ blocks: /* @__PURE__ */ new Map()
2906
+ };
1668
2907
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1669
2908
  shape.falsy,
1670
2909
  statesByValue,
@@ -1672,7 +2911,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1672
2911
  listNames,
1673
2912
  parent,
1674
2913
  allocateId
1675
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2914
+ ) : {
2915
+ bindings: [],
2916
+ plans: [],
2917
+ blocks: /* @__PURE__ */ new Map()
2918
+ };
1676
2919
  if (!mergeAnalysis(truthyAnalysis) || !mergeAnalysis(falsyAnalysis)) return void 0;
1677
2920
  for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1678
2921
  dependencies.add(dependency);
@@ -1913,6 +3156,57 @@ function keyedRowBindingObject(binding, parameters) {
1913
3156
  if (binding.name) {
1914
3157
  properties.push(types.objectProperty(types.identifier("name"), types.stringLiteral(binding.name)));
1915
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
+ }
1916
3210
  properties.push(
1917
3211
  types.objectProperty(
1918
3212
  types.identifier("read"),
@@ -2107,7 +3401,7 @@ function keyedRowConditionalObject(conditional, parameters) {
2107
3401
  }
2108
3402
  return types.objectExpression(properties);
2109
3403
  }
2110
- function keyedRowsBoundary(blockRuntime, plan) {
3404
+ function keyedRowsBoundary(blockRuntime, plan, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
2111
3405
  const name = types.jsxMemberExpression(
2112
3406
  types.jsxIdentifier(blockRuntime.name),
2113
3407
  types.jsxIdentifier("KeyedRows")
@@ -2141,6 +3435,16 @@ function keyedRowsBoundary(blockRuntime, plan) {
2141
3435
  types.jsxIdentifier("items"),
2142
3436
  types.jsxExpressionContainer(types.arrowFunctionExpression([], cloneExpression(plan.collection)))
2143
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
+ ] : [],
2144
3448
  types.jsxAttribute(
2145
3449
  types.jsxIdentifier("structureDependencies"),
2146
3450
  types.jsxExpressionContainer(
@@ -2149,6 +3453,36 @@ function keyedRowsBoundary(blockRuntime, plan) {
2149
3453
  )
2150
3454
  )
2151
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
+ ] : [],
2152
3486
  types.jsxAttribute(
2153
3487
  types.jsxIdentifier("rowKey"),
2154
3488
  types.jsxExpressionContainer(types.cloneNode(plan.keyCallback, true))
@@ -2475,7 +3809,9 @@ function conditionalRangesBoundary(blockRuntime, plan) {
2475
3809
  function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allowedComponentNames) {
2476
3810
  const name = element.openingElement.name;
2477
3811
  if (!types.isJSXIdentifier(name) || !isComponentName(name.name)) {
2478
- return { reason: "component islands require a direct component identifier" };
3812
+ return {
3813
+ reason: "component islands require a direct component identifier"
3814
+ };
2479
3815
  }
2480
3816
  if (!allowedComponentNames.has(name.name)) {
2481
3817
  return {
@@ -2483,19 +3819,27 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2483
3819
  };
2484
3820
  }
2485
3821
  if (meaningfulJsxChildren(element).length > 0) {
2486
- 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
+ };
2487
3825
  }
2488
3826
  const dependencies = /* @__PURE__ */ new Set();
2489
3827
  for (const attribute of element.openingElement.attributes) {
2490
3828
  if (types.isJSXSpreadAttribute(attribute)) {
2491
- 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
+ };
2492
3832
  }
2493
3833
  const attributeName = jsxAttributeName(attribute);
2494
3834
  if (!attributeName) {
2495
- 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
+ };
2496
3838
  }
2497
3839
  if (attributeName === "ref" || attributeName === "key" || attributeName === "children") {
2498
- 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
+ };
2499
3843
  }
2500
3844
  if (!types.isJSXExpressionContainer(attribute.value) || types.isJSXEmptyExpression(attribute.value.expression)) {
2501
3845
  continue;
@@ -2514,7 +3858,9 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2514
3858
  (dependency) => dependencies.add(dependency)
2515
3859
  );
2516
3860
  }
2517
- return { dependencies: [...dependencies].sort((left, right) => left - right) };
3861
+ return {
3862
+ dependencies: [...dependencies].sort((left, right) => left - right)
3863
+ };
2518
3864
  }
2519
3865
  function componentIslandBoundary(blockRuntime, plan, source) {
2520
3866
  const name = types.jsxMemberExpression(
@@ -2771,6 +4117,7 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
2771
4117
  dependencies: keyedRows.dependencies,
2772
4118
  source: child,
2773
4119
  collection: keyedRows.collection,
4120
+ collectionDependency: keyedRows.collectionDependency,
2774
4121
  structureDependencies: keyedRows.structureDependencies,
2775
4122
  keyCallback: keyedRows.keyCallback,
2776
4123
  renderCallback: keyedRows.renderCallback,
@@ -3012,7 +4359,7 @@ function conditionalBlockBoundary(blockRuntime, plan, source) {
3012
4359
  true
3013
4360
  );
3014
4361
  }
3015
- function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
4362
+ function lowerComposableBlocks(root, plans, blockRuntime, listNames, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
3016
4363
  const planBySource = new Map(
3017
4364
  plans.map((plan) => [plan.source, plan])
3018
4365
  );
@@ -3037,7 +4384,15 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
3037
4384
  return mixedRangesBoundary(blockRuntime, plan2);
3038
4385
  }
3039
4386
  if (plan2?.kind === "keyed-rows") {
3040
- return keyedRowsBoundary(blockRuntime, plan2);
4387
+ return keyedRowsBoundary(
4388
+ blockRuntime,
4389
+ plan2,
4390
+ keyedMapUpdateHints,
4391
+ keyedArrayFilterHintedStateIndices,
4392
+ keyedArrayPrependHintedStateIndices,
4393
+ keyedArrayPositionHintedStateIndices,
4394
+ keyedArrayReorderHintedStateIndices
4395
+ );
3041
4396
  }
3042
4397
  if (plan2?.kind === "keyed-ranges") {
3043
4398
  return keyedRangesBoundary(blockRuntime, plan2);
@@ -3400,7 +4755,7 @@ function wrapperElement(definitionIdentifier, props) {
3400
4755
  const attributes = props ? [types.jsxSpreadAttribute(types.cloneNode(props))] : [];
3401
4756
  return types.jsxElement(types.jsxOpeningElement(name, attributes, true), null, [], true);
3402
4757
  }
3403
- function compileCandidate(candidate, createComponentIdentifier, 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) {
3404
4759
  const { path, name, statementPath } = candidate;
3405
4760
  if (path.node.async || path.node.generator)
3406
4761
  return "async and generator components are not supported";
@@ -3412,6 +4767,7 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3412
4767
  const safeGlobals = new Set(
3413
4768
  [...SAFE_GLOBAL_CALLS, "Math"].filter((name2) => !path.scope.getBinding(name2))
3414
4769
  );
4770
+ const globalCollections = new Set(["Set", "Map"].filter((name2) => !path.scope.getBinding(name2)));
3415
4771
  const states = [];
3416
4772
  const locals = [];
3417
4773
  let returned;
@@ -3459,9 +4815,17 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3459
4815
  if (declaration.init.async || declaration.init.generator) {
3460
4816
  return `event handler ${declaration.id.name} must be synchronous`;
3461
4817
  }
3462
- 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
+ });
3463
4823
  } else {
3464
- 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
+ });
3465
4829
  }
3466
4830
  }
3467
4831
  if (states.length === 0) return "no local useState binding was found";
@@ -3600,7 +4964,378 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3600
4964
  }
3601
4965
  if (blockAnalysis.reason) return blockAnalysis.reason;
3602
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
+ }
3603
5315
  const blockPlans = blockAnalysis.plans || [];
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
+ );
3604
5339
  markShortCircuitBindings(analysis.bindings || []);
3605
5340
  assignStableBindingTargets(analysis.bindings || []);
3606
5341
  const stateParameter = path.scope.generateUidIdentifier("farmState");
@@ -3613,11 +5348,21 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3613
5348
  blockParameter,
3614
5349
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set()
3615
5350
  );
5351
+ const appliedRemovalHintedStateIndices = /* @__PURE__ */ new Set([
5352
+ ...appliedFilterHintedStateIndices,
5353
+ ...appliedRollingWindowHintedStateIndices,
5354
+ ...appliedSliceHintedStateIndices
5355
+ ]);
3616
5356
  const rootWithBlocks = lowerComposableBlocks(
3617
5357
  rootWithTargets,
3618
5358
  blockPlans,
3619
5359
  blockParameter,
3620
- listNames
5360
+ listNames,
5361
+ hasKeyedUpdateHints,
5362
+ appliedRemovalHintedStateIndices,
5363
+ appliedPrependHintedStateIndices,
5364
+ appliedPositionHintedStateIndices,
5365
+ appliedReorderHintedStateIndices
3621
5366
  );
3622
5367
  const rewrittenRoot = rewriteStateAccess(
3623
5368
  rootWithBlocks,
@@ -3703,7 +5448,10 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3703
5448
  )
3704
5449
  )
3705
5450
  )
3706
- ])
5451
+ ]),
5452
+ types.arrayExpression(
5453
+ runtimeFeatures.map((feature) => types.cloneNode(runtimeFeatureIdentifiers.get(feature)))
5454
+ )
3707
5455
  ]);
3708
5456
  path.get("body").replaceWith(
3709
5457
  types.blockStatement([
@@ -3713,6 +5461,7 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3713
5461
  statementPath.insertAfter(
3714
5462
  types.variableDeclaration("const", [types.variableDeclarator(definitionIdentifier, definition)])
3715
5463
  );
5464
+ for (const feature of runtimeFeatures) usedRuntimeFeatures.add(feature);
3716
5465
  return void 0;
3717
5466
  }
3718
5467
  function collectCandidates(programPath) {
@@ -3751,6 +5500,20 @@ function collectCandidates(programPath) {
3751
5500
  async function compileReactModule(code, id, options) {
3752
5501
  const compiled = [];
3753
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
+ };
3754
5517
  const plugin = () => ({
3755
5518
  name: "farm-react-aot",
3756
5519
  visitor: {
@@ -3780,6 +5543,43 @@ async function compileReactModule(code, id, options) {
3780
5543
  }
3781
5544
  const candidates = collectCandidates(programPath);
3782
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
+ );
5576
+ const runtimeFeatureIdentifiers = new Map(
5577
+ Object.entries(COMPILER_RUNTIME_FEATURE_EXPORTS).map(([feature, exportName]) => [
5578
+ feature,
5579
+ programPath.scope.generateUidIdentifier(exportName)
5580
+ ])
5581
+ );
5582
+ const usedRuntimeFeatures = /* @__PURE__ */ new Set();
3783
5583
  for (const candidate of candidates) {
3784
5584
  const functionDirectives = new Set(
3785
5585
  types.isBlockStatement(candidate.path.node.body) ? directiveValues(candidate.path.node.body) : []
@@ -3790,6 +5590,19 @@ async function compileReactModule(code, id, options) {
3790
5590
  const reason = compileCandidate(
3791
5591
  candidate,
3792
5592
  createComponentIdentifier,
5593
+ keyedMapUpdateIdentifier,
5594
+ keyedArrayAppendIdentifier,
5595
+ keyedArrayFilterIdentifier,
5596
+ keyedArrayPrependIdentifier,
5597
+ keyedArrayPositionIdentifier,
5598
+ keyedArrayReorderIdentifier,
5599
+ keyedArrayRollingWindowIdentifier,
5600
+ keyedArraySliceIdentifier,
5601
+ keyedCollectionUpdateIdentifier,
5602
+ keyedCollectionMutationIdentifier,
5603
+ runtimeFeatureIdentifiers,
5604
+ usedRuntimeFeatures,
5605
+ optimizationCounts,
3793
5606
  useStateNames,
3794
5607
  reactNames,
3795
5608
  listNames,
@@ -3813,7 +5626,71 @@ async function compileReactModule(code, id, options) {
3813
5626
  [
3814
5627
  types.importSpecifier(
3815
5628
  createComponentIdentifier,
3816
- types.identifier("createCompiledComponent")
5629
+ types.identifier("createCompiledComponentWithFeatures")
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
+ ] : [],
5689
+ ...[...usedRuntimeFeatures].sort().map(
5690
+ (feature) => types.importSpecifier(
5691
+ types.cloneNode(runtimeFeatureIdentifiers.get(feature)),
5692
+ types.identifier(COMPILER_RUNTIME_FEATURE_EXPORTS[feature])
5693
+ )
3817
5694
  )
3818
5695
  ],
3819
5696
  types.stringLiteral("@farm.js/react/compiler-runtime")
@@ -3845,7 +5722,8 @@ async function compileReactModule(code, id, options) {
3845
5722
  code: result?.code || code,
3846
5723
  map: result?.map || null,
3847
5724
  compiled,
3848
- diagnostics
5725
+ diagnostics,
5726
+ optimizations: optimizationCounts
3849
5727
  };
3850
5728
  }
3851
5729
  function projectRelativeId(projectRoot, id) {
@@ -3856,10 +5734,34 @@ function createReactCompilerReport(projectRoot, observations) {
3856
5734
  let componentsConsidered = 0;
3857
5735
  let compiled = 0;
3858
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;
3859
5749
  const modules = [...observations].map(({ id, result }) => {
3860
5750
  componentsConsidered += result.compiled.length + result.diagnostics.length;
3861
5751
  compiled += result.compiled.length;
3862
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;
3863
5765
  const moduleId = projectRelativeId(projectRoot, id);
3864
5766
  const fallbacks = result.diagnostics.map((diagnostic) => {
3865
5767
  reasonCounts.set(diagnostic.reason, (reasonCounts.get(diagnostic.reason) || 0) + 1);
@@ -3868,6 +5770,7 @@ function createReactCompilerReport(projectRoot, observations) {
3868
5770
  return {
3869
5771
  id: moduleId,
3870
5772
  compiled: [...result.compiled].sort(),
5773
+ optimizations: result.optimizations,
3871
5774
  fallbacks
3872
5775
  };
3873
5776
  }).sort((left, right) => left.id.localeCompare(right.id));
@@ -3878,7 +5781,19 @@ function createReactCompilerReport(projectRoot, observations) {
3878
5781
  modules: modules.length,
3879
5782
  componentsConsidered,
3880
5783
  compiled,
3881
- 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
3882
5797
  },
3883
5798
  fallbackReasons,
3884
5799
  modules