@llui/dom 0.2.0 → 0.2.1

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.
Files changed (87) hide show
  1. package/dist/binding-registry.d.ts +10 -0
  2. package/dist/binding-registry.d.ts.map +1 -0
  3. package/dist/binding-registry.js +96 -0
  4. package/dist/binding-registry.js.map +1 -0
  5. package/dist/binding.d.ts +22 -0
  6. package/dist/binding.d.ts.map +1 -1
  7. package/dist/binding.js +56 -5
  8. package/dist/binding.js.map +1 -1
  9. package/dist/build-flags.d.ts +25 -0
  10. package/dist/build-flags.d.ts.map +1 -0
  11. package/dist/build-flags.js +18 -0
  12. package/dist/build-flags.js.map +1 -0
  13. package/dist/el-split.d.ts.map +1 -1
  14. package/dist/el-split.js +8 -4
  15. package/dist/el-split.js.map +1 -1
  16. package/dist/el-template.d.ts.map +1 -1
  17. package/dist/el-template.js +15 -18
  18. package/dist/el-template.js.map +1 -1
  19. package/dist/elements.d.ts.map +1 -1
  20. package/dist/elements.js +12 -4
  21. package/dist/elements.js.map +1 -1
  22. package/dist/hmr.js +9 -4
  23. package/dist/hmr.js.map +1 -1
  24. package/dist/index.d.ts +2 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/internal/test-component-builder.d.ts +21 -0
  29. package/dist/internal/test-component-builder.d.ts.map +1 -0
  30. package/dist/internal/test-component-builder.js +65 -0
  31. package/dist/internal/test-component-builder.js.map +1 -0
  32. package/dist/internal.d.ts +1 -0
  33. package/dist/internal.d.ts.map +1 -1
  34. package/dist/internal.js +1 -0
  35. package/dist/internal.js.map +1 -1
  36. package/dist/lifetime.d.ts.map +1 -1
  37. package/dist/lifetime.js +16 -3
  38. package/dist/lifetime.js.map +1 -1
  39. package/dist/mount.d.ts +1 -0
  40. package/dist/mount.d.ts.map +1 -1
  41. package/dist/mount.js +32 -22
  42. package/dist/mount.js.map +1 -1
  43. package/dist/primitives/branch.d.ts.map +1 -1
  44. package/dist/primitives/branch.js +29 -16
  45. package/dist/primitives/branch.js.map +1 -1
  46. package/dist/primitives/client-only.d.ts +1 -1
  47. package/dist/primitives/client-only.d.ts.map +1 -1
  48. package/dist/primitives/client-only.js +3 -3
  49. package/dist/primitives/client-only.js.map +1 -1
  50. package/dist/primitives/context.d.ts.map +1 -1
  51. package/dist/primitives/context.js +6 -3
  52. package/dist/primitives/context.js.map +1 -1
  53. package/dist/primitives/each.d.ts.map +1 -1
  54. package/dist/primitives/each.js +121 -74
  55. package/dist/primitives/each.js.map +1 -1
  56. package/dist/primitives/foreign.js +2 -1
  57. package/dist/primitives/foreign.js.map +1 -1
  58. package/dist/primitives/lazy.d.ts +1 -1
  59. package/dist/primitives/lazy.d.ts.map +1 -1
  60. package/dist/primitives/lazy.js +11 -5
  61. package/dist/primitives/lazy.js.map +1 -1
  62. package/dist/primitives/portal.js +2 -1
  63. package/dist/primitives/portal.js.map +1 -1
  64. package/dist/primitives/sample.d.ts.map +1 -1
  65. package/dist/primitives/sample.js +22 -16
  66. package/dist/primitives/sample.js.map +1 -1
  67. package/dist/primitives/track.d.ts +54 -0
  68. package/dist/primitives/track.d.ts.map +1 -0
  69. package/dist/primitives/track.js +54 -0
  70. package/dist/primitives/track.js.map +1 -0
  71. package/dist/primitives/virtual-each.js +4 -2
  72. package/dist/primitives/virtual-each.js.map +1 -1
  73. package/dist/render-context.d.ts +27 -1
  74. package/dist/render-context.d.ts.map +1 -1
  75. package/dist/render-context.js +82 -33
  76. package/dist/render-context.js.map +1 -1
  77. package/dist/ssr.d.ts.map +1 -1
  78. package/dist/ssr.js +5 -3
  79. package/dist/ssr.js.map +1 -1
  80. package/dist/types.d.ts +2 -2
  81. package/dist/types.d.ts.map +1 -1
  82. package/dist/types.js.map +1 -1
  83. package/dist/update-loop.d.ts +1 -0
  84. package/dist/update-loop.d.ts.map +1 -1
  85. package/dist/update-loop.js +115 -50
  86. package/dist/update-loop.js.map +1 -1
  87. package/package.json +1 -1
@@ -1,8 +1,10 @@
1
- import { getRenderContext, setRenderContext, clearRenderContext, enterAccessor, exitAccessor, } from '../render-context.js';
1
+ import { getRenderContext, setRenderContext, clearRenderContext, enterAccessor, exitAccessor, getInstanceViewBag, } from '../render-context.js';
2
2
  import { createLifetime, disposeLifetime, disposeLifetimesBulk, addDisposer, removeOrphanedChildren, } from '../lifetime.js';
3
3
  import { getFlatBindings, setFlatBindings } from '../binding.js';
4
4
  import { FULL_MASK } from '../update-loop.js';
5
- import { createView } from '../view-helpers.js';
5
+ // v0.4 size-cut (Tier 1.2): no more createView fallback every compiled
6
+ // component carries a __view factory; each() pulls the bag from the owning
7
+ // instance via the render context.
6
8
  // Clear callbacks — registered by selector.bind() during render, called by reconcileClear().
7
9
  // Eliminates per-row disposers (1000 Set.delete calls → 1 registry.clear() call).
8
10
  let activeClearCallbacks = null;
@@ -83,56 +85,62 @@ export function each(opts) {
83
85
  let lastItemsRef = initialItems;
84
86
  // Dev-only diff tracking: if the owning component has an _eachDiffLog
85
87
  // (installed by devtools), we capture key sets before/after each
86
- // key-mutating reconcile call and emit an EachDiff entry. The siteId
87
- // is derived from this each() block's position in the flat block
88
- // array at registration time stable for the lifetime of the block.
88
+ // key-mutating reconcile call and emit an EachDiff entry. Wrapped in
89
+ // `import.meta.env?.DEV` so production builds dead-code the entire
90
+ // block saves ~1-2 kB in the prod bundle. The reconcile-side call
91
+ // sites are also gated below; the stubs (`null`/no-op) keep types
92
+ // and call sites consistent across modes.
89
93
  const inst = ctx.instance;
90
- const eachSiteId = inst?._eachDiffLog !== undefined ? `each#${blocks.length}` : '';
91
- const snapshotKeys = () => {
92
- if (inst?._eachDiffLog === undefined)
93
- return null;
94
- const keys = [];
95
- for (let i = 0; i < entries.length; i++)
96
- keys.push(String(entries[i].key));
97
- return keys;
98
- };
99
- const emitDiff = (oldKeys) => {
100
- if (oldKeys === null || inst?._eachDiffLog === undefined)
101
- return;
102
- const newKeys = [];
103
- for (let i = 0; i < entries.length; i++)
104
- newKeys.push(String(entries[i].key));
105
- const oldKeySet = new Set(oldKeys);
106
- const newKeySet = new Set(newKeys);
107
- const added = [];
108
- const removed = [];
109
- const moved = [];
110
- const reused = [];
111
- for (const k of newKeys)
112
- if (!oldKeySet.has(k))
113
- added.push(k);
114
- for (const k of oldKeys)
115
- if (!newKeySet.has(k))
116
- removed.push(k);
117
- for (let i = 0; i < newKeys.length; i++) {
118
- const k = newKeys[i];
119
- if (!oldKeySet.has(k))
120
- continue;
121
- const from = oldKeys.indexOf(k);
122
- if (from !== i)
123
- moved.push({ key: k, from, to: i });
124
- else
125
- reused.push(k);
126
- }
127
- inst._eachDiffLog.push({
128
- updateIndex: inst._updateCounter ?? 0,
129
- eachSiteId,
130
- added,
131
- removed,
132
- moved,
133
- reused,
134
- });
135
- };
94
+ let snapshotKeys = () => null;
95
+ let emitDiff = () => { };
96
+ if (import.meta.env?.DEV) {
97
+ const eachSiteId = inst?._eachDiffLog !== undefined ? `each#${blocks.length}` : '';
98
+ snapshotKeys = () => {
99
+ if (inst?._eachDiffLog === undefined)
100
+ return null;
101
+ const keys = [];
102
+ for (let i = 0; i < entries.length; i++)
103
+ keys.push(String(entries[i].key));
104
+ return keys;
105
+ };
106
+ emitDiff = (oldKeys) => {
107
+ if (oldKeys === null || inst?._eachDiffLog === undefined)
108
+ return;
109
+ const newKeys = [];
110
+ for (let i = 0; i < entries.length; i++)
111
+ newKeys.push(String(entries[i].key));
112
+ const oldKeySet = new Set(oldKeys);
113
+ const newKeySet = new Set(newKeys);
114
+ const added = [];
115
+ const removed = [];
116
+ const moved = [];
117
+ const reused = [];
118
+ for (const k of newKeys)
119
+ if (!oldKeySet.has(k))
120
+ added.push(k);
121
+ for (const k of oldKeys)
122
+ if (!newKeySet.has(k))
123
+ removed.push(k);
124
+ for (let i = 0; i < newKeys.length; i++) {
125
+ const k = newKeys[i];
126
+ if (!oldKeySet.has(k))
127
+ continue;
128
+ const from = oldKeys.indexOf(k);
129
+ if (from !== i)
130
+ moved.push({ key: k, from, to: i });
131
+ else
132
+ reused.push(k);
133
+ }
134
+ inst._eachDiffLog.push({
135
+ updateIndex: inst._updateCounter ?? 0,
136
+ eachSiteId,
137
+ added,
138
+ removed,
139
+ moved,
140
+ reused,
141
+ });
142
+ };
143
+ }
136
144
  const block = {
137
145
  mask: opts.__mask ?? FULL_MASK,
138
146
  maskHi: opts.__maskHi ?? 0,
@@ -146,9 +154,17 @@ export function each(opts) {
146
154
  return;
147
155
  lastItemsRef = newItems;
148
156
  const oldKeys = snapshotKeys();
149
- const report = opts.onTransition ? { entering: [], leaving: [] } : null;
157
+ // Transition support gated by `__LLUI_TRANSITIONS__` build flag.
158
+ // When false, the `report` allocation, the `onTransition` invocation,
159
+ // and (via type narrowing inside `reconcileEntries`) the per-entry
160
+ // leave/enter helpers all dead-code-eliminate.
161
+ const report = typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && opts.onTransition
162
+ ? { entering: [], leaving: [] }
163
+ : null;
150
164
  reconcileEntries(entries, newItems, opts, parentLifetime, parent, anchor, endAnchor, ctx, state, leaving, report);
151
- if (opts.onTransition && report) {
165
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' &&
166
+ __LLUI_TRANSITIONS__ &&
167
+ opts.onTransition) {
152
168
  opts.onTransition({ entering: report.entering, leaving: report.leaving, parent });
153
169
  }
154
170
  emitDiff(oldKeys);
@@ -192,7 +208,8 @@ export function each(opts) {
192
208
  const scopes = [];
193
209
  for (let i = 0; i < entries.length; i++) {
194
210
  const s = entries[i].scope;
195
- s.disposalCause = 'each-remove';
211
+ if (import.meta.env?.DEV)
212
+ s.disposalCause = 'each-remove';
196
213
  scopes.push(s);
197
214
  }
198
215
  disposeLifetimesBulk(scopes);
@@ -236,7 +253,8 @@ export function each(opts) {
236
253
  removeCallbacks[ci](entry.key);
237
254
  for (const node of entry.nodes)
238
255
  parent.removeChild(node);
239
- entry.scope.disposalCause = 'each-remove';
256
+ if (import.meta.env?.DEV)
257
+ entry.scope.disposalCause = 'each-remove';
240
258
  disposeLifetime(entry.scope, true);
241
259
  entries[oi] = null;
242
260
  didRemove = true;
@@ -286,8 +304,10 @@ export function each(opts) {
286
304
  }
287
305
  activeClearCallbacks = null;
288
306
  activeRemoveCallbacks = null;
289
- // Fire initial enter for mount-time items
290
- if (opts.enter) {
307
+ // Fire initial enter for mount-time items. Build-flag-gated:
308
+ // `__LLUI_TRANSITIONS__` lets the DCE drop this loop when the app
309
+ // doesn't use animation callbacks.
310
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && opts.enter) {
291
311
  for (const entry of entries) {
292
312
  if (entry.nodes.length > 0)
293
313
  opts.enter(entry.nodes);
@@ -350,13 +370,20 @@ function removeEntry(entry, opts, leaving) {
350
370
  if (node.parentNode)
351
371
  node.parentNode.removeChild(node);
352
372
  }
353
- entry.scope.disposalCause = 'each-remove';
373
+ if (import.meta.env?.DEV)
374
+ entry.scope.disposalCause = 'each-remove';
354
375
  disposeLifetime(entry.scope);
355
376
  const idx = leaving.indexOf(entry);
356
377
  if (idx !== -1)
357
378
  leaving.splice(idx, 1);
358
379
  };
359
- if (opts.leave && entry.nodes.length > 0) {
380
+ // Build-flag-gated leave animation. When `__LLUI_TRANSITIONS__` is
381
+ // false the whole block dead-code-eliminates and `removeEntry`
382
+ // collapses to its `removeNow()` call.
383
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' &&
384
+ __LLUI_TRANSITIONS__ &&
385
+ opts.leave &&
386
+ entry.nodes.length > 0) {
360
387
  const result = opts.leave(entry.nodes);
361
388
  if (result && typeof result.then === 'function') {
362
389
  leaving.push(entry);
@@ -367,7 +394,13 @@ function removeEntry(entry, opts, leaving) {
367
394
  removeNow();
368
395
  }
369
396
  function fireEnter(entry, opts) {
370
- if (opts.enter && entry.nodes.length > 0) {
397
+ // Build-flag-gated enter animation. `fireEnter` collapses to a no-op
398
+ // function when `__LLUI_TRANSITIONS__` is false; terser then inlines
399
+ // and drops the callers' calls.
400
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' &&
401
+ __LLUI_TRANSITIONS__ &&
402
+ opts.enter &&
403
+ entry.nodes.length > 0) {
371
404
  void opts.enter(entry.nodes);
372
405
  }
373
406
  }
@@ -377,7 +410,8 @@ function buildEntry(item, index, opts, parentLifetime, ctx, state) {
377
410
  // Full scope features (disposers, bindings, children) are only needed when
378
411
  // the render callback uses structural primitives or selector.bind().
379
412
  const scope = createLifetime(parentLifetime);
380
- scope._kind = 'each';
413
+ if (import.meta.env?.DEV)
414
+ scope._kind = 'each';
381
415
  const currentState = (state ?? ctx.state);
382
416
  const send = ctx.send;
383
417
  // Create entry before render so itemAccessor closures can capture it
@@ -454,7 +488,11 @@ function buildEntry(item, index, opts, parentLifetime, ctx, state) {
454
488
  // The View bag — lets each.render use `h.text`, `h.scope`, `h.sample`,
455
489
  // etc. without reaching for the top-level imports. Each entry gets a
456
490
  // fresh View so its `send` is bound to this row's dispatch path.
457
- buildBag.h = createView(send);
491
+ // v0.4 Tier 1.2 + cache follow-up: the bag is constructed once per
492
+ // owning instance and reused for every row. Pre-cache: 1000 rows = 1000
493
+ // bag allocations + 1000 __view calls. Post-cache: 1 per instance.
494
+ // (Test-mode createView fallback is gated inside getInstanceViewBag.)
495
+ buildBag.h = getInstanceViewBag(ctx.instance, send);
458
496
  // Row factory: pass compiler-injected template + update function through to render
459
497
  const rfOpts = opts;
460
498
  if (rfOpts.__tpl)
@@ -480,12 +518,16 @@ function collectNodes(target, nodes) {
480
518
  function reconcileEntries(entries, newItems, opts, parentLifetime, parent, anchor, endAnchor, ctx, state, leaving, report) {
481
519
  const oldLen = entries.length;
482
520
  const newLen = newItems.length;
483
- const hasLeave = !!opts.leave;
521
+ // Build-flag-gated: when `__LLUI_TRANSITIONS__` is false, `hasLeave`
522
+ // folds to `false`, the report branch folds away, and `removeEntry`
523
+ // collapses to synchronous removal. Per-item removal fast path
524
+ // (lines below this) becomes unreachable.
525
+ const hasLeave = typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && !!opts.leave;
484
526
  // Fast path 1: clear all — bulk DOM removal.
485
527
  // When opts.leave is set, each item needs its own leave animation, so
486
528
  // fall through to per-item removal instead of Range.deleteContents().
487
529
  if (newLen === 0) {
488
- if (report) {
530
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && report) {
489
531
  for (const entry of entries)
490
532
  collectNodes(report.leaving, entry.nodes);
491
533
  }
@@ -508,7 +550,8 @@ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, ancho
508
550
  const scopes = [];
509
551
  for (let i = 0; i < entries.length; i++) {
510
552
  const s = entries[i].scope;
511
- s.disposalCause = 'each-remove';
553
+ if (import.meta.env?.DEV)
554
+ s.disposalCause = 'each-remove';
512
555
  scopes.push(s);
513
556
  }
514
557
  disposeLifetimesBulk(scopes);
@@ -536,7 +579,7 @@ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, ancho
536
579
  frag.appendChild(node);
537
580
  }
538
581
  parent.insertBefore(frag, ref);
539
- if (report) {
582
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && report) {
540
583
  for (const entry of newlyAdded)
541
584
  collectNodes(report.entering, entry.nodes);
542
585
  }
@@ -608,7 +651,7 @@ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, ancho
608
651
  }
609
652
  }
610
653
  if (!anyShared) {
611
- if (report) {
654
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && report) {
612
655
  for (const entry of entries)
613
656
  collectNodes(report.leaving, entry.nodes);
614
657
  }
@@ -623,7 +666,8 @@ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, ancho
623
666
  const oldLifetimes = [];
624
667
  for (let i = 0; i < entries.length; i++) {
625
668
  const s = entries[i].scope;
626
- s.disposalCause = 'each-remove';
669
+ if (import.meta.env?.DEV)
670
+ s.disposalCause = 'each-remove';
627
671
  oldLifetimes.push(s);
628
672
  }
629
673
  disposeLifetimesBulk(oldLifetimes);
@@ -640,7 +684,7 @@ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, ancho
640
684
  frag.appendChild(node);
641
685
  }
642
686
  parent.insertBefore(frag, anchor.nextSibling);
643
- if (report) {
687
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && report) {
644
688
  for (const entry of newlyAdded)
645
689
  collectNodes(report.entering, entry.nodes);
646
690
  }
@@ -678,7 +722,7 @@ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, ancho
678
722
  let didBulkDetach = false;
679
723
  for (const entry of entries) {
680
724
  if (!usedKeys.has(entry.key)) {
681
- if (report)
725
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && report)
682
726
  collectNodes(report.leaving, entry.nodes);
683
727
  if (hasLeave) {
684
728
  removeEntry(entry, opts, leaving);
@@ -696,7 +740,8 @@ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, ancho
696
740
  if (node.parentNode === parent)
697
741
  parent.removeChild(node);
698
742
  }
699
- entry.scope.disposalCause = 'each-remove';
743
+ if (import.meta.env?.DEV)
744
+ entry.scope.disposalCause = 'each-remove';
700
745
  disposeLifetime(entry.scope, true);
701
746
  didBulkDetach = true;
702
747
  }
@@ -744,12 +789,14 @@ function reconcileEntries(entries, newItems, opts, parentLifetime, parent, ancho
744
789
  entries.length = newEntries.length;
745
790
  for (let i = 0; i < newEntries.length; i++)
746
791
  entries[i] = newEntries[i];
747
- if (report) {
792
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && report) {
748
793
  for (const entry of newlyAdded)
749
794
  collectNodes(report.entering, entry.nodes);
750
795
  }
751
- // Fire enter for newly-added entries (after DOM insertion)
752
- if (opts.enter) {
796
+ // Fire enter for newly-added entries (after DOM insertion). Gated
797
+ // by `__LLUI_TRANSITIONS__` build flag — bench-shape apps skip
798
+ // the whole branch.
799
+ if (typeof __LLUI_TRANSITIONS__ !== 'undefined' && __LLUI_TRANSITIONS__ && opts.enter) {
753
800
  for (const entry of newlyAdded)
754
801
  fireEnter(entry, opts);
755
802
  }