Custom rendering of Hugo posts and notes

Han Oostdijk

2019/08/13

Date last run: 11Aug2021

When an updated version of the tesseract package became available I realised that a post about working with magick and tesseract was possibly no longer valid. So I thought I should create a new post with a reference to the old one, but then I realised that the old .Rmd file would be rendered anyway with the new package.

Browsing the internet I found a post by Hiroaki Yutani that learned me how to custom build the website with the build script. My idea was to change that script in such a way that it would honor a blacklist: .Rmarkdown or .Rmd files in that list would not be rendered (again).
After coding this I realized that I got into problems because of the two ways I use the rendering facilities of blogdown : sometimes I use the 'Serve Site' addin and sometimes I use the blogdown::build_site function directly.
By setting the blogdown.method option to 'custom' and using an adapted form of Hiroaki’s script I ensure that the result of the rendering is always an .md file, avoiding in this way the problem that mixing of the two rendering methods sometimes led to duplicate entries in the list with posts and notes.

The ways for rendering the contents

For both ways the parameter method is set to 'custom' because that is set as default value by the option blogdown.method in .Rprofile . This ensures that the build script is used in both cases. So we have the two ways:

Code changes

To get it working I had to apply the following changes

The blogdown::build_site function

This function was not changed, but is displayed here because it makes clear why the other functions and script were changed the way they are now. Because the variable method is set to 'custom' (due to the blogdown.method option in .Rprofile) the function only passes control to our build script.

blogdown::build_site
  function (local = FALSE, method = c("html", "custom"), run_hugo = TRUE, 
      build_rmd = FALSE) 
  {
      if (missing(method)) 
          method = getOption("blogdown.method", method)
      method = match.arg(method)
      on.exit(run_script("R/build.R", as.character(local)), add = TRUE)
      if (method == "custom") 
          return()
      if (!xfun::isFALSE(build_rmd)) {
          if (is.character(build_rmd) && length(build_rmd) == 1) {
              build_rmd = switch(build_rmd, timestamp = timestamp_filter, 
                  md5sum = md5sum_filter, build_rmd)
          }
          files = if (is.character(build_rmd)) 
              build_rmd
          else {
              files = list_rmds("content", TRUE)
              if (is.function(build_rmd)) 
                  build_rmd(files)
              else {
                  if (length(files)) 
                    getOption("blogdown.files_filter", identity)(files)
              }
          }
          build_rmds(files)
      }
      if (run_hugo) 
          on.exit(hugo_build(local), add = TRUE)
      on.exit(run_script("R/build2.R", as.character(local)), add = TRUE)
      invisible()
  }
  <bytecode: 0x0000000012f2ca78>
  <environment: namespace:blogdown>

The .Rprofile file

The .Rprofile file was added to set some options for the blogdown package.

The build script

This is the version derived from the script by Hiroaki Yutani.

# script called from blogdown::build_site (see help info there)
# build_site should then be called as
# blogdown::build_site(local = FALSE, method = "custom")

# adopted from https://yutani.rbind.io/post/2017-10-25-blogdown-custom/
# added removal of existing html and markdown files
# added handling of Rmarkdown files (like .Rmd files)
# added a blacklist
#   (passed from original session via option 'blogdown.HOQC.blacklist')

# catch "local" arg passed from blogdown::build_site()

# inserted remove of existing html and markdown files
to_unlink_files <- list.files("content", "\\.html$|\\.markdown$",
	recursive = TRUE, full.names = TRUE)
for (to_unlink_file in to_unlink_files) {
	unlink(to_unlink_file)
}

# local : T from serv_site and F from build_site
local <- commandArgs(TRUE)[1]

# set common options ofr knitr
knitr::opts_knit$set(
	base.dir = normalizePath("static/", mustWork = TRUE),
	base.url = "/"
)

knitr::opts_chunk$set(
	cache.path = normalizePath("cache/", mustWork = TRUE),
	collapse = TRUE,
	comment  = "#>"
)

# list up Rmd files
Rmd_files <- list.files("content", "\\.Rmd$|\\.Rmarkdown$",
	recursive = TRUE, full.names = TRUE)

# inserted section to avoid handling of blacklisted Rmd-type files
blacklist   <- getOption('blogdown.HOQC.blacklist',c())
blacklisted <- purrr::map_lgl(Rmd_files,
	~ any(stringr::str_detect(.,blacklist)))
# message("blacklist: \n  ", paste(Rmd_files[blacklisted], collapse = "\n  "))
Rmd_files <- Rmd_files[!blacklisted]

# list up md files
out_files  <- sub("\\.Rmd$|\\.Rmarkdown$", ".md", Rmd_files)
names(out_files) <- Rmd_files

# knit it when:
#   1) the correspondent md file does not exist yet
#   2) the Rmd file was updated after the last time md file had been generated
needs_knitted <- !file.exists(out_files) | utils::file_test("-ot", out_files, Rmd_files)
#needs_knitted <- rep(TRUE,length(Rmd_files)) # to force all to be knitted
# message("skip: \n    ", paste(Rmd_files[!needs_knitted], collapse = "\n    "))

for (rmd in Rmd_files[needs_knitted]) {
	base_name <- tools::file_path_sans_ext(basename(rmd))
	knitr::opts_chunk$set(
		fig.path = glue::glue("post/{base_name}_files/figure-html/")
	)
	set.seed(1984)
	knitr::knit(input = rmd, output = out_files[rmd], encoding = "UTF-8")
}

# local : T from serv_site and F from build_site
#  T : with drafts, F : no drafts in website in public
blogdown::hugo_build(local = local)

SessionInfo

This document was produced on 11Aug2021 with the following R environment:

#> R version 4.1.0 (2021-05-18)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 19042)
#> 
#> Matrix products: default
#> 
#> locale:
#> [1] LC_COLLATE=English_United States.1252 
#> [2] LC_CTYPE=English_United States.1252   
#> [3] LC_MONETARY=English_United States.1252
#> [4] LC_NUMERIC=C                          
#> [5] LC_TIME=English_United States.1252    
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] blogdown_0.21.34
#> 
#> loaded via a namespace (and not attached):
#>  [1] compiler_4.1.0 magrittr_2.0.1 tools_4.1.0    glue_1.4.2     stringi_1.7.3 
#>  [6] knitr_1.33     stringr_1.4.0  xfun_0.24      rlang_0.4.11   evaluate_0.14 
#> [11] purrr_0.3.4