Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
da683b9
Initial commit of domRef removal on vcomp
dmjio Dec 10, 2025
59bab56
lint
dmjio Dec 10, 2025
b435cb4
Address watch warnings
dmjio Dec 10, 2025
3903d9f
Regen JS
dmjio Dec 10, 2025
bf174cb
Drop namespace, tag from `VComp`. Add mount combos.
dmjio Dec 10, 2025
a48d2db
Update `mount()` and `unmount()` layers.
dmjio Dec 10, 2025
b969def
Adjust action type
dmjio Dec 10, 2025
a558938
Update test
dmjio Dec 10, 2025
c7e87f7
remove children field
dmjio Dec 11, 2025
ef8b1cc
return null in drill
dmjio Dec 11, 2025
f361609
use parent field
dmjio Dec 11, 2025
ac356d6
drop redundant comment
dmjio Dec 11, 2025
aa13eb6
Merge branch 'master' into drop-vcomp-domref
dmjio Dec 11, 2025
e65d30c
Check if drilled successfully
dmjio Dec 11, 2025
3bc8750
Check if `child` exists.
dmjio Dec 11, 2025
94c0700
Add `drilled` check.
dmjio Dec 11, 2025
bbb85dc
Naming conventions
dmjio Dec 11, 2025
1d84f1b
Call `setVCompRef`
dmjio Dec 11, 2025
684c11c
Add `VText` case in `createElement`
dmjio Dec 11, 2025
172b886
Regen JS
dmjio Dec 11, 2025
2019d8d
Check .nextSibling existence
dmjio Dec 11, 2025
452e860
Check `.nextSibling`
dmjio Dec 11, 2025
00c8bca
Flip mounting combinators
dmjio Dec 11, 2025
7acc77c
Update `nextSibling`
dmjio Dec 11, 2025
8f97f89
Use onMounted callback
dmjio Dec 11, 2025
0d97ea8
Get nextSibling first.
dmjio Dec 11, 2025
5356da3
Make fields optional
dmjio Dec 11, 2025
2bfc6f5
Regen JS
dmjio Dec 11, 2025
ae9b381
Add comments
dmjio Dec 11, 2025
81b5d3c
Handle recursive component mounting.
dmjio Dec 12, 2025
6aa5428
Simplify not found case in syncChildren.
dmjio Dec 12, 2025
8a0385c
Handle DOM operations in `mount()`.
dmjio Dec 12, 2025
608a928
Add getDOMRef helper
dmjio Dec 12, 2025
8cb8b47
Add recursive vcomp mounting delegation test case
dmjio Dec 12, 2025
c195fc1
Regen JS
dmjio Dec 12, 2025
58f49e8
Add more DOMRef
dmjio Dec 12, 2025
0b4b30e
Revise comments for 'mount_' function
dmjio Dec 12, 2025
18c68bd
Regen JS
dmjio Dec 12, 2025
8d1e2ac
Shuffle argument order
dmjio Dec 12, 2025
416e873
return drill
dmjio Dec 12, 2025
645aee9
Update recursive component mounting
dmjio Dec 12, 2025
7b3c86e
Update test
dmjio Dec 12, 2025
c39883f
Drop break;
dmjio Dec 12, 2025
10cb58e
sp
dmjio Dec 12, 2025
0e8ff77
add test cases
dmjio Dec 12, 2025
618fdfc
add proper test cases
dmjio Dec 12, 2025
fb6b270
Update hydrate, add tests
dmjio Dec 12, 2025
5553503
Merge branch 'master' into drop-vcomp-domref
dmjio Dec 12, 2025
a31c335
Merge branch 'master' into drop-vcomp-domref
dmjio Dec 12, 2025
3ecf145
lint
dmjio Dec 12, 2025
b667841
Add `context-dom.spec.ts`. (#1279)
dmjio Dec 12, 2025
9b29691
Update nextSibling tests.
dmjio Dec 13, 2025
dcd2139
Merge branch 'master' into drop-vcomp-domref
dmjio Dec 13, 2025
e174a02
Merge branch 'master' into drop-vcomp-domref
dmjio Dec 14, 2025
c486f99
Export mount combinators top-level.
dmjio Dec 14, 2025
29922ea
swap args
dmjio Dec 14, 2025
b5306e0
Adjust swapDOMRef test
dmjio Dec 14, 2025
da6341f
regen js
dmjio Dec 14, 2025
c60e844
Drop dead code
dmjio Dec 14, 2025
9cc8b70
Remove dead code
dmjio Dec 14, 2025
ab92b58
Drop firstChild
dmjio Dec 14, 2025
9ba5ef0
Add assertion to hydrate.spec.ts.
dmjio Dec 15, 2025
e030e70
haddock nits
dmjio Dec 15, 2025
433f0b9
Drop setVCompRef
dmjio Dec 15, 2025
c85f78f
watch-test clean
dmjio Dec 15, 2025
66fcfd6
Don't prerender "key"
dmjio Dec 15, 2025
c5326a1
Update hydration spec
dmjio Dec 15, 2025
98d0a77
Expect true in hydrate spec
dmjio Dec 15, 2025
6b816ad
Drop dead test
dmjio Dec 15, 2025
924790c
Update hydration and spec.
dmjio Dec 15, 2025
74cbbab
Update test to hydrate on node
dmjio Dec 15, 2025
381d09d
Don't recurse on vcomp in hydrate.ts
dmjio Dec 15, 2025
557eb85
Update hydrate.ts
dmjio Dec 15, 2025
4aaf7d9
regen js
dmjio Dec 15, 2025
46eca48
Make event delegation global
dmjio Dec 15, 2025
8fbf231
lint
dmjio Dec 15, 2025
05cdefc
Revert `getParentComponentId`, regen JS, use mock parent object
dmjio Dec 16, 2025
783f079
Thread `ComponentId` as parent `ComponentId` through `initialize`.
dmjio Dec 16, 2025
a675404
Only set componentId on vcomp
dmjio Dec 16, 2025
aa6019d
drop `liftJSM`
dmjio Dec 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
329 changes: 182 additions & 147 deletions js/miso.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion js/miso.prod.js

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/Miso.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ module Miso
, startComponent
, component
, (+>)
, mount
, mount_
-- ** Sink
, withSink
, Sink
Expand Down Expand Up @@ -132,7 +134,7 @@ miso :: Eq model => (URI -> App model action) -> JSM ()
miso f = withJS $ do
vcomp <- f <$> getURI
body <- FFI.getBody
initialize Hydrate isRoot vcomp (pure body)
initialize rootComponentId Hydrate isRoot vcomp (pure body)
-----------------------------------------------------------------------------
-- | Synonym for 'startComponent'.
--
Expand Down Expand Up @@ -181,8 +183,8 @@ initComponent
=> Component parent model action
-> JSM (ComponentState model action)
initComponent vcomp@Component {..} = do
mount <- mountElement (getMountPoint mountPoint)
initialize Draw isRoot vcomp (pure mount)
root <- mountElement (getMountPoint mountPoint)
initialize rootComponentId Draw isRoot vcomp (pure root)
----------------------------------------------------------------------------
isRoot :: Bool
isRoot = True
Expand Down
4 changes: 2 additions & 2 deletions src/Miso/Effect.hs
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ type Effect parent model action = RWS (ComponentInfo parent) [Schedule action] m
--
-- All t'IO' is by default asynchronous, use the 'sync' function for synchronous
-- execution. Beware 'sync' can block the render thread for a specific
-- 'Component'.
-- t'Miso.Types.Component'.
--
-- N.B. During 'Miso.Types.Component' unmounting, all effects are evaluated
-- N.B. During t'Miso.Types.Component' unmounting, all effects are evaluated
-- synchronously.
--
-- @since 1.9.0.0
Expand Down
3 changes: 0 additions & 3 deletions src/Miso/FFI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ module Miso.FFI
, toLocaleString
, getSeconds
, getMilliseconds
-- ** 'Miso.Types.Component'
, getParentComponentId
, getComponentId
-- ** DOM Traversal
, nextSibling
, previousSibling
Expand Down
23 changes: 4 additions & 19 deletions src/Miso/FFI/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,6 @@ module Miso.FFI.Internal
-- * Utils
, getMilliseconds
, getSeconds
-- * 'Miso.Types.Component'
, getParentComponentId
, getComponentId
-- * Element
, files
, click
Expand Down Expand Up @@ -393,7 +390,7 @@ eventJSON x y = do
moduleMiso <- jsg "miso"
moduleMiso # "eventJSON" $ [x,y]
-----------------------------------------------------------------------------
-- | Populate the `classList` Set on the virtual DOM.
-- | Populate the 'Miso.Html.Property.classList' Set on the virtual DOM.
populateClass
:: JSVal
-- ^ Node
Expand Down Expand Up @@ -516,13 +513,13 @@ undelegate mountPoint events debug callback ctx = do
--
-- See [hydration](https://en.wikipedia.org/wiki/Hydration_(web_development))
--
hydrate :: Bool -> JSVal -> JSVal -> JSM ()
hydrate logLevel mountPoint vtree = void $ do
hydrate :: Bool -> JSVal -> JSVal -> JSM JSVal
hydrate logLevel mountPoint vtree = do
ll <- toJSVal logLevel
drawingContext <- getDrawingContext
hydrationContext <- getHydrationContext
moduleMiso <- jsg "miso"
void $ moduleMiso # "hydrate" $ [ll, mountPoint, vtree, hydrationContext, drawingContext]
moduleMiso # "hydrate" $ (ll, mountPoint, vtree, hydrationContext, drawingContext)
-----------------------------------------------------------------------------
-- | Fails silently if the element is not found.
--
Expand Down Expand Up @@ -747,18 +744,6 @@ getSeconds date =
fromJSValUnchecked =<< do
date # "getSeconds" $ ([] :: [MisoString])
-----------------------------------------------------------------------------
-- | Climb the tree, get the parent.
getParentComponentId :: JSVal -> JSM (Maybe Int)
getParentComponentId domRef =
fromJSVal =<< do
jsg "miso" # "getParentComponentId" $ [domRef]
-----------------------------------------------------------------------------
-- | Get access to the 'Miso.Effect.ComponentId'
-- N.B. you * must * call this on the DOMRef, otherwise, problems.
-- For use in 'Miso.Event.onMounted', etc.
getComponentId :: JSVal -> JSM Int
getComponentId vtree = fromJSValUnchecked =<< vtree ! "componentId"
-----------------------------------------------------------------------------
-- | Fetch next sibling DOM node
--
-- @since 1.9.0.0
Expand Down
5 changes: 3 additions & 2 deletions src/Miso/Html/Render.hs
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ renderBuilder (VNode ns tag attrs children) = mconcat
, ns == MATHML
]

renderBuilder (VComp ns tag attrs (SomeComponent vcomp)) =
renderBuilder (VNode ns tag attrs vkids)
renderBuilder (VComp _ (SomeComponent vcomp)) =
foldMap renderBuilder vkids
where
#ifdef SSR
vkids = [ unsafeCoerce $ (view vcomp) $ getInitialComponentModel vcomp ]
Expand All @@ -113,6 +113,7 @@ renderAttrs (ClassList classes) =
, fromMisoString (MS.unwords classes)
, stringUtf8 "\""
]
renderAttrs (Property "key" _) = mempty
renderAttrs (Property key value) =
mconcat
[ fromMisoString key
Expand Down
5 changes: 3 additions & 2 deletions src/Miso/Hydrate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import qualified Miso.FFI.Internal as FFI
import Miso.Types
-----------------------------------------------------------------------------
-- | Hydration of a t'VTree'
hydrate :: LogLevel -> DOMRef -> VTree -> JSM ()
hydrate :: LogLevel -> DOMRef -> VTree -> JSM Bool
hydrate loggingLevel domRef vtree = do
jval <- toJSVal vtree
FFI.hydrate (loggingLevel `elem` [DebugHydrate, DebugAll]) domRef jval
fromJSValUnchecked =<<
FFI.hydrate (loggingLevel `elem` [DebugHydrate, DebugAll]) domRef jval
-----------------------------------------------------------------------------
70 changes: 44 additions & 26 deletions src/Miso/Runtime.hs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,15 @@ import Miso.Effect ( ComponentInfo(..), Sub, Sink, Effect, Schedule(..
-- | Helper function to abstract out initialization of t'Miso.Types.Component' between top-level API functions.
initialize
:: (Eq parent, Eq model)
=> Hydrate
=> ComponentId
-> Hydrate
-> Bool
-- ^ Is the root node being rendered?
-> Component parent model action
-> JSM DOMRef
-- ^ Callback function is used for obtaining the t'Miso.Types.Component' 'DOMRef'.
-> JSM (ComponentState model action)
initialize hydrate isRoot Component {..} getComponentMountPoint = do
initialize componentParentId hydrate isRoot Component {..} getComponentMountPoint = do
Waiter {..} <- liftIO waiter
componentActions <- liftIO (newIORef S.empty)
let
Expand All @@ -152,22 +153,25 @@ initialize hydrate isRoot Component {..} getComponentMountPoint = do
#ifdef BENCH
start <- if isRoot then FFI.now else pure 0
#endif
vtree <- buildVTree hydrate componentSink logLevel events (view initializedModel)
vtree <- buildVTree componentId hydrate componentSink logLevel events (view initializedModel)
#ifdef BENCH
end <- if isRoot then FFI.now else pure 0
when isRoot $ FFI.consoleLog $ ms (printf "buildVTree: %.3f ms" (end - start) :: String)
#endif
ref <- liftIO (newIORef vtree)
case hydrate of
Draw -> do
Diff.diff Nothing (Just vtree) componentDOMRef
pure ref
Hydrate -> do
Hydrate.hydrate logLevel componentDOMRef vtree
liftIO (newIORef vtree)
when isRoot $ do
hydrated <- Hydrate.hydrate logLevel componentDOMRef vtree
when (not hydrated) $ do
newTree <- buildVTree componentId Draw componentSink logLevel events (view initializedModel)
liftIO (atomicWriteIORef ref newTree)
Diff.diff Nothing (Just newTree) componentDOMRef
pure ref
componentDOMRef <# ("componentId" :: MisoString) $ componentId
componentParentId <- do
FFI.getParentComponentId componentDOMRef >>= \case
Nothing -> pure rootComponentId
Just parentId -> pure parentId
componentSubThreads <- liftIO (newIORef M.empty)
forM_ subs $ \sub -> do
threadId <- FFI.forkJSM (sub componentSink)
Expand All @@ -185,7 +189,7 @@ initialize hydrate isRoot Component {..} getComponentMountPoint = do
updatedName <- liftIO $ updatedModel `seq` makeStableName updatedModel
isDirty <- liftIO (readTVarIO componentIsDirty)
when ((currentName /= updatedName && currentModel /= updatedModel) || isDirty) $ do
newVTree <- buildVTree Draw componentSink logLevel events (view updatedModel)
newVTree <- buildVTree componentId Draw componentSink logLevel events (view updatedModel)
oldVTree <- liftIO (readIORef componentVTree)
void waitForAnimationFrame
Diff.diff (Just oldVTree) (Just newVTree) componentDOMRef
Expand Down Expand Up @@ -233,14 +237,14 @@ initialize hydrate isRoot Component {..} getComponentMountPoint = do
let vcomp = ComponentState
{ componentNotify = notify
, componentEvents = events
, componentParentId = componentParentId
, ..
}

registerComponent vcomp
if isRoot
then
delegator componentDOMRef componentVTree
events (logLevel `elem` [DebugEvents, DebugAll])
delegator componentDOMRef componentVTree events (logLevel `elem` [DebugEvents, DebugAll])
else
addToDelegatedEvents logLevel events
forM_ initialAction componentSink
Expand Down Expand Up @@ -820,33 +824,47 @@ killSubscribers componentId =
-- process we go between the Haskell heap and the JS heap.
buildVTree
:: Eq model
=> Hydrate
=> ComponentId
-> Hydrate
-> Sink action
-> LogLevel
-> Events
-> View model action
-> JSM VTree
buildVTree hydrate snk logLevel_ events_ = \case
VComp ns tag attrs (SomeComponent app) -> do
buildVTree parentId hydrate snk logLevel_ events_ = \case
VComp attrs (SomeComponent app) -> do
vcomp <- create

mountCallback <- do
FFI.syncCallback2 $ \vcomp continuation -> do
domRef <- vcomp ! ("domRef" :: MisoString)
ComponentState {..} <- initialize hydrate False app (pure domRef)
FFI.syncCallback2 $ \parent_ continuation -> do
ComponentState {..} <- initialize parentId Draw False app (pure parent_)
vtree <- toJSVal =<< liftIO (readIORef componentVTree)
FFI.set "parent" vcomp (Object vtree)
vcompId <- toJSVal componentId
FFI.set "componentId" vcompId (Object domRef)
void $ call continuation global [vcompId, vtree]

unmountCallback <- toJSVal =<< do
FFI.syncCallback1 $ \domRef -> do
componentId <- liftJSM (FFI.getComponentId domRef)
FFI.syncCallback1 $ \vcompId -> do
componentId <- fromJSValUnchecked vcompId
IM.lookup componentId <$> liftIO (readIORef components) >>= \case
Nothing -> pure ()
Just componentState ->
unmount app componentState
vcomp <- createNode "vcomp" ns tag

case hydrate of
Hydrate -> do
-- Mock .domRef for use during hydration
domRef <- toJSVal =<< create
ComponentState {..} <- initialize parentId hydrate False app (pure domRef)
vtree <- toJSVal =<< liftIO (readIORef componentVTree)
FFI.set "parent" vcomp (Object vtree)
vcompId <- toJSVal componentId
FFI.set "componentId" vcompId vcomp
FFI.set "child" vtree vcomp
Draw -> do
FFI.set "child" jsNull vcomp

setAttrs vcomp attrs snk (logLevel app) (events app)
flip (FFI.set "children") vcomp =<< toJSVal ([] :: [MisoString])
flip (FFI.set "mount") vcomp =<< toJSVal mountCallback
FFI.set "unmount" unmountCallback vcomp
FFI.set "eventPropagation" (eventPropagation app) vcomp
Expand All @@ -858,11 +876,11 @@ buildVTree hydrate snk logLevel_ events_ = \case
vchildren <- toJSVal =<< procreate vnode
flip (FFI.set "children") vnode vchildren
flip (FFI.set "type") vnode =<< toJSVal VNodeType
pure $ VTree vnode
pure (VTree vnode)
where
procreate parentVTree = do
kidsViews <- forM kids $ \kid -> do
VTree child <- buildVTree hydrate snk logLevel_ events_ kid
VTree child <- buildVTree parentId hydrate snk logLevel_ events_ kid
FFI.set "parent" parentVTree child
pure child
setNextSibling kidsViews
Expand All @@ -876,7 +894,7 @@ buildVTree hydrate snk logLevel_ events_ = \case
forM_ key $ \k -> FFI.set "key" (ms k) vtree
FFI.set "ns" ("text" :: JSString) vtree
FFI.set "text" t vtree
pure $ VTree vtree
pure (VTree vtree)
-----------------------------------------------------------------------------
-- | @createNode@
-- A helper function for constructing a vtree (used for @vcomp@ and @vnode@)
Expand Down
Loading