rstudio / htmltools

Tools for HTML generation and output
https://rstudio.github.io/htmltools/
214 stars 67 forks source link

`runtime: shiny` in Rmarkdown doesn't know how to handle htmlDependency() #375

Closed daattali closed 1 year ago

daattali commented 1 year ago

If an htmltools::htmlDependency() gets added to the UI of a regular shiny app, shiny knows how to treat it as a dependency and add the scripts/css files to the head.

If it gets added to a shiny-powered Rmarkdown, it gets converted to text.

Example:

The following shiny app adds the fontawesome dependencies, and therefore the icon shows up

library(shiny)

ui <- fluidPage(
  fontawesome::fa_html_dependency(),
  tags$i(class = "fas fa-pencil")
)

server <- function(input, output, session) {}

shinyApp(ui, server)

The following Rmarkdown treats the dependency as text

---
runtime: shiny
---

```{r}
fontawesome::fa_html_dependency()
tags$i(class = "fas fa-pencil")

The rmarkdown version does work if I wrap the dependency in an empty `attachDependencies()`:

```r
htmltools::attachDependencies(HTML(''), fontawesome::fa_html_dependency())

This is technically not a bug because the documentation of htmlDependency() does say that these objects are meant to go inside attachDependencies(), but in practice because it works in regular shiny apps I would expect it to also work in Rmd.

daattali commented 1 year ago

I see in the docs for attachDependencies:

As of htmltools 0.3.4, HTML dependencies can be attached without using attachDependencies. Instead, they can be added inline, like a child object of a tag or tagList().

So I suppose that's why it wasn't working, because it wasn't inside a tag. I do see that

tagList(fontawesome::fa_html_dependency())

Makes it work in an Rmd. But that's a little bit ugly - it that how I'm expected to add a dependency in Rmd?

gadenbuie commented 1 year ago

it that how I'm expected to add a dependency in Rmd?

That's right, you can either add the dependency within a bare tagList() or you can include the dependency in other tags if you have a function that emits HTML.

I'm not sure about the reason for this behavior, but I do think this is how dependencies have worked in rmarkdown for a long time. The rmarkdown team might have more insight and my understanding is that either rmarkdown or knitr would be in a position to change this behavior if desired.

daattali commented 1 year ago

My (completely wild and potentially wrong) guess is that this wasn't a purposeful decision, rather rmarkdown simply never took care of the care of handling a htmlDependency(). I'll wait for someone from that team to chime in and let us know whether that's true or not.

Do you know who to tag from that team?

gadenbuie commented 1 year ago

The root issue is that there isn't a knit_print.html_dependency method, which could be (or should be) provided by htmltools. I'm going to move this issue over to htmltools.

cderv commented 1 year ago

The root issue is that there isn't a knit_print.html_dependency method, which could be (or should be) provided by htmltools.

👋 R Markdown team here 😉

@gadenbuie had it right. Yes this is how the information about dependency is passed to knitr usually. This is explained in the vignette about custom print method : https://cran.r-project.org/web/packages/knitr/vignettes/knit_print.html#metadata

So dependencies needs to be wrapped into tagList() because htmltools::knit_print.shiny.tag.list takes care of doing what is need to pass the meta to knitr using knitr::asis_output()

Those type of things are supposed to be handled in associated package and not in knitr directly. htmltools seems the right place for that.