@farm.js/react 0.1.0-beta.5 → 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
@@ -162,6 +162,216 @@ Later list updates patch surviving rows by key, create or remove only the change
162
162
  longest increasing subsequence (LIS) to minimize DOM moves during a reorder. The outer user
163
163
  component and the list callback do not rerun for those compiler-cell updates.
164
164
 
165
+ When a separate state cell or primitive prop is compared with the exact row-key expression using
166
+ `===` or `!==`, the compiler emits a key-directed binding proof. A selection change such as
167
+ `item.id === selectedId` then looks up only the previously selected and newly selected row
168
+ instances. Class, attribute, style, and leaf-text bindings on those rows update without evaluating
169
+ the other keyed instances. The proof requires one reactive target used only in strict comparisons;
170
+ mixed structural dependencies, ambiguous expressions, non-primitive runtime targets, React-owned
171
+ rows, and unsupported shapes keep the complete scan or React fallback. No option is required, and
172
+ the compiler report exposes the emitted binding count as `keyedIdentityTargets`.
173
+
174
+ For multi-selection, an exact local-state expression such as `markedIds.has(item.id)` receives a
175
+ separate Set-membership proof. The runtime compares the previous and next native `Set` snapshots and
176
+ patches only row keys in their symmetric difference. It accepts ordinary native sets containing
177
+ primitive keys. Different fields, extra reactive dependencies, Set subclasses or proxies, object
178
+ members, customized `has` behavior, React-owned rows, nested blocks, and structural dependencies
179
+ stay on the existing complete or React-owned paths. The compiler report exposes this count as
180
+ `keyedMembershipTargets`.
181
+
182
+ For per-row data, an exact local-state expression such as `statusById.get(item.id)` receives a
183
+ separate Map-lookup proof. The runtime compares the previous and next native `Map` snapshots and
184
+ patches only row keys whose mapped primitive value changed. It accepts ordinary native maps with
185
+ primitive keys and primitive or nullish values. Different fields, extra dependencies, Map
186
+ subclasses or proxies, object values, customized `get` behavior, React-owned rows, nested blocks,
187
+ and structural dependencies keep the existing complete or React-owned paths. The compiler report
188
+ exposes this count as `keyedMapLookupTargets`.
189
+
190
+ When a targeted Set or Map is compiler-owned, Farm can also carry the exact mutated keys from a
191
+ proven immutable functional update:
192
+
193
+ ```tsx
194
+ setMarkedIds((current) => {
195
+ const next = new Set(current);
196
+ if (next.has(id)) next.delete(id);
197
+ else next.add(id);
198
+ return next;
199
+ });
200
+
201
+ setStatusById((current) => new Map(current).set(id, "ready"));
202
+ ```
203
+
204
+ The application still creates its normal immutable collection. The generated update records only
205
+ the native `add`, `delete`, or `set` calls that execute, and the runtime validates those keys
206
+ against the previous committed collection. It can then update the relevant keyed rows without
207
+ iterating every old and new Set member or Map entry first. Queued functional updates compose, and
208
+ the runtime periodically compacts its persistent snapshot so a long update chain stays bounded.
209
+
210
+ This proof is deliberately conservative. The state must start from a compiler-owned native
211
+ `Set`/`Map`; every setter use must be a fresh same-kind replacement or a recognized immutable
212
+ updater; and the collection, updater parameter, and cloned draft must not escape or be mutated
213
+ through unknown code. Shadowed constructors, custom collections, object keys, object Map values,
214
+ direct state mutation, aliased drafts, or a failed runtime check use the existing snapshot or
215
+ React-owned path before compiled bindings run. No option or syntax is added. The compiler report
216
+ counts emitted native mutation sites as `keyedCollectionUpdateHints`.
217
+
218
+ For a direct keyed `useState` collection, the compiler also recognizes conservative same-order
219
+ updates such as:
220
+
221
+ ```tsx
222
+ setItems((current) =>
223
+ current.map((item) => (item.id === targetId ? { ...item, selected: !item.selected } : item)),
224
+ );
225
+ ```
226
+
227
+ The generated native `map()` records which indexes returned new item identities. Farm then
228
+ validates that length, keys, and positions are unchanged and patches only those row instances. The
229
+ user's `map()` remains O(n); this removes the keyed runtime's second full key-and-binding scan.
230
+ Queued hints compose. Key changes, structural edits, relevant mixed dependencies, and failed runtime
231
+ checks use the existing complete reconciliation and LIS path. Non-functional setters, derived
232
+ collections, block-bodied or mutating mappers, and other unproven forms are simply not hinted. No
233
+ new option is required. A compiler report exposes the emitted-site count as
234
+ `keyedMapUpdateHints`. The separate hint runtime capability is imported only when that count is
235
+ nonzero, so direct-only and ordinary keyed builds do not retain it.
236
+
237
+ The same optional runtime recognizes conservative immutable appends on a direct keyed array:
238
+
239
+ ```tsx
240
+ setItems((current) => [...current, nextItem]);
241
+ setItems((current) => [...current, ...nextItems]);
242
+ ```
243
+
244
+ Because every existing item keeps its key and index, Farm validates the committed source and the
245
+ queued append chain, reads keys and descriptors only for the appended suffix, and mounts that
246
+ suffix in one fragment. Existing row DOM is left untouched. The concise functional updater and
247
+ native arrays are required; middle insertion, removal, direct replacement, duplicate
248
+ keys, rows that read the collection itself, and failed runtime checks use complete keyed
249
+ reconciliation. A key that reads the collection prevents hint emission entirely. The compiler
250
+ report exposes the emitted-site count as
251
+ `keyedArrayAppendHints`.
252
+
253
+ The mirror-image prepend form is supported when the keyed row and key do not read the row index:
254
+
255
+ ```tsx
256
+ setItems((current) => [nextItem, ...current]);
257
+ setItems((current) => [...nextItems, ...current]);
258
+ ```
259
+
260
+ Farm validates the committed source and queued prepend chain, creates only the new prefix, inserts
261
+ it before the first existing row, and shifts the stored indexes used by delegated row events.
262
+ Existing row DOM and bindings stay in place. Index-aware rows, collection-reading bindings or
263
+ keys, React-owned row structures, middle insertion, direct replacement, duplicate keys, custom or
264
+ sparse arrays, and failed validation use complete keyed reconciliation. The compiler report
265
+ exposes the emitted-site count as `keyedArrayPrependHints`.
266
+
267
+ Literal native slices can carry their exact retained interval into the same removal runtime:
268
+
269
+ ```tsx
270
+ setItems((current) => current.slice(1_000));
271
+ setItems((current) => current.slice(0, -1_000));
272
+ setItems((current) => current.slice(2, 8));
273
+ ```
274
+
275
+ For compiler-owned host rows whose render and key do not read the row index, Farm validates the
276
+ committed source and queued slice chain, preserves every surviving DOM row, and removes only rows
277
+ outside the retained interval. A slice-only chain does not reread surviving keys, descriptors, or
278
+ bindings. One or two build-time safe-integer bounds are required. Runtime bounds, block-bodied or
279
+ chained updates, custom slice methods, sparse or subclassed arrays, index-aware or
280
+ collection-reading rows, React-owned structures, and failed validation keep complete keyed
281
+ reconciliation. No option or component is added. The compiler report exposes the emitted-site
282
+ count as `keyedArraySliceHints`.
283
+
284
+ A fixed-size feed can combine that retained tail with a new keyed suffix:
285
+
286
+ ```tsx
287
+ setItems((current) => [...current.slice(1), nextItem]);
288
+ setItems((current) => [...current.slice(1_000), ...nextItems]);
289
+ ```
290
+
291
+ Farm executes the ordinary native slice and array construction, then validates the committed
292
+ source, retained item identities, and incoming keys. It removes only the expired prefix, leaves
293
+ the retained DOM rows in place, and creates only the incoming suffix. This first form supports one
294
+ build-time safe-integer slice bound and compiler-owned, index-independent host rows. Reused keys,
295
+ block-bodied updaters, custom slice behavior, collection-reading or index-aware rows, nested or
296
+ React-owned rows, queued uncommitted windows, and failed checks use complete keyed reconciliation.
297
+ The optional all-hint runtime is selected only for modules that emit this optimization. Reports
298
+ expose the site count as `keyedArrayRollingWindowHints`.
299
+
300
+ Native known-position updates can avoid a complete keyed scan too:
301
+
302
+ ```tsx
303
+ setItems((current) => current.toSpliced(selectedIndex, 0, nextItem));
304
+ setItems((current) => current.toSpliced(selectedIndex, 1));
305
+ setItems((current) => current.with(selectedIndex, replacement));
306
+ ```
307
+
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.
322
+
323
+ A direct native reverse can carry its complete permutation to a separate optional runtime:
324
+
325
+ ```tsx
326
+ setItems((current) => current.toReversed());
327
+ ```
328
+
329
+ Farm preserves the method lookup, call result, and errors, then records metadata only for the native
330
+ method on a committed ordinary array. The runtime verifies equal lengths and every reversed item
331
+ identity before moving the existing keyed elements with the minimum `n - 1` connected DOM moves.
332
+ It does not reread row keys, descriptors, or bindings and does not run the generic LIS calculation.
333
+ Index-aware or collection-reading rows, arguments, computed or chained calls, block-bodied
334
+ updaters, custom methods, sparse or subclassed behavior, queued uncommitted reversals, nested or
335
+ React-owned rows, and failed checks keep complete keyed reconciliation. Reports expose the site
336
+ count as `keyedArrayReorderHints`, and modules without one omit the reorder runtime. Farm does not
337
+ polyfill `Array.prototype.toReversed`.
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
+
359
+ Concise immutable filters on a direct keyed array can carry removal positions into the same
360
+ optional runtime:
361
+
362
+ ```tsx
363
+ setItems((current) => current.filter((item) => item.id !== removedId));
364
+ ```
365
+
366
+ For compiler-owned host rows whose render and key do not observe the row index, Farm validates the
367
+ native filter chain, every surviving item identity, and every surviving key before removing only
368
+ the rejected DOM rows. It does not recreate descriptors or reread bindings for unchanged rows,
369
+ and queued filters compose before one compiler flush. Index-aware rows or predicates,
370
+ block-bodied updates, custom filter methods, sparse or subclassed arrays, collection-reading
371
+ bindings or keys, React-owned row structures, mixed dirty dependencies, and failed validation use
372
+ complete keyed reconciliation. No option or new component is required. The compiler report exposes
373
+ the emitted-site count as `keyedArrayFilterHints`.
374
+
165
375
  An otherwise eligible host row may also contain an inline synchronous React event:
166
376
 
167
377
  ```tsx
@@ -438,7 +648,23 @@ compiler: {
438
648
 
439
649
  The default path is `.farm/react-compiler.json`. The report covers the production browser graph and
440
650
  contains project-relative module paths, compiled component names, fallback details, and fallback
441
- reasons aggregated by count. A custom project-relative `reportFile` also enables reporting.
651
+ reasons aggregated by count. Its summary and per-module `optimizations` also report
652
+ `keyedIdentityTargets`, the number of scalar key-directed row bindings;
653
+ `keyedMapLookupTargets`, the number of native-Map keyed lookup bindings;
654
+ `keyedMembershipTargets`, the number of native-Set membership bindings;
655
+ `keyedCollectionUpdateHints`, the number of compiler-proven native Set/Map mutation sites; and
656
+ `keyedMapUpdateHints`, the number of compiler-proven direct keyed `map()` update sites; and
657
+ `keyedArrayAppendHints`, the number of compiler-proven direct keyed-array append sites; and
658
+ `keyedArrayFilterHints`, the number of compiler-proven direct keyed-array filter sites; and
659
+ `keyedArrayPrependHints`, the number of compiler-proven direct keyed-array prepend 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
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
664
+ `keyedArrayRollingWindowHints`, the number of compiler-proven retained-tail plus incoming-suffix
665
+ sites; and
666
+ `keyedArraySliceHints`, the number of compiler-proven direct keyed-array slice sites. A custom
667
+ project-relative `reportFile` also enables reporting.
442
668
 
443
669
  The runtime test compares the same counter interaction on both paths: ordinary React performs a
444
670
  second component render and commit, while the compiled component remains at one render and one