mdoc
mdoc copied to clipboard
PostModifier for ScalaJS
For a dom dsl, I would like to show, which html code will be generated:
div("hello")
using a PostModifier, I want to render it into a temporary html element and extract the innerHTML of that element.
But as far as I understand, the PostModifier runs only on the JVM, so this is currently not possible.
Any ideas?
Or is it possible to wrap the code with other code before execution in the browser? That would also be an option for me.
Thank you for reporting! The Scala.js support is implemented with an entirely separate compile/evaluate pipeline making it difficult to support JS/JVM with a unified API. A JS PostModifier
would need to evaluate inside the browser every time HTML gets rendered while a JVM PostModifier
evaluates once before the output markdown is generated.
Can you elaborate on your use-case? What would you concretely like to write in the markdown source and what should concrete render in the output?
Or is it possible to wrap the code with other code before execution in the browser? That would also be an option for me.
Is it an option to manually wrap the code with a function call? For example, if you have an innerHTML
helper method that performs the logic for you:
```scala mdoc:js
innerHTML {
div("hello")
}
```
I started to work on the outwatch documentation again and want to automatically generate the corresponding html code for static content:
https://github.com/outwatch/outwatch#concatenating-strings
It doesn't matter much if it's evaluated at build-time with node or in the browser
(I'm on mobile now and can add some code later)
Yes, wrapping with a visible shared function call would have been my next feasible workaround.
Have you considered writing an invisible helper method called “div” that implements your desired functionality?
Have you considered writing an invisible helper method called “div” that implements your desired functionality?
Very interesting idea!
This is what I tried:
```scala mdoc:js:shared:invisible
def div(modifiers: VDomModifier*):String = {
val vnode = dsl.div(modifiers)
val domNode = org.scalajs.dom.document.createElement("div")
OutWatch.renderInto[IO](domNode, vnode).unsafeRunSync()
domNode.innerHTML
}
```
```scala mdoc:js
div("Hello ", "World!", color := "tomato")
```
But then I realized that return values are not printed in mdoc:js. I was only able to write the result into the current node:
```scala mdoc:js
node.innerText = div("Hello ", "World!", color := "tomato")
```
I cannot put the node.innerText = ...
inside the invisible function, since it would be a different node
, right?
edit: I just tried. when :shared
is used, there is no more node
variable in scope.
Originally, I imagined having a custom modifier vnode
like this:
```scala mdoc:js:vnode
div("Hello ", "World!", color := "tomato")
```
Which adds a comment, containing the innerHTML
representation:
```scala
div("Hello ", "World!", color := "tomato")
// <div style="color: tomato;">Hello World!</div>
```
One thing we could do is to make the node variable available through a global var or an implicit. Then you could update innerHTML. Do you think that would resolve your usecase?
Yes, I think either would solve my case. An implicit sounds more elegant to me. Would there be any downsides?
Sounds good. I opened #539 to add a new implicit val nodeImplicit: HTMLElementImplicit
in scope for every code fence that you can pass around to invisible helper methods.
I think the use case would be to use the output of mdoc:js
as a snapshot test, i.e., the test should be considered failed if the generated output changes.
For reference, this is what I ended up with in the outwatch docs:
- https://github.com/outwatch/outwatch/blob/master/docs/readme.md?plain=1#L152
- rendered html preview: https://outwatch.github.io/docs/readme.html#concatenating-strings