diff --git a/R/vignette.R b/R/vignette.R index ceab26aa0..389bdb423 100644 --- a/R/vignette.R +++ b/R/vignette.R @@ -10,28 +10,57 @@ #' * Adds `inst/doc` to `.gitignore` so built vignettes aren't tracked. #' * Adds `vignettes/*.html` and `vignettes/*.R` to `.gitignore` so #' you never accidentally track rendered vignettes. -#' @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. -#' @seealso The [vignettes chapter](https://r-pkgs.org/vignettes.html) of -#' [R Packages](https://r-pkgs.org). +#' * For `*.qmd`, adds Quarto-related patterns to `.gitignore` and +#' `.Rbuildignore`. +#' @param name File name to use for new vignette. Should consist only of +#' numbers, letters, `_` and `-`. Lower case is recommended. Can include the +#' `".Rmd"` or `".qmd"` file extension, which also dictates whether to place +#' an R Markdown or Quarto vignette. R Markdown (`".Rmd"`) is the current +#' default, but it is anticipated that Quarto (`".qmd"`) will become the +#' default in the future. +#' @param title The title of the vignette. If not provided, a title is generated +#' from `name`. +#' @seealso +#' * The [vignettes chapter](https://r-pkgs.org/vignettes.html) of +#' [R Packages](https://r-pkgs.org) +#' * The pkgdown vignette on Quarto: +#' `vignette("quarto", package = "pkgdown")` +#' * The quarto (as in the R package) vignette on HTML vignettes: +#' `vignette("hello", package = "quarto")` #' @export #' @examples #' \dontrun{ #' use_vignette("how-to-do-stuff", "How to do stuff") +#' use_vignette("r-markdown-is-classic.Rmd", "R Markdown is classic") +#' use_vignette("quarto-is-cool.qmd", "Quarto is cool") #' } -use_vignette <- function(name, title = name) { +use_vignette <- function(name, title = NULL) { check_is_package("use_vignette()") check_required(name) + maybe_name(title) + + ext <- get_vignette_extension(name) + if (ext == "qmd") { + check_installed("quarto") + check_installed("pkgdown", version = "2.1.0") + } + + name <- path_ext_remove(name) check_vignette_name(name) + title <- title %||% name use_dependency("knitr", "Suggests") - use_dependency("rmarkdown", "Suggests") - - proj_desc_field_update("VignetteBuilder", "knitr", overwrite = TRUE) use_git_ignore("inst/doc") - use_vignette_template("vignette.Rmd", name, title) + if (tolower(ext) == "rmd") { + use_dependency("rmarkdown", "Suggests") + proj_desc_field_update("VignetteBuilder", "knitr", overwrite = TRUE, append = TRUE) + use_vignette_template("vignette.Rmd", name, title) + } else { + use_dependency("quarto", "Suggests") + proj_desc_field_update("VignetteBuilder", "quarto", overwrite = TRUE, append = TRUE) + use_vignette_template("vignette.qmd", name, title) + } invisible() } @@ -58,16 +87,23 @@ use_vignette_template <- function(template, name, title, subdir = NULL) { check_name(title) maybe_name(subdir) + ext <- get_vignette_extension(template) + use_directory("vignettes") if (!is.null(subdir)) { use_directory(path("vignettes", subdir)) } use_git_ignore(c("*.html", "*.R"), directory = "vignettes") + if (ext == "qmd") { + use_build_ignore("vignettes/.quarto") + use_build_ignore("vignettes/*_files") + use_git_ignore("*_files", "vignettes") + } if (is.null(subdir)) { - path <- path("vignettes", asciify(name), ext = "Rmd") + path <- path("vignettes", asciify(name), ext = ext) } else { - path <- path("vignettes", subdir, asciify(name), ext = "Rmd") + path <- path("vignettes", subdir, asciify(name), ext = ext) } data <- list( @@ -102,3 +138,29 @@ check_vignette_name <- function(name) { valid_vignette_name <- function(x) { grepl("^[[:alpha:]][[:alnum:]_-]*$", x) } + +check_vignette_extension <- function(ext) { + valid_exts <- c("Rmd", "rmd", "qmd") + valid_exts_cli <- cli::cli_vec( + valid_exts, + style = list("vec-last" = ", or ") + ) + if (! ext %in% valid_exts) { + ui_abort(c( + "Invalid file extension: {.val {ext}}", + "usethis can only create a vignette or article with one of these + extensions: {.val {valid_exts_cli}}." + )) + + } +} + +get_vignette_extension <- function(name) { + ext <- path_ext(name) + if (nzchar(ext)) { + check_vignette_extension(ext) + } else { + ext <- "Rmd" + } + ext +} diff --git a/inst/templates/article.qmd b/inst/templates/article.qmd new file mode 100644 index 000000000..a4f4ac1fa --- /dev/null +++ b/inst/templates/article.qmd @@ -0,0 +1,12 @@ +--- +title: "{{{ vignette_title }}}" +knitr: + opts_chunk: + collapse: true + comment: '#>' +--- + +```{r} +#| label: setup +library({{Package}}) +``` diff --git a/inst/templates/vignette.qmd b/inst/templates/vignette.qmd new file mode 100644 index 000000000..e46284de0 --- /dev/null +++ b/inst/templates/vignette.qmd @@ -0,0 +1,16 @@ +--- +title: "{{{ vignette_title }}}" +vignette: > + %\VignetteIndexEntry{{{ braced_vignette_title }}} + %\VignetteEngine{quarto::html} + %\VignetteEncoding{UTF-8} +knitr: + opts_chunk: + collapse: true + comment: '#>' +--- + +```{r} +#| label: setup +library({{Package}}) +``` diff --git a/man/use_vignette.Rd b/man/use_vignette.Rd index 8c5fa70fa..dfd7dac07 100644 --- a/man/use_vignette.Rd +++ b/man/use_vignette.Rd @@ -5,15 +5,20 @@ \alias{use_article} \title{Create a vignette or article} \usage{ -use_vignette(name, title = name) +use_vignette(name, title = NULL) use_article(name, title = name) } \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{name}{File name to use for new vignette. Should consist only of +numbers, letters, \verb{_} and \code{-}. Lower case is recommended. Can include the +\code{".Rmd"} or \code{".qmd"} file extension, which also dictates whether to place +an R Markdown or Quarto vignette. R Markdown (\code{".Rmd"}) is the current +default, but it is anticipated that Quarto (\code{".qmd"}) will become the +default in the future.} -\item{title}{The title of the vignette.} +\item{title}{The title of the vignette. If not provided, a title is generated +from \code{name}.} } \description{ Creates a new vignette or article in \verb{vignettes/}. Articles are a special @@ -28,15 +33,25 @@ automatically). \item Adds \code{inst/doc} to \code{.gitignore} so built vignettes aren't tracked. \item Adds \verb{vignettes/*.html} and \verb{vignettes/*.R} to \code{.gitignore} so you never accidentally track rendered vignettes. +\item For \verb{*.qmd}, adds Quarto-related patterns to \code{.gitignore} and +\code{.Rbuildignore}. } } \examples{ \dontrun{ use_vignette("how-to-do-stuff", "How to do stuff") +use_vignette("r-markdown-is-classic.Rmd", "R Markdown is classic") +use_vignette("quarto-is-cool.qmd", "Quarto is cool") } } \seealso{ -The \href{https://r-pkgs.org/vignettes.html}{vignettes chapter} of -\href{https://r-pkgs.org}{R Packages}. +\itemize{ +\item The \href{https://r-pkgs.org/vignettes.html}{vignettes chapter} of +\href{https://r-pkgs.org}{R Packages} +\item The pkgdown vignette on Quarto: +\code{vignette("quarto", package = "pkgdown")} +\item The quarto (as in the R package) vignette on HTML vignettes: +\code{vignette("hello", package = "quarto")} +} } diff --git a/tests/testthat/test-vignette.R b/tests/testthat/test-vignette.R index 82b7a4690..c580faa7f 100644 --- a/tests/testthat/test-vignette.R +++ b/tests/testthat/test-vignette.R @@ -15,7 +15,7 @@ test_that("use_vignette() gives useful errors", { }) }) -test_that("use_vignette() does the promised setup", { +test_that("use_vignette() does the promised setup, Rmd", { create_local_package() use_vignette("name", "title") @@ -32,6 +32,43 @@ test_that("use_vignette() does the promised setup", { expect_identical(proj_desc()$get_field("VignetteBuilder"), "knitr") }) +test_that("use_vignette() does the promised setup, qmd", { + create_local_package() + local_check_installed() + + use_vignette("name.qmd", "title") + expect_proj_file("vignettes/name.qmd") + + ignores <- read_utf8(proj_path(".gitignore")) + expect_true("inst/doc" %in% ignores) + + deps <- proj_deps() + expect_true( + all(c("knitr", "quarto") %in% deps$package[deps$type == "Suggests"]) + ) + + expect_identical(proj_desc()$get_field("VignetteBuilder"), "quarto") +}) + +test_that("use_vignette() does the promised setup, mix of Rmd and qmd", { + create_local_package() + local_check_installed() + + use_vignette("older-vignette", "older Rmd vignette") + use_vignette("newer-vignette.qmd", "newer qmd vignette") + expect_proj_file("vignettes/older-vignette.Rmd") + expect_proj_file("vignettes/newer-vignette.qmd") + + deps <- proj_deps() + expect_true( + all(c("knitr", "quarto", "rmarkdown") %in% deps$package[deps$type == "Suggests"]) + ) + + vignette_builder <- proj_desc()$get_field("VignetteBuilder") + expect_match(vignette_builder, "knitr", fixed = TRUE) + expect_match(vignette_builder, "quarto", fixed = TRUE) +}) + # use_article ------------------------------------------------------------- test_that("use_article goes in article subdirectory", {