rmarkdown icon indicating copy to clipboard operation
rmarkdown copied to clipboard

Resources with a space in name are not copied when using runtime: shiny

Open Gutschlhofer opened this issue 6 years ago • 2 comments

Including a .css file via YAML header and runtime: shiny does not work when the filename of the .Rmd contains a space.

After ages of searching for the bug of why I cannot include external .css file and breaking it down to the lowest level (I swapped around between files and with and without runtime: shiny without any other code except for the YAML header), I realised that it works when I remove the space I had in the .Rmd file. So instead of x y.Rmd I had to rename it to xy.Rmd and now it works.

Long story short, the following code only works when the filename of the .Rmd document does not contain a space character and I think it would be good to add this functionality because I do not think it's obvious for users that they have to use filenames that do not include any spaces.

---
runtime: shiny
title: 'My Title'
output: 
  html_document:
    css: www/style.css
---

Gutschlhofer avatar Jul 23 '18 19:07 Gutschlhofer

Thanks for the report and sorry for the delay.

This was not easy to investigate but I think I have found the issue. Shiny runtime will modify the way the Rmd is rendered as it will be generated in a tmp directory for the app to run from. Resources file like style.css should be copied there. html_document_base has a special copy_resources flag for that, that rmarkdown::run() will activate, and will set output_dir, intermediates_dir. The issue lies with this last one.

It happens when no space in filename but it fails when there is a space. Here is a reprex without shiny

library(rmarkdown)
#> Warning: le package 'rmarkdown' a été compilé avec la version R 4.0.3

dir.create(tmp_dir <- tempfile())
old <- setwd(tmp_dir)

xfun::write_utf8(c(
  "---",
  "title: something",
  "output:",
  "  html_document:" ,
  "    css: style.css",
  "---",
  "",
  "# A header"), "test.Rmd")

xfun::write_utf8(c(
  "h1 {",
  "  color: red;",
  "}"), 
  "style.css"
)
  
render_with_copy <- function(input_file, output_dir = "out") {
  render(
    input = input_file,
    # output_file = dest_file,
    output_dir = output_dir,
    output_options = list(
      self_contained = FALSE,
      copy_resources = TRUE),
    intermediates_dir = output_dir,
    quiet = TRUE
  )
}

# base scenario: style.css stays in input dir
render("test.Rmd", output_options = list(self_contained = FALSE), output_dir = "out")
#> processing file: test.Rmd
#> output file: test.knit.md
#> "C:/Users/chris/scoop/apps/rstudio-daily/current/bin/pandoc/pandoc" +RTS -K512m -RTS test.utf8.md --to html4 --from markdown+autolink_bare_uris+tex_math_single_backslash --output pandoc315075df3add.html --lua-filter "C:\Users\chris\Documents\R\win-library\4.0\rmarkdown\rmarkdown\lua\pagebreak.lua" --lua-filter "C:\Users\chris\Documents\R\win-library\4.0\rmarkdown\rmarkdown\lua\latex-div.lua" --email-obfuscation none --standalone --section-divs --template "C:\Users\chris\Documents\R\win-library\4.0\rmarkdown\rmd\h\default.html" --no-highlight --variable highlightjs=1 --css style.css --variable "theme:bootstrap" --include-in-header "C:\Users\chris\AppData\Local\Temp\RtmpwLnWPy\rmarkdown-str3150ffeb38.html" --mathjax --variable "mathjax-url:https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
#> 
#> Output created: out/test.html
fs::file_exists("style.css")
#> style.css 
#>      TRUE
fs::file_exists("out/test_files/style.css")
#> out/test_files/style.css 
#>                    FALSE
fs::dir_delete("out")

# shiny runtime will copy the resource files
render_with_copy("test.Rmd", "out")
fs::file_exists("out/test_files/style.css")
#> out/test_files/style.css 
#>                     TRUE
fs::dir_delete("out")

fs::file_copy("test.Rmd", "test with space.Rmd")
render_with_copy("test with space.Rmd", "out")
fs::file_exists("out/test-with-space_files/style.css")
#> out/test-with-space_files/style.css 
#>                               FALSE
fs::dir_delete("out")

setwd(old)
fs::dir_delete(tmp_dir)

Created on 2020-10-28 by the reprex package (v0.3.0.9001)

cderv avatar Oct 28 '20 11:10 cderv

Ok I found know why this happens:

  • We have a special treatment for filename with special character. We rename it without space and put it in the intermediate directory if one is specified. https://github.com/rstudio/rmarkdown/blob/aa180719408cd49913d79717c8c94489148cfd23/R/render.R#L351-L378

  • So if one intermediates_dir is specified, the input is changed. Meaning, the working directory from which everything is run is changed: https://github.com/rstudio/rmarkdown/blob/aa180719408cd49913d79717c8c94489148cfd23/R/render.R#L384

  • As the resources are not moved, they are no more relative to the input file. And this is why it fails when they should be copied https://github.com/rstudio/rmarkdown/blob/51224f06906bbc9ba86a627ebce49ecf66a6c941/R/html_resource_copy.R#L38-L43

  • style.css is correctly detected, but file_exist("style.css") (third test above) will fail because we are no more relative to where the css file is. Hence it is not found and not copied to where is should, so not found by the html file that is modified.

@yihui I need to discuss this with you some day before trying to fix this in order to find the correct fix, less impactful. I see several solution.

The issue here can be sum up by :

  • local resources file are not found when
    • filename has a special char
    • intermediates_dir is specified.
    • resources are supposed to be copied (copy_resources = TRUE)

cderv avatar Oct 28 '20 15:10 cderv