@ape-egg/vibe 2.1.17 → 2.1.18

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.1.18] - 2026-06-22
4
+
5
+ ### Fixed
6
+
7
+ - **Loop-scoped bindings in slot content passed to a component inside an iteration didn't resolve** (`runtime/iterate.js`) — content projected into a `<component src>` is captured raw (`_vibeSlotContent`) and inlined only when `processComponent` runs, by which point the row's iteration scope is gone. So a `@[...]` in that slot content rooted in a loop alias (`item`/`index`/outer) or `this` couldn't resolve later: value bindings rendered `undefined` and name-bindings (`<icon @[row.icon]>`) never set their attribute. The new `resolveSlotContentBindings` pre-resolves those bindings into registry-backed global refs — the same snapshot mechanism the component's own prop attributes use — before the scope is lost, so the inlined slot hydrates against the right values and the row's update path refreshes them in place (the wrapper inherits `_vibeIterPropExprs` / `data-vibe-iter-prop`, so `refreshIterationComponentProps` re-evaluates them on each item change). Globals-only bindings are left raw and resolve through the normal reactive path. Tests: `e2e-runtime/slot-name-binding-loop.html`, `tests/e2e/slot-name-binding.spec.js`.
8
+
3
9
  ## [2.1.17] - 2026-06-22
4
10
 
5
11
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ape-egg/vibe",
3
- "version": "2.1.17",
3
+ "version": "2.1.18",
4
4
  "type": "module",
5
5
  "description": "Runtime-first reactivity with optional compiler",
6
6
  "main": "index.js",
@@ -368,6 +368,50 @@ export const releaseOrphanedIterationProps = (nodes) => {
368
368
  }
369
369
  };
370
370
 
371
+ // Slot content projected into a <component src> is captured raw (`_vibeSlotContent`)
372
+ // and inlined only when processComponent runs — by which point the row's iteration
373
+ // scope is gone. Any `@[...]` in that content rooted in a loop alias (item/index/
374
+ // outer) or `this` therefore can't resolve later: value bindings render undefined
375
+ // and name-bindings (`<icon @[row.icon]>`) never set their attribute. Pre-resolve
376
+ // those into registry-backed global refs here — the same snapshot mechanism the
377
+ // component's own prop attributes use — so the inlined slot hydrates against the
378
+ // right values and the row's update path refreshes them in place (the wrapper
379
+ // inherits `_vibeIterPropExprs`/`data-vibe-iter-prop`, so refreshIterationComponentProps
380
+ // re-evaluates them on each item change). Globals-only bindings are left raw; they
381
+ // resolve through the normal reactive path against the inlined component's scope.
382
+ const resolveSlotContentBindings = (el, scopedState, aliases) => {
383
+ const html = el._vibeSlotContent;
384
+ if (!html || !html.includes('@[')) return;
385
+ const registry = ensureIterPropsRegistry();
386
+ const idByExpr = new Map();
387
+ const rewritten = html.replace(BINDING_REGEX, (whole, expr) => {
388
+ const usesLocalScope =
389
+ /\bthis\b/.test(expr) ||
390
+ (aliases && extractDependencies(expr).some((d) => aliases.has(d)));
391
+ if (!usesLocalScope) return whole;
392
+ let id = idByExpr.get(expr);
393
+ if (id === undefined) {
394
+ let value;
395
+ try {
396
+ value = evalInScope(expr, scopedState, el);
397
+ } catch {
398
+ return whole;
399
+ }
400
+ if (value === undefined) return whole;
401
+ id = `_p${__vibeIterPropCounter++}`;
402
+ registry[id] = value;
403
+ idByExpr.set(expr, id);
404
+ (el._vibeIterPropExprs = el._vibeIterPropExprs || []).push({ id, attrName: null, expr });
405
+ (el._vibeIterPropIds = el._vibeIterPropIds || []).push(id);
406
+ }
407
+ return `@[window.__vibeiterprops.${id}]`;
408
+ });
409
+ if (idByExpr.size) {
410
+ el._vibeSlotContent = rewritten;
411
+ el.setAttribute('data-vibe-iter-prop', '');
412
+ }
413
+ };
414
+
371
415
  // For <component src> elements inside an iteration instance, evaluate any
372
416
  // `@[expr]` attribute bindings against the iteration's scoped state and route
373
417
  // every resolved value through the global iteration-prop registry. The prop
@@ -433,6 +477,7 @@ export const resolveIterationComponentProps = (nodes, scopedState, aliases) => {
433
477
  // Leave binding raw — processComponent will handle it as a binding
434
478
  }
435
479
  }
480
+ resolveSlotContentBindings(el, scopedState, aliases);
436
481
  }
437
482
  }
438
483
  };