Skip to content

Commit 6b2bf3b

Browse files
committed
Support README.qmd
1 parent 2a4e7a3 commit 6b2bf3b

7 files changed

Lines changed: 120 additions & 20 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Suggests:
5050
httr2 (>= 1.0.0),
5151
knitr (>= 1.39),
5252
lintr (>= 3.0.0),
53+
quarto (>= 1.5.1),
5354
remotes (>= 2.5.0),
5455
rmarkdown (>= 2.14),
5556
rstudioapi (>= 0.13),

NEWS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Deprecations
1313

1414
Other improvements
1515

16+
* `build_readme()` gains support for `README.qmd` and renders using Quarto (#2620).
1617
* `install()` now installs dependencies with `pak::local_install_deps()` instead of `remotes::install_deps()`. This lets us default to `upgrade = FALSE`, so that existing dependencies are only upgraded when a newer version is actually required (#2486). `keep_source` now defaults to `TRUE` when `build = FALSE`, so that source references are automatically preserved during development installs.
1718
* `build_manual()` reports more details on failure (#2586).
1819
* `build_site()` now just calls `pkgdown::build_site()`, meaning that you will get more (informative) output by default (#2578).
@@ -27,8 +28,7 @@ Other improvements
2728

2829
# devtools 2.4.6
2930

30-
* Functions that use httr now explicitly check that it is installed
31-
(@catalamarti, #2573).
31+
* Functions that use httr now explicitly check that it is installed (@catalamarti, #2573).
3232

3333
* `test_coverage()` now works if the package has not been installed.
3434

R/build-readme.R

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#' `r lifecycle::badge("deprecated")`
55
#'
66
#' `build_rmd()` is deprecated, as it is a low-level helper for internal use. To
7-
#' render your package's `README.Rmd` or `README.qmd`, use [build_readme()]. To
7+
#' render your package's `README.qmd` or `README.Rmd`, use [build_readme()]. To
88
#' preview a vignette or article, use functions like [pkgdown::build_site()] or
99
#' [pkgdown::build_article()].
1010
#'
@@ -86,20 +86,21 @@ build_rmd_impl <- function(
8686

8787
#' Build README
8888
#'
89-
#' Renders an executable README, such as `README.Rmd`, to `README.md`.
90-
#' Specifically, `build_readme()`:
89+
#' Renders an executable README, i.e. `README.qmd` or `README.Rmd`, to
90+
#' `README.md`. Specifically, `build_readme()`:
9191
#' * Installs a copy of the package's current source to a temporary library
9292
#' * Renders the README in a clean R session
9393
#'
94-
#' @param path Path to the package to build the README.
94+
#' @param path Path to the top-level directory of the source package.
9595
#' @param quiet If `TRUE`, suppresses most output. Set to `FALSE`
9696
#' if you need to debug.
97-
#' @param ... Additional arguments passed to [rmarkdown::render()].
97+
#' @param ... Additional arguments passed to [rmarkdown::render()], in the
98+
#' case of `README.Rmd`. Not used for `README.qmd`
9899
#' @export
99100
build_readme <- function(path = ".", quiet = TRUE, ...) {
100101
pkg <- as.package(path)
101102

102-
regexp <- paste0(path_file(pkg$path), "/(inst/)?readme[.]rmd$")
103+
regexp <- paste0(path_file(pkg$path), "/(inst/)?readme[.](r|q)md$")
103104
readme_path <- path_abs(dir_ls(
104105
pkg$path,
105106
ignore.case = TRUE,
@@ -109,13 +110,54 @@ build_readme <- function(path = ".", quiet = TRUE, ...) {
109110
))
110111

111112
if (length(readme_path) == 0) {
112-
cli::cli_abort("Can't find {.file README.Rmd} or {.file inst/README.Rmd}.")
113+
cli::cli_abort(
114+
"Can't find {.file README.qmd} or {.file README.Rmd}, at the top-level or
115+
below {.file inst/}."
116+
)
113117
}
114118
if (length(readme_path) > 1) {
119+
rel_paths <- path_rel(readme_path, pkg$path)
115120
cli::cli_abort(
116-
"Can't have both {.file README.Rmd} and {.file inst/README.Rmd}."
121+
"Found multiple executable READMEs: {.file {rel_paths}}. There can only be
122+
one."
117123
)
118124
}
119125

120-
build_rmd_impl(readme_path, path = path, quiet = quiet, ...)
126+
if (path_ext(readme_path) == "qmd") {
127+
build_qmd_readme(readme_path, path = path, quiet = quiet)
128+
} else {
129+
build_rmd_impl(readme_path, path = path, quiet = quiet, ...)
130+
}
131+
}
132+
133+
build_qmd_readme <- function(readme_path, path = ".", quiet = TRUE) {
134+
pkg <- as.package(path)
135+
136+
check_installed("quarto")
137+
save_all()
138+
139+
local_install(pkg, quiet = TRUE)
140+
141+
if (!quiet) {
142+
cli::cli_inform(c(i = "Building {.path {readme_path}}"))
143+
}
144+
145+
# Quarto spawns its own R process for knitr, which won't inherit .libPaths().
146+
147+
# Pass library paths via R_LIBS_USER so the quarto subprocess finds the
148+
# temporarily installed package first, ahead of any user-installed version.
149+
lib_paths <- paste(.libPaths(), collapse = .Platform$path.sep)
150+
151+
callr::r_safe(
152+
function(input, quiet, lib_paths) {
153+
withr::local_envvar(R_LIBS_USER = lib_paths)
154+
quarto::quarto_render(input = input, quiet = quiet)
155+
},
156+
args = list(input = readme_path, quiet = quiet, lib_paths = lib_paths),
157+
show = TRUE,
158+
spinner = FALSE,
159+
stderr = "2>&1"
160+
)
161+
162+
invisible(TRUE)
121163
}

man/build_readme.Rd

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

man/build_rmd.Rd

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

tests/testthat/_snaps/build-readme.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@
44
build_readme(pkg)
55
Condition
66
Error in `build_readme()`:
7-
! Can't find 'README.Rmd' or 'inst/README.Rmd'.
7+
! Can't find 'README.qmd' or 'README.Rmd', at the top-level or below 'inst/'.
88

99
---
1010

1111
Code
1212
build_readme(pkg)
1313
Condition
1414
Error in `build_readme()`:
15-
! Can't have both 'README.Rmd' and 'inst/README.Rmd'.
15+
! Found multiple executable READMEs: 'README.Rmd' and 'inst/README.Rmd'. There can only be one.
16+
17+
# errors if both README.qmd and README.Rmd exist
18+
19+
Code
20+
build_readme(pkg)
21+
Condition
22+
Error in `build_readme()`:
23+
! Found multiple executable READMEs: 'README.Rmd' and 'README.qmd'. There can only be one.
1624

1725
# build_rmd() is deprecated
1826

tests/testthat/test-build-readme.R

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
test_that("can build README in root directory", {
1+
test_that("can build README.Rmd in root directory", {
22
skip_on_cran()
33

44
pkg <- local_package_create()
@@ -14,7 +14,7 @@ test_that("can build README in root directory", {
1414
expect_false(file_exists(path(pkg, "README.html")))
1515
})
1616

17-
test_that("can build README in inst/", {
17+
test_that("can build README.Rmd in inst/", {
1818
skip_on_cran()
1919

2020
pkg <- local_package_create()
@@ -37,6 +37,47 @@ test_that("can build README in inst/", {
3737
expect_false(file_exists(path(pkg, "inst", "README.html")))
3838
})
3939

40+
test_that("can build README.qmd in root directory", {
41+
skip_on_cran()
42+
skip_if_not_installed("quarto")
43+
skip_if(!nzchar(Sys.which("quarto")), "quarto cli not available")
44+
45+
pkg <- local_package_create()
46+
usethis::ui_silence(
47+
usethis::with_project(
48+
pkg,
49+
use_readme_qmd(open = FALSE)
50+
)
51+
)
52+
53+
build_readme(pkg, quiet = TRUE)
54+
expect_true(file_exists(path(pkg, "README.md")))
55+
})
56+
57+
test_that("can build README.qmd in inst/", {
58+
skip_on_cran()
59+
skip_if_not_installed("quarto")
60+
skip_if(!nzchar(Sys.which("quarto")), "quarto cli not available")
61+
62+
pkg <- local_package_create()
63+
usethis::ui_silence(
64+
usethis::with_project(
65+
pkg,
66+
use_readme_qmd(open = FALSE)
67+
)
68+
)
69+
dir_create(pkg, "inst")
70+
file_move(
71+
path(pkg, "README.qmd"),
72+
path(pkg, "inst", "README.qmd")
73+
)
74+
75+
build_readme(pkg, quiet = TRUE)
76+
expect_true(file_exists(path(pkg, "inst", "README.md")))
77+
expect_false(file_exists(path(pkg, "README.qmd")))
78+
expect_false(file_exists(path(pkg, "README.md")))
79+
})
80+
4081
test_that("useful errors if too few or too many", {
4182
pkg <- local_package_create()
4283
expect_snapshot(build_readme(pkg), error = TRUE)
@@ -52,6 +93,13 @@ test_that("useful errors if too few or too many", {
5293
expect_snapshot(build_readme(pkg), error = TRUE)
5394
})
5495

96+
test_that("errors if both README.qmd and README.Rmd exist", {
97+
pkg <- local_package_create()
98+
file_create(path(pkg, "README.Rmd"))
99+
file_create(path(pkg, "README.qmd"))
100+
expect_snapshot(build_readme(pkg), error = TRUE)
101+
})
102+
55103
test_that("don't error for README in another directory", {
56104
skip_on_cran()
57105

0 commit comments

Comments
 (0)