@barefootjs/jsx 0.34.0 → 0.35.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.
- package/dist/analyzer-context.d.ts.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1072 -903
- package/dist/ir-to-client-js/collect-elements.d.ts +22 -2
- package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-component-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-composite-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-inner-loop.d.ts +14 -2
- package/dist/ir-to-client-js/control-flow/plan/build-inner-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts +11 -0
- package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts +6 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-plain-row.d.ts +52 -0
- package/dist/ir-to-client-js/control-flow/plan/build-plain-row.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts +5 -2
- package/dist/ir-to-client-js/control-flow/plan/build-reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/inner-loop.d.ts +24 -44
- package/dist/ir-to-client-js/control-flow/plan/inner-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/lazy-conditional.d.ts +6 -7
- package/dist/ir-to-client-js/control-flow/plan/lazy-conditional.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/lazy-preamble.d.ts +5 -7
- package/dist/ir-to-client-js/control-flow/plan/lazy-preamble.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts +14 -0
- package/dist/ir-to-client-js/control-flow/plan/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +10 -6
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/shared.d.ts +11 -9
- package/dist/ir-to-client-js/control-flow/shared.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/composite-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/inner-loop.d.ts +6 -14
- package/dist/ir-to-client-js/control-flow/stringify/inner-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts +1 -3
- package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts +19 -3
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts +5 -1
- package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/csr-substitute.d.ts +16 -0
- package/dist/ir-to-client-js/csr-substitute.d.ts.map +1 -1
- package/dist/ir-to-client-js/emit-reactive.d.ts +54 -0
- package/dist/ir-to-client-js/emit-reactive.d.ts.map +1 -1
- package/dist/ir-to-client-js/emit-registration.d.ts.map +1 -1
- package/dist/ir-to-client-js/html-template.d.ts +2 -2
- package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
- package/dist/ir-to-client-js/imports.d.ts +2 -2
- package/dist/ir-to-client-js/imports.d.ts.map +1 -1
- package/dist/ir-to-client-js/index.d.ts.map +1 -1
- package/dist/ir-to-client-js/markup-slots.d.ts +21 -0
- package/dist/ir-to-client-js/markup-slots.d.ts.map +1 -0
- package/dist/ir-to-client-js/reactivity.d.ts +62 -18
- package/dist/ir-to-client-js/reactivity.d.ts.map +1 -1
- package/dist/ir-to-client-js/safe-html.d.ts +158 -0
- package/dist/ir-to-client-js/safe-html.d.ts.map +1 -0
- package/dist/ir-to-client-js/types.d.ts +18 -2
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/ir-to-client-js/utils.d.ts +34 -1
- package/dist/ir-to-client-js/utils.d.ts.map +1 -1
- package/dist/jsx-to-ir.d.ts.map +1 -1
- package/dist/prop-rewrite.d.ts +8 -3
- package/dist/prop-rewrite.d.ts.map +1 -1
- package/dist/props-binding.d.ts +22 -1
- package/dist/props-binding.d.ts.map +1 -1
- package/dist/reactivity-checker.d.ts +18 -0
- package/dist/reactivity-checker.d.ts.map +1 -1
- package/dist/ssr-defaults.d.ts.map +1 -1
- package/dist/types.d.ts +5 -10
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +138 -22
- package/src/__tests__/binding-scope-ratchet.test.ts +25 -2
- package/src/__tests__/build-plain-row-consolidation.test.ts +120 -0
- package/src/__tests__/child-prop-fallback-wrap.test.ts +4 -2
- package/src/__tests__/client-template-escape-soundness.test.ts +538 -0
- package/src/__tests__/compiler-stress-1244.test.ts +8 -8
- package/src/__tests__/csr-template-loop-shadowing.test.ts +4 -2
- package/src/__tests__/flatmap-segments.test.ts +1 -1
- package/src/__tests__/issue-2868-cond-arm-tag-collision.test.ts +185 -0
- package/src/__tests__/issue-2869-attr-dedup-guard.test.ts +141 -0
- package/src/__tests__/lazy-conditional.test.ts +52 -12
- package/src/__tests__/lazy-preamble.test.ts +11 -11
- package/src/__tests__/lazy-row-eligibility.test.ts +8 -3
- package/src/__tests__/loop-child-reactive-attr-const-shadow.test.ts +25 -3
- package/src/__tests__/loop-index-reactivity.test.ts +208 -0
- package/src/__tests__/loop-item-conditional-codegen.test.ts +4 -2
- package/src/__tests__/map-arbitrary-body.test.ts +1 -1
- package/src/__tests__/map-body-brand-checker.test.ts +176 -0
- package/src/__tests__/nested-loop-index-param.test.ts +41 -8
- package/src/__tests__/nested-loop-non-outer-reactivity.test.ts +177 -0
- package/src/__tests__/nested-loop-plain.test.ts +39 -25
- package/src/__tests__/nested-loop-reactive-attrs.test.ts +16 -6
- package/src/__tests__/preamble-branch-arm-rerun.test.ts +135 -0
- package/src/__tests__/reactive-attrs-in-map.test.ts +13 -10
- package/src/__tests__/rewrite-destructured-props.test.ts +37 -0
- package/src/__tests__/safe-html-splice-door.test.ts +61 -0
- package/src/__tests__/ssr-defaults.test.ts +59 -0
- package/src/__tests__/style-css-var-reactive.test.ts +4 -1
- package/src/analyzer-context.ts +12 -10
- package/src/analyzer.ts +4 -0
- package/src/index.ts +8 -2
- package/src/ir-to-client-js/collect-elements.ts +126 -32
- package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +20 -28
- package/src/ir-to-client-js/control-flow/plan/build-component-loop.ts +9 -7
- package/src/ir-to-client-js/control-flow/plan/build-composite-loop.ts +12 -6
- package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +36 -59
- package/src/ir-to-client-js/control-flow/plan/build-lazy-row.ts +25 -4
- package/src/ir-to-client-js/control-flow/plan/build-loop-child-arm.ts +36 -9
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +35 -35
- package/src/ir-to-client-js/control-flow/plan/build-plain-row.ts +116 -0
- package/src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts +23 -8
- package/src/ir-to-client-js/control-flow/plan/inner-loop.ts +24 -45
- package/src/ir-to-client-js/control-flow/plan/lazy-conditional.ts +11 -10
- package/src/ir-to-client-js/control-flow/plan/lazy-preamble.ts +11 -13
- package/src/ir-to-client-js/control-flow/plan/lazy-row-eligibility.ts +13 -3
- package/src/ir-to-client-js/control-flow/plan/loop-child-arm.ts +14 -0
- package/src/ir-to-client-js/control-flow/plan/loop.ts +10 -6
- package/src/ir-to-client-js/control-flow/shared.ts +40 -17
- package/src/ir-to-client-js/control-flow/stringify/composite-loop.ts +2 -1
- package/src/ir-to-client-js/control-flow/stringify/inner-loop.ts +20 -66
- package/src/ir-to-client-js/control-flow/stringify/insert.ts +4 -2
- package/src/ir-to-client-js/control-flow/stringify/lazy-row.ts +22 -15
- package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +53 -11
- package/src/ir-to-client-js/control-flow/stringify/loop.ts +4 -2
- package/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts +29 -18
- package/src/ir-to-client-js/csr-substitute.ts +29 -0
- package/src/ir-to-client-js/emit-reactive.ts +92 -6
- package/src/ir-to-client-js/emit-registration.ts +2 -4
- package/src/ir-to-client-js/html-template.ts +67 -245
- package/src/ir-to-client-js/imports.ts +3 -0
- package/src/ir-to-client-js/index.ts +4 -4
- package/src/ir-to-client-js/markup-slots.ts +25 -0
- package/src/ir-to-client-js/reactivity.ts +73 -27
- package/src/ir-to-client-js/safe-html.ts +243 -0
- package/src/ir-to-client-js/types.ts +19 -3
- package/src/ir-to-client-js/utils.ts +58 -10
- package/src/jsx-to-ir.ts +136 -24
- package/src/prop-rewrite.ts +8 -4
- package/src/props-binding.ts +37 -1
- package/src/reactivity-checker.ts +60 -0
- package/src/ssr-defaults.ts +45 -0
- package/src/types.ts +5 -10
- package/src/__tests__/text-slot-escaping.test.ts +0 -65
|
@@ -441,7 +441,7 @@ export function initExample(__scope, _p = {}) {
|
|
|
441
441
|
|
|
442
442
|
mapArray(() => (todos()).flatMap((todo) => {
|
|
443
443
|
if (todo.hidden) return []
|
|
444
|
-
return todo.tags.map(tag => ({ k: (\`\${todo.id}:\${tag}\`), h: \`<li>\${escapeText(
|
|
444
|
+
return todo.tags.map(tag => ({ k: (\`\${todo.id}:\${tag}\`), h: \`<li>\${escapeText(tag)}</li>\` }))
|
|
445
445
|
}), _s0, (__bfD, __bfI) => String(__bfD.k ?? __bfI), (__bfD, __idx, __existing) => {
|
|
446
446
|
let __el = __existing
|
|
447
447
|
if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }
|
|
@@ -458,7 +458,7 @@ export function initExample(__scope, _p = {}) {
|
|
|
458
458
|
|
|
459
459
|
hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s0"><!--bf-loop:l0-->\${([]).flatMap((todo) => {
|
|
460
460
|
if (todo.hidden) return []
|
|
461
|
-
return todo.tags.map(tag => \`<li>\${escapeText(
|
|
461
|
+
return todo.tags.map(tag => \`<li>\${escapeText(tag)}</li>\`)
|
|
462
462
|
}).join('')}<!--bf-/loop:l0--></div>\` })
|
|
463
463
|
export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
|
|
464
464
|
`;
|
|
@@ -2550,6 +2550,99 @@ hydrate('StatementExample', { init: initStatementExample, template: (_p) => \`<d
|
|
|
2550
2550
|
export function StatementExample(_p, __bfKey) { return createComponent('StatementExample', _p, __bfKey) }"
|
|
2551
2551
|
`;
|
|
2552
2552
|
|
|
2553
|
+
exports[`docs/core/reactivity/untrack.md doc-examples L79 — (no label) 1`] = `
|
|
2554
|
+
"import { $, createComponent, createSignal, escapeAttr, escapeText, hydrate, lazySlots, mapArrayLazy, qsa, textOrNode, untrack } from '@barefootjs/client/runtime'
|
|
2555
|
+
|
|
2556
|
+
var renderPreview = renderPreview ?? function(id) {
|
|
2557
|
+
return \`<p>Preview for item \${id}</p>\`
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
export function initGallery(__scope, _p = {}) {
|
|
2561
|
+
if (!__scope) return
|
|
2562
|
+
const __scopeId = __scope.getAttribute('bf-s')
|
|
2563
|
+
|
|
2564
|
+
const [items, setItems] = createSignal([{ id: 1, label: 'First' }, { id: 2, label: 'Second' }])
|
|
2565
|
+
const rename = () => setItems(items().map(item => ({ id: item.id, label: item.label + '!' })))
|
|
2566
|
+
|
|
2567
|
+
const [_s0, _s3] = $(__scope, 's0', 's3')
|
|
2568
|
+
|
|
2569
|
+
if (_s0) _s0.addEventListener('click', rename)
|
|
2570
|
+
const __tpl_l0 = document.createElement('template')
|
|
2571
|
+
__tpl_l0.innerHTML = \`<li data-key="" bf="s2"><!--bf:s1--><!--/--></li>\`
|
|
2572
|
+
const __lzp_l0 = [[0]]
|
|
2573
|
+
const __lzs_l0 = [{ id: 's1', kind: 'text', path: [] }]
|
|
2574
|
+
const __lzsc_l0 = [{ id: 's1', kind: 'text', path: __lzp_l0[0] }]
|
|
2575
|
+
mapArrayLazy(() => items(), _s3, (item) => String(item.id), {
|
|
2576
|
+
createRow: (__e, __idx) => {
|
|
2577
|
+
const item = () => __e.item
|
|
2578
|
+
const __el = __tpl_l0.content.firstElementChild.cloneNode(true)
|
|
2579
|
+
const __r = __e.refs = [__el, lazySlots(__el, __lzsc_l0)]
|
|
2580
|
+
const __l = __e.last = []
|
|
2581
|
+
{ const __t = __r[0]
|
|
2582
|
+
if (__t) {
|
|
2583
|
+
{ const __x = item().label
|
|
2584
|
+
{ const __v = __x; if (__v != null) __t.setAttribute('title', String(__v)); else __t.removeAttribute('title') }
|
|
2585
|
+
__l[0] = __x }
|
|
2586
|
+
} }
|
|
2587
|
+
{ const __t = __r[0]
|
|
2588
|
+
if (__t) {
|
|
2589
|
+
{ const __x = untrack(() => renderPreview(item().id))
|
|
2590
|
+
{ const __v = __x; if (__v != null) __t.setAttribute('data-preview', String(__v)); else __t.removeAttribute('data-preview') }
|
|
2591
|
+
__l[1] = __x }
|
|
2592
|
+
} }
|
|
2593
|
+
{ const __x = item().label
|
|
2594
|
+
__r[1]('s1', textOrNode(__x))
|
|
2595
|
+
__l[2] = __x }
|
|
2596
|
+
return __el
|
|
2597
|
+
},
|
|
2598
|
+
applyItem: (__e) => {
|
|
2599
|
+
const item = () => __e.item
|
|
2600
|
+
const __r = __e.refs ?? (__e.refs = [])
|
|
2601
|
+
const __l = __e.last ?? (__e.last = [])
|
|
2602
|
+
{ const __t = 0 in __r ? __r[0] : (__r[0] = qsa(__e.primaryEl, '[bf="s2"]'))
|
|
2603
|
+
if (__t) {
|
|
2604
|
+
{ const __x = item().label
|
|
2605
|
+
if (!(0 in __l) || !Object.is(__l[0], __x)) {
|
|
2606
|
+
{ const __v = __x; if (__v != null) __t.setAttribute('title', String(__v)); else __t.removeAttribute('title') }
|
|
2607
|
+
}
|
|
2608
|
+
__l[0] = __x }
|
|
2609
|
+
} }
|
|
2610
|
+
{ const __t = 0 in __r ? __r[0] : (__r[0] = qsa(__e.primaryEl, '[bf="s2"]'))
|
|
2611
|
+
if (__t) {
|
|
2612
|
+
{ const __x = untrack(() => renderPreview(item().id))
|
|
2613
|
+
if (!(1 in __l) || !Object.is(__l[1], __x)) {
|
|
2614
|
+
{ const __v = __x; if (__v != null) __t.setAttribute('data-preview', String(__v)); else __t.removeAttribute('data-preview') }
|
|
2615
|
+
}
|
|
2616
|
+
__l[1] = __x }
|
|
2617
|
+
} }
|
|
2618
|
+
const __d = __r[1] ?? (__r[1] = lazySlots(__e.primaryEl, __lzs_l0))
|
|
2619
|
+
{ const __x = item().label
|
|
2620
|
+
if (!(2 in __l) || !Object.is(__l[2], __x)) __d('s1', textOrNode(__x))
|
|
2621
|
+
__l[2] = __x }
|
|
2622
|
+
},
|
|
2623
|
+
applyOuter: (__es, __seed) => {
|
|
2624
|
+
for (const __e of __es) {
|
|
2625
|
+
const item = () => __e.item
|
|
2626
|
+
const __r = __e.refs ?? (__e.refs = [])
|
|
2627
|
+
const __l = __e.last ?? (__e.last = [])
|
|
2628
|
+
{ const __t = 0 in __r ? __r[0] : (__r[0] = qsa(__e.primaryEl, '[bf="s2"]'))
|
|
2629
|
+
if (__t) {
|
|
2630
|
+
{ const __x = untrack(() => renderPreview(item().id))
|
|
2631
|
+
if (__seed ? (__t.getAttribute('data-preview') !== (__x != null ? String(__x) : null)) : (!(1 in __l) || !Object.is(__l[1], __x))) {
|
|
2632
|
+
{ const __v = __x; if (__v != null) __t.setAttribute('data-preview', String(__v)); else __t.removeAttribute('data-preview') }
|
|
2633
|
+
}
|
|
2634
|
+
__l[1] = __x }
|
|
2635
|
+
} }
|
|
2636
|
+
}
|
|
2637
|
+
},
|
|
2638
|
+
}, 'l0')
|
|
2639
|
+
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
hydrate('Gallery', { init: initGallery, template: (_p) => \`<div><button bf="s0">Rename all</button><ul bf="s3"><!--bf-loop:l0-->\${([{ id: 1, label: 'First' }, { id: 2, label: 'Second' }]).map((item) => \`<li data-key="\${escapeAttr(item.id)}" \${(item.label) != null ? 'title="' + escapeAttr(item.label) + '"' : ''} \${(untrack(() => renderPreview(item.id))) != null ? 'data-preview="' + escapeAttr(untrack(() => renderPreview(item.id))) + '"' : ''} bf="s2"><!--bf:s1-->\${escapeText(item.label)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></ul></div>\` })
|
|
2643
|
+
export function Gallery(_p, __bfKey) { return createComponent('Gallery', _p, __bfKey) }"
|
|
2644
|
+
`;
|
|
2645
|
+
|
|
2553
2646
|
exports[`docs/core/reactivity/props-reactivity.md doc-examples L16 — (no label) 1`] = `
|
|
2554
2647
|
"import { createComponent, hydrate } from '@barefootjs/client/runtime'
|
|
2555
2648
|
|
|
@@ -2635,12 +2728,13 @@ export function initExample(__scope, _p = {}) {
|
|
|
2635
2728
|
})
|
|
2636
2729
|
|
|
2637
2730
|
// Reactive child component props
|
|
2731
|
+
{ const __l = []
|
|
2638
2732
|
createEffect(() => {
|
|
2639
2733
|
const [__Child_s0El] = $c(__scope, 's0')
|
|
2640
2734
|
if (__Child_s0El) {
|
|
2641
2735
|
if ('value' in __Child_s0El) { const __val = String(count()); if (__Child_s0El.value !== __val) __Child_s0El.value = __val }
|
|
2642
2736
|
}
|
|
2643
|
-
})
|
|
2737
|
+
}) }
|
|
2644
2738
|
|
|
2645
2739
|
// Initialize child components with props
|
|
2646
2740
|
initChild('Child', _s0, { get value() { return count() } })
|
|
@@ -2651,7 +2745,7 @@ export function Example(_p, __bfKey) { return createComponent('Example', _p, __b
|
|
|
2651
2745
|
`;
|
|
2652
2746
|
|
|
2653
2747
|
exports[`docs/core/reactivity/props-reactivity.md doc-examples L85 — Compiled props object 1`] = `
|
|
2654
|
-
"import { createComponent, createEffect, createSignal, escapeTextOrMarkup, escapeTextOrNode, hydrate, lazySlots } from '@barefootjs/client/runtime'
|
|
2748
|
+
"import { createComponent, createEffect, createSignal, escapeTextOrMarkup, escapeTextOrNode, hydrate, lazySlots, escapeText } from '@barefootjs/client/runtime'
|
|
2655
2749
|
|
|
2656
2750
|
export function initTodoItem(__scope, _p = {}) {
|
|
2657
2751
|
if (!__scope) return
|
|
@@ -2688,8 +2782,6 @@ export function Dashboard(_p, __bfKey) { return createComponent('Dashboard__b3c3
|
|
|
2688
2782
|
export function initExample(__scope, _p = {}) {
|
|
2689
2783
|
if (!__scope) return
|
|
2690
2784
|
const __scopeId = __scope.getAttribute('bf-s')
|
|
2691
|
-
|
|
2692
|
-
const children = _p.children
|
|
2693
2785
|
const [count, setCount] = createSignal(0)
|
|
2694
2786
|
const [isLoggedIn] = createSignal(false)
|
|
2695
2787
|
const [todos] = createSignal([])
|
|
@@ -2700,7 +2792,7 @@ export function initExample(__scope, _p = {}) {
|
|
|
2700
2792
|
|
|
2701
2793
|
}
|
|
2702
2794
|
|
|
2703
|
-
hydrate('Example', { init: initExample, template: (_p) => \`<div>\${get} value() \${} return count() } } </div>\` })
|
|
2795
|
+
hydrate('Example', { init: initExample, template: (_p) => \`<div>\${escapeText(get)} value() \${escapeText()} return count() } } </div>\` })
|
|
2704
2796
|
export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
|
|
2705
2797
|
`;
|
|
2706
2798
|
|
|
@@ -2752,7 +2844,7 @@ export function Counter(_p, __bfKey) { return createComponent('Counter', _p, __b
|
|
|
2752
2844
|
`;
|
|
2753
2845
|
|
|
2754
2846
|
exports[`docs/core/components/component-authoring.mdx doc-examples L79 — (no label) 1`] = `
|
|
2755
|
-
"import { $, __bfSlot, createComponent, createSignal, hydrate, insert } from '@barefootjs/client/runtime'
|
|
2847
|
+
"import { $, __bfSlot, createComponent, createSignal, escapeText, hydrate, insert } from '@barefootjs/client/runtime'
|
|
2756
2848
|
|
|
2757
2849
|
|
|
2758
2850
|
export function initToggle(__scope, _p = {}) {
|
|
@@ -2776,7 +2868,7 @@ export function initToggle(__scope, _p = {}) {
|
|
|
2776
2868
|
if (_s1) _s1.addEventListener('click', () => { setOn(prev => !prev) })
|
|
2777
2869
|
}
|
|
2778
2870
|
|
|
2779
|
-
hydrate('Toggle', { init: initToggle, template: (_p) => \`<button bf="s1">\${(false) ? \`<!--bf-cond-start:s0-->\${'ON'}<!--bf-cond-end:s0-->\` : \`<!--bf-cond-start:s0-->\${'OFF'}<!--bf-cond-end:s0-->\`}</button>\` })
|
|
2871
|
+
hydrate('Toggle', { init: initToggle, template: (_p) => \`<button bf="s1">\${(false) ? \`<!--bf-cond-start:s0-->\${escapeText('ON')}<!--bf-cond-end:s0-->\` : \`<!--bf-cond-start:s0-->\${escapeText('OFF')}<!--bf-cond-end:s0-->\`}</button>\` })
|
|
2780
2872
|
export function Toggle(_p, __bfKey) { return createComponent('Toggle', _p, __bfKey) }"
|
|
2781
2873
|
`;
|
|
2782
2874
|
|
|
@@ -3255,12 +3347,17 @@ export function initExample(__scope, _p = {}) {
|
|
|
3255
3347
|
|
|
3256
3348
|
|
|
3257
3349
|
// Reactive child component props
|
|
3350
|
+
{ const __l = []
|
|
3258
3351
|
createEffect(() => {
|
|
3259
3352
|
const [__Dialog_s9El] = $c(__scope, 's9')
|
|
3260
3353
|
if (__Dialog_s9El) {
|
|
3261
|
-
|
|
3354
|
+
{ const __x = open()
|
|
3355
|
+
if (!(0 in __l) || !Object.is(__l[0], __x)) {
|
|
3356
|
+
__Dialog_s9El.open = !!(__x)
|
|
3357
|
+
}
|
|
3358
|
+
__l[0] = __x }
|
|
3262
3359
|
}
|
|
3263
|
-
})
|
|
3360
|
+
}) }
|
|
3264
3361
|
|
|
3265
3362
|
// Initialize child components with props
|
|
3266
3363
|
initChild('Dialog', _s9, { get open() { return open() }, onOpenChange: setOpen })
|
|
@@ -3823,7 +3920,7 @@ hydrate('StatementExample', { init: initStatementExample, template: (_p) => \`<d
|
|
|
3823
3920
|
export function StatementExample(_p, __bfKey) { return createComponent('StatementExample', _p, __bfKey) }"
|
|
3824
3921
|
`;
|
|
3825
3922
|
|
|
3826
|
-
exports[`docs/core/components/portals.md doc-examples
|
|
3923
|
+
exports[`docs/core/components/portals.md doc-examples L141 — (no label) 1`] = `
|
|
3827
3924
|
"import { $, createComponent, createEffect, createPortal, hydrate, isSSRPortal, useContext } from '@barefootjs/client/runtime'
|
|
3828
3925
|
|
|
3829
3926
|
|
|
@@ -3862,7 +3959,7 @@ hydrate('DialogOverlay__b3c36eee', { init: initDialogOverlay, template: (_p) =>
|
|
|
3862
3959
|
export function DialogOverlay(_p, __bfKey) { return createComponent('DialogOverlay__b3c36eee', _p, __bfKey) }"
|
|
3863
3960
|
`;
|
|
3864
3961
|
|
|
3865
|
-
exports[`docs/core/components/portals.md doc-examples
|
|
3962
|
+
exports[`docs/core/components/portals.md doc-examples L197 — (no label) 1`] = `
|
|
3866
3963
|
"import { createComponent, createEffect, createMemo, createPortal, createSignal, hydrate, onCleanup, onMount, untrack } from '@barefootjs/client/runtime'
|
|
3867
3964
|
|
|
3868
3965
|
|
|
@@ -3938,11 +4035,16 @@ export function initButton(__scope, _p = {}) {
|
|
|
3938
4035
|
|
|
3939
4036
|
const [_s0] = $(__scope, 's0')
|
|
3940
4037
|
|
|
4038
|
+
{ const __l = []
|
|
3941
4039
|
createEffect(() => {
|
|
3942
4040
|
if (_s0) {
|
|
3943
|
-
{ const
|
|
4041
|
+
{ const __x = classes
|
|
4042
|
+
if (!(0 in __l) || !Object.is(__l[0], __x)) {
|
|
4043
|
+
{ const __v = __x; if (__v != null) _s0.setAttribute('class', String(__v)); else _s0.removeAttribute('class') }
|
|
4044
|
+
}
|
|
4045
|
+
__l[0] = __x }
|
|
3944
4046
|
}
|
|
3945
|
-
})
|
|
4047
|
+
}) }
|
|
3946
4048
|
|
|
3947
4049
|
if (_s0) applyRestAttrs(_s0, _p, ["class"])
|
|
3948
4050
|
|
|
@@ -4111,24 +4213,38 @@ export function initProductPage(__scope, _p = {}) {
|
|
|
4111
4213
|
__bfw_s2('s2', escapeTextOrNode(__val))
|
|
4112
4214
|
})
|
|
4113
4215
|
|
|
4216
|
+
{ const __l = []
|
|
4114
4217
|
createEffect(() => {
|
|
4115
4218
|
if (_s4) {
|
|
4116
|
-
{ const
|
|
4219
|
+
{ const __x = _p.product.image
|
|
4220
|
+
if (!(0 in __l) || !Object.is(__l[0], __x)) {
|
|
4221
|
+
{ const __v = __x; if (__v != null) _s4.setAttribute('src', String(__v)); else _s4.removeAttribute('src') }
|
|
4222
|
+
}
|
|
4223
|
+
__l[0] = __x }
|
|
4117
4224
|
}
|
|
4118
|
-
})
|
|
4225
|
+
}) }
|
|
4119
4226
|
|
|
4120
4227
|
|
|
4121
4228
|
// Reactive child component props
|
|
4229
|
+
{ const __l = []
|
|
4122
4230
|
createEffect(() => {
|
|
4123
4231
|
const [__ReviewStars_s5El] = $c(__scope, 's5')
|
|
4124
4232
|
if (__ReviewStars_s5El) {
|
|
4125
|
-
{ const
|
|
4233
|
+
{ const __x = product.rating
|
|
4234
|
+
if (!(0 in __l) || !Object.is(__l[0], __x)) {
|
|
4235
|
+
{ const __v = __x; if (__v != null) __ReviewStars_s5El.setAttribute('rating', String(__v)); else __ReviewStars_s5El.removeAttribute('rating') }
|
|
4236
|
+
}
|
|
4237
|
+
__l[0] = __x }
|
|
4126
4238
|
}
|
|
4127
4239
|
const [__AddToCart_s6El] = $c(__scope, 's6')
|
|
4128
4240
|
if (__AddToCart_s6El) {
|
|
4129
|
-
{ const
|
|
4241
|
+
{ const __x = product.id
|
|
4242
|
+
if (!(1 in __l) || !Object.is(__l[1], __x)) {
|
|
4243
|
+
{ const __v = __x; if (__v != null) __AddToCart_s6El.setAttribute('productId', String(__v)); else __AddToCart_s6El.removeAttribute('productId') }
|
|
4244
|
+
}
|
|
4245
|
+
__l[1] = __x }
|
|
4130
4246
|
}
|
|
4131
|
-
})
|
|
4247
|
+
}) }
|
|
4132
4248
|
|
|
4133
4249
|
// Initialize child components with props
|
|
4134
4250
|
initChild('ReviewStars', _s5, { get rating() { return product.rating } })
|
|
@@ -4337,7 +4453,7 @@ export function Example(_p, __bfKey) { return createComponent('Example', _p, __b
|
|
|
4337
4453
|
`;
|
|
4338
4454
|
|
|
4339
4455
|
exports[`docs/core/adapters/hono-adapter.md doc-examples L177 — (no label) 1`] = `
|
|
4340
|
-
"import { createComponent, createEffect, createSignal, escapeTextOrMarkup, escapeTextOrNode, hydrate, lazySlots, $, __bfSlot, insert } from '@barefootjs/client/runtime'
|
|
4456
|
+
"import { createComponent, createEffect, createSignal, escapeTextOrMarkup, escapeTextOrNode, hydrate, lazySlots, $, __bfSlot, escapeText, insert } from '@barefootjs/client/runtime'
|
|
4341
4457
|
|
|
4342
4458
|
export function initTodoItem(__scope, _p = {}) {
|
|
4343
4459
|
if (!__scope) return
|
|
@@ -4396,7 +4512,7 @@ export function initExample(__scope, _p = {}) {
|
|
|
4396
4512
|
|
|
4397
4513
|
}
|
|
4398
4514
|
|
|
4399
|
-
hydrate('Example', { init: initExample, template: (_p) => \`<div>\${on() ? \`<!--bf-cond-start:s0-->\${'ON'}<!--bf-cond-end:s0-->\` : \`<!--bf-cond-start:s0-->\${'OFF'}<!--bf-cond-end:s0-->\`}</div>\` })
|
|
4515
|
+
hydrate('Example', { init: initExample, template: (_p) => \`<div>\${on() ? \`<!--bf-cond-start:s0-->\${escapeText('ON')}<!--bf-cond-end:s0-->\` : \`<!--bf-cond-start:s0-->\${escapeText('OFF')}<!--bf-cond-end:s0-->\`}</div>\` })
|
|
4400
4516
|
export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
|
|
4401
4517
|
`;
|
|
4402
4518
|
|
|
@@ -192,8 +192,16 @@ const ALLOWLIST: Record<string, Partial<Record<Pattern, number>>> = {
|
|
|
192
192
|
},
|
|
193
193
|
// FLOOR (shape 3): `irToPlaceholderTemplate`'s loop-param accessor-rewrite
|
|
194
194
|
// spec forwarding — see the canonical docstring on
|
|
195
|
-
// `wrapExprWithLoopParams` in `ir-to-client-js/utils.ts`.
|
|
196
|
-
|
|
195
|
+
// `wrapExprWithLoopParams` in `ir-to-client-js/utils.ts`. Grew by 3 for
|
|
196
|
+
// #2868: `collectLoopChildConditionals` gained an optional `loopParams`
|
|
197
|
+
// parameter (the full ancestor loop-param chain) so a conditional arm
|
|
198
|
+
// inside an inner `.map()` can render against an outer loop's item/index
|
|
199
|
+
// too — same accessor-rewrite-payload shape, one more forwarding site.
|
|
200
|
+
// Grew by 1 more merging #2861's `parentScope` param (a DIFFERENT,
|
|
201
|
+
// independent extension of the same function for the reactivity-scope
|
|
202
|
+
// concern) alongside #2868's `loopParams` — both trailing params now
|
|
203
|
+
// coexist on `collectLoopChildConditionals`'s signature.
|
|
204
|
+
'packages/jsx/src/ir-to-client-js/collect-elements.ts': { loopParams: 13 },
|
|
197
205
|
// FLOOR (shape 2): `computeCsrInlinability`'s fixed-point constant-chain
|
|
198
206
|
// inlining loop over `ctx.localConstants` — component-scope const
|
|
199
207
|
// resolution, not loop-row scope.
|
|
@@ -202,6 +210,16 @@ const ALLOWLIST: Record<string, Partial<Record<Pattern, number>>> = {
|
|
|
202
210
|
// pass a loop-param accessor-rewrite spec (see the file's own comments) —
|
|
203
211
|
// the remaining mentions are all in that reasoning, not device usage.
|
|
204
212
|
'packages/jsx/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts': { loopParams: 4 },
|
|
213
|
+
// FLOOR (shape 3, new for #2868): a doc comment pointing at
|
|
214
|
+
// `collect-elements.ts`'s `irToHtmlTemplate` `loopParams` — explaining why
|
|
215
|
+
// `cond.whenTrueHtml`/`whenFalseHtml` must not be re-wrapped post-hoc.
|
|
216
|
+
'packages/jsx/src/ir-to-client-js/control-flow/plan/build-lazy-row.ts': { loopParams: 1 },
|
|
217
|
+
// FLOOR (shape 3, new for #2868): same doc-comment reference as
|
|
218
|
+
// `build-lazy-row.ts` above.
|
|
219
|
+
'packages/jsx/src/ir-to-client-js/control-flow/plan/build-loop-child-arm.ts': { loopParams: 1 },
|
|
220
|
+
// FLOOR (shape 3, new for #2868): same doc-comment reference as
|
|
221
|
+
// `build-lazy-row.ts` above.
|
|
222
|
+
'packages/jsx/src/ir-to-client-js/control-flow/plan/build-reactive-effects.ts': { loopParams: 1 },
|
|
205
223
|
// FLOOR (shape 3): `irToHtmlTemplate`/`irToPlaceholderTemplate`'s
|
|
206
224
|
// loop-param accessor-rewrite spec — see `ir-to-client-js/utils.ts`'s
|
|
207
225
|
// `wrapExprWithLoopParams` docstring (the canonical explanation, pointed
|
|
@@ -221,6 +239,11 @@ const ALLOWLIST: Record<string, Partial<Record<Pattern, number>>> = {
|
|
|
221
239
|
// `loopParams`-named parameter in `ir-to-client-js/` forwards. See its
|
|
222
240
|
// docstring (added Stage 4) for the full shape-3 reasoning.
|
|
223
241
|
'packages/jsx/src/ir-to-client-js/utils.ts': { loopParams: 4 },
|
|
242
|
+
// FLOOR (shape 3, new for #2868): `TopLevelLoop.templateIndexed` /
|
|
243
|
+
// `BranchLoop.templateIndexed`'s doc comments point at `irToHtmlTemplate`'s
|
|
244
|
+
// `loopParams` to explain why the index-wrapped render is a second
|
|
245
|
+
// structured render, not a post-hoc regex pass over `template`.
|
|
246
|
+
'packages/jsx/src/ir-to-client-js/types.ts': { loopParams: 1 },
|
|
224
247
|
// `jsx-to-ir.ts` — MIGRATED to 0 in Stage 4: `makeBindingEnv`'s
|
|
225
248
|
// `loopParams: boundNames` field now reads `loopValueBoundNames:
|
|
226
249
|
// boundNames`, matching `free-refs.ts`'s renamed field. No entry needed
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression pin for the `buildPlainRowCore` extraction
|
|
3
|
+
* (`control-flow/plan/build-plain-row.ts`).
|
|
4
|
+
*
|
|
5
|
+
* `buildPlainLoopPlan` (build-loop.ts, `callSite: 'plain'`) and
|
|
6
|
+
* `buildBranchLoopPlan` (build-branch-loop.ts, `callSite: 'branch-plain'`)
|
|
7
|
+
* used to duplicate the "wrap item → decide lazy → wrap index" sequence
|
|
8
|
+
* verbatim. This pins that both call sites now go through the ONE shared
|
|
9
|
+
* implementation and cannot silently diverge again (CLAUDE.md's "one
|
|
10
|
+
* decision, two implementations, no test comparing them"):
|
|
11
|
+
*
|
|
12
|
+
* - A row forced eager for a reason UNRELATED to the index (here, an
|
|
13
|
+
* imperative ref — #2859 follow-on lifted the index-only refusal, so a
|
|
14
|
+
* bare index reference no longer forces eager on its own) still gets
|
|
15
|
+
* its index wrapped into a live accessor call at BOTH call sites.
|
|
16
|
+
* - A lazy-eligible row (index-referencing or not) goes through
|
|
17
|
+
* `mapArrayLazy` at BOTH call sites, with no leaked eager-only index
|
|
18
|
+
* wrap in the shared `mapPreambleWrapped`/`template` strings — the
|
|
19
|
+
* exact shape of the original #2859 CI regression.
|
|
20
|
+
*/
|
|
21
|
+
import { describe, test, expect } from 'bun:test'
|
|
22
|
+
import { compileJSX } from '../compiler'
|
|
23
|
+
import { TestAdapter } from '../adapters/test-adapter'
|
|
24
|
+
|
|
25
|
+
const adapter = new TestAdapter()
|
|
26
|
+
|
|
27
|
+
function clientJsFor(source: string): string {
|
|
28
|
+
const result = compileJSX(source, 'Repro.tsx', { adapter })
|
|
29
|
+
const errors = result.errors.filter(e => e.severity === 'error')
|
|
30
|
+
if (errors.length > 0) throw new Error(errors.map(e => e.message).join('\n'))
|
|
31
|
+
return result.files.find(f => f.type === 'clientJs')!.content
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('buildPlainRowCore — shared wrap-item/decide-lazy/wrap-index sequence', () => {
|
|
35
|
+
test('top-level plain loop: a ref forces eager, and the index is wrapped into a live accessor call', () => {
|
|
36
|
+
const js = clientJsFor(`
|
|
37
|
+
'use client'
|
|
38
|
+
import { createSignal } from '@barefootjs/client'
|
|
39
|
+
type Row = { id: number; label: string }
|
|
40
|
+
export function Repro() {
|
|
41
|
+
const [rows] = createSignal<Row[]>([])
|
|
42
|
+
const refs: HTMLLIElement[] = []
|
|
43
|
+
return <ul>{rows().map((row, i) => <li key={row.id} ref={el => { refs[i] = el! }}>{String(i + 1)}. {row.label}</li>)}</ul>
|
|
44
|
+
}
|
|
45
|
+
`)
|
|
46
|
+
expect(js).not.toContain('mapArrayLazy(')
|
|
47
|
+
expect(js).toMatch(/i\(\)\s*\+\s*1/)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('branch-plain loop (ternary): the SAME ref-bearing shape wraps the index identically', () => {
|
|
51
|
+
const js = clientJsFor(`
|
|
52
|
+
'use client'
|
|
53
|
+
import { createSignal } from '@barefootjs/client'
|
|
54
|
+
type Row = { id: number; label: string }
|
|
55
|
+
export function Repro() {
|
|
56
|
+
const [active, setActive] = createSignal(true)
|
|
57
|
+
const [rows] = createSignal<Row[]>([])
|
|
58
|
+
const refs: HTMLLIElement[] = []
|
|
59
|
+
return <div>{active() ? rows().map((row, i) => <li key={row.id} ref={el => { refs[i] = el! }}>{String(i + 1)}. {row.label}</li>) : <span>Empty</span>}</div>
|
|
60
|
+
}
|
|
61
|
+
`)
|
|
62
|
+
expect(js).not.toContain('mapArrayLazy(')
|
|
63
|
+
expect(js).toMatch(/i\(\)\s*\+\s*1/)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('top-level plain loop: a lazy-eligible row (no index reference) goes lazy with no leaked index wrap', () => {
|
|
67
|
+
const js = clientJsFor(`
|
|
68
|
+
'use client'
|
|
69
|
+
import { createSignal } from '@barefootjs/client'
|
|
70
|
+
type Row = { id: number; label: string }
|
|
71
|
+
export function Repro() {
|
|
72
|
+
const [rows] = createSignal<Row[]>([{ id: 1, label: 'alpha' }])
|
|
73
|
+
const [selected, setSelected] = createSignal(1)
|
|
74
|
+
return (
|
|
75
|
+
<ul>
|
|
76
|
+
{rows().map(row => (
|
|
77
|
+
<li key={row.id} onClick={() => setSelected(row.id)}>{String(selected() === row.id ? 'YES' : 'NO')}</li>
|
|
78
|
+
))}
|
|
79
|
+
</ul>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
`)
|
|
83
|
+
expect(js).toContain('mapArrayLazy(')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('branch-plain loop (ternary): the SAME lazy-eligible shape also goes lazy', () => {
|
|
87
|
+
const js = clientJsFor(`
|
|
88
|
+
'use client'
|
|
89
|
+
import { createSignal } from '@barefootjs/client'
|
|
90
|
+
type Row = { id: number; label: string }
|
|
91
|
+
export function Repro() {
|
|
92
|
+
const [active] = createSignal(true)
|
|
93
|
+
const [rows] = createSignal<Row[]>([{ id: 1, label: 'alpha' }])
|
|
94
|
+
const [selected, setSelected] = createSignal(1)
|
|
95
|
+
return (
|
|
96
|
+
<div>
|
|
97
|
+
{active() ? rows().map(row => (
|
|
98
|
+
<li key={row.id} onClick={() => setSelected(row.id)}>{String(selected() === row.id ? 'YES' : 'NO')}</li>
|
|
99
|
+
)) : <span>Empty</span>}
|
|
100
|
+
</div>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
`)
|
|
104
|
+
expect(js).toContain('mapArrayLazy(')
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('a ref callback closing over the loop index reads the live accessor at the top-level call site (#2859)', () => {
|
|
108
|
+
const js = clientJsFor(`
|
|
109
|
+
'use client'
|
|
110
|
+
import { createSignal } from '@barefootjs/client'
|
|
111
|
+
type Row = { id: number; label: string }
|
|
112
|
+
export function Repro() {
|
|
113
|
+
const [rows] = createSignal<Row[]>([])
|
|
114
|
+
const refs: HTMLLIElement[] = []
|
|
115
|
+
return <ul>{rows().map((row, i) => <li key={row.id} ref={el => { refs[i] = el! }}>{row.label}</li>)}</ul>
|
|
116
|
+
}
|
|
117
|
+
`)
|
|
118
|
+
expect(js).toMatch(/refs\[i\(\)\]/)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
@@ -260,7 +260,9 @@ describe('Solid-style wrap-by-default fallback for child-component props (#942)'
|
|
|
260
260
|
// The emitted code rewrites `props.xxx` to the internal props-param
|
|
261
261
|
// name (`_p.xxx`) via rewriteDestructuredPropsInExpr. Assert on the
|
|
262
262
|
// emitted form — the gate's `expandedValue.includes('props.')` check
|
|
263
|
-
// still runs against the source expression before rewriting.
|
|
264
|
-
|
|
263
|
+
// still runs against the source expression before rewriting. The
|
|
264
|
+
// dedup-guarded write (#2869) computes into `__x` first, then assigns
|
|
265
|
+
// `__v = __x` inside `emitAttrUpdate`'s own generic branch.
|
|
266
|
+
expect(clientJs).toMatch(/__x\s*=\s*_p\.title/)
|
|
265
267
|
})
|
|
266
268
|
})
|