@barefootjs/client 0.33.0 → 0.33.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.
@@ -169152,6 +169152,7 @@ var ErrorCodes = {
169152
169152
  MISSING_KEY_IN_LIST: "BF023",
169153
169153
  MISSING_KEY_IN_NESTED_LIST: "BF024",
169154
169154
  UNSUPPORTED_DESTRUCTURE_REST: "BF025",
169155
+ RETURN_VALUE_NOT_JSX: "BF027",
169155
169156
  PROPS_DESTRUCTURING: "BF043",
169156
169157
  SIGNAL_GETTER_NOT_CALLED: "BF044",
169157
169158
  JSX_IN_LOCAL_FUNCTION: "BF045",
@@ -169183,6 +169184,7 @@ var errorMessages = {
169183
169184
  [ErrorCodes.MISSING_KEY_IN_LIST]: "Missing key attribute in list rendering. Add a key prop for efficient updates",
169184
169185
  [ErrorCodes.MISSING_KEY_IN_NESTED_LIST]: "Nested .map() loop requires key attribute for event delegation. Add a key prop to elements in the inner loop",
169185
169186
  [ErrorCodes.UNSUPPORTED_DESTRUCTURE_REST]: "Computed property key in .map() callback destructure is not supported. Rewrite the callback to destructure explicit bindings (e.g., `({ a, b }) => ...`) so the compiler can rewrite references to per-item signal accessors.",
169187
+ [ErrorCodes.RETURN_VALUE_NOT_JSX]: "Component's return value is not recognized as JSX — return the JSX expression directly instead of binding it to a local variable first.",
169186
169188
  [ErrorCodes.PROPS_DESTRUCTURING]: "Props destructuring in function parameters breaks reactivity. Use props object directly.",
169187
169189
  [ErrorCodes.SIGNAL_GETTER_NOT_CALLED]: "Signal/memo getter passed without calling it. Use getter() to read the value.",
169188
169190
  [ErrorCodes.JSX_IN_LOCAL_FUNCTION]: "Local function returns JSX but cannot be inlined. Extract it as a top-level PascalCase component or use a single return statement.",
@@ -23,7 +23,7 @@ export { findScope, find, $, $c, $t, qsa, qsaChildScope, qsaChildScopes, cssEsca
23
23
  export { date } from './date.ts';
24
24
  export { hydrate, rehydrateAll, rehydrateScope, disposeScope, flushHydration, getRegisteredDef } from './hydrate.ts';
25
25
  export { registerComponent, getComponentInit, initChild, upsertChild } from './registry.ts';
26
- export { insert, type BranchConfig, type BranchTemplateResult } from './insert.ts';
26
+ export { insert, findCondContainer, type BranchConfig, type BranchTemplateResult } from './insert.ts';
27
27
  export { __bfSlot } from './branch-slot.ts';
28
28
  export { __bfText } from './dynamic-text.ts';
29
29
  export { hydratedScopes } from './hydration-state.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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"}
@@ -1487,7 +1487,14 @@ function renderChild(name, props, key, slotSuffix) {
1487
1487
  if (!templateFn) {
1488
1488
  return `<div ${bfsAttr}${slotAttrs}${keyAttr}></div>`;
1489
1489
  }
1490
- const raw = templateFn(props);
1490
+ const prevParentScopeId = _parentScopeId;
1491
+ _parentScopeId = `${scopePrefix}${suffix}`;
1492
+ let raw;
1493
+ try {
1494
+ raw = templateFn(props);
1495
+ } finally {
1496
+ _parentScopeId = prevParentScopeId;
1497
+ }
1491
1498
  let html = raw.trim().replace(PLACEHOLDER_ATTR_PATTERN, _parentScopeId ? ` bf-s="${_parentScopeId}"` : "");
1492
1499
  const firstElMatch = html.match(/<(\w+)/);
1493
1500
  if (!firstElMatch)
@@ -2916,6 +2923,15 @@ function findCondStartInRange(anchor, id) {
2916
2923
  }
2917
2924
  return null;
2918
2925
  }
2926
+ function findCondContainer(scope, id) {
2927
+ const want = `bf-cond-start:${id}`;
2928
+ for (const comment of commentsInScope(scope)) {
2929
+ if (comment.nodeValue === want) {
2930
+ return comment.parentElement ?? scope;
2931
+ }
2932
+ }
2933
+ return scope;
2934
+ }
2919
2935
  var EMPTY_SLOTS = [];
2920
2936
  function normalizeTemplate(value) {
2921
2937
  return typeof value === "string" ? { html: value, slots: EMPTY_SLOTS } : value;
@@ -3319,6 +3335,7 @@ export {
3319
3335
  flushHydration,
3320
3336
  findSiblingSlot,
3321
3337
  findScope,
3338
+ findCondContainer,
3322
3339
  find,
3323
3340
  escapeTextOrNode,
3324
3341
  escapeTextOrMarkup,
@@ -5,6 +5,27 @@
5
5
  * SolidJS-inspired replacement for legacy cond() that properly
6
6
  * handles event binding for both branches.
7
7
  */
8
+ /**
9
+ * Resolve the DOM element that actually hosts an element-scoped loop-row
10
+ * conditional's branch content (#2705). A nested `mapArray` inside the
11
+ * branch's `bindEvents` needs a container to search for its own loop
12
+ * markers, but has no `bf="<slot>"` marker to query when the conditional
13
+ * sits inside a wrapper element the container-slot collector never visits
14
+ * (`collectInnerLoops`'s branch walk only sees elements INSIDE the
15
+ * branch's own IR subtree — a wrapper enclosing the conditional from the
16
+ * outside, e.g. `<article>{cond && items.map(...)}</article>`, is never
17
+ * one of them, so `containerSlotId` comes back `null`).
18
+ *
19
+ * The one DOM anchor available in that case is the conditional's own
20
+ * `<!--bf-cond-start:id-->` marker: whatever element that marker's parent
21
+ * is, is where `insert()` keeps the branch's content — adopted from SSR or
22
+ * freshly spliced by `updateFragmentConditional`, either way the marker
23
+ * stays put as one of the range's boundaries. Falls back to `scope` itself
24
+ * when no such marker is found (an element-conditional branch with no
25
+ * comment markers, or a conditional with no wrapper at all — `scope` IS
26
+ * the right container in that shape too) — never throws.
27
+ */
28
+ export declare function findCondContainer(scope: Element, id: string): Element;
8
29
  /**
9
30
  * Result returned by a branch's `template()` when the template captures
10
31
  * live DOM nodes via `__bfSlot` (#1213). `html` carries the marker-bearing
@@ -1 +1 @@
1
- {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../src/runtime/insert.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAmFH;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,IAAI,EAAE,CAAA;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,QAAQ,EAAE,MAAM,MAAM,GAAG,oBAAoB,CAAA;IAE7C;;;;;;OAMG;IACH,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;CACrF;AAyBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,EAC/B,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,OAAO,EAC1B,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,YAAY,EACvB,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CA+IN"}
1
+ {"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../../src/runtime/insert.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAmFH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAQrE;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,IAAI,EAAE,CAAA;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,QAAQ,EAAE,MAAM,MAAM,GAAG,oBAAoB,CAAA;IAE7C;;;;;;OAMG;IACH,UAAU,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;CACrF;AAyBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,EAC/B,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,OAAO,EAC1B,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,YAAY,EACvB,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI,CA+IN"}
@@ -1893,7 +1893,14 @@ function renderChild(name, props, key, slotSuffix) {
1893
1893
  if (!templateFn) {
1894
1894
  return `<div ${bfsAttr}${slotAttrs}${keyAttr}></div>`;
1895
1895
  }
1896
- const raw = templateFn(props);
1896
+ const prevParentScopeId = _parentScopeId;
1897
+ _parentScopeId = `${scopePrefix}${suffix}`;
1898
+ let raw;
1899
+ try {
1900
+ raw = templateFn(props);
1901
+ } finally {
1902
+ _parentScopeId = prevParentScopeId;
1903
+ }
1897
1904
  let html = raw.trim().replace(PLACEHOLDER_ATTR_PATTERN, _parentScopeId ? ` bf-s="${_parentScopeId}"` : "");
1898
1905
  const firstElMatch = html.match(/<(\w+)/);
1899
1906
  if (!firstElMatch)
@@ -3316,6 +3323,15 @@ function findCondStartInRange(anchor, id) {
3316
3323
  }
3317
3324
  return null;
3318
3325
  }
3326
+ function findCondContainer(scope, id) {
3327
+ const want = `bf-cond-start:${id}`;
3328
+ for (const comment of commentsInScope(scope)) {
3329
+ if (comment.nodeValue === want) {
3330
+ return comment.parentElement ?? scope;
3331
+ }
3332
+ }
3333
+ return scope;
3334
+ }
3319
3335
  var EMPTY_SLOTS = [];
3320
3336
  function normalizeTemplate(value) {
3321
3337
  return typeof value === "string" ? { html: value, slots: EMPTY_SLOTS } : value;
@@ -3718,6 +3734,7 @@ export {
3718
3734
  flushHydration,
3719
3735
  findSiblingSlot,
3720
3736
  findScope,
3737
+ findCondContainer,
3721
3738
  find,
3722
3739
  escapeTextOrNode,
3723
3740
  escapeTextOrMarkup,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@barefootjs/client",
3
- "version": "0.33.0",
3
+ "version": "0.33.1",
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.33.0"
58
+ "@barefootjs/shared": "0.33.1"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@barefootjs/jsx": ">=0.2.0"
@@ -441,25 +441,25 @@ export function renderChild(
441
441
  return `<div ${bfsAttr}${slotAttrs}${keyAttr}></div>`
442
442
  }
443
443
 
444
- // NOTE: does NOT push `_parentScopeId` to this child's own derived scope
445
- // while `templateFn` evaluates. That was tried (deriving a GRANDCHILD's
446
- // scope from this scope rather than the caller's, to fix a third
447
- // composition level collapsing onto the second `grandchild-composition`)
448
- // but it collides with `comment: true` wrapper transparency (#1211
449
- // synthesized inline-JSX-callback wrappers, e.g. a `renderNode` callback
450
- // prop): such a wrapper's OWN scope element IS its single child's element,
451
- // and its init resolves itself via `$c(scope, 's0')`'s short-suffix
452
- // self-match fallback (`query.ts`'s `$cSingle`). Pushing this scope makes
453
- // a nested descendant of that SAME child derive `..._s0_s0` whenever the
454
- // descendant's own first slot id also happens to be `s0` — the PRECISE
455
- // suffix match then finds that unrelated descendant instead of falling
456
- // through to the self-match, silently misrouting `initChild` (confirmed
457
- // via `site/ui`'s xyflow Highlight-Depth demo, where this broke the
458
- // `--node-glow` style effect). `grandchild-composition` stays a known
459
- // limitation (CSR conformance skip) until the self-match lookup can be
460
- // made unambiguous against a nested descendant sharing the same slot
461
- // suffix.
462
- const raw = templateFn(props)
444
+ // Push `_parentScopeId` to THIS child's own derived scope while its
445
+ // `templateFn` evaluates, so a grandchild rendered inside it derives its
446
+ // scope from THIS scope rather than the caller's (#2649). Without this a
447
+ // third composition level collapses onto the second: with `_parentScopeId`
448
+ // left at the caller's id, a grandchild whose slot suffix happens to match
449
+ // this child's OWN slot suffix computes the exact same string as this
450
+ // child's bf-s (`grandchild-composition`'s `test_s0` reused instead of
451
+ // deriving `test_s0_s0`). Restored via `finally` so a sibling rendered
452
+ // after this call (or the caller's own remaining template) sees the
453
+ // original `_parentScopeId`, matching the caller-restore discipline
454
+ // `materializeComponent` already uses around its own `templateFn` call.
455
+ const prevParentScopeId = _parentScopeId
456
+ _parentScopeId = `${scopePrefix}${suffix}`
457
+ let raw: string
458
+ try {
459
+ raw = templateFn(props)
460
+ } finally {
461
+ _parentScopeId = prevParentScopeId
462
+ }
463
463
 
464
464
  // The placeholder substitution is anchored to the exact `bf-s="…"`
465
465
  // shape so user content that contains the sentinel as text survives
@@ -125,7 +125,7 @@ export { findScope, find, $, $c, $t, qsa, qsaChildScope, qsaChildScopes, cssEsca
125
125
  export { date } from './date.ts'
126
126
  export { hydrate, rehydrateAll, rehydrateScope, disposeScope, flushHydration, getRegisteredDef } from './hydrate.ts'
127
127
  export { registerComponent, getComponentInit, initChild, upsertChild } from './registry.ts'
128
- export { insert, type BranchConfig, type BranchTemplateResult } from './insert.ts'
128
+ export { insert, findCondContainer, type BranchConfig, type BranchTemplateResult } from './insert.ts'
129
129
  export { __bfSlot } from './branch-slot.ts'
130
130
  // `__bfText` (dynamic-text.ts) and `$t` (query.ts) are kept: one narrow
131
131
  // emission site — a `@client`-nested-inside-a-top-level-conditional dynamic
@@ -87,6 +87,36 @@ function findCondStartInRange(anchor: Comment, id: string): Comment | null {
87
87
  return null
88
88
  }
89
89
 
90
+ /**
91
+ * Resolve the DOM element that actually hosts an element-scoped loop-row
92
+ * conditional's branch content (#2705). A nested `mapArray` inside the
93
+ * branch's `bindEvents` needs a container to search for its own loop
94
+ * markers, but has no `bf="<slot>"` marker to query when the conditional
95
+ * sits inside a wrapper element the container-slot collector never visits
96
+ * (`collectInnerLoops`'s branch walk only sees elements INSIDE the
97
+ * branch's own IR subtree — a wrapper enclosing the conditional from the
98
+ * outside, e.g. `<article>{cond && items.map(...)}</article>`, is never
99
+ * one of them, so `containerSlotId` comes back `null`).
100
+ *
101
+ * The one DOM anchor available in that case is the conditional's own
102
+ * `<!--bf-cond-start:id-->` marker: whatever element that marker's parent
103
+ * is, is where `insert()` keeps the branch's content — adopted from SSR or
104
+ * freshly spliced by `updateFragmentConditional`, either way the marker
105
+ * stays put as one of the range's boundaries. Falls back to `scope` itself
106
+ * when no such marker is found (an element-conditional branch with no
107
+ * comment markers, or a conditional with no wrapper at all — `scope` IS
108
+ * the right container in that shape too) — never throws.
109
+ */
110
+ export function findCondContainer(scope: Element, id: string): Element {
111
+ const want = `bf-cond-start:${id}`
112
+ for (const comment of commentsInScope(scope)) {
113
+ if (comment.nodeValue === want) {
114
+ return (comment.parentElement as Element | null) ?? scope
115
+ }
116
+ }
117
+ return scope
118
+ }
119
+
90
120
  /**
91
121
  * Result returned by a branch's `template()` when the template captures
92
122
  * live DOM nodes via `__bfSlot` (#1213). `html` carries the marker-bearing