@getforma/core 1.0.8 → 1.0.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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkBARF67I6_cjs = require('./chunk-BARF67I6.cjs');
3
+ var chunk6FW5E54W_cjs = require('./chunk-6FW5E54W.cjs');
4
4
  var chunkQLPCVK7C_cjs = require('./chunk-QLPCVK7C.cjs');
5
5
 
6
6
  // src/dom/element.ts
@@ -156,7 +156,7 @@ function getCache(el) {
156
156
  }
157
157
  function handleClass(el, _key, value) {
158
158
  if (typeof value === "function") {
159
- chunkBARF67I6_cjs.internalEffect(() => {
159
+ chunk6FW5E54W_cjs.internalEffect(() => {
160
160
  const v = value();
161
161
  const cache = getCache(el);
162
162
  if (cache["class"] === v) return;
@@ -178,34 +178,50 @@ function handleClass(el, _key, value) {
178
178
  }
179
179
  }
180
180
  }
181
+ function parseCssString(css) {
182
+ const obj = {};
183
+ for (const decl of css.split(";")) {
184
+ const colon = decl.indexOf(":");
185
+ if (colon < 0) continue;
186
+ const prop = decl.slice(0, colon).trim();
187
+ const val = decl.slice(colon + 1).trim();
188
+ if (prop && val) {
189
+ const camel = prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
190
+ obj[camel] = val;
191
+ }
192
+ }
193
+ return obj;
194
+ }
195
+ function applyStyleObj(el, obj, prevKeys) {
196
+ const style = el.style;
197
+ const nextKeys = Object.keys(obj);
198
+ for (const k of prevKeys) {
199
+ if (!(k in obj)) {
200
+ style.removeProperty(k.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()));
201
+ }
202
+ }
203
+ Object.assign(style, obj);
204
+ return nextKeys;
205
+ }
181
206
  function handleStyle(el, _key, value) {
182
207
  if (typeof value === "function") {
183
208
  let prevKeys = [];
184
- chunkBARF67I6_cjs.internalEffect(() => {
209
+ chunk6FW5E54W_cjs.internalEffect(() => {
185
210
  const v = value();
186
211
  if (typeof v === "string") {
187
212
  const cache = getCache(el);
188
213
  if (cache["style"] === v) return;
189
214
  cache["style"] = v;
190
- prevKeys = [];
191
- el.style.cssText = v;
215
+ prevKeys = applyStyleObj(el, parseCssString(v), prevKeys);
192
216
  } else if (v && typeof v === "object") {
193
- const style = el.style;
194
- const nextKeys = Object.keys(v);
195
- for (const k of prevKeys) {
196
- if (!(k in v)) {
197
- style.removeProperty(k.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()));
198
- }
199
- }
200
- Object.assign(style, v);
201
- prevKeys = nextKeys;
217
+ prevKeys = applyStyleObj(el, v, prevKeys);
202
218
  }
203
219
  });
204
220
  } else if (typeof value === "string") {
205
221
  const cache = getCache(el);
206
222
  if (cache["style"] === value) return;
207
223
  cache["style"] = value;
208
- el.style.cssText = value;
224
+ applyStyleObj(el, parseCssString(value), []);
209
225
  } else if (value && typeof value === "object") {
210
226
  Object.assign(el.style, value);
211
227
  }
@@ -220,7 +236,7 @@ function handleEvent(el, key, value) {
220
236
  }
221
237
  function handleInnerHTML(el, _key, value) {
222
238
  if (typeof value === "function") {
223
- chunkBARF67I6_cjs.internalEffect(() => {
239
+ chunk6FW5E54W_cjs.internalEffect(() => {
224
240
  const resolved = value();
225
241
  if (resolved == null) {
226
242
  el.innerHTML = "";
@@ -264,7 +280,7 @@ function handleInnerHTML(el, _key, value) {
264
280
  function handleXLink(el, key, value) {
265
281
  const localName = key.slice(6);
266
282
  if (typeof value === "function") {
267
- chunkBARF67I6_cjs.internalEffect(() => {
283
+ chunk6FW5E54W_cjs.internalEffect(() => {
268
284
  const v = value();
269
285
  if (v == null || v === false) {
270
286
  el.removeAttributeNS(XLINK_NS, localName);
@@ -282,7 +298,7 @@ function handleXLink(el, key, value) {
282
298
  }
283
299
  function handleBooleanAttr(el, key, value) {
284
300
  if (typeof value === "function") {
285
- chunkBARF67I6_cjs.internalEffect(() => {
301
+ chunk6FW5E54W_cjs.internalEffect(() => {
286
302
  const v = value();
287
303
  const cache = getCache(el);
288
304
  if (cache[key] === v) return;
@@ -306,7 +322,7 @@ function handleBooleanAttr(el, key, value) {
306
322
  }
307
323
  function handleGenericAttr(el, key, value) {
308
324
  if (typeof value === "function") {
309
- chunkBARF67I6_cjs.internalEffect(() => {
325
+ chunk6FW5E54W_cjs.internalEffect(() => {
310
326
  const v = value();
311
327
  if (v == null || v === false) {
312
328
  const cache = getCache(el);
@@ -378,7 +394,7 @@ function applyStaticProp(el, key, value) {
378
394
  }
379
395
  if (key === "style") {
380
396
  if (typeof value === "string") {
381
- el.style.cssText = value;
397
+ applyStyleObj(el, parseCssString(value), []);
382
398
  } else if (value && typeof value === "object") {
383
399
  Object.assign(el.style, value);
384
400
  }
@@ -449,7 +465,7 @@ function appendChild(parent, child) {
449
465
  }
450
466
  currentNode = null;
451
467
  };
452
- chunkBARF67I6_cjs.internalEffect(() => {
468
+ chunk6FW5E54W_cjs.internalEffect(() => {
453
469
  const v = child();
454
470
  let resolved = v;
455
471
  if (Array.isArray(v)) {
@@ -599,7 +615,7 @@ function createShow(when, thenFn, elseFn = () => null) {
599
615
  let currentNode = null;
600
616
  let lastTruthy = null;
601
617
  let currentDispose = null;
602
- const showDispose = chunkBARF67I6_cjs.internalEffect(() => {
618
+ const showDispose = chunk6FW5E54W_cjs.internalEffect(() => {
603
619
  const truthy = !!when();
604
620
  const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
605
621
  const DEBUG_LABEL = DEBUG ? thenFn.toString().slice(0, 60) : "";
@@ -631,9 +647,9 @@ function createShow(when, thenFn, elseFn = () => null) {
631
647
  const branchFn = truthy ? thenFn : elseFn;
632
648
  if (branchFn) {
633
649
  let branchDispose;
634
- currentNode = chunkBARF67I6_cjs.createRoot((dispose) => {
650
+ currentNode = chunk6FW5E54W_cjs.createRoot((dispose) => {
635
651
  branchDispose = dispose;
636
- return chunkBARF67I6_cjs.untrack(() => branchFn());
652
+ return chunk6FW5E54W_cjs.untrack(() => branchFn());
637
653
  });
638
654
  currentDispose = branchDispose;
639
655
  } else {
@@ -643,6 +659,13 @@ function createShow(when, thenFn, elseFn = () => null) {
643
659
  parent.insertBefore(currentNode, endMarker);
644
660
  }
645
661
  });
662
+ chunk6FW5E54W_cjs.registerDisposer(() => {
663
+ if (currentDispose) {
664
+ currentDispose();
665
+ currentDispose = null;
666
+ }
667
+ currentNode = null;
668
+ });
646
669
  fragment2.__showDispose = () => {
647
670
  showDispose();
648
671
  if (currentDispose) {
@@ -684,7 +707,7 @@ function applyDynamicProps(el, props) {
684
707
  }
685
708
  const fn = value;
686
709
  const attrKey = key;
687
- chunkBARF67I6_cjs.internalEffect(() => {
710
+ chunk6FW5E54W_cjs.internalEffect(() => {
688
711
  const v = fn();
689
712
  if (v === false || v == null) {
690
713
  el.removeAttribute(attrKey);
@@ -796,13 +819,13 @@ function setupShowEffect(desc, marker) {
796
819
  let elseFragment = null;
797
820
  const hasSSRContent = marker.start.nextSibling !== marker.end;
798
821
  if (!hasSSRContent && currentCondition) {
799
- if (chunkBARF67I6_cjs.__DEV__) console.warn("[forma] Hydration: show condition mismatch \u2014 SSR empty but client condition is true");
822
+ if (chunk6FW5E54W_cjs.__DEV__) console.warn("[forma] Hydration: show condition mismatch \u2014 SSR empty but client condition is true");
800
823
  const trueBranch = desc.whenTrue();
801
824
  if (trueBranch instanceof Node) {
802
825
  marker.start.parentNode.insertBefore(trueBranch, marker.end);
803
826
  }
804
827
  }
805
- chunkBARF67I6_cjs.internalEffect(() => {
828
+ chunk6FW5E54W_cjs.internalEffect(() => {
806
829
  const next = !!desc.condition();
807
830
  if (next === currentCondition) return;
808
831
  currentCondition = next;
@@ -849,7 +872,7 @@ function adoptBranchContent(desc, regionStart, regionEnd) {
849
872
  }
850
873
  function adoptNode(desc, ssrEl) {
851
874
  if (!ssrEl || ssrEl.tagName !== desc.tag.toUpperCase()) {
852
- if (chunkBARF67I6_cjs.__DEV__) console.warn(`Hydration mismatch: expected <${desc.tag}>, got <${ssrEl?.tagName?.toLowerCase() ?? "nothing"}>`);
875
+ if (chunk6FW5E54W_cjs.__DEV__) console.warn(`Hydration mismatch: expected <${desc.tag}>, got <${ssrEl?.tagName?.toLowerCase() ?? "nothing"}>`);
853
876
  const fresh = descriptorToElement(desc);
854
877
  if (ssrEl) ssrEl.replaceWith(fresh);
855
878
  return;
@@ -923,7 +946,7 @@ function adoptNode(desc, ssrEl) {
923
946
  }
924
947
  node = node.nextSibling;
925
948
  }
926
- const currentItems = chunkBARF67I6_cjs.untrack(() => child.items());
949
+ const currentItems = chunk6FW5E54W_cjs.untrack(() => child.items());
927
950
  const listKeyFn = child.keyFn;
928
951
  const listRenderFn = child.renderFn;
929
952
  const useIndexFallback = ssrKeyMap.size === 0 && ssrElements.length > 0;
@@ -947,7 +970,7 @@ function adoptNode(desc, ssrEl) {
947
970
  adoptedNodes.push(ssrNode);
948
971
  adoptedItems.push(item);
949
972
  } else {
950
- if (chunkBARF67I6_cjs.__DEV__) console.warn(`[FormaJS] Hydration: list item key "${key}" not found in SSR \u2014 rendering fresh`);
973
+ if (chunk6FW5E54W_cjs.__DEV__) console.warn(`[FormaJS] Hydration: list item key "${key}" not found in SSR \u2014 rendering fresh`);
951
974
  const prevHydrating = hydrating;
952
975
  hydrating = false;
953
976
  try {
@@ -969,7 +992,7 @@ function adoptNode(desc, ssrEl) {
969
992
  }
970
993
  } else {
971
994
  for (const [unusedKey, unusedNode] of ssrKeyMap) {
972
- if (chunkBARF67I6_cjs.__DEV__) console.warn(`[FormaJS] Hydration: removing extra SSR list item with key "${unusedKey}"`);
995
+ if (chunk6FW5E54W_cjs.__DEV__) console.warn(`[FormaJS] Hydration: removing extra SSR list item with key "${unusedKey}"`);
973
996
  if (unusedNode.parentNode) {
974
997
  unusedNode.parentNode.removeChild(unusedNode);
975
998
  }
@@ -993,7 +1016,7 @@ function adoptNode(desc, ssrEl) {
993
1016
  }
994
1017
  let reconcileNodes = adoptedNodes.slice();
995
1018
  let reconcileItems = adoptedItems.slice();
996
- chunkBARF67I6_cjs.internalEffect(() => {
1019
+ chunk6FW5E54W_cjs.internalEffect(() => {
997
1020
  const newItems = child.items();
998
1021
  const parent2 = start.parentNode;
999
1022
  if (!parent2) return;
@@ -1009,7 +1032,7 @@ function adoptNode(desc, ssrEl) {
1009
1032
  try {
1010
1033
  const key = listKeyFn(item);
1011
1034
  const [getIndex, setIndex] = chunkQLPCVK7C_cjs.createSignal(0);
1012
- const element = chunkBARF67I6_cjs.untrack(() => listRenderFn(item, getIndex));
1035
+ const element = chunk6FW5E54W_cjs.untrack(() => listRenderFn(item, getIndex));
1013
1036
  cache.set(key, { element, item, getIndex, setIndex });
1014
1037
  return element;
1015
1038
  } finally {
@@ -1058,12 +1081,12 @@ function adoptNode(desc, ssrEl) {
1058
1081
  const endMarker = findClosingMarker(cursor);
1059
1082
  let textNode = cursor.nextSibling;
1060
1083
  if (!textNode || textNode.nodeType !== 3) {
1061
- if (chunkBARF67I6_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node for marker ${data} \u2014 SSR walker should emit content between markers`);
1084
+ if (chunk6FW5E54W_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node for marker ${data} \u2014 SSR walker should emit content between markers`);
1062
1085
  const created = document.createTextNode("");
1063
1086
  cursor.parentNode.insertBefore(created, endMarker || cursor.nextSibling);
1064
1087
  textNode = created;
1065
1088
  }
1066
- chunkBARF67I6_cjs.internalEffect(() => {
1089
+ chunk6FW5E54W_cjs.internalEffect(() => {
1067
1090
  textNode.data = String(child());
1068
1091
  });
1069
1092
  cursor = endMarker ? endMarker.nextSibling : textNode.nextSibling;
@@ -1073,11 +1096,11 @@ function adoptNode(desc, ssrEl) {
1073
1096
  if (end) {
1074
1097
  let textNode = findTextBetween(start, end);
1075
1098
  if (!textNode) {
1076
- if (chunkBARF67I6_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node for show marker ${start.data} \u2014 SSR walker should emit content between markers`);
1099
+ if (chunk6FW5E54W_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node for show marker ${start.data} \u2014 SSR walker should emit content between markers`);
1077
1100
  textNode = document.createTextNode("");
1078
1101
  start.parentNode.insertBefore(textNode, end);
1079
1102
  }
1080
- chunkBARF67I6_cjs.internalEffect(() => {
1103
+ chunk6FW5E54W_cjs.internalEffect(() => {
1081
1104
  textNode.data = String(child());
1082
1105
  });
1083
1106
  cursor = end.nextSibling;
@@ -1090,14 +1113,14 @@ function adoptNode(desc, ssrEl) {
1090
1113
  } else if (cursor && cursor.nodeType === 3) {
1091
1114
  const textNode = cursor;
1092
1115
  cursor = cursor.nextSibling;
1093
- chunkBARF67I6_cjs.internalEffect(() => {
1116
+ chunk6FW5E54W_cjs.internalEffect(() => {
1094
1117
  textNode.data = String(child());
1095
1118
  });
1096
1119
  } else {
1097
- if (chunkBARF67I6_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node in empty <${ssrEl.tagName.toLowerCase()}> \u2014 IR may not cover this component`);
1120
+ if (chunk6FW5E54W_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node in empty <${ssrEl.tagName.toLowerCase()}> \u2014 IR may not cover this component`);
1098
1121
  const textNode = document.createTextNode("");
1099
1122
  ssrEl.appendChild(textNode);
1100
- chunkBARF67I6_cjs.internalEffect(() => {
1123
+ chunk6FW5E54W_cjs.internalEffect(() => {
1101
1124
  textNode.data = String(child());
1102
1125
  });
1103
1126
  }
@@ -1111,7 +1134,7 @@ function adoptNode(desc, ssrEl) {
1111
1134
  function hydrateIsland(component, target) {
1112
1135
  const hasSSRContent = target.childElementCount > 0 || target.childNodes.length > 0 && Array.from(target.childNodes).some((n) => n.nodeType === 1 || n.nodeType === 3 && n.data.trim());
1113
1136
  if (!hasSSRContent) {
1114
- if (chunkBARF67I6_cjs.__DEV__) {
1137
+ if (chunk6FW5E54W_cjs.__DEV__) {
1115
1138
  const name = target.getAttribute("data-forma-component") || "unknown";
1116
1139
  console.warn(
1117
1140
  `[forma] Island "${name}" has no SSR content \u2014 falling back to CSR. This means the IR walker did not render content between ISLAND_START and ISLAND_END.`
@@ -1427,14 +1450,14 @@ function createList(items, keyFn, renderFn, options) {
1427
1450
  let currentNodes = [];
1428
1451
  let currentItems = [];
1429
1452
  const updateOnItemChange = options?.updateOnItemChange ?? "none";
1430
- chunkBARF67I6_cjs.internalEffect(() => {
1453
+ chunk6FW5E54W_cjs.internalEffect(() => {
1431
1454
  const newItems = items();
1432
1455
  const parent = startMarker.parentNode;
1433
1456
  if (!parent) {
1434
1457
  return;
1435
1458
  }
1436
1459
  if (!Array.isArray(newItems)) {
1437
- if (chunkBARF67I6_cjs.__DEV__) {
1460
+ if (chunk6FW5E54W_cjs.__DEV__) {
1438
1461
  console.warn("[forma] createList: value is not an array, treating as empty");
1439
1462
  }
1440
1463
  for (const node of currentNodes) {
@@ -1452,7 +1475,7 @@ function createList(items, keyFn, renderFn, options) {
1452
1475
  break;
1453
1476
  }
1454
1477
  }
1455
- if (chunkBARF67I6_cjs.__DEV__) {
1478
+ if (chunk6FW5E54W_cjs.__DEV__) {
1456
1479
  const seen = /* @__PURE__ */ new Set();
1457
1480
  for (const item of cleanItems) {
1458
1481
  const key = keyFn(item);
@@ -1472,7 +1495,7 @@ function createList(items, keyFn, renderFn, options) {
1472
1495
  if (node[ABORT_SYM3] || node[CACHE_SYM2] || node[DYNAMIC_CHILD_SYM2]) {
1473
1496
  return;
1474
1497
  }
1475
- const next = chunkBARF67I6_cjs.untrack(() => renderFn(item, cached.getIndex));
1498
+ const next = chunk6FW5E54W_cjs.untrack(() => renderFn(item, cached.getIndex));
1476
1499
  if (canPatchStaticElement(node, next)) {
1477
1500
  patchStaticElement(node, next);
1478
1501
  cached.element = node;
@@ -1482,18 +1505,26 @@ function createList(items, keyFn, renderFn, options) {
1482
1505
  const cached = cache.get(key);
1483
1506
  if (cached) cached.item = item;
1484
1507
  };
1508
+ const oldCache = cache;
1485
1509
  const result = reconcileList(
1486
1510
  parent,
1487
1511
  currentItems,
1488
1512
  cleanItems,
1489
1513
  currentNodes,
1490
1514
  keyFn,
1491
- // createFn: create element + cache entry
1515
+ // createFn: create element + cache entry.
1516
+ // Each item is rendered inside createRoot + untrack so that inner
1517
+ // effects are owned by the item's root (not the parent), and are
1518
+ // disposed when the item is removed from the list.
1492
1519
  (item) => {
1493
1520
  const key = keyFn(item);
1494
1521
  const [getIndex, setIndex] = chunkQLPCVK7C_cjs.createSignal(0);
1495
- const element = chunkBARF67I6_cjs.untrack(() => renderFn(item, getIndex));
1496
- cache.set(key, { element, item, getIndex, setIndex });
1522
+ let itemDispose;
1523
+ const element = chunk6FW5E54W_cjs.createRoot((dispose) => {
1524
+ itemDispose = dispose;
1525
+ return chunk6FW5E54W_cjs.untrack(() => renderFn(item, getIndex));
1526
+ });
1527
+ cache.set(key, { element, item, getIndex, setIndex, dispose: itemDispose });
1497
1528
  return element;
1498
1529
  },
1499
1530
  updateRow,
@@ -1503,16 +1534,29 @@ function createList(items, keyFn, renderFn, options) {
1503
1534
  const newCache = /* @__PURE__ */ new Map();
1504
1535
  for (let i = 0; i < cleanItems.length; i++) {
1505
1536
  const key = keyFn(cleanItems[i]);
1506
- const cached = cache.get(key);
1537
+ const cached = oldCache.get(key);
1507
1538
  if (cached) {
1508
1539
  cached.setIndex(i);
1509
1540
  newCache.set(key, cached);
1510
1541
  }
1511
1542
  }
1543
+ for (const [key, cached] of oldCache) {
1544
+ if (!newCache.has(key)) {
1545
+ cached.dispose();
1546
+ }
1547
+ }
1512
1548
  cache = newCache;
1513
1549
  currentNodes = result.nodes;
1514
1550
  currentItems = result.items;
1515
1551
  });
1552
+ chunk6FW5E54W_cjs.registerDisposer(() => {
1553
+ for (const cached of cache.values()) {
1554
+ cached.dispose();
1555
+ }
1556
+ cache = /* @__PURE__ */ new Map();
1557
+ currentNodes = [];
1558
+ currentItems = [];
1559
+ });
1516
1560
  return fragment2;
1517
1561
  }
1518
1562
 
@@ -1524,5 +1568,5 @@ exports.fragment = fragment;
1524
1568
  exports.h = h;
1525
1569
  exports.hydrateIsland = hydrateIsland;
1526
1570
  exports.reconcileList = reconcileList;
1527
- //# sourceMappingURL=chunk-7L3KHGEA.cjs.map
1528
- //# sourceMappingURL=chunk-7L3KHGEA.cjs.map
1571
+ //# sourceMappingURL=chunk-T33QUD2Y.cjs.map
1572
+ //# sourceMappingURL=chunk-T33QUD2Y.cjs.map