diff --git a/NEWS.md b/NEWS.md index 311e2707d..f976a930d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # usethis (development version) -## Bug fixes and minor improvements +* `use_vignette()` and `use_article()` gain `type` to allow creating Quarto vignettes and articles (@olivroy, #1997). * `use_package()` now decreases a package minimum version required when `min_version` is lower than what is currently specified in the DESCRIPTION diff --git a/R/vignette.R b/R/vignette.R index ceab26aa0..13f53a00c 100644 --- a/R/vignette.R +++ b/R/vignette.R @@ -13,40 +13,59 @@ #' @param name Base for file name to use for new vignette. Should consist only #' of numbers, letters, `_` and `-`. Lower case is recommended. #' @param title The title of the vignette. +#' @param type One of `"quarto"` or `"rmarkdown"` #' @seealso The [vignettes chapter](https://r-pkgs.org/vignettes.html) of -#' [R Packages](https://r-pkgs.org). +#' [R Packages](https://r-pkgs.org) and the [Quarto vignettes](https://quarto-dev.github.io/quarto-r/articles/hello.html) reference. #' @export #' @examples #' \dontrun{ #' use_vignette("how-to-do-stuff", "How to do stuff") #' } -use_vignette <- function(name, title = name) { +use_vignette <- function(name, title = name, type = c("rmarkdown", "quarto")) { check_is_package("use_vignette()") check_required(name) check_vignette_name(name) + type <- arg_match(type) use_dependency("knitr", "Suggests") - use_dependency("rmarkdown", "Suggests") - proj_desc_field_update("VignetteBuilder", "knitr", overwrite = TRUE) + if (type == "rmarkdown") { + use_dependency("rmarkdown", "Suggests") + proj_desc_field_update("VignetteBuilder", "knitr", overwrite = FALSE) + use_vignette_template("vignette.Rmd", name, title) + } else if (type == "quarto") { + proj_desc_field_update("VignetteBuilder", "quarto", overwrite = FALSE) + use_vignette_template("vignette.qmd", name, title) + use_build_ignore("vignettes/*_files") + } use_git_ignore("inst/doc") - use_vignette_template("vignette.Rmd", name, title) invisible() } #' @export #' @rdname use_vignette -use_article <- function(name, title = name) { +use_article <- function(name, title = name, type = c("rmarkdown", "quarto")) { check_is_package("use_article()") + type <- arg_match(type) deps <- proj_deps() - if (!"rmarkdown" %in% deps$package) { - proj_desc_field_update("Config/Needs/website", "rmarkdown", append = TRUE) + + if (type == "rmarkdown") { + if (!"rmarkdown" %in% deps$package) { + proj_desc_field_update("Config/Needs/website", "rmarkdown", append = TRUE) + } + + use_vignette_template("article.Rmd", name, title, subdir = "articles") + } else if (type == "quarto") { + if (!"quarto" %in% deps$package) { + proj_desc_field_update("Config/Needs/website", "quarto", append = TRUE) + } + + use_vignette_template("article.qmd", name, title, subdir = "articles") } - use_vignette_template("article.Rmd", name, title, subdir = "articles") use_build_ignore("vignettes/articles") invisible() @@ -62,12 +81,20 @@ use_vignette_template <- function(template, name, title, subdir = NULL) { if (!is.null(subdir)) { use_directory(path("vignettes", subdir)) } + use_git_ignore(c("*.html", "*.R"), directory = "vignettes") + # make sure nothing else is caught. (this should be assured as `.` are not allowed.) + vignette_ext <- path_ext(template) + arg_match0(vignette_ext, c("qmd", "Rmd")) + if (vignette_ext == "qmd" && uses_git()) { + # https://quarto-dev.github.io/quarto-r/articles/hello.html + use_git_ignore("*_files", directory = "vignettes") + } if (is.null(subdir)) { - path <- path("vignettes", asciify(name), ext = "Rmd") + path <- path("vignettes", asciify(name), ext = vignette_ext) } else { - path <- path("vignettes", subdir, asciify(name), ext = "Rmd") + path <- path("vignettes", subdir, asciify(name), ext = vignette_ext) } data <- list( diff --git a/inst/templates/article.qmd b/inst/templates/article.qmd new file mode 100644 index 000000000..78f975db2 --- /dev/null +++ b/inst/templates/article.qmd @@ -0,0 +1,12 @@ +--- +title: "{{{ vignette_title }}}" +format: html +knitr: + opts_chunk: + collapse: true + comment: "#>" +--- + +```{r setup} +library({{Package}}) +``` diff --git a/inst/templates/vignette.qmd b/inst/templates/vignette.qmd new file mode 100644 index 000000000..b587c2d2c --- /dev/null +++ b/inst/templates/vignette.qmd @@ -0,0 +1,18 @@ +--- +title: "{{{ vignette_title }}}" +format: html +knitr: + opts_chunk: + collapse: true + comment: "#>" +vignette: > + %\VignetteIndexEntry{{{ braced_vignette_title }}} + %\VignetteEngine{quarto::html} + %\VignetteEncoding{UTF-8} +--- + +```{r} +#| label: setup +library({{Package}}) +``` + diff --git a/man/use_vignette.Rd b/man/use_vignette.Rd index 8c5fa70fa..efc54f2cc 100644 --- a/man/use_vignette.Rd +++ b/man/use_vignette.Rd @@ -5,15 +5,17 @@ \alias{use_article} \title{Create a vignette or article} \usage{ -use_vignette(name, title = name) +use_vignette(name, title = name, type = c("rmarkdown", "quarto")) -use_article(name, title = name) +use_article(name, title = name, type = c("rmarkdown", "quarto")) } \arguments{ \item{name}{Base for file name to use for new vignette. Should consist only of numbers, letters, \verb{_} and \code{-}. Lower case is recommended.} \item{title}{The title of the vignette.} + +\item{type}{One of \code{"quarto"} or \code{"rmarkdown"}} } \description{ Creates a new vignette or article in \verb{vignettes/}. Articles are a special @@ -38,5 +40,5 @@ use_vignette("how-to-do-stuff", "How to do stuff") } \seealso{ The \href{https://r-pkgs.org/vignettes.html}{vignettes chapter} of -\href{https://r-pkgs.org}{R Packages}. +\href{https://r-pkgs.org}{R Packages} and the \href{https://quarto-dev.github.io/quarto-r/articles/hello.html}{Quarto vignettes} reference. } diff --git a/tests/testthat/test-vignette.R b/tests/testthat/test-vignette.R index 82b7a4690..b09f92ef7 100644 --- a/tests/testthat/test-vignette.R +++ b/tests/testthat/test-vignette.R @@ -25,13 +25,32 @@ test_that("use_vignette() does the promised setup", { expect_true("inst/doc" %in% ignores) deps <- proj_deps() - expect_true( - all(c("knitr", "rmarkdown") %in% deps$package[deps$type == "Suggests"]) + expect_contains( + deps$package[deps$type == "Suggests"], + c("knitr", "rmarkdown") ) expect_identical(proj_desc()$get_field("VignetteBuilder"), "knitr") }) +test_that("use_vignette() works with Quarto", { + create_local_package() + + use_vignette("name", "title", type = "quarto") + expect_proj_file("vignettes/name.qmd") + + ignores <- read_utf8(proj_path(".gitignore")) + expect_true("inst/doc" %in% ignores) + + deps <- proj_deps() + expect_contains( + deps$package[deps$type == "Suggests"], + "knitr" + ) + + expect_identical(proj_desc()$get_field("VignetteBuilder"), "quarto") +}) + # use_article ------------------------------------------------------------- test_that("use_article goes in article subdirectory", { @@ -54,6 +73,19 @@ test_that("use_article() adds rmarkdown to Config/Needs/website", { ) }) +test_that("use_article() adds quarto to Config/Needs/website", { + create_local_package() + local_interactive(FALSE) + + proj_desc_field_update("Config/Needs/website", "somepackage", append = TRUE) + use_article("name", "title", "quarto") + + expect_setequal( + proj_desc()$get_list("Config/Needs/website"), + c("quarto", "somepackage") + ) +}) + # helpers ----------------------------------------------------------------- test_that("valid_vignette_name() works", {