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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.cjs CHANGED
@@ -68,14 +68,46 @@ var COMPILER_RUNTIME_FEATURE_EXPORTS = {
68
68
  "conditional-ranges": "conditionalRangesRuntimeFeature",
69
69
  "keyed-list": "keyedListRuntimeFeature",
70
70
  "keyed-rows": "keyedRowsRuntimeFeature",
71
+ "keyed-rows-hinted": "keyedRowsHintedRuntimeFeature",
72
+ "keyed-rows-position-hinted": "keyedRowsPositionHintedRuntimeFeature",
73
+ "keyed-rows-reorder-hinted": "keyedRowsReorderHintedRuntimeFeature",
74
+ "keyed-rows-all-hinted": "keyedRowsAllHintedRuntimeFeature",
75
+ "keyed-rows-every-hinted": "keyedRowsEveryHintedRuntimeFeature",
76
+ "keyed-rows-filter-hinted": "keyedRowsFilterHintedRuntimeFeature",
77
+ "keyed-rows-prepend-hinted": "keyedRowsPrependHintedRuntimeFeature",
78
+ "keyed-rows-filter-prepend-hinted": "keyedRowsFilterPrependHintedRuntimeFeature",
71
79
  "keyed-rows-conditional": "keyedRowsConditionalRuntimeFeature",
80
+ "keyed-rows-conditional-hinted": "keyedRowsConditionalHintedRuntimeFeature",
81
+ "keyed-rows-conditional-position-hinted": "keyedRowsConditionalPositionHintedRuntimeFeature",
82
+ "keyed-rows-conditional-reorder-hinted": "keyedRowsConditionalReorderHintedRuntimeFeature",
83
+ "keyed-rows-conditional-all-hinted": "keyedRowsConditionalAllHintedRuntimeFeature",
84
+ "keyed-rows-conditional-every-hinted": "keyedRowsConditionalEveryHintedRuntimeFeature",
85
+ "keyed-rows-conditional-filter-hinted": "keyedRowsConditionalFilterHintedRuntimeFeature",
86
+ "keyed-rows-conditional-prepend-hinted": "keyedRowsConditionalPrependHintedRuntimeFeature",
87
+ "keyed-rows-conditional-filter-prepend-hinted": "keyedRowsConditionalFilterPrependHintedRuntimeFeature",
72
88
  "keyed-rows-host": "keyedRowsHostRuntimeFeature",
89
+ "keyed-rows-host-hinted": "keyedRowsHostHintedRuntimeFeature",
90
+ "keyed-rows-host-position-hinted": "keyedRowsHostPositionHintedRuntimeFeature",
91
+ "keyed-rows-host-reorder-hinted": "keyedRowsHostReorderHintedRuntimeFeature",
92
+ "keyed-rows-host-all-hinted": "keyedRowsHostAllHintedRuntimeFeature",
93
+ "keyed-rows-host-every-hinted": "keyedRowsHostEveryHintedRuntimeFeature",
94
+ "keyed-rows-host-filter-hinted": "keyedRowsHostFilterHintedRuntimeFeature",
95
+ "keyed-rows-host-prepend-hinted": "keyedRowsHostPrependHintedRuntimeFeature",
96
+ "keyed-rows-host-filter-prepend-hinted": "keyedRowsHostFilterPrependHintedRuntimeFeature",
73
97
  "keyed-rows-complete": "keyedRowsCompleteRuntimeFeature",
98
+ "keyed-rows-complete-hinted": "keyedRowsCompleteHintedRuntimeFeature",
99
+ "keyed-rows-complete-position-hinted": "keyedRowsCompletePositionHintedRuntimeFeature",
100
+ "keyed-rows-complete-reorder-hinted": "keyedRowsCompleteReorderHintedRuntimeFeature",
101
+ "keyed-rows-complete-all-hinted": "keyedRowsCompleteAllHintedRuntimeFeature",
102
+ "keyed-rows-complete-every-hinted": "keyedRowsCompleteEveryHintedRuntimeFeature",
103
+ "keyed-rows-complete-filter-hinted": "keyedRowsCompleteFilterHintedRuntimeFeature",
104
+ "keyed-rows-complete-prepend-hinted": "keyedRowsCompletePrependHintedRuntimeFeature",
105
+ "keyed-rows-complete-filter-prepend-hinted": "keyedRowsCompleteFilterPrependHintedRuntimeFeature",
74
106
  "keyed-ranges": "keyedRangesRuntimeFeature",
75
107
  "mixed-ranges": "mixedRangesRuntimeFeature",
76
108
  component: "componentRuntimeFeature"
77
109
  };
78
- function runtimeFeaturesForPlans(plans) {
110
+ function runtimeFeaturesForPlans(plans, keyedMapUpdateHints, keyedArrayFilterHints, keyedArrayPrependHints, keyedArrayPositionHints, keyedArrayReorderHints, keyedArrayRollingWindowHints) {
79
111
  const features = /* @__PURE__ */ new Set();
80
112
  let keyedRowsHaveConditionals = false;
81
113
  let keyedRowsHaveHostBlocks = false;
@@ -90,9 +122,10 @@ function runtimeFeaturesForPlans(plans) {
90
122
  }
91
123
  }
92
124
  if (plans.some((plan) => plan.kind === "keyed-rows")) {
93
- features.add(
94
- keyedRowsHaveConditionals && keyedRowsHaveHostBlocks ? "keyed-rows-complete" : keyedRowsHaveConditionals ? "keyed-rows-conditional" : keyedRowsHaveHostBlocks ? "keyed-rows-host" : "keyed-rows"
95
- );
125
+ const keyedRowsFeature = keyedRowsHaveConditionals && keyedRowsHaveHostBlocks ? "keyed-rows-complete" : keyedRowsHaveConditionals ? "keyed-rows-conditional" : keyedRowsHaveHostBlocks ? "keyed-rows-host" : "keyed-rows";
126
+ const arrayRangeHints = keyedArrayRollingWindowHints || keyedArrayFilterHints || keyedArrayPrependHints;
127
+ 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" : "";
128
+ features.add(`${keyedRowsFeature}${hintSuffix}`);
96
129
  }
97
130
  return [...features].sort();
98
131
  }
@@ -141,6 +174,174 @@ function referencesIdentifier(expression, name) {
141
174
  });
142
175
  return referenced;
143
176
  }
177
+ function rewriteFreeIdentifierNames(expression, replacements) {
178
+ if (replacements.size === 0) return cloneExpression(expression);
179
+ const file = expressionFile(cloneExpression(expression));
180
+ core.traverse(file, {
181
+ ReferencedIdentifier(path) {
182
+ if (path.scope.hasBinding(path.node.name)) return;
183
+ const replacement = replacements.get(path.node.name);
184
+ if (!replacement) return;
185
+ path.replaceWith(core.types.cloneNode(replacement));
186
+ path.skip();
187
+ }
188
+ });
189
+ return file.program.body[0].expression;
190
+ }
191
+ function keyedIdentityTarget(binding, keyCallback, renderCallback, reactiveByValue, structureDependencies) {
192
+ const dependencies = binding.dependencies || [];
193
+ if (dependencies.length !== 1 || structureDependencies.has(dependencies[0])) return void 0;
194
+ const keyExpression = returnedExpression(keyCallback);
195
+ const keyItem = keyCallback.params[0];
196
+ const rowItem = renderCallback.params[0];
197
+ if (!keyExpression || !core.types.isIdentifier(keyItem) || !core.types.isIdentifier(rowItem)) return void 0;
198
+ const replacements = /* @__PURE__ */ new Map([[keyItem.name, rowItem]]);
199
+ const keyIndex = keyCallback.params[1];
200
+ const rowIndex = renderCallback.params[1];
201
+ if (core.types.isIdentifier(keyIndex) && core.types.isIdentifier(rowIndex)) {
202
+ replacements.set(keyIndex.name, rowIndex);
203
+ }
204
+ const rowKeyExpression = rewriteFreeIdentifierNames(keyExpression, replacements);
205
+ const file = expressionFile(cloneExpression(binding.value));
206
+ let target;
207
+ let valid = true;
208
+ let comparisons = 0;
209
+ core.traverse(file, {
210
+ ReferencedIdentifier(path) {
211
+ if (!valid || path.scope.hasBinding(path.node.name)) return;
212
+ const reactive = reactiveByValue.get(path.node.name);
213
+ if (!reactive) return;
214
+ if (reactive.index !== dependencies[0] || target && target !== reactive) {
215
+ valid = false;
216
+ path.stop();
217
+ return;
218
+ }
219
+ const parent = path.parentPath;
220
+ if (!parent.isBinaryExpression() || parent.node.operator !== "===" && parent.node.operator !== "!==") {
221
+ valid = false;
222
+ path.stop();
223
+ return;
224
+ }
225
+ const other = parent.node.left === path.node ? parent.node.right : parent.node.left;
226
+ if (!core.types.isExpression(other) || !core.types.isNodesEquivalent(other, rowKeyExpression)) {
227
+ valid = false;
228
+ path.stop();
229
+ return;
230
+ }
231
+ target = reactive;
232
+ comparisons += 1;
233
+ }
234
+ });
235
+ return valid && target && comparisons > 0 ? { dependency: target.index, value: core.types.identifier(target.valueName) } : void 0;
236
+ }
237
+ function isPotentialKeyedMembershipCall(expression, reactiveByValue) {
238
+ if (expression.arguments.length !== 1 || !core.types.isExpression(expression.arguments[0]) || !core.types.isMemberExpression(expression.callee) || expression.callee.computed || !core.types.isIdentifier(expression.callee.object) || !core.types.isIdentifier(expression.callee.property, { name: "has" })) {
239
+ return false;
240
+ }
241
+ const reactive = reactiveByValue.get(expression.callee.object.name);
242
+ return Boolean(reactive?.setterName);
243
+ }
244
+ function containsPotentialKeyedMembershipCall(expression, reactiveByValue) {
245
+ let found = false;
246
+ core.traverse(expressionFile(cloneExpression(expression)), {
247
+ CallExpression(path) {
248
+ if (!isPotentialKeyedMembershipCall(path.node, reactiveByValue)) return;
249
+ found = true;
250
+ path.stop();
251
+ }
252
+ });
253
+ return found;
254
+ }
255
+ function isPotentialKeyedMapLookupCall(expression, reactiveByValue) {
256
+ if (expression.arguments.length !== 1 || !core.types.isExpression(expression.arguments[0]) || !core.types.isMemberExpression(expression.callee) || expression.callee.computed || !core.types.isIdentifier(expression.callee.object) || !core.types.isIdentifier(expression.callee.property, { name: "get" })) {
257
+ return false;
258
+ }
259
+ const reactive = reactiveByValue.get(expression.callee.object.name);
260
+ return Boolean(reactive?.setterName);
261
+ }
262
+ function containsPotentialKeyedMapLookupCall(expression, reactiveByValue) {
263
+ let found = false;
264
+ core.traverse(expressionFile(cloneExpression(expression)), {
265
+ CallExpression(path) {
266
+ if (!isPotentialKeyedMapLookupCall(path.node, reactiveByValue)) return;
267
+ found = true;
268
+ path.stop();
269
+ }
270
+ });
271
+ return found;
272
+ }
273
+ function keyedMembershipTarget(binding, keyCallback, renderCallback, reactiveByValue, structureDependencies) {
274
+ const dependencies = binding.dependencies || [];
275
+ if (dependencies.length !== 1 || structureDependencies.has(dependencies[0])) return void 0;
276
+ const keyExpression = returnedExpression(keyCallback);
277
+ const keyItem = keyCallback.params[0];
278
+ const rowItem = renderCallback.params[0];
279
+ if (!keyExpression || !core.types.isIdentifier(keyItem) || !core.types.isIdentifier(rowItem)) return void 0;
280
+ const replacements = /* @__PURE__ */ new Map([[keyItem.name, rowItem]]);
281
+ const keyIndex = keyCallback.params[1];
282
+ const rowIndex = renderCallback.params[1];
283
+ if (core.types.isIdentifier(keyIndex) && core.types.isIdentifier(rowIndex)) {
284
+ replacements.set(keyIndex.name, rowIndex);
285
+ }
286
+ const rowKeyExpression = rewriteFreeIdentifierNames(keyExpression, replacements);
287
+ const file = expressionFile(cloneExpression(binding.value));
288
+ let target;
289
+ let valid = true;
290
+ let calls = 0;
291
+ core.traverse(file, {
292
+ ReferencedIdentifier(path) {
293
+ if (!valid || path.scope.hasBinding(path.node.name)) return;
294
+ const reactive = reactiveByValue.get(path.node.name);
295
+ if (!reactive) return;
296
+ const member = path.parentPath;
297
+ const call = member.parentPath;
298
+ if (!reactive.setterName || reactive.index !== dependencies[0] || target && target !== reactive || !member.isMemberExpression() || member.node.object !== path.node || member.node.computed || !core.types.isIdentifier(member.node.property, { name: "has" }) || !call || !call.isCallExpression() || call.node.callee !== member.node || call.node.arguments.length !== 1 || !core.types.isExpression(call.node.arguments[0]) || !core.types.isNodesEquivalent(call.node.arguments[0], rowKeyExpression)) {
299
+ valid = false;
300
+ path.stop();
301
+ return;
302
+ }
303
+ target = reactive;
304
+ calls += 1;
305
+ }
306
+ });
307
+ return valid && target && calls > 0 ? { dependency: target.index, value: core.types.identifier(target.valueName) } : void 0;
308
+ }
309
+ function keyedMapLookupTarget(binding, keyCallback, renderCallback, reactiveByValue, structureDependencies) {
310
+ const dependencies = binding.dependencies || [];
311
+ if (dependencies.length !== 1 || structureDependencies.has(dependencies[0])) return void 0;
312
+ const keyExpression = returnedExpression(keyCallback);
313
+ const keyItem = keyCallback.params[0];
314
+ const rowItem = renderCallback.params[0];
315
+ if (!keyExpression || !core.types.isIdentifier(keyItem) || !core.types.isIdentifier(rowItem)) return void 0;
316
+ const replacements = /* @__PURE__ */ new Map([[keyItem.name, rowItem]]);
317
+ const keyIndex = keyCallback.params[1];
318
+ const rowIndex = renderCallback.params[1];
319
+ if (core.types.isIdentifier(keyIndex) && core.types.isIdentifier(rowIndex)) {
320
+ replacements.set(keyIndex.name, rowIndex);
321
+ }
322
+ const rowKeyExpression = rewriteFreeIdentifierNames(keyExpression, replacements);
323
+ const file = expressionFile(cloneExpression(binding.value));
324
+ let target;
325
+ let valid = true;
326
+ let calls = 0;
327
+ core.traverse(file, {
328
+ ReferencedIdentifier(path) {
329
+ if (!valid || path.scope.hasBinding(path.node.name)) return;
330
+ const reactive = reactiveByValue.get(path.node.name);
331
+ if (!reactive) return;
332
+ const member = path.parentPath;
333
+ const call = member.parentPath;
334
+ if (!reactive.setterName || reactive.index !== dependencies[0] || target && target !== reactive || !member.isMemberExpression() || member.node.object !== path.node || member.node.computed || !core.types.isIdentifier(member.node.property, { name: "get" }) || !call || !call.isCallExpression() || call.node.callee !== member.node || call.node.arguments.length !== 1 || !core.types.isExpression(call.node.arguments[0]) || !core.types.isNodesEquivalent(call.node.arguments[0], rowKeyExpression)) {
335
+ valid = false;
336
+ path.stop();
337
+ return;
338
+ }
339
+ target = reactive;
340
+ calls += 1;
341
+ }
342
+ });
343
+ return valid && target && calls > 0 ? { dependency: target.index, value: core.types.identifier(target.valueName) } : void 0;
344
+ }
144
345
  function collectReferencedLocals(expression, names) {
145
346
  const references = /* @__PURE__ */ new Set();
146
347
  core.traverse(expressionFile(cloneExpression(expression)), {
@@ -241,7 +442,9 @@ function rewriteHandlerAccess(root, handlersByName) {
241
442
  path.skip();
242
443
  }
243
444
  });
244
- return reason ? { reason } : { root: file.program.body[0].expression };
445
+ return reason ? { reason } : {
446
+ root: file.program.body[0].expression
447
+ };
245
448
  }
246
449
  function isSafeCompilerCall(expression, safeGlobals) {
247
450
  if (core.types.isIdentifier(expression.callee)) {
@@ -249,7 +452,7 @@ function isSafeCompilerCall(expression, safeGlobals) {
249
452
  }
250
453
  return core.types.isMemberExpression(expression.callee) && !expression.callee.computed && core.types.isIdentifier(expression.callee.object, { name: "Math" }) && safeGlobals.has("Math") && core.types.isIdentifier(expression.callee.property) && SAFE_MATH_CALLS.has(expression.callee.property.name);
251
454
  }
252
- function validateDerivedExpression(expression, safeGlobals) {
455
+ function validateDerivedExpression(expression, safeGlobals, additionalSafeCall) {
253
456
  let unsupported;
254
457
  const reject = (reason, path) => {
255
458
  unsupported = reason;
@@ -260,7 +463,9 @@ function validateDerivedExpression(expression, safeGlobals) {
260
463
  AssignmentExpression: (path) => reject("assignments", path),
261
464
  AwaitExpression: (path) => reject("await expressions", path),
262
465
  CallExpression(path) {
263
- if (!isSafeCompilerCall(path.node, safeGlobals)) reject("function calls", path);
466
+ if (!isSafeCompilerCall(path.node, safeGlobals) && !additionalSafeCall?.(path.node)) {
467
+ reject("function calls", path);
468
+ }
264
469
  },
265
470
  ClassExpression: (path) => reject("class expressions", path),
266
471
  Function: (path) => reject("function expressions", path),
@@ -300,6 +505,879 @@ function validateSetterUsage(root, statesBySetter) {
300
505
  });
301
506
  return reason;
302
507
  }
508
+ function isUnchangedMapItem(expression, item) {
509
+ return core.types.isIdentifier(expression, { name: item.name });
510
+ }
511
+ function isSafeKeyedMapReplacement(expression, item, safeGlobals) {
512
+ if (!core.types.isObjectExpression(expression)) return false;
513
+ let spreadsItem = false;
514
+ for (const property of expression.properties) {
515
+ if (core.types.isSpreadElement(property)) {
516
+ if (!core.types.isIdentifier(property.argument, { name: item.name })) return false;
517
+ spreadsItem = true;
518
+ continue;
519
+ }
520
+ if (!core.types.isObjectProperty(property) || property.computed || !core.types.isExpression(property.value)) {
521
+ return false;
522
+ }
523
+ if (validateDerivedExpression(property.value, safeGlobals)) return false;
524
+ }
525
+ return spreadsItem;
526
+ }
527
+ function isSafeKeyedMapResult(expression, item, safeGlobals) {
528
+ if (!core.types.isConditionalExpression(expression)) return false;
529
+ if (validateDerivedExpression(expression.test, safeGlobals)) return false;
530
+ return isUnchangedMapItem(expression.consequent, item) && isSafeKeyedMapReplacement(expression.alternate, item, safeGlobals) || isUnchangedMapItem(expression.alternate, item) && isSafeKeyedMapReplacement(expression.consequent, item, safeGlobals);
531
+ }
532
+ function rewriteKeyedMapUpdateHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
533
+ if (hintedStateIndices.size === 0) return { root: core.types.cloneNode(root, true), count: 0 };
534
+ const file = expressionFile(core.types.cloneNode(root, true));
535
+ let count = 0;
536
+ core.traverse(file, {
537
+ CallExpression(path) {
538
+ const callee = path.get("callee");
539
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
540
+ const state = statesBySetter.get(callee.node.name);
541
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
542
+ return;
543
+ }
544
+ const updater = path.node.arguments[0];
545
+ if (!core.types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0]) || !core.types.isCallExpression(updater.body) || !core.types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !core.types.isIdentifier(updater.body.callee.object, {
546
+ name: updater.params[0].name
547
+ }) || !core.types.isIdentifier(updater.body.callee.property, { name: "map" })) {
548
+ return;
549
+ }
550
+ const callback = updater.body.arguments[0];
551
+ if (!core.types.isArrowFunctionExpression(callback) || callback.async || callback.generator || callback.params.length === 0 || callback.params.length > 3 || callback.params.some((parameter) => !core.types.isIdentifier(parameter)) || !core.types.isExpression(callback.body)) {
552
+ return;
553
+ }
554
+ const item = callback.params[0];
555
+ if (!isSafeKeyedMapResult(callback.body, item, safeGlobals)) return;
556
+ const changedIndices = path.scope.generateUidIdentifier("farmChangedIndices");
557
+ const mappedItem = path.scope.generateUidIdentifier("farmMappedItem");
558
+ const nextValue = path.scope.generateUidIdentifier("farmNextItems");
559
+ const index = core.types.isIdentifier(callback.params[1]) ? core.types.cloneNode(callback.params[1]) : path.scope.generateUidIdentifier("farmMapIndex");
560
+ const wrappedCallback = core.types.cloneNode(callback, true);
561
+ if (wrappedCallback.params.length === 1) wrappedCallback.params.push(core.types.cloneNode(index));
562
+ wrappedCallback.body = core.types.blockStatement([
563
+ core.types.variableDeclaration("const", [
564
+ core.types.variableDeclarator(core.types.cloneNode(mappedItem), core.types.cloneNode(callback.body, true))
565
+ ]),
566
+ core.types.ifStatement(
567
+ core.types.binaryExpression("!==", core.types.cloneNode(mappedItem), core.types.cloneNode(item)),
568
+ core.types.expressionStatement(
569
+ core.types.callExpression(
570
+ core.types.memberExpression(core.types.cloneNode(changedIndices), core.types.identifier("push")),
571
+ [core.types.cloneNode(index)]
572
+ )
573
+ )
574
+ ),
575
+ core.types.returnStatement(core.types.cloneNode(mappedItem))
576
+ ]);
577
+ const mapCall = core.types.cloneNode(updater.body, true);
578
+ mapCall.arguments[0] = wrappedCallback;
579
+ const previous = core.types.cloneNode(updater.params[0]);
580
+ path.node.arguments[0] = core.types.arrowFunctionExpression(
581
+ [core.types.cloneNode(previous)],
582
+ core.types.blockStatement([
583
+ core.types.variableDeclaration("const", [
584
+ core.types.variableDeclarator(core.types.cloneNode(changedIndices), core.types.arrayExpression([]))
585
+ ]),
586
+ core.types.variableDeclaration("const", [core.types.variableDeclarator(core.types.cloneNode(nextValue), mapCall)]),
587
+ core.types.returnStatement(
588
+ core.types.callExpression(core.types.cloneNode(helperIdentifier), [
589
+ core.types.cloneNode(previous),
590
+ core.types.cloneNode(nextValue),
591
+ core.types.cloneNode(changedIndices)
592
+ ])
593
+ )
594
+ ])
595
+ );
596
+ count += 1;
597
+ path.skip();
598
+ }
599
+ });
600
+ return {
601
+ root: file.program.body[0].expression,
602
+ count
603
+ };
604
+ }
605
+ function rewriteKeyedArrayAppendHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
606
+ if (hintedStateIndices.size === 0) return { root: core.types.cloneNode(root, true), count: 0 };
607
+ const file = expressionFile(core.types.cloneNode(root, true));
608
+ let count = 0;
609
+ core.traverse(file, {
610
+ CallExpression(path) {
611
+ const callee = path.get("callee");
612
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
613
+ const state = statesBySetter.get(callee.node.name);
614
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
615
+ return;
616
+ }
617
+ const updater = path.node.arguments[0];
618
+ if (!core.types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0]) || !core.types.isArrayExpression(updater.body) || updater.body.elements.length < 2 || updater.body.elements.some((element) => element === null) || !core.types.isSpreadElement(updater.body.elements[0]) || !core.types.isIdentifier(updater.body.elements[0].argument, {
619
+ name: updater.params[0].name
620
+ })) {
621
+ return;
622
+ }
623
+ const safeAppendValue = (value) => {
624
+ if (core.types.isArrayExpression(value)) {
625
+ return value.elements.every(
626
+ (element) => element !== null && !core.types.isJSXNamespacedName(element) && !core.types.isArgumentPlaceholder(element) && safeAppendValue(core.types.isSpreadElement(element) ? element.argument : element)
627
+ );
628
+ }
629
+ if (core.types.isObjectExpression(value)) {
630
+ return value.properties.every((property) => {
631
+ if (core.types.isSpreadElement(property)) return safeAppendValue(property.argument);
632
+ if (!core.types.isObjectProperty(property) || !core.types.isExpression(property.value)) return false;
633
+ return (!property.computed || core.types.isExpression(property.key) && safeAppendValue(property.key)) && safeAppendValue(property.value);
634
+ });
635
+ }
636
+ return validateDerivedExpression(value, safeGlobals) === void 0;
637
+ };
638
+ if (updater.body.elements.slice(1).some((element) => {
639
+ if (!element || core.types.isJSXNamespacedName(element) || core.types.isArgumentPlaceholder(element)) {
640
+ return true;
641
+ }
642
+ return !safeAppendValue(core.types.isSpreadElement(element) ? element.argument : element);
643
+ })) {
644
+ return;
645
+ }
646
+ const previous = core.types.cloneNode(updater.params[0]);
647
+ const nextValue = path.scope.generateUidIdentifier("farmNextItems");
648
+ path.node.arguments[0] = core.types.arrowFunctionExpression(
649
+ [core.types.cloneNode(previous)],
650
+ core.types.blockStatement([
651
+ core.types.variableDeclaration("const", [
652
+ core.types.variableDeclarator(core.types.cloneNode(nextValue), core.types.cloneNode(updater.body, true))
653
+ ]),
654
+ core.types.returnStatement(
655
+ core.types.callExpression(core.types.cloneNode(helperIdentifier), [
656
+ core.types.cloneNode(previous),
657
+ core.types.cloneNode(nextValue)
658
+ ])
659
+ )
660
+ ])
661
+ );
662
+ count += 1;
663
+ path.skip();
664
+ }
665
+ });
666
+ return {
667
+ root: file.program.body[0].expression,
668
+ count
669
+ };
670
+ }
671
+ function rewriteKeyedArrayPrependHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
672
+ if (hintedStateIndices.size === 0) {
673
+ return { root: core.types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
674
+ }
675
+ const file = expressionFile(core.types.cloneNode(root, true));
676
+ const stateIndices = /* @__PURE__ */ new Set();
677
+ let count = 0;
678
+ core.traverse(file, {
679
+ CallExpression(path) {
680
+ const callee = path.get("callee");
681
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
682
+ const state = statesBySetter.get(callee.node.name);
683
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
684
+ return;
685
+ }
686
+ const updater = path.node.arguments[0];
687
+ if (!core.types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0]) || !core.types.isArrayExpression(updater.body) || updater.body.elements.length < 2 || updater.body.elements.some((element) => element === null)) {
688
+ return;
689
+ }
690
+ const suffix = updater.body.elements[updater.body.elements.length - 1];
691
+ if (!core.types.isSpreadElement(suffix) || !core.types.isIdentifier(suffix.argument, { name: updater.params[0].name })) {
692
+ return;
693
+ }
694
+ const safePrependValue = (value) => {
695
+ if (core.types.isArrayExpression(value)) {
696
+ return value.elements.every(
697
+ (element) => element !== null && !core.types.isJSXNamespacedName(element) && !core.types.isArgumentPlaceholder(element) && safePrependValue(core.types.isSpreadElement(element) ? element.argument : element)
698
+ );
699
+ }
700
+ if (core.types.isObjectExpression(value)) {
701
+ return value.properties.every((property) => {
702
+ if (core.types.isSpreadElement(property)) return safePrependValue(property.argument);
703
+ if (!core.types.isObjectProperty(property) || !core.types.isExpression(property.value)) return false;
704
+ return (!property.computed || core.types.isExpression(property.key) && safePrependValue(property.key)) && safePrependValue(property.value);
705
+ });
706
+ }
707
+ return validateDerivedExpression(value, safeGlobals) === void 0;
708
+ };
709
+ if (updater.body.elements.slice(0, -1).some((element) => {
710
+ if (!element || core.types.isJSXNamespacedName(element) || core.types.isArgumentPlaceholder(element)) {
711
+ return true;
712
+ }
713
+ return !safePrependValue(core.types.isSpreadElement(element) ? element.argument : element);
714
+ })) {
715
+ return;
716
+ }
717
+ const previous = core.types.cloneNode(updater.params[0]);
718
+ const nextValue = path.scope.generateUidIdentifier("farmNextItems");
719
+ path.node.arguments[0] = core.types.arrowFunctionExpression(
720
+ [core.types.cloneNode(previous)],
721
+ core.types.blockStatement([
722
+ core.types.variableDeclaration("const", [
723
+ core.types.variableDeclarator(core.types.cloneNode(nextValue), core.types.cloneNode(updater.body, true))
724
+ ]),
725
+ core.types.returnStatement(
726
+ core.types.callExpression(core.types.cloneNode(helperIdentifier), [
727
+ core.types.cloneNode(previous),
728
+ core.types.cloneNode(nextValue)
729
+ ])
730
+ )
731
+ ])
732
+ );
733
+ count += 1;
734
+ stateIndices.add(state.index);
735
+ path.skip();
736
+ }
737
+ });
738
+ return {
739
+ root: file.program.body[0].expression,
740
+ count,
741
+ stateIndices
742
+ };
743
+ }
744
+ function rewriteKeyedArrayRollingWindowHints(root, hintedStateIndices, statesBySetter, sliceHelperIdentifier, rollingWindowHelperIdentifier, safeGlobals) {
745
+ if (hintedStateIndices.size === 0) {
746
+ return { root: core.types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
747
+ }
748
+ const file = expressionFile(core.types.cloneNode(root, true));
749
+ const stateIndices = /* @__PURE__ */ new Set();
750
+ let count = 0;
751
+ core.traverse(file, {
752
+ CallExpression(path) {
753
+ const callee = path.get("callee");
754
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
755
+ const state = statesBySetter.get(callee.node.name);
756
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
757
+ return;
758
+ }
759
+ const updater = path.node.arguments[0];
760
+ if (!core.types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0]) || !core.types.isArrayExpression(updater.body) || updater.body.elements.length < 2 || updater.body.elements.some((element) => element === null)) {
761
+ return;
762
+ }
763
+ const retainedSpread = updater.body.elements[0];
764
+ if (!core.types.isSpreadElement(retainedSpread) || !core.types.isCallExpression(retainedSpread.argument) || retainedSpread.argument.arguments.length !== 1 || !core.types.isMemberExpression(retainedSpread.argument.callee) || retainedSpread.argument.callee.computed || !core.types.isIdentifier(retainedSpread.argument.callee.object, {
765
+ name: updater.params[0].name
766
+ }) || !core.types.isIdentifier(retainedSpread.argument.callee.property, { name: "slice" }) || !core.types.isExpression(retainedSpread.argument.arguments[0])) {
767
+ return;
768
+ }
769
+ const retainedStart = staticSliceIndex(retainedSpread.argument.arguments[0]);
770
+ if (retainedStart === void 0 || retainedStart === 0) return;
771
+ const safeIncomingValue = (value) => {
772
+ if (core.types.isArrayExpression(value)) {
773
+ return value.elements.every(
774
+ (element) => element !== null && !core.types.isJSXNamespacedName(element) && !core.types.isArgumentPlaceholder(element) && safeIncomingValue(core.types.isSpreadElement(element) ? element.argument : element)
775
+ );
776
+ }
777
+ if (core.types.isObjectExpression(value)) {
778
+ return value.properties.every((property) => {
779
+ if (core.types.isSpreadElement(property)) return safeIncomingValue(property.argument);
780
+ if (!core.types.isObjectProperty(property) || !core.types.isExpression(property.value)) return false;
781
+ return (!property.computed || core.types.isExpression(property.key) && safeIncomingValue(property.key)) && safeIncomingValue(property.value);
782
+ });
783
+ }
784
+ return validateDerivedExpression(value, safeGlobals) === void 0;
785
+ };
786
+ if (updater.body.elements.slice(1).some((element) => {
787
+ if (!element || core.types.isJSXNamespacedName(element) || core.types.isArgumentPlaceholder(element)) {
788
+ return true;
789
+ }
790
+ return !safeIncomingValue(core.types.isSpreadElement(element) ? element.argument : element);
791
+ })) {
792
+ return;
793
+ }
794
+ const previous = core.types.cloneNode(updater.params[0]);
795
+ const sliceMethod = path.scope.generateUidIdentifier("farmSlice");
796
+ const retained = path.scope.generateUidIdentifier("farmRetainedItems");
797
+ const nextValue = path.scope.generateUidIdentifier("farmNextItems");
798
+ const nextElements = core.types.cloneNode(updater.body, true).elements;
799
+ nextElements[0] = core.types.spreadElement(core.types.cloneNode(retained));
800
+ path.node.arguments[0] = core.types.arrowFunctionExpression(
801
+ [core.types.cloneNode(previous)],
802
+ core.types.blockStatement([
803
+ core.types.variableDeclaration("const", [
804
+ core.types.variableDeclarator(
805
+ core.types.cloneNode(sliceMethod),
806
+ core.types.memberExpression(core.types.cloneNode(previous), core.types.identifier("slice"))
807
+ ),
808
+ core.types.variableDeclarator(
809
+ core.types.cloneNode(retained),
810
+ core.types.callExpression(core.types.cloneNode(sliceHelperIdentifier), [
811
+ core.types.cloneNode(previous),
812
+ core.types.cloneNode(sliceMethod),
813
+ core.types.numericLiteral(retainedStart)
814
+ ])
815
+ ),
816
+ core.types.variableDeclarator(core.types.cloneNode(nextValue), core.types.arrayExpression(nextElements))
817
+ ]),
818
+ core.types.returnStatement(
819
+ core.types.callExpression(core.types.cloneNode(rollingWindowHelperIdentifier), [
820
+ core.types.cloneNode(previous),
821
+ core.types.cloneNode(retained),
822
+ core.types.cloneNode(nextValue)
823
+ ])
824
+ )
825
+ ])
826
+ );
827
+ count += 1;
828
+ stateIndices.add(state.index);
829
+ path.skip();
830
+ }
831
+ });
832
+ return {
833
+ root: file.program.body[0].expression,
834
+ count,
835
+ stateIndices
836
+ };
837
+ }
838
+ function staticSliceIndex(expression) {
839
+ if (core.types.isNumericLiteral(expression) && Number.isSafeInteger(expression.value)) {
840
+ return expression.value;
841
+ }
842
+ if (core.types.isUnaryExpression(expression) && (expression.operator === "+" || expression.operator === "-") && core.types.isNumericLiteral(expression.argument)) {
843
+ const value = expression.operator === "-" ? -expression.argument.value : expression.argument.value;
844
+ return Number.isSafeInteger(value) ? value : void 0;
845
+ }
846
+ return void 0;
847
+ }
848
+ function rewriteKeyedArrayPositionHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
849
+ if (hintedStateIndices.size === 0) {
850
+ return { root: core.types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
851
+ }
852
+ const file = expressionFile(core.types.cloneNode(root, true));
853
+ const stateIndices = /* @__PURE__ */ new Set();
854
+ let count = 0;
855
+ core.traverse(file, {
856
+ CallExpression(path) {
857
+ const callee = path.get("callee");
858
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
859
+ const state = statesBySetter.get(callee.node.name);
860
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
861
+ return;
862
+ }
863
+ const updater = path.node.arguments[0];
864
+ if (!core.types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0]) || !core.types.isCallExpression(updater.body) || !core.types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !core.types.isIdentifier(updater.body.callee.object, { name: updater.params[0].name }) || !core.types.isIdentifier(updater.body.callee.property)) {
865
+ return;
866
+ }
867
+ const methodName = updater.body.callee.property.name;
868
+ const args = updater.body.arguments;
869
+ const kind = methodName === "with" && args.length === 2 ? "replace" : methodName === "toSpliced" && args.length === 3 && core.types.isNumericLiteral(args[1], { value: 0 }) ? "insert" : void 0;
870
+ if (!kind || !core.types.isExpression(args[0]) || !core.types.isExpression(args.at(-1))) return;
871
+ const position = staticSliceIndex(args[0]);
872
+ const item = args.at(-1);
873
+ if (position === void 0 || !core.types.isExpression(item) || validateDerivedExpression(item, safeGlobals) !== void 0) {
874
+ return;
875
+ }
876
+ const previous = core.types.cloneNode(updater.params[0]);
877
+ const method = path.scope.generateUidIdentifier(
878
+ methodName === "with" ? "farmWith" : "farmToSpliced"
879
+ );
880
+ path.node.arguments[0] = core.types.arrowFunctionExpression(
881
+ [core.types.cloneNode(previous)],
882
+ core.types.blockStatement([
883
+ core.types.variableDeclaration("const", [
884
+ core.types.variableDeclarator(
885
+ core.types.cloneNode(method),
886
+ core.types.memberExpression(core.types.cloneNode(previous), core.types.identifier(methodName))
887
+ )
888
+ ]),
889
+ core.types.returnStatement(
890
+ core.types.callExpression(core.types.cloneNode(helperIdentifier), [
891
+ core.types.cloneNode(previous),
892
+ core.types.cloneNode(method),
893
+ core.types.stringLiteral(kind),
894
+ ...args.map((argument) => core.types.cloneNode(argument, true))
895
+ ])
896
+ )
897
+ ])
898
+ );
899
+ count += 1;
900
+ stateIndices.add(state.index);
901
+ path.skip();
902
+ }
903
+ });
904
+ return {
905
+ root: file.program.body[0].expression,
906
+ count,
907
+ stateIndices
908
+ };
909
+ }
910
+ function rewriteKeyedArrayReorderHints(root, hintedStateIndices, statesBySetter, helperIdentifier) {
911
+ if (hintedStateIndices.size === 0) {
912
+ return { root: core.types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
913
+ }
914
+ const file = expressionFile(core.types.cloneNode(root, true));
915
+ const stateIndices = /* @__PURE__ */ new Set();
916
+ let count = 0;
917
+ core.traverse(file, {
918
+ CallExpression(path) {
919
+ const callee = path.get("callee");
920
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
921
+ const state = statesBySetter.get(callee.node.name);
922
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
923
+ return;
924
+ }
925
+ const updater = path.node.arguments[0];
926
+ if (!core.types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0]) || !core.types.isCallExpression(updater.body) || updater.body.arguments.length !== 0 || !core.types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !core.types.isIdentifier(updater.body.callee.object, { name: updater.params[0].name }) || !core.types.isIdentifier(updater.body.callee.property, { name: "toReversed" })) {
927
+ return;
928
+ }
929
+ const previous = core.types.cloneNode(updater.params[0]);
930
+ const method = path.scope.generateUidIdentifier("farmToReversed");
931
+ path.node.arguments[0] = core.types.arrowFunctionExpression(
932
+ [core.types.cloneNode(previous)],
933
+ core.types.blockStatement([
934
+ core.types.variableDeclaration("const", [
935
+ core.types.variableDeclarator(
936
+ core.types.cloneNode(method),
937
+ core.types.memberExpression(core.types.cloneNode(previous), core.types.identifier("toReversed"))
938
+ )
939
+ ]),
940
+ core.types.returnStatement(
941
+ core.types.callExpression(core.types.cloneNode(helperIdentifier), [
942
+ core.types.cloneNode(previous),
943
+ core.types.cloneNode(method)
944
+ ])
945
+ )
946
+ ])
947
+ );
948
+ count += 1;
949
+ stateIndices.add(state.index);
950
+ path.skip();
951
+ }
952
+ });
953
+ return {
954
+ root: file.program.body[0].expression,
955
+ count,
956
+ stateIndices
957
+ };
958
+ }
959
+ function rewriteKeyedArraySliceHints(root, hintedStateIndices, statesBySetter, helperIdentifier) {
960
+ if (hintedStateIndices.size === 0) {
961
+ return { root: core.types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
962
+ }
963
+ const file = expressionFile(core.types.cloneNode(root, true));
964
+ const stateIndices = /* @__PURE__ */ new Set();
965
+ let count = 0;
966
+ core.traverse(file, {
967
+ CallExpression(path) {
968
+ const callee = path.get("callee");
969
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
970
+ const state = statesBySetter.get(callee.node.name);
971
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
972
+ return;
973
+ }
974
+ const updater = path.node.arguments[0];
975
+ if (!core.types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0]) || !core.types.isCallExpression(updater.body) || updater.body.arguments.length < 1 || updater.body.arguments.length > 2 || !core.types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !core.types.isIdentifier(updater.body.callee.object, { name: updater.params[0].name }) || !core.types.isIdentifier(updater.body.callee.property, { name: "slice" })) {
976
+ return;
977
+ }
978
+ const indexes = [];
979
+ for (const argument of updater.body.arguments) {
980
+ if (!core.types.isExpression(argument)) return;
981
+ const value = staticSliceIndex(argument);
982
+ if (value === void 0) return;
983
+ indexes.push(value);
984
+ }
985
+ if (indexes.length === 1 && indexes[0] === 0) return;
986
+ const previous = core.types.cloneNode(updater.params[0]);
987
+ const sliceMethod = path.scope.generateUidIdentifier("farmSlice");
988
+ path.node.arguments[0] = core.types.arrowFunctionExpression(
989
+ [core.types.cloneNode(previous)],
990
+ core.types.blockStatement([
991
+ core.types.variableDeclaration("const", [
992
+ core.types.variableDeclarator(
993
+ core.types.cloneNode(sliceMethod),
994
+ core.types.memberExpression(core.types.cloneNode(previous), core.types.identifier("slice"))
995
+ )
996
+ ]),
997
+ core.types.returnStatement(
998
+ core.types.callExpression(core.types.cloneNode(helperIdentifier), [
999
+ core.types.cloneNode(previous),
1000
+ core.types.cloneNode(sliceMethod),
1001
+ ...indexes.map((value) => core.types.numericLiteral(value))
1002
+ ])
1003
+ )
1004
+ ])
1005
+ );
1006
+ count += 1;
1007
+ stateIndices.add(state.index);
1008
+ path.skip();
1009
+ }
1010
+ });
1011
+ return {
1012
+ root: file.program.body[0].expression,
1013
+ count,
1014
+ stateIndices
1015
+ };
1016
+ }
1017
+ function rewriteKeyedArrayFilterHints(root, hintedStateIndices, statesBySetter, helperIdentifier, safeGlobals) {
1018
+ if (hintedStateIndices.size === 0) {
1019
+ return { root: core.types.cloneNode(root, true), count: 0, stateIndices: /* @__PURE__ */ new Set() };
1020
+ }
1021
+ const file = expressionFile(core.types.cloneNode(root, true));
1022
+ const stateIndices = /* @__PURE__ */ new Set();
1023
+ let count = 0;
1024
+ core.traverse(file, {
1025
+ CallExpression(path) {
1026
+ const callee = path.get("callee");
1027
+ if (!callee.isIdentifier() || callee.scope.hasBinding(callee.node.name)) return;
1028
+ const state = statesBySetter.get(callee.node.name);
1029
+ if (!state || !hintedStateIndices.has(state.index) || path.node.arguments.length !== 1) {
1030
+ return;
1031
+ }
1032
+ const updater = path.node.arguments[0];
1033
+ if (!core.types.isArrowFunctionExpression(updater) || updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0]) || !core.types.isCallExpression(updater.body) || updater.body.arguments.length !== 1 || !core.types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !core.types.isIdentifier(updater.body.callee.object, {
1034
+ name: updater.params[0].name
1035
+ }) || !core.types.isIdentifier(updater.body.callee.property, { name: "filter" })) {
1036
+ return;
1037
+ }
1038
+ const predicate = updater.body.arguments[0];
1039
+ if (!core.types.isArrowFunctionExpression(predicate) || predicate.async || predicate.generator || predicate.params.length !== 1 || !core.types.isIdentifier(predicate.params[0]) || !core.types.isExpression(predicate.body) || validateDerivedExpression(predicate.body, safeGlobals)) {
1040
+ return;
1041
+ }
1042
+ const previous = core.types.cloneNode(updater.params[0]);
1043
+ const filterMethod = path.scope.generateUidIdentifier("farmFilter");
1044
+ path.node.arguments[0] = core.types.arrowFunctionExpression(
1045
+ [core.types.cloneNode(previous)],
1046
+ core.types.blockStatement([
1047
+ core.types.variableDeclaration("const", [
1048
+ core.types.variableDeclarator(
1049
+ core.types.cloneNode(filterMethod),
1050
+ core.types.memberExpression(core.types.cloneNode(previous), core.types.identifier("filter"))
1051
+ )
1052
+ ]),
1053
+ core.types.returnStatement(
1054
+ core.types.callExpression(core.types.cloneNode(helperIdentifier), [
1055
+ core.types.cloneNode(previous),
1056
+ core.types.cloneNode(filterMethod),
1057
+ core.types.cloneNode(predicate, true)
1058
+ ])
1059
+ )
1060
+ ])
1061
+ );
1062
+ count += 1;
1063
+ stateIndices.add(state.index);
1064
+ path.skip();
1065
+ }
1066
+ });
1067
+ return {
1068
+ root: file.program.body[0].expression,
1069
+ count,
1070
+ stateIndices
1071
+ };
1072
+ }
1073
+ function collectionConstructorName(kind) {
1074
+ return kind === "set" ? "Set" : "Map";
1075
+ }
1076
+ function isCollectionConstruction(expression, kind, previous) {
1077
+ if (!core.types.isNewExpression(expression) || !core.types.isIdentifier(expression.callee, {
1078
+ name: collectionConstructorName(kind)
1079
+ })) {
1080
+ return false;
1081
+ }
1082
+ if (!previous) return true;
1083
+ return expression.arguments.length === 1 && core.types.isIdentifier(expression.arguments[0], { name: previous.name });
1084
+ }
1085
+ function collectionStateInitializerIsOwned(initialValue, kind, constructorIsGlobal) {
1086
+ if (!initialValue || !constructorIsGlobal) return false;
1087
+ if (isCollectionConstruction(initialValue, kind)) return true;
1088
+ if (!core.types.isArrowFunctionExpression(initialValue) && !core.types.isFunctionExpression(initialValue)) {
1089
+ return false;
1090
+ }
1091
+ if (core.types.isBlockStatement(initialValue.body) && initialValue.body.body.length !== 1) return false;
1092
+ const value = returnedExpression(initialValue);
1093
+ return Boolean(value && isCollectionConstruction(value, kind));
1094
+ }
1095
+ function collectionStateHasOnlyOwnedReads(root, state, kind) {
1096
+ const file = expressionFile(core.types.cloneNode(root, true));
1097
+ let valid = true;
1098
+ const readMethods = kind === "set" ? /* @__PURE__ */ new Set(["has"]) : /* @__PURE__ */ new Set(["get", "has"]);
1099
+ core.traverse(file, {
1100
+ ReferencedIdentifier(path) {
1101
+ if (!valid || !core.types.isIdentifier(path.node) || path.node.name !== state.valueName || path.scope.hasBinding(path.node.name)) {
1102
+ return;
1103
+ }
1104
+ const parent = path.parentPath;
1105
+ if (parent.isNewExpression() && core.types.isIdentifier(parent.node.callee, {
1106
+ name: collectionConstructorName(kind)
1107
+ }) && parent.node.arguments.length === 1 && parent.node.arguments[0] === path.node) {
1108
+ return;
1109
+ }
1110
+ if (!parent.isMemberExpression() || parent.node.object !== path.node || parent.node.computed || !core.types.isIdentifier(parent.node.property)) {
1111
+ valid = false;
1112
+ path.stop();
1113
+ return;
1114
+ }
1115
+ const property = parent.node.property.name;
1116
+ if (property === "size") return;
1117
+ const call = parent.parentPath;
1118
+ if (!readMethods.has(property) || !call?.isCallExpression() || call.node.callee !== parent.node) {
1119
+ valid = false;
1120
+ path.stop();
1121
+ }
1122
+ }
1123
+ });
1124
+ return valid;
1125
+ }
1126
+ function collectionMutationOperation(kind, method) {
1127
+ if (kind === "set" && method === "add") return "set-add";
1128
+ if (kind === "set" && method === "delete") return "set-delete";
1129
+ if (kind === "map" && method === "set") return "map-set";
1130
+ if (kind === "map" && method === "delete") return "map-delete";
1131
+ return void 0;
1132
+ }
1133
+ function collectionMutationArgumentsAreValid(operation, args) {
1134
+ if (!operation || args.some((argument) => !core.types.isExpression(argument))) return false;
1135
+ return operation === "map-set" ? args.length === 2 : args.length === 1;
1136
+ }
1137
+ function prepareKeyedCollectionUpdater(updater, kind, updateHelper, mutationHelper) {
1138
+ if (updater.async || updater.generator || updater.params.length !== 1 || !core.types.isIdentifier(updater.params[0])) {
1139
+ return void 0;
1140
+ }
1141
+ const previous = updater.params[0];
1142
+ if (core.types.isExpression(updater.body)) {
1143
+ if (!core.types.isCallExpression(updater.body) || !core.types.isMemberExpression(updater.body.callee) || updater.body.callee.computed || !core.types.isIdentifier(updater.body.callee.property)) {
1144
+ return void 0;
1145
+ }
1146
+ const operation = collectionMutationOperation(kind, updater.body.callee.property.name);
1147
+ if (operation !== "set-add" && operation !== "map-set" || !collectionMutationArgumentsAreValid(operation, updater.body.arguments) || !isCollectionConstruction(updater.body.callee.object, kind, previous)) {
1148
+ return void 0;
1149
+ }
1150
+ const collection = uniqueLocalIdentifier("_farmCollection", [updater]);
1151
+ const body = core.types.blockStatement([
1152
+ core.types.variableDeclaration("const", [
1153
+ core.types.variableDeclarator(
1154
+ core.types.cloneNode(collection),
1155
+ core.types.cloneNode(updater.body.callee.object, true)
1156
+ )
1157
+ ]),
1158
+ core.types.returnStatement(
1159
+ core.types.callExpression(core.types.cloneNode(updateHelper), [
1160
+ core.types.cloneNode(previous),
1161
+ core.types.callExpression(core.types.cloneNode(mutationHelper), [
1162
+ core.types.cloneNode(collection),
1163
+ core.types.memberExpression(core.types.cloneNode(collection), core.types.cloneNode(updater.body.callee.property)),
1164
+ core.types.stringLiteral(operation),
1165
+ ...updater.body.arguments.map(
1166
+ (argument) => core.types.cloneNode(argument, true)
1167
+ )
1168
+ ]),
1169
+ core.types.stringLiteral(kind)
1170
+ ])
1171
+ )
1172
+ ]);
1173
+ return {
1174
+ updater: core.types.arrowFunctionExpression([core.types.cloneNode(previous)], body),
1175
+ mutations: 1
1176
+ };
1177
+ }
1178
+ const cloned = core.types.cloneNode(updater, true);
1179
+ const file = expressionFile(cloned);
1180
+ let functionPath;
1181
+ core.traverse(file, {
1182
+ Function(path) {
1183
+ if (functionPath) return;
1184
+ if (path.isArrowFunctionExpression() || path.isFunctionExpression()) {
1185
+ functionPath = path;
1186
+ }
1187
+ }
1188
+ });
1189
+ if (!functionPath || !core.types.isBlockStatement(functionPath.node.body)) return void 0;
1190
+ const clonedPrevious = functionPath.node.params[0];
1191
+ if (!core.types.isIdentifier(clonedPrevious)) return void 0;
1192
+ let collectionDeclaration;
1193
+ const updaterBody = functionPath.get("body");
1194
+ if (!updaterBody.isBlockStatement()) return void 0;
1195
+ for (const statement of updaterBody.get("body")) {
1196
+ if (!statement.isVariableDeclaration({ kind: "const" })) continue;
1197
+ for (const declaration of statement.get("declarations")) {
1198
+ if (declaration.isVariableDeclarator() && core.types.isIdentifier(declaration.node.id) && core.types.isExpression(declaration.node.init) && isCollectionConstruction(declaration.node.init, kind, clonedPrevious)) {
1199
+ if (collectionDeclaration) return void 0;
1200
+ collectionDeclaration = declaration;
1201
+ }
1202
+ }
1203
+ }
1204
+ if (!collectionDeclaration || !core.types.isIdentifier(collectionDeclaration.node.id)) return void 0;
1205
+ if (collectionDeclaration.scope.getBinding(collectionConstructorName(kind))) return void 0;
1206
+ const collectionName = collectionDeclaration.node.id.name;
1207
+ const collectionBinding = collectionDeclaration.scope.getBinding(collectionName);
1208
+ const previousBinding = functionPath.scope.getBinding(clonedPrevious.name);
1209
+ if (!collectionBinding || !previousBinding) return void 0;
1210
+ let valid = true;
1211
+ let mutations = 0;
1212
+ core.traverse(file, {
1213
+ ReferencedIdentifier(path) {
1214
+ if (!valid) return;
1215
+ const binding = path.scope.getBinding(path.node.name);
1216
+ const belongsToUpdater = path.getFunctionParent()?.node === functionPath?.node;
1217
+ if (binding === collectionBinding) {
1218
+ if (!belongsToUpdater) {
1219
+ valid = false;
1220
+ path.stop();
1221
+ return;
1222
+ }
1223
+ const parent = path.parentPath;
1224
+ if (parent.isReturnStatement() && parent.node.argument === path.node) return;
1225
+ if (!parent.isMemberExpression() || parent.node.object !== path.node || parent.node.computed || !core.types.isIdentifier(parent.node.property)) {
1226
+ valid = false;
1227
+ path.stop();
1228
+ return;
1229
+ }
1230
+ const method = parent.node.property.name;
1231
+ if (method === "size") return;
1232
+ const call = parent.parentPath;
1233
+ if (!call?.isCallExpression() || call.node.callee !== parent.node) {
1234
+ valid = false;
1235
+ path.stop();
1236
+ return;
1237
+ }
1238
+ const operation = collectionMutationOperation(kind, method);
1239
+ const read = kind === "set" ? method === "has" : method === "get" || method === "has";
1240
+ if (read) return;
1241
+ if (!collectionMutationArgumentsAreValid(operation, call.node.arguments) || call.parentPath.isMemberExpression() && call.parentPath.node.object === call.node) {
1242
+ valid = false;
1243
+ path.stop();
1244
+ return;
1245
+ }
1246
+ mutations += 1;
1247
+ return;
1248
+ }
1249
+ if (binding === previousBinding) {
1250
+ if (!belongsToUpdater) {
1251
+ valid = false;
1252
+ path.stop();
1253
+ return;
1254
+ }
1255
+ const parent = path.parentPath;
1256
+ if (parent.isReturnStatement() && parent.node.argument === path.node) return;
1257
+ if (parent.isNewExpression() && parent.node.arguments.length === 1 && parent.node.arguments[0] === path.node && core.types.isIdentifier(parent.node.callee, {
1258
+ name: collectionConstructorName(kind)
1259
+ })) {
1260
+ return;
1261
+ }
1262
+ if (parent.isMemberExpression() && parent.node.object === path.node && !parent.node.computed && core.types.isIdentifier(parent.node.property)) {
1263
+ const method = parent.node.property.name;
1264
+ const call = parent.parentPath;
1265
+ if (method === "size" || (kind === "set" ? method === "has" : method === "get" || method === "has") && call?.isCallExpression() && call.node.callee === parent.node) {
1266
+ return;
1267
+ }
1268
+ }
1269
+ valid = false;
1270
+ path.stop();
1271
+ }
1272
+ },
1273
+ ReturnStatement(path) {
1274
+ if (!valid || path.getFunctionParent()?.node !== functionPath?.node || !path.node.argument) {
1275
+ return;
1276
+ }
1277
+ const value = path.node.argument;
1278
+ const returnsOwnedCollection = core.types.isIdentifier(value) && (path.scope.getBinding(value.name) === collectionBinding || path.scope.getBinding(value.name) === previousBinding) || isCollectionConstruction(value, kind);
1279
+ if (!returnsOwnedCollection) {
1280
+ valid = false;
1281
+ path.stop();
1282
+ }
1283
+ }
1284
+ });
1285
+ if (!valid || mutations === 0) return void 0;
1286
+ core.traverse(file, {
1287
+ CallExpression(path) {
1288
+ if (path.getFunctionParent()?.node !== functionPath?.node) return;
1289
+ const callee = path.node.callee;
1290
+ if (!core.types.isMemberExpression(callee) || callee.computed || !core.types.isIdentifier(callee.object, { name: collectionName }) || path.scope.getBinding(collectionName) !== collectionBinding || !core.types.isIdentifier(callee.property)) {
1291
+ return;
1292
+ }
1293
+ const operation = collectionMutationOperation(kind, callee.property.name);
1294
+ if (!collectionMutationArgumentsAreValid(operation, path.node.arguments)) return;
1295
+ path.replaceWith(
1296
+ core.types.callExpression(core.types.cloneNode(mutationHelper), [
1297
+ core.types.cloneNode(callee.object),
1298
+ core.types.memberExpression(core.types.cloneNode(callee.object), core.types.cloneNode(callee.property)),
1299
+ core.types.stringLiteral(operation),
1300
+ ...path.node.arguments.map((argument) => core.types.cloneNode(argument, true))
1301
+ ])
1302
+ );
1303
+ path.skip();
1304
+ },
1305
+ ReturnStatement(path) {
1306
+ if (path.getFunctionParent()?.node !== functionPath?.node || !path.node.argument) return;
1307
+ path.node.argument = core.types.callExpression(core.types.cloneNode(updateHelper), [
1308
+ core.types.cloneNode(clonedPrevious),
1309
+ core.types.cloneNode(path.node.argument, true),
1310
+ core.types.stringLiteral(kind)
1311
+ ]);
1312
+ }
1313
+ });
1314
+ const prepared = file.program.body[0].expression;
1315
+ return core.types.isArrowFunctionExpression(prepared) || core.types.isFunctionExpression(prepared) ? { updater: prepared, mutations } : void 0;
1316
+ }
1317
+ function rewriteKeyedCollectionUpdateHints(root, targetKinds, states, statesBySetter, updateHelper, mutationHelper, globalCollections) {
1318
+ if (targetKinds.size === 0) return { root: core.types.cloneNode(root, true), count: 0 };
1319
+ const file = expressionFile(core.types.cloneNode(root, true));
1320
+ let count = 0;
1321
+ for (const [stateIndex, kind] of targetKinds) {
1322
+ const state = states[stateIndex];
1323
+ if (!state || !collectionStateInitializerIsOwned(
1324
+ state.initialValue,
1325
+ kind,
1326
+ globalCollections.has(collectionConstructorName(kind))
1327
+ ) || !collectionStateHasOnlyOwnedReads(root, state, kind)) {
1328
+ continue;
1329
+ }
1330
+ const replacements = /* @__PURE__ */ new Map();
1331
+ let valid = true;
1332
+ core.traverse(file, {
1333
+ CallExpression(path) {
1334
+ if (!valid) return;
1335
+ const callee = path.get("callee");
1336
+ if (!callee.isIdentifier({ name: state.setterName }) || callee.scope.hasBinding(callee.node.name) || statesBySetter.get(callee.node.name)?.index !== stateIndex) {
1337
+ return;
1338
+ }
1339
+ if (path.node.arguments.length !== 1 || !core.types.isExpression(path.node.arguments[0])) {
1340
+ valid = false;
1341
+ path.stop();
1342
+ return;
1343
+ }
1344
+ const update = path.node.arguments[0];
1345
+ if (path.scope.getBinding(collectionConstructorName(kind))) {
1346
+ valid = false;
1347
+ path.stop();
1348
+ return;
1349
+ }
1350
+ if (isCollectionConstruction(update, kind)) return;
1351
+ if (!core.types.isArrowFunctionExpression(update) && !core.types.isFunctionExpression(update)) {
1352
+ valid = false;
1353
+ path.stop();
1354
+ return;
1355
+ }
1356
+ const prepared = prepareKeyedCollectionUpdater(update, kind, updateHelper, mutationHelper);
1357
+ if (!prepared) {
1358
+ valid = false;
1359
+ path.stop();
1360
+ return;
1361
+ }
1362
+ replacements.set(path.node, prepared);
1363
+ }
1364
+ });
1365
+ if (!valid || replacements.size === 0) continue;
1366
+ core.traverse(file, {
1367
+ CallExpression(path) {
1368
+ const prepared = replacements.get(path.node);
1369
+ if (!prepared) return;
1370
+ path.node.arguments[0] = core.types.cloneNode(prepared.updater, true);
1371
+ count += prepared.mutations;
1372
+ path.skip();
1373
+ }
1374
+ });
1375
+ }
1376
+ return {
1377
+ root: file.program.body[0].expression,
1378
+ count
1379
+ };
1380
+ }
303
1381
  function rewriteStateAccess(expression, stateParameter, statesByValue, statesBySetter) {
304
1382
  const file = expressionFile(cloneExpression(expression));
305
1383
  core.traverse(file, {
@@ -550,21 +1628,28 @@ function analyzeMapList(expression, statesByValue, safeGlobals) {
550
1628
  const collection = callee.object;
551
1629
  const collectionUnsupported = validateCollectionPipeline(collection, safeGlobals);
552
1630
  if (collectionUnsupported) {
553
- return { reason: `keyed list collection cannot use ${collectionUnsupported}` };
1631
+ return {
1632
+ reason: `keyed list collection cannot use ${collectionUnsupported}`
1633
+ };
554
1634
  }
555
1635
  if (expression.arguments.length !== 1 || !core.types.isArrowFunctionExpression(expression.arguments[0]) && !core.types.isFunctionExpression(expression.arguments[0])) {
556
1636
  return { reason: "keyed list map must use one inline render callback" };
557
1637
  }
558
1638
  const callback = expression.arguments[0];
559
1639
  if (containsDirectHookCall(callback)) {
560
- return { reason: "Hooks cannot be called directly inside a keyed list callback" };
1640
+ return {
1641
+ reason: "Hooks cannot be called directly inside a keyed list callback"
1642
+ };
561
1643
  }
562
1644
  const row = returnedExpression(callback);
563
1645
  if (!row || !core.types.isJSXElement(row)) {
564
1646
  return { reason: "keyed list map callbacks must return one React element" };
565
1647
  }
566
1648
  const key = jsxKeyExpression(row);
567
- if (!key) return { reason: "automatically compiled lists require an explicit item key" };
1649
+ if (!key)
1650
+ return {
1651
+ reason: "automatically compiled lists require an explicit item key"
1652
+ };
568
1653
  const keyReason = validateKeyFunction(callback, key, safeGlobals);
569
1654
  if (keyReason) return { reason: keyReason };
570
1655
  return { dependencies: collectStateDependencies(expression, statesByValue) };
@@ -579,7 +1664,9 @@ function listAttributeExpression(element, name) {
579
1664
  }
580
1665
  function analyzePublicList(element, statesByValue, safeGlobals) {
581
1666
  if (element.openingElement.attributes.some((attribute) => core.types.isJSXSpreadAttribute(attribute))) {
582
- return { reason: "compiled List boundaries do not support JSX attribute spreads" };
1667
+ return {
1668
+ reason: "compiled List boundaries do not support JSX attribute spreads"
1669
+ };
583
1670
  }
584
1671
  const each = listAttributeExpression(element, "each");
585
1672
  const by = listAttributeExpression(element, "by");
@@ -597,7 +1684,9 @@ function analyzePublicList(element, statesByValue, safeGlobals) {
597
1684
  if (keyReason) return { reason: keyReason };
598
1685
  const children = meaningfulJsxChildren(element);
599
1686
  if (children.length !== 1 || !core.types.isJSXExpressionContainer(children[0]) || core.types.isJSXEmptyExpression(children[0].expression) || !core.types.isArrowFunctionExpression(children[0].expression) && !core.types.isFunctionExpression(children[0].expression)) {
600
- return { reason: "compiled List children must use one inline render function" };
1687
+ return {
1688
+ reason: "compiled List children must use one inline render function"
1689
+ };
601
1690
  }
602
1691
  const render = children[0].expression;
603
1692
  if (!returnedExpression(render)) {
@@ -711,13 +1800,20 @@ function stabilizeDeferredEventCurrentTargets(root, compilerSetters) {
711
1800
  const expression = file.program.body[0].expression;
712
1801
  return core.types.isJSXElement(expression) ? expression : core.types.cloneNode(root, true);
713
1802
  }
714
- function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters) {
1803
+ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters, keyedTargetReactiveByValue) {
715
1804
  if (renderCallback.async || renderCallback.generator || renderCallback.params.length < 1 || renderCallback.params.length > 2 || !core.types.isIdentifier(renderCallback.params[0]) || renderCallback.params[1] !== void 0 && !core.types.isIdentifier(renderCallback.params[1])) {
716
- return { reason: "compiled keyed rows require item and optional index identifiers" };
1805
+ return {
1806
+ reason: "compiled keyed rows require item and optional index identifiers"
1807
+ };
717
1808
  }
718
1809
  const bindings = [];
719
1810
  const events = [];
720
1811
  const conditionals = [];
1812
+ const validateBindingExpression = (expression) => validateDerivedExpression(
1813
+ expression,
1814
+ safeGlobals,
1815
+ keyedTargetReactiveByValue ? (call) => isPotentialKeyedMembershipCall(call, keyedTargetReactiveByValue) || isPotentialKeyedMapLookupCall(call, keyedTargetReactiveByValue) : void 0
1816
+ );
721
1817
  const visit = (element, path) => {
722
1818
  const tag = element.openingElement.name;
723
1819
  if (!core.types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
@@ -796,7 +1892,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
796
1892
  if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
797
1893
  return "compiled keyed row style names must use camelCase or a CSS custom property";
798
1894
  }
799
- const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
1895
+ const unsupported2 = validateBindingExpression(property.value);
800
1896
  if (unsupported2) {
801
1897
  return `compiled keyed row style ${propertyName} cannot use ${unsupported2}`;
802
1898
  }
@@ -810,7 +1906,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
810
1906
  continue;
811
1907
  }
812
1908
  if (name === "children") return "compiled keyed row children props are not supported yet";
813
- const unsupported = validateDerivedExpression(expression, safeGlobals);
1909
+ const unsupported = validateBindingExpression(expression);
814
1910
  if (unsupported) return `compiled keyed row ${name} cannot use ${unsupported}`;
815
1911
  bindings.push({
816
1912
  kind: "attribute",
@@ -860,7 +1956,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
860
1956
  (child) => core.types.isJSXExpressionContainer(child) && !core.types.isJSXEmptyExpression(child.expression)
861
1957
  );
862
1958
  for (const child of expressions) {
863
- const unsupported = validateDerivedExpression(child.expression, safeGlobals);
1959
+ const unsupported = validateBindingExpression(child.expression);
864
1960
  if (unsupported) return `compiled keyed row text cannot use ${unsupported}`;
865
1961
  }
866
1962
  if (nestedElements.length > 0 && expressions.length > 0) {
@@ -876,7 +1972,11 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
876
1972
  parts.push(cloneExpression(child.expression));
877
1973
  }
878
1974
  }
879
- bindings.push({ kind: "text", path: [...path], value: core.types.arrayExpression(parts) });
1975
+ bindings.push({
1976
+ kind: "text",
1977
+ path: [...path],
1978
+ value: core.types.arrayExpression(parts)
1979
+ });
880
1980
  }
881
1981
  let elementIndex = 0;
882
1982
  for (const child of element.children) {
@@ -890,7 +1990,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
890
1990
  const reason = visit(row, []);
891
1991
  return reason ? { reason } : { bindings, events, conditionals };
892
1992
  }
893
- function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false) {
1993
+ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false, allowMembershipTargets = false) {
894
1994
  let collection;
895
1995
  let keyCallback;
896
1996
  let renderCallback;
@@ -943,7 +2043,8 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
943
2043
  row,
944
2044
  renderCallback,
945
2045
  safeGlobals,
946
- new Set([...statesByValue.values()].map((state) => state.setterName))
2046
+ new Set([...statesByValue.values()].map((state) => state.setterName)),
2047
+ allowMembershipTargets ? statesByValue : void 0
947
2048
  );
948
2049
  if (rowAnalysis.reason && !acceptUnsupportedRow) return void 0;
949
2050
  const keyExpression = returnedExpression(keyCallback);
@@ -956,6 +2057,7 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
956
2057
  return {
957
2058
  source,
958
2059
  collection,
2060
+ collectionDependency: core.types.isIdentifier(collection) ? statesByValue.get(collection.name)?.index : void 0,
959
2061
  structureDependencies: [...structureDependencies].sort((left, right) => left - right),
960
2062
  keyCallback,
961
2063
  renderCallback,
@@ -984,7 +2086,74 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
984
2086
  }
985
2087
  const children = meaningfulJsxChildren(container);
986
2088
  if (children.length !== 1) return void 0;
987
- return analyzeKeyedRowChild(children[0], statesByValue, safeGlobals, listNames, true);
2089
+ const shape = analyzeKeyedRowChild(
2090
+ children[0],
2091
+ statesByValue,
2092
+ safeGlobals,
2093
+ listNames,
2094
+ true,
2095
+ true
2096
+ );
2097
+ if (!shape || shape.rowReason) return shape;
2098
+ const needsMembershipProof = (binding) => containsPotentialKeyedMembershipCall(binding.value, statesByValue);
2099
+ const needsMapLookupProof = (binding) => containsPotentialKeyedMapLookupCall(binding.value, statesByValue);
2100
+ if (shape.conditionals.length > 0 || shape.events.length > 0 && !canDelegateKeyedRowEvents({
2101
+ conditionals: shape.conditionals,
2102
+ events: shape.events,
2103
+ source: container
2104
+ })) {
2105
+ return shape.bindings.some(
2106
+ (binding) => needsMembershipProof(binding) || needsMapLookupProof(binding)
2107
+ ) ? {
2108
+ ...shape,
2109
+ rowReason: "keyed collection targets require compiler-owned rows without React-owned branches or events"
2110
+ } : shape;
2111
+ }
2112
+ const structureDependencies = new Set(shape.structureDependencies);
2113
+ const bindings = shape.bindings.map((binding) => ({
2114
+ ...binding,
2115
+ identityTarget: keyedIdentityTarget(
2116
+ binding,
2117
+ shape.keyCallback,
2118
+ shape.renderCallback,
2119
+ statesByValue,
2120
+ structureDependencies
2121
+ ),
2122
+ membershipTarget: keyedMembershipTarget(
2123
+ binding,
2124
+ shape.keyCallback,
2125
+ shape.renderCallback,
2126
+ statesByValue,
2127
+ structureDependencies
2128
+ ),
2129
+ mapLookupTarget: keyedMapLookupTarget(
2130
+ binding,
2131
+ shape.keyCallback,
2132
+ shape.renderCallback,
2133
+ statesByValue,
2134
+ structureDependencies
2135
+ )
2136
+ }));
2137
+ if (bindings.some(
2138
+ (binding, index) => needsMembershipProof(shape.bindings[index]) && !binding.membershipTarget
2139
+ )) {
2140
+ return {
2141
+ ...shape,
2142
+ rowReason: "keyed membership targets must call one local Set state with the exact row key"
2143
+ };
2144
+ }
2145
+ if (bindings.some(
2146
+ (binding, index) => needsMapLookupProof(shape.bindings[index]) && !binding.mapLookupTarget
2147
+ )) {
2148
+ return {
2149
+ ...shape,
2150
+ rowReason: "keyed Map lookup targets must call one local Map state with the exact row key"
2151
+ };
2152
+ }
2153
+ return {
2154
+ ...shape,
2155
+ bindings
2156
+ };
988
2157
  }
989
2158
  function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
990
2159
  if (container.openingElement.attributes.some(
@@ -1140,7 +2309,11 @@ function analyzeHostConditionalTree(branch, safeGlobals, allowStaticEvents = fal
1140
2309
  parts.push(cloneExpression(child.expression));
1141
2310
  }
1142
2311
  }
1143
- bindings.push({ kind: "text", path: [...path], value: core.types.arrayExpression(parts) });
2312
+ bindings.push({
2313
+ kind: "text",
2314
+ path: [...path],
2315
+ value: core.types.arrayExpression(parts)
2316
+ });
1144
2317
  }
1145
2318
  let elementIndex = 0;
1146
2319
  for (const child of element.children) {
@@ -1312,7 +2485,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1312
2485
  listNames,
1313
2486
  id,
1314
2487
  allocateId
1315
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2488
+ ) : {
2489
+ bindings: [],
2490
+ plans: [],
2491
+ blocks: /* @__PURE__ */ new Map()
2492
+ };
1316
2493
  const falsy = shape.falsy ? analyzeRecursiveHostTree(
1317
2494
  shape.falsy,
1318
2495
  statesByValue,
@@ -1320,7 +2497,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1320
2497
  listNames,
1321
2498
  id,
1322
2499
  allocateId
1323
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2500
+ ) : {
2501
+ bindings: [],
2502
+ plans: [],
2503
+ blocks: /* @__PURE__ */ new Map()
2504
+ };
1324
2505
  if (truthy.reason) return truthy.reason;
1325
2506
  if (falsy.reason) return falsy.reason;
1326
2507
  plans.push(...truthy.plans, ...falsy.plans);
@@ -1478,7 +2659,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1478
2659
  parts.push(cloneExpression(child.expression));
1479
2660
  }
1480
2661
  }
1481
- bindings.push({ kind: "text", path: [...path], value: core.types.arrayExpression(parts) });
2662
+ bindings.push({
2663
+ kind: "text",
2664
+ path: [...path],
2665
+ value: core.types.arrayExpression(parts)
2666
+ });
1482
2667
  }
1483
2668
  let elementIndex = 0;
1484
2669
  for (const child of element.children) {
@@ -1545,7 +2730,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1545
2730
  listNames,
1546
2731
  parent,
1547
2732
  allocateId
1548
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2733
+ ) : {
2734
+ bindings: [],
2735
+ plans: [],
2736
+ blocks: /* @__PURE__ */ new Map()
2737
+ };
1549
2738
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1550
2739
  shape.falsy,
1551
2740
  statesByValue,
@@ -1553,7 +2742,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1553
2742
  listNames,
1554
2743
  parent,
1555
2744
  allocateId
1556
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2745
+ ) : {
2746
+ bindings: [],
2747
+ plans: [],
2748
+ blocks: /* @__PURE__ */ new Map()
2749
+ };
1557
2750
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1558
2751
  const dependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1559
2752
  for (const binding of [...truthyAnalysis.bindings || [], ...falsyAnalysis.bindings || []]) {
@@ -1599,7 +2792,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1599
2792
  listNames,
1600
2793
  parent,
1601
2794
  allocateId
1602
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2795
+ ) : {
2796
+ bindings: [],
2797
+ plans: [],
2798
+ blocks: /* @__PURE__ */ new Map()
2799
+ };
1603
2800
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1604
2801
  shape.falsy,
1605
2802
  statesByValue,
@@ -1607,7 +2804,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1607
2804
  listNames,
1608
2805
  parent,
1609
2806
  allocateId
1610
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2807
+ ) : {
2808
+ bindings: [],
2809
+ plans: [],
2810
+ blocks: /* @__PURE__ */ new Map()
2811
+ };
1611
2812
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1612
2813
  nestedPlans.push(...truthyAnalysis.plans, ...falsyAnalysis.plans);
1613
2814
  for (const [element, plan] of truthyAnalysis.blocks) descriptorBlocks.set(element, plan);
@@ -1700,7 +2901,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1700
2901
  listNames,
1701
2902
  parent,
1702
2903
  allocateId
1703
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2904
+ ) : {
2905
+ bindings: [],
2906
+ plans: [],
2907
+ blocks: /* @__PURE__ */ new Map()
2908
+ };
1704
2909
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1705
2910
  shape.falsy,
1706
2911
  statesByValue,
@@ -1708,7 +2913,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1708
2913
  listNames,
1709
2914
  parent,
1710
2915
  allocateId
1711
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2916
+ ) : {
2917
+ bindings: [],
2918
+ plans: [],
2919
+ blocks: /* @__PURE__ */ new Map()
2920
+ };
1712
2921
  if (!mergeAnalysis(truthyAnalysis) || !mergeAnalysis(falsyAnalysis)) return void 0;
1713
2922
  for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1714
2923
  dependencies.add(dependency);
@@ -1949,6 +3158,57 @@ function keyedRowBindingObject(binding, parameters) {
1949
3158
  if (binding.name) {
1950
3159
  properties.push(core.types.objectProperty(core.types.identifier("name"), core.types.stringLiteral(binding.name)));
1951
3160
  }
3161
+ if (binding.identityTarget) {
3162
+ properties.push(
3163
+ core.types.objectProperty(
3164
+ core.types.identifier("identityTarget"),
3165
+ core.types.objectExpression([
3166
+ core.types.objectProperty(
3167
+ core.types.identifier("dependency"),
3168
+ core.types.numericLiteral(binding.identityTarget.dependency)
3169
+ ),
3170
+ core.types.objectProperty(
3171
+ core.types.identifier("read"),
3172
+ core.types.arrowFunctionExpression([], cloneExpression(binding.identityTarget.value))
3173
+ )
3174
+ ])
3175
+ )
3176
+ );
3177
+ }
3178
+ if (binding.membershipTarget) {
3179
+ properties.push(
3180
+ core.types.objectProperty(
3181
+ core.types.identifier("membershipTarget"),
3182
+ core.types.objectExpression([
3183
+ core.types.objectProperty(
3184
+ core.types.identifier("dependency"),
3185
+ core.types.numericLiteral(binding.membershipTarget.dependency)
3186
+ ),
3187
+ core.types.objectProperty(
3188
+ core.types.identifier("read"),
3189
+ core.types.arrowFunctionExpression([], cloneExpression(binding.membershipTarget.value))
3190
+ )
3191
+ ])
3192
+ )
3193
+ );
3194
+ }
3195
+ if (binding.mapLookupTarget) {
3196
+ properties.push(
3197
+ core.types.objectProperty(
3198
+ core.types.identifier("mapLookupTarget"),
3199
+ core.types.objectExpression([
3200
+ core.types.objectProperty(
3201
+ core.types.identifier("dependency"),
3202
+ core.types.numericLiteral(binding.mapLookupTarget.dependency)
3203
+ ),
3204
+ core.types.objectProperty(
3205
+ core.types.identifier("read"),
3206
+ core.types.arrowFunctionExpression([], cloneExpression(binding.mapLookupTarget.value))
3207
+ )
3208
+ ])
3209
+ )
3210
+ );
3211
+ }
1952
3212
  properties.push(
1953
3213
  core.types.objectProperty(
1954
3214
  core.types.identifier("read"),
@@ -2143,7 +3403,7 @@ function keyedRowConditionalObject(conditional, parameters) {
2143
3403
  }
2144
3404
  return core.types.objectExpression(properties);
2145
3405
  }
2146
- function keyedRowsBoundary(blockRuntime, plan) {
3406
+ function keyedRowsBoundary(blockRuntime, plan, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
2147
3407
  const name = core.types.jsxMemberExpression(
2148
3408
  core.types.jsxIdentifier(blockRuntime.name),
2149
3409
  core.types.jsxIdentifier("KeyedRows")
@@ -2177,6 +3437,16 @@ function keyedRowsBoundary(blockRuntime, plan) {
2177
3437
  core.types.jsxIdentifier("items"),
2178
3438
  core.types.jsxExpressionContainer(core.types.arrowFunctionExpression([], cloneExpression(plan.collection)))
2179
3439
  ),
3440
+ ...keyedMapUpdateHints ? [
3441
+ core.types.jsxAttribute(
3442
+ core.types.jsxIdentifier("dependencies"),
3443
+ core.types.jsxExpressionContainer(
3444
+ core.types.arrayExpression(
3445
+ plan.dependencies.map((dependency) => core.types.numericLiteral(dependency))
3446
+ )
3447
+ )
3448
+ )
3449
+ ] : [],
2180
3450
  core.types.jsxAttribute(
2181
3451
  core.types.jsxIdentifier("structureDependencies"),
2182
3452
  core.types.jsxExpressionContainer(
@@ -2185,6 +3455,36 @@ function keyedRowsBoundary(blockRuntime, plan) {
2185
3455
  )
2186
3456
  )
2187
3457
  ),
3458
+ ...!keyedMapUpdateHints || plan.collectionDependency === void 0 ? [] : [
3459
+ core.types.jsxAttribute(
3460
+ core.types.jsxIdentifier("collectionDependency"),
3461
+ core.types.jsxExpressionContainer(core.types.numericLiteral(plan.collectionDependency))
3462
+ )
3463
+ ],
3464
+ ...plan.collectionDependency !== void 0 && keyedArrayFilterHintedStateIndices.has(plan.collectionDependency) ? [
3465
+ core.types.jsxAttribute(
3466
+ core.types.jsxIdentifier("filterIndexIndependent"),
3467
+ core.types.jsxExpressionContainer(core.types.booleanLiteral(true))
3468
+ )
3469
+ ] : [],
3470
+ ...plan.collectionDependency !== void 0 && keyedArrayPrependHintedStateIndices.has(plan.collectionDependency) ? [
3471
+ core.types.jsxAttribute(
3472
+ core.types.jsxIdentifier("prependIndexIndependent"),
3473
+ core.types.jsxExpressionContainer(core.types.booleanLiteral(true))
3474
+ )
3475
+ ] : [],
3476
+ ...plan.collectionDependency !== void 0 && keyedArrayPositionHintedStateIndices.has(plan.collectionDependency) ? [
3477
+ core.types.jsxAttribute(
3478
+ core.types.jsxIdentifier("positionIndexIndependent"),
3479
+ core.types.jsxExpressionContainer(core.types.booleanLiteral(true))
3480
+ )
3481
+ ] : [],
3482
+ ...plan.collectionDependency !== void 0 && keyedArrayReorderHintedStateIndices.has(plan.collectionDependency) ? [
3483
+ core.types.jsxAttribute(
3484
+ core.types.jsxIdentifier("reorderIndexIndependent"),
3485
+ core.types.jsxExpressionContainer(core.types.booleanLiteral(true))
3486
+ )
3487
+ ] : [],
2188
3488
  core.types.jsxAttribute(
2189
3489
  core.types.jsxIdentifier("rowKey"),
2190
3490
  core.types.jsxExpressionContainer(core.types.cloneNode(plan.keyCallback, true))
@@ -2511,7 +3811,9 @@ function conditionalRangesBoundary(blockRuntime, plan) {
2511
3811
  function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allowedComponentNames) {
2512
3812
  const name = element.openingElement.name;
2513
3813
  if (!core.types.isJSXIdentifier(name) || !isComponentName(name.name)) {
2514
- return { reason: "component islands require a direct component identifier" };
3814
+ return {
3815
+ reason: "component islands require a direct component identifier"
3816
+ };
2515
3817
  }
2516
3818
  if (!allowedComponentNames.has(name.name)) {
2517
3819
  return {
@@ -2519,19 +3821,27 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2519
3821
  };
2520
3822
  }
2521
3823
  if (meaningfulJsxChildren(element).length > 0) {
2522
- return { reason: `component island ${name.name} does not support children yet` };
3824
+ return {
3825
+ reason: `component island ${name.name} does not support children yet`
3826
+ };
2523
3827
  }
2524
3828
  const dependencies = /* @__PURE__ */ new Set();
2525
3829
  for (const attribute of element.openingElement.attributes) {
2526
3830
  if (core.types.isJSXSpreadAttribute(attribute)) {
2527
- return { reason: `component island ${name.name} does not support JSX attribute spreads` };
3831
+ return {
3832
+ reason: `component island ${name.name} does not support JSX attribute spreads`
3833
+ };
2528
3834
  }
2529
3835
  const attributeName = jsxAttributeName(attribute);
2530
3836
  if (!attributeName) {
2531
- return { reason: `component island ${name.name} does not support namespaced JSX attributes` };
3837
+ return {
3838
+ reason: `component island ${name.name} does not support namespaced JSX attributes`
3839
+ };
2532
3840
  }
2533
3841
  if (attributeName === "ref" || attributeName === "key" || attributeName === "children") {
2534
- return { reason: `component island ${name.name} does not support ${attributeName} yet` };
3842
+ return {
3843
+ reason: `component island ${name.name} does not support ${attributeName} yet`
3844
+ };
2535
3845
  }
2536
3846
  if (!core.types.isJSXExpressionContainer(attribute.value) || core.types.isJSXEmptyExpression(attribute.value.expression)) {
2537
3847
  continue;
@@ -2550,7 +3860,9 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2550
3860
  (dependency) => dependencies.add(dependency)
2551
3861
  );
2552
3862
  }
2553
- return { dependencies: [...dependencies].sort((left, right) => left - right) };
3863
+ return {
3864
+ dependencies: [...dependencies].sort((left, right) => left - right)
3865
+ };
2554
3866
  }
2555
3867
  function componentIslandBoundary(blockRuntime, plan, source) {
2556
3868
  const name = core.types.jsxMemberExpression(
@@ -2807,6 +4119,7 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
2807
4119
  dependencies: keyedRows.dependencies,
2808
4120
  source: child,
2809
4121
  collection: keyedRows.collection,
4122
+ collectionDependency: keyedRows.collectionDependency,
2810
4123
  structureDependencies: keyedRows.structureDependencies,
2811
4124
  keyCallback: keyedRows.keyCallback,
2812
4125
  renderCallback: keyedRows.renderCallback,
@@ -3048,7 +4361,7 @@ function conditionalBlockBoundary(blockRuntime, plan, source) {
3048
4361
  true
3049
4362
  );
3050
4363
  }
3051
- function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
4364
+ function lowerComposableBlocks(root, plans, blockRuntime, listNames, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
3052
4365
  const planBySource = new Map(
3053
4366
  plans.map((plan) => [plan.source, plan])
3054
4367
  );
@@ -3073,7 +4386,15 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
3073
4386
  return mixedRangesBoundary(blockRuntime, plan2);
3074
4387
  }
3075
4388
  if (plan2?.kind === "keyed-rows") {
3076
- return keyedRowsBoundary(blockRuntime, plan2);
4389
+ return keyedRowsBoundary(
4390
+ blockRuntime,
4391
+ plan2,
4392
+ keyedMapUpdateHints,
4393
+ keyedArrayFilterHintedStateIndices,
4394
+ keyedArrayPrependHintedStateIndices,
4395
+ keyedArrayPositionHintedStateIndices,
4396
+ keyedArrayReorderHintedStateIndices
4397
+ );
3077
4398
  }
3078
4399
  if (plan2?.kind === "keyed-ranges") {
3079
4400
  return keyedRangesBoundary(blockRuntime, plan2);
@@ -3436,7 +4757,7 @@ function wrapperElement(definitionIdentifier, props) {
3436
4757
  const attributes = props ? [core.types.jsxSpreadAttribute(core.types.cloneNode(props))] : [];
3437
4758
  return core.types.jsxElement(core.types.jsxOpeningElement(name, attributes, true), null, [], true);
3438
4759
  }
3439
- function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureIdentifiers, usedRuntimeFeatures, useStateNames, reactNames, listNames, moduleId, reactivity) {
4760
+ function compileCandidate(candidate, createComponentIdentifier, keyedMapUpdateIdentifier, keyedArrayAppendIdentifier, keyedArrayFilterIdentifier, keyedArrayPrependIdentifier, keyedArrayPositionIdentifier, keyedArrayReorderIdentifier, keyedArrayRollingWindowIdentifier, keyedArraySliceIdentifier, keyedCollectionUpdateIdentifier, keyedCollectionMutationIdentifier, runtimeFeatureIdentifiers, usedRuntimeFeatures, optimizationCounts, useStateNames, reactNames, listNames, moduleId, reactivity) {
3440
4761
  const { path, name, statementPath } = candidate;
3441
4762
  if (path.node.async || path.node.generator)
3442
4763
  return "async and generator components are not supported";
@@ -3448,6 +4769,7 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3448
4769
  const safeGlobals = new Set(
3449
4770
  [...SAFE_GLOBAL_CALLS, "Math"].filter((name2) => !path.scope.getBinding(name2))
3450
4771
  );
4772
+ const globalCollections = new Set(["Set", "Map"].filter((name2) => !path.scope.getBinding(name2)));
3451
4773
  const states = [];
3452
4774
  const locals = [];
3453
4775
  let returned;
@@ -3495,9 +4817,17 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3495
4817
  if (declaration.init.async || declaration.init.generator) {
3496
4818
  return `event handler ${declaration.id.name} must be synchronous`;
3497
4819
  }
3498
- locals.push({ kind: "handler", name: declaration.id.name, value: declaration.init });
4820
+ locals.push({
4821
+ kind: "handler",
4822
+ name: declaration.id.name,
4823
+ value: declaration.init
4824
+ });
3499
4825
  } else {
3500
- locals.push({ kind: "derived", name: declaration.id.name, value: declaration.init });
4826
+ locals.push({
4827
+ kind: "derived",
4828
+ name: declaration.id.name,
4829
+ value: declaration.init
4830
+ });
3501
4831
  }
3502
4832
  }
3503
4833
  if (states.length === 0) return "no local useState binding was found";
@@ -3636,8 +4966,378 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3636
4966
  }
3637
4967
  if (blockAnalysis.reason) return blockAnalysis.reason;
3638
4968
  if (analysis.reason) return analysis.reason;
4969
+ const hintedStateIndices = new Set(
4970
+ (blockAnalysis.plans || []).filter(
4971
+ (plan) => plan.kind === "keyed-rows" && plan.collectionDependency !== void 0
4972
+ ).map((plan) => plan.collectionDependency)
4973
+ );
4974
+ const hintedRoot = rewriteKeyedMapUpdateHints(
4975
+ expandedReactiveRoot,
4976
+ hintedStateIndices,
4977
+ statesBySetter,
4978
+ keyedMapUpdateIdentifier,
4979
+ safeGlobals
4980
+ );
4981
+ let appliedKeyedMapUpdateHints = 0;
4982
+ if (hintedRoot.count > 0) {
4983
+ const hintedBlockAnalysis = analyzeComposableBlocks(
4984
+ hintedRoot.root,
4985
+ reactiveByValue,
4986
+ safeGlobals,
4987
+ listNames,
4988
+ allowedComponentNames
4989
+ );
4990
+ const hintedAnalysis = analyzeHostTree(
4991
+ hintedRoot.root,
4992
+ reactiveByValue,
4993
+ safeGlobals,
4994
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
4995
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
4996
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
4997
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
4998
+ );
4999
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5000
+ expandedReactiveRoot = hintedRoot.root;
5001
+ blockAnalysis = hintedBlockAnalysis;
5002
+ analysis = hintedAnalysis;
5003
+ appliedKeyedMapUpdateHints = hintedRoot.count;
5004
+ optimizationCounts.keyedMapUpdateHints += hintedRoot.count;
5005
+ }
5006
+ }
5007
+ const appendHintedStateIndices = new Set(hintedStateIndices);
5008
+ for (const plan of blockAnalysis.plans || []) {
5009
+ if (plan.kind !== "keyed-rows" || plan.collectionDependency === void 0) continue;
5010
+ const keyExpression = returnedExpression(plan.keyCallback);
5011
+ if (!keyExpression || collectStateDependencies(keyExpression, reactiveByValue).includes(plan.collectionDependency)) {
5012
+ appendHintedStateIndices.delete(plan.collectionDependency);
5013
+ }
5014
+ }
5015
+ const appendHintedRoot = rewriteKeyedArrayAppendHints(
5016
+ expandedReactiveRoot,
5017
+ appendHintedStateIndices,
5018
+ statesBySetter,
5019
+ keyedArrayAppendIdentifier,
5020
+ safeGlobals
5021
+ );
5022
+ let appliedKeyedArrayAppendHints = 0;
5023
+ if (appendHintedRoot.count > 0) {
5024
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5025
+ appendHintedRoot.root,
5026
+ reactiveByValue,
5027
+ safeGlobals,
5028
+ listNames,
5029
+ allowedComponentNames
5030
+ );
5031
+ const hintedAnalysis = analyzeHostTree(
5032
+ appendHintedRoot.root,
5033
+ reactiveByValue,
5034
+ safeGlobals,
5035
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5036
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5037
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5038
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5039
+ );
5040
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5041
+ expandedReactiveRoot = appendHintedRoot.root;
5042
+ blockAnalysis = hintedBlockAnalysis;
5043
+ analysis = hintedAnalysis;
5044
+ appliedKeyedArrayAppendHints = appendHintedRoot.count;
5045
+ optimizationCounts.keyedArrayAppendHints += appendHintedRoot.count;
5046
+ }
5047
+ }
5048
+ const shiftedIndexIndependentStateIndices = new Set(hintedStateIndices);
5049
+ for (const plan of blockAnalysis.plans || []) {
5050
+ if (plan.kind !== "keyed-rows" || plan.collectionDependency === void 0) continue;
5051
+ const keyExpression = returnedExpression(plan.keyCallback);
5052
+ if (plan.renderCallback.params.length > 1 || !keyExpression || collectStateDependencies(keyExpression, reactiveByValue).includes(plan.collectionDependency)) {
5053
+ shiftedIndexIndependentStateIndices.delete(plan.collectionDependency);
5054
+ }
5055
+ }
5056
+ const prependHintedRoot = rewriteKeyedArrayPrependHints(
5057
+ expandedReactiveRoot,
5058
+ shiftedIndexIndependentStateIndices,
5059
+ statesBySetter,
5060
+ keyedArrayPrependIdentifier,
5061
+ safeGlobals
5062
+ );
5063
+ let appliedKeyedArrayPrependHints = 0;
5064
+ let appliedPrependHintedStateIndices = /* @__PURE__ */ new Set();
5065
+ if (prependHintedRoot.count > 0) {
5066
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5067
+ prependHintedRoot.root,
5068
+ reactiveByValue,
5069
+ safeGlobals,
5070
+ listNames,
5071
+ allowedComponentNames
5072
+ );
5073
+ const hintedAnalysis = analyzeHostTree(
5074
+ prependHintedRoot.root,
5075
+ reactiveByValue,
5076
+ safeGlobals,
5077
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5078
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5079
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5080
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5081
+ );
5082
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5083
+ expandedReactiveRoot = prependHintedRoot.root;
5084
+ blockAnalysis = hintedBlockAnalysis;
5085
+ analysis = hintedAnalysis;
5086
+ appliedKeyedArrayPrependHints = prependHintedRoot.count;
5087
+ appliedPrependHintedStateIndices = prependHintedRoot.stateIndices;
5088
+ optimizationCounts.keyedArrayPrependHints += prependHintedRoot.count;
5089
+ }
5090
+ }
5091
+ const rollingWindowHintedRoot = rewriteKeyedArrayRollingWindowHints(
5092
+ expandedReactiveRoot,
5093
+ shiftedIndexIndependentStateIndices,
5094
+ statesBySetter,
5095
+ keyedArraySliceIdentifier,
5096
+ keyedArrayRollingWindowIdentifier,
5097
+ safeGlobals
5098
+ );
5099
+ let appliedKeyedArrayRollingWindowHints = 0;
5100
+ let appliedRollingWindowHintedStateIndices = /* @__PURE__ */ new Set();
5101
+ if (rollingWindowHintedRoot.count > 0) {
5102
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5103
+ rollingWindowHintedRoot.root,
5104
+ reactiveByValue,
5105
+ safeGlobals,
5106
+ listNames,
5107
+ allowedComponentNames
5108
+ );
5109
+ const hintedAnalysis = analyzeHostTree(
5110
+ rollingWindowHintedRoot.root,
5111
+ reactiveByValue,
5112
+ safeGlobals,
5113
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5114
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5115
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5116
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5117
+ );
5118
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5119
+ expandedReactiveRoot = rollingWindowHintedRoot.root;
5120
+ blockAnalysis = hintedBlockAnalysis;
5121
+ analysis = hintedAnalysis;
5122
+ appliedKeyedArrayRollingWindowHints = rollingWindowHintedRoot.count;
5123
+ appliedRollingWindowHintedStateIndices = rollingWindowHintedRoot.stateIndices;
5124
+ optimizationCounts.keyedArrayRollingWindowHints += rollingWindowHintedRoot.count;
5125
+ }
5126
+ }
5127
+ const positionHintedRoot = rewriteKeyedArrayPositionHints(
5128
+ expandedReactiveRoot,
5129
+ shiftedIndexIndependentStateIndices,
5130
+ statesBySetter,
5131
+ keyedArrayPositionIdentifier,
5132
+ safeGlobals
5133
+ );
5134
+ let appliedKeyedArrayPositionHints = 0;
5135
+ let appliedPositionHintedStateIndices = /* @__PURE__ */ new Set();
5136
+ if (positionHintedRoot.count > 0) {
5137
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5138
+ positionHintedRoot.root,
5139
+ reactiveByValue,
5140
+ safeGlobals,
5141
+ listNames,
5142
+ allowedComponentNames
5143
+ );
5144
+ const hintedAnalysis = analyzeHostTree(
5145
+ positionHintedRoot.root,
5146
+ reactiveByValue,
5147
+ safeGlobals,
5148
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5149
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5150
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5151
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5152
+ );
5153
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5154
+ expandedReactiveRoot = positionHintedRoot.root;
5155
+ blockAnalysis = hintedBlockAnalysis;
5156
+ analysis = hintedAnalysis;
5157
+ appliedKeyedArrayPositionHints = positionHintedRoot.count;
5158
+ appliedPositionHintedStateIndices = positionHintedRoot.stateIndices;
5159
+ optimizationCounts.keyedArrayPositionHints += positionHintedRoot.count;
5160
+ }
5161
+ }
5162
+ const reorderHintedRoot = rewriteKeyedArrayReorderHints(
5163
+ expandedReactiveRoot,
5164
+ shiftedIndexIndependentStateIndices,
5165
+ statesBySetter,
5166
+ keyedArrayReorderIdentifier
5167
+ );
5168
+ let appliedKeyedArrayReorderHints = 0;
5169
+ let appliedReorderHintedStateIndices = /* @__PURE__ */ new Set();
5170
+ if (reorderHintedRoot.count > 0) {
5171
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5172
+ reorderHintedRoot.root,
5173
+ reactiveByValue,
5174
+ safeGlobals,
5175
+ listNames,
5176
+ allowedComponentNames
5177
+ );
5178
+ const hintedAnalysis = analyzeHostTree(
5179
+ reorderHintedRoot.root,
5180
+ reactiveByValue,
5181
+ safeGlobals,
5182
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5183
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5184
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5185
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5186
+ );
5187
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5188
+ expandedReactiveRoot = reorderHintedRoot.root;
5189
+ blockAnalysis = hintedBlockAnalysis;
5190
+ analysis = hintedAnalysis;
5191
+ appliedKeyedArrayReorderHints = reorderHintedRoot.count;
5192
+ appliedReorderHintedStateIndices = reorderHintedRoot.stateIndices;
5193
+ optimizationCounts.keyedArrayReorderHints += reorderHintedRoot.count;
5194
+ }
5195
+ }
5196
+ const sliceHintedRoot = rewriteKeyedArraySliceHints(
5197
+ expandedReactiveRoot,
5198
+ shiftedIndexIndependentStateIndices,
5199
+ statesBySetter,
5200
+ keyedArraySliceIdentifier
5201
+ );
5202
+ let appliedKeyedArraySliceHints = 0;
5203
+ let appliedSliceHintedStateIndices = /* @__PURE__ */ new Set();
5204
+ if (sliceHintedRoot.count > 0) {
5205
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5206
+ sliceHintedRoot.root,
5207
+ reactiveByValue,
5208
+ safeGlobals,
5209
+ listNames,
5210
+ allowedComponentNames
5211
+ );
5212
+ const hintedAnalysis = analyzeHostTree(
5213
+ sliceHintedRoot.root,
5214
+ reactiveByValue,
5215
+ safeGlobals,
5216
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5217
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5218
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5219
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5220
+ );
5221
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5222
+ expandedReactiveRoot = sliceHintedRoot.root;
5223
+ blockAnalysis = hintedBlockAnalysis;
5224
+ analysis = hintedAnalysis;
5225
+ appliedKeyedArraySliceHints = sliceHintedRoot.count;
5226
+ appliedSliceHintedStateIndices = sliceHintedRoot.stateIndices;
5227
+ optimizationCounts.keyedArraySliceHints += sliceHintedRoot.count;
5228
+ }
5229
+ }
5230
+ const filterHintedRoot = rewriteKeyedArrayFilterHints(
5231
+ expandedReactiveRoot,
5232
+ shiftedIndexIndependentStateIndices,
5233
+ statesBySetter,
5234
+ keyedArrayFilterIdentifier,
5235
+ safeGlobals
5236
+ );
5237
+ let appliedKeyedArrayFilterHints = 0;
5238
+ let appliedFilterHintedStateIndices = /* @__PURE__ */ new Set();
5239
+ if (filterHintedRoot.count > 0) {
5240
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5241
+ filterHintedRoot.root,
5242
+ reactiveByValue,
5243
+ safeGlobals,
5244
+ listNames,
5245
+ allowedComponentNames
5246
+ );
5247
+ const hintedAnalysis = analyzeHostTree(
5248
+ filterHintedRoot.root,
5249
+ reactiveByValue,
5250
+ safeGlobals,
5251
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5252
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5253
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5254
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5255
+ );
5256
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5257
+ expandedReactiveRoot = filterHintedRoot.root;
5258
+ blockAnalysis = hintedBlockAnalysis;
5259
+ analysis = hintedAnalysis;
5260
+ appliedKeyedArrayFilterHints = filterHintedRoot.count;
5261
+ appliedFilterHintedStateIndices = filterHintedRoot.stateIndices;
5262
+ optimizationCounts.keyedArrayFilterHints += filterHintedRoot.count;
5263
+ }
5264
+ }
5265
+ const keyedCollectionTargetKinds = /* @__PURE__ */ new Map();
5266
+ const conflictingKeyedCollectionTargets = /* @__PURE__ */ new Set();
5267
+ for (const plan of blockAnalysis.plans || []) {
5268
+ if (plan.kind !== "keyed-rows") continue;
5269
+ for (const binding of plan.bindings) {
5270
+ const target = binding.membershipTarget || binding.mapLookupTarget;
5271
+ if (!target) continue;
5272
+ const kind = binding.membershipTarget ? "set" : "map";
5273
+ const previousKind = keyedCollectionTargetKinds.get(target.dependency);
5274
+ if (previousKind && previousKind !== kind) {
5275
+ conflictingKeyedCollectionTargets.add(target.dependency);
5276
+ } else {
5277
+ keyedCollectionTargetKinds.set(target.dependency, kind);
5278
+ }
5279
+ }
5280
+ }
5281
+ for (const dependency of conflictingKeyedCollectionTargets) {
5282
+ keyedCollectionTargetKinds.delete(dependency);
5283
+ }
5284
+ const keyedCollectionHintedRoot = rewriteKeyedCollectionUpdateHints(
5285
+ expandedReactiveRoot,
5286
+ keyedCollectionTargetKinds,
5287
+ states,
5288
+ statesBySetter,
5289
+ keyedCollectionUpdateIdentifier,
5290
+ keyedCollectionMutationIdentifier,
5291
+ globalCollections
5292
+ );
5293
+ if (keyedCollectionHintedRoot.count > 0) {
5294
+ const hintedBlockAnalysis = analyzeComposableBlocks(
5295
+ keyedCollectionHintedRoot.root,
5296
+ reactiveByValue,
5297
+ safeGlobals,
5298
+ listNames,
5299
+ allowedComponentNames
5300
+ );
5301
+ const hintedAnalysis = analyzeHostTree(
5302
+ keyedCollectionHintedRoot.root,
5303
+ reactiveByValue,
5304
+ safeGlobals,
5305
+ hintedBlockAnalysis.conditionalExpressions || /* @__PURE__ */ new Set(),
5306
+ hintedBlockAnalysis.keyedExpressions || /* @__PURE__ */ new Set(),
5307
+ hintedBlockAnalysis.ownedElements || /* @__PURE__ */ new Set(),
5308
+ hintedBlockAnalysis.componentElements || /* @__PURE__ */ new Set()
5309
+ );
5310
+ if (!hintedBlockAnalysis.reason && !hintedAnalysis.reason) {
5311
+ expandedReactiveRoot = keyedCollectionHintedRoot.root;
5312
+ blockAnalysis = hintedBlockAnalysis;
5313
+ analysis = hintedAnalysis;
5314
+ optimizationCounts.keyedCollectionUpdateHints += keyedCollectionHintedRoot.count;
5315
+ }
5316
+ }
3639
5317
  const blockPlans = blockAnalysis.plans || [];
3640
- const runtimeFeatures = runtimeFeaturesForPlans(blockPlans);
5318
+ optimizationCounts.keyedIdentityTargets += blockPlans.reduce(
5319
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.identityTarget !== void 0).length : 0),
5320
+ 0
5321
+ );
5322
+ optimizationCounts.keyedMembershipTargets += blockPlans.reduce(
5323
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.membershipTarget !== void 0).length : 0),
5324
+ 0
5325
+ );
5326
+ optimizationCounts.keyedMapLookupTargets += blockPlans.reduce(
5327
+ (count, plan) => count + (plan.kind === "keyed-rows" ? plan.bindings.filter((binding) => binding.mapLookupTarget !== void 0).length : 0),
5328
+ 0
5329
+ );
5330
+ const hasKeyedUpdateHints = appliedKeyedMapUpdateHints > 0 || appliedKeyedArrayAppendHints > 0 || appliedKeyedArrayFilterHints > 0 || appliedKeyedArrayPrependHints > 0 || appliedKeyedArrayPositionHints > 0 || appliedKeyedArrayReorderHints > 0 || appliedKeyedArrayRollingWindowHints > 0 || appliedKeyedArraySliceHints > 0;
5331
+ const hasKeyedArrayRemovalHints = appliedKeyedArrayFilterHints > 0 || appliedKeyedArrayRollingWindowHints > 0 || appliedKeyedArraySliceHints > 0;
5332
+ const runtimeFeatures = runtimeFeaturesForPlans(
5333
+ blockPlans,
5334
+ hasKeyedUpdateHints,
5335
+ hasKeyedArrayRemovalHints,
5336
+ appliedKeyedArrayPrependHints > 0,
5337
+ appliedKeyedArrayPositionHints > 0,
5338
+ appliedKeyedArrayReorderHints > 0,
5339
+ appliedKeyedArrayRollingWindowHints > 0
5340
+ );
3641
5341
  markShortCircuitBindings(analysis.bindings || []);
3642
5342
  assignStableBindingTargets(analysis.bindings || []);
3643
5343
  const stateParameter = path.scope.generateUidIdentifier("farmState");
@@ -3650,11 +5350,21 @@ function compileCandidate(candidate, createComponentIdentifier, runtimeFeatureId
3650
5350
  blockParameter,
3651
5351
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set()
3652
5352
  );
5353
+ const appliedRemovalHintedStateIndices = /* @__PURE__ */ new Set([
5354
+ ...appliedFilterHintedStateIndices,
5355
+ ...appliedRollingWindowHintedStateIndices,
5356
+ ...appliedSliceHintedStateIndices
5357
+ ]);
3653
5358
  const rootWithBlocks = lowerComposableBlocks(
3654
5359
  rootWithTargets,
3655
5360
  blockPlans,
3656
5361
  blockParameter,
3657
- listNames
5362
+ listNames,
5363
+ hasKeyedUpdateHints,
5364
+ appliedRemovalHintedStateIndices,
5365
+ appliedPrependHintedStateIndices,
5366
+ appliedPositionHintedStateIndices,
5367
+ appliedReorderHintedStateIndices
3658
5368
  );
3659
5369
  const rewrittenRoot = rewriteStateAccess(
3660
5370
  rootWithBlocks,
@@ -3792,6 +5502,20 @@ function collectCandidates(programPath) {
3792
5502
  async function compileReactModule(code, id, options) {
3793
5503
  const compiled = [];
3794
5504
  const diagnostics = [];
5505
+ const optimizationCounts = {
5506
+ keyedArrayAppendHints: 0,
5507
+ keyedArrayFilterHints: 0,
5508
+ keyedArrayPrependHints: 0,
5509
+ keyedArrayPositionHints: 0,
5510
+ keyedArrayReorderHints: 0,
5511
+ keyedArrayRollingWindowHints: 0,
5512
+ keyedArraySliceHints: 0,
5513
+ keyedCollectionUpdateHints: 0,
5514
+ keyedIdentityTargets: 0,
5515
+ keyedMapLookupTargets: 0,
5516
+ keyedMembershipTargets: 0,
5517
+ keyedMapUpdateHints: 0
5518
+ };
3795
5519
  const plugin = () => ({
3796
5520
  name: "farm-react-aot",
3797
5521
  visitor: {
@@ -3821,6 +5545,36 @@ async function compileReactModule(code, id, options) {
3821
5545
  }
3822
5546
  const candidates = collectCandidates(programPath);
3823
5547
  const createComponentIdentifier = programPath.scope.generateUidIdentifier("createCompiledComponent");
5548
+ const keyedMapUpdateIdentifier = programPath.scope.generateUidIdentifier(
5549
+ "createCompilerKeyedMapUpdate"
5550
+ );
5551
+ const keyedArrayAppendIdentifier = programPath.scope.generateUidIdentifier(
5552
+ "createCompilerKeyedArrayAppend"
5553
+ );
5554
+ const keyedArrayFilterIdentifier = programPath.scope.generateUidIdentifier(
5555
+ "createCompilerKeyedArrayFilter"
5556
+ );
5557
+ const keyedArrayPrependIdentifier = programPath.scope.generateUidIdentifier(
5558
+ "createCompilerKeyedArrayPrepend"
5559
+ );
5560
+ const keyedArrayPositionIdentifier = programPath.scope.generateUidIdentifier(
5561
+ "createCompilerKeyedArrayPositionUpdate"
5562
+ );
5563
+ const keyedArrayReorderIdentifier = programPath.scope.generateUidIdentifier(
5564
+ "createCompilerKeyedArrayReorder"
5565
+ );
5566
+ const keyedArrayRollingWindowIdentifier = programPath.scope.generateUidIdentifier(
5567
+ "createCompilerKeyedArrayRollingWindow"
5568
+ );
5569
+ const keyedArraySliceIdentifier = programPath.scope.generateUidIdentifier(
5570
+ "createCompilerKeyedArraySlice"
5571
+ );
5572
+ const keyedCollectionUpdateIdentifier = programPath.scope.generateUidIdentifier(
5573
+ "createCompilerKeyedCollectionUpdate"
5574
+ );
5575
+ const keyedCollectionMutationIdentifier = programPath.scope.generateUidIdentifier(
5576
+ "applyCompilerKeyedCollectionMutation"
5577
+ );
3824
5578
  const runtimeFeatureIdentifiers = new Map(
3825
5579
  Object.entries(COMPILER_RUNTIME_FEATURE_EXPORTS).map(([feature, exportName]) => [
3826
5580
  feature,
@@ -3838,8 +5592,19 @@ async function compileReactModule(code, id, options) {
3838
5592
  const reason = compileCandidate(
3839
5593
  candidate,
3840
5594
  createComponentIdentifier,
5595
+ keyedMapUpdateIdentifier,
5596
+ keyedArrayAppendIdentifier,
5597
+ keyedArrayFilterIdentifier,
5598
+ keyedArrayPrependIdentifier,
5599
+ keyedArrayPositionIdentifier,
5600
+ keyedArrayReorderIdentifier,
5601
+ keyedArrayRollingWindowIdentifier,
5602
+ keyedArraySliceIdentifier,
5603
+ keyedCollectionUpdateIdentifier,
5604
+ keyedCollectionMutationIdentifier,
3841
5605
  runtimeFeatureIdentifiers,
3842
5606
  usedRuntimeFeatures,
5607
+ optimizationCounts,
3843
5608
  useStateNames,
3844
5609
  reactNames,
3845
5610
  listNames,
@@ -3865,6 +5630,64 @@ async function compileReactModule(code, id, options) {
3865
5630
  createComponentIdentifier,
3866
5631
  core.types.identifier("createCompiledComponentWithFeatures")
3867
5632
  ),
5633
+ ...optimizationCounts.keyedMapUpdateHints > 0 ? [
5634
+ core.types.importSpecifier(
5635
+ keyedMapUpdateIdentifier,
5636
+ core.types.identifier("createCompilerKeyedMapUpdate")
5637
+ )
5638
+ ] : [],
5639
+ ...optimizationCounts.keyedArrayAppendHints > 0 ? [
5640
+ core.types.importSpecifier(
5641
+ keyedArrayAppendIdentifier,
5642
+ core.types.identifier("createCompilerKeyedArrayAppend")
5643
+ )
5644
+ ] : [],
5645
+ ...optimizationCounts.keyedArrayFilterHints > 0 ? [
5646
+ core.types.importSpecifier(
5647
+ keyedArrayFilterIdentifier,
5648
+ core.types.identifier("createCompilerKeyedArrayFilter")
5649
+ )
5650
+ ] : [],
5651
+ ...optimizationCounts.keyedArrayPrependHints > 0 ? [
5652
+ core.types.importSpecifier(
5653
+ keyedArrayPrependIdentifier,
5654
+ core.types.identifier("createCompilerKeyedArrayPrepend")
5655
+ )
5656
+ ] : [],
5657
+ ...optimizationCounts.keyedArrayPositionHints > 0 ? [
5658
+ core.types.importSpecifier(
5659
+ keyedArrayPositionIdentifier,
5660
+ core.types.identifier("createCompilerKeyedArrayPositionUpdate")
5661
+ )
5662
+ ] : [],
5663
+ ...optimizationCounts.keyedArrayReorderHints > 0 ? [
5664
+ core.types.importSpecifier(
5665
+ keyedArrayReorderIdentifier,
5666
+ core.types.identifier("createCompilerKeyedArrayReorder")
5667
+ )
5668
+ ] : [],
5669
+ ...optimizationCounts.keyedArrayRollingWindowHints > 0 ? [
5670
+ core.types.importSpecifier(
5671
+ keyedArrayRollingWindowIdentifier,
5672
+ core.types.identifier("createCompilerKeyedArrayRollingWindow")
5673
+ )
5674
+ ] : [],
5675
+ ...optimizationCounts.keyedArraySliceHints > 0 || optimizationCounts.keyedArrayRollingWindowHints > 0 ? [
5676
+ core.types.importSpecifier(
5677
+ keyedArraySliceIdentifier,
5678
+ core.types.identifier("createCompilerKeyedArraySlice")
5679
+ )
5680
+ ] : [],
5681
+ ...optimizationCounts.keyedCollectionUpdateHints > 0 ? [
5682
+ core.types.importSpecifier(
5683
+ keyedCollectionUpdateIdentifier,
5684
+ core.types.identifier("createCompilerKeyedCollectionUpdate")
5685
+ ),
5686
+ core.types.importSpecifier(
5687
+ keyedCollectionMutationIdentifier,
5688
+ core.types.identifier("applyCompilerKeyedCollectionMutation")
5689
+ )
5690
+ ] : [],
3868
5691
  ...[...usedRuntimeFeatures].sort().map(
3869
5692
  (feature) => core.types.importSpecifier(
3870
5693
  core.types.cloneNode(runtimeFeatureIdentifiers.get(feature)),
@@ -3901,7 +5724,8 @@ async function compileReactModule(code, id, options) {
3901
5724
  code: result?.code || code,
3902
5725
  map: result?.map || null,
3903
5726
  compiled,
3904
- diagnostics
5727
+ diagnostics,
5728
+ optimizations: optimizationCounts
3905
5729
  };
3906
5730
  }
3907
5731
  function projectRelativeId(projectRoot, id) {
@@ -3912,10 +5736,34 @@ function createReactCompilerReport(projectRoot, observations) {
3912
5736
  let componentsConsidered = 0;
3913
5737
  let compiled = 0;
3914
5738
  let fallback = 0;
5739
+ let keyedArrayAppendHints = 0;
5740
+ let keyedArrayFilterHints = 0;
5741
+ let keyedArrayPrependHints = 0;
5742
+ let keyedArrayPositionHints = 0;
5743
+ let keyedArrayReorderHints = 0;
5744
+ let keyedArrayRollingWindowHints = 0;
5745
+ let keyedArraySliceHints = 0;
5746
+ let keyedCollectionUpdateHints = 0;
5747
+ let keyedIdentityTargets = 0;
5748
+ let keyedMapLookupTargets = 0;
5749
+ let keyedMembershipTargets = 0;
5750
+ let keyedMapUpdateHints = 0;
3915
5751
  const modules = [...observations].map(({ id, result }) => {
3916
5752
  componentsConsidered += result.compiled.length + result.diagnostics.length;
3917
5753
  compiled += result.compiled.length;
3918
5754
  fallback += result.diagnostics.length;
5755
+ keyedArrayAppendHints += result.optimizations.keyedArrayAppendHints || 0;
5756
+ keyedArrayFilterHints += result.optimizations.keyedArrayFilterHints || 0;
5757
+ keyedArrayPrependHints += result.optimizations.keyedArrayPrependHints || 0;
5758
+ keyedArrayPositionHints += result.optimizations.keyedArrayPositionHints || 0;
5759
+ keyedArrayReorderHints += result.optimizations.keyedArrayReorderHints || 0;
5760
+ keyedArrayRollingWindowHints += result.optimizations.keyedArrayRollingWindowHints || 0;
5761
+ keyedArraySliceHints += result.optimizations.keyedArraySliceHints || 0;
5762
+ keyedCollectionUpdateHints += result.optimizations.keyedCollectionUpdateHints || 0;
5763
+ keyedIdentityTargets += result.optimizations.keyedIdentityTargets || 0;
5764
+ keyedMapLookupTargets += result.optimizations.keyedMapLookupTargets || 0;
5765
+ keyedMembershipTargets += result.optimizations.keyedMembershipTargets || 0;
5766
+ keyedMapUpdateHints += result.optimizations.keyedMapUpdateHints;
3919
5767
  const moduleId = projectRelativeId(projectRoot, id);
3920
5768
  const fallbacks = result.diagnostics.map((diagnostic) => {
3921
5769
  reasonCounts.set(diagnostic.reason, (reasonCounts.get(diagnostic.reason) || 0) + 1);
@@ -3924,6 +5772,7 @@ function createReactCompilerReport(projectRoot, observations) {
3924
5772
  return {
3925
5773
  id: moduleId,
3926
5774
  compiled: [...result.compiled].sort(),
5775
+ optimizations: result.optimizations,
3927
5776
  fallbacks
3928
5777
  };
3929
5778
  }).sort((left, right) => left.id.localeCompare(right.id));
@@ -3934,7 +5783,19 @@ function createReactCompilerReport(projectRoot, observations) {
3934
5783
  modules: modules.length,
3935
5784
  componentsConsidered,
3936
5785
  compiled,
3937
- fallback
5786
+ fallback,
5787
+ keyedArrayAppendHints,
5788
+ keyedArrayFilterHints,
5789
+ keyedArrayPrependHints,
5790
+ keyedArrayPositionHints,
5791
+ keyedArrayReorderHints,
5792
+ keyedArrayRollingWindowHints,
5793
+ keyedArraySliceHints,
5794
+ keyedCollectionUpdateHints,
5795
+ keyedIdentityTargets,
5796
+ keyedMapLookupTargets,
5797
+ keyedMembershipTargets,
5798
+ keyedMapUpdateHints
3938
5799
  },
3939
5800
  fallbackReasons,
3940
5801
  modules