Description
Initial checklist
- I read the support docs
- I read the contributing guide
- I agree to follow the code of conduct
- I searched issues and couldn’t find anything (or linked relevant results below)
Problem
Really like the unified ecosystem! I'm currently working on a MutationObserver-based project where those DOM mutations need to be serialized and deserialized. To reconstruct a DOM mutation (e.g., changing the value of a text node), I need to keep track of all DOM nodes that have been deserialized by hast-util-to-dom
and connect those to the appropriate hast node and vice-versa. Overview for context:
Record (not relevant for this feature request)
- Take a snapshot of the whole DOM and serialize it to a virtual DOM (hast)
- MutationObserver detects a text change: listener contains the target DOM node, and with the help of a custom node store, everything gets tracked and serialized
Replay
- Deserialize the entire DOM snapshot with
hast-util-to-dom
- Identify the target DOM node and apply the text change: Unfortunately, this step is not possible with the current
hast-util-to-dom
functionality because there's no reliable way to map a generated DOM node to a hast node.
Solution
By adding a visitor function, it's possible to inject custom logic to the tree traversal to accomplish the DOM<>VDOM mapping needed in the above example.
options.onNode: (hastNode: HastNode, domNode: Node) => void
Usage example:
function deserialize(
// A custom store to track all DOM<>VDOM associations
store: NodeStore,
// The virtual DOM to deserialize
vdom: HastNode,
): Node {
return toDom(vdom, {
// The new visitor function that receives both nodes as arguments
onNode: (hastNode: HastNode, domNode: Node) => {
// Custom code here...
store.addNode(domNode, hastNode);
},
});
}
Although not required for my use case, such a visitor function could also be used in the future similarly to unist-util-visit-parents#visitor to transform nodes while the tree is being traversed instead of looping multiple times through the entire tree.
Alternatives
There's currently no viable alternative. What I've considered for my specific use case:
- Add a
data-node-id
attribute to the DOM node, which gets included in HastElement.properties. However, this is only possible for (Hast)Element nodes. Text and many other DOM nodes don't have attributes. - A selector-based approach to identify the nodes (e.g., with XPath) is not an option because DOM nodes could have been added or removed, breaking the selectors.
Happy to provide a PR with the addition!