@@ -37,7 +37,6 @@
37-- In "hydration mode", the preexisting DOM __must contain__ what the builder
37-- In "hydration mode", the preexisting DOM __must contain__ what the builder
38-- will expect at switchover time (the time at which parity with the static
38-- will expect at switchover time (the time at which parity with the static
39-- renderer is reached, and the time after which the page is "live").
39-- renderer is reached, and the time after which the page is "live").
40−-- There can be extra nodes, but no missing or incorrect nodes.
42-- For example, displaying the current time as text should be done inside
41-- For example, displaying the current time as text should be done inside
43-- 'Reflex.Dom.Prerender.prerender' to ensure that we don't attempt to hydrate the incorrect text.
42-- 'Reflex.Dom.Prerender.prerender' to ensure that we don't attempt to hydrate the incorrect text.
@@ -52,16 +51,17 @@ module Reflex.Dom.Builder.Immediate
52 , runHydrationRunnerT
51 , runHydrationRunnerT
53 , ImmediateDomBuilderT
52 , ImmediateDomBuilderT
54 , runHydrationDomBuilderT
53 , runHydrationDomBuilderT
56 , getHydrationMode
54 , getHydrationMode
57 , addHydrationStep
55 , addHydrationStep
58 , addHydrationStepWithSetup
56 , addHydrationStepWithSetup
59 , setPreviousNode
57 , setPreviousNode
60−
58+ , insertAfterPreviousNode
61 , askParent
60 , askParent
62 , askEvents
61 , askEvents
64 , textNodeInternal
63 , textNodeInternal
64+ , removeSubsequentNodes
65 , deleteBetweenExclusive
65 , deleteBetweenExclusive
66 , extractBetweenExclusive
66 , extractBetweenExclusive
67 , deleteUpTo
67 , deleteUpTo
@@ -73,7 +73,6 @@ module Reflex.Dom.Builder.Immediate
73 , EventFilterTriggerRef (..)
73 , EventFilterTriggerRef (..)
75 , elementInternal
75 , elementInternal
77 , HydrationDomSpace
76 , HydrationDomSpace
78 , GhcjsDomSpace
77 , GhcjsDomSpace
79 , GhcjsDomHandler (..)
78 , GhcjsDomHandler (..)
@@ -125,13 +124,13 @@ import Control.Monad.Exception
125import Control.Monad.Primitive
124import Control.Monad.Primitive
126import Control.Monad.Reader
125import Control.Monad.Reader
127import Control.Monad.Ref
126import Control.Monad.Ref
128−import Control.Monad.State.Strict (StateT, evalStateT, mapStateT, get, put)
127+import Control.Monad.State.Strict (StateT, mapStateT, get, modify', gets, runStateT)
129import Data.Bitraversable
128import Data.Bitraversable
130import Data.Default
129import Data.Default
131import Data.Dependent.Map (DMap)
130import Data.Dependent.Map (DMap)
132import Data.Dependent.Sum
131import Data.Dependent.Sum
133import Data.FastMutableIntMap (PatchIntMap (..))
132import Data.FastMutableIntMap (PatchIntMap (..))
134−import Data.Foldable (for_)
133+import Data.Foldable (for_, traverse_)
135import Data.Functor.Compose
134import Data.Functor.Compose
136import Data.Functor.Constant
135import Data.Functor.Constant
137import Data.Functor.Misc
136import Data.Functor.Misc
@@ -141,7 +140,6 @@ import Data.IntMap.Strict (IntMap)
141import Data.Maybe
140import Data.Maybe
142import Data.Some (Some(..))
141import Data.Some (Some(..))
143import Data.Text (Text)
142import Data.Text (Text)
144−import Data.Typeable (Typeable)
145import Foreign.JavaScript.Internal.Utils
143import Foreign.JavaScript.Internal.Utils
146import Foreign.JavaScript.TH
144import Foreign.JavaScript.TH
147import GHCJS.DOM.Document (Document, createDocumentFragment, createElement, createElementNS, createTextNode, createComment)
145import GHCJS.DOM.Document (Document, createDocumentFragment, createElement, createElementNS, createTextNode, createComment)
@@ -260,22 +258,35 @@ instance (Reflex t, MonadFix m) => DomRenderHook t (HydrationDomBuilderT s t m)
261-- | The monad which performs the delayed actions to reuse prerendered nodes and set up events.
259-- | The monad which performs the delayed actions to reuse prerendered nodes and set up events.
262-- State contains reference to the previous node sibling, if any, and the reader contains reference to the parent node.
260-- State contains reference to the previous node sibling, if any, and the reader contains reference to the parent node.
263−newtype HydrationRunnerT t m a = HydrationRunnerT { unHydrationRunnerT :: StateT (Maybe Node) (ReaderT Node (DomRenderHookT t m)) a }
261+newtype HydrationRunnerT t m a = HydrationRunnerT { unHydrationRunnerT :: StateT HydrationState (ReaderT Node (DomRenderHookT t m)) a }
264 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
262 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
265#if MIN_VERSION_base(4,9,1)
263#if MIN_VERSION_base(4,9,1)
266 , MonadAsyncException
264 , MonadAsyncException
268+data HydrationState = HydrationState
269+ { _hydrationState_previousNode :: Maybe Node
270+ , _hydrationState_failed :: Bool
270{-# INLINABLE localRunner #-}
273{-# INLINABLE localRunner #-}
271localRunner :: (MonadJSM m, Monad m) => HydrationRunnerT t m a -> Maybe Node -> Node -> HydrationRunnerT t m a
274localRunner :: (MonadJSM m, Monad m) => HydrationRunnerT t m a -> Maybe Node -> Node -> HydrationRunnerT t m a
272−localRunner (HydrationRunnerT m) s parent = HydrationRunnerT . lift $ local (\_ -> parent) (evalStateT m s)
275+localRunner (HydrationRunnerT m) s parent = do
276+ s0 <- HydrationRunnerT get
277+ (a, s') <- HydrationRunnerT $ lift $ local (\_ -> parent) $ runStateT m (s0 { _hydrationState_previousNode = s })
278+ traverse_ removeSubsequentNodes $ _hydrationState_previousNode s'
274{-# INLINABLE runHydrationRunnerT #-}
281{-# INLINABLE runHydrationRunnerT #-}
275runHydrationRunnerT
282runHydrationRunnerT
276 :: (MonadRef m, Ref m ~ IORef, Monad m, PerformEvent t m, MonadFix m, MonadReflexCreateTrigger t m, MonadJSM m, MonadJSM (Performable m))
283 :: (MonadRef m, Ref m ~ IORef, Monad m, PerformEvent t m, MonadFix m, MonadReflexCreateTrigger t m, MonadJSM m, MonadJSM (Performable m))
277 => HydrationRunnerT t m a -> Maybe Node -> Node -> Chan [DSum (EventTriggerRef t) TriggerInvocation] -> m a
284 => HydrationRunnerT t m a -> Maybe Node -> Node -> Chan [DSum (EventTriggerRef t) TriggerInvocation] -> m a
278−runHydrationRunnerT (HydrationRunnerT m) s parent = runDomRenderHookT (runReaderT (evalStateT m s) parent)
285+runHydrationRunnerT (HydrationRunnerT m) s parent events = flip runDomRenderHookT events $ flip runReaderT parent $ do
286+ (a, s') <- runStateT m (HydrationState s False)
287+ traverse_ removeSubsequentNodes $ _hydrationState_previousNode s'
288+ when (_hydrationState_failed s') $ liftIO $ putStrLn "reflex-dom warning: hydration failed: the DOM was not as expected at switchover time. This may be due to invalid HTML which the browser has altered upon parsing, some external JS altering the DOM, or the page being served from an outdated cache."
280instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (HydrationRunnerT t m) where
291instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (HydrationRunnerT t m) where
281 {-# INLINABLE newEventWithTrigger #-}
292 {-# INLINABLE newEventWithTrigger #-}
@@ -305,7 +316,7 @@ addHydrationStepWithSetup setup f = getHydrationMode >>= \case
305 HydrationMode_Immediate -> pure ()
316 HydrationMode_Immediate -> pure ()
306 HydrationMode_Hydrating -> do
317 HydrationMode_Hydrating -> do
307 switchover <- HydrationDomBuilderT $ asks _hydrationDomBuilderEnv_switchover
318 switchover <- HydrationDomBuilderT $ asks _hydrationDomBuilderEnv_switchover
308− (s, _) <- lift $ runWithReplace setup $ return () <$ switchover -- 4% more memory than above
319+ (s, _) <- lift $ runWithReplace setup $ return () <$ switchover
309 addHydrationStep (f s)
320 addHydrationStep (f s)
311-- | Add a hydration step
322-- | Add a hydration step
@@ -409,11 +420,11 @@ data HydrationMode
410{-# INLINABLE getPreviousNode #-}
421{-# INLINABLE getPreviousNode #-}
411getPreviousNode :: Monad m => HydrationRunnerT t m (Maybe DOM.Node)
422getPreviousNode :: Monad m => HydrationRunnerT t m (Maybe DOM.Node)
412−getPreviousNode = HydrationRunnerT get
423+getPreviousNode = HydrationRunnerT $ gets _hydrationState_previousNode
414{-# INLINABLE setPreviousNode #-}
425{-# INLINABLE setPreviousNode #-}
415setPreviousNode :: Monad m => Maybe DOM.Node -> HydrationRunnerT t m ()
426setPreviousNode :: Monad m => Maybe DOM.Node -> HydrationRunnerT t m ()
416−setPreviousNode = HydrationRunnerT . put
427+setPreviousNode n = HydrationRunnerT $ modify' (\hs -> hs { _hydrationState_previousNode = n })
418{-# INLINABLE askUnreadyChildren #-}
429{-# INLINABLE askUnreadyChildren #-}
419askUnreadyChildren :: Monad m => HydrationDomBuilderT s t m (IORef Word)
430askUnreadyChildren :: Monad m => HydrationDomBuilderT s t m (IORef Word)
@@ -427,35 +438,11 @@ askCommitAction = HydrationDomBuilderT $ asks _hydrationDomBuilderEnv_commitActi
427getHydrationMode :: MonadIO m => HydrationDomBuilderT s t m HydrationMode
438getHydrationMode :: MonadIO m => HydrationDomBuilderT s t m HydrationMode
428getHydrationMode = liftIO . readIORef =<< HydrationDomBuilderT (asks _hydrationDomBuilderEnv_hydrationMode)
439getHydrationMode = liftIO . readIORef =<< HydrationDomBuilderT (asks _hydrationDomBuilderEnv_hydrationMode)
430−{-# INLINE makeNodeInternal #-}
441+-- | Remove all nodes after given node
431−makeNodeInternal
442+removeSubsequentNodes :: (MonadJSM m, IsNode n) => n -> m ()
432− :: forall node s t m. (MonadJSM m, IsNode node, Typeable node)
443+removeSubsequentNodes n = liftJSM $ do
433− => HydrationDomBuilderT s t m node -> HydrationDomBuilderT s t m node
444+ f <- eval ("(function(n) { while (n.nextSibling) { (n.parentNode).removeChild(n.nextSibling); }; })" :: Text)
434−makeNodeInternal mkNode = do
445+ void $ call f f [n]
439−{-# INLINE hydrateNode #-}
440−-- | This function expects the existing DOM at the current hydration node to be
441−-- correct. It skips any nodes that are the wrong type, or that fail the
442−-- @check@. The previous node reference is also updated.
444− :: (MonadJSM m, IsNode node, Typeable node)
445− => (node -> HydrationRunnerT t m Bool) -> (DOM.JSVal -> node) -> HydrationRunnerT t m node
446−hydrateNode check constructor = do
448− lastHydrationNode <- getPreviousNode
449− let go mLastNode = do
450− node <- maybe (Node.getFirstChildUnchecked parent) Node.getNextSiblingUnchecked mLastNode
451− DOM.castTo constructor node >>= \case
452− Nothing -> go (Just node)
453− Just tn -> check tn >>= \case
455− False -> go (Just node)
456− n <- go lastHydrationNode
457− setPreviousNode $ Just $ toNode n
460-- | s and e must both be children of the same node and s must precede e;
447-- | s and e must both be children of the same node and s must precede e;
461-- all nodes between s and e will be removed, but s and e will not be removed
448-- all nodes between s and e will be removed, but s and e will not be removed
@@ -671,6 +658,16 @@ instance er ~ EventResult => Default (GhcjsEventSpec er) where
671 runReaderT (defaultDomEventHandler e en) evt
658 runReaderT (defaultDomEventHandler e en) evt
661+makeElement :: MonadJSM m => Document -> Text -> ElementConfig er t s -> m DOM.Element
662+makeElement doc elementTag cfg = do
663+ e <- uncheckedCastTo DOM.Element <$> case cfg ^. namespace of
664+ Nothing -> createElement doc elementTag
665+ Just ens -> createElementNS doc (Just ens) elementTag
666+ iforM_ (cfg ^. initialAttributes) $ \(AttributeName mAttrNamespace n) v -> case mAttrNamespace of
667+ Nothing -> setAttribute e n v
668+ Just ans -> setAttributeNS e (Just ans) n v
674{-# INLINE elementImmediate #-}
671{-# INLINE elementImmediate #-}
675elementImmediate
672elementImmediate
676 :: ( RawDocument (DomBuilderSpace (HydrationDomBuilderT s t m)) ~ Document, EventSpec s ~ GhcjsEventSpec
673 :: ( RawDocument (DomBuilderSpace (HydrationDomBuilderT s t m)) ~ Document, EventSpec s ~ GhcjsEventSpec
@@ -683,14 +680,9 @@ elementImmediate elementTag cfg child = do
683 doc <- askDocument
680 doc <- askDocument
684 ctx <- askJSM
681 ctx <- askJSM
685 events <- askEvents
682 events <- askEvents
686− e <- makeNodeInternal . liftJSM $ do
683+ parent <- getParent
687− e <- uncheckedCastTo DOM.Element <$> case cfg ^. namespace of
684+ e <- makeElement doc elementTag cfg
688− Nothing -> createElement doc elementTag
685+ appendChild_ parent e
689− Just ens -> createElementNS doc (Just ens) elementTag
690− iforM_ (cfg ^. initialAttributes) $ \(AttributeName mAttrNamespace n) v -> case mAttrNamespace of
691− Nothing -> setAttribute e n v
692− Just ans -> setAttributeNS e (Just ans) n v
694 -- Run the child builder with updated parent and previous sibling references
686 -- Run the child builder with updated parent and previous sibling references
695 result <- localEnv (\env -> env { _hydrationDomBuilderEnv_parent = Left $ toNode e }) child
687 result <- localEnv (\env -> env { _hydrationDomBuilderEnv_parent = Left $ toNode e }) child
696 let rawCfg = extractRawElementConfig cfg
688 let rawCfg = extractRawElementConfig cfg
@@ -758,16 +750,16 @@ hydrateElement
758 -> ElementConfig er t HydrationDomSpace
750 -> ElementConfig er t HydrationDomSpace
759 -> HydrationDomBuilderT HydrationDomSpace t m a
751 -> HydrationDomBuilderT HydrationDomSpace t m a
760 -> HydrationDomBuilderT HydrationDomSpace t m ((Element er HydrationDomSpace t, a), IORef DOM.Element)
752 -> HydrationDomBuilderT HydrationDomSpace t m ((Element er HydrationDomSpace t, a), IORef DOM.Element)
761−hydrateElement _elementTag cfg child = do
753+hydrateElement elementTag cfg child = do
762 ctx <- askJSM
754 ctx <- askJSM
763 events <- askEvents
755 events <- askEvents
764 -- Schedule everything for after postBuild, except for getting the result itself
756 -- Schedule everything for after postBuild, except for getting the result itself
765− parent <- liftIO $ newIORef $ error "Parent not yet initialized"
757+ parentRef <- liftIO $ newIORef $ error "Parent not yet initialized"
766 e' <- liftIO $ newIORef $ error "hydrateElement: Element not yet initialized"
758 e' <- liftIO $ newIORef $ error "hydrateElement: Element not yet initialized"
767 env <- HydrationDomBuilderT ask
759 env <- HydrationDomBuilderT ask
768 childDelayedRef <- liftIO $ newIORef $ pure ()
760 childDelayedRef <- liftIO $ newIORef $ pure ()
769 let env' = env
761 let env' = env
770− { _hydrationDomBuilderEnv_parent = Right parent
762+ { _hydrationDomBuilderEnv_parent = Right parentRef
771 , _hydrationDomBuilderEnv_delayed = childDelayedRef
763 , _hydrationDomBuilderEnv_delayed = childDelayedRef
773 result <- HydrationDomBuilderT $ lift $ runReaderT (unHydrationDomBuilderT child) env'
765 result <- HydrationDomBuilderT $ lift $ runReaderT (unHydrationDomBuilderT child) env'
@@ -779,10 +771,35 @@ hydrateElement _elementTag cfg child = do
779 Just ns -> hasAttributeNS e (Just ns) ssrAttr <* removeAttributeNS e (Just ns) ssrAttr
771 Just ns -> hasAttributeNS e (Just ns) ssrAttr <* removeAttributeNS e (Just ns) ssrAttr
780 childDom <- liftIO $ readIORef childDelayedRef
772 childDom <- liftIO $ readIORef childDelayedRef
781 let rawCfg = extractRawElementConfig cfg
773 let rawCfg = extractRawElementConfig cfg
782 addHydrationStep $ do
775 addHydrationStep $ do
777+ lastHydrationNode <- getPreviousNode
778+ let go mLastNode = maybe (Node.getFirstChild parent) Node.getNextSibling mLastNode >>= \case
779+ Nothing -> do -- ran out of nodes, create the element
780+ HydrationRunnerT $ modify' $ \s -> s { _hydrationState_failed = True }
781+ e <- makeElement doc elementTag cfg
782+ insertAfterPreviousNode e
784+ Just node -> DOM.castTo DOM.Element node >>= \case
785+ Nothing -> go (Just node) -- this node is not an element, skip
783− e <- hydrateNode hasSSRAttribute DOM.Element
786+ Just e -> hasSSRAttribute e >>= \case
787+ False -> go (Just node) -- this element was not added by the static renderer, skip
789+ t <- Element.getTagName e
790+ -- TODO: check attributes?
791+ if T.toCaseFold elementTag == T.toCaseFold t
793+ -- we came to some other statically rendered element, so something has gone wrong
795+ HydrationRunnerT $ modify' $ \s -> s { _hydrationState_failed = True }
796+ n <- makeElement doc elementTag cfg
797+ insertAfterPreviousNode n
799+ e <- go lastHydrationNode
800+ setPreviousNode $ Just $ toNode e
784 -- Update the parent node used by the children
801 -- Update the parent node used by the children
785− liftIO $ writeIORef parent $ toNode e
802+ liftIO $ writeIORef parentRef $ toNode e
786 liftIO $ writeIORef e' e
803 liftIO $ writeIORef e' e
787 -- Setup events, store the result so we can wait on it later
804 -- Setup events, store the result so we can wait on it later
788 refs <- wrap events e rawCfg
805 refs <- wrap events e rawCfg
@@ -1125,7 +1142,10 @@ textNodeImmediate
1125 :: (RawDocument (DomBuilderSpace (HydrationDomBuilderT s t m)) ~ Document, MonadJSM m, Reflex t, MonadFix m)
1142 :: (RawDocument (DomBuilderSpace (HydrationDomBuilderT s t m)) ~ Document, MonadJSM m, Reflex t, MonadFix m)
1126 => TextNodeConfig t -> HydrationDomBuilderT s t m DOM.Text
1143 => TextNodeConfig t -> HydrationDomBuilderT s t m DOM.Text
1127textNodeImmediate (TextNodeConfig !t mSetContents) = do
1144textNodeImmediate (TextNodeConfig !t mSetContents) = do
1128− n <- makeNodeInternal (askDocument >>= \doc -> createTextNode doc t)
1147+ n <- createTextNode doc t
1129 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
1149 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
@@ -1144,10 +1164,11 @@ textNodeInternal
1144 :: (Adjustable t m, MonadHold t m, MonadJSM m, MonadFix m, Reflex t)
1164 :: (Adjustable t m, MonadHold t m, MonadJSM m, MonadFix m, Reflex t)
1145 => TextNodeConfig t -> HydrationDomBuilderT HydrationDomSpace t m (TextNode HydrationDomSpace t)
1165 => TextNodeConfig t -> HydrationDomBuilderT HydrationDomSpace t m (TextNode HydrationDomSpace t)
1146textNodeInternal tc@(TextNodeConfig !t mSetContents) = do
1166textNodeInternal tc@(TextNodeConfig !t mSetContents) = do
1147 getHydrationMode >>= \case
1168 getHydrationMode >>= \case
1148 HydrationMode_Immediate -> void $ textNodeImmediate tc
1169 HydrationMode_Immediate -> void $ textNodeImmediate tc
1149 HydrationMode_Hydrating -> addHydrationStepWithSetup (maybe (pure $ pure t) (hold t) mSetContents) $ \currentText -> do
1170 HydrationMode_Hydrating -> addHydrationStepWithSetup (maybe (pure $ pure t) (hold t) mSetContents) $ \currentText -> do
1150− n <- hydrateTextNode =<< sample currentText
1171+ n <- hydrateTextNode doc =<< sample currentText
1151 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
1172 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
1152 pure $ TextNode ()
1173 pure $ TextNode ()
@@ -1161,20 +1182,22 @@ textNodeInternal tc@(TextNodeConfig !t mSetContents) = do
1161-- expect. We also have a special case for empty text nodes - we always create
1182-- expect. We also have a special case for empty text nodes - we always create
1162-- the and add them after the previous node reference.
1183-- the and add them after the previous node reference.
1163{-# INLINE hydrateTextNode #-}
1184{-# INLINE hydrateTextNode #-}
1164−hydrateTextNode :: MonadJSM m => Text -> HydrationRunnerT t m DOM.Text
1185+hydrateTextNode :: MonadJSM m => Document -> Text -> HydrationRunnerT t m DOM.Text
1165−hydrateTextNode t@"" = do
1186+hydrateTextNode doc t@"" = do
1166− doc <- Node.getOwnerDocumentUnchecked =<< askParent
1167 tn <- createTextNode doc t
1187 tn <- createTextNode doc t
1168 insertAfterPreviousNode tn
1188 insertAfterPreviousNode tn
1170−hydrateTextNode t = do
1190+hydrateTextNode doc t = do
1171 n <- join $ go <$> askParent <*> getPreviousNode
1191 n <- join $ go <$> askParent <*> getPreviousNode
1172 setPreviousNode $ Just $ toNode n
1192 setPreviousNode $ Just $ toNode n
1173 return n
1193 return n
1175− go parent mLastNode = do
1195+ go parent mLastNode = maybe (Node.getFirstChild parent) Node.getNextSibling mLastNode >>= \case
1176− node <- maybe (Node.getFirstChildUnchecked parent) Node.getNextSiblingUnchecked mLastNode
1196+ Nothing -> do
1197+ n <- createTextNode doc t
1198+ insertAfterPreviousNode n
1177− DOM.castTo DOM.Text node >>= \case
1200+ Just node -> DOM.castTo DOM.Text node >>= \case
1178 Nothing -> go parent $ Just node
1201 Nothing -> go parent $ Just node
1179 Just originalNode -> do
1202 Just originalNode -> do
1180 originalText <- Node.getTextContentUnchecked originalNode
1203 originalText <- Node.getTextContentUnchecked originalNode
@@ -1185,29 +1208,59 @@ hydrateTextNode t = do
1185 -- required text and a subsequent sibling node containing the rest of the text.
1208 -- required text and a subsequent sibling node containing the rest of the text.
1186 DOM.splitText_ originalNode $ fromIntegral $ T.length t
1209 DOM.splitText_ originalNode $ fromIntegral $ T.length t
1187 return originalNode
1210 return originalNode
1188− Nothing -> go parent $ Just $ toNode originalNode
1211+ Nothing -> do
1212+ n <- createTextNode doc t
1213+ insertAfterPreviousNode n
1190{-# INLINE commentNodeImmediate #-}
1216{-# INLINE commentNodeImmediate #-}
1191commentNodeImmediate
1217commentNodeImmediate
1192 :: (RawDocument (DomBuilderSpace (HydrationDomBuilderT s t m)) ~ Document, MonadJSM m, Reflex t, MonadFix m)
1218 :: (RawDocument (DomBuilderSpace (HydrationDomBuilderT s t m)) ~ Document, MonadJSM m, Reflex t, MonadFix m)
1193 => CommentNodeConfig t -> HydrationDomBuilderT s t m DOM.Comment
1219 => CommentNodeConfig t -> HydrationDomBuilderT s t m DOM.Comment
1194commentNodeImmediate (CommentNodeConfig !t mSetContents) = do
1220commentNodeImmediate (CommentNodeConfig !t mSetContents) = do
1195− n <- makeNodeInternal (askDocument >>= \doc -> createComment doc t)
1223+ n <- createComment doc t
1196 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
1225 mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
1199{-# INLINE commentNodeInternal #-}
1228{-# INLINE commentNodeInternal #-}
1200commentNodeInternal
1229commentNodeInternal
1201− :: (Ref m ~ IORef, MonadRef m, PerformEvent t m, MonadReflexCreateTrigger t m, MonadJSM (Performable m), MonadJSM m, MonadFix m, Reflex t, Adjustable t m)
1230+ :: (Ref m ~ IORef, MonadRef m, PerformEvent t m, MonadReflexCreateTrigger t m, MonadJSM (Performable m), MonadJSM m, MonadFix m, Reflex t, Adjustable t m, MonadHold t m, MonadSample t m)
1202 => CommentNodeConfig t -> HydrationDomBuilderT HydrationDomSpace t m (CommentNode HydrationDomSpace t)
1231 => CommentNodeConfig t -> HydrationDomBuilderT HydrationDomSpace t m (CommentNode HydrationDomSpace t)
1203−commentNodeInternal tc@(CommentNodeConfig _t mSetContents) = do
1232+commentNodeInternal tc@(CommentNodeConfig t0 mSetContents) = do
1204 getHydrationMode >>= \case
1234 getHydrationMode >>= \case
1205 HydrationMode_Immediate -> void $ commentNodeInternal tc
1235 HydrationMode_Immediate -> void $ commentNodeInternal tc
1206− HydrationMode_Hydrating -> addHydrationStep $ do
1236+ HydrationMode_Hydrating -> addHydrationStepWithSetup (maybe (pure $ pure t0) (hold t0) mSetContents) $ \bt -> do
1207− n <- hydrateNode (const $ pure True) DOM.Comment
1237+ t <- sample bt
1208− mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
1238+ void $ hydrateComment doc t mSetContents
1209 pure $ CommentNode ()
1239 pure $ CommentNode ()
1241+{-# INLINE hydrateComment #-}
1242+hydrateComment :: (MonadJSM m, Reflex t, MonadFix m) => Document -> Text -> Maybe (Event t Text) -> HydrationRunnerT t m DOM.Comment
1243+hydrateComment doc t mSetContents = do
1244+ parent <- askParent
1245+ let go mLastNode = maybe (Node.getFirstChild parent) Node.getNextSibling mLastNode >>= \case
1247+ c <- createComment doc t
1248+ insertAfterPreviousNode c
1250+ Just node -> DOM.castTo DOM.Comment node >>= \case
1251+ Nothing -> go (Just node)
1253+ t' <- Node.getTextContentUnchecked c
1257+ c' <- createComment doc t
1258+ insertAfterPreviousNode c'
1260+ n <- go =<< getPreviousNode
1261+ setPreviousNode $ Just $ toNode n
1262+ mapM_ (requestDomAction_ . fmap (setNodeValue n . Just)) mSetContents
1212-- | We leave markers in the static builder as comments, and rip these comments
1265-- | We leave markers in the static builder as comments, and rip these comments
1213-- out at hydration time, replacing them with empty text nodes.
1266-- out at hydration time, replacing them with empty text nodes.