@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 +6 -0
- package/package.json +1 -1
- package/runtime/iterate.js +45 -0
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
package/runtime/iterate.js
CHANGED
|
@@ -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
|
};
|