@barefootjs/jsx 0.26.4 → 0.28.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.
- package/dist/compiler.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +941 -130
- package/dist/ir-to-client-js/client-only-elision.d.ts +103 -0
- package/dist/ir-to-client-js/client-only-elision.d.ts.map +1 -0
- package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts +6 -0
- package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts +2 -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-insert.d.ts +7 -0
- package/dist/ir-to-client-js/control-flow/plan/build-insert.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts +107 -0
- package/dist/ir-to-client-js/control-flow/plan/build-lazy-row.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts +9 -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/lazy-row-eligibility.d.ts +203 -0
- package/dist/ir-to-client-js/control-flow/plan/lazy-row-eligibility.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +10 -0
- 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 +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/branch-loop.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/claim-plan.d.ts +54 -0
- package/dist/ir-to-client-js/control-flow/stringify/claim-plan.d.ts.map +1 -0
- 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/insert.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts +98 -0
- package/dist/ir-to-client-js/control-flow/stringify/lazy-row.d.ts.map +1 -0
- package/dist/ir-to-client-js/control-flow/stringify/loop-child-arm.d.ts +5 -0
- 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 +17 -20
- 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 +63 -4
- package/dist/ir-to-client-js/control-flow/stringify/reactive-effects.d.ts.map +1 -1
- package/dist/ir-to-client-js/control-flow.d.ts +1 -1
- package/dist/ir-to-client-js/control-flow.d.ts.map +1 -1
- package/dist/ir-to-client-js/element-refs.d.ts +8 -3
- package/dist/ir-to-client-js/element-refs.d.ts.map +1 -1
- package/dist/ir-to-client-js/emit-reactive.d.ts +8 -1
- package/dist/ir-to-client-js/emit-reactive.d.ts.map +1 -1
- package/dist/ir-to-client-js/html-template.d.ts +24 -0
- 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/reactivity.d.ts.map +1 -1
- package/dist/ir-to-client-js/types.d.ts +9 -1
- package/dist/ir-to-client-js/types.d.ts.map +1 -1
- package/dist/ir-to-client-js/utils.d.ts +1 -1
- package/dist/types.d.ts +25 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +542 -689
- package/src/__tests__/child-components-in-map.test.ts +4 -4
- package/src/__tests__/client-js-generation.test.ts +23 -12
- package/src/__tests__/composite-branch-loop.test.ts +14 -5
- package/src/__tests__/conditional-mapArray-key.test.ts +7 -1
- package/src/__tests__/create-selector.test.ts +21 -7
- package/src/__tests__/cross-file-client-signal.test.ts +5 -1
- package/src/__tests__/early-return-scope-var-ref.test.ts +10 -11
- package/src/__tests__/inline-jsx-callback.test.ts +6 -4
- package/src/__tests__/ir-jsx-props.test.ts +6 -5
- package/src/__tests__/lazy-row-eligibility.test.ts +692 -0
- package/src/__tests__/loop-branch-bare-expression-reactive-text.test.ts +98 -0
- package/src/__tests__/loop-fallback-wrap.test.ts +34 -16
- package/src/__tests__/loop-hoisted-template.test.ts +10 -6
- package/src/__tests__/module-level-signal.audit.test.ts +7 -2
- package/src/__tests__/nested-loop-conditional.test.ts +9 -7
- package/src/__tests__/nested-loop-reactive-attrs.test.ts +2 -2
- package/src/__tests__/preamble-region-patch.test.ts +18 -12
- package/src/__tests__/profile-cond-binding-ids.test.ts +3 -2
- package/src/__tests__/profile-loop-binding-ids.test.ts +4 -3
- package/src/__tests__/profile-nested-binding-ids.test.ts +11 -6
- package/src/__tests__/reactive-attrs-in-map.test.ts +6 -4
- package/src/__tests__/static-loop-csr-materialize.test.ts +6 -1
- package/src/__tests__/template-closure.test.ts +1 -1
- package/src/compiler.ts +11 -0
- package/src/index.ts +1 -0
- package/src/ir-to-client-js/client-only-elision.ts +273 -0
- package/src/ir-to-client-js/collect-elements.ts +56 -17
- package/src/ir-to-client-js/control-flow/plan/branch-loop.ts +6 -0
- package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +35 -12
- package/src/ir-to-client-js/control-flow/plan/build-insert.ts +9 -2
- package/src/ir-to-client-js/control-flow/plan/build-lazy-row.ts +305 -0
- package/src/ir-to-client-js/control-flow/plan/build-loop.ts +44 -12
- package/src/ir-to-client-js/control-flow/plan/lazy-row-eligibility.ts +440 -0
- package/src/ir-to-client-js/control-flow/plan/loop.ts +10 -0
- package/src/ir-to-client-js/control-flow/shared.ts +1 -1
- package/src/ir-to-client-js/control-flow/stringify/branch-loop.ts +34 -4
- package/src/ir-to-client-js/control-flow/stringify/claim-plan.ts +68 -0
- package/src/ir-to-client-js/control-flow/stringify/inner-loop.ts +18 -9
- package/src/ir-to-client-js/control-flow/stringify/insert.ts +17 -11
- package/src/ir-to-client-js/control-flow/stringify/lazy-row.ts +478 -0
- package/src/ir-to-client-js/control-flow/stringify/loop-child-arm.ts +41 -17
- package/src/ir-to-client-js/control-flow/stringify/loop.ts +71 -46
- package/src/ir-to-client-js/control-flow/stringify/reactive-effects.ts +241 -31
- package/src/ir-to-client-js/control-flow.ts +8 -3
- package/src/ir-to-client-js/element-refs.ts +9 -11
- package/src/ir-to-client-js/emit-reactive.ts +50 -14
- package/src/ir-to-client-js/html-template.ts +29 -9
- package/src/ir-to-client-js/imports.ts +13 -4
- package/src/ir-to-client-js/reactivity.ts +3 -2
- package/src/ir-to-client-js/types.ts +10 -2
- package/src/ir-to-client-js/utils.ts +1 -1
- package/src/jsx-to-ir.ts +26 -8
- package/src/types.ts +25 -2
|
@@ -211,7 +211,7 @@ describe('child components inside .map() (#344)', () => {
|
|
|
211
211
|
expect(clientJs!.content).toContain('toggleRow(index)')
|
|
212
212
|
})
|
|
213
213
|
|
|
214
|
-
test('dynamic signal array: component generates
|
|
214
|
+
test('dynamic signal array: component generates mapArray with createComponent', () => {
|
|
215
215
|
const source = `
|
|
216
216
|
'use client'
|
|
217
217
|
import { createSignal } from '@barefootjs/client'
|
|
@@ -321,7 +321,7 @@ describe('child components inside .map() (#344)', () => {
|
|
|
321
321
|
const uniqueVars = new Set(allVars)
|
|
322
322
|
expect(allVars.length).toBe(uniqueVars.size)
|
|
323
323
|
|
|
324
|
-
// Component slot ref ($c) and
|
|
324
|
+
// Component slot ref ($c) and mapArray should both be present
|
|
325
325
|
expect(content).toContain('$c(__scope')
|
|
326
326
|
expect(content).toContain('mapArray')
|
|
327
327
|
})
|
|
@@ -355,7 +355,7 @@ describe('child components inside .map() (#344)', () => {
|
|
|
355
355
|
expect(clientJs).toBeDefined()
|
|
356
356
|
const content = clientJs!.content
|
|
357
357
|
|
|
358
|
-
// Should use
|
|
358
|
+
// Should use mapArray with createComponent
|
|
359
359
|
expect(content).toContain('mapArray')
|
|
360
360
|
expect(content).toContain("createComponent('TableRow'")
|
|
361
361
|
|
|
@@ -650,7 +650,7 @@ describe('child components inside .map() (#344)', () => {
|
|
|
650
650
|
expect(clientJs).toBeDefined()
|
|
651
651
|
const content = clientJs!.content
|
|
652
652
|
|
|
653
|
-
// Dynamic array should use
|
|
653
|
+
// Dynamic array should use mapArray and event delegation
|
|
654
654
|
expect(content).toContain('mapArray')
|
|
655
655
|
expect(content).toContain(".addEventListener('click', (__bfEvt) => {")
|
|
656
656
|
expect(content).toContain('handleClick(item.id)')
|
|
@@ -151,7 +151,7 @@ describe('Client JS generation', () => {
|
|
|
151
151
|
})
|
|
152
152
|
|
|
153
153
|
describe('map with index parameter', () => {
|
|
154
|
-
test('includes index parameter in
|
|
154
|
+
test('includes index parameter in mapArray renderItem callback', () => {
|
|
155
155
|
const source = `
|
|
156
156
|
'use client'
|
|
157
157
|
import { createMemo } from '@barefootjs/client'
|
|
@@ -1760,7 +1760,10 @@ describe('Client JS generation', () => {
|
|
|
1760
1760
|
// update. See `preamble-region-patch.test.ts` for the full region
|
|
1761
1761
|
// contract (row-template marker, region-patch effect, SSR parity).
|
|
1762
1762
|
expect(clientJs?.content).toContain('${escapeText(label)}')
|
|
1763
|
-
|
|
1763
|
+
// Slot unification A3: preamble regions now write through a claimed
|
|
1764
|
+
// 'markup' slot instead of `patchSlotRange`.
|
|
1765
|
+
expect(clientJs?.content).toContain("kind: 'markup'")
|
|
1766
|
+
expect(clientJs?.content).toContain('lazySlots(__el')
|
|
1764
1767
|
})
|
|
1765
1768
|
|
|
1766
1769
|
test('handles block body with multiple variable declarations', () => {
|
|
@@ -2423,11 +2426,12 @@ describe('Client JS generation', () => {
|
|
|
2423
2426
|
expect(clientJs).toBeDefined()
|
|
2424
2427
|
const content = clientJs!.content
|
|
2425
2428
|
|
|
2426
|
-
// Text node updates route through
|
|
2427
|
-
//
|
|
2428
|
-
//
|
|
2429
|
-
//
|
|
2430
|
-
|
|
2429
|
+
// Text node updates route through a claimed 'markup' slot writer (slot
|
|
2430
|
+
// unification A3), whose `writeMarkup` renders nullish values as ''
|
|
2431
|
+
// (not the string "undefined") and also splices live Nodes by
|
|
2432
|
+
// identity (#1663) — the same contract `__bfText` used to provide.
|
|
2433
|
+
// The nullish guard lives in the runtime writer, not inline here.
|
|
2434
|
+
expect(content).toContain("kind: 'markup'")
|
|
2431
2435
|
expect(content).not.toMatch(/\.nodeValue = String\(__val\)(?! )/)
|
|
2432
2436
|
})
|
|
2433
2437
|
|
|
@@ -2464,8 +2468,10 @@ describe('Client JS generation', () => {
|
|
|
2464
2468
|
expect(clientJs).toBeDefined()
|
|
2465
2469
|
const content = clientJs!.content
|
|
2466
2470
|
|
|
2467
|
-
// text() inside a component inside a conditional
|
|
2468
|
-
|
|
2471
|
+
// text() inside a component inside a conditional writes through a
|
|
2472
|
+
// claimed 'markup' slot built fresh in the branch's own bindEvents
|
|
2473
|
+
// (slot unification A3) — re-claimed on every branch activation.
|
|
2474
|
+
expect(content).toContain("lazySlots(__branchScope, [{ id: '^s2', kind: 'markup', path: [] }])")
|
|
2469
2475
|
})
|
|
2470
2476
|
|
|
2471
2477
|
test('propagates insideConditional through fragment children', () => {
|
|
@@ -2495,8 +2501,10 @@ describe('Client JS generation', () => {
|
|
|
2495
2501
|
expect(clientJs).toBeDefined()
|
|
2496
2502
|
const content = clientJs!.content
|
|
2497
2503
|
|
|
2498
|
-
// text() inside a fragment inside a conditional
|
|
2499
|
-
|
|
2504
|
+
// text() inside a fragment inside a conditional writes through a
|
|
2505
|
+
// claimed 'markup' slot built fresh in the branch's own bindEvents
|
|
2506
|
+
// (slot unification A3) — re-claimed on every branch activation.
|
|
2507
|
+
expect(content).toContain("lazySlots(__branchScope, [{ id: 's2', kind: 'markup', path: [] }])")
|
|
2500
2508
|
})
|
|
2501
2509
|
})
|
|
2502
2510
|
|
|
@@ -2630,7 +2638,10 @@ describe('Client JS generation', () => {
|
|
|
2630
2638
|
const js = result.files.find(f => f.type === 'clientJs')!.content
|
|
2631
2639
|
// The branch arm's mapArray call must live inside a
|
|
2632
2640
|
// createDisposableEffect that is pushed into __disposers.
|
|
2633
|
-
|
|
2641
|
+
// `mapArray(` or `mapArrayLazy(` — a lazy-row-eligible branch loop
|
|
2642
|
+
// (spec/slot-unification.md §9.4) uses the latter and has exactly the
|
|
2643
|
+
// same disposal obligation.
|
|
2644
|
+
expect(js).toMatch(/__disposers\.push\(createDisposableEffect\(\(\)\s*=>\s*\{[\s\S]*?mapArray(Lazy)?\(/)
|
|
2634
2645
|
})
|
|
2635
2646
|
|
|
2636
2647
|
test('nested conditional inside a conditional branch is wrapped in createDisposableEffect', () => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* BarefootJS Compiler - Composite loops inside conditional branches (#724)
|
|
3
3
|
*
|
|
4
4
|
* Verifies that loops with child components inside ternary conditionals
|
|
5
|
-
* generate
|
|
5
|
+
* generate mapArray with createComponent in the branch's bindEvents.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { describe, test, expect } from 'bun:test'
|
|
@@ -52,8 +52,11 @@ describe('composite loops inside conditional branches (#724)', () => {
|
|
|
52
52
|
expect(clientJs).toBeDefined()
|
|
53
53
|
const js = clientJs!.content
|
|
54
54
|
|
|
55
|
-
// Should use
|
|
56
|
-
|
|
55
|
+
// Should use mapArray inside the branch's bindEvents
|
|
56
|
+
// `mapArray(` or `mapArrayLazy(` — a lazy-row-eligible branch loop
|
|
57
|
+
// (spec/slot-unification.md §9.4) uses the latter; either satisfies
|
|
58
|
+
// "keyed reconciliation, not a composite/component shape".
|
|
59
|
+
expect(js).toMatch(/\bmapArray(Lazy)?\(/)
|
|
57
60
|
|
|
58
61
|
// After O-1's mode-collapse PR, child component init goes through the
|
|
59
62
|
// runtime `upsertChild` helper instead of an emit-side
|
|
@@ -96,7 +99,10 @@ describe('composite loops inside conditional branches (#724)', () => {
|
|
|
96
99
|
const js = clientJs!.content
|
|
97
100
|
|
|
98
101
|
// Should use mapArray for per-item reactivity
|
|
99
|
-
|
|
102
|
+
// `mapArray(` or `mapArrayLazy(` — a lazy-row-eligible branch loop
|
|
103
|
+
// (spec/slot-unification.md §9.4) uses the latter; either satisfies
|
|
104
|
+
// "keyed reconciliation, not a composite/component shape".
|
|
105
|
+
expect(js).toMatch(/\bmapArray(Lazy)?\(/)
|
|
100
106
|
|
|
101
107
|
// Should NOT use createComponent inside the init body (no child
|
|
102
108
|
// components in this fixture). The CLI also emits a callable shim
|
|
@@ -147,7 +153,10 @@ describe('composite loops inside conditional branches (#724)', () => {
|
|
|
147
153
|
const js = clientJs!.content
|
|
148
154
|
|
|
149
155
|
// Should use mapArray for the loop
|
|
150
|
-
|
|
156
|
+
// `mapArray(` or `mapArrayLazy(` — a lazy-row-eligible branch loop
|
|
157
|
+
// (spec/slot-unification.md §9.4) uses the latter; either satisfies
|
|
158
|
+
// "keyed reconciliation, not a composite/component shape".
|
|
159
|
+
expect(js).toMatch(/\bmapArray(Lazy)?\(/)
|
|
151
160
|
|
|
152
161
|
// Should have event delegation (addEventListener + closest pattern)
|
|
153
162
|
expect(js).toContain(".addEventListener('click', (__bfEvt) => {")
|
|
@@ -29,11 +29,17 @@ function clientJs(source: string, file = 'CondKey.tsx'): string {
|
|
|
29
29
|
return cjs!.content
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Reconciler call sites, whichever family they use. A lazy-row-eligible loop
|
|
34
|
+
* (spec/slot-unification.md §9.4) emits `mapArrayLazy(`; everything else keeps
|
|
35
|
+
* `mapArray(`. The key expression this suite asserts on is the same third
|
|
36
|
+
* argument in both shapes, so match the family, not one member of it.
|
|
37
|
+
*/
|
|
32
38
|
function mapArrayCalls(content: string): string[] {
|
|
33
39
|
return content
|
|
34
40
|
.split('\n')
|
|
35
41
|
.map((ln) => ln.trim())
|
|
36
|
-
.filter((ln) => ln.startsWith('mapArray('))
|
|
42
|
+
.filter((ln) => ln.startsWith('mapArray(') || ln.startsWith('mapArrayLazy('))
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
describe('mapArray key extraction across conditional branches (#1098)', () => {
|
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
* `isSelected(row.id)` as reactive with no analyzer changes — the callee's
|
|
8
8
|
* type carries the brand, not the call's return type. These tests pin:
|
|
9
9
|
* - the classic js-framework-benchmark row compiles with `isSelected(row.id)`
|
|
10
|
-
* wired
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* wired through the lazy row graph — ZERO emitted per-row effects, the
|
|
11
|
+
* loop-level effect living in `mapArrayLazy` (spec §9). Until §9.3a
|
|
12
|
+
* was lifted this shape was gate-INELIGIBLE (an opaque local whose call
|
|
13
|
+
* is the reactive read could not be primed) and compiled to one per-row
|
|
14
|
+
* `createEffect`; the runtime re-subscribe seam replaced that, so the
|
|
15
|
+
* loop is emitted like any other. Either way it is never
|
|
16
|
+
* the legacy `selected() === row.id` shape, which subscribes every row
|
|
17
|
+
* to the raw signal.
|
|
13
18
|
* - the loop-param accessor wrap applies only to the call ARGUMENT
|
|
14
19
|
* (`row.id` -> `row().id`), leaving the `isSelected` callee untouched
|
|
15
20
|
* - the hoisted skeleton fast path (#2223) still fires for a selector-driven
|
|
@@ -36,7 +41,7 @@ function compile(source: string, filename: string): { clientJs: string; template
|
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
describe('createSelector (perf, #2143 gap 5)', () => {
|
|
39
|
-
test('isSelected(row.id) in a loop body compiles
|
|
44
|
+
test('isSelected(row.id) in a loop body compiles through the lazy row graph', () => {
|
|
40
45
|
const source = `
|
|
41
46
|
'use client'
|
|
42
47
|
import { createSignal, createSelector } from '@barefootjs/client'
|
|
@@ -66,12 +71,21 @@ describe('createSelector (perf, #2143 gap 5)', () => {
|
|
|
66
71
|
// The callee itself is never rewritten to an accessor call.
|
|
67
72
|
expect(clientJs).not.toContain('isSelected()(')
|
|
68
73
|
|
|
69
|
-
//
|
|
70
|
-
//
|
|
74
|
+
// The selector-driven class attribute costs NO emitted effect at all:
|
|
75
|
+
// the loop is lazy-eligible, so its one loop-level effect lives inside
|
|
76
|
+
// `mapArrayLazy` rather than in the emitted row body. It is also never
|
|
77
|
+
// the O(rows) `selected() === row.id` shape.
|
|
71
78
|
const effectCount = (clientJs.match(/createEffect\(/g) ?? []).length
|
|
72
|
-
expect(effectCount).toBe(
|
|
79
|
+
expect(effectCount).toBe(0)
|
|
80
|
+
expect(clientJs).toContain('mapArrayLazy(')
|
|
73
81
|
expect(clientJs).not.toContain('selected() ===')
|
|
74
82
|
|
|
83
|
+
// `isSelected` is an opaque local (its CALL is the reactive read). Nothing
|
|
84
|
+
// in the emission marks that: the runtime's re-subscribe seam applies to
|
|
85
|
+
// every loop-level outer effect, so the compiler has no judgement to
|
|
86
|
+
// encode here (§9.3a).
|
|
87
|
+
expect(clientJs).not.toContain('outerNeedsResubscribe')
|
|
88
|
+
|
|
75
89
|
// The hoisted skeleton fast path (#2223) still fires: a single-line
|
|
76
90
|
// renderItem clones from a once-per-loop template.
|
|
77
91
|
expect(clientJs).toMatch(/const __tpl_\w+ = document\.createElement\('template'\)/)
|
|
@@ -104,7 +104,11 @@ export function Viewer() {
|
|
|
104
104
|
const files = Object.fromEntries(r.files.map(f => [f.path, f.content]))
|
|
105
105
|
const ssr = Object.values(files).find(c => c.includes('export function Viewer'))
|
|
106
106
|
expect(ssr).toBeDefined()
|
|
107
|
-
|
|
107
|
+
// Slot unification Step B: `{val()}` is the SPAN's only, non-adjacent
|
|
108
|
+
// child, so its marker pair is elided entirely (see the identical note
|
|
109
|
+
// in module-level-signal.audit.test.ts) — the client's claim plan
|
|
110
|
+
// creates the missing Text node from `elidedPath` alone.
|
|
111
|
+
expect(ssr).not.toContain('bfText(')
|
|
108
112
|
expect(ssr).not.toContain("const val = () =>")
|
|
109
113
|
})
|
|
110
114
|
|
|
@@ -65,12 +65,10 @@ describe('JSX expression referencing a local declared inside an early-return if-
|
|
|
65
65
|
|
|
66
66
|
const content = clientJsContent(result)
|
|
67
67
|
|
|
68
|
-
// Pre-fix
|
|
69
|
-
//
|
|
70
|
-
// inlined directly into the template, so
|
|
71
|
-
//
|
|
72
|
-
// shape.
|
|
73
|
-
expect(content).not.toContain('updateClientMarker')
|
|
68
|
+
// Pre-fix, `aLocal` leaked into a reactive-update block at outer init
|
|
69
|
+
// scope where it doesn't exist (see file docstring). Post-fix: the JSX
|
|
70
|
+
// is inlined directly into the template, so no reactive-update block is
|
|
71
|
+
// needed at all for this static shape.
|
|
74
72
|
expect(content).not.toMatch(/\baLocal\b/)
|
|
75
73
|
|
|
76
74
|
const hydrate = hydrateLine(result)
|
|
@@ -97,9 +95,10 @@ describe('JSX expression referencing a local declared inside an early-return if-
|
|
|
97
95
|
const content = clientJsContent(result)
|
|
98
96
|
// The bare `label` identifier must not appear in the emitted init
|
|
99
97
|
// function — it's resolved to its initializer value at IR-build
|
|
100
|
-
// time.
|
|
101
|
-
|
|
102
|
-
expect(content).
|
|
98
|
+
// time. `@client` writes through a claimed 'text' slot writer
|
|
99
|
+
// (`claim-slots.ts`).
|
|
100
|
+
expect(content).not.toMatch(/__bfw_\w+\([^)]*\blabel\b[^)]*\)/)
|
|
101
|
+
expect(content).toContain("__bfw_s4('s4', 'hello')")
|
|
103
102
|
})
|
|
104
103
|
|
|
105
104
|
test('ternary scope var (`readOnly ? null : <jsx/>`) routes to client-only conditional', () => {
|
|
@@ -124,8 +123,8 @@ describe('JSX expression referencing a local declared inside an early-return if-
|
|
|
124
123
|
expect(result.errors.filter(e => e.severity === 'error')).toHaveLength(0)
|
|
125
124
|
|
|
126
125
|
const content = clientJsContent(result)
|
|
127
|
-
// Pre-fix
|
|
128
|
-
//
|
|
126
|
+
// Pre-fix, `compactResize` leaked into the emitted reactive-update
|
|
127
|
+
// block as an undeclared name. Post-fix: the ternary becomes an
|
|
129
128
|
// IRConditional with `clientOnly: true`, routed through `insert()`
|
|
130
129
|
// — and `props.readOnly` is rewritten to `_p.readOnly`.
|
|
131
130
|
expect(content).not.toMatch(/\bcompactResize\b/)
|
|
@@ -440,10 +440,12 @@ describe('inline JSX as object-literal arrow value (#1663)', () => {
|
|
|
440
440
|
expect(js).toMatch(/hydrate\(\s*['"]BFInlineJsxCallback1(?:__|['"])/)
|
|
441
441
|
})
|
|
442
442
|
|
|
443
|
-
test('dynamic JSX-returning call routes the slot through
|
|
443
|
+
test('dynamic JSX-returning call routes the slot through a claimed markup slot', () => {
|
|
444
444
|
// The child expression `{themeLogo(props.id)}` evaluates to a live
|
|
445
445
|
// component element on the client; the slot update must splice it in by
|
|
446
|
-
// identity via
|
|
446
|
+
// identity via a claimed 'markup' slot writer (slot unification A3)
|
|
447
|
+
// rather than stringify it into "[object …]" — the same contract
|
|
448
|
+
// `__bfText` used to provide.
|
|
447
449
|
const source = `
|
|
448
450
|
'use client'
|
|
449
451
|
import { BrandLogo } from './brand-logo'
|
|
@@ -458,7 +460,7 @@ describe('inline JSX as object-literal arrow value (#1663)', () => {
|
|
|
458
460
|
}
|
|
459
461
|
`
|
|
460
462
|
const js = clientJs(source)
|
|
461
|
-
expect(js).toContain('
|
|
462
|
-
expect(js).toMatch(/
|
|
463
|
+
expect(js).toContain("kind: 'markup'")
|
|
464
|
+
expect(js).toMatch(/lazySlots[^']*from '@barefootjs\/client\/runtime'/)
|
|
463
465
|
})
|
|
464
466
|
})
|
|
@@ -359,11 +359,12 @@ describe('JSX props (#559)', () => {
|
|
|
359
359
|
|
|
360
360
|
const clientJs = result.files.find(f => f.type === 'clientJs')
|
|
361
361
|
expect(clientJs).toBeDefined()
|
|
362
|
-
// The text update routes through
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
|
|
362
|
+
// The text update routes through a claimed 'markup' slot writer (slot
|
|
363
|
+
// unification A3), whose `writeMarkup` preserves the server-rendered
|
|
364
|
+
// DOM for `__isSlot` values (and splices live Nodes by identity)
|
|
365
|
+
// instead of the old inline `nodeValue = String(...)` assignment —
|
|
366
|
+
// the same contract `__bfText` used to provide (#1663).
|
|
367
|
+
expect(clientJs!.content).toContain("kind: 'markup'")
|
|
367
368
|
})
|
|
368
369
|
})
|
|
369
370
|
})
|