Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Hugo's LiveReload work on RStudio Server #738

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: blogdown
Title: Create Blogs and Websites with R Markdown
Version: 1.13.2
Version: 1.13.4
Authors@R: c(
person("Yihui", "Xie", role = c("aut", "cre"), email = "[email protected]", comment = c(ORCID = "0000-0003-0645-5666")),
person("Christophe", "Dervieux", role = "aut", email = "[email protected]", comment = c(ORCID = "0000-0003-4474-2498")),
Expand Down Expand Up @@ -67,3 +67,4 @@ Config/Needs/website: pkgdown, tidyverse/tidytemplate, rstudio/quillt, rstudio/w
Encoding: UTF-8
RoxygenNote: 7.2.1
SystemRequirements: Hugo (<https://gohugo.io>) and Pandoc (<https://pandoc.org>)
Remotes: yihui/servr
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# CHANGES IN blogdown VERSION 1.14

- `blogdown::serve_site()` works fully on RStudio server now. Previously, Hugo's LiveReload and absolute URLs on pages did not work. Both problems have been fixed. The former issue requires the Hugo version to be equal to or greater than `0.80.0`.

- When rendering Rmd posts that involve time-consuming and intensive computing while serving the site, the `hugo` process can die (for unknown reasons). Now the `hugo` process will be suspended before rendering Rmd posts, and resumed after the rendering is done. Hopefully this will keep the server process alive (thanks, @XiangyunHuang, https://d.cosx.org/d/423509).

# CHANGES IN blogdown VERSION 1.13
Expand Down
69 changes: 62 additions & 7 deletions R/serve.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ serve_site = function(..., .site_dir = NULL) {
serve(..., .site_dir = .site_dir)
}

server_ready = function(url) {
server_ready = function(url, base = NULL) {
# for some reason, R cannot read localhost, but 127.0.0.1 works
url = sub('^http://localhost:', 'http://127.0.0.1:', url)
# need to include a subpath from RStudio translation
if (!is.null(base)) url = paste0(url, '/', rstudioapi::translateLocalUrl(base))
!inherits(
xfun::try_silent(suppressWarnings(readLines(url))), 'try-error'
)
Expand Down Expand Up @@ -111,10 +113,20 @@ serve_it = function(pdir = publish_dir(), baseurl = site_base_dir()) {

owd = setwd(root); on.exit(setwd(owd), add = TRUE)

rsv = is_rstudio_server()
if (rsv) baseurl = '' # don't use baseurl on RStudio Server

server = servr::server_config(..., baseurl = baseurl, hosturl = function(host) {
if (g == 'hugo' && host == '127.0.0.1') 'localhost' else host
})

if (rsv) {
port2 = servr::random_port(exclude = server$port)
server2 = servr::create_server(
port = port2, browser = FALSE, handler = proxy_handler(server$port, port2)
)
}

# launch the hugo/jekyll/hexo server
cmd = if (g == 'hugo') find_hugo() else g
host = server$host; port = server$port; intv = server$interval
Expand All @@ -127,7 +139,10 @@ serve_it = function(pdir = publish_dir(), baseurl = site_base_dir()) {
# RStudio Server uses a proxy like http://localhost:8787/p/56a946ed/ for
# http://localhost:4321, so we must use relativeURLs = TRUE:
# https://github.com/rstudio/blogdown/issues/124
tweak_hugo_env(server = TRUE, relativeURLs = if (is_rstudio_server()) TRUE)
tweak_hugo_env(
baseURL = if (rsv) rstudioapi::translateLocalUrl(server2$url, TRUE),
relativeURLs = if (rsv) TRUE, server = TRUE
)
if (length(list_rmds(pattern = bundle_regex('.R(md|markdown)$'))))
create_shortcode('postref.html', 'blogdown/postref')
}
Expand Down Expand Up @@ -174,7 +189,7 @@ serve_it = function(pdir = publish_dir(), baseurl = site_base_dir()) {
'Failed to serve the site; see if blogdown::build_site() gives more info.'
} else err, call. = FALSE)
}
if (server_ready(server$url)) break
if (server_ready(server$url, if (rsv) server2$url)) break
if (i >= get_option('blogdown.server.timeout', 30)) {
s = proc_kill(pid) # if s == 0, the server must have been started successfully
stop(if (s == 0) c(
Expand All @@ -189,7 +204,7 @@ serve_it = function(pdir = publish_dir(), baseurl = site_base_dir()) {
}
i = i + 1
}
server$browse()
if (rsv) server2$browse(TRUE) else server$browse()
# server is correctly started so we record the directory served
opts$append(served_dirs = root)
Sys.setenv(BLOGDOWN_SERVING_DIR = root)
Expand Down Expand Up @@ -287,14 +302,54 @@ get_config2 = function(key, default) {
}

# refresh the viewer because hugo's livereload doesn't work on RStudio
# Server: https://github.com/rstudio/rstudio/issues/8096 (TODO: check if
# it's fixed in the future: https://github.com/gohugoio/hugo/pull/6698)
# Server: https://github.com/rstudio/rstudio/issues/8096
refresh_viewer = function() {
if (!is_rstudio_server()) return()
# Hugo 0.80.0 has fixed this issue: https://github.com/gohugoio/hugo/pull/6698
if (!is_rstudio_server() || hugo_available('0.80.0')) return()
server_wait()
rstudioapi::executeCommand('viewerRefresh')
}

server_wait = function() {
Sys.sleep(get_option('blogdown.server.wait', 2))
}

# port is hugo's port; port2 is servr proxy's port
proxy_handler = function(port, port2) {
# hugo and proxy's base urls
b1 = rstudioapi::translateLocalUrl(sprintf('http://localhost:%d', port))
b2 = rstudioapi::translateLocalUrl(sprintf('http://localhost:%d', port2))
function(req) {
path = req$PATH_INFO
# add proxy's base url and remove redundant base urls if absent
path = gsub(sprintf('^/(%s)*', b2), b2, path, perl = TRUE)
path = paste0('/', path)
u = sprintf('http://127.0.0.1:%d%s', port, path)
# for HTML pages, read their content; for other resources, redirect to hugo
if (grepl('[.]html?|/$', path)) {
fix_livereload(proxy_response(u), b1, b2)
} else {
servr::redirect(rstudioapi::translateLocalUrl(u, TRUE))
}
}
}

proxy_response = function(url, status = 200L, type = mime::guess_type(url, empty = 'text/html')) {
tryCatch(list(
status = status, headers = list('Content-Type' = type),
body = paste(readLines(url, encoding = 'UTF-8', warn = FALSE), collapse = '\n')
), error = function(e) list(
status = 404L, headers = list('Content-Type' = 'text/plain'),
body = paste('Not found:', url)
))
}

# fix the location of hugo's livereload.js: re-route from the proxy to hugo
# (unfortunately RStudio Server seems to interfere with loading livereload.js;
# all other assets can be loaded correctly, but I don't know why livereload.js
# is an exception)
fix_livereload = function(res, b1, b2) {
if (!identical(res$headers[['Content-Type']], 'text/html')) return(res)
res$body = gsub(sprintf('(src="/)(%slivereload[.]js)', b2), sprintf('\\1%s\\2', b1), res$body)
res
}
2 changes: 1 addition & 1 deletion R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ tweak_hugo_env = function(baseURL = NULL, relativeURLs = NULL, server = FALSE) {
c2 = if (is.null(relativeURLs)) get_config('relativeurls', FALSE, config) else relativeURLs
if (server && c1) b = paste0(if (grepl('^//', b)) 'http:' else 'http://example.org/', b)

vars = c(HUGO_BASEURL = if (c2) '/' else b, HUGO_RELATIVEURLS = tolower(c2))
vars = c(HUGO_BASEURL = if (c2 && !server) '/' else b, HUGO_RELATIVEURLS = tolower(c2))
if (server) {
vars = c(vars, HUGO_BLOGDOWN_POST_RELREF = 'true')
c3 = get_config('ignoreErrors', NA, config)
Expand Down