@farm.js/react 0.1.0-beta.8 → 0.1.0-beta.9
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 +365 -62
- package/dist/compiler-runtime.cjs +2102 -241
- package/dist/compiler-runtime.cjs.map +1 -1
- package/dist/compiler-runtime.d.mts +50 -3
- package/dist/compiler-runtime.d.ts +50 -3
- package/dist/compiler-runtime.mjs +2074 -242
- package/dist/compiler-runtime.mjs.map +1 -1
- package/dist/vite.cjs +1546 -107
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +1548 -109
- package/dist/vite.mjs.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ definitions, while generated code uses the tree-shakable feature entry.
|
|
|
42
42
|
|
|
43
43
|
The persisted production-size fixtures and regression gate live in
|
|
44
44
|
[`RUNTIME_SIZE_RESULTS.md`](./RUNTIME_SIZE_RESULTS.md). The recorded direct-only runtime premium is
|
|
45
|
-
|
|
45
|
+
82.6% smaller than the complete compatibility runtime premium; the feature-heavy keyed benchmark
|
|
46
46
|
application removes 6,185 gzip bytes from its previous compiler-on build.
|
|
47
47
|
|
|
48
48
|
For selective adoption, use annotation mode:
|
|
@@ -220,19 +220,27 @@ updates such as:
|
|
|
220
220
|
|
|
221
221
|
```tsx
|
|
222
222
|
setItems((current) =>
|
|
223
|
-
current
|
|
223
|
+
current
|
|
224
|
+
.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item))
|
|
225
|
+
.map((item) => (item.id === targetId ? { ...item, selected: !item.selected } : item)),
|
|
224
226
|
);
|
|
225
227
|
```
|
|
226
228
|
|
|
227
|
-
|
|
228
|
-
validates
|
|
229
|
-
user
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
229
|
+
Farm executes every generated native `map()` and then compares the committed and final item
|
|
230
|
+
identities once. It validates final length, keys, and positions and patches each final changed row
|
|
231
|
+
once; intermediate arrays never receive DOM work. Every user `map()` remains O(n); this removes the
|
|
232
|
+
keyed runtime's second full key-and-binding scan. Queued hints compose. The source method lookup,
|
|
233
|
+
native result, callback order, and thrown errors are preserved.
|
|
234
|
+
|
|
235
|
+
Each stage requires an inline synchronous conditional mapper that returns the original item or an
|
|
236
|
+
object-spread replacement. An unsupported mapper anywhere in the chain disables the complete
|
|
237
|
+
setter hint. Custom methods still execute but record no metadata. Key changes, structural edits,
|
|
238
|
+
sparse or subclassed arrays, relevant mixed dependencies, and failed runtime checks use the
|
|
239
|
+
existing complete reconciliation and LIS path. Non-functional setters, derived collections,
|
|
240
|
+
block-bodied or mutating mappers, and other unproven forms are simply not hinted. No new option is
|
|
241
|
+
required. A compiler report counts prepared map calls as `keyedMapUpdateHints`. The separate hint
|
|
242
|
+
runtime capability is imported only when that count is nonzero, so direct-only and ordinary keyed
|
|
243
|
+
builds do not retain it.
|
|
236
244
|
|
|
237
245
|
The same optional runtime recognizes conservative immutable appends on a direct keyed array:
|
|
238
246
|
|
|
@@ -250,52 +258,148 @@ reconciliation. A key that reads the collection prevents hint emission entirely.
|
|
|
250
258
|
report exposes the emitted-site count as
|
|
251
259
|
`keyedArrayAppendHints`.
|
|
252
260
|
|
|
261
|
+
Adjacent removal and append setters can share the same committed-row proof:
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
setItems((current) => current.filter((item) => item.id !== expiredId));
|
|
265
|
+
setItems((current) => [...current, incoming]);
|
|
266
|
+
|
|
267
|
+
setItems((current) => current.slice(start, end));
|
|
268
|
+
setItems((current) => [...current, ...incoming]);
|
|
269
|
+
|
|
270
|
+
setItems((current) => current.slice(start, end));
|
|
271
|
+
setItems((current) => [...current, incoming]);
|
|
272
|
+
setItems((current) =>
|
|
273
|
+
current.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item)),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
setItems((current) => current.filter((item) => item.id !== expiredId));
|
|
277
|
+
setItems((current) =>
|
|
278
|
+
current.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item)),
|
|
279
|
+
);
|
|
280
|
+
setItems((current) => [...current, incoming]);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
The setters still execute in order and still create their normal native arrays. Before changing the
|
|
284
|
+
DOM, Farm validates the original committed token, every filter or slice survivor, the complete final
|
|
285
|
+
length, and every incoming key. It prepares all incoming keys, descriptors, bindings, and detached
|
|
286
|
+
host rows first; only then does it remove rejected rows, update survivor indexes, and append the new
|
|
287
|
+
suffix in one fragment. Existing survivors are neither rebound nor recreated, and additional
|
|
288
|
+
adjacent append setters are collapsed into the same final suffix. This keeps the unavoidable native
|
|
289
|
+
filter or slice work while removing the keyed runtime's second full scan.
|
|
290
|
+
|
|
291
|
+
One or more immediately adjacent safe same-key `map()` setters may run between the filter or slice
|
|
292
|
+
and the append, after the append, or in both positions. A safe map may also precede the structural
|
|
293
|
+
removal. Farm carries the original survivor indices or retained interval through those native maps
|
|
294
|
+
and records each replacement against its committed row. Before touching the DOM it validates the
|
|
295
|
+
complete dense result, every survivor identity, every changed key and binding, and every incoming
|
|
296
|
+
row. It then removes only rejected rows, patches only changed survivors, and creates the suffix from
|
|
297
|
+
its final values. The owner stays mounted, survivor DOM identity is preserved, and the suffix is
|
|
298
|
+
appended once.
|
|
299
|
+
|
|
300
|
+
Only concise updater results that form one direct chain for the same state array are linked. The row
|
|
301
|
+
and key must be compiler-owned and index-independent. A reorder in the structural-append chain, an
|
|
302
|
+
unsupported or non-adjacent map, an unhinted update to that state, another dirty row dependency,
|
|
303
|
+
collection-reading binding, custom, sparse, or subclassed array, nested or React-owned row,
|
|
304
|
+
duplicate final key, or reuse of any committed key in the appended suffix keeps complete React
|
|
305
|
+
reconciliation before a DOM write. A mapped survivor whose key changes also falls back before
|
|
306
|
+
mutation. No API or configuration is added. Compiler reports use the existing
|
|
307
|
+
`keyedArrayFilterHints`, `keyedArraySliceHints`, and `keyedArrayAppendHints` counts, and modules
|
|
308
|
+
without both accepted operations omit the composed runtime.
|
|
309
|
+
|
|
253
310
|
The mirror-image prepend form is supported when the keyed row and key do not read the row index:
|
|
254
311
|
|
|
255
312
|
```tsx
|
|
256
313
|
setItems((current) => [nextItem, ...current]);
|
|
257
314
|
setItems((current) => [...nextItems, ...current]);
|
|
315
|
+
|
|
316
|
+
setItems((current) => current.filter((item) => item.id !== expiredId));
|
|
317
|
+
setItems((current) => [nextItem, ...current]);
|
|
318
|
+
|
|
319
|
+
setItems((current) => current.slice(start, end));
|
|
320
|
+
setItems((current) => [...nextItems, ...current]);
|
|
321
|
+
|
|
322
|
+
setItems((current) => current.filter((item) => item.id !== expiredId));
|
|
323
|
+
setItems((current) => [nextItem, ...current]);
|
|
324
|
+
setItems((current) =>
|
|
325
|
+
current.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item)),
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
setItems((current) => current.slice(start, end));
|
|
329
|
+
setItems((current) =>
|
|
330
|
+
current.map((item) => (item.id === targetId ? { ...item, label: nextLabel } : item)),
|
|
331
|
+
);
|
|
332
|
+
setItems((current) => [nextItem, ...current]);
|
|
258
333
|
```
|
|
259
334
|
|
|
260
335
|
Farm validates the committed source and queued prepend chain, creates only the new prefix, inserts
|
|
261
336
|
it before the first existing row, and shifts the stored indexes used by delegated row events.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
337
|
+
When an adjacent filter or bounded slice runs first, the compiler also carries the exact survivor
|
|
338
|
+
positions into the prepend. The runtime removes only rejected rows, preserves every surviving DOM
|
|
339
|
+
node, and creates only the final prefix instead of rescanning and rebinding the whole result.
|
|
340
|
+
Multiple adjacent prepends share the same proof.
|
|
341
|
+
|
|
342
|
+
One or more immediately adjacent safe same-key `map()` setters may run after the prepend or between
|
|
343
|
+
the removal and prepend. A safe map may also run immediately before the removal. Farm carries every
|
|
344
|
+
survivor back to its committed row, validates the complete dense result and every final key, then
|
|
345
|
+
patches only changed survivors and creates the prefix from its final mapped values. A surviving DOM
|
|
346
|
+
node keeps its identity even when its item object changes.
|
|
347
|
+
|
|
348
|
+
Index-aware rows, collection-reading bindings or keys, React-owned or nested row structures,
|
|
349
|
+
middle insertion, direct replacement, a reordered structural chain, an unsupported or non-adjacent
|
|
350
|
+
map, duplicate final keys, reuse of any committed key in the new prefix, custom or sparse arrays,
|
|
351
|
+
and failed validation use complete keyed reconciliation before any DOM mutation. A mapped survivor
|
|
352
|
+
whose key changes also falls back before mutation. The compiler report exposes the emitted-site
|
|
353
|
+
count as `keyedArrayPrependHints`; no option or component API is added, and modules without the
|
|
354
|
+
accepted removal, prepend, and map chain omit its mapped runtime.
|
|
355
|
+
|
|
356
|
+
Native slices with compiler-safe bounds can carry their exact retained interval into the same
|
|
357
|
+
removal runtime:
|
|
268
358
|
|
|
269
359
|
```tsx
|
|
270
360
|
setItems((current) => current.slice(1_000));
|
|
271
361
|
setItems((current) => current.slice(0, -1_000));
|
|
272
362
|
setItems((current) => current.slice(2, 8));
|
|
363
|
+
setItems((current) => current.slice(trimCount));
|
|
364
|
+
setItems((current) => current.slice(visible.start, visible.end));
|
|
273
365
|
```
|
|
274
366
|
|
|
275
367
|
For compiler-owned host rows whose render and key do not read the row index, Farm validates the
|
|
276
368
|
committed source and queued slice chain, preserves every surviving DOM row, and removes only rows
|
|
277
369
|
outside the retained interval. A slice-only chain does not reread surviving keys, descriptors, or
|
|
278
|
-
bindings. One or two
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
370
|
+
bindings. One or two safe-integer literals or compiler-safe runtime expressions are required.
|
|
371
|
+
Identifiers, property reads, side-effect-free arithmetic and conditionals, and safe `Math` calls
|
|
372
|
+
are supported. Farm preserves native method lookup, argument evaluation, coercion, results, and
|
|
373
|
+
errors, then records metadata only when the evaluated bounds are already safe integers. Calls,
|
|
374
|
+
assignments, updates, unsafe evaluated bounds, block-bodied or chained updates, custom slice
|
|
375
|
+
methods, sparse or subclassed arrays, index-aware or collection-reading rows, React-owned
|
|
376
|
+
structures, and failed validation keep complete keyed reconciliation. No option or component is
|
|
377
|
+
added. The compiler report exposes the emitted-site count as `keyedArraySliceHints`.
|
|
283
378
|
|
|
284
379
|
A fixed-size feed can combine that retained tail with a new keyed suffix:
|
|
285
380
|
|
|
286
381
|
```tsx
|
|
287
382
|
setItems((current) => [...current.slice(1), nextItem]);
|
|
288
383
|
setItems((current) => [...current.slice(1_000), ...nextItems]);
|
|
384
|
+
|
|
385
|
+
const trimCount = pageSize * pagesToExpire;
|
|
386
|
+
setItems((current) => [...current.slice(trimCount), ...nextItems]);
|
|
289
387
|
```
|
|
290
388
|
|
|
291
|
-
Farm executes the ordinary native slice and array construction, then validates the
|
|
292
|
-
source, retained item identities, and incoming keys.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
389
|
+
Farm executes the ordinary native slice and array construction, then validates the complete hint
|
|
390
|
+
chain back to the committed source, retained item identities, and final incoming keys. Multiple
|
|
391
|
+
rolling setters queued before one compiler flush collapse into one cumulative prefix removal; rows
|
|
392
|
+
introduced by an earlier setter are created only if they remain in the final suffix. Farm removes
|
|
393
|
+
only the expired committed prefix, leaves the retained DOM rows in place, and creates only that
|
|
394
|
+
final incoming suffix. This form supports one
|
|
395
|
+
safe-integer literal or compiler-safe runtime slice bound and compiler-owned, index-independent
|
|
396
|
+
host rows. Identifiers, property reads, side-effect-free arithmetic and conditionals, and safe
|
|
397
|
+
`Math` calls are supported while preserving native lookup, evaluation, results, and errors.
|
|
398
|
+
Effectful expressions, reused keys, block-bodied updaters, custom slice behavior,
|
|
399
|
+
collection-reading or index-aware rows, nested or React-owned rows, mixed or unhinted queued chains,
|
|
400
|
+
unsafe or no-op evaluated bounds, and failed checks use complete keyed reconciliation. The
|
|
401
|
+
optional all-hint runtime is selected only for modules that emit this optimization. Reports expose
|
|
402
|
+
the site count as `keyedArrayRollingWindowHints`.
|
|
299
403
|
|
|
300
404
|
Native known-position updates can avoid a complete keyed scan too:
|
|
301
405
|
|
|
@@ -330,12 +434,13 @@ through one document fragment. It then removes only the replaced window. When th
|
|
|
330
434
|
has the same length and the same keys in the same order, Farm prepares every binding read and
|
|
331
435
|
changed DOM target across the complete window first, patches the existing rows in place, and
|
|
332
436
|
updates the stored row objects used by later events. That path creates no descriptors or DOM rows,
|
|
333
|
-
and preserves row identity, focus, and selection. A
|
|
334
|
-
from inside its own removed interval and
|
|
335
|
-
reused binding updates, new descriptors, binding snapshots, and detached rows before
|
|
336
|
-
write. It removes only retired rows, preserves each reused row, batches adjacent new
|
|
337
|
-
fragment, and applies LIS only to the reused part of that
|
|
338
|
-
rows needed by the local permutation. Rows outside the
|
|
437
|
+
and preserves row identity, focus, and selection. A window may instead grow or shrink while it
|
|
438
|
+
reorders keys from inside its own removed interval and mixes them with globally fresh keys. Farm
|
|
439
|
+
prepares all reused binding updates, new descriptors, binding snapshots, and detached rows before
|
|
440
|
+
the first DOM write. It removes only retired rows, preserves each reused row, batches adjacent new
|
|
441
|
+
rows in a fragment, updates shifted suffix indexes, and applies LIS only to the reused part of that
|
|
442
|
+
interval so it moves the fewest connected rows needed by the local permutation. Rows outside the
|
|
443
|
+
interval are not rerendered or rebound.
|
|
339
444
|
Multiple length-preserving same-key windows
|
|
340
445
|
queued before one compiler flush compose into one atomic refresh. Fixed-length queued windows may
|
|
341
446
|
mix same-key refreshes with globally new final keys, and both disjoint and overlapping windows are
|
|
@@ -350,9 +455,8 @@ collection-reading rows, custom methods, position expressions with user calls or
|
|
|
350
455
|
runtime positions that are not safe integers, dynamic, zero, negative, or fractional removal
|
|
351
456
|
counts, other `toSpliced()` forms, block-bodied updaters, unsafe incoming expressions, queued
|
|
352
457
|
chains containing a structural window or unhinted intermediate update, existing keys moved from
|
|
353
|
-
outside the removed interval,
|
|
354
|
-
|
|
355
|
-
path mutates the DOM.
|
|
458
|
+
outside the removed interval, duplicate final keys, nested or React-owned rows, and failed checks
|
|
459
|
+
use complete keyed reconciliation before the fast path mutates the DOM.
|
|
356
460
|
Reports expose the site count as `keyedArrayPositionHints`. Batch insertion and exact-window
|
|
357
461
|
replacement use progressively separate optional runtime capabilities, so existing single-position
|
|
358
462
|
and batch-only bundles do not retain window validation or replacement code.
|
|
@@ -364,14 +468,208 @@ setItems((current) => current.toReversed());
|
|
|
364
468
|
```
|
|
365
469
|
|
|
366
470
|
Farm preserves the method lookup, call result, and errors, then records metadata only for the native
|
|
367
|
-
method on
|
|
368
|
-
identity before moving the existing keyed elements
|
|
369
|
-
It does not reread row keys, descriptors, or bindings
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
471
|
+
method on an ordinary array whose chain starts at the committed collection. One direct reverse
|
|
472
|
+
verifies equal lengths and every reversed item identity before moving the existing keyed elements
|
|
473
|
+
with the minimum `n - 1` connected DOM moves. It does not reread row keys, descriptors, or bindings
|
|
474
|
+
and does not run the generic LIS calculation. Consecutive concise native `toReversed()` and
|
|
475
|
+
`toSorted()` setters queued before one flush compose into one final validated permutation. The
|
|
476
|
+
runtime skips intermediate DOM states. When the proven chain contains only reversals, Farm tracks
|
|
477
|
+
their parity relative to the committed rows: an even count validates exact identity and performs
|
|
478
|
+
no DOM moves, source-item map construction, or LIS; an odd count validates exact reverse and uses
|
|
479
|
+
the same minimum `n - 1` moves as one reverse. A chain containing a sort keeps the general final
|
|
480
|
+
permutation and uses LIS once. A single concise updater may also chain two or more native reorder
|
|
481
|
+
operations:
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
setItems((current) => current.toSorted((left, right) => left.rank - right.rank).toReversed());
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Farm evaluates every lookup and call in JavaScript order, carries the same committed token through
|
|
488
|
+
the pipeline, and reconciles only its final result. Index-aware or collection-reading rows,
|
|
489
|
+
arguments to `toReversed()`, referenced comparators, computed methods, block-bodied updaters, custom
|
|
490
|
+
methods, sparse or subclassed behavior, structural calls after reordering, an unhinted intermediate
|
|
491
|
+
update, nested or React-owned rows, and failed checks keep complete keyed reconciliation.
|
|
492
|
+
|
|
493
|
+
A compiler-safe `filter()` or bounded `slice()` prefix may precede the native reorder suffix:
|
|
494
|
+
|
|
495
|
+
```tsx
|
|
496
|
+
setItems((current) =>
|
|
497
|
+
current
|
|
498
|
+
.filter((item) => item.visible)
|
|
499
|
+
.toSorted((left, right) => left.rank - right.rank)
|
|
500
|
+
.toReversed(),
|
|
501
|
+
);
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
Farm validates the complete survivor set and final order before the first DOM write, removes only
|
|
505
|
+
rejected rows, and applies LIS once to the survivors. Concise filter/slice and reorder setters also
|
|
506
|
+
compose when queued in that order before one flush. Unsupported calls or a structural call after a
|
|
507
|
+
reorder fall back. Reports count each compiled step in the existing filter, slice, sort, or reverse
|
|
508
|
+
counter, and modules without those operations omit their optional runtimes. Farm does not polyfill
|
|
509
|
+
`Array.prototype.toReversed`.
|
|
510
|
+
|
|
511
|
+
A compiler-safe same-key map may also appear before or after native reorder steps:
|
|
512
|
+
|
|
513
|
+
```tsx
|
|
514
|
+
setItems((current) =>
|
|
515
|
+
current
|
|
516
|
+
.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item))
|
|
517
|
+
.map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item))
|
|
518
|
+
.toSorted((left, right) => left.rank - right.rank),
|
|
519
|
+
);
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Farm executes every native map and reorder call normally. For consecutive accepted maps, it checks
|
|
523
|
+
each native call but compares only the input and final result of that map segment, avoiding a full
|
|
524
|
+
lineage scan for every intermediate array. It verifies the committed token, dense-array shape, a
|
|
525
|
+
one-to-one source-item match, and every final replacement key before touching the DOM, then runs one
|
|
526
|
+
LIS and patches each changed row once. Unchanged rows need no second key or binding read. Queued
|
|
527
|
+
supported map-and-reorder setters compose against the same committed rows. Every accepted map
|
|
528
|
+
requires an inline synchronous conditional mapper that returns either the original item or an
|
|
529
|
+
object-spread replacement, plus index-independent compiler-owned host rows. Changed keys,
|
|
530
|
+
referenced or block-bodied callbacks, unconditional replacements, `thisArg`, structural calls in
|
|
531
|
+
the same chain, computed or custom methods, sparse or subclassed arrays, collection-reading
|
|
532
|
+
bindings, nested or React-owned rows, and failed checks use complete keyed reconciliation. Reports
|
|
533
|
+
use the existing map, sort, and reorder hint counters, and unrelated modules do not retain this
|
|
534
|
+
optional runtime.
|
|
535
|
+
|
|
536
|
+
The maps may also finish the concise pipeline:
|
|
537
|
+
|
|
538
|
+
```tsx
|
|
539
|
+
setItems((current) =>
|
|
540
|
+
current
|
|
541
|
+
.toReversed()
|
|
542
|
+
.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item))
|
|
543
|
+
.map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item)),
|
|
544
|
+
);
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Farm retains the preceding reorder instead of treating the mapped result as an unhinted snapshot.
|
|
548
|
+
An exact reverse uses the minimum-move reverse path without a general source-item lookup or LIS. A
|
|
549
|
+
sort remains an ambiguous permutation, so Farm performs one validated source lookup and LIS pass.
|
|
550
|
+
Safe maps on both sides of a reorder flatten their replacements back to the same committed rows.
|
|
551
|
+
|
|
552
|
+
Safe maps may also run before, between, or after index-independent `filter` or `slice` steps in one
|
|
553
|
+
concise functional setter. A final native reorder is optional:
|
|
554
|
+
|
|
555
|
+
```tsx
|
|
556
|
+
setItems((current) =>
|
|
557
|
+
current
|
|
558
|
+
.filter((item) => item.visible)
|
|
559
|
+
.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item))
|
|
560
|
+
.slice(0, limit)
|
|
561
|
+
.toSorted((left, right) => left.rank - right.rank),
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
setItems((current) =>
|
|
565
|
+
current
|
|
566
|
+
.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item))
|
|
567
|
+
.filter((item) => item.visible)
|
|
568
|
+
.slice(0, limit),
|
|
569
|
+
);
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
The structural helpers retain both the committed survivor indices and mapped-item sources across
|
|
573
|
+
each accepted step. The final commit removes rejected rows, performs LIS moves only when a reorder
|
|
574
|
+
needs them, and patches bindings only for changed survivors. Every native callback and method still
|
|
575
|
+
runs in JavaScript order.
|
|
576
|
+
|
|
577
|
+
The structural and map steps may also be split across immediately adjacent setter calls in either
|
|
578
|
+
order:
|
|
579
|
+
|
|
580
|
+
```tsx
|
|
581
|
+
// Map, then structural work.
|
|
582
|
+
setItems((current) =>
|
|
583
|
+
current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)),
|
|
584
|
+
);
|
|
585
|
+
setItems((current) => current.filter((item) => item.visible));
|
|
586
|
+
setItems((current) => current.slice(0, limit));
|
|
587
|
+
|
|
588
|
+
// Structural work, then maps.
|
|
589
|
+
setItems((current) => current.filter((item) => item.visible));
|
|
590
|
+
setItems((current) => current.slice(0, limit));
|
|
591
|
+
setItems((current) =>
|
|
592
|
+
current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)),
|
|
593
|
+
);
|
|
594
|
+
setItems((current) =>
|
|
595
|
+
current.map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item)),
|
|
596
|
+
);
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
Farm links only consecutive calls to the same setter. A leading map retains its first committed
|
|
600
|
+
source through later structural work; a terminal map consumes the saved survivor lineage from the
|
|
601
|
+
preceding filter or slice. Multiple safe steps in either segment may share the proof, and mixed
|
|
602
|
+
map/structural/map sequences compose. Every result and key is validated before the queued commit
|
|
603
|
+
touches the DOM. An intervening statement, another setter, an unsafe callback, a changed key, or
|
|
604
|
+
failed runtime validation keeps complete keyed reconciliation.
|
|
605
|
+
|
|
606
|
+
The survivor proof may continue through adjacent native reorder setters:
|
|
607
|
+
|
|
608
|
+
```tsx
|
|
609
|
+
setItems((current) => current.filter((item) => item.visible));
|
|
610
|
+
setItems((current) =>
|
|
611
|
+
current.map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item)),
|
|
612
|
+
);
|
|
613
|
+
setItems((current) => current.toSorted((left, right) => left.rank - right.rank));
|
|
614
|
+
setItems((current) => current.toReversed());
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
Farm retains the committed survivor positions and same-key replacements until the final queued
|
|
618
|
+
value. The commit removes rejected rows, patches changed survivors, and applies the final order in
|
|
619
|
+
one validated DOM transaction. Exact reverses use the minimum-move reverse path; an arbitrary sort
|
|
620
|
+
uses one source lookup and LIS. Non-adjacent calls, another setter, unsupported syntax, or a failed
|
|
621
|
+
shape/key check keep complete reconciliation.
|
|
622
|
+
|
|
623
|
+
The reorder and safe maps may also be adjacent setter calls in the same synchronous block:
|
|
624
|
+
|
|
625
|
+
```tsx
|
|
626
|
+
setItems((current) => current.toReversed());
|
|
627
|
+
setItems((current) =>
|
|
628
|
+
current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)),
|
|
629
|
+
);
|
|
630
|
+
setItems((current) =>
|
|
631
|
+
current.map((item) => (item.id === editedId ? { ...item, rank: nextRank } : item)),
|
|
632
|
+
);
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
The compiler links only consecutive calls to the same setter. It keeps the reorder token through
|
|
636
|
+
each accepted native map, then validates row keys before applying the same exact-reverse or
|
|
637
|
+
sort-permutation path at commit. Adjacent compiler-safe filter or slice work switches to the
|
|
638
|
+
structural proof above. An intervening statement, another setter, an unsupported map, or a changed
|
|
639
|
+
key keeps the ordinary complete fallback. Standalone map-only components do not retain the
|
|
640
|
+
map-and-reorder runtime.
|
|
641
|
+
|
|
642
|
+
The mirror order is supported as well: two or more adjacent concise same-key map setters may be
|
|
643
|
+
followed by a concise native reverse or sort setter. Farm records their replacement lineage from
|
|
644
|
+
the committed collection and carries it into the final reorder. Only adjacent calls to the same
|
|
645
|
+
setter are linked; intervening work and unsupported updates retain complete reconciliation.
|
|
646
|
+
|
|
647
|
+
Mapped lineage also continues through multiple adjacent native reorder setters. For example,
|
|
648
|
+
`map -> reverse -> sort -> reverse` keeps the same committed-row proof until the final value, then
|
|
649
|
+
performs one validated keyed reconciliation. A concise `map().toReversed()` pipeline may start the
|
|
650
|
+
same chain. Adjacent compiler-safe filter or slice work changes it into a structural chain while
|
|
651
|
+
retaining the committed source. Every updater and native method still executes in order; another
|
|
652
|
+
setter, intervening work, or an unsupported method ends the chain and preserves the complete
|
|
653
|
+
fallback.
|
|
654
|
+
|
|
655
|
+
A reverse before the safe map pipeline may be queued in a separate setter:
|
|
656
|
+
|
|
657
|
+
```tsx
|
|
658
|
+
setItems((current) => current.toReversed());
|
|
659
|
+
setItems((current) =>
|
|
660
|
+
current.map((item) => (item.id === editedId ? { ...item, label: nextLabel } : item)).toReversed(),
|
|
661
|
+
);
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
A direct `toReversed()` suffix is more specific than an arbitrary permutation. When all preceding
|
|
665
|
+
maps retain committed row order, Farm validates the mirrored source-to-result relation and every
|
|
666
|
+
changed binding before touching the DOM, then performs the minimum-move reverse without building a
|
|
667
|
+
second item lookup map or running LIS. Additional exact reversals toggle that proof between reverse
|
|
668
|
+
and identity. Therefore two reversals after safe maps patch changed rows in committed order without
|
|
669
|
+
moving DOM nodes or creating the generic source map; three use the exact reverse path. Exact reverse
|
|
670
|
+
metadata also composes across queued setters: a queued reverse followed by safe maps and another
|
|
671
|
+
reverse retains exact identity, so only changed row bindings are patched. A preceding sort or any
|
|
672
|
+
ambiguous order keeps the general permutation path.
|
|
375
673
|
|
|
376
674
|
A direct native immutable sort can use the same optional reorder runtime:
|
|
377
675
|
|
|
@@ -382,15 +680,18 @@ setLabels((current) => current.toSorted());
|
|
|
382
680
|
|
|
383
681
|
Farm recognizes a concise functional setter with no comparator or an inline synchronous comparator
|
|
384
682
|
from the compiler-safe expression subset. It preserves the original method lookup, comparator,
|
|
385
|
-
stable native result, and errors. After the native sort runs, the runtime validates
|
|
386
|
-
|
|
387
|
-
uses LIS to move only `n - LIS` keyed DOM nodes
|
|
388
|
-
bindings.
|
|
683
|
+
stable native result, and errors. After the native sort runs, the runtime validates an ordinary
|
|
684
|
+
dense array whose reorder chain starts at the committed collection, equal lengths, and a unique
|
|
685
|
+
one-to-one item-identity permutation. It then uses LIS to move only `n - LIS` keyed DOM nodes
|
|
686
|
+
without rereading row keys, descriptors, or bindings. Multiple concise native sorts and reverses
|
|
687
|
+
queued in one flush, or chained in one concise updater, share the original committed token and
|
|
688
|
+
reconcile only their final permutation. The native sorting work itself is unchanged.
|
|
389
689
|
|
|
390
690
|
Index-aware or collection-reading rows, referenced comparators, block-bodied updaters, computed or
|
|
391
|
-
chained calls, custom methods, sparse or subclassed arrays, duplicate item identities,
|
|
392
|
-
|
|
393
|
-
|
|
691
|
+
unsupported chained calls, custom methods, sparse or subclassed arrays, duplicate item identities,
|
|
692
|
+
unhinted intermediate updates, structural calls after reordering, nested or React-owned rows, and
|
|
693
|
+
failed checks keep complete keyed reconciliation. A compiler-safe filter/slice prefix is supported.
|
|
694
|
+
Reports count each compiled sort step as a `keyedArraySortHints` entry; sort shares the optional
|
|
394
695
|
reorder runtime, and Farm does not polyfill `Array.prototype.toSorted`.
|
|
395
696
|
|
|
396
697
|
Concise immutable filters on a direct keyed array can carry removal positions into the same
|
|
@@ -690,19 +991,21 @@ reasons aggregated by count. Its summary and per-module `optimizations` also rep
|
|
|
690
991
|
`keyedMapLookupTargets`, the number of native-Map keyed lookup bindings;
|
|
691
992
|
`keyedMembershipTargets`, the number of native-Set membership bindings;
|
|
692
993
|
`keyedCollectionUpdateHints`, the number of compiler-proven native Set/Map mutation sites; and
|
|
693
|
-
`keyedMapUpdateHints`, the number of compiler-proven
|
|
994
|
+
`keyedMapUpdateHints`, the number of compiler-proven keyed `map()` update calls; and
|
|
694
995
|
`keyedArrayAppendHints`, the number of compiler-proven direct keyed-array append sites; and
|
|
695
|
-
`keyedArrayFilterHints`, the number of compiler-proven
|
|
996
|
+
`keyedArrayFilterHints`, the number of compiler-proven keyed-array filter sites, including supported
|
|
997
|
+
structural reorder pipelines; and
|
|
696
998
|
`keyedArrayPrependHints`, the number of compiler-proven direct keyed-array prepend sites; and
|
|
697
999
|
`keyedArrayPositionHints`, the number of compiler-proven native exact-position insertion, single or
|
|
698
1000
|
contiguous-range removal, single-row replacement, or exact-window replacement sites, including
|
|
699
1001
|
guarded compiler-safe runtime positions;
|
|
700
1002
|
and
|
|
701
|
-
`keyedArrayReorderHints`, the number of compiler-proven
|
|
702
|
-
`keyedArraySortHints`, the number of compiler-proven
|
|
1003
|
+
`keyedArrayReorderHints`, the number of compiler-proven native keyed-array reverse steps; and
|
|
1004
|
+
`keyedArraySortHints`, the number of compiler-proven native keyed-array sort steps; and
|
|
703
1005
|
`keyedArrayRollingWindowHints`, the number of compiler-proven retained-tail plus incoming-suffix
|
|
704
1006
|
sites; and
|
|
705
|
-
`keyedArraySliceHints`, the number of compiler-proven
|
|
1007
|
+
`keyedArraySliceHints`, the number of compiler-proven keyed-array slice sites with literal or guarded
|
|
1008
|
+
compiler-safe runtime bounds, including supported structural reorder pipelines. A custom
|
|
706
1009
|
project-relative `reportFile` also enables reporting.
|
|
707
1010
|
|
|
708
1011
|
The runtime test compares the same counter interaction on both paths: ordinary React performs a
|
|
@@ -711,9 +1014,9 @@ commit and updates its two bindings directly. This is a deterministic structural
|
|
|
711
1014
|
assertion; it is not presented as a cross-machine timing benchmark.
|
|
712
1015
|
|
|
713
1016
|
Application and prototype calls, dynamic style objects, handlers outside JSX events, nested,
|
|
714
|
-
computed, and rest props patterns, async handlers, unkeyed or index-keyed lists,
|
|
715
|
-
unsupported conditional roots, effects, and more advanced hook support intentionally stay
|
|
716
|
-
in this release. Compiler-owned keyed rows support either one dedicated host-only map/`List` or
|
|
1017
|
+
computed, and rest props patterns, async handlers, unkeyed or index-keyed lists, unsupported map
|
|
1018
|
+
chains, unsupported conditional roots, effects, and more advanced hook support intentionally stay
|
|
1019
|
+
on React in this release. Compiler-owned keyed rows support either one dedicated host-only map/`List` or
|
|
717
1020
|
one or more non-interactive ranges in a nested or component-root host container. A dedicated
|
|
718
1021
|
non-interactive row may also contain multiple recursive logical or ternary host branches beside
|
|
719
1022
|
stateful static host siblings, plus recursively nested non-interactive keyed ranges scoped by every
|