Fix DOM-preserving hydration: text node mismatch + conditional markers

Two issues with the initial hydration implementation:

1. Text node mismatch: SSR merges adjacent text into one node
   ("0 / 16") but client renders three separate children. When the
   cursor ran out, new nodes were created but dom-append was
   unconditionally skipped. Fix: only skip append when the child
   already has a parent (existing SSR node). New nodes (nil parent)
   get appended even during hydration.

2. Conditional markers: dispatch-render-form for if/when/cond in
   island scope was injecting comment markers during hydration,
   corrupting the DOM. Fix: skip the reactive conditional machinery
   during hydration — just evaluate and render the active branch
   normally, walking the cursor. Reactivity for conditionals
   activates after the first user-triggered re-render.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 14:27:37 +00:00
parent a2a4d17d53
commit ab50c4516e
3 changed files with 54 additions and 20 deletions

View File

@@ -375,7 +375,11 @@
(fn () (render-to-dom arg env new-ns)))
(spread? child)
nil
:else (when (not (hydrating?)) (dom-append el child)))))
:else (when
(or
(not (hydrating?))
(nil? (dom-parent child)))
(dom-append el child)))))
(assoc state "i" (inc (get state "i"))))))))
(dict "i" 0 "skip" false)
args)
@@ -477,9 +481,18 @@
((args :as list) (env :as dict) (ns :as string))
(if
(hydrating?)
(do
(for-each (fn (x) (render-to-dom x env ns)) args)
(create-fragment))
(let
((frag (create-fragment)))
(for-each
(fn
(x)
(let
((result (render-to-dom x env ns)))
(when
(and (not (spread? result)) (nil? (dom-parent result)))
(dom-append frag result))))
args)
frag)
(let
((frag (create-fragment)))
(for-each
@@ -553,7 +566,7 @@
(cond
(= name "if")
(if
(island-scope?)
(and (island-scope?) (not (hydrating?)))
(let
((marker (create-comment "r-if"))
(current-nodes (list))
@@ -617,7 +630,7 @@
(create-fragment)))))
(= name "when")
(if
(island-scope?)
(and (island-scope?) (not (hydrating?)))
(let
((marker (create-comment "r-when"))
(current-nodes (list))
@@ -701,7 +714,7 @@
frag)))
(= name "cond")
(if
(island-scope?)
(and (island-scope?) (not (hydrating?)))
(let
((marker (create-comment "r-cond"))
(current-nodes (list))
@@ -1080,7 +1093,9 @@
(c)
(let
((result (render-to-dom c env ns)))
(when (not (hydrating?)) (dom-append el result))))
(when
(or (not (hydrating?)) (nil? (dom-parent result)))
(dom-append el result))))
children)
(do (when (hydrating?) (hydrate-exit-element)) el)))))
(define
@@ -1135,7 +1150,9 @@
(c)
(let
((result (render-to-dom c env ns)))
(when (not (hydrating?)) (dom-append el result))))
(when
(or (not (hydrating?)) (nil? (dom-parent result)))
(dom-append el result))))
children)
(do (when (hydrating?) (hydrate-exit-element)) el)))))
(define

File diff suppressed because one or more lines are too long

View File

@@ -375,7 +375,11 @@
(fn () (render-to-dom arg env new-ns)))
(spread? child)
nil
:else (when (not (hydrating?)) (dom-append el child)))))
:else (when
(or
(not (hydrating?))
(nil? (dom-parent child)))
(dom-append el child)))))
(assoc state "i" (inc (get state "i"))))))))
(dict "i" 0 "skip" false)
args)
@@ -477,9 +481,18 @@
((args :as list) (env :as dict) (ns :as string))
(if
(hydrating?)
(do
(for-each (fn (x) (render-to-dom x env ns)) args)
(create-fragment))
(let
((frag (create-fragment)))
(for-each
(fn
(x)
(let
((result (render-to-dom x env ns)))
(when
(and (not (spread? result)) (nil? (dom-parent result)))
(dom-append frag result))))
args)
frag)
(let
((frag (create-fragment)))
(for-each
@@ -553,7 +566,7 @@
(cond
(= name "if")
(if
(island-scope?)
(and (island-scope?) (not (hydrating?)))
(let
((marker (create-comment "r-if"))
(current-nodes (list))
@@ -617,7 +630,7 @@
(create-fragment)))))
(= name "when")
(if
(island-scope?)
(and (island-scope?) (not (hydrating?)))
(let
((marker (create-comment "r-when"))
(current-nodes (list))
@@ -701,7 +714,7 @@
frag)))
(= name "cond")
(if
(island-scope?)
(and (island-scope?) (not (hydrating?)))
(let
((marker (create-comment "r-cond"))
(current-nodes (list))
@@ -1080,7 +1093,9 @@
(c)
(let
((result (render-to-dom c env ns)))
(when (not (hydrating?)) (dom-append el result))))
(when
(or (not (hydrating?)) (nil? (dom-parent result)))
(dom-append el result))))
children)
(do (when (hydrating?) (hydrate-exit-element)) el)))))
(define
@@ -1135,7 +1150,9 @@
(c)
(let
((result (render-to-dom c env ns)))
(when (not (hydrating?)) (dom-append el result))))
(when
(or (not (hydrating?)) (nil? (dom-parent result)))
(dom-append el result))))
children)
(do (when (hydrating?) (hydrate-exit-element)) el)))))
(define