Commit1b039fe6Recorded17 Jul 2026Repositorysigil-web-client
sigil-web-client: v0.15.3 normalize '() content to '()
Message
web-client-normalize-children fell through '() to the (else (list value)) branch, producing (()): a phantom empty text child. That left an emptied element's old-records list non-null, so reconcile-children! never ran the empty->populated bulk clear, and foreign children of a reused element could survive a full replacement. Treat the empty content list like #f (no children). Adds a web-client-normalize-children test group (empty, #f, content list, single atom).
Changed
package.sgl | 2 +-
src/sigil/web/client.sgl | 11 +++++++++--
test/test-client.sgl | 19 +++++++++++++++++++
3 files changed, 29 insertions(+), 3 deletions(-)Diff
package.sglmodified
@@ -14,7 +14,7 @@
14
15
(package 16
name: "sigil-web-client"−17
version: "0.15.2"+17
version: "0.15.3" 18
sigil: "^0.17" 19
description: "Client-side WASM UI framework for Sigil: react-like browser app rendering" 20
url: "https://codeberg.org/sigil/sigil-web-client"src/sigil/web/client.sglmodified
@@ -19,7 +19,10 @@
19
web-client-reset! 20
;; Exported for tests: the pure child-order diff used by the keyed 21
;; reconciler. Not part of the stable public API.−22
web-client-child-moves)+22
web-client-child-moves+23
;; Exported for tests: the SXML child-content normalizer. Not part of+24
;; the stable public API.+25
web-client-normalize-children) 26
27
(begin 28
(define web-client-roots '())@@ -148,7 +151,11 @@
151
152
(define (web-client-normalize-children value) 153
(cond−151
((not value) '())+154
;; #f and the empty content list both mean "no children". Without the+155
;; null? case '() falls through to (else (list value)) => (()), a+156
;; phantom empty text child that leaves the old-records list non-null,+157
;; so reconcile-children! never runs the empty->populated bulk clear.+158
((or (not value) (null? value)) '()) 159
((and (web-client-sxml-element? value) 160
(or (eq? (web-client-sxml-tag value) '*TOP*) 161
(eq? (web-client-sxml-tag value) 'document)))test/test-client.sglmodified
@@ -23,6 +23,25 @@
23
(test "clears retained root state" 24
(assert-true (web-client-reset!)))) 25
+26
;; Normalizing element content into a flat child list. The empty-content case+27
;; is load-bearing: '() must stay '() so an emptied element reconciles as+28
;; genuinely empty (old-records null => the empty->populated bulk clear fires).+29
;; Before the null? case, '() fell to (else (list value)) => (()), a phantom+30
;; empty text child that suppressed the clear and leaked foreign children.+31
(test-group "web-client-normalize-children"+32
(test "empty content stays empty"+33
(assert-equal '() (web-client-normalize-children '())))+34
+35
(test "#f content is empty"+36
(assert-equal '() (web-client-normalize-children #f)))+37
+38
(test "a content list passes through unchanged"+39
(assert-equal '((span "a") (span "b"))+40
(web-client-normalize-children '((span "a") (span "b")))))+41
+42
(test "a single atom is wrapped as one child"+43
(assert-equal '("hello") (web-client-normalize-children "hello"))))+44
45
;; The pure child-order diff behind the keyed reconciler. Node handles are 46
;; plain integers here, exactly as the DOM bridge represents them. Each op is 47
;; (node . ref): insert node before ref, ref #f = append at the end.