@barefootjs/jsx 0.26.2 → 0.26.4

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.
Files changed (98) hide show
  1. package/dist/adapters/interface.d.ts +29 -0
  2. package/dist/adapters/interface.d.ts.map +1 -1
  3. package/dist/adapters/jsx-adapter.d.ts +9 -0
  4. package/dist/adapters/jsx-adapter.d.ts.map +1 -1
  5. package/dist/adapters/loop-bound-names.d.ts.map +1 -1
  6. package/dist/adapters/parsed-expr-emitter.d.ts +0 -10
  7. package/dist/adapters/parsed-expr-emitter.d.ts.map +1 -1
  8. package/dist/adapters/test-adapter.d.ts.map +1 -1
  9. package/dist/analyzer-context.d.ts +11 -1
  10. package/dist/analyzer-context.d.ts.map +1 -1
  11. package/dist/analyzer.d.ts +40 -1
  12. package/dist/analyzer.d.ts.map +1 -1
  13. package/dist/expression-parser.d.ts.map +1 -1
  14. package/dist/index.js +1086 -269
  15. package/dist/ir-to-client-js/build-references.d.ts.map +1 -1
  16. package/dist/ir-to-client-js/collect-elements.d.ts.map +1 -1
  17. package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts +14 -1
  18. package/dist/ir-to-client-js/control-flow/plan/branch-loop.d.ts.map +1 -1
  19. package/dist/ir-to-client-js/control-flow/plan/build-branch-loop.d.ts.map +1 -1
  20. package/dist/ir-to-client-js/control-flow/plan/build-component-loop.d.ts.map +1 -1
  21. package/dist/ir-to-client-js/control-flow/plan/build-composite-loop.d.ts.map +1 -1
  22. package/dist/ir-to-client-js/control-flow/plan/build-event-delegation.d.ts.map +1 -1
  23. package/dist/ir-to-client-js/control-flow/plan/build-inner-loop.d.ts.map +1 -1
  24. package/dist/ir-to-client-js/control-flow/plan/build-loop.d.ts.map +1 -1
  25. package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts +16 -1
  26. package/dist/ir-to-client-js/control-flow/plan/event-delegation.d.ts.map +1 -1
  27. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts +39 -0
  28. package/dist/ir-to-client-js/control-flow/plan/loop.d.ts.map +1 -1
  29. package/dist/ir-to-client-js/control-flow/shared.d.ts +12 -1
  30. package/dist/ir-to-client-js/control-flow/shared.d.ts.map +1 -1
  31. package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts +1 -1
  32. package/dist/ir-to-client-js/control-flow/stringify/event-delegation.d.ts.map +1 -1
  33. package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts +19 -0
  34. package/dist/ir-to-client-js/control-flow/stringify/loop.d.ts.map +1 -1
  35. package/dist/ir-to-client-js/control-flow.d.ts.map +1 -1
  36. package/dist/ir-to-client-js/html-template.d.ts +67 -1
  37. package/dist/ir-to-client-js/html-template.d.ts.map +1 -1
  38. package/dist/ir-to-client-js/imports.d.ts +2 -2
  39. package/dist/ir-to-client-js/imports.d.ts.map +1 -1
  40. package/dist/ir-to-client-js/plan/build-static-array-child-init.d.ts.map +1 -1
  41. package/dist/ir-to-client-js/reactivity.d.ts.map +1 -1
  42. package/dist/ir-to-client-js/types.d.ts +29 -4
  43. package/dist/ir-to-client-js/types.d.ts.map +1 -1
  44. package/dist/jsx-to-ir.d.ts.map +1 -1
  45. package/dist/loop-destructure.d.ts.map +1 -1
  46. package/dist/strip-types.d.ts +18 -0
  47. package/dist/strip-types.d.ts.map +1 -1
  48. package/dist/types.d.ts +143 -32
  49. package/dist/types.d.ts.map +1 -1
  50. package/package.json +2 -2
  51. package/src/__tests__/__snapshots__/doc-examples.test.ts.snap +277 -33
  52. package/src/__tests__/client-js-generation.test.ts +18 -5
  53. package/src/__tests__/compiler-runtime-contract.test.ts +4 -4
  54. package/src/__tests__/compiler-stress-1244.test.ts +12 -1
  55. package/src/__tests__/delegated-handler-preamble.test.ts +153 -0
  56. package/src/__tests__/event-delegation-scope-2367.test.ts +108 -0
  57. package/src/__tests__/flatmap-segments.test.ts +262 -0
  58. package/src/__tests__/map-arbitrary-body.test.ts +226 -0
  59. package/src/__tests__/map-body-no-silent-divergence.test.ts +405 -0
  60. package/src/__tests__/map-multi-return-body.test.ts +172 -0
  61. package/src/__tests__/preamble-region-patch.test.ts +150 -0
  62. package/src/__tests__/static-loop-csr-materialize.test.ts +3 -2
  63. package/src/__tests__/unsupported-expression.test.ts +20 -2
  64. package/src/adapters/interface.ts +40 -0
  65. package/src/adapters/jsx-adapter.ts +10 -0
  66. package/src/adapters/loop-bound-names.ts +6 -2
  67. package/src/adapters/parsed-expr-emitter.ts +5 -10
  68. package/src/adapters/test-adapter.ts +10 -0
  69. package/src/analyzer-context.ts +35 -1
  70. package/src/analyzer.ts +162 -24
  71. package/src/compiler.ts +2 -2
  72. package/src/expression-parser.ts +27 -19
  73. package/src/ir-to-client-js/build-references.ts +19 -2
  74. package/src/ir-to-client-js/collect-elements.ts +69 -13
  75. package/src/ir-to-client-js/control-flow/plan/branch-loop.ts +14 -1
  76. package/src/ir-to-client-js/control-flow/plan/build-branch-loop.ts +18 -5
  77. package/src/ir-to-client-js/control-flow/plan/build-component-loop.ts +4 -0
  78. package/src/ir-to-client-js/control-flow/plan/build-composite-loop.ts +15 -2
  79. package/src/ir-to-client-js/control-flow/plan/build-event-delegation.ts +32 -3
  80. package/src/ir-to-client-js/control-flow/plan/build-inner-loop.ts +26 -3
  81. package/src/ir-to-client-js/control-flow/plan/build-loop.ts +53 -2
  82. package/src/ir-to-client-js/control-flow/plan/event-delegation.ts +16 -1
  83. package/src/ir-to-client-js/control-flow/plan/loop.ts +40 -0
  84. package/src/ir-to-client-js/control-flow/shared.ts +28 -2
  85. package/src/ir-to-client-js/control-flow/stringify/branch-loop.ts +25 -3
  86. package/src/ir-to-client-js/control-flow/stringify/event-delegation.ts +76 -21
  87. package/src/ir-to-client-js/control-flow/stringify/loop.ts +65 -1
  88. package/src/ir-to-client-js/control-flow.ts +11 -0
  89. package/src/ir-to-client-js/html-template.ts +244 -39
  90. package/src/ir-to-client-js/imports.ts +1 -1
  91. package/src/ir-to-client-js/plan/build-static-array-child-init.ts +22 -10
  92. package/src/ir-to-client-js/reactivity.ts +6 -0
  93. package/src/ir-to-client-js/types.ts +28 -3
  94. package/src/jsx-to-ir.ts +1059 -167
  95. package/src/loop-destructure.ts +9 -5
  96. package/src/rich-type-refusal.ts +6 -2
  97. package/src/strip-types.ts +47 -0
  98. package/src/types.ts +159 -35
@@ -358,7 +358,157 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
358
358
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
359
359
  `;
360
360
 
361
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L59 — ✅ Sort then render 1`] = `
361
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L59 — ✅ Projection works on every adapter 1`] = `
362
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate, mapArray, patchLeaf } from '@barefootjs/client/runtime'
363
+
364
+ export function initTodoItem(__scope, _p = {}) {
365
+ if (!__scope) return
366
+ const __scopeId = __scope.getAttribute('bf-s')
367
+
368
+ const [_s0] = $t(__scope, 's0')
369
+
370
+ let __anchor_s0 = _s0
371
+ createEffect(() => {
372
+ const __val = String(_p.todo)
373
+ __anchor_s0 = __bfText(__anchor_s0, __val)
374
+ })
375
+
376
+ }
377
+
378
+ hydrate('TodoItem__b3c36eee', { init: initTodoItem, template: (_p) => \`<li bf="s1"><!--bf:s0-->\${escapeText(String(_p.todo))}<!--/--></li>\` })
379
+ export function TodoItem(_p, __bfKey) { return createComponent('TodoItem__b3c36eee', _p, __bfKey) }
380
+ export function initItem(__scope, _p = {}) {
381
+ if (!__scope) return
382
+ const __scopeId = __scope.getAttribute('bf-s')
383
+
384
+ const [_s0] = $t(__scope, 's0')
385
+
386
+ let __anchor_s0 = _s0
387
+ createEffect(() => {
388
+ const __val = String(_p.item)
389
+ __anchor_s0 = __bfText(__anchor_s0, __val)
390
+ })
391
+
392
+ }
393
+
394
+ hydrate('Item__b3c36eee', { init: initItem, template: (_p) => \`<li bf="s1"><!--bf:s0-->\${escapeText(String(_p.item))}<!--/--></li>\` })
395
+ export function Item(_p, __bfKey) { return createComponent('Item__b3c36eee', _p, __bfKey) }
396
+ function initDashboard() {}
397
+
398
+ hydrate('Dashboard__b3c36eee', { init: initDashboard, template: (_p) => \`<div>D</div>\` })
399
+ export function Dashboard(_p, __bfKey) { return createComponent('Dashboard__b3c36eee', _p, __bfKey) }
400
+ export function initExample(__scope, _p = {}) {
401
+ if (!__scope) return
402
+ const __scopeId = __scope.getAttribute('bf-s')
403
+
404
+ const children = _p.children
405
+ const [count, setCount] = createSignal(0)
406
+ const [isLoggedIn] = createSignal(false)
407
+ const [todos] = createSignal([])
408
+ const [items] = createSignal([])
409
+ const [filter] = createSignal('all')
410
+ const [accepted] = createSignal(false)
411
+ const [text, setText] = createSignal('')
412
+
413
+ const [_s1] = $(__scope, 's1')
414
+
415
+ mapArray(() => (todos()).flatMap((todo) => todo.tags.map((tag) => ({ k: (\`\${todo.id}:\${tag}\`), h: \`<li data-key-1="\${escapeAttr(\`\${todo.id}:\${tag}\`)}"><!--bf:s0-->\${escapeText(tag)}<!--/--></li>\` }))), _s1, (__bfD, __bfI) => String(__bfD.k ?? __bfI), (__bfD, __idx, __existing) => {
416
+ let __el = __existing
417
+ if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }
418
+ let __last = __existing ? undefined : __bfD().h
419
+ createEffect(() => {
420
+ const __html = __bfD().h
421
+ if (__last === undefined) { __last = __html; return }
422
+ if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }
423
+ })
424
+ return __el
425
+ }, 'l1')
426
+
427
+ }
428
+
429
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l1-->\${([]).flatMap((todo) => \`<!--bf-loop:l0-->\${todo.tags.map((tag) => \`<li data-key-1="\${escapeAttr(\`\${todo.id}:\${tag}\`)}"><!--bf:s0-->\${escapeText(tag)}<!--/--></li>\`).join('')}<!--bf-/loop:l0-->\`).join('')}<!--bf-/loop:l1--></div>\` })
430
+ export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
431
+ `;
432
+
433
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L64 — ✅ Statement body — JS-runtime adapters; /* @client */ on Go/Mojo etc. 1`] = `
434
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, mapArray, patchLeaf } from '@barefootjs/client/runtime'
435
+
436
+ export function initTodoItem(__scope, _p = {}) {
437
+ if (!__scope) return
438
+ const __scopeId = __scope.getAttribute('bf-s')
439
+
440
+ const [_s0] = $t(__scope, 's0')
441
+
442
+ let __anchor_s0 = _s0
443
+ createEffect(() => {
444
+ const __val = String(_p.todo)
445
+ __anchor_s0 = __bfText(__anchor_s0, __val)
446
+ })
447
+
448
+ }
449
+
450
+ hydrate('TodoItem__b3c36eee', { init: initTodoItem, template: (_p) => \`<li bf="s1"><!--bf:s0-->\${escapeText(String(_p.todo))}<!--/--></li>\` })
451
+ export function TodoItem(_p, __bfKey) { return createComponent('TodoItem__b3c36eee', _p, __bfKey) }
452
+ export function initItem(__scope, _p = {}) {
453
+ if (!__scope) return
454
+ const __scopeId = __scope.getAttribute('bf-s')
455
+
456
+ const [_s0] = $t(__scope, 's0')
457
+
458
+ let __anchor_s0 = _s0
459
+ createEffect(() => {
460
+ const __val = String(_p.item)
461
+ __anchor_s0 = __bfText(__anchor_s0, __val)
462
+ })
463
+
464
+ }
465
+
466
+ hydrate('Item__b3c36eee', { init: initItem, template: (_p) => \`<li bf="s1"><!--bf:s0-->\${escapeText(String(_p.item))}<!--/--></li>\` })
467
+ export function Item(_p, __bfKey) { return createComponent('Item__b3c36eee', _p, __bfKey) }
468
+ function initDashboard() {}
469
+
470
+ hydrate('Dashboard__b3c36eee', { init: initDashboard, template: (_p) => \`<div>D</div>\` })
471
+ export function Dashboard(_p, __bfKey) { return createComponent('Dashboard__b3c36eee', _p, __bfKey) }
472
+ export function initExample(__scope, _p = {}) {
473
+ if (!__scope) return
474
+ const __scopeId = __scope.getAttribute('bf-s')
475
+
476
+ const children = _p.children
477
+ const [count, setCount] = createSignal(0)
478
+ const [isLoggedIn] = createSignal(false)
479
+ const [todos] = createSignal([])
480
+ const [items] = createSignal([])
481
+ const [filter] = createSignal('all')
482
+ const [accepted] = createSignal(false)
483
+ const [text, setText] = createSignal('')
484
+
485
+ const [_s0] = $(__scope, 's0')
486
+
487
+ mapArray(() => (todos()).flatMap((todo) => {
488
+ if (todo.hidden) return []
489
+ return todo.tags.map(tag => ({ k: (\`\${todo.id}:\${tag}\`), h: \`<li>\${escapeText((tag))}</li>\` }))
490
+ }), _s0, (__bfD, __bfI) => String(__bfD.k ?? __bfI), (__bfD, __idx, __existing) => {
491
+ let __el = __existing
492
+ if (!__el) { const __tpl = document.createElement('template'); __tpl.innerHTML = __bfD().h; __el = __tpl.content.firstElementChild }
493
+ let __last = __existing ? undefined : __bfD().h
494
+ createEffect(() => {
495
+ const __html = __bfD().h
496
+ if (__last === undefined) { __last = __html; return }
497
+ if (__html !== __last) { __last = __html; patchLeaf(__el, __html) }
498
+ })
499
+ return __el
500
+ }, 'l0')
501
+
502
+ }
503
+
504
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s0"><!--bf-loop:l0-->\${([]).flatMap((todo) => {
505
+ if (todo.hidden) return []
506
+ return todo.tags.map(tag => \`<li>\${escapeText((tag))}</li>\`)
507
+ }).join('')}<!--bf-/loop:l0--></div>\` })
508
+ export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
509
+ `;
510
+
511
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L74 — ✅ Sort then render 1`] = `
362
512
  "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
363
513
 
364
514
  export function initTodoItem(__scope, _p = {}) {
@@ -423,7 +573,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
423
573
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
424
574
  `;
425
575
 
426
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L64 — ✅ Filter, sort, then render 1`] = `
576
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L79 — ✅ Filter, sort, then render 1`] = `
427
577
  "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
428
578
 
429
579
  export function initTodoItem(__scope, _p = {}) {
@@ -488,7 +638,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
488
638
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
489
639
  `;
490
640
 
491
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L69 — ✅ Multi-key: sort by price, break ties by name 1`] = `
641
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L84 — ✅ Multi-key: sort by price, break ties by name 1`] = `
492
642
  "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
493
643
 
494
644
  export function initTodoItem(__scope, _p = {}) {
@@ -553,7 +703,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
553
703
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
554
704
  `;
555
705
 
556
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L74 — ✅ Relational ternary 1`] = `
706
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L89 — ✅ Relational ternary 1`] = `
557
707
  "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
558
708
 
559
709
  export function initTodoItem(__scope, _p = {}) {
@@ -618,7 +768,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
618
768
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
619
769
  `;
620
770
 
621
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L85 — (no label) 1`] = `
771
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L100 — (no label) 1`] = `
622
772
  "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
623
773
 
624
774
  export function initTodoItem(__scope, _p = {}) {
@@ -682,7 +832,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div><button bf="s0"
682
832
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
683
833
  `;
684
834
 
685
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L125 — ✅ Nested \`.filter()\` / \`.map()\` now compiles everywhere 1`] = `
835
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L142 — ✅ Nested \`.filter()\` / \`.map()\` now compiles everywhere 1`] = `
686
836
  "import { $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
687
837
 
688
838
  export function initTodoItem(__scope, _p = {}) {
@@ -748,7 +898,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf:
748
898
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
749
899
  `;
750
900
 
751
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L132 — ❌ BF101 on Go/Mojo; works on Hono 1`] = `
901
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L149 — ❌ BF101 on Go/Mojo; works on Hono 1`] = `
752
902
  "import { $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
753
903
 
754
904
  export function initTodoItem(__scope, _p = {}) {
@@ -814,7 +964,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf:
814
964
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
815
965
  `;
816
966
 
817
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L135 — ✅ Add /* @client */ to evaluate on the client 1`] = `
967
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L152 — ✅ Add /* @client */ to evaluate on the client 1`] = `
818
968
  "import { $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, updateClientMarker } from '@barefootjs/client/runtime'
819
969
 
820
970
  export function initTodoItem(__scope, _p = {}) {
@@ -877,7 +1027,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
877
1027
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
878
1028
  `;
879
1029
 
880
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L142 — ❌ BF101 on Go/Mojo; works on Hono 1`] = `
1030
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L159 — ❌ BF101 on Go/Mojo; works on Hono 1`] = `
881
1031
  "import { $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
882
1032
 
883
1033
  export function initTodoItem(__scope, _p = {}) {
@@ -943,7 +1093,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf:
943
1093
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
944
1094
  `;
945
1095
 
946
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L145 — ✅ Use /* @client */ 1`] = `
1096
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L162 — ✅ Use /* @client */ 1`] = `
947
1097
  "import { $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, updateClientMarker } from '@barefootjs/client/runtime'
948
1098
 
949
1099
  export function initTodoItem(__scope, _p = {}) {
@@ -1006,7 +1156,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
1006
1156
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
1007
1157
  `;
1008
1158
 
1009
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L162 — ❌ BF101 on Go/Mojo; works on Hono 1`] = `
1159
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L179 — ❌ BF101 on Go/Mojo; works on Hono 1`] = `
1010
1160
  "import { $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
1011
1161
 
1012
1162
  export function initTodoItem(__scope, _p = {}) {
@@ -1072,7 +1222,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf:
1072
1222
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
1073
1223
  `;
1074
1224
 
1075
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L165 — ✅ Use arrow functions for adapter portability 1`] = `
1225
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L182 — ✅ Use arrow functions for adapter portability 1`] = `
1076
1226
  "import { $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
1077
1227
 
1078
1228
  export function initTodoItem(__scope, _p = {}) {
@@ -1138,7 +1288,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf:
1138
1288
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
1139
1289
  `;
1140
1290
 
1141
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L178 — ✅ Value-producing block bodies normalize (let-inline) and lower everywhere 1`] = `
1291
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L195 — ✅ Value-producing block bodies normalize (let-inline) and lower everywhere 1`] = `
1142
1292
  "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
1143
1293
 
1144
1294
  export function initTodoItem(__scope, _p = {}) {
@@ -1203,7 +1353,7 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
1203
1353
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
1204
1354
  `;
1205
1355
 
1206
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L193 Use /* @client */ 1`] = `
1356
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L205 BF021 on Go/Mojo a JS-runtime target (Hono, CSR) runs the comparator 1`] = `
1207
1357
  "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
1208
1358
 
1209
1359
  export function initTodoItem(__scope, _p = {}) {
@@ -1268,7 +1418,101 @@ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-
1268
1418
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
1269
1419
  `;
1270
1420
 
1271
- exports[`docs/core/rendering/jsx-compatibility.md doc-examples L211 — ✅ Use /* @client */, or inline / re-declare the comparator locally 1`] = `
1421
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L210 — ✅ Use /* @client */ 1`] = `
1422
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
1423
+
1424
+ export function initTodoItem(__scope, _p = {}) {
1425
+ if (!__scope) return
1426
+ const __scopeId = __scope.getAttribute('bf-s')
1427
+
1428
+ const [_s0] = $t(__scope, 's0')
1429
+
1430
+ let __anchor_s0 = _s0
1431
+ createEffect(() => {
1432
+ const __val = String(_p.todo)
1433
+ __anchor_s0 = __bfText(__anchor_s0, __val)
1434
+ })
1435
+
1436
+ }
1437
+
1438
+ hydrate('TodoItem__b3c36eee', { init: initTodoItem, template: (_p) => \`<li bf="s1"><!--bf:s0-->\${escapeText(String(_p.todo))}<!--/--></li>\` })
1439
+ export function TodoItem(_p, __bfKey) { return createComponent('TodoItem__b3c36eee', _p, __bfKey) }
1440
+ export function initItem(__scope, _p = {}) {
1441
+ if (!__scope) return
1442
+ const __scopeId = __scope.getAttribute('bf-s')
1443
+
1444
+ const [_s0] = $t(__scope, 's0')
1445
+
1446
+ let __anchor_s0 = _s0
1447
+ createEffect(() => {
1448
+ const __val = String(_p.item)
1449
+ __anchor_s0 = __bfText(__anchor_s0, __val)
1450
+ })
1451
+
1452
+ }
1453
+
1454
+ hydrate('Item__b3c36eee', { init: initItem, template: (_p) => \`<li bf="s1"><!--bf:s0-->\${escapeText(String(_p.item))}<!--/--></li>\` })
1455
+ export function Item(_p, __bfKey) { return createComponent('Item__b3c36eee', _p, __bfKey) }
1456
+ function initDashboard() {}
1457
+
1458
+ hydrate('Dashboard__b3c36eee', { init: initDashboard, template: (_p) => \`<div>D</div>\` })
1459
+ export function Dashboard(_p, __bfKey) { return createComponent('Dashboard__b3c36eee', _p, __bfKey) }
1460
+ export function initExample(__scope, _p = {}) {
1461
+ if (!__scope) return
1462
+ const __scopeId = __scope.getAttribute('bf-s')
1463
+
1464
+ const children = _p.children
1465
+ const [count, setCount] = createSignal(0)
1466
+ const [isLoggedIn] = createSignal(false)
1467
+ const [todos] = createSignal([])
1468
+ const [items] = createSignal([])
1469
+ const [filter] = createSignal('all')
1470
+ const [accepted] = createSignal(false)
1471
+ const [text, setText] = createSignal('')
1472
+
1473
+ const [_s1] = $(__scope, 's1')
1474
+
1475
+ mapArray(() => items().sort((a, b) => { let r = 0; r = a.name > b.name ? 1 : -1; return r }), _s1, (item) => String(item.id), (item, __idx, __existing) => {
1476
+ if (__existing) { initChild('Item__b3c36eee', __existing, { get item() { return item() } }); return __existing }
1477
+ return createComponent('Item__b3c36eee', { get item() { return item() } }, item().id)
1478
+ }, 'l0')
1479
+
1480
+ }
1481
+
1482
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).sort((a, b) => { let r = 0; r = a.name > b.name ? 1 : -1; return r }).map((item) => \`\${renderChild('Item__b3c36eee', {item: item}, item.id)}\`).join('')}<!--bf-/loop:l0--></div>\` })
1483
+ export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
1484
+ `;
1485
+
1486
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L222 — ❌ BF021 on Go/Mojo — \`byPrice\` is imported, not declared in this file 1`] = `
1487
+ "import { $, $t, createComponent, createEffect, escapeAttr, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
1488
+ import { byPrice } from './comparators'
1489
+
1490
+
1491
+ export function initSortedList(__scope, _p = {}) {
1492
+ if (!__scope) return
1493
+ const __scopeId = __scope.getAttribute('bf-s')
1494
+
1495
+ const items = _p.items
1496
+
1497
+ const [_s1] = $(__scope, 's1')
1498
+
1499
+ const __tpl_l0 = document.createElement('template')
1500
+ __tpl_l0.innerHTML = \`<li data-key=""><!--bf:s0--><!--/--></li>\`
1501
+ mapArray(() => items.sort(byPrice), _s1, (item) => String(item.id), (item, __idx, __existing) => {
1502
+ const __el = __existing ?? __tpl_l0.content.firstElementChild.cloneNode(true)
1503
+ const __p = __existing ? null : [__el.firstChild]
1504
+ { const __rt_s0 = __p ? tAfter(__p[0]) : $t(__el, 's0')[0]
1505
+ if (__rt_s0) createEffect(() => { __rt_s0.textContent = String(item().name) }) }
1506
+ return __el
1507
+ }, 'l0')
1508
+
1509
+ }
1510
+
1511
+ hydrate('SortedList__b3c36eee', { init: initSortedList, template: (_p) => \`<ul bf="s1"><!--bf-loop:l0-->\${_p.items.sort(byPrice).map((item) => \`<li data-key="\${escapeAttr(item.id)}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></ul>\` })
1512
+ export function SortedList(_p, __bfKey) { return createComponent('SortedList__b3c36eee', _p, __bfKey) }"
1513
+ `;
1514
+
1515
+ exports[`docs/core/rendering/jsx-compatibility.md doc-examples L228 — ✅ Use /* @client */, or inline / re-declare the comparator locally 1`] = `
1272
1516
  "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, initChild, mapArray, renderChild } from '@barefootjs/client/runtime'
1273
1517
 
1274
1518
  export function initTodoItem(__scope, _p = {}) {
@@ -4351,7 +4595,7 @@ export function Example(_p, __bfKey) { return createComponent('Example', _p, __b
4351
4595
  `;
4352
4596
 
4353
4597
  exports[`docs/core/adapters/hono-adapter.md doc-examples L190 — (no label) 1`] = `
4354
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
4598
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
4355
4599
 
4356
4600
  export function initTodoItem(__scope, _p = {}) {
4357
4601
  if (!__scope) return
@@ -4416,7 +4660,7 @@ export function initExample(__scope, _p = {}) {
4416
4660
 
4417
4661
  }
4418
4662
 
4419
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).map((item) => \`<li data-key="\${item}"><!--bf:s0-->\${escapeText(item)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4663
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).map((item) => \`<li data-key="\${escapeAttr(item)}"><!--bf:s0-->\${escapeText(item)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4420
4664
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
4421
4665
  `;
4422
4666
 
@@ -4473,7 +4717,7 @@ export function Counter(_p, __bfKey) { return createComponent('Counter', _p, __b
4473
4717
  `;
4474
4718
 
4475
4719
  exports[`docs/core/adapters/go-template-adapter.md doc-examples L168 — (no label) 1`] = `
4476
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
4720
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
4477
4721
 
4478
4722
  export function initTodoItem(__scope, _p = {}) {
4479
4723
  if (!__scope) return
@@ -4538,12 +4782,12 @@ export function initExample(__scope, _p = {}) {
4538
4782
 
4539
4783
  }
4540
4784
 
4541
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).map((item) => \`<li data-key="\${item}"><!--bf:s0-->\${escapeText(item)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4785
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).map((item) => \`<li data-key="\${escapeAttr(item)}"><!--bf:s0-->\${escapeText(item)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4542
4786
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
4543
4787
  `;
4544
4788
 
4545
4789
  exports[`docs/core/adapters/go-template-adapter.md doc-examples L180 — (no label) 1`] = `
4546
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
4790
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
4547
4791
 
4548
4792
  export function initTodoItem(__scope, _p = {}) {
4549
4793
  if (!__scope) return
@@ -4608,12 +4852,12 @@ export function initExample(__scope, _p = {}) {
4608
4852
 
4609
4853
  }
4610
4854
 
4611
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).filter(item => item.active).map((item) => \`<li data-key="\${item.id}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4855
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).filter(item => item.active).map((item) => \`<li data-key="\${escapeAttr(item.id)}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4612
4856
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
4613
4857
  `;
4614
4858
 
4615
4859
  exports[`docs/core/adapters/go-template-adapter.md doc-examples L194 — (no label) 1`] = `
4616
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
4860
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate } from '@barefootjs/client/runtime'
4617
4861
 
4618
4862
  export function initTodoItem(__scope, _p = {}) {
4619
4863
  if (!__scope) return
@@ -4679,7 +4923,7 @@ export function initExample(__scope, _p = {}) {
4679
4923
 
4680
4924
  }
4681
4925
 
4682
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${items.toSorted((a, b) => a.priority - b.priority).map((t) => \`<li data-key="\${t.id}"><!--bf:s0-->\${escapeText(t.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4926
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${items.toSorted((a, b) => a.priority - b.priority).map((t) => \`<li data-key="\${escapeAttr(t.id)}"><!--bf:s0-->\${escapeText(t.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4683
4927
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
4684
4928
  `;
4685
4929
 
@@ -4894,7 +5138,7 @@ export function Example(_p, __bfKey) { return createComponent('Example', _p, __b
4894
5138
  `;
4895
5139
 
4896
5140
  exports[`docs/core/adapters/custom-adapter.md doc-examples L195 — (no label) 1`] = `
4897
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
5141
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate } from '@barefootjs/client/runtime'
4898
5142
 
4899
5143
  export function initTodoItem(__scope, _p = {}) {
4900
5144
  if (!__scope) return
@@ -4960,12 +5204,12 @@ export function initExample(__scope, _p = {}) {
4960
5204
 
4961
5205
  }
4962
5206
 
4963
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${items.map((item) => \`<li data-key="\${item.id}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5207
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${items.map((item) => \`<li data-key="\${escapeAttr(item.id)}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
4964
5208
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
4965
5209
  `;
4966
5210
 
4967
5211
  exports[`docs/core/adapters/custom-adapter.md doc-examples L200 — (no label) 1`] = `
4968
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate } from '@barefootjs/client/runtime'
5212
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate } from '@barefootjs/client/runtime'
4969
5213
 
4970
5214
  export function initTodoItem(__scope, _p = {}) {
4971
5215
  if (!__scope) return
@@ -5031,7 +5275,7 @@ export function initExample(__scope, _p = {}) {
5031
5275
 
5032
5276
  }
5033
5277
 
5034
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${items.map((item) => \`<li data-key="\${item.id}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5278
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${items.map((item) => \`<li data-key="\${escapeAttr(item.id)}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5035
5279
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
5036
5280
  `;
5037
5281
 
@@ -5045,7 +5289,7 @@ export function StatementExample(_p, __bfKey) { return createComponent('Statemen
5045
5289
  `;
5046
5290
 
5047
5291
  exports[`docs/core/advanced/compiler-internals.md doc-examples L126 — (no label) 1`] = `
5048
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
5292
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
5049
5293
 
5050
5294
  export function initTodoItem(__scope, _p = {}) {
5051
5295
  if (!__scope) return
@@ -5110,7 +5354,7 @@ export function initExample(__scope, _p = {}) {
5110
5354
 
5111
5355
  }
5112
5356
 
5113
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).filter(t => !t.done).toSorted((a, b) => a.date - b.date).map((t) => \`<li data-key="\${t.id}"><!--bf:s0-->\${escapeText(t.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5357
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).filter(t => !t.done).toSorted((a, b) => a.date - b.date).map((t) => \`<li data-key="\${escapeAttr(t.id)}"><!--bf:s0-->\${escapeText(t.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5114
5358
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
5115
5359
  `;
5116
5360
 
@@ -5177,7 +5421,7 @@ export function StatementExample(_p, __bfKey) { return createComponent('Statemen
5177
5421
  `;
5178
5422
 
5179
5423
  exports[`docs/core/advanced/performance.md doc-examples L67 — ✅ Stable key — DOM nodes reused when list changes 1`] = `
5180
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
5424
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
5181
5425
 
5182
5426
  export function initTodoItem(__scope, _p = {}) {
5183
5427
  if (!__scope) return
@@ -5242,12 +5486,12 @@ export function initExample(__scope, _p = {}) {
5242
5486
 
5243
5487
  }
5244
5488
 
5245
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).map((item) => \`<li data-key="\${item.id}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5489
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).map((item) => \`<li data-key="\${escapeAttr(item.id)}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5246
5490
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
5247
5491
  `;
5248
5492
 
5249
5493
  exports[`docs/core/advanced/performance.md doc-examples L70 — ❌ Index key — DOM nodes recreated on reorder 1`] = `
5250
- "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
5494
+ "import { $, $t, __bfText, createComponent, createEffect, createSignal, escapeAttr, escapeText, hydrate, mapArray, tAfter } from '@barefootjs/client/runtime'
5251
5495
 
5252
5496
  export function initTodoItem(__scope, _p = {}) {
5253
5497
  if (!__scope) return
@@ -5312,7 +5556,7 @@ export function initExample(__scope, _p = {}) {
5312
5556
 
5313
5557
  }
5314
5558
 
5315
- hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).map((item, i) => \`<li data-key="\${i}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5559
+ hydrate('Example', { init: initExample, template: (_p) => \`<div bf="s1"><!--bf-loop:l0-->\${([]).map((item, i) => \`<li data-key="\${escapeAttr(i)}"><!--bf:s0-->\${escapeText(item.name)}<!--/--></li>\`).join('')}<!--bf-/loop:l0--></div>\` })
5316
5560
  export function Example(_p, __bfKey) { return createComponent('Example', _p, __bfKey) }"
5317
5561
  `;
5318
5562
 
@@ -1753,8 +1753,14 @@ describe('Client JS generation', () => {
1753
1753
 
1754
1754
  // Should contain the preamble in the map callback
1755
1755
  expect(clientJs?.content).toContain('const label = item.name.toUpperCase()')
1756
- // Should contain the template with the label reference
1757
- expect(clientJs?.content).toContain('${label}')
1756
+ // The sole child expression reads the preamble local `label` (#2389
1757
+ // patch-on-update): it is classified as a preamble-patched region —
1758
+ // slot-marked and escaped, like an ordinary text slot — rather than a
1759
+ // bare `${label}` interpolation that would freeze on a same-key
1760
+ // update. See `preamble-region-patch.test.ts` for the full region
1761
+ // contract (row-template marker, region-patch effect, SSR parity).
1762
+ expect(clientJs?.content).toContain('${escapeText(label)}')
1763
+ expect(clientJs?.content).toContain('patchSlotRange')
1758
1764
  })
1759
1765
 
1760
1766
  test('handles block body with multiple variable declarations', () => {
@@ -1824,11 +1830,11 @@ describe('Client JS generation', () => {
1824
1830
  }
1825
1831
  const loop = findLoop(ir.root)
1826
1832
  expect(loop).toBeTruthy()
1827
- expect(loop.mapPreamble).toBeUndefined()
1833
+ expect(loop.preamble).toBeUndefined()
1828
1834
  }
1829
1835
  })
1830
1836
 
1831
- test('generates mapPreamble in IR', () => {
1837
+ test('generates the structured preamble in IR', () => {
1832
1838
  const source = `
1833
1839
  'use client'
1834
1840
  import { createSignal } from '@barefootjs/client'
@@ -1866,7 +1872,14 @@ describe('Client JS generation', () => {
1866
1872
  }
1867
1873
  const loop = findLoop(ir.root)
1868
1874
  expect(loop).toBeTruthy()
1869
- expect(loop.mapPreamble).toContain('const label = item.name.toUpperCase()')
1875
+ // Structured carrier (Stage 3 root cure): the declaration lands in the
1876
+ // preamble's js segments; the IR stays plain serializable data.
1877
+ const jsText = loop.preamble.segments
1878
+ .filter((s: any) => s.kind === 'js')
1879
+ .map((s: any) => s.text)
1880
+ .join('')
1881
+ expect(jsText).toContain('const label = item.name.toUpperCase()')
1882
+ expect(loop.preamble.declaredNames).toContain('label')
1870
1883
  expect(loop.children).toHaveLength(1)
1871
1884
  expect(loop.children[0].type).toBe('element')
1872
1885
  })
@@ -154,11 +154,11 @@ describe('Compiler-Runtime Contract', () => {
154
154
  expect(js).not.toContain('data-key-2')
155
155
  // Outer loop items must have data-key in the SSR branch template.
156
156
  // Post-O-7 the emission is an unguarded template-literal interpolation
157
- // (`data-key="${group.id}"`) instead of a `... != null ? ... : ''`
158
- // ternary string concat.
159
- expect(js).toMatch(/data-key="\$\{group\.id\}"/)
157
+ // — escapeAttr-wrapped like every dynamic attribute, so a `"` in a key
158
+ // can't corrupt the client-assembled HTML or diverge from SSR bytes.
159
+ expect(js).toMatch(/data-key="\$\{escapeAttr\(group\.id\)\}"/)
160
160
  // Inner loop items must have data-key-1 in the SSR branch template.
161
- expect(js).toMatch(/data-key-1="\$\{item\.id\}"/)
161
+ expect(js).toMatch(/data-key-1="\$\{escapeAttr\(item\.id\)\}"/)
162
162
  })
163
163
 
164
164
  test('loop inside conditional wires reactive conditionals in mapArray callback', () => {
@@ -1115,7 +1115,18 @@ describe('<Async> body error path — sync throw + async reject (#1375)', () =>
1115
1115
  })
1116
1116
 
1117
1117
  describe('createPortal inside .map() — per-item portal owner tracking', () => {
1118
- test('one portal created per loop item', () => {
1118
+ // SURFACED LIMITATION. Passing *inline* JSX as a `createPortal` argument is
1119
+ // an unsupported shape: there is no compiler lowering for it (the JSX is
1120
+ // never compiled to DOM ops) and no runtime per-loop-item portal tracking.
1121
+ // Here the `createPortal(<div>…</div>, …)` sits as a non-return statement in
1122
+ // a `.map()` callback, so as of Stage 3 of spec/callback-fidelity.md the
1123
+ // generic "map body builds JSX in a preamble statement" guard refuses it
1124
+ // with BF021 — replacing what was previously a *silent* leak of the raw
1125
+ // `<div>{it.body}</div>` verbatim into the client bundle. The body below
1126
+ // asserts the intended end state (compiles clean, one portal per item);
1127
+ // drop `.todo` once inline-JSX createPortal (and per-item owner tracking)
1128
+ // is actually implemented.
1129
+ test.todo('one portal created per loop item', () => {
1119
1130
  const src = `
1120
1131
  'use client'
1121
1132
  import { createSignal, createPortal } from '@barefootjs/client'