Xaringan: Publishing with RStudio Connect

Created on 30 Jan 2018  路  7Comments  路  Source: yihui/xaringan

I'm not sure if this is a remark.js issue, a xaringan.js issue, or an RStudio Connect issue. As this issue only occurs when publishing with RStudio Connect, I can't provide a reproducible example (only 1 slide and 1 image is needed though).

Issue: When publishing to RStudio Connect, images imported from a project folder (e.g., resources/myImage.svg) will not display.

Works --- when knitting to HTML on our RStudio Server Pro (not published), the HTML references the image.
Works --- when manually exporting the HTML and the resources folder to a non-server filesystem (i.e., is served locally), the HTML still references the image and renders correctly.

Extra info
I am including "resource_files: resources" in the YAML header.
The Rstudio GUI confirms that I am including the resources directory when deploying the bundle.
I can't manually access the deployment folder (/var/lib/rstudio-connect/) to confirm the resources folder and image were deployed since I don't have root access at my company.

Most helpful comment

This is the helper function that I use:

library(rsconnect)

deploy_xaringan <- function(doc){
  qualified_doc <- normalizePath(doc, winslash = "/")
  app_files <-  c(
    basename(qualified_doc),
    list.files("images", pattern = ".*.png", full.names = TRUE, recursive = TRUE),
    list.files("images", pattern = ".*.gif", full.names = TRUE, recursive = TRUE),
    list.files("include", pattern = ".*.png", full.names = TRUE, recursive = TRUE),
    list.files("include", pattern = ".*.jpg", full.names = TRUE, recursive = TRUE),
    list.files("theme", pattern = "*.png", full.names = TRUE, recursive = TRUE)
    # list.files("libs", full.names = TRUE, recursive = TRUE)
  )
  res_files <- rmarkdown::find_external_resources(qualified_doc)
  app_files <- c(app_files, res_files$path)
  rsconnect::deployApp(
    appDir = dirname(qualified_doc),
    appPrimaryDoc = basename(qualified_doc),
    appTitle = "Taking TensorFlow into Production ThinkR 2019",
    appFiles = app_files,
    forceUpdate = TRUE,
    server = "colorado.rstudio.com"
  )
}

deploy_xaringan("tensorflow-production-2019-06.html")

All 7 comments

Here's a reproducible example. Knitting the document works just fine. When publishing to connect, the image does not display. The tag looks just fine, but the console outputs "Failed to load resource: the server responded with a status of 404 (Not Found)."

When publishing, the RStudio IDE indicates the image file was included in the bundle.

---
title: "Images on RStudio Connect"
resource_files: resources
output:
  xaringan::moon_reader:
    lib_dir: libs
---

```{r setup, include=FALSE}
options(htmltools.dir.version = FALSE)

First, save Sharingan_triple.svg locally in a resources folder. Then:

```html
![](resources/Sharingan_triple.svg)

Anyone have any thoughts?

I assume most xaringan users don't use RStudio Connect and therefore haven't faced the issue yet and are not eager to reproduce/fix it.

Maybe you would have more luck posting this in the corresponding repo that deals with RStudio connect? (idk which one that is tbh).
Also I have no clue about the magic going on behind Rstudio Connect and hence cannot help in any way here.

The main problem is that xaringan slides are not self-contained by default, and I don't favor or support the self-contained mode well in this package (see ?xaringan::moon_reader). HTML files that are not self-contained are probably tricky for RStudio Connect. I don't use RStudio Connect to publish my slides, and I have never tested it myself.

I'm documenting a workaround here, but will also speak to the RStudio Connect team and the rmarkdown team to identify a proper solution.

The underlying problem is that RStudio Connect doesn't construct the bundle correctly. Specifically, not all of the theme and other dependencies are discovered correctly when creating the bundle.

Overview

The workaround is to explicitly list all of your included files and other dependencies in your R Markdown document. This must be done by doing both of the following:

  1. Include theme information in the YAML header
  2. Include references to included files in an R chunk

Detail

In the following example I deploy a markdown with a structure of:

00_introduction.Rmd
--- include
    * rstudio.css
--- images/00
    * image_1.png
    * image_2.png

1. Specify resource files in the YAML header

You must explicitly specify resource_files in the YAML header. This can contain the names of folders, e.g. an include folder where you have css files.

---
title: "title"
output:  
  xaringan::moon_reader:
    lib_dir: libs
    css: [ "include/rstudio.css" ]
resource_files:
  - include
  - images/00
---

2. Explicitly specify included files in an R Markdown chunk

To tell Connect to include files in the bundle, use rmarkdown::output_metadata$set(). Specifically, you must provide a list of file names to rsc_output_files.

In the following example I use list.files() to automatically detect all files in a specific folder. This only works because I put all of my embedded images in a subfolder called images/00, but you could also explicitly name all of your files.

``````r

{r, include=FALSE} rmarkdown::output_metadata$set( rsc_output_files = list.files("images/00", recursive = TRUE, full.names = TRUE) )
``````

I think this issue has two sides depending on how the xaringan presentation is deployed (From Html or from Rmd) and if it is with the IDE or with rsconnect::deployDoc

I use an account on beta.rstudioconnect.com to do the test with https://github.com/cderv/test-xaringan
I inlcuded:

  • a custom css file in include/custom.css
  • an image in images/xaringan.png and included in the doc as <img src = "images/xaringan.png", width = 100> slide 3.

Deploying html file directly

This means the xaringan presentation is rendered locally and only html file and its dependencies should be published

Using rsconnect::deployDoc

rsconnect::deployDoc("test-prez.html", server = "beta.rstudioconnect.com")

this will use rmarkdown::find_external_resources on the html file to find the ressources to include with deployment. The algorithm can work on an html file and find all <link href, <img src=
or other. It got those

rmarkdown::find_external_resources("test-prez.html")

result

#>                                            path explicit  web
#> 1               libs/htmlwidgets/htmlwidgets.js    FALSE TRUE
#> 2                     libs/jquery/jquery.min.js    FALSE TRUE
#> 3                      libs/leaflet/leaflet.css    FALSE TRUE
#> 4                       libs/leaflet/leaflet.js    FALSE TRUE
#> 5                libs/leafletfix/leafletfix.css    FALSE TRUE
#> 6         libs/Proj4Leaflet/proj4-compressed.js    FALSE TRUE
#> 7             libs/Proj4Leaflet/proj4leaflet.js    FALSE TRUE
#> 8      libs/rstudio_leaflet/rstudio_leaflet.css    FALSE TRUE
#> 9               libs/leaflet-binding/leaflet.js    FALSE TRUE
#> 10 libs/datatables-css/datatables-crosstalk.css    FALSE TRUE
#> 11        libs/datatables-binding/datatables.js    FALSE TRUE
#> 12   libs/dt-core/css/jquery.dataTables.min.css    FALSE TRUE
#> 13 libs/dt-core/css/jquery.dataTables.extra.css    FALSE TRUE
#> 14     libs/dt-core/js/jquery.dataTables.min.js    FALSE TRUE
#> 15             libs/crosstalk/css/crosstalk.css    FALSE TRUE
#> 16           libs/crosstalk/js/crosstalk.min.js    FALSE TRUE
#> 17                          include\\custom.css    FALSE TRUE
#> 18                              test-prez_files    FALSE TRUE

So it find a number of files including the libs file, the css file and presentation files in test-prez_files.
Everything is found but the image file. This is because of remark.js behavior. The html code in the markdown presentation is put as markdown in the source html, and remark.js does the rendering live in the client. See code there. That is why rmarkdown::find_external_resources does not find this file.

There could be a way to adapt this function to work better with xaringan... 馃

Otherwise, it would require a special deployXaringan function that encapsulate deployApp and deals with the dependencies cleverly - but not sure it would work considering the other findings below 馃

Using the IDE

It doesn't seem to work that way with the IDE publish button. The rmarkdown::find_external_resources is not used to find ressources. It seems to parse the Rmd file, even when clicking from the viewer.

Publishing the Rmd file

This means we send the source code and its dependencies to the server, and that the presentation should be render on the rstudio connect server

Using deployDoc

The function rmarkdown::find_external_resources will also be used to find the dependencies.

rmarkdown::find_external_resources("test-prez.Rmd")
#> Le chargement a n茅cessit茅 le package : leaflet
#> Le chargement a n茅cessit茅 le package : DT
#>                  path explicit  web
#> 1 include\\custom.css    FALSE TRUE

The image file is not found either automatically. It requires to be specified in the yaml header as indicated by @andrie

resource_files:
  - images

In this case

rmarkdown::find_external_resources("test-prez.Rmd")
#> Le chargement a n茅cessit茅 le package : leaflet
#> Le chargement a n茅cessit茅 le package : DT
#>                  path explicit  web
#> 1 images/xaringan.png     TRUE TRUE
#> 2 include\\custom.css    FALSE TRUE

The image is found and will be included when deployed with

rsconnect::deployDoc("test-prez.Rmd", server = "beta.rstudioconnect.com", appTitle="test-prez_Rmd")

The libs folder is not an issue because it is created when the render is happening in the server.

However, the problem is the folder images that is in the bundle but is not made accessible by rstudio connect server to the html presentation unless you explicitly do indicate it in the yaml header

rmd_output_metadata:
  rsc_output_files: 
    - images/xaringan.png

However, you have to be explicit (file by file) because a folder is not possible here. So, the other option is like @andrie shows, to create this metadata from R code. The documentation about this is in RStudio Connect user's doc

Using the IDE

It work the same in this case.

Conclusion

So the issue with xaringan presentation is with the dependencies that are used inside the presentation and not available to the web page because not exposed by RStudio Connect. One thing I noticed in the code source of the html when sent to render on the server is that all the resources (custom.css and js libs) where included in the html file as inline code. And in the Rstudio connect deployment log we can see the --self-contained flag in pandoc conversion

log extract

2019/06/08 21:19:19.427857755 /opt/rstudio-connect/ext/pandoc2/pandoc +RTS -K512m -RTS test-prez.utf8.md 
--to html4 --from markdown+autolink_bare_uris+ascii_identifiers+tex_math_single_backslash+smart 
--output /opt/rstudio-connect/mnt/report/test-prez.html --email-obfuscation none 
--self-contained -V 'mathjax-url=https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-MML-AM_CHTML' -V 'title-slide-class=center, middle, inverse, title-slide' 
--standalone --section-divs --template /opt/rstudio-connect/mnt/packrat/3.4.0/v2/library/xaringan/e15e68ee43911de64c81d071e65d4ed6/xaringan/rmarkdown/templates/xaringan/resources/default.html --no-highlight --css include/custom.css --include-in-header /opt/rstudio-connect/mnt/tmp/RtmpeWSBGe/rmarkdown-str272b55ad814c.html --include-before-body /opt/rstudio-connect/mnt/tmp/RtmpeWSBGe/xaringan272b22aab6e9.md --include-after-body /opt/rstudio-connect/mnt/tmp/RtmpeWSBGe/xaringan272b2c8cd899.js --variable title-slide=true --variable math=true 

That means RStudio connect is rendering Rmardown with self_contained = TRUE even if the yaml file says FALSE. This would make perfect sense because file are not exposed by default on RStudio connect, and it also explains why other format are not an issue, because there work better than xaringan with self_contained = TRUE (often as the default like with revealjs::revealjs_presentation)

We should note that even with self_contained = TRUE, the image file included in markdown or html in the body are not transform by pandoc to self contained because it is remark.js which does the job in the browser directly. This is how xaringan presentation works. As @yihui says, xaringan is better when self_contained = FALSE

That is why it needs the files to be available from the server. The rsc_output_files is key here to expose the files through RStudio connect.

I hope this search and analysis could be useful to help document or find a fix.
@Yihui, @andrie I can give you access to the files deployed to RStudio connect beta if you want to dig further with the product team or rsconnect one. I would be happy to do it myself if you point me to the correct person to show this. I can even make a more detailed report if necessary.

Currently, the better workaround to publish a xaringan presentation to rstudio connect is to put all dependencies in the same folder and use @andrie trick to add the to the metadata by a list.files.
This may be wrapped into an helper function - I'll look into that.

Hope it helps.

This is the helper function that I use:

library(rsconnect)

deploy_xaringan <- function(doc){
  qualified_doc <- normalizePath(doc, winslash = "/")
  app_files <-  c(
    basename(qualified_doc),
    list.files("images", pattern = ".*.png", full.names = TRUE, recursive = TRUE),
    list.files("images", pattern = ".*.gif", full.names = TRUE, recursive = TRUE),
    list.files("include", pattern = ".*.png", full.names = TRUE, recursive = TRUE),
    list.files("include", pattern = ".*.jpg", full.names = TRUE, recursive = TRUE),
    list.files("theme", pattern = "*.png", full.names = TRUE, recursive = TRUE)
    # list.files("libs", full.names = TRUE, recursive = TRUE)
  )
  res_files <- rmarkdown::find_external_resources(qualified_doc)
  app_files <- c(app_files, res_files$path)
  rsconnect::deployApp(
    appDir = dirname(qualified_doc),
    appPrimaryDoc = basename(qualified_doc),
    appTitle = "Taking TensorFlow into Production ThinkR 2019",
    appFiles = app_files,
    forceUpdate = TRUE,
    server = "colorado.rstudio.com"
  )
}

deploy_xaringan("tensorflow-production-2019-06.html")
Was this page helpful?
0 / 5 - 0 ratings

Related issues

tcgriffith picture tcgriffith  路  4Comments

beatrizmilz picture beatrizmilz  路  4Comments

ndphillips picture ndphillips  路  3Comments

grawil picture grawil  路  4Comments

burgerga picture burgerga  路  3Comments