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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -300,19 +300,25 @@ expose the site count as `keyedArrayRollingWindowHints`.
300
300
  Native known-position updates can avoid a complete keyed scan too:
301
301
 
302
302
  ```tsx
303
- setItems((current) => current.toSpliced(500, 0, nextItem));
304
- setItems((current) => current.with(500, replacement));
303
+ setItems((current) => current.toSpliced(selectedIndex, 0, nextItem));
304
+ setItems((current) => current.toSpliced(selectedIndex, 1));
305
+ setItems((current) => current.with(selectedIndex, replacement));
305
306
  ```
306
307
 
307
- The compiler recognizes only a concise functional setter, a build-time safe-integer position, one
308
- inserted item with zero removals, or one direct replacement. Farm preserves the ordinary native
309
- method call and records metadata only after validating the committed native source and result. The
310
- runtime creates one inserted row or patches/replaces one row at the known position without
311
- rereading every existing key, descriptor, and binding. Index-aware or collection-reading rows,
312
- custom methods, other `toSpliced()` forms, block-bodied updaters, unsafe incoming expressions,
313
- queued uncommitted updates, reused keys, nested or React-owned rows, and failed checks use complete
314
- keyed reconciliation. Reports expose the site count as `keyedArrayPositionHints`; the capability is
315
- tree-shaken from modules that do not emit it.
308
+ The compiler recognizes only a concise functional setter and either a safe-integer literal or a
309
+ compiler-safe runtime position expression, such as an identifier, property read, arithmetic,
310
+ conditional, or safe `Math` call. The update must insert one item with zero removals, remove one
311
+ item, or directly replace one item. Farm preserves method lookup, evaluates the position and
312
+ remaining arguments once in their original order, executes the ordinary native call, and records
313
+ metadata only after validating the committed native source, result, and actual safe-integer
314
+ position. The runtime creates one inserted row, removes one known row, or patches/replaces one row
315
+ at that position without rereading every existing key, descriptor, and binding. Surviving rows keep
316
+ their DOM identity. Index-aware or collection-reading rows, custom methods, position expressions
317
+ with user calls or mutations, runtime positions that are not safe integers, other `toSpliced()`
318
+ forms, block-bodied updaters, unsafe incoming expressions, queued uncommitted updates, reused keys,
319
+ nested or React-owned rows, and failed checks use complete keyed reconciliation. Reports expose the
320
+ site count as `keyedArrayPositionHints`; the capability is tree-shaken from modules that do not emit
321
+ it.
316
322
 
317
323
  A direct native reverse can carry its complete permutation to a separate optional runtime:
318
324
 
@@ -330,6 +336,26 @@ React-owned rows, and failed checks keep complete keyed reconciliation. Reports
330
336
  count as `keyedArrayReorderHints`, and modules without one omit the reorder runtime. Farm does not
331
337
  polyfill `Array.prototype.toReversed`.
332
338
 
339
+ A direct native immutable sort can use the same optional reorder runtime:
340
+
341
+ ```tsx
342
+ setItems((current) => current.toSorted((left, right) => left.rank - right.rank));
343
+ setLabels((current) => current.toSorted());
344
+ ```
345
+
346
+ Farm recognizes a concise functional setter with no comparator or an inline synchronous comparator
347
+ from the compiler-safe expression subset. It preserves the original method lookup, comparator,
348
+ stable native result, and errors. After the native sort runs, the runtime validates a committed
349
+ ordinary dense array, equal lengths, and a unique one-to-one item-identity permutation. It then
350
+ uses LIS to move only `n - LIS` keyed DOM nodes without rereading row keys, descriptors, or
351
+ bindings. The native sorting work itself is unchanged.
352
+
353
+ Index-aware or collection-reading rows, referenced comparators, block-bodied updaters, computed or
354
+ chained calls, custom methods, sparse or subclassed arrays, duplicate item identities, queued
355
+ uncommitted sorts, nested or React-owned rows, and failed checks keep complete keyed
356
+ reconciliation. Reports expose the site count as `keyedArraySortHints`; sort shares the optional
357
+ reorder runtime, and Farm does not polyfill `Array.prototype.toSorted`.
358
+
333
359
  Concise immutable filters on a direct keyed array can carry removal positions into the same
334
360
  optional runtime:
335
361
 
@@ -631,9 +657,10 @@ reasons aggregated by count. Its summary and per-module `optimizations` also rep
631
657
  `keyedArrayAppendHints`, the number of compiler-proven direct keyed-array append sites; and
632
658
  `keyedArrayFilterHints`, the number of compiler-proven direct keyed-array filter sites; and
633
659
  `keyedArrayPrependHints`, the number of compiler-proven direct keyed-array prepend sites; and
634
- `keyedArrayPositionHints`, the number of compiler-proven native known-position insertion or
635
- replacement sites; and
660
+ `keyedArrayPositionHints`, the number of compiler-proven native exact-position insertion, removal,
661
+ or replacement sites, including guarded compiler-safe runtime positions; and
636
662
  `keyedArrayReorderHints`, the number of compiler-proven direct native keyed-array reverse sites; and
663
+ `keyedArraySortHints`, the number of compiler-proven direct native keyed-array sort sites; and
637
664
  `keyedArrayRollingWindowHints`, the number of compiler-proven retained-tail plus incoming-suffix
638
665
  sites; and
639
666
  `keyedArraySliceHints`, the number of compiler-proven direct keyed-array slice sites. A custom
@@ -43,6 +43,7 @@ var NATIVE_ARRAY_SLICE = Array.prototype.slice;
43
43
  var NATIVE_ARRAY_TO_SPLICED = Array.prototype.toSpliced;
44
44
  var NATIVE_ARRAY_WITH = Array.prototype.with;
45
45
  var NATIVE_ARRAY_TO_REVERSED = Array.prototype.toReversed;
46
+ var NATIVE_ARRAY_TO_SORTED = Array.prototype.toSorted;
46
47
  var NATIVE_REFLECT_APPLY = Reflect.apply;
47
48
  function compilerObject(value) {
48
49
  return typeof value === "object" && value !== null || typeof value === "function" ? value : void 0;
@@ -163,7 +164,7 @@ function compilerKeyedArrayPosition(value, sourceToken, expectedLength) {
163
164
  const target = compilerObject(value);
164
165
  if (!target || !sourceToken || !Array.isArray(value)) return void 0;
165
166
  const update = COMPILER_KEYED_ARRAY_POSITIONS.get(target);
166
- if (!update || update.sourceToken !== sourceToken || update.sourceLength !== expectedLength || update.resultLength !== value.length || update.position < 0 || update.position > expectedLength || update.kind === "insert" && update.resultLength !== expectedLength + 1 || update.kind === "replace" && (update.position >= expectedLength || update.resultLength !== expectedLength)) {
167
+ if (!update || update.sourceToken !== sourceToken || update.sourceLength !== expectedLength || update.resultLength !== value.length || update.position < 0 || update.position > expectedLength || update.kind === "insert" && update.resultLength !== expectedLength + 1 || update.kind === "remove" && (update.position >= expectedLength || update.resultLength !== expectedLength - 1) || update.kind === "replace" && (update.position >= expectedLength || update.resultLength !== expectedLength)) {
167
168
  return void 0;
168
169
  }
169
170
  return update;
@@ -380,8 +381,9 @@ function createCompilerKeyedArrayPositionUpdate(previous, method, kind, position
380
381
  const sourceLength = previous.length;
381
382
  const normalizedPosition = position < 0 ? Math.max(sourceLength + position, 0) : Math.min(position, sourceLength);
382
383
  const validInsert = kind === "insert" && method === NATIVE_ARRAY_TO_SPLICED && args.length === 2 && Object.is(args[0], 0) && value.length === sourceLength + 1;
384
+ const validRemove = kind === "remove" && method === NATIVE_ARRAY_TO_SPLICED && args.length === 1 && Object.is(args[0], 1) && normalizedPosition < sourceLength && value.length === sourceLength - 1;
383
385
  const validReplace = kind === "replace" && method === NATIVE_ARRAY_WITH && args.length === 1 && position >= -sourceLength && position < sourceLength && value.length === sourceLength;
384
- if (!validInsert && !validReplace) return value;
386
+ if (!validInsert && !validRemove && !validReplace) return value;
385
387
  const sourceToken = compilerKeyedCollectionToken(previousTarget);
386
388
  if (!sourceToken) return value;
387
389
  COMPILER_KEYED_ARRAY_POSITIONS.set(valueTarget, {
@@ -419,6 +421,33 @@ function createCompilerKeyedArrayReorder(previous, method) {
419
421
  }
420
422
  return value;
421
423
  }
424
+ function createCompilerKeyedArraySort(previous, method, ...args) {
425
+ const value = NATIVE_REFLECT_APPLY(
426
+ method,
427
+ previous,
428
+ args
429
+ );
430
+ try {
431
+ const previousTarget = compilerObject(previous);
432
+ const valueTarget = compilerObject(value);
433
+ if (!previousTarget || !valueTarget || !COMPILER_KEYED_COMMITTED_COLLECTIONS.has(previousTarget) || !Array.isArray(previous) || !Array.isArray(value) || Object.getPrototypeOf(previous) !== NATIVE_ARRAY_PROTOTYPE || Object.getPrototypeOf(value) !== NATIVE_ARRAY_PROTOTYPE || method !== NATIVE_ARRAY_TO_SORTED || args.length > 1 || args.length === 1 && args[0] !== void 0 && typeof args[0] !== "function" || value.length !== previous.length) {
434
+ return value;
435
+ }
436
+ for (let index = 0; index < previous.length; index += 1) {
437
+ if (!(index in previous)) return value;
438
+ }
439
+ const sourceToken = compilerKeyedCollectionToken(previousTarget);
440
+ if (!sourceToken) return value;
441
+ COMPILER_KEYED_ARRAY_REORDERS.set(valueTarget, {
442
+ kind: "sort",
443
+ sourceToken,
444
+ sourceLength: previous.length,
445
+ resultLength: value.length
446
+ });
447
+ } catch {
448
+ }
449
+ return value;
450
+ }
422
451
  function applyCompilerKeyedCollectionMutation(collection, method, operation, key, value) {
423
452
  const args = operation === "map-set" ? [key, value] : [key];
424
453
  const result = NATIVE_REFLECT_APPLY(method, collection, args);
@@ -2758,6 +2787,16 @@ function reconcileCompilerKeyedArrayPosition(props, dirtyState, collectionToken,
2758
2787
  if (!update || !Array.isArray(finalValue)) return void 0;
2759
2788
  const previousInstances = [...instances.values()];
2760
2789
  const previous = previousInstances[update.position];
2790
+ if (update.kind === "remove") {
2791
+ if (!previous || previous.index !== update.position) return void 0;
2792
+ previous.scope?.cleanup();
2793
+ previous.element.remove();
2794
+ previousInstances.splice(update.position, 1);
2795
+ for (let index = update.position; index < previousInstances.length; index += 1) {
2796
+ previousInstances[index].index = index;
2797
+ }
2798
+ return new Map(previousInstances.map((instance) => [instance.key, instance]));
2799
+ }
2761
2800
  let item;
2762
2801
  let key;
2763
2802
  try {
@@ -2834,8 +2873,47 @@ function reconcileCompilerKeyedArrayReorder(props, dirtyState, collectionToken,
2834
2873
  }
2835
2874
  const finalValue = props.items();
2836
2875
  const update = compilerKeyedArrayReorder(finalValue, collectionToken, instances.size);
2837
- if (!update || update.kind !== "reverse" || !Array.isArray(finalValue)) return void 0;
2876
+ if (!update || !Array.isArray(finalValue)) return void 0;
2838
2877
  const previousInstances = [...instances.values()];
2878
+ if (update.kind === "sort") {
2879
+ const instancesByItem = /* @__PURE__ */ new Map();
2880
+ try {
2881
+ for (let sourceIndex = 0; sourceIndex < previousInstances.length; sourceIndex += 1) {
2882
+ const instance = previousInstances[sourceIndex];
2883
+ if (instance.index !== sourceIndex || instancesByItem.has(instance.item)) return void 0;
2884
+ instancesByItem.set(instance.item, instance);
2885
+ }
2886
+ const nextInstances = [];
2887
+ const sequence = [];
2888
+ for (let targetIndex = 0; targetIndex < finalValue.length; targetIndex += 1) {
2889
+ const item = finalValue[targetIndex];
2890
+ const instance = instancesByItem.get(item);
2891
+ if (!instance || !Object.is(instance.item, item)) return void 0;
2892
+ instancesByItem.delete(item);
2893
+ nextInstances.push(instance);
2894
+ sequence.push(instance.index);
2895
+ }
2896
+ if (instancesByItem.size > 0) return void 0;
2897
+ const activeElement2 = root.ownerDocument.activeElement;
2898
+ const restoreFocus2 = Boolean(activeElement2 && root.contains(activeElement2));
2899
+ const stablePositions = longestIncreasingSubsequencePositions(sequence);
2900
+ let anchor2 = null;
2901
+ for (let index = nextInstances.length - 1; index >= 0; index -= 1) {
2902
+ const instance = nextInstances[index];
2903
+ if (!stablePositions.has(index) && instance.element.nextSibling !== anchor2) {
2904
+ root.insertBefore(instance.element, anchor2);
2905
+ }
2906
+ anchor2 = instance.element;
2907
+ instance.index = index;
2908
+ }
2909
+ if (restoreFocus2 && activeElement2?.isConnected && activeElement2.ownerDocument.activeElement !== activeElement2 && "focus" in activeElement2) {
2910
+ activeElement2.focus({ preventScroll: true });
2911
+ }
2912
+ return new Map(nextInstances.map((instance) => [instance.key, instance]));
2913
+ } catch {
2914
+ return void 0;
2915
+ }
2916
+ }
2839
2917
  try {
2840
2918
  for (let sourceIndex = 0; sourceIndex < previousInstances.length; sourceIndex += 1) {
2841
2919
  const instance = previousInstances[sourceIndex];
@@ -5147,6 +5225,7 @@ exports.createCompilerKeyedArrayPrepend = createCompilerKeyedArrayPrepend;
5147
5225
  exports.createCompilerKeyedArrayReorder = createCompilerKeyedArrayReorder;
5148
5226
  exports.createCompilerKeyedArrayRollingWindow = createCompilerKeyedArrayRollingWindow;
5149
5227
  exports.createCompilerKeyedArraySlice = createCompilerKeyedArraySlice;
5228
+ exports.createCompilerKeyedArraySort = createCompilerKeyedArraySort;
5150
5229
  exports.createCompilerKeyedCollectionUpdate = createCompilerKeyedCollectionUpdate;
5151
5230
  exports.createCompilerKeyedMapUpdate = createCompilerKeyedMapUpdate;
5152
5231
  exports.hostConditionalRuntimeFeature = hostConditionalRuntimeFeature;