@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 +227 -1
- package/dist/compiler-runtime.cjs +1830 -19
- package/dist/compiler-runtime.cjs.map +1 -1
- package/dist/compiler-runtime.d.mts +84 -1
- package/dist/compiler-runtime.d.ts +84 -1
- package/dist/compiler-runtime.mjs +1788 -20
- package/dist/compiler-runtime.mjs.map +1 -1
- package/dist/vite.cjs +2026 -54
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +2026 -54
- package/dist/vite.mjs.map +1 -1
- package/package.json +4 -4
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.
|
|
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
|