@barefootjs/client 0.28.1 → 0.29.0

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.
@@ -6,12 +6,39 @@
6
6
  */
7
7
  import type { ComponentDef } from './types.ts';
8
8
  export declare function setParentScopeId(id: string | null): void;
9
- /** Where `mapArray` wants a fresh loop row connected before its `init` runs. */
9
+ /**
10
+ * Where `mapArray` wants a fresh loop row connected before its `init` runs.
11
+ *
12
+ * `mounted` is written back by `mountRowRoot` so the caller can undo the
13
+ * connection if the rest of the row's body then throws — see `createItemScope`.
14
+ * Without it a half-built row stays visible, which detached rows never did.
15
+ */
10
16
  export type RowMountPoint = {
11
17
  container: Node;
12
18
  anchor: Node | null;
19
+ mounted?: Element | null;
13
20
  };
14
21
  export declare function setRowMountPoint(p: RowMountPoint | null): RowMountPoint | null;
22
+ /**
23
+ * Connect a loop row whose root is a template clone, before the emitted
24
+ * renderItem body's tail runs.
25
+ *
26
+ * The tail is where the row's nested children initialise (`upsertChild` and
27
+ * friends), and `useContext` resolves by walking `parentElement` — so a child
28
+ * that inits inside a detached row finds no ancestors and falls through to the
29
+ * global last-writer-wins context store, reading whichever provider on the page
30
+ * wrote last. `createComponent` row roots already avoid this by consuming the
31
+ * same ambient (step 7b); a clone root has no runtime function in the middle,
32
+ * so the compiler emits this call for it instead.
33
+ *
34
+ * Take-and-clear, like the `createComponent` path: a nested `createComponent`
35
+ * driven by this row's own children must not re-use the row's mount point.
36
+ *
37
+ * No-op when there is no ambient point — the hydration branch never calls this
38
+ * (its row came from SSR markup and is already in the document), and a loop
39
+ * runtime that does not hand one down leaves the row exactly as it was.
40
+ */
41
+ export declare function mountRowRoot(el: Element): Element;
15
42
  /**
16
43
  * Create a component instance with DOM element and initialized state.
17
44
  *
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/runtime/component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAS9C,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAExD;AAED,gFAAgF;AAChF,MAAM,MAAM,aAAa,GAAG;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAA;CAAE,CAAA;AAsBpE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,aAAa,GAAG,IAAI,CAI9E;AAQD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;;;GAGG;AACH;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,GAAG,YAAY,EAChC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EACrB,IAAI,CAAC,EAAE,uBAAuB,EAC9B,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GACvB,WAAW,CAcb;AA2ND;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAiDR;AA+DD;;;;;GAKG;AACH;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAG9D;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,gBAAgB,CAcjF"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/runtime/component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAS9C,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAExD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAA;AAsB9F,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,GAAG,aAAa,GAAG,IAAI,CAI9E;AAQD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAQjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH;;;GAGG;AACH;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,GAAG,YAAY,EAChC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACnC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EACrB,IAAI,CAAC,EAAE,uBAAuB,EAC9B,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,GACvB,WAAW,CAcb;AA2ND;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAiDR;AA+DD;;;;;GAKG;AACH;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOjD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGjD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAG9D;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,gBAAgB,CAcjF"}
@@ -15,7 +15,7 @@ export { mapArrayLazy, type LazyRowEntry, type LazyRowPlan } from './map-array-l
15
15
  export { patchLeaf } from './patch-leaf.ts';
16
16
  export { claimSlots, lazySlots, lazyClaimSlots, textOrNode, type SlotSpec, type ClaimPlan, type ClaimedSlots, type ClaimedSlotsRW, type SlotWriter } from './claim-slots.ts';
17
17
  export { registerTemplate, getTemplate, hasTemplate, type TemplateFn } from './template.ts';
18
- export { createComponent, renderChild, parseHTML, escapeAttr, escapeText, escapeTextOrNode, } from './component.ts';
18
+ export { createComponent, mountRowRoot, renderChild, parseHTML, escapeAttr, escapeText, escapeTextOrNode, } from './component.ts';
19
19
  export { applyRestAttrs } from './apply-rest-attrs.ts';
20
20
  export { spreadAttrs } from './spread-attrs.ts';
21
21
  export { styleToCss } from './style.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"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"}
@@ -1361,6 +1361,14 @@ function takeRowMountPoint() {
1361
1361
  _rowMountPoint = null;
1362
1362
  return p;
1363
1363
  }
1364
+ function mountRowRoot(el) {
1365
+ const p = takeRowMountPoint();
1366
+ if (p) {
1367
+ p.container.insertBefore(el, p.anchor);
1368
+ p.mounted = el;
1369
+ }
1370
+ return el;
1371
+ }
1364
1372
  function createComponent(nameOrDef, props = {}, key, slot, mountAt) {
1365
1373
  const element = materializeComponent(nameOrDef, props, key, slot, mountAt);
1366
1374
  if (mountAt && mountAt.parentNode && element !== mountAt) {
@@ -1738,7 +1746,7 @@ function* itemRootElements(primaryEl) {
1738
1746
  if (n.nodeType === Node.COMMENT_NODE) {
1739
1747
  const v = n.nodeValue ?? "";
1740
1748
  if (v === BF_LOOP_ITEM || v === BF_LOOP_START || v.startsWith(startPrefix) || v === BF_LOOP_END || v.startsWith(endPrefix)) {
1741
- return;
1749
+ break;
1742
1750
  }
1743
1751
  } else if (n.nodeType === Node.ELEMENT_NODE) {
1744
1752
  yield n;
@@ -1911,9 +1919,16 @@ function createItemScope(item, index, renderItem, existingPrimary, existingExtra
1911
1919
  const prevRowMount = ownsRowMount ? setRowMountPoint(rowMount) : null;
1912
1920
  try {
1913
1921
  primaryEl = renderItem(itemAccessor, index, existingPrimary);
1922
+ } catch (err) {
1923
+ const parked = rowMount?.mounted;
1924
+ if (parked?.parentNode)
1925
+ parked.remove();
1926
+ throw err;
1914
1927
  } finally {
1915
1928
  if (ownsRowMount)
1916
1929
  setRowMountPoint(prevRowMount);
1930
+ if (rowMount)
1931
+ rowMount.mounted = null;
1917
1932
  }
1918
1933
  if (existingPrimary) {
1919
1934
  extras = existingExtras ?? [];
@@ -3248,6 +3263,7 @@ export {
3248
3263
  parseHTML,
3249
3264
  onMount,
3250
3265
  onCleanup,
3266
+ mountRowRoot,
3251
3267
  mapArrayLazy,
3252
3268
  mapArrayAnchored,
3253
3269
  mapArray,
@@ -1 +1 @@
1
- {"version":3,"file":"map-array.d.ts","sourceRoot":"","sources":["../../src/runtime/map-array.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAqCH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,WAAW,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CA+BhD;AA8DD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CA2B3E;AAkFD;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,QAAQ,EAAE,MAAM,CAAC,EAAE,EACnB,SAAS,EAAE,WAAW,GAAG,IAAI,EAC7B,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,EACnD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,WAAW,KAAK,WAAW,EACjF,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CA6RN;AAqHD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,QAAQ,EAAE,MAAM,CAAC,EAAE,EACnB,SAAS,EAAE,WAAW,GAAG,IAAI,EAC7B,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,EACnD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,gBAAgB,GAAG,OAAO,EAC5F,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CA+FN"}
1
+ {"version":3,"file":"map-array.d.ts","sourceRoot":"","sources":["../../src/runtime/map-array.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAqCH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,WAAW,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CA+BhD;AA8DD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mCAAmC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CA2B3E;AA2FD;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,QAAQ,EAAE,MAAM,CAAC,EAAE,EACnB,SAAS,EAAE,WAAW,GAAG,IAAI,EAC7B,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,EACnD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,WAAW,KAAK,WAAW,EACjF,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CA6RN;AAqHD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,QAAQ,EAAE,MAAM,CAAC,EAAE,EACnB,SAAS,EAAE,WAAW,GAAG,IAAI,EAC7B,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,GAAG,IAAI,EACnD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,KAAK,gBAAgB,GAAG,OAAO,EAC5F,QAAQ,CAAC,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CA+FN"}
@@ -20,18 +20,22 @@
20
20
  * cannot escape into a neighbouring item or into nodes outside the
21
21
  * loop.
22
22
  * 3. The CSR-only `__bfExtras` stash. During renderItem-body setup
23
- * (between the template clone and the function's return), the
24
- * primary and extras are still detached nodes `__el.nextSibling`
25
- * is `null` and step 2 yields nothing. Reading `__bfExtras` lets
26
- * lookups reach the still-pending extras before `mapArray` inserts
27
- * them into the DOM.
23
+ * (between the template clone and the function's return), the extras
24
+ * are not siblings yet. Reading `__bfExtras` lets lookups reach them
25
+ * before `mapArray` inserts them into the DOM.
28
26
  *
29
- * Step 3's reliance on the primary being detached during setup is why
30
- * `createItemScope` un-parks a row that turns out to carry extras: the
31
- * connect-before-init mount point applies to `createComponent` row roots,
32
- * which are single-root by construction and so never reach this module. An
33
- * attached primary would make step 2's sibling walk run past the item's own
34
- * roots into a neighbouring item's elements before step 3 is ever consulted.
27
+ * Step 3 must stay reachable whether or not the primary is attached, which
28
+ * is why step 2 ends its walk with `break` rather than `return`. Only the
29
+ * sibling walk is bounded; ending the generator there would skip the stash.
30
+ *
31
+ * This used to be a `return`, and the difference was invisible: with a
32
+ * detached primary `nextSibling` is `null`, so the walk never runs and
33
+ * control reaches the stash regardless. Attaching the primary made the very
34
+ * first sibling a boundary comment, which ended the generator before the
35
+ * stash was consulted and left the item's child placeholders unreplaced.
36
+ * Measured, not deduced — and note this is NOT the failure the earlier
37
+ * comment here described: the walk never ran past the item into a
38
+ * neighbouring one, it stopped correctly and then gave up too early.
35
39
  */
36
40
  /**
37
41
  * Find an element matching `selector` within an item's range. Searches
@@ -1 +1 @@
1
- {"version":3,"file":"qsa-item.d.ts","sourceRoot":"","sources":["../../src/runtime/qsa-item.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAsCH;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAQnF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,OAAO,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EACrB,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,GAC3B,WAAW,GAAG,IAAI,CAuBpB"}
1
+ {"version":3,"file":"qsa-item.d.ts","sourceRoot":"","sources":["../../src/runtime/qsa-item.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AA4CH;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAQnF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,OAAO,EAClB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EACrB,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,GAC3B,WAAW,GAAG,IAAI,CAuBpB"}
@@ -1767,6 +1767,14 @@ function takeRowMountPoint() {
1767
1767
  _rowMountPoint = null;
1768
1768
  return p;
1769
1769
  }
1770
+ function mountRowRoot(el) {
1771
+ const p = takeRowMountPoint();
1772
+ if (p) {
1773
+ p.container.insertBefore(el, p.anchor);
1774
+ p.mounted = el;
1775
+ }
1776
+ return el;
1777
+ }
1770
1778
  function createComponent(nameOrDef, props = {}, key, slot, mountAt) {
1771
1779
  const element = materializeComponent(nameOrDef, props, key, slot, mountAt);
1772
1780
  if (mountAt && mountAt.parentNode && element !== mountAt) {
@@ -2144,7 +2152,7 @@ function* itemRootElements(primaryEl) {
2144
2152
  if (n.nodeType === Node.COMMENT_NODE) {
2145
2153
  const v = n.nodeValue ?? "";
2146
2154
  if (v === BF_LOOP_ITEM || v === BF_LOOP_START || v.startsWith(startPrefix) || v === BF_LOOP_END || v.startsWith(endPrefix)) {
2147
- return;
2155
+ break;
2148
2156
  }
2149
2157
  } else if (n.nodeType === Node.ELEMENT_NODE) {
2150
2158
  yield n;
@@ -2316,9 +2324,16 @@ function createItemScope(item, index, renderItem, existingPrimary, existingExtra
2316
2324
  const prevRowMount = ownsRowMount ? setRowMountPoint(rowMount) : null;
2317
2325
  try {
2318
2326
  primaryEl = renderItem(itemAccessor, index, existingPrimary);
2327
+ } catch (err) {
2328
+ const parked = rowMount?.mounted;
2329
+ if (parked?.parentNode)
2330
+ parked.remove();
2331
+ throw err;
2319
2332
  } finally {
2320
2333
  if (ownsRowMount)
2321
2334
  setRowMountPoint(prevRowMount);
2335
+ if (rowMount)
2336
+ rowMount.mounted = null;
2322
2337
  }
2323
2338
  if (existingPrimary) {
2324
2339
  extras = existingExtras ?? [];
@@ -3647,6 +3662,7 @@ export {
3647
3662
  parseHTML,
3648
3663
  onMount,
3649
3664
  onCleanup,
3665
+ mountRowRoot,
3650
3666
  mapArrayLazy,
3651
3667
  mapArrayAnchored,
3652
3668
  mapArray,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/client",
3
- "version": "0.28.1",
3
+ "version": "0.29.0",
4
4
  "description": "BarefootJS client package: reactive primitives (SSR-safe) plus browser runtime under the `/runtime` subpath (compiler target)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -55,7 +55,7 @@
55
55
  "directory": "packages/client"
56
56
  },
57
57
  "dependencies": {
58
- "@barefootjs/shared": "0.28.1"
58
+ "@barefootjs/shared": "0.29.0"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@barefootjs/jsx": ">=0.2.0"
@@ -25,8 +25,14 @@ export function setParentScopeId(id: string | null): void {
25
25
  _parentScopeId = id
26
26
  }
27
27
 
28
- /** Where `mapArray` wants a fresh loop row connected before its `init` runs. */
29
- export type RowMountPoint = { container: Node; anchor: Node | null }
28
+ /**
29
+ * Where `mapArray` wants a fresh loop row connected before its `init` runs.
30
+ *
31
+ * `mounted` is written back by `mountRowRoot` so the caller can undo the
32
+ * connection if the rest of the row's body then throws — see `createItemScope`.
33
+ * Without it a half-built row stays visible, which detached rows never did.
34
+ */
35
+ export type RowMountPoint = { container: Node; anchor: Node | null; mounted?: Element | null }
30
36
 
31
37
  // Ambient mount point for a loop row.
32
38
  //
@@ -60,6 +66,35 @@ function takeRowMountPoint(): RowMountPoint | null {
60
66
  return p
61
67
  }
62
68
 
69
+ /**
70
+ * Connect a loop row whose root is a template clone, before the emitted
71
+ * renderItem body's tail runs.
72
+ *
73
+ * The tail is where the row's nested children initialise (`upsertChild` and
74
+ * friends), and `useContext` resolves by walking `parentElement` — so a child
75
+ * that inits inside a detached row finds no ancestors and falls through to the
76
+ * global last-writer-wins context store, reading whichever provider on the page
77
+ * wrote last. `createComponent` row roots already avoid this by consuming the
78
+ * same ambient (step 7b); a clone root has no runtime function in the middle,
79
+ * so the compiler emits this call for it instead.
80
+ *
81
+ * Take-and-clear, like the `createComponent` path: a nested `createComponent`
82
+ * driven by this row's own children must not re-use the row's mount point.
83
+ *
84
+ * No-op when there is no ambient point — the hydration branch never calls this
85
+ * (its row came from SSR markup and is already in the document), and a loop
86
+ * runtime that does not hand one down leaves the row exactly as it was.
87
+ */
88
+ export function mountRowRoot(el: Element): Element {
89
+ const p = takeRowMountPoint()
90
+ if (p) {
91
+ p.container.insertBefore(el, p.anchor)
92
+ // Record it so the row can be un-mounted if the body throws after this.
93
+ p.mounted = el
94
+ }
95
+ return el
96
+ }
97
+
63
98
  /**
64
99
  * Create a component instance with DOM element and initialized state.
65
100
  *
@@ -99,6 +99,7 @@ export { registerTemplate, getTemplate, hasTemplate, type TemplateFn } from './t
99
99
  // Component creation
100
100
  export {
101
101
  createComponent,
102
+ mountRowRoot,
102
103
  renderChild,
103
104
  parseHTML,
104
105
  escapeAttr,
@@ -20,7 +20,7 @@
20
20
 
21
21
  import { createSignal, createEffect, createRoot } from '@barefootjs/client/reactive'
22
22
  import { hydratedScopes } from './hydration-state.ts'
23
- import { setRowMountPoint } from './component.ts'
23
+ import { setRowMountPoint, type RowMountPoint } from './component.ts'
24
24
  import {
25
25
  BF_KEY,
26
26
  BF_LOOP_START,
@@ -234,7 +234,7 @@ function createItemScope<T>(
234
234
  existingPrimary?: HTMLElement,
235
235
  existingExtras?: HTMLElement[],
236
236
  existingStart?: Comment | null,
237
- rowMount?: { container: Node; anchor: Node | null } | null,
237
+ rowMount?: RowMountPoint | null,
238
238
  ): ItemScope<T> {
239
239
  let primaryEl!: HTMLElement
240
240
  let dispose!: () => void
@@ -246,10 +246,11 @@ function createItemScope<T>(
246
246
  dispose = d
247
247
  const [itemAccessor, itemSetter] = createSignal(item)
248
248
  setItem = itemSetter
249
- // Fresh row: hand the mount point to the row's own `createComponent` so
250
- // its `init` observes a connected element. A renderItem body that clones a
251
- // template instead of calling `createComponent` (composite / plain loops)
252
- // leaves it unconsumed, hence the restore below rather than a plain clear.
249
+ // Fresh row: hand the mount point down so the row's own root — whether a
250
+ // `createComponent` call or a `mountRowRoot(clone)` observes a connected
251
+ // element when the body's tail initialises its children. A body that does
252
+ // neither (a plain loop's clone) leaves the point unconsumed, hence the
253
+ // restore below rather than a plain clear.
253
254
  //
254
255
  // Only touch the ambient when we are the one setting it, and put back what
255
256
  // was there rather than `null`: this row's `init` can drive a nested
@@ -259,8 +260,16 @@ function createItemScope<T>(
259
260
  const prevRowMount = ownsRowMount ? setRowMountPoint(rowMount) : null
260
261
  try {
261
262
  primaryEl = renderItem(itemAccessor, index, existingPrimary)
263
+ } catch (err) {
264
+ // A row that connected itself and then failed to finish would stay
265
+ // visible as a half-built row. Detached rows never could, so undo the
266
+ // connection before rethrowing and keep the failure mode as it was.
267
+ const parked = rowMount?.mounted
268
+ if (parked?.parentNode) parked.remove()
269
+ throw err
262
270
  } finally {
263
271
  if (ownsRowMount) setRowMountPoint(prevRowMount)
272
+ if (rowMount) rowMount.mounted = null
264
273
  }
265
274
  if (existingPrimary) {
266
275
  extras = existingExtras ?? []
@@ -20,18 +20,22 @@
20
20
  * cannot escape into a neighbouring item or into nodes outside the
21
21
  * loop.
22
22
  * 3. The CSR-only `__bfExtras` stash. During renderItem-body setup
23
- * (between the template clone and the function's return), the
24
- * primary and extras are still detached nodes `__el.nextSibling`
25
- * is `null` and step 2 yields nothing. Reading `__bfExtras` lets
26
- * lookups reach the still-pending extras before `mapArray` inserts
27
- * them into the DOM.
23
+ * (between the template clone and the function's return), the extras
24
+ * are not siblings yet. Reading `__bfExtras` lets lookups reach them
25
+ * before `mapArray` inserts them into the DOM.
28
26
  *
29
- * Step 3's reliance on the primary being detached during setup is why
30
- * `createItemScope` un-parks a row that turns out to carry extras: the
31
- * connect-before-init mount point applies to `createComponent` row roots,
32
- * which are single-root by construction and so never reach this module. An
33
- * attached primary would make step 2's sibling walk run past the item's own
34
- * roots into a neighbouring item's elements before step 3 is ever consulted.
27
+ * Step 3 must stay reachable whether or not the primary is attached, which
28
+ * is why step 2 ends its walk with `break` rather than `return`. Only the
29
+ * sibling walk is bounded; ending the generator there would skip the stash.
30
+ *
31
+ * This used to be a `return`, and the difference was invisible: with a
32
+ * detached primary `nextSibling` is `null`, so the walk never runs and
33
+ * control reaches the stash regardless. Attaching the primary made the very
34
+ * first sibling a boundary comment, which ended the generator before the
35
+ * stash was consulted and left the item's child placeholders unreplaced.
36
+ * Measured, not deduced — and note this is NOT the failure the earlier
37
+ * comment here described: the walk never ran past the item into a
38
+ * neighbouring one, it stopped correctly and then gave up too early.
35
39
  */
36
40
 
37
41
  import { BF_LOOP_ITEM, BF_LOOP_START, BF_LOOP_END } from '@barefootjs/shared'
@@ -51,10 +55,16 @@ function* itemRootElements(primaryEl: Element): Iterable<Element> {
51
55
  // Hard stops: another item starts, or the loop range ends. The
52
56
  // BF_LOOP_START check defends against a sibling loop block whose
53
57
  // start marker happens to follow ours.
58
+ // `break`, not `return`: stop the SIBLING walk, but still fall through
59
+ // to the `__bfExtras` stash below. A `return` ends the generator, and
60
+ // the stash is the only way to reach extras that are not siblings yet.
61
+ // That difference is invisible while the primary is detached — then
62
+ // `nextSibling` is `null`, this loop never runs, and control reaches the
63
+ // stash anyway — and decides the outcome the moment it is attached.
54
64
  if (v === BF_LOOP_ITEM
55
65
  || v === BF_LOOP_START || v.startsWith(startPrefix)
56
66
  || v === BF_LOOP_END || v.startsWith(endPrefix)) {
57
- return
67
+ break
58
68
  }
59
69
  } else if (n.nodeType === Node.ELEMENT_NODE) {
60
70
  yield n as Element