Skip to content

Commit a6abe43

Browse files
authored
Use nanonext http server for preview (#2975)
* Use nanonext http server for preview * Update search docs section * Add `stop_preview()` * Add pkgdown reference entry * Keywords internal `stop_preview()`
1 parent f3ec6e2 commit a6abe43

10 files changed

Lines changed: 151 additions & 25 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Imports:
3535
jsonlite,
3636
lifecycle,
3737
openssl,
38+
nanonext (>= 1.8.0),
3839
purrr (>= 1.0.0),
3940
ragg (>= 1.4.0),
4041
rlang (>= 1.1.4),

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export(pkgdown_sitrep)
138138
export(preview_site)
139139
export(rd2html)
140140
export(render_page)
141+
export(stop_preview)
141142
export(template_articles)
142143
export(template_navbar)
143144
export(template_reference)

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# pkgdown (development version)
22

3+
* When previewing a site, it is now served via a local http server. This enables dynamic features such as search to work correctly (@shikokuchuo, #2975).
4+
35
* do not autolink code that is in a link (href) in Rd files (#2972)
46

57
# pkgdown 2.2.0

R/build-search-docs.R

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,7 @@ build_sitemap <- function(pkg = ".") {
6464
#' search:
6565
#' exclude: ['news/index.html']
6666
#' ```
67-
#' # Debugging and local testing
68-
#'
69-
#' Locally (as opposed to on GitHub Pages or Netlify for instance),
70-
#' search won't work if you simply use pkgdown preview of the static files.
71-
#' You can use `servr::httw("docs")` instead.
67+
#' # Debugging
7268
#'
7369
#' If search is not working, run `pkgdown::pkgdown_sitrep()` to eliminate
7470
#' common issues such as the absence of URL in the pkgdown configuration file

R/pkgdown-package.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,5 @@ release_bullets <- function() {
1818
"Update `vignette/translations.Rmd` with any new languages"
1919
)
2020
}
21+
22+
the <- new.env(parent = emptyenv())

R/preview.R

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,69 @@
11
#' Open site in browser
22
#'
3-
#' `preview_site()` opens your pkgdown site in your browser. pkgdown has been
4-
#' carefully designed to work even when served from the file system like
5-
#' this; the only part that doesn't work is search. You can use `servr::httw("docs/")`
6-
#' to create a server to make search work locally.
3+
#' `preview_site()` opens your pkgdown site in your browser, served via a
4+
#' local HTTP server. This enables dynamic features such as search to work
5+
#' correctly in preview.
76
#'
7+
#' @seealso [stop_preview()] to stop the server.
88
#' @inheritParams build_article
99
#' @param path Path relative to destination
1010
#' @export
1111
preview_site <- function(pkg = ".", path = ".", preview = TRUE) {
12-
path <- local_path(pkg, path)
12+
pkg <- as_pkgdown(pkg)
13+
abs_path <- local_path(pkg, path)
1314

1415
check_bool(preview, allow_na = TRUE)
1516
if (is.na(preview)) {
1617
preview <- interactive() && !is_testing()
1718
}
1819

1920
if (preview) {
21+
root <- pkg$dst_path
22+
23+
if (is.null(the$server) || !identical(the$server_root, root)) {
24+
the$server <- nanonext::http_server(
25+
url = "http://127.0.0.1:0",
26+
handlers = nanonext::handler_directory("/", root)
27+
)
28+
the$server$start()
29+
the$server_root <- root
30+
}
31+
2032
cli::cli_inform(c(i = "Previewing site"))
21-
utils::browseURL(path)
33+
url <- paste0(the$server$url, "/", if (path != ".") path)
34+
utils::browseURL(url)
35+
}
36+
37+
invisible()
38+
}
39+
40+
#' Stop HTTP preview
41+
#'
42+
#' Stops the HTTP server started by [preview_site()], if active. This can be
43+
#' called manually, but is not strictly necessary as the server is
44+
#' automatically stopped when previewing a new site or ending the R session.
45+
#'
46+
#' @export
47+
#' @keywords internal
48+
stop_preview <- function() {
49+
if (!is.null(the$server)) {
50+
the$server$close()
51+
the$server <- NULL
52+
the$server_root <- NULL
53+
cli::cli_inform(c(i = "Stopped preview"))
2254
}
2355

2456
invisible()
2557
}
2658

2759
local_path <- function(pkg, path, call = caller_env()) {
28-
pkg <- as_pkgdown(pkg)
2960
check_string(path, call = call)
3061

3162
abs_path <- path_abs(path, pkg$dst_path)
3263
if (!file_exists(abs_path)) {
3364
cli::cli_abort("Can't find file {.path {path}}.", call = call)
3465
}
3566

36-
if (is_dir(abs_path)) {
37-
abs_path <- path(abs_path, "index.html")
38-
}
3967
abs_path
4068
}
4169

man/build_search.Rd

Lines changed: 1 addition & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/preview_site.Rd

Lines changed: 6 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/stop_preview.Rd

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-preview.R

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
local_preview_clean <- function(env = caller_env()) {
2+
the$server <- NULL
3+
withr::defer(the$server <- NULL, envir = env)
4+
}
5+
16
test_that("checks its inputs", {
27
pkg <- local_pkgdown_site()
38

@@ -19,6 +24,85 @@ test_that("local_path adds index.html if needed", {
1924
dir_create(path(pkg$dst_path, "reference"))
2025
expect_equal(
2126
local_path(pkg, "reference"),
22-
path(pkg$dst_path, "reference", "index.html")
27+
path(pkg$dst_path, "reference")
2328
)
2429
})
30+
31+
test_that("preview starts new server when none exists", {
32+
pkg <- local_pkgdown_site()
33+
local_preview_clean()
34+
urls <- character()
35+
withr::local_options(browser = function(url) urls <<- c(urls, url))
36+
37+
preview_site(pkg, preview = TRUE)
38+
39+
expect_false(is.null(the$server))
40+
expect_equal(the$server_root, pkg$dst_path)
41+
expect_length(urls, 1)
42+
expect_equal(urls[[1]], paste0(the$server$url, "/"))
43+
})
44+
45+
test_that("preview reuses server for same root", {
46+
pkg <- local_pkgdown_site()
47+
local_preview_clean()
48+
urls <- character()
49+
withr::local_options(browser = function(url) urls <<- c(urls, url))
50+
51+
preview_site(pkg, preview = TRUE)
52+
server1 <- the$server
53+
54+
file_create(path(pkg$dst_path, "test.html"))
55+
preview_site(pkg, path = "test.html", preview = TRUE)
56+
57+
expect_identical(the$server, server1)
58+
expect_length(urls, 2)
59+
expect_equal(urls[[2]], paste0(server1$url, "/test.html"))
60+
})
61+
62+
test_that("preview starts new server for different root", {
63+
pkg1 <- local_pkgdown_site()
64+
pkg2 <- local_pkgdown_site()
65+
local_preview_clean()
66+
urls <- character()
67+
withr::local_options(browser = function(url) urls <<- c(urls, url))
68+
69+
preview_site(pkg1, preview = TRUE)
70+
server1 <- the$server
71+
72+
preview_site(pkg2, preview = TRUE)
73+
74+
expect_false(identical(the$server, server1))
75+
expect_equal(the$server_root, pkg2$dst_path)
76+
})
77+
78+
test_that("stop_preview stops server", {
79+
pkg <- local_pkgdown_site()
80+
local_preview_clean()
81+
withr::local_options(browser = function(url) {})
82+
83+
preview_site(pkg, preview = TRUE)
84+
expect_false(is.null(the$server))
85+
86+
stop_preview()
87+
expect_null(the$server)
88+
expect_null(the$server_root)
89+
})
90+
91+
test_that("preview constructs correct URLs for sub-paths", {
92+
pkg <- local_pkgdown_site()
93+
local_preview_clean()
94+
urls <- character()
95+
withr::local_options(browser = function(url) urls <<- c(urls, url))
96+
97+
dir_create(path(pkg$dst_path, "reference"))
98+
file_create(path(pkg$dst_path, "reference", "foo.html"))
99+
100+
preview_site(pkg, preview = TRUE)
101+
base_url <- the$server$url
102+
103+
preview_site(pkg, path = "reference", preview = TRUE)
104+
expect_equal(urls[[2]], paste0(base_url, "/reference"))
105+
106+
preview_site(pkg, path = "reference/foo.html", preview = TRUE)
107+
expect_equal(urls[[3]], paste0(base_url, "/reference/foo.html"))
108+
})

0 commit comments

Comments
 (0)