@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/README.md CHANGED
@@ -32,6 +32,19 @@ binding. The runtime uses direct subscription indexes rather than proxies or a c
32
32
  scan. Use `compiler: { reactivity: "static" }` to retain only the complete build-time dependency
33
33
  lists for comparison or diagnosis.
34
34
 
35
+ Compiler output also selects its runtime capabilities at build time. A direct counter imports only
36
+ the cell scheduler and direct-binding core; conditionals, keyed rows and LIS, nested host ranges,
37
+ and component islands are retained only when a compiled component in that module needs them. Plain
38
+ keyed rows omit the larger recursive-host and row-conditional extensions. This selection is
39
+ automatic and does not add a configuration option or asynchronous runtime loading. The legacy
40
+ `createCompiledComponent` export remains a complete compatibility entry for hand-authored
41
+ definitions, while generated code uses the tree-shakable feature entry.
42
+
43
+ The persisted production-size fixtures and regression gate live in
44
+ [`RUNTIME_SIZE_RESULTS.md`](./RUNTIME_SIZE_RESULTS.md). The recorded direct-only runtime premium is
45
+ 73.6% smaller than the complete compatibility runtime premium; the feature-heavy keyed benchmark
46
+ application removes 6,185 gzip bytes from its previous compiler-on build.
47
+
35
48
  For selective adoption, use annotation mode:
36
49
 
37
50
  ```ts
@@ -149,6 +162,190 @@ Later list updates patch surviving rows by key, create or remove only the change
149
162
  longest increasing subsequence (LIS) to minimize DOM moves during a reorder. The outer user
150
163
  component and the list callback do not rerun for those compiler-cell updates.
151
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(500, 0, nextItem));
304
+ setItems((current) => current.with(500, replacement));
305
+ ```
306
+
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.
316
+
317
+ A direct native reverse can carry its complete permutation to a separate optional runtime:
318
+
319
+ ```tsx
320
+ setItems((current) => current.toReversed());
321
+ ```
322
+
323
+ Farm preserves the method lookup, call result, and errors, then records metadata only for the native
324
+ method on a committed ordinary array. The runtime verifies equal lengths and every reversed item
325
+ identity before moving the existing keyed elements with the minimum `n - 1` connected DOM moves.
326
+ It does not reread row keys, descriptors, or bindings and does not run the generic LIS calculation.
327
+ Index-aware or collection-reading rows, arguments, computed or chained calls, block-bodied
328
+ updaters, custom methods, sparse or subclassed behavior, queued uncommitted reversals, nested or
329
+ React-owned rows, and failed checks keep complete keyed reconciliation. Reports expose the site
330
+ count as `keyedArrayReorderHints`, and modules without one omit the reorder runtime. Farm does not
331
+ polyfill `Array.prototype.toReversed`.
332
+
333
+ Concise immutable filters on a direct keyed array can carry removal positions into the same
334
+ optional runtime:
335
+
336
+ ```tsx
337
+ setItems((current) => current.filter((item) => item.id !== removedId));
338
+ ```
339
+
340
+ For compiler-owned host rows whose render and key do not observe the row index, Farm validates the
341
+ native filter chain, every surviving item identity, and every surviving key before removing only
342
+ the rejected DOM rows. It does not recreate descriptors or reread bindings for unchanged rows,
343
+ and queued filters compose before one compiler flush. Index-aware rows or predicates,
344
+ block-bodied updates, custom filter methods, sparse or subclassed arrays, collection-reading
345
+ bindings or keys, React-owned row structures, mixed dirty dependencies, and failed validation use
346
+ complete keyed reconciliation. No option or new component is required. The compiler report exposes
347
+ the emitted-site count as `keyedArrayFilterHints`.
348
+
152
349
  An otherwise eligible host row may also contain an inline synchronous React event:
153
350
 
154
351
  ```tsx
@@ -425,7 +622,22 @@ compiler: {
425
622
 
426
623
  The default path is `.farm/react-compiler.json`. The report covers the production browser graph and
427
624
  contains project-relative module paths, compiled component names, fallback details, and fallback
428
- reasons aggregated by count. A custom project-relative `reportFile` also enables reporting.
625
+ reasons aggregated by count. Its summary and per-module `optimizations` also report
626
+ `keyedIdentityTargets`, the number of scalar key-directed row bindings;
627
+ `keyedMapLookupTargets`, the number of native-Map keyed lookup bindings;
628
+ `keyedMembershipTargets`, the number of native-Set membership bindings;
629
+ `keyedCollectionUpdateHints`, the number of compiler-proven native Set/Map mutation sites; and
630
+ `keyedMapUpdateHints`, the number of compiler-proven direct keyed `map()` update sites; and
631
+ `keyedArrayAppendHints`, the number of compiler-proven direct keyed-array append sites; and
632
+ `keyedArrayFilterHints`, the number of compiler-proven direct keyed-array filter sites; and
633
+ `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
636
+ `keyedArrayReorderHints`, the number of compiler-proven direct native keyed-array reverse sites; and
637
+ `keyedArrayRollingWindowHints`, the number of compiler-proven retained-tail plus incoming-suffix
638
+ sites; and
639
+ `keyedArraySliceHints`, the number of compiler-proven direct keyed-array slice sites. A custom
640
+ project-relative `reportFile` also enables reporting.
429
641
 
430
642
  The runtime test compares the same counter interaction on both paths: ordinary React performs a
431
643
  second component render and commit, while the compiled component remains at one render and one