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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.cjs CHANGED
@@ -62,6 +62,73 @@ function canDelegateKeyedRowEvents(plan) {
62
62
  (event) => DELEGATABLE_KEYED_ROW_EVENTS.has(event.name.replace(/Capture$/, ""))
63
63
  );
64
64
  }
65
+ var COMPILER_RUNTIME_FEATURE_EXPORTS = {
66
+ conditional: "conditionalRuntimeFeature",
67
+ "host-conditional": "hostConditionalRuntimeFeature",
68
+ "conditional-ranges": "conditionalRangesRuntimeFeature",
69
+ "keyed-list": "keyedListRuntimeFeature",
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",
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",
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",
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",
106
+ "keyed-ranges": "keyedRangesRuntimeFeature",
107
+ "mixed-ranges": "mixedRangesRuntimeFeature",
108
+ component: "componentRuntimeFeature"
109
+ };
110
+ function runtimeFeaturesForPlans(plans, keyedMapUpdateHints, keyedArrayFilterHints, keyedArrayPrependHints, keyedArrayPositionHints, keyedArrayReorderHints, keyedArrayRollingWindowHints) {
111
+ const features = /* @__PURE__ */ new Set();
112
+ let keyedRowsHaveConditionals = false;
113
+ let keyedRowsHaveHostBlocks = false;
114
+ for (const plan of plans) {
115
+ if (plan.kind === "keyed-rows") {
116
+ keyedRowsHaveConditionals || (keyedRowsHaveConditionals = plan.conditionals.length > 0);
117
+ keyedRowsHaveHostBlocks || (keyedRowsHaveHostBlocks = Boolean(plan.descriptorBlocks?.size));
118
+ continue;
119
+ }
120
+ if (plan.kind in COMPILER_RUNTIME_FEATURE_EXPORTS) {
121
+ features.add(plan.kind);
122
+ }
123
+ }
124
+ if (plans.some((plan) => plan.kind === "keyed-rows")) {
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}`);
129
+ }
130
+ return [...features].sort();
131
+ }
65
132
  var SAFE_GLOBAL_CALLS = /* @__PURE__ */ new Set(["Boolean", "Number", "String"]);
66
133
  var SAFE_MATH_CALLS = /* @__PURE__ */ new Set(["abs", "ceil", "floor", "max", "min", "round", "sign", "trunc"]);
67
134
  function isComponentName(name) {
@@ -107,6 +174,174 @@ function referencesIdentifier(expression, name) {
107
174
  });
108
175
  return referenced;
109
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
+ }
110
345
  function collectReferencedLocals(expression, names) {
111
346
  const references = /* @__PURE__ */ new Set();
112
347
  core.traverse(expressionFile(cloneExpression(expression)), {
@@ -207,7 +442,9 @@ function rewriteHandlerAccess(root, handlersByName) {
207
442
  path.skip();
208
443
  }
209
444
  });
210
- return reason ? { reason } : { root: file.program.body[0].expression };
445
+ return reason ? { reason } : {
446
+ root: file.program.body[0].expression
447
+ };
211
448
  }
212
449
  function isSafeCompilerCall(expression, safeGlobals) {
213
450
  if (core.types.isIdentifier(expression.callee)) {
@@ -215,7 +452,7 @@ function isSafeCompilerCall(expression, safeGlobals) {
215
452
  }
216
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);
217
454
  }
218
- function validateDerivedExpression(expression, safeGlobals) {
455
+ function validateDerivedExpression(expression, safeGlobals, additionalSafeCall) {
219
456
  let unsupported;
220
457
  const reject = (reason, path) => {
221
458
  unsupported = reason;
@@ -226,7 +463,9 @@ function validateDerivedExpression(expression, safeGlobals) {
226
463
  AssignmentExpression: (path) => reject("assignments", path),
227
464
  AwaitExpression: (path) => reject("await expressions", path),
228
465
  CallExpression(path) {
229
- if (!isSafeCompilerCall(path.node, safeGlobals)) reject("function calls", path);
466
+ if (!isSafeCompilerCall(path.node, safeGlobals) && !additionalSafeCall?.(path.node)) {
467
+ reject("function calls", path);
468
+ }
230
469
  },
231
470
  ClassExpression: (path) => reject("class expressions", path),
232
471
  Function: (path) => reject("function expressions", path),
@@ -266,6 +505,879 @@ function validateSetterUsage(root, statesBySetter) {
266
505
  });
267
506
  return reason;
268
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
+ }
269
1381
  function rewriteStateAccess(expression, stateParameter, statesByValue, statesBySetter) {
270
1382
  const file = expressionFile(cloneExpression(expression));
271
1383
  core.traverse(file, {
@@ -516,21 +1628,28 @@ function analyzeMapList(expression, statesByValue, safeGlobals) {
516
1628
  const collection = callee.object;
517
1629
  const collectionUnsupported = validateCollectionPipeline(collection, safeGlobals);
518
1630
  if (collectionUnsupported) {
519
- return { reason: `keyed list collection cannot use ${collectionUnsupported}` };
1631
+ return {
1632
+ reason: `keyed list collection cannot use ${collectionUnsupported}`
1633
+ };
520
1634
  }
521
1635
  if (expression.arguments.length !== 1 || !core.types.isArrowFunctionExpression(expression.arguments[0]) && !core.types.isFunctionExpression(expression.arguments[0])) {
522
1636
  return { reason: "keyed list map must use one inline render callback" };
523
1637
  }
524
1638
  const callback = expression.arguments[0];
525
1639
  if (containsDirectHookCall(callback)) {
526
- 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
+ };
527
1643
  }
528
1644
  const row = returnedExpression(callback);
529
1645
  if (!row || !core.types.isJSXElement(row)) {
530
1646
  return { reason: "keyed list map callbacks must return one React element" };
531
1647
  }
532
1648
  const key = jsxKeyExpression(row);
533
- 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
+ };
534
1653
  const keyReason = validateKeyFunction(callback, key, safeGlobals);
535
1654
  if (keyReason) return { reason: keyReason };
536
1655
  return { dependencies: collectStateDependencies(expression, statesByValue) };
@@ -545,7 +1664,9 @@ function listAttributeExpression(element, name) {
545
1664
  }
546
1665
  function analyzePublicList(element, statesByValue, safeGlobals) {
547
1666
  if (element.openingElement.attributes.some((attribute) => core.types.isJSXSpreadAttribute(attribute))) {
548
- 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
+ };
549
1670
  }
550
1671
  const each = listAttributeExpression(element, "each");
551
1672
  const by = listAttributeExpression(element, "by");
@@ -563,7 +1684,9 @@ function analyzePublicList(element, statesByValue, safeGlobals) {
563
1684
  if (keyReason) return { reason: keyReason };
564
1685
  const children = meaningfulJsxChildren(element);
565
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)) {
566
- 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
+ };
567
1690
  }
568
1691
  const render = children[0].expression;
569
1692
  if (!returnedExpression(render)) {
@@ -677,13 +1800,20 @@ function stabilizeDeferredEventCurrentTargets(root, compilerSetters) {
677
1800
  const expression = file.program.body[0].expression;
678
1801
  return core.types.isJSXElement(expression) ? expression : core.types.cloneNode(root, true);
679
1802
  }
680
- function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters) {
1803
+ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters, keyedTargetReactiveByValue) {
681
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])) {
682
- 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
+ };
683
1808
  }
684
1809
  const bindings = [];
685
1810
  const events = [];
686
1811
  const conditionals = [];
1812
+ const validateBindingExpression = (expression) => validateDerivedExpression(
1813
+ expression,
1814
+ safeGlobals,
1815
+ keyedTargetReactiveByValue ? (call) => isPotentialKeyedMembershipCall(call, keyedTargetReactiveByValue) || isPotentialKeyedMapLookupCall(call, keyedTargetReactiveByValue) : void 0
1816
+ );
687
1817
  const visit = (element, path) => {
688
1818
  const tag = element.openingElement.name;
689
1819
  if (!core.types.isJSXIdentifier(tag) || !/^[a-z]/.test(tag.name)) {
@@ -762,7 +1892,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
762
1892
  if (!propertyName || propertyName.includes("-") && !propertyName.startsWith("--")) {
763
1893
  return "compiled keyed row style names must use camelCase or a CSS custom property";
764
1894
  }
765
- const unsupported2 = validateDerivedExpression(property.value, safeGlobals);
1895
+ const unsupported2 = validateBindingExpression(property.value);
766
1896
  if (unsupported2) {
767
1897
  return `compiled keyed row style ${propertyName} cannot use ${unsupported2}`;
768
1898
  }
@@ -776,7 +1906,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
776
1906
  continue;
777
1907
  }
778
1908
  if (name === "children") return "compiled keyed row children props are not supported yet";
779
- const unsupported = validateDerivedExpression(expression, safeGlobals);
1909
+ const unsupported = validateBindingExpression(expression);
780
1910
  if (unsupported) return `compiled keyed row ${name} cannot use ${unsupported}`;
781
1911
  bindings.push({
782
1912
  kind: "attribute",
@@ -826,7 +1956,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
826
1956
  (child) => core.types.isJSXExpressionContainer(child) && !core.types.isJSXEmptyExpression(child.expression)
827
1957
  );
828
1958
  for (const child of expressions) {
829
- const unsupported = validateDerivedExpression(child.expression, safeGlobals);
1959
+ const unsupported = validateBindingExpression(child.expression);
830
1960
  if (unsupported) return `compiled keyed row text cannot use ${unsupported}`;
831
1961
  }
832
1962
  if (nestedElements.length > 0 && expressions.length > 0) {
@@ -842,7 +1972,11 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
842
1972
  parts.push(cloneExpression(child.expression));
843
1973
  }
844
1974
  }
845
- 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
+ });
846
1980
  }
847
1981
  let elementIndex = 0;
848
1982
  for (const child of element.children) {
@@ -856,7 +1990,7 @@ function analyzeKeyedRowTree(row, renderCallback, safeGlobals, compilerSetters)
856
1990
  const reason = visit(row, []);
857
1991
  return reason ? { reason } : { bindings, events, conditionals };
858
1992
  }
859
- function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false) {
1993
+ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acceptUnsupportedRow = false, allowMembershipTargets = false) {
860
1994
  let collection;
861
1995
  let keyCallback;
862
1996
  let renderCallback;
@@ -909,7 +2043,8 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
909
2043
  row,
910
2044
  renderCallback,
911
2045
  safeGlobals,
912
- new Set([...statesByValue.values()].map((state) => state.setterName))
2046
+ new Set([...statesByValue.values()].map((state) => state.setterName)),
2047
+ allowMembershipTargets ? statesByValue : void 0
913
2048
  );
914
2049
  if (rowAnalysis.reason && !acceptUnsupportedRow) return void 0;
915
2050
  const keyExpression = returnedExpression(keyCallback);
@@ -922,6 +2057,7 @@ function analyzeKeyedRowChild(child, statesByValue, safeGlobals, listNames, acce
922
2057
  return {
923
2058
  source,
924
2059
  collection,
2060
+ collectionDependency: core.types.isIdentifier(collection) ? statesByValue.get(collection.name)?.index : void 0,
925
2061
  structureDependencies: [...structureDependencies].sort((left, right) => left - right),
926
2062
  keyCallback,
927
2063
  renderCallback,
@@ -950,7 +2086,74 @@ function analyzeKeyedRowsContainer(container, statesByValue, safeGlobals, listNa
950
2086
  }
951
2087
  const children = meaningfulJsxChildren(container);
952
2088
  if (children.length !== 1) return void 0;
953
- 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
+ };
954
2157
  }
955
2158
  function analyzeKeyedRangesContainer(container, statesByValue, safeGlobals, listNames, allowSingleRange = false) {
956
2159
  if (container.openingElement.attributes.some(
@@ -1106,7 +2309,11 @@ function analyzeHostConditionalTree(branch, safeGlobals, allowStaticEvents = fal
1106
2309
  parts.push(cloneExpression(child.expression));
1107
2310
  }
1108
2311
  }
1109
- 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
+ });
1110
2317
  }
1111
2318
  let elementIndex = 0;
1112
2319
  for (const child of element.children) {
@@ -1278,7 +2485,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1278
2485
  listNames,
1279
2486
  id,
1280
2487
  allocateId
1281
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2488
+ ) : {
2489
+ bindings: [],
2490
+ plans: [],
2491
+ blocks: /* @__PURE__ */ new Map()
2492
+ };
1282
2493
  const falsy = shape.falsy ? analyzeRecursiveHostTree(
1283
2494
  shape.falsy,
1284
2495
  statesByValue,
@@ -1286,7 +2497,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1286
2497
  listNames,
1287
2498
  id,
1288
2499
  allocateId
1289
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2500
+ ) : {
2501
+ bindings: [],
2502
+ plans: [],
2503
+ blocks: /* @__PURE__ */ new Map()
2504
+ };
1290
2505
  if (truthy.reason) return truthy.reason;
1291
2506
  if (falsy.reason) return falsy.reason;
1292
2507
  plans.push(...truthy.plans, ...falsy.plans);
@@ -1444,7 +2659,11 @@ function analyzeRecursiveHostTree(branch, statesByValue, safeGlobals, listNames,
1444
2659
  parts.push(cloneExpression(child.expression));
1445
2660
  }
1446
2661
  }
1447
- 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
+ });
1448
2667
  }
1449
2668
  let elementIndex = 0;
1450
2669
  for (const child of element.children) {
@@ -1511,7 +2730,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1511
2730
  listNames,
1512
2731
  parent,
1513
2732
  allocateId
1514
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2733
+ ) : {
2734
+ bindings: [],
2735
+ plans: [],
2736
+ blocks: /* @__PURE__ */ new Map()
2737
+ };
1515
2738
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1516
2739
  shape.falsy,
1517
2740
  statesByValue,
@@ -1519,7 +2742,11 @@ function analyzeHostConditionalContainer(container, statesByValue, safeGlobals,
1519
2742
  listNames,
1520
2743
  parent,
1521
2744
  allocateId
1522
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2745
+ ) : {
2746
+ bindings: [],
2747
+ plans: [],
2748
+ blocks: /* @__PURE__ */ new Map()
2749
+ };
1523
2750
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1524
2751
  const dependencies = new Set(collectStateDependencies(shape.test, statesByValue));
1525
2752
  for (const binding of [...truthyAnalysis.bindings || [], ...falsyAnalysis.bindings || []]) {
@@ -1565,7 +2792,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1565
2792
  listNames,
1566
2793
  parent,
1567
2794
  allocateId
1568
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2795
+ ) : {
2796
+ bindings: [],
2797
+ plans: [],
2798
+ blocks: /* @__PURE__ */ new Map()
2799
+ };
1569
2800
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1570
2801
  shape.falsy,
1571
2802
  statesByValue,
@@ -1573,7 +2804,11 @@ function analyzeConditionalRangesContainer(container, statesByValue, safeGlobals
1573
2804
  listNames,
1574
2805
  parent,
1575
2806
  allocateId
1576
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2807
+ ) : {
2808
+ bindings: [],
2809
+ plans: [],
2810
+ blocks: /* @__PURE__ */ new Map()
2811
+ };
1577
2812
  if (truthyAnalysis.reason || falsyAnalysis.reason) return void 0;
1578
2813
  nestedPlans.push(...truthyAnalysis.plans, ...falsyAnalysis.plans);
1579
2814
  for (const [element, plan] of truthyAnalysis.blocks) descriptorBlocks.set(element, plan);
@@ -1666,7 +2901,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1666
2901
  listNames,
1667
2902
  parent,
1668
2903
  allocateId
1669
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2904
+ ) : {
2905
+ bindings: [],
2906
+ plans: [],
2907
+ blocks: /* @__PURE__ */ new Map()
2908
+ };
1670
2909
  const falsyAnalysis = shape.falsy ? analyzeRecursiveHostTree(
1671
2910
  shape.falsy,
1672
2911
  statesByValue,
@@ -1674,7 +2913,11 @@ function analyzeMixedRangesContainer(container, statesByValue, safeGlobals, list
1674
2913
  listNames,
1675
2914
  parent,
1676
2915
  allocateId
1677
- ) : { bindings: [], plans: [], blocks: /* @__PURE__ */ new Map() };
2916
+ ) : {
2917
+ bindings: [],
2918
+ plans: [],
2919
+ blocks: /* @__PURE__ */ new Map()
2920
+ };
1678
2921
  if (!mergeAnalysis(truthyAnalysis) || !mergeAnalysis(falsyAnalysis)) return void 0;
1679
2922
  for (const dependency of collectStateDependencies(shape.test, statesByValue)) {
1680
2923
  dependencies.add(dependency);
@@ -1915,6 +3158,57 @@ function keyedRowBindingObject(binding, parameters) {
1915
3158
  if (binding.name) {
1916
3159
  properties.push(core.types.objectProperty(core.types.identifier("name"), core.types.stringLiteral(binding.name)));
1917
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
+ }
1918
3212
  properties.push(
1919
3213
  core.types.objectProperty(
1920
3214
  core.types.identifier("read"),
@@ -2109,7 +3403,7 @@ function keyedRowConditionalObject(conditional, parameters) {
2109
3403
  }
2110
3404
  return core.types.objectExpression(properties);
2111
3405
  }
2112
- function keyedRowsBoundary(blockRuntime, plan) {
3406
+ function keyedRowsBoundary(blockRuntime, plan, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
2113
3407
  const name = core.types.jsxMemberExpression(
2114
3408
  core.types.jsxIdentifier(blockRuntime.name),
2115
3409
  core.types.jsxIdentifier("KeyedRows")
@@ -2143,6 +3437,16 @@ function keyedRowsBoundary(blockRuntime, plan) {
2143
3437
  core.types.jsxIdentifier("items"),
2144
3438
  core.types.jsxExpressionContainer(core.types.arrowFunctionExpression([], cloneExpression(plan.collection)))
2145
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
+ ] : [],
2146
3450
  core.types.jsxAttribute(
2147
3451
  core.types.jsxIdentifier("structureDependencies"),
2148
3452
  core.types.jsxExpressionContainer(
@@ -2151,6 +3455,36 @@ function keyedRowsBoundary(blockRuntime, plan) {
2151
3455
  )
2152
3456
  )
2153
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
+ ] : [],
2154
3488
  core.types.jsxAttribute(
2155
3489
  core.types.jsxIdentifier("rowKey"),
2156
3490
  core.types.jsxExpressionContainer(core.types.cloneNode(plan.keyCallback, true))
@@ -2477,7 +3811,9 @@ function conditionalRangesBoundary(blockRuntime, plan) {
2477
3811
  function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allowedComponentNames) {
2478
3812
  const name = element.openingElement.name;
2479
3813
  if (!core.types.isJSXIdentifier(name) || !isComponentName(name.name)) {
2480
- return { reason: "component islands require a direct component identifier" };
3814
+ return {
3815
+ reason: "component islands require a direct component identifier"
3816
+ };
2481
3817
  }
2482
3818
  if (!allowedComponentNames.has(name.name)) {
2483
3819
  return {
@@ -2485,19 +3821,27 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2485
3821
  };
2486
3822
  }
2487
3823
  if (meaningfulJsxChildren(element).length > 0) {
2488
- 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
+ };
2489
3827
  }
2490
3828
  const dependencies = /* @__PURE__ */ new Set();
2491
3829
  for (const attribute of element.openingElement.attributes) {
2492
3830
  if (core.types.isJSXSpreadAttribute(attribute)) {
2493
- 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
+ };
2494
3834
  }
2495
3835
  const attributeName = jsxAttributeName(attribute);
2496
3836
  if (!attributeName) {
2497
- 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
+ };
2498
3840
  }
2499
3841
  if (attributeName === "ref" || attributeName === "key" || attributeName === "children") {
2500
- 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
+ };
2501
3845
  }
2502
3846
  if (!core.types.isJSXExpressionContainer(attribute.value) || core.types.isJSXEmptyExpression(attribute.value.expression)) {
2503
3847
  continue;
@@ -2516,7 +3860,9 @@ function analyzeComponentIslandElement(element, statesByValue, safeGlobals, allo
2516
3860
  (dependency) => dependencies.add(dependency)
2517
3861
  );
2518
3862
  }
2519
- return { dependencies: [...dependencies].sort((left, right) => left - right) };
3863
+ return {
3864
+ dependencies: [...dependencies].sort((left, right) => left - right)
3865
+ };
2520
3866
  }
2521
3867
  function componentIslandBoundary(blockRuntime, plan, source) {
2522
3868
  const name = core.types.jsxMemberExpression(
@@ -2773,6 +4119,7 @@ function analyzeComposableBlocks(root, statesByValue, safeGlobals, listNames, al
2773
4119
  dependencies: keyedRows.dependencies,
2774
4120
  source: child,
2775
4121
  collection: keyedRows.collection,
4122
+ collectionDependency: keyedRows.collectionDependency,
2776
4123
  structureDependencies: keyedRows.structureDependencies,
2777
4124
  keyCallback: keyedRows.keyCallback,
2778
4125
  renderCallback: keyedRows.renderCallback,
@@ -3014,7 +4361,7 @@ function conditionalBlockBoundary(blockRuntime, plan, source) {
3014
4361
  true
3015
4362
  );
3016
4363
  }
3017
- function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
4364
+ function lowerComposableBlocks(root, plans, blockRuntime, listNames, keyedMapUpdateHints, keyedArrayFilterHintedStateIndices, keyedArrayPrependHintedStateIndices, keyedArrayPositionHintedStateIndices, keyedArrayReorderHintedStateIndices) {
3018
4365
  const planBySource = new Map(
3019
4366
  plans.map((plan) => [plan.source, plan])
3020
4367
  );
@@ -3039,7 +4386,15 @@ function lowerComposableBlocks(root, plans, blockRuntime, listNames) {
3039
4386
  return mixedRangesBoundary(blockRuntime, plan2);
3040
4387
  }
3041
4388
  if (plan2?.kind === "keyed-rows") {
3042
- return keyedRowsBoundary(blockRuntime, plan2);
4389
+ return keyedRowsBoundary(
4390
+ blockRuntime,
4391
+ plan2,
4392
+ keyedMapUpdateHints,
4393
+ keyedArrayFilterHintedStateIndices,
4394
+ keyedArrayPrependHintedStateIndices,
4395
+ keyedArrayPositionHintedStateIndices,
4396
+ keyedArrayReorderHintedStateIndices
4397
+ );
3043
4398
  }
3044
4399
  if (plan2?.kind === "keyed-ranges") {
3045
4400
  return keyedRangesBoundary(blockRuntime, plan2);
@@ -3402,7 +4757,7 @@ function wrapperElement(definitionIdentifier, props) {
3402
4757
  const attributes = props ? [core.types.jsxSpreadAttribute(core.types.cloneNode(props))] : [];
3403
4758
  return core.types.jsxElement(core.types.jsxOpeningElement(name, attributes, true), null, [], true);
3404
4759
  }
3405
- function compileCandidate(candidate, createComponentIdentifier, 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) {
3406
4761
  const { path, name, statementPath } = candidate;
3407
4762
  if (path.node.async || path.node.generator)
3408
4763
  return "async and generator components are not supported";
@@ -3414,6 +4769,7 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3414
4769
  const safeGlobals = new Set(
3415
4770
  [...SAFE_GLOBAL_CALLS, "Math"].filter((name2) => !path.scope.getBinding(name2))
3416
4771
  );
4772
+ const globalCollections = new Set(["Set", "Map"].filter((name2) => !path.scope.getBinding(name2)));
3417
4773
  const states = [];
3418
4774
  const locals = [];
3419
4775
  let returned;
@@ -3461,9 +4817,17 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3461
4817
  if (declaration.init.async || declaration.init.generator) {
3462
4818
  return `event handler ${declaration.id.name} must be synchronous`;
3463
4819
  }
3464
- 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
+ });
3465
4825
  } else {
3466
- 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
+ });
3467
4831
  }
3468
4832
  }
3469
4833
  if (states.length === 0) return "no local useState binding was found";
@@ -3602,7 +4966,378 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3602
4966
  }
3603
4967
  if (blockAnalysis.reason) return blockAnalysis.reason;
3604
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
+ }
3605
5317
  const blockPlans = blockAnalysis.plans || [];
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
+ );
3606
5341
  markShortCircuitBindings(analysis.bindings || []);
3607
5342
  assignStableBindingTargets(analysis.bindings || []);
3608
5343
  const stateParameter = path.scope.generateUidIdentifier("farmState");
@@ -3615,11 +5350,21 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3615
5350
  blockParameter,
3616
5351
  blockAnalysis.ownedElements || /* @__PURE__ */ new Set()
3617
5352
  );
5353
+ const appliedRemovalHintedStateIndices = /* @__PURE__ */ new Set([
5354
+ ...appliedFilterHintedStateIndices,
5355
+ ...appliedRollingWindowHintedStateIndices,
5356
+ ...appliedSliceHintedStateIndices
5357
+ ]);
3618
5358
  const rootWithBlocks = lowerComposableBlocks(
3619
5359
  rootWithTargets,
3620
5360
  blockPlans,
3621
5361
  blockParameter,
3622
- listNames
5362
+ listNames,
5363
+ hasKeyedUpdateHints,
5364
+ appliedRemovalHintedStateIndices,
5365
+ appliedPrependHintedStateIndices,
5366
+ appliedPositionHintedStateIndices,
5367
+ appliedReorderHintedStateIndices
3623
5368
  );
3624
5369
  const rewrittenRoot = rewriteStateAccess(
3625
5370
  rootWithBlocks,
@@ -3705,7 +5450,10 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3705
5450
  )
3706
5451
  )
3707
5452
  )
3708
- ])
5453
+ ]),
5454
+ core.types.arrayExpression(
5455
+ runtimeFeatures.map((feature) => core.types.cloneNode(runtimeFeatureIdentifiers.get(feature)))
5456
+ )
3709
5457
  ]);
3710
5458
  path.get("body").replaceWith(
3711
5459
  core.types.blockStatement([
@@ -3715,6 +5463,7 @@ function compileCandidate(candidate, createComponentIdentifier, useStateNames, r
3715
5463
  statementPath.insertAfter(
3716
5464
  core.types.variableDeclaration("const", [core.types.variableDeclarator(definitionIdentifier, definition)])
3717
5465
  );
5466
+ for (const feature of runtimeFeatures) usedRuntimeFeatures.add(feature);
3718
5467
  return void 0;
3719
5468
  }
3720
5469
  function collectCandidates(programPath) {
@@ -3753,6 +5502,20 @@ function collectCandidates(programPath) {
3753
5502
  async function compileReactModule(code, id, options) {
3754
5503
  const compiled = [];
3755
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
+ };
3756
5519
  const plugin = () => ({
3757
5520
  name: "farm-react-aot",
3758
5521
  visitor: {
@@ -3782,6 +5545,43 @@ async function compileReactModule(code, id, options) {
3782
5545
  }
3783
5546
  const candidates = collectCandidates(programPath);
3784
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
+ );
5578
+ const runtimeFeatureIdentifiers = new Map(
5579
+ Object.entries(COMPILER_RUNTIME_FEATURE_EXPORTS).map(([feature, exportName]) => [
5580
+ feature,
5581
+ programPath.scope.generateUidIdentifier(exportName)
5582
+ ])
5583
+ );
5584
+ const usedRuntimeFeatures = /* @__PURE__ */ new Set();
3785
5585
  for (const candidate of candidates) {
3786
5586
  const functionDirectives = new Set(
3787
5587
  core.types.isBlockStatement(candidate.path.node.body) ? directiveValues(candidate.path.node.body) : []
@@ -3792,6 +5592,19 @@ async function compileReactModule(code, id, options) {
3792
5592
  const reason = compileCandidate(
3793
5593
  candidate,
3794
5594
  createComponentIdentifier,
5595
+ keyedMapUpdateIdentifier,
5596
+ keyedArrayAppendIdentifier,
5597
+ keyedArrayFilterIdentifier,
5598
+ keyedArrayPrependIdentifier,
5599
+ keyedArrayPositionIdentifier,
5600
+ keyedArrayReorderIdentifier,
5601
+ keyedArrayRollingWindowIdentifier,
5602
+ keyedArraySliceIdentifier,
5603
+ keyedCollectionUpdateIdentifier,
5604
+ keyedCollectionMutationIdentifier,
5605
+ runtimeFeatureIdentifiers,
5606
+ usedRuntimeFeatures,
5607
+ optimizationCounts,
3795
5608
  useStateNames,
3796
5609
  reactNames,
3797
5610
  listNames,
@@ -3815,7 +5628,71 @@ async function compileReactModule(code, id, options) {
3815
5628
  [
3816
5629
  core.types.importSpecifier(
3817
5630
  createComponentIdentifier,
3818
- core.types.identifier("createCompiledComponent")
5631
+ core.types.identifier("createCompiledComponentWithFeatures")
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
+ ] : [],
5691
+ ...[...usedRuntimeFeatures].sort().map(
5692
+ (feature) => core.types.importSpecifier(
5693
+ core.types.cloneNode(runtimeFeatureIdentifiers.get(feature)),
5694
+ core.types.identifier(COMPILER_RUNTIME_FEATURE_EXPORTS[feature])
5695
+ )
3819
5696
  )
3820
5697
  ],
3821
5698
  core.types.stringLiteral("@farm.js/react/compiler-runtime")
@@ -3847,7 +5724,8 @@ async function compileReactModule(code, id, options) {
3847
5724
  code: result?.code || code,
3848
5725
  map: result?.map || null,
3849
5726
  compiled,
3850
- diagnostics
5727
+ diagnostics,
5728
+ optimizations: optimizationCounts
3851
5729
  };
3852
5730
  }
3853
5731
  function projectRelativeId(projectRoot, id) {
@@ -3858,10 +5736,34 @@ function createReactCompilerReport(projectRoot, observations) {
3858
5736
  let componentsConsidered = 0;
3859
5737
  let compiled = 0;
3860
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;
3861
5751
  const modules = [...observations].map(({ id, result }) => {
3862
5752
  componentsConsidered += result.compiled.length + result.diagnostics.length;
3863
5753
  compiled += result.compiled.length;
3864
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;
3865
5767
  const moduleId = projectRelativeId(projectRoot, id);
3866
5768
  const fallbacks = result.diagnostics.map((diagnostic) => {
3867
5769
  reasonCounts.set(diagnostic.reason, (reasonCounts.get(diagnostic.reason) || 0) + 1);
@@ -3870,6 +5772,7 @@ function createReactCompilerReport(projectRoot, observations) {
3870
5772
  return {
3871
5773
  id: moduleId,
3872
5774
  compiled: [...result.compiled].sort(),
5775
+ optimizations: result.optimizations,
3873
5776
  fallbacks
3874
5777
  };
3875
5778
  }).sort((left, right) => left.id.localeCompare(right.id));
@@ -3880,7 +5783,19 @@ function createReactCompilerReport(projectRoot, observations) {
3880
5783
  modules: modules.length,
3881
5784
  componentsConsidered,
3882
5785
  compiled,
3883
- 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
3884
5799
  },
3885
5800
  fallbackReasons,
3886
5801
  modules