From 565726d79c815d947b74d59fc05403ec6b1e3cb4 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Mon, 10 Jun 2024 17:57:32 -0500 Subject: [PATCH 01/49] WIP --- DESCRIPTION | 7 ++- R/build-quarto-articles.R | 77 ++++++++++++++++++++++++++ R/package.R | 3 + R/utils.R | 4 ++ inst/BS5/templates/content-quarto.html | 31 +++++++++++ inst/quarto/template.html | 61 ++++++++++++++++++++ vignettes/_quarto.yaml | 2 + vignettes/quarto.qmd | 61 ++++++++++++++++++++ 8 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 R/build-quarto-articles.R create mode 100644 inst/BS5/templates/content-quarto.html create mode 100644 inst/quarto/template.html create mode 100644 vignettes/_quarto.yaml create mode 100644 vignettes/quarto.qmd diff --git a/DESCRIPTION b/DESCRIPTION index 90a057567..258e5aa03 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -54,6 +54,7 @@ Suggests: magick, methods, pkgload (>= 1.0.2), + quarto, rsconnect, rstudioapi, rticles, @@ -61,7 +62,10 @@ Suggests: testthat (>= 3.1.3), tools VignetteBuilder: - knitr + knitr, + quarto +Remotes: + r-lib/evaluate Config/Needs/website: usethis, servr Config/potools/style: explicit Config/testthat/edition: 3 @@ -71,4 +75,3 @@ Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.1 SystemRequirements: pandoc -Remotes: r-lib/evaluate diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R new file mode 100644 index 000000000..e0f49ef90 --- /dev/null +++ b/R/build-quarto-articles.R @@ -0,0 +1,77 @@ +build_quarto_articles <- function(pkg = ".") { + check_required("quarto") + pkg <- as_pkgdown(pkg) + + metadata_path <- withr::local_tempfile( + fileext = ".yml", + pattern = "pkgdown-quarto-metadata-" + ) + metadata <- quarto_metadata(pkg) + yaml::write_yaml( + metadata, + metadata_path, + handlers = list(logical = yaml::verbatim_logical) + ) + + output_dir <- withr::local_tempdir("pkgdown-quarto-") + + project_path <- path(pkg$src_path, "vignettes", "_quarto.yaml") + if (!file_exists(project_path)) { + file_create(project_path) + withr::defer(file_delete(project_path)) + } + + output_dir <- "~/Desktop/test" + quarto::quarto_render( + path(pkg$src_path, "vignettes"), + metadata_file = metadata_path, + # quarto_args = c("--output-dir", output_dir), + as_job = FALSE + ) + + qmds <- dir_ls(path(pkg$src_path, "vignettes"), glob = "*.qmd") + htmls <- path_ext_set(qmds, "html") + parsed <- lapply(htmls, quarto_parse_rendered) + +} + +quarto_metadata <- function(pkg) { + list( + lang = pkg$lang, + format = list( + html = list( + template = system_file("quarto", "template.html", package = "pkgdown"), + minimal = TRUE, + theme = "none", + `highlight-style` = "none", + `html-math-method` = config_math_rendering(pkg), + `embed-resources` = FALSE, + toc = FALSE # pkgdown generates with js + ) + ) + ) +} + +quarto_parse_rendered <- function(path) { + html <- xml2::read_html(path) + + meta_div <- xml2::xml_find_first(html, "//body/div[@class='meta']") + + list( + pagetitle = xpath_text(html, "//head/title"), + includes = list( + head = as.character(xpath_xml(html, "//head/script|//meta/link")), + before = xpath_contents(html, "//body/div[@class='includes-before']"), + after = xpath_contents(html, "//body/div[@class='includes-after']"), + style = xpath_text(html, "//head/style") + ), + meta = list( + title = xpath_content(meta_div, "./h1"), + subtitle = xpath_contents(meta_div, "./p[@class='subtitle']"), + author = xpath_contents(meta_div, "./p[@class='author']"), + date = xpath_contents(meta_div, "./p[@class='date']"), + abstract = xpath_contents(meta_div, "./div[@class='abstract']") + ), + body = xpath_contents(html, "//main") + ) +} diff --git a/R/package.R b/R/package.R index ce80d7b88..ceacc53a7 100644 --- a/R/package.R +++ b/R/package.R @@ -319,6 +319,9 @@ package_vignettes <- function(path = ".") { vig_path <- vig_path[!grepl("^_", path_file(vig_path))] vig_path <- vig_path[!grepl("^tutorials", path_dir(vig_path))] + # quarto::quarto_inspect("vignettes/quarto.qmd")$formats$html$pandoc$`output-file` + # quarto::quarto_inspect("vignettes/quarto.qmd")$formats$html$metadata + yaml <- purrr::map(path(base, vig_path), rmarkdown::yaml_front_matter) title <- purrr::map_chr(yaml, list("title", 1), .default = "UNKNOWN TITLE") desc <- purrr::map_chr(yaml, list("description", 1), .default = NA_character_) diff --git a/R/utils.R b/R/utils.R index bb33a495a..47e16d5ab 100644 --- a/R/utils.R +++ b/R/utils.R @@ -187,6 +187,10 @@ xpath_xml <- function(x, xpath) { x <- xml2::xml_find_all(x, xpath) structure(x, class = c("pkgdown_xml", class(x))) } +xpath_contents <- function(x, xpath) { + x <- xml2::xml_find_all(x, xpath) + paste0(as.character(xml2::xml_contents(x), options = c("format", "no_declaration")), collapse = "") +} xpath_attr <- function(x, xpath, attr) { gsub("\r", "", xml2::xml_attr(xml2::xml_find_all(x, xpath), attr), fixed = TRUE) } diff --git a/inst/BS5/templates/content-quarto.html b/inst/BS5/templates/content-quarto.html new file mode 100644 index 000000000..64fc584e4 --- /dev/null +++ b/inst/BS5/templates/content-quarto.html @@ -0,0 +1,31 @@ +
+
+ + + {{#abtract}}
+

{{#translate}}{{abstract}}{{/translate}}

+ {{{.}}} +
{{/abstract}} + + {{{body}}} +
+ + {{#toc}} + + {{/toc}} +
diff --git a/inst/quarto/template.html b/inst/quarto/template.html new file mode 100644 index 000000000..fc92a990e --- /dev/null +++ b/inst/quarto/template.html @@ -0,0 +1,61 @@ + + + + + $pagetitle$ + + +$for(css)$ + +$endfor$ +$for(header-includes)$ + $header-includes$ +$endfor$ + + + +
+$for(include-before)$ +$include-before$ +$endfor$ +
+ +
+$if(title)$ +

$title$

+$endif$ + + $if(subtitle)$ +

$subtitle$

+$endif$ + +$for(author)$ +

$author$

+$endfor$ + +$if(date)$ +

$date$

+$endif$ + +$if(abstract)$ +
+ $abstract$ +
+$endif$ + +
+ +
+$body$ +
+ +
+$for(include-after)$ +$include-after$ +$endfor$ +
+ + + diff --git a/vignettes/_quarto.yaml b/vignettes/_quarto.yaml new file mode 100644 index 000000000..1f499b51c --- /dev/null +++ b/vignettes/_quarto.yaml @@ -0,0 +1,2 @@ +project: + render: ['*.qmd'] diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd new file mode 100644 index 000000000..c381986a9 --- /dev/null +++ b/vignettes/quarto.qmd @@ -0,0 +1,61 @@ +--- +title: "quarto" +vignette: > + %\VignetteIndexEntry{quarto} + %\VignetteEngine{quarto::html} + %\VignetteEncoding{UTF-8} +knitr: + opts_chunk: + collapse: true + comment: '#>' +--- + +```{r setup} +library(pkgdown) +``` + +pkgdown effectively uses quarto only to generate HTML and then supplies its own CSS and JS. This means that when quarto introduces new features, pkgdown may lag behind in their support. If you're trying out something that doens't work (and isn't mentioned explicitly below), please [file an issue](https://github.com/r-lib/pkgdown/issues) so we can look into it. + +## Limitations + +* pkgdown assumes that you're using [quarto vignette style](https://quarto-dev.github.io/quarto-r/articles/hello.html), or more generally an html format with [`minimal: true`](https://quarto.org/docs/output-formats/html-basics.html#minimal-html). + +* pkgdown provides its own implementations of bootstrap themes, anchor sections, and code copying. The following options are not yet supported. + + ```yaml + # reference popups + citations-hover: true + footnotes-hover: true + crossrefs-hover: true + + # tabsets + # ??? + + # responsive figures + fig-responsive: true + ``` + +* pkgdown ignores includes that are added before and after the body text. This means that you can't use [page layouts](https://quarto.org/docs/output-formats/page-layout.html#article) + +* pkgdown will pass the `lang` setting on to quarto, but the set of available language is not perfectly matched. Learn more in , including how to supply your own translations. + +## Supported features + +The following sections demonstrate a bunch of useful quarto features so that we can make sure that they work. + +### Code + +```{r} +1 + 1 +2 + 2 +``` + +## To do + +* Code annotations +* Reference pop ups +* To do lists +* Figures +* Equations +* Cross-references +* Footnotes From c04dba319196c846e06c4838ffe93c7fd4f12959 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 09:50:55 -0500 Subject: [PATCH 02/49] Complete end-to-end prototype --- R/build-quarto-articles.R | 34 ++++++++++++++++++++------ R/render.R | 2 +- R/utils.R | 9 ++++++- inst/BS5/templates/content-quarto.html | 6 ++--- inst/quarto/template.html | 2 +- vignettes/problem.qmd | 9 +++++++ vignettes/quarto.qmd | 4 ++- 7 files changed, 51 insertions(+), 15 deletions(-) create mode 100644 vignettes/problem.qmd diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index e0f49ef90..0daf4a51f 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -1,4 +1,4 @@ -build_quarto_articles <- function(pkg = ".") { +build_quarto_articles <- function(pkg = ".", quiet = TRUE) { check_required("quarto") pkg <- as_pkgdown(pkg) @@ -21,18 +21,33 @@ build_quarto_articles <- function(pkg = ".") { withr::defer(file_delete(project_path)) } - output_dir <- "~/Desktop/test" + # output_dir <- "~/Desktop/test" quarto::quarto_render( path(pkg$src_path, "vignettes"), metadata_file = metadata_path, - # quarto_args = c("--output-dir", output_dir), + execute_dir = output_dir, + quarto_args = c("--output-dir", output_dir), + quiet = quiet, as_job = FALSE ) - qmds <- dir_ls(path(pkg$src_path, "vignettes"), glob = "*.qmd") - htmls <- path_ext_set(qmds, "html") - parsed <- lapply(htmls, quarto_parse_rendered) + + htmls <- dir_ls(output_dir, glob = "*.html") + out_path <- path("articles", path_rel(htmls, output_dir)) + data <- lapply(htmls, quarto_parse_rendered) + + purrr::walk2(data, out_path, function(data, path) { + render_page(pkg, "quarto", data, path) + }) + resources <- setdiff(dir_ls(output_dir, recurse = TRUE), htmls) + resources <- resources[!is_dir(resources)] + file_copy_to( + src_paths = resources, + dst_paths = path(pkg$dst_path, "articles", path_rel(resources, output_dir)), + src_root = output_dir, + dst_root = pkg$dst_path + ) } quarto_metadata <- function(pkg) { @@ -57,8 +72,11 @@ quarto_parse_rendered <- function(path) { meta_div <- xml2::xml_find_first(html, "//body/div[@class='meta']") + browser() list( - pagetitle = xpath_text(html, "//head/title"), + pagetitle = escape_html(xpath_text(html, "//head/title")), + toc = TRUE, + source = "???", includes = list( head = as.character(xpath_xml(html, "//head/script|//meta/link")), before = xpath_contents(html, "//body/div[@class='includes-before']"), @@ -66,7 +84,7 @@ quarto_parse_rendered <- function(path) { style = xpath_text(html, "//head/style") ), meta = list( - title = xpath_content(meta_div, "./h1"), + title = xpath_contents(meta_div, "./h1"), subtitle = xpath_contents(meta_div, "./p[@class='subtitle']"), author = xpath_contents(meta_div, "./p[@class='author']"), date = xpath_contents(meta_div, "./p[@class='date']"), diff --git a/R/render.R b/R/render.R index 072cdd31d..6e2c3240d 100644 --- a/R/render.R +++ b/R/render.R @@ -23,7 +23,7 @@ render_page <- function(pkg = ".", name, data, path, depth = NULL, quiet = FALSE pkg <- as_pkgdown(pkg) if (is.null(depth)) { - depth <- length(strsplit(path, "/")[[1]]) - 1L + depth <- dir_depth(path) } html <- render_page_html(pkg, name = name, data = data, depth = depth) diff --git a/R/utils.R b/R/utils.R index 47e16d5ab..751d7939d 100644 --- a/R/utils.R +++ b/R/utils.R @@ -3,6 +3,7 @@ up_path <- function(depth) { } dir_depth <- function(x) { + # length(strsplit(path, "/")[[1]]) - 1L purrr::map_int(strsplit(x, ""), function(x) sum(x == "/")) } @@ -189,7 +190,13 @@ xpath_xml <- function(x, xpath) { } xpath_contents <- function(x, xpath) { x <- xml2::xml_find_all(x, xpath) - paste0(as.character(xml2::xml_contents(x), options = c("format", "no_declaration")), collapse = "") + + contents <- xml2::xml_contents(x) + if (length(contents) == 0) { + return(NULL) + } + strings <- as.character(contents, options = c("format", "no_declaration")) + paste0(strings, collapse = "") } xpath_attr <- function(x, xpath, attr) { gsub("\r", "", xml2::xml_attr(xml2::xml_find_all(x, xpath), attr), fixed = TRUE) diff --git a/inst/BS5/templates/content-quarto.html b/inst/BS5/templates/content-quarto.html index 64fc584e4..bf041dd6c 100644 --- a/inst/BS5/templates/content-quarto.html +++ b/inst/BS5/templates/content-quarto.html @@ -1,9 +1,9 @@
- {{#abtract}}
+ {{#abstract}}

{{#translate}}{{abstract}}{{/translate}}

{{{.}}}
{{/abstract}} diff --git a/inst/quarto/template.html b/inst/quarto/template.html index fc92a990e..4d2d021f6 100644 --- a/inst/quarto/template.html +++ b/inst/quarto/template.html @@ -27,7 +27,7 @@

$title$

$endif$ - $if(subtitle)$ +$if(subtitle)$

$subtitle$

$endif$ diff --git a/vignettes/problem.qmd b/vignettes/problem.qmd new file mode 100644 index 000000000..cbef218f1 --- /dev/null +++ b/vignettes/problem.qmd @@ -0,0 +1,9 @@ +--- +title: "problem" +vignette: > + %\VignetteIndexEntry{quarto} + %\VignetteEngine{quarto::html} + %\VignetteEncoding{UTF-8} +--- + +it's diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index c381986a9..66808b889 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -44,10 +44,12 @@ pkgdown effectively uses quarto only to generate HTML and then supplies its own The following sections demonstrate a bunch of useful quarto features so that we can make sure that they work. ### Code - +a ```{r} 1 + 1 2 + 2 + +plot(1:3) ``` ## To do From af13a4d6565fe76578c12d228e602d1d85139830 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 09:57:23 -0500 Subject: [PATCH 03/49] Fix encoding issue --- R/build-quarto-articles.R | 4 +--- vignettes/problem.qmd | 9 --------- 2 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 vignettes/problem.qmd diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 0daf4a51f..cfd0ffbdb 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -68,11 +68,9 @@ quarto_metadata <- function(pkg) { } quarto_parse_rendered <- function(path) { - html <- xml2::read_html(path) - + html <- xml2::read_html(path,encoding = "UTF-8") meta_div <- xml2::xml_find_first(html, "//body/div[@class='meta']") - browser() list( pagetitle = escape_html(xpath_text(html, "//head/title")), toc = TRUE, diff --git a/vignettes/problem.qmd b/vignettes/problem.qmd deleted file mode 100644 index cbef218f1..000000000 --- a/vignettes/problem.qmd +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "problem" -vignette: > - %\VignetteIndexEntry{quarto} - %\VignetteEngine{quarto::html} - %\VignetteEncoding{UTF-8} ---- - -it's From 89c225c7d2c9256015bfbae7731db9396bf169e1 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 10:06:53 -0500 Subject: [PATCH 04/49] Code tidying --- R/build-quarto-articles.R | 10 +++++----- R/render.R | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index cfd0ffbdb..42addf80d 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -13,15 +13,14 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { handlers = list(logical = yaml::verbatim_logical) ) - output_dir <- withr::local_tempdir("pkgdown-quarto-") - + # Need to simulate a project so we can build entire directory project_path <- path(pkg$src_path, "vignettes", "_quarto.yaml") if (!file_exists(project_path)) { file_create(project_path) withr::defer(file_delete(project_path)) } - # output_dir <- "~/Desktop/test" + output_dir <- withr::local_tempdir("pkgdown-quarto-") quarto::quarto_render( path(pkg$src_path, "vignettes"), metadata_file = metadata_path, @@ -30,16 +29,15 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { quiet = quiet, as_job = FALSE ) - htmls <- dir_ls(output_dir, glob = "*.html") out_path <- path("articles", path_rel(htmls, output_dir)) data <- lapply(htmls, quarto_parse_rendered) - purrr::walk2(data, out_path, function(data, path) { render_page(pkg, "quarto", data, path) }) + # Copy resources resources <- setdiff(dir_ls(output_dir, recurse = TRUE), htmls) resources <- resources[!is_dir(resources)] file_copy_to( @@ -48,6 +46,8 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { src_root = output_dir, dst_root = pkg$dst_path ) + + invisible() } quarto_metadata <- function(pkg) { diff --git a/R/render.R b/R/render.R index 6e2c3240d..648330d76 100644 --- a/R/render.R +++ b/R/render.R @@ -64,7 +64,7 @@ render_page_html <- function(pkg, name, data = list(), depth = 0L) { rendered <- render_template(template, components) # Strip trailing whitespace - rendered <- gsub("\\s+\n", "\n", rendered, perl = TRUE) + rendered <- gsub(" +\n", "\n", rendered, perl = TRUE) xml2::read_html(rendered, encoding = "UTF-8") } From a396af6fa2ef15e95ac26119b62dc922e7b16082 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 10:19:01 -0500 Subject: [PATCH 05/49] Start working through formatting --- R/build-quarto-articles.R | 1 + inst/BS5/assets/pkgdown.scss | 13 +++++++++++++ vignettes/quarto.qmd | 26 +++++++++++++++----------- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 42addf80d..262a473f7 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -33,6 +33,7 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { htmls <- dir_ls(output_dir, glob = "*.html") out_path <- path("articles", path_rel(htmls, output_dir)) data <- lapply(htmls, quarto_parse_rendered) + purrr::walk2(data, out_path, function(data, path) { render_page(pkg, "quarto", data, path) }) diff --git a/inst/BS5/assets/pkgdown.scss b/inst/BS5/assets/pkgdown.scss index aa51029ef..13a7715c2 100644 --- a/inst/BS5/assets/pkgdown.scss +++ b/inst/BS5/assets/pkgdown.scss @@ -296,6 +296,14 @@ dt:target + dd { padding-left: 2rem; } +// task-list +ul.task-list{list-style: none;} +ul.task-list li input[type="checkbox"] { + width: 0.8em; + margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ + vertical-align: middle; +} + // orcid badge .orcid { color: #A6CE39; @@ -364,6 +372,11 @@ a[href='#main'] { .lifecycle-experimental, .lifecycle-deprecated { background-color: rgb(253, 128, 8); color: var(--bs-black);} +// From quarto, a convention to get smallcaps +span.smallcaps {font-variant: small-caps;} + + + /* Footnotes ---------------------------------------------------------------- */ a.footnote-ref { diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 66808b889..86abbfe86 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -1,7 +1,7 @@ --- -title: "quarto" +title: quarto vignettes vignette: > - %\VignetteIndexEntry{quarto} + %\VignetteIndexEntry{quarto vignettes} %\VignetteEngine{quarto::html} %\VignetteEncoding{UTF-8} knitr: @@ -29,7 +29,7 @@ pkgdown effectively uses quarto only to generate HTML and then supplies its own crossrefs-hover: true # tabsets - # ??? + tabsets: true # responsive figures fig-responsive: true @@ -43,8 +43,12 @@ pkgdown effectively uses quarto only to generate HTML and then supplies its own The following sections demonstrate a bunch of useful quarto features so that we can make sure that they work. +### Inline formatting + +* [Small caps]{.smallcaps} + ### Code -a + ```{r} 1 + 1 2 + 2 @@ -54,10 +58,10 @@ plot(1:3) ## To do -* Code annotations -* Reference pop ups -* To do lists -* Figures -* Equations -* Cross-references -* Footnotes +* [x] Task/to do lists +* [ ] Code annotations +* [ ] Reference pop ups +* [ ] Figures +* [ ] Equations +* [ ] Cross-references +* [ ] Footnotes From 1646d052aea59735778a2108506caa7e573a6839 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 10:32:57 -0500 Subject: [PATCH 06/49] Figures, equations, and cross-references --- inst/BS5/assets/pkgdown.scss | 118 +++++++++++++++++++++++++++++++++++ vignettes/pitbull.jpg | Bin 0 -> 53524 bytes vignettes/quarto.qmd | 30 ++++++++- vignettes/shar-pei.jpg | Bin 0 -> 52857 bytes 4 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 vignettes/pitbull.jpg create mode 100644 vignettes/shar-pei.jpg diff --git a/inst/BS5/assets/pkgdown.scss b/inst/BS5/assets/pkgdown.scss index 13a7715c2..0f8c852ff 100644 --- a/inst/BS5/assets/pkgdown.scss +++ b/inst/BS5/assets/pkgdown.scss @@ -585,3 +585,121 @@ mark { } } } + +/* Quarto specific ---------------------------------------------------------- */ +// Selectively copied from https://github.com/quarto-dev/quarto-cli/blob/main/src/resources/formats/html/_quarto-rules.scss#L110 + +// layout and figures + +figure.figure { + display: block; +} + +.quarto-layout-panel { + margin-bottom: 1em; +} + +.quarto-layout-panel > figure { + width: 100%; +} +.quarto-layout-panel > figure > figcaption, +.quarto-layout-panel > .panel-caption { + margin-top: 10pt; +} + +.quarto-layout-panel > .table-caption { + margin-top: 0px; +} + +.table-caption p { + margin-bottom: 0.5em; +} + +.quarto-layout-row { + display: flex; + flex-direction: row; + align-items: flex-start; +} +.quarto-layout-valign-top { + align-items: flex-start; +} +.quarto-layout-valign-bottom { + align-items: flex-end; +} +.quarto-layout-valign-center { + align-items: center; +} +.quarto-layout-cell { + position: relative; + margin-right: 20px; +} +.quarto-layout-cell:last-child { + margin-right: 0; +} +.quarto-layout-cell figure, +.quarto-layout-cell > p { + margin: 0.2em; +} +.quarto-layout-cell img { + max-width: 100%; +} +.quarto-layout-cell .html-widget { + width: 100% !important; +} +.quarto-layout-cell div figure p { + margin: 0; +} +.quarto-layout-cell figure { + display: block; + margin-inline-start: 0; + margin-inline-end: 0; +} +.quarto-layout-cell table { + display: inline-table; +} +.quarto-layout-cell-subref figcaption, +figure .quarto-layout-row figure figcaption { + text-align: center; + font-style: italic; +} +.quarto-figure { + position: relative; + margin-bottom: 1em; +} + +.quarto-figure > figure { + width: 100%; + margin-bottom: 0; +} +.quarto-figure-left > figure > p, +.quarto-figure-left > figure > div /* for mermaid and dot diagrams */ { + text-align: left; +} +.quarto-figure-center > figure > p, +.quarto-figure-center > figure > div /* for mermaid and dot diagrams */ { + text-align: center; +} +.quarto-figure-right > figure > p, +.quarto-figure-right > figure > div /* for mermaid and dot diagrams */ { + text-align: right; +} + +.quarto-figure > figure > div.cell-annotation, +.quarto-figure > figure > div code { + text-align: left; /* override align center for code blocks */ +} + +figure > p:empty { + display: none; +} +figure > p:first-child { + margin-top: 0; + margin-bottom: 0; +} + +figure > figcaption.quarto-float-caption-bottom { + margin-bottom: 0.5em; +} +figure > figcaption.quarto-float-caption-top { + margin-top: 0.5em; +} diff --git a/vignettes/pitbull.jpg b/vignettes/pitbull.jpg new file mode 100644 index 0000000000000000000000000000000000000000..13eeeb9829093de6f171e9662388532695f3262d GIT binary patch literal 53524 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 literal 0 HcmV?d00001 diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 86abbfe86..65e3c2bfa 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -56,12 +56,36 @@ The following sections demonstrate a bunch of useful quarto features so that we plot(1:3) ``` +### Figures + +::: {#fig-puppies layout-ncol=2} +![A sketch of a pitbull puppy](pitbull.jpg){#fig-pitbull} + +![A sketch of a sharpei puppy](shar-pei.jpg){#fig-shar-pei} + +Cute puppies +::: + +### Equations +$$ +\frac{\partial \mathrm C}{ \partial \mathrm t } + \frac{1}{2}\sigma^{2} \mathrm S^{2} +\frac{\partial^{2} \mathrm C}{\partial \mathrm C^2} + + \mathrm r \mathrm S \frac{\partial \mathrm C}{\partial \mathrm S}\ = + \mathrm r \mathrm C +$$ {#eq-black-scholes} + +### Cross references + +See @fig-puppies for two cute puppies. + +Black-Scholes (@eq-black-scholes) is a mathematical model that seeks to explain the behavior of financial derivatives, most commonly options. + ## To do * [x] Task/to do lists * [ ] Code annotations * [ ] Reference pop ups -* [ ] Figures -* [ ] Equations -* [ ] Cross-references +* [x] Figures +* [x] Equations +* [x] Cross-references * [ ] Footnotes diff --git a/vignettes/shar-pei.jpg b/vignettes/shar-pei.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98908610d00e4edeefbfc27583f703e8cff2511c GIT binary patch literal 52857 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 R&Hrs~_Fwa}|2+Sj`(Grm$DIHG literal 0 HcmV?d00001 From 2e257de9e3b38c4f58822947c1b689d1c9e4373e Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 10:52:02 -0500 Subject: [PATCH 07/49] Features that need extra deps in head --- R/build-quarto-articles.R | 4 ++-- R/utils.R | 9 +++++++-- inst/BS5/templates/head.html | 1 + vignettes/quarto.qmd | 22 ++++++++++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 262a473f7..8028ae752 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -69,7 +69,7 @@ quarto_metadata <- function(pkg) { } quarto_parse_rendered <- function(path) { - html <- xml2::read_html(path,encoding = "UTF-8") + html <- xml2::read_html(path, encoding = "UTF-8") meta_div <- xml2::xml_find_first(html, "//body/div[@class='meta']") list( @@ -77,7 +77,7 @@ quarto_parse_rendered <- function(path) { toc = TRUE, source = "???", includes = list( - head = as.character(xpath_xml(html, "//head/script|//meta/link")), + head = xml2str(xpath_xml(html, "//head/script|//head/link")), before = xpath_contents(html, "//body/div[@class='includes-before']"), after = xpath_contents(html, "//body/div[@class='includes-after']"), style = xpath_text(html, "//head/style") diff --git a/R/utils.R b/R/utils.R index 751d7939d..ae369f58e 100644 --- a/R/utils.R +++ b/R/utils.R @@ -193,11 +193,16 @@ xpath_contents <- function(x, xpath) { contents <- xml2::xml_contents(x) if (length(contents) == 0) { - return(NULL) + NULL + } else { + xml2str(contents) } - strings <- as.character(contents, options = c("format", "no_declaration")) +} +xml2str <- function(x) { + strings <- as.character(x, options = c("format", "no_declaration")) paste0(strings, collapse = "") } + xpath_attr <- function(x, xpath, attr) { gsub("\r", "", xml2::xml_attr(xml2::xml_find_all(x, xpath), attr), fixed = TRUE) } diff --git a/inst/BS5/templates/head.html b/inst/BS5/templates/head.html index b0c7b6bbe..e1b49079d 100644 --- a/inst/BS5/templates/head.html +++ b/inst/BS5/templates/head.html @@ -17,6 +17,7 @@ {{#uses_katex}}{{/uses_katex}} {{{headdeps}}} +{{#includes}}{{{head}}}{{/includes}} diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 65e3c2bfa..cfb596448 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -74,6 +74,28 @@ $$ \mathrm r \mathrm C $$ {#eq-black-scholes} +### Diagrams + +```{mermaid} +flowchart LR + A[Hard edge] --> B(Round edge) + B --> C{Decision} + C --> D[Result one] + C --> E[Result two] +``` + +### HTML widgets + +```{r, echo=FALSE} +path1 <- tempfile() +writeLines(letters, path1) +path2 <- tempfile() +writeLines(letters[-(10:11)], path2) + +diffviewer::visual_diff(path1, path2) +``` + + ### Cross references See @fig-puppies for two cute puppies. From 2c62d5107223f24fcc1620b4ac6c0ba63fead04a Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 11:00:45 -0500 Subject: [PATCH 08/49] Temporarily create project file if needed --- R/build-quarto-articles.R | 2 +- vignettes/_quarto.yaml | 2 -- vignettes/quarto.qmd | 11 +++++++++++ 3 files changed, 12 insertions(+), 3 deletions(-) delete mode 100644 vignettes/_quarto.yaml diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 8028ae752..854ad9c50 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -16,7 +16,7 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { # Need to simulate a project so we can build entire directory project_path <- path(pkg$src_path, "vignettes", "_quarto.yaml") if (!file_exists(project_path)) { - file_create(project_path) + yaml::write_yaml(list(project = list(render = list("*.qmd"))), project_path) withr::defer(file_delete(project_path)) } diff --git a/vignettes/_quarto.yaml b/vignettes/_quarto.yaml deleted file mode 100644 index 1f499b51c..000000000 --- a/vignettes/_quarto.yaml +++ /dev/null @@ -1,2 +0,0 @@ -project: - render: ['*.qmd'] diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index cfb596448..7eb04573e 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -16,8 +16,19 @@ library(pkgdown) pkgdown effectively uses quarto only to generate HTML and then supplies its own CSS and JS. This means that when quarto introduces new features, pkgdown may lag behind in their support. If you're trying out something that doens't work (and isn't mentioned explicitly below), please [file an issue](https://github.com/r-lib/pkgdown/issues) so we can look into it. +## Operation + +pkgdown turns your articles directory into a quarto project by temporarily adding a `_quarto.yml` to your articles. You can also add your own if you want to control options for all quarto articles. If you do so, and you have a mix of `.qmd` and `.Rmd` files, you'll need to include the following yaml so that RMarkdown can continue to handle the .Rmd files: + +```yaml +project: + render: ['*.qmd'] +``` + ## Limitations +* Only `format: html` is currently supported. + * pkgdown assumes that you're using [quarto vignette style](https://quarto-dev.github.io/quarto-r/articles/hello.html), or more generally an html format with [`minimal: true`](https://quarto.org/docs/output-formats/html-basics.html#minimal-html). * pkgdown provides its own implementations of bootstrap themes, anchor sections, and code copying. The following options are not yet supported. From 926c25dd1e6a60135f991dd8223242555643df2b Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 11:15:36 -0500 Subject: [PATCH 09/49] Manully drop jquery deps --- R/build-quarto-articles.R | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 854ad9c50..270b5b326 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -36,8 +36,10 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { purrr::walk2(data, out_path, function(data, path) { render_page(pkg, "quarto", data, path) + update_html(path(pkg$dst_path, out_path), tweak_quarto_html) }) + # Copy resources resources <- setdiff(dir_ls(output_dir, recurse = TRUE), htmls) resources <- resources[!is_dir(resources)] @@ -59,7 +61,7 @@ quarto_metadata <- function(pkg) { template = system_file("quarto", "template.html", package = "pkgdown"), minimal = TRUE, theme = "none", - `highlight-style` = "none", + # `highlight-style` = "none", `html-math-method` = config_math_rendering(pkg), `embed-resources` = FALSE, toc = FALSE # pkgdown generates with js @@ -72,12 +74,16 @@ quarto_parse_rendered <- function(path) { html <- xml2::read_html(path, encoding = "UTF-8") meta_div <- xml2::xml_find_first(html, "//body/div[@class='meta']") + # Manually drop any jquery deps + head <- xpath_xml(html, "//head/script|//head/link") + head <- head[!grepl("jquery", xml2::xml_attr(head, "src"))] + list( pagetitle = escape_html(xpath_text(html, "//head/title")), toc = TRUE, source = "???", includes = list( - head = xml2str(xpath_xml(html, "//head/script|//head/link")), + head = xml2str(head), before = xpath_contents(html, "//body/div[@class='includes-before']"), after = xpath_contents(html, "//body/div[@class='includes-after']"), style = xpath_text(html, "//head/style") @@ -92,3 +98,11 @@ quarto_parse_rendered <- function(path) { body = xpath_contents(html, "//main") ) } + +tweak_quarto_html <- function(html) { + # If top-level headings use h1, move everything down one level + h1 <- xml2::xml_find_all(html, "//h1") + if (length(h1) > 1) { + tweak_section_levels(html) + } +} From 914d759ae8c5d03ead316f20b949bae4ddd28c78 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 11:44:29 -0500 Subject: [PATCH 10/49] Split out build_rmarkdown_article() once more --- R/build-article.R | 32 ++++++++++++++++++++++++++++---- pkgdown/_pkgdown.yml | 1 + vignettes/quarto.qmd | 1 + 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/R/build-article.R b/R/build-article.R index 84ec3dafd..eae418b85 100644 --- a/R/build-article.R +++ b/R/build-article.R @@ -36,10 +36,34 @@ build_article <- function(name, return(invisible()) } - cli::cli_inform("Reading {src_path(input)}") + build_rmarkdown_article( + pkg = pkg, + input_file = input, + input_path = input_path, + output_file = output_file, + output_path = output_path, + depth = depth, + seed = seed, + new_process = new_process, + pandoc_args = pandoc_args, + quiet = quiet + ) +} + +build_rmarkdown_article <- function(pkg, + input_file, + input_path, + output_file, + output_path, + depth, + seed = NULL, + new_process = TRUE, + pandoc_args = character(), + quiet = TRUE) { + cli::cli_inform("Reading {src_path(input_file)}") digest <- file_digest(output_path) - data <- data_article(pkg, input) + data <- data_article(pkg, input_file) if (data$as_is) { if (identical(data$ext, "html")) { setup <- rmarkdown_setup_custom(pkg, input_path, depth = depth, data = data) @@ -68,7 +92,7 @@ build_article <- function(name, if (new_process) { path <- withCallingHandlers( callr::r_safe(rmarkdown_render_with_seed, args = args, show = !quiet), - error = function(cnd) wrap_rmarkdown_error(cnd, input) + error = function(cnd) wrap_rmarkdown_error(cnd, input_file) ) } else { path <- inject(rmarkdown_render_with_seed(!!!args)) @@ -93,9 +117,9 @@ build_article <- function(name, } invisible(path) - } + data_article <- function(pkg, input, call = caller_env()) { yaml <- rmarkdown::yaml_front_matter(path_abs(input, pkg$src_path)) diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index f47693756..8d092af3b 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -33,6 +33,7 @@ articles: navbar: ~ contents: - customise + - quarto - translations - accessibility - linking diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 7eb04573e..1a1e9171f 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -61,6 +61,7 @@ The following sections demonstrate a bunch of useful quarto features so that we ### Code ```{r} +#| fig.alt: A plot of the numbers 1, 2, and 3 1 + 1 2 + 2 From 4ada20ee66e202ef91ebf247ee5070bbc7fc05e1 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 12:07:03 -0500 Subject: [PATCH 11/49] Fix mermaid styling --- inst/BS5/assets/pkgdown.scss | 18 ++++++++++++++++-- vignettes/quarto.qmd | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/inst/BS5/assets/pkgdown.scss b/inst/BS5/assets/pkgdown.scss index 0f8c852ff..0976066b7 100644 --- a/inst/BS5/assets/pkgdown.scss +++ b/inst/BS5/assets/pkgdown.scss @@ -586,10 +586,9 @@ mark { } } -/* Quarto specific ---------------------------------------------------------- */ +/* Quarto specific =========================================================== */ // Selectively copied from https://github.com/quarto-dev/quarto-cli/blob/main/src/resources/formats/html/_quarto-rules.scss#L110 -// layout and figures figure.figure { display: block; @@ -703,3 +702,18 @@ figure > figcaption.quarto-float-caption-bottom { figure > figcaption.quarto-float-caption-top { margin-top: 0.5em; } + +// mermaid --------------------------------------------------------- + +:root { + --mermaid-bg-color: transparent; + --mermaid-edge-color: var(--bs-secondary); + --mermaid-fg-color: var(--bs-body-color); + --mermaid-fg-color--lighter: RGBA(var(--bs-body-color-rgb), 0.9); + --mermaid-fg-color--lightest: RGBA(var(--bs-body-color-rgb), 0.8); + --mermaid-font-family: var(--bs-body-font-family); + --mermaid-label-bg-color: var(--bs-primary); + --mermaid-label-fg-color: var(--bs-body-color); + --mermaid-node-bg-color: RGBA(var(--bs-primary-rgb), 0.1); + --mermaid-node-fg-color: var(--bs-primary); +} diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 1a1e9171f..e2cca58d4 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -29,6 +29,8 @@ project: * Only `format: html` is currently supported. +* You can't customise mermaid styles using quarto mermaid themes. If you want to change the colours, you'll need to provide your own custom CSS as shown in [the quarto docs](https://quarto.org/docs/authoring/diagrams.html#customizing-mermaid). + * pkgdown assumes that you're using [quarto vignette style](https://quarto-dev.github.io/quarto-r/articles/hello.html), or more generally an html format with [`minimal: true`](https://quarto.org/docs/output-formats/html-basics.html#minimal-html). * pkgdown provides its own implementations of bootstrap themes, anchor sections, and code copying. The following options are not yet supported. From 90b8e6229aebe1b73ac4f8aca037141424acd3ca Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 14:56:53 -0500 Subject: [PATCH 12/49] Plumb up to build_articles() --- R/build-articles.R | 3 ++- R/build-quarto-articles.R | 52 +++++++++++++++++++++++++-------------- R/package.R | 40 ++++++++++++++++++++++++------ R/utils.R | 6 ++++- vignettes/quarto.qmd | 3 +++ 5 files changed, 76 insertions(+), 28 deletions(-) diff --git a/R/build-articles.R b/R/build-articles.R index 6f095bb63..77ce72b41 100644 --- a/R/build-articles.R +++ b/R/build-articles.R @@ -212,13 +212,14 @@ build_articles <- function(pkg = ".", build_articles_index(pkg) unwrap_purrr_error(purrr::walk( - pkg$vignettes$name, + pkg$vignettes$name[pkg$vignettes$type == "rmd"], build_article, pkg = pkg, lazy = lazy, seed = seed, quiet = quiet )) + build_quarto_articles(pkg, quiet = quiet) preview_site(pkg, "articles", preview = preview) } diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 270b5b326..222ac2768 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -2,18 +2,25 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { check_required("quarto") pkg <- as_pkgdown(pkg) + qmds <- pkg$vignettes[pkg$vignettes$type == "qmd", ] + if (nrow(qmds) == 0) { + return() + } + qmds$output <- path_rel(qmds$file_out, "articles") + + for (file in qmds$file_in) { + cli::cli_inform("Reading {src_path(file)}") + } + old_digest <- purrr::map_chr(path(pkg$dst_path, qmds$file_out), file_digest) + + # Override default quarto format metadata_path <- withr::local_tempfile( fileext = ".yml", pattern = "pkgdown-quarto-metadata-" ) - metadata <- quarto_metadata(pkg) - yaml::write_yaml( - metadata, - metadata_path, - handlers = list(logical = yaml::verbatim_logical) - ) + write_yaml(quarto_format(pkg), metadata_path) - # Need to simulate a project so we can build entire directory + # If needed, temporarily make a quarto project so we can build entire dir project_path <- path(pkg$src_path, "vignettes", "_quarto.yaml") if (!file_exists(project_path)) { yaml::write_yaml(list(project = list(render = list("*.qmd"))), project_path) @@ -30,18 +37,26 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { as_job = FALSE ) - htmls <- dir_ls(output_dir, glob = "*.html") - out_path <- path("articles", path_rel(htmls, output_dir)) - data <- lapply(htmls, quarto_parse_rendered) - - purrr::walk2(data, out_path, function(data, path) { - render_page(pkg, "quarto", data, path) - update_html(path(pkg$dst_path, out_path), tweak_quarto_html) + # Read generated data from quarto template and render into pkgdown template + purrr::walk2(qmds$file_in, qmds$file_out, function(input_file, output_file) { + built_path <- path(output_dir, path_rel(output_file, "articles")) + data <- data_quarto_article(pkg, built_path, input_file) + render_page(pkg, "quarto", data, output_file, quiet = TRUE) + update_html(path(pkg$dst_path, output_file), tweak_quarto_html) }) + # Report on which files have changed + new_digest <- purrr::map_chr(path(pkg$dst_path, qmds$file_out), file_digest) + changed <- new_digest != old_digest + for (file in qmds$file_out[changed]) { + writing_file(path_rel(file, pkg$dst_path), file) + } # Copy resources - resources <- setdiff(dir_ls(output_dir, recurse = TRUE), htmls) + resources <- setdiff( + dir_ls(output_dir, recurse = TRUE), + path(output_dir, qmds$output) + ) resources <- resources[!is_dir(resources)] file_copy_to( src_paths = resources, @@ -53,7 +68,7 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { invisible() } -quarto_metadata <- function(pkg) { +quarto_format <- function(pkg) { list( lang = pkg$lang, format = list( @@ -61,7 +76,6 @@ quarto_metadata <- function(pkg) { template = system_file("quarto", "template.html", package = "pkgdown"), minimal = TRUE, theme = "none", - # `highlight-style` = "none", `html-math-method` = config_math_rendering(pkg), `embed-resources` = FALSE, toc = FALSE # pkgdown generates with js @@ -70,7 +84,7 @@ quarto_metadata <- function(pkg) { ) } -quarto_parse_rendered <- function(path) { +data_quarto_article <- function(pkg, path, input_path) { html <- xml2::read_html(path, encoding = "UTF-8") meta_div <- xml2::xml_find_first(html, "//body/div[@class='meta']") @@ -81,7 +95,7 @@ quarto_parse_rendered <- function(path) { list( pagetitle = escape_html(xpath_text(html, "//head/title")), toc = TRUE, - source = "???", + source = repo_source(pkg, input_path), includes = list( head = xml2str(head), before = xpath_contents(html, "//body/div[@class='includes-before']"), diff --git a/R/package.R b/R/package.R index ceacc53a7..e5252a9e6 100644 --- a/R/package.R +++ b/R/package.R @@ -319,14 +319,12 @@ package_vignettes <- function(path = ".") { vig_path <- vig_path[!grepl("^_", path_file(vig_path))] vig_path <- vig_path[!grepl("^tutorials", path_dir(vig_path))] - # quarto::quarto_inspect("vignettes/quarto.qmd")$formats$html$pandoc$`output-file` - # quarto::quarto_inspect("vignettes/quarto.qmd")$formats$html$metadata + type <- tolower(path_ext(vig_path)) - yaml <- purrr::map(path(base, vig_path), rmarkdown::yaml_front_matter) - title <- purrr::map_chr(yaml, list("title", 1), .default = "UNKNOWN TITLE") - desc <- purrr::map_chr(yaml, list("description", 1), .default = NA_character_) - ext <- purrr::map_chr(yaml, c("pkgdown", "extension"), .default = "html") - title[ext == "pdf"] <- paste0(title[ext == "pdf"], " (PDF)") + meta <- purrr::map(path(base, vig_path), article_metadata) + title <- purrr::map_chr(meta, "title") + desc <- purrr::map_chr(meta, "desc") + ext <- purrr::map_chr(meta, "ext") # Vignettes will be written to /articles/ with path relative to vignettes/ # *except* for vignettes in vignettes/articles, which are moved up a level @@ -339,6 +337,7 @@ package_vignettes <- function(path = ".") { out <- tibble::tibble( name = as.character(path_ext_remove(vig_path)), + type = type, file_in = as.character(file_in), file_out = as.character(file_out), title = title, @@ -348,6 +347,33 @@ package_vignettes <- function(path = ".") { out[order(path_file(out$file_out)), ] } +article_metadata <- function(path) { + if (path_ext(path) == "qmd") { + inspect <- quarto::quarto_inspect(path) + meta <- inspect$formats$html$metadata + + out <- list( + title = meta$title %||% "UNKNOWN TITLE", + desc = meta$description %||% NA_character_, + # inspect$formats$html$pandoc$`output-file` + ext = "html" + ) + } else { + yaml <- rmarkdown::yaml_front_matter(path) + out <- list( + title = yaml$title[[1]] %||% "UNKNOWN TITLE", + desc = yaml$description[[1]] %||% NA_character_, + ext = yaml$pkgdown$extension %||% "html" + ) + } + + if (out$ext == "pdf") { + out$title <- paste0(out$title, " (PDF)") + } + + out +} + find_template_config <- function(package, bs_version = NULL, error_call = caller_env()) { diff --git a/R/utils.R b/R/utils.R index ae369f58e..117dfd019 100644 --- a/R/utils.R +++ b/R/utils.R @@ -179,7 +179,11 @@ print.print_yaml <- function(x, ...) { } write_yaml <- function(x, path) { - write_lines(yaml::as.yaml(x), path = path) + yaml::write_yaml( + x, + path, + handlers = list(logical = yaml::verbatim_logical) + ) } # Helpers for testing ----------------------------------------------------- diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index e2cca58d4..8b1adffd8 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -1,5 +1,8 @@ --- title: quarto vignettes +description: > + Learn how quarto vignettes work with pkgdown, including currently supported + features and known limitations. vignette: > %\VignetteIndexEntry{quarto vignettes} %\VignetteEngine{quarto::html} From 2fd27b6c50ca8f55268498cb5403c2b13ba95375 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 15:00:55 -0500 Subject: [PATCH 13/49] Update call --- R/build-article.R | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/R/build-article.R b/R/build-article.R index eae418b85..4d77ae5cc 100644 --- a/R/build-article.R +++ b/R/build-article.R @@ -59,11 +59,12 @@ build_rmarkdown_article <- function(pkg, seed = NULL, new_process = TRUE, pandoc_args = character(), - quiet = TRUE) { + quiet = TRUE, + call = caller_env() ) { cli::cli_inform("Reading {src_path(input_file)}") digest <- file_digest(output_path) - data <- data_article(pkg, input_file) + data <- data_article(pkg, input_file, call = call) if (data$as_is) { if (identical(data$ext, "html")) { setup <- rmarkdown_setup_custom(pkg, input_path, depth = depth, data = data) @@ -92,7 +93,7 @@ build_rmarkdown_article <- function(pkg, if (new_process) { path <- withCallingHandlers( callr::r_safe(rmarkdown_render_with_seed, args = args, show = !quiet), - error = function(cnd) wrap_rmarkdown_error(cnd, input_file) + error = function(cnd) wrap_rmarkdown_error(cnd, input_file, call) ) } else { path <- inject(rmarkdown_render_with_seed(!!!args)) From 3681ef946d8c4a986333abe1b326fa9bb8e518af Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 15:36:31 -0500 Subject: [PATCH 14/49] Fixes --- R/build-quarto-articles.R | 8 +++----- R/render.R | 4 +--- vignettes/quarto.qmd | 5 +++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 222ac2768..4f3968b7d 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -6,7 +6,6 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { if (nrow(qmds) == 0) { return() } - qmds$output <- path_rel(qmds$file_out, "articles") for (file in qmds$file_in) { cli::cli_inform("Reading {src_path(file)}") @@ -49,15 +48,14 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { new_digest <- purrr::map_chr(path(pkg$dst_path, qmds$file_out), file_digest) changed <- new_digest != old_digest for (file in qmds$file_out[changed]) { - writing_file(path_rel(file, pkg$dst_path), file) + writing_file(path(pkg$dst_path, file), file) } # Copy resources resources <- setdiff( - dir_ls(output_dir, recurse = TRUE), - path(output_dir, qmds$output) + dir_ls(output_dir, recurse = TRUE, type = "file"), + path(output_dir, path_rel(qmds$file_out, "articles")) ) - resources <- resources[!is_dir(resources)] file_copy_to( src_paths = resources, dst_paths = path(pkg$dst_path, "articles", path_rel(resources, output_dir)), diff --git a/R/render.R b/R/render.R index 648330d76..51fad944f 100644 --- a/R/render.R +++ b/R/render.R @@ -234,9 +234,7 @@ write_if_different <- function(pkg, contents, path, quiet = FALSE, check = TRUE) full_path <- path_abs(path, start = pkg$dst_path) if (check && !made_by_pkgdown(full_path)) { - if (!quiet) { - cli::cli_inform("Skipping {.file {path}}: not generated by pkgdown") - } + cli::cli_inform("Skipping {.file {path}}: not generated by pkgdown") return(FALSE) } diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 8b1adffd8..4579f8a82 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -104,9 +104,10 @@ flowchart LR ### HTML widgets ```{r, echo=FALSE} -path1 <- tempfile() +dir <- tempdir() +path1 <- file.path(dir, "a.txt") writeLines(letters, path1) -path2 <- tempfile() +path2 <- file.path(dir, "b.txt") writeLines(letters[-(10:11)], path2) diffviewer::visual_diff(path1, path2) From 9dd3ac3b92e396c77362521ed9ce40b9f4883596 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 15:38:27 -0500 Subject: [PATCH 15/49] Revert accidental remotes change --- DESCRIPTION | 2 -- 1 file changed, 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index fd683bd3f..8c9db6df1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -64,8 +64,6 @@ Suggests: VignetteBuilder: knitr, quarto -Remotes: - r-lib/evaluate Config/Needs/website: usethis, servr Config/potools/style: explicit Config/testthat/edition: 3 From cb7ec92b83edb737df2eb054c70ca726f3fc4792 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 16:16:39 -0500 Subject: [PATCH 16/49] Organise pkgdown.scss a bit better --- inst/BS5/assets/pkgdown.scss | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/inst/BS5/assets/pkgdown.scss b/inst/BS5/assets/pkgdown.scss index 0976066b7..86d40cc6b 100644 --- a/inst/BS5/assets/pkgdown.scss +++ b/inst/BS5/assets/pkgdown.scss @@ -296,14 +296,6 @@ dt:target + dd { padding-left: 2rem; } -// task-list -ul.task-list{list-style: none;} -ul.task-list li input[type="checkbox"] { - width: 0.8em; - margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ - vertical-align: middle; -} - // orcid badge .orcid { color: #A6CE39; @@ -372,11 +364,6 @@ a[href='#main'] { .lifecycle-experimental, .lifecycle-deprecated { background-color: rgb(253, 128, 8); color: var(--bs-black);} -// From quarto, a convention to get smallcaps -span.smallcaps {font-variant: small-caps;} - - - /* Footnotes ---------------------------------------------------------------- */ a.footnote-ref { @@ -589,6 +576,18 @@ mark { /* Quarto specific =========================================================== */ // Selectively copied from https://github.com/quarto-dev/quarto-cli/blob/main/src/resources/formats/html/_quarto-rules.scss#L110 +span.smallcaps {font-variant: small-caps;} + +// task-list -------------------------------------------------------------- + +ul.task-list{list-style: none;} +ul.task-list li input[type="checkbox"] { + width: 0.8em; + margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ + vertical-align: middle; +} + +// Figure and layout -------------------------------------------------------- figure.figure { display: block; From 68f128697645318cee3df95f4708be033b49dc2f Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 17:03:52 -0500 Subject: [PATCH 17/49] Add support for more features --- R/build-quarto-articles.R | 2 ++ R/tweak-tags.R | 2 +- inst/BS5/templates/content-quarto.html | 5 ++++ vignettes/quarto.qmd | 35 +++++++++++++++++++++++--- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 4f3968b7d..b520d38fa 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -76,6 +76,8 @@ quarto_format <- function(pkg) { theme = "none", `html-math-method` = config_math_rendering(pkg), `embed-resources` = FALSE, + `citations-hover` = TRUE, + `link-citations` = TRUE, toc = FALSE # pkgdown generates with js ) ) diff --git a/R/tweak-tags.R b/R/tweak-tags.R index d6a962b39..064e2e054 100644 --- a/R/tweak-tags.R +++ b/R/tweak-tags.R @@ -156,7 +156,7 @@ tweak_tables <- function(html) { # from https://github.com/rstudio/bookdown/blob/ed31991df3bb826b453f9f50fb43c66508822a2d/R/bs4_book.R#L307 tweak_footnotes <- function(html) { - container <- xml2::xml_find_all(html, ".//div[contains(@class, 'footnotes')]") + container <- xml2::xml_find_all(html, ".//div[contains(@class, 'footnotes')]|.//section[contains(@class, 'footnotes')]") if (length(container) != 1) { return() } diff --git a/inst/BS5/templates/content-quarto.html b/inst/BS5/templates/content-quarto.html index bf041dd6c..fb6134547 100644 --- a/inst/BS5/templates/content-quarto.html +++ b/inst/BS5/templates/content-quarto.html @@ -18,7 +18,12 @@

{{{title}}}

{{{.}}}
{{/abstract}} + {{#includes}}{{{before}}}{{/includes}} + {{{body}}} + + {{#includes}}{{{after}}}{{/includes}} +
{{#toc}} diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 4579f8a82..88d2c1fe6 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -11,6 +11,8 @@ knitr: opts_chunk: collapse: true comment: '#>' +bibliography: test/jss.bib + --- ```{r setup} @@ -32,6 +34,8 @@ project: * Only `format: html` is currently supported. +* Callouts are not currently supported (). + * You can't customise mermaid styles using quarto mermaid themes. If you want to change the colours, you'll need to provide your own custom CSS as shown in [the quarto docs](https://quarto.org/docs/authoring/diagrams.html#customizing-mermaid). * pkgdown assumes that you're using [quarto vignette style](https://quarto-dev.github.io/quarto-r/articles/hello.html), or more generally an html format with [`minimal: true`](https://quarto.org/docs/output-formats/html-basics.html#minimal-html). @@ -63,6 +67,14 @@ The following sections demonstrate a bunch of useful quarto features so that we * [Small caps]{.smallcaps} +* Keyboard shortcut: {{< kbd Shift-Ctrl-P >}} + +* Here is a footnote reference[^1] + +* Citation: @JSSv059i10 + +[^1]: And here is the footnote. + ### Code ```{r} @@ -73,6 +85,20 @@ The following sections demonstrate a bunch of useful quarto features so that we plot(1:3) ``` +With annotations: + +```r +library(tidyverse) +library(palmerpenguins) +penguins |> # <1> + mutate( # <2> + bill_ratio = bill_depth_mm / bill_length_mm, # <2> + bill_area = bill_depth_mm * bill_length_mm # <2> + ) # <2> +``` +1. Take `penguins`, and then, +2. add new columns for the bill ratio and bill area. + ### Figures ::: {#fig-puppies layout-ncol=2} @@ -122,10 +148,13 @@ Black-Scholes (@eq-black-scholes) is a mathematical model that seeks to explain ## To do -* [x] Task/to do lists * [ ] Code annotations -* [ ] Reference pop ups +* [x] Citations +* [x] Task/to do lists * [x] Figures * [x] Equations * [x] Cross-references -* [ ] Footnotes +* [x] Footnotes +* [x] Callouts + +## References From fa30ca7102cdc741ae8ce6078024f4b397b6ce7e Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Tue, 11 Jun 2024 17:08:01 -0500 Subject: [PATCH 18/49] Thinking about tomorrow's todos --- vignettes/quarto.qmd | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 88d2c1fe6..5be30936c 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -36,26 +36,9 @@ project: * Callouts are not currently supported (). -* You can't customise mermaid styles using quarto mermaid themes. If you want to change the colours, you'll need to provide your own custom CSS as shown in [the quarto docs](https://quarto.org/docs/authoring/diagrams.html#customizing-mermaid). +* pkgdown assumes that you're using [quarto vignette style](https://quarto-dev.github.io/quarto-r/articles/hello.html), or more generally an html format with [`minimal: true`](https://quarto.org/docs/output-formats/html-basics.html#minimal-html). Specifically, only HTML vignettes are currently supported. -* pkgdown assumes that you're using [quarto vignette style](https://quarto-dev.github.io/quarto-r/articles/hello.html), or more generally an html format with [`minimal: true`](https://quarto.org/docs/output-formats/html-basics.html#minimal-html). - -* pkgdown provides its own implementations of bootstrap themes, anchor sections, and code copying. The following options are not yet supported. - - ```yaml - # reference popups - citations-hover: true - footnotes-hover: true - crossrefs-hover: true - - # tabsets - tabsets: true - - # responsive figures - fig-responsive: true - ``` - -* pkgdown ignores includes that are added before and after the body text. This means that you can't use [page layouts](https://quarto.org/docs/output-formats/page-layout.html#article) +* You can't customise mermaid styles with quarto mermaid themes. If you want to change the colours, you'll need to provide your own custom CSS as shown in [the quarto docs](https://quarto.org/docs/authoring/diagrams.html#customizing-mermaid). * pkgdown will pass the `lang` setting on to quarto, but the set of available language is not perfectly matched. Learn more in , including how to supply your own translations. @@ -149,6 +132,7 @@ Black-Scholes (@eq-black-scholes) is a mathematical model that seeks to explain ## To do * [ ] Code annotations +* [ ] Tabsets * [x] Citations * [x] Task/to do lists * [x] Figures From 658f471d087c4aff9d4fb8412372a1f3753bcd90 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 07:34:36 -0500 Subject: [PATCH 19/49] Install quarto --- .github/workflows/pkgdown.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index b37ac1ff3..2ce13cecf 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -24,9 +24,14 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-pandoc@v2 + - name: Set up Quarto + uses: quarto-dev/quarto-actions/setup@v2 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tinytex: true - - uses: r-lib/actions/setup-tinytex@v2 + - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: From 64cfe4cccfccb4c56f41aa500c87a0c868e56ad0 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 08:06:07 -0500 Subject: [PATCH 20/49] Install quarto in more places --- .github/workflows/R-CMD-check.yaml | 9 ++++++++- .github/workflows/netlify.yaml | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index d9fced246..fda4d83da 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -44,7 +44,14 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - - uses: r-lib/actions/setup-r@v2 + - name: Set up Quarto + uses: quarto-dev/quarto-actions/setup@v2 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tinytex: true + + - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} diff --git a/.github/workflows/netlify.yaml b/.github/workflows/netlify.yaml index d9994b1c3..a85bbe130 100644 --- a/.github/workflows/netlify.yaml +++ b/.github/workflows/netlify.yaml @@ -16,6 +16,13 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 + - name: Set up Quarto + uses: quarto-dev/quarto-actions/setup@v2 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tinytex: true + - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true From 3831ccff1c654e57b0dad88968da0e9ab4bdbe66 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 08:07:56 -0500 Subject: [PATCH 21/49] Ignore quarto working directory --- .Rbuildignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.Rbuildignore b/.Rbuildignore index dd8e33561..620b2328e 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -20,3 +20,4 @@ ^CRAN-SUBMISSION$ ^tools$ ^\.lintr.R$ +^vignettes/\.quarto$ From 1f68636ad0cd04b613f532f88cfe0db38e0e0c48 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 08:21:28 -0500 Subject: [PATCH 22/49] Separate out heavy test features to keep vignettes light --- vignettes/quarto.qmd | 46 +------------------------ vignettes/test/quarto-features.qmd | 54 ++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 45 deletions(-) create mode 100644 vignettes/test/quarto-features.qmd diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 5be30936c..810576939 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -11,8 +11,6 @@ knitr: opts_chunk: collapse: true comment: '#>' -bibliography: test/jss.bib - --- ```{r setup} @@ -44,18 +42,14 @@ project: ## Supported features -The following sections demonstrate a bunch of useful quarto features so that we can make sure that they work. +The following sections demonstrate a bunch of useful quarto features so that we can make sure that they work. ### Inline formatting * [Small caps]{.smallcaps} -* Keyboard shortcut: {{< kbd Shift-Ctrl-P >}} - * Here is a footnote reference[^1] -* Citation: @JSSv059i10 - [^1]: And here is the footnote. ### Code @@ -68,20 +62,6 @@ The following sections demonstrate a bunch of useful quarto features so that we plot(1:3) ``` -With annotations: - -```r -library(tidyverse) -library(palmerpenguins) -penguins |> # <1> - mutate( # <2> - bill_ratio = bill_depth_mm / bill_length_mm, # <2> - bill_area = bill_depth_mm * bill_length_mm # <2> - ) # <2> -``` -1. Take `penguins`, and then, -2. add new columns for the bill ratio and bill area. - ### Figures ::: {#fig-puppies layout-ncol=2} @@ -100,28 +80,6 @@ $$ \mathrm r \mathrm C $$ {#eq-black-scholes} -### Diagrams - -```{mermaid} -flowchart LR - A[Hard edge] --> B(Round edge) - B --> C{Decision} - C --> D[Result one] - C --> E[Result two] -``` - -### HTML widgets - -```{r, echo=FALSE} -dir <- tempdir() -path1 <- file.path(dir, "a.txt") -writeLines(letters, path1) -path2 <- file.path(dir, "b.txt") -writeLines(letters[-(10:11)], path2) - -diffviewer::visual_diff(path1, path2) -``` - ### Cross references @@ -140,5 +98,3 @@ Black-Scholes (@eq-black-scholes) is a mathematical model that seeks to explain * [x] Cross-references * [x] Footnotes * [x] Callouts - -## References diff --git a/vignettes/test/quarto-features.qmd b/vignettes/test/quarto-features.qmd new file mode 100644 index 000000000..7969c81c8 --- /dev/null +++ b/vignettes/test/quarto-features.qmd @@ -0,0 +1,54 @@ +--- +title: quarto features +knitr: + opts_chunk: + collapse: true + comment: '#>' +bibliography: jss.bib +--- + +## Citations + +* Citation: @JSSv059i10 + +## Code annotations + +```r +library(tidyverse) +library(palmerpenguins) +penguins |> # <1> + mutate( # <2> + bill_ratio = bill_depth_mm / bill_length_mm, # <2> + bill_area = bill_depth_mm * bill_length_mm # <2> + ) # <2> +``` +1. Take `penguins`, and then, +2. add new columns for the bill ratio and bill area. + +## Diagrams + +```{mermaid} +flowchart LR + A[Hard edge] --> B(Round edge) + B --> C{Decision} + C --> D[Result one] + C --> E[Result two] +``` + +## HTML widgets + +```{r, echo=FALSE} +dir <- tempdir() +path1 <- file.path(dir, "a.txt") +writeLines(letters, path1) +path2 <- file.path(dir, "b.txt") +writeLines(letters[-(10:11)], path2) + +diffviewer::visual_diff(path1, path2) +``` + +## Keyboard + +* Keyboard shortcut: {{< kbd Shift-Ctrl-P >}} + +## References From 1bfa1c01fbe3c4ed0de5ac298af7fa6b0c98f5e9 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 08:36:37 -0500 Subject: [PATCH 23/49] Fix space --- .github/workflows/R-CMD-check.yaml | 2 +- .github/workflows/netlify.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index fda4d83da..31f2b4f54 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -51,7 +51,7 @@ jobs: with: tinytex: true - - uses: r-lib/actions/setup-r@v2 + - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} diff --git a/.github/workflows/netlify.yaml b/.github/workflows/netlify.yaml index a85bbe130..ffcbd1edc 100644 --- a/.github/workflows/netlify.yaml +++ b/.github/workflows/netlify.yaml @@ -16,7 +16,7 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - - name: Set up Quarto + - name: Set up Quarto uses: quarto-dev/quarto-actions/setup@v2 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9ee23a0071469ae839f3dec695d7d7d736a98e39 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 08:39:50 -0500 Subject: [PATCH 24/49] Mention GHA setup --- vignettes/quarto.qmd | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 810576939..d6b899365 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -28,6 +28,17 @@ project: render: ['*.qmd'] ``` +### GitHub actions + +Currently, you'll need to manually install Quarto in your GitHub actions. ([Hopefully this will change in the future](https://github.com/r-lib/actions/issues/866)). Add the following lines to install quarto: + +```yaml + - name: Set up Quarto + uses: quarto-dev/quarto-actions/setup@v2 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} +``` + ## Limitations * Only `format: html` is currently supported. From 7d01800e004a3855805e6f4e5d6f04267b39917f Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 09:01:27 -0500 Subject: [PATCH 25/49] Support non-html vignettes --- R/build-quarto-articles.R | 13 ++++++++++--- R/package.R | 5 ++--- vignettes/quarto.qmd | 2 -- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index b520d38fa..08c142f67 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -10,6 +10,8 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { for (file in qmds$file_in) { cli::cli_inform("Reading {src_path(file)}") } + cli::cli_inform("Running {.code quarto render}") + old_digest <- purrr::map_chr(path(pkg$dst_path, qmds$file_out), file_digest) # Override default quarto format @@ -39,9 +41,14 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { # Read generated data from quarto template and render into pkgdown template purrr::walk2(qmds$file_in, qmds$file_out, function(input_file, output_file) { built_path <- path(output_dir, path_rel(output_file, "articles")) - data <- data_quarto_article(pkg, built_path, input_file) - render_page(pkg, "quarto", data, output_file, quiet = TRUE) - update_html(path(pkg$dst_path, output_file), tweak_quarto_html) + if (path_ext(output_file) == "html") { + data <- data_quarto_article(pkg, built_path, input_file) + render_page(pkg, "quarto", data, output_file, quiet = TRUE) + + update_html(path(pkg$dst_path, output_file), tweak_quarto_html) + } else { + file_copy(built_path, path(pkg$dst_path, output_file), overwrite = TRUE) + } }) # Report on which files have changed diff --git a/R/package.R b/R/package.R index e5252a9e6..7b0068722 100644 --- a/R/package.R +++ b/R/package.R @@ -350,13 +350,12 @@ package_vignettes <- function(path = ".") { article_metadata <- function(path) { if (path_ext(path) == "qmd") { inspect <- quarto::quarto_inspect(path) - meta <- inspect$formats$html$metadata + meta <- inspect$formats[[1]]$metadata out <- list( title = meta$title %||% "UNKNOWN TITLE", desc = meta$description %||% NA_character_, - # inspect$formats$html$pandoc$`output-file` - ext = "html" + ext = path_ext(inspect$formats[[1]]$pandoc$`output-file`) %||% "html" ) } else { yaml <- rmarkdown::yaml_front_matter(path) diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index d6b899365..817bdd0aa 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -41,8 +41,6 @@ Currently, you'll need to manually install Quarto in your GitHub actions. ([Hope ## Limitations -* Only `format: html` is currently supported. - * Callouts are not currently supported (). * pkgdown assumes that you're using [quarto vignette style](https://quarto-dev.github.io/quarto-r/articles/hello.html), or more generally an html format with [`minimal: true`](https://quarto.org/docs/output-formats/html-basics.html#minimal-html). Specifically, only HTML vignettes are currently supported. From e251b87d6ddaeb18e722e868028333ccb176462e Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 09:06:04 -0500 Subject: [PATCH 26/49] Tweak copying messages --- R/build-quarto-articles.R | 3 ++- R/utils-fs.R | 17 +++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 08c142f67..5c4b9b4cd 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -67,7 +67,8 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { src_paths = resources, dst_paths = path(pkg$dst_path, "articles", path_rel(resources, output_dir)), src_root = output_dir, - dst_root = pkg$dst_path + dst_root = pkg$dst_path, + src_label = NULL ) invisible() diff --git a/R/utils-fs.R b/R/utils-fs.R index 5c1b98b02..984415a07 100644 --- a/R/utils-fs.R +++ b/R/utils-fs.R @@ -33,7 +33,7 @@ file_copy_to <- function(src_paths, dst_paths, src_root, dst_root, - src_label = "", + src_label = NULL, dst_label = "") { # Ensure all the "to" directories exist dst_dirs <- unique(path_dir(dst_paths)) @@ -41,12 +41,17 @@ file_copy_to <- function(src_paths, eq <- purrr::map2_lgl(src_paths, dst_paths, file_equal) if (any(!eq)) { - src <- paste0(src_label, path_rel(src_paths[!eq], src_root)) dst <- paste0(dst_label, path_rel(dst_paths[!eq], dst_root)) - - purrr::walk2(src, dst, function(src, dst) { - cli::cli_inform("Copying {src_path(src)} to {dst_path(dst)}") - }) + if (is.null(src_label)) { + purrr::walk(dst, function(dst) { + cli::cli_inform("Copying {dst_path(dst)}") + }) + } else { + src <- paste0(src_label, path_rel(src_paths[!eq], src_root)) + purrr::walk2(src, dst, function(src, dst) { + cli::cli_inform("Copying {src_path(src)} to {dst_path(dst)}") + }) + } } file_copy(src_paths[!eq], dst_paths[!eq], overwrite = TRUE) From 0fa7c9f4271179ee6c315d0a2db739e0aa05c18f Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 09:35:33 -0500 Subject: [PATCH 27/49] Don't actually need pkgdown --- vignettes/quarto.qmd | 4 ---- 1 file changed, 4 deletions(-) diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 817bdd0aa..0dd8e3d57 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -13,10 +13,6 @@ knitr: comment: '#>' --- -```{r setup} -library(pkgdown) -``` - pkgdown effectively uses quarto only to generate HTML and then supplies its own CSS and JS. This means that when quarto introduces new features, pkgdown may lag behind in their support. If you're trying out something that doens't work (and isn't mentioned explicitly below), please [file an issue](https://github.com/r-lib/pkgdown/issues) so we can look into it. ## Operation From 1c1640009cf08a660669302ad4b6ee6f07c1a8b1 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 09:40:04 -0500 Subject: [PATCH 28/49] Add news bullet --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 40d0a8419..0f5c6e07f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # pkgdown (development version) +* `build_articles()` and `build_article()` now support articles/vignettes written with quarto. Combining the disparate quarto and pkgdown templating systems is a delicate art, so while I've done my best to make it work, there may be some rough edges. So please file an issue you encounter quarto features that don't work quite right. Learn more in `vignette("quarto")`(#2210). * `preview_page()` has been deprecated (#2650). * `build_article()` now translates the "Abstract" title if it's used. * `build_*()` (apart from `build_site()`) functions no longer default to previewing in interactive sessions since they now all emit specific links to newly generated files. From 8b39778e6689166aae34fd9745e3d62809313562 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 09:52:39 -0500 Subject: [PATCH 29/49] Make build_article() work with quarto --- R/build-article.R | 29 +++++++++++++++++------------ R/build-quarto-articles.R | 23 +++++++++++++++++------ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/R/build-article.R b/R/build-article.R index 4d77ae5cc..ccc42105a 100644 --- a/R/build-article.R +++ b/R/build-article.R @@ -28,6 +28,7 @@ build_article <- function(name, input <- pkg$vignettes$file_in[vig] output_file <- pkg$vignettes$file_out[vig] depth <- pkg$vignettes$depth[vig] + type <- pkg$vignettes$type[vig] input_path <- path_abs(input, pkg$src_path) output_path <- path_abs(output_file, pkg$dst_path) @@ -36,18 +37,22 @@ build_article <- function(name, return(invisible()) } - build_rmarkdown_article( - pkg = pkg, - input_file = input, - input_path = input_path, - output_file = output_file, - output_path = output_path, - depth = depth, - seed = seed, - new_process = new_process, - pandoc_args = pandoc_args, - quiet = quiet - ) + if (type == "rmd") { + build_rmarkdown_article( + pkg = pkg, + input_file = input, + input_path = input_path, + output_file = output_file, + output_path = output_path, + depth = depth, + seed = seed, + new_process = new_process, + pandoc_args = pandoc_args, + quiet = quiet + ) + } else { + build_quarto_articles(pkg = pkg, article = name) + } } build_rmarkdown_article <- function(pkg, diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 5c4b9b4cd..8295fc066 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -1,8 +1,11 @@ -build_quarto_articles <- function(pkg = ".", quiet = TRUE) { +build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { check_required("quarto") pkg <- as_pkgdown(pkg) qmds <- pkg$vignettes[pkg$vignettes$type == "qmd", ] + if (!is.null(article)) { + qmds <- qmds[qmds$name == article, ] + } if (nrow(qmds) == 0) { return() } @@ -22,15 +25,23 @@ build_quarto_articles <- function(pkg = ".", quiet = TRUE) { write_yaml(quarto_format(pkg), metadata_path) # If needed, temporarily make a quarto project so we can build entire dir - project_path <- path(pkg$src_path, "vignettes", "_quarto.yaml") - if (!file_exists(project_path)) { - yaml::write_yaml(list(project = list(render = list("*.qmd"))), project_path) - withr::defer(file_delete(project_path)) + if (is.null(article)) { + project_path <- path(pkg$src_path, "vignettes", "_quarto.yaml") + if (!file_exists(project_path)) { + yaml::write_yaml(list(project = list(render = list("*.qmd"))), project_path) + withr::defer(file_delete(project_path)) + } } + if (is.null(article)) { + src_path <- path(pkg$src_path, "vignettes") + } else { + src_path <- path(pkg$src_path, qmds$file_in) + } + output_dir <- withr::local_tempdir("pkgdown-quarto-") quarto::quarto_render( - path(pkg$src_path, "vignettes"), + src_path, metadata_file = metadata_path, execute_dir = output_dir, quarto_args = c("--output-dir", output_dir), From d6338e8cecd95726ac3450799b597f1819f99ee5 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 09:56:05 -0500 Subject: [PATCH 30/49] Don't need tinytex from quarto --- .github/workflows/R-CMD-check.yaml | 2 -- .github/workflows/netlify.yaml | 2 -- .github/workflows/pkgdown.yaml | 2 -- 3 files changed, 6 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 31f2b4f54..79698f464 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -48,8 +48,6 @@ jobs: uses: quarto-dev/quarto-actions/setup@v2 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tinytex: true - uses: r-lib/actions/setup-r@v2 with: diff --git a/.github/workflows/netlify.yaml b/.github/workflows/netlify.yaml index ffcbd1edc..24592a7da 100644 --- a/.github/workflows/netlify.yaml +++ b/.github/workflows/netlify.yaml @@ -20,8 +20,6 @@ jobs: uses: quarto-dev/quarto-actions/setup@v2 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tinytex: true - uses: r-lib/actions/setup-r@v2 with: diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 2ce13cecf..4c7367129 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -28,8 +28,6 @@ jobs: uses: quarto-dev/quarto-actions/setup@v2 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tinytex: true - uses: r-lib/actions/setup-pandoc@v2 From 731a48056f8811cbae56bf3a0737f67e29af8bf8 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 10:32:39 -0500 Subject: [PATCH 31/49] Update vignettes/quarto.qmd Co-authored-by: Hugo Gruson <10783929+Bisaloo@users.noreply.github.com> --- vignettes/quarto.qmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/quarto.qmd b/vignettes/quarto.qmd index 0dd8e3d57..b24eec23e 100644 --- a/vignettes/quarto.qmd +++ b/vignettes/quarto.qmd @@ -13,7 +13,7 @@ knitr: comment: '#>' --- -pkgdown effectively uses quarto only to generate HTML and then supplies its own CSS and JS. This means that when quarto introduces new features, pkgdown may lag behind in their support. If you're trying out something that doens't work (and isn't mentioned explicitly below), please [file an issue](https://github.com/r-lib/pkgdown/issues) so we can look into it. +pkgdown effectively uses quarto only to generate HTML and then supplies its own CSS and JS. This means that when quarto introduces new features, pkgdown may lag behind in their support. If you're trying out something that doesn't work (and isn't mentioned explicitly below), please [file an issue](https://github.com/r-lib/pkgdown/issues) so we can look into it. ## Operation From 9a655093b86d692a8ad0be39fa7f086638665e67 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 16:42:37 -0500 Subject: [PATCH 32/49] Simplify/standardise quarto setup --- .github/workflows/R-CMD-check.yaml | 7 ++----- .github/workflows/pkgdown.yaml | 5 +---- .github/workflows/test-coverage.yaml | 2 ++ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 540f874d2..414d29cd6 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -44,12 +44,9 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-pandoc@v2 + - uses: quarto-dev/quarto-actions/setup@v2 - - name: Set up Quarto - uses: quarto-dev/quarto-actions/setup@v2 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 4c7367129..1b952d124 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -24,10 +24,7 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Quarto - uses: quarto-dev/quarto-actions/setup@v2 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: quarto-dev/quarto-actions/setup@v2 - uses: r-lib/actions/setup-pandoc@v2 diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 2aec33d37..4d9c2aa2a 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -21,6 +21,8 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 + - uses: quarto-dev/quarto-actions/setup@v2 + - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true From a275962cb13ac7953c517082a1a63e9a5cc474fd Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 17:08:15 -0500 Subject: [PATCH 33/49] Start adding tests And make sure that the heading tweaks actually work --- R/build-news.R | 2 +- R/build-quarto-articles.R | 1 + R/utils.R | 11 +++-- tests/testthat/helper.R | 6 ++- tests/testthat/test-build-quarto-articles.R | 53 +++++++++++++++++++++ 5 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/testthat/test-build-quarto-articles.R diff --git a/R/build-news.R b/R/build-news.R index da554025a..97b1ca46c 100644 --- a/R/build-news.R +++ b/R/build-news.R @@ -332,7 +332,7 @@ tweak_news_anchor <- function(html, version) { } tweak_section_levels <- function(html) { - sections <- xml2::xml_find_all(html, ".//div[contains(@class, 'section level')]") + sections <- xml2::xml_find_all(html, ".//div[contains(@class, 'section level')]|//main/section") # Update headings xml2::xml_set_name(xml2::xml_find_all(sections, ".//h5"), "h6") diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 8295fc066..fb2570bbf 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -97,6 +97,7 @@ quarto_format <- function(pkg) { `embed-resources` = FALSE, `citations-hover` = TRUE, `link-citations` = TRUE, + `section-divs` = TRUE, toc = FALSE # pkgdown generates with js ) ) diff --git a/R/utils.R b/R/utils.R index 117dfd019..0a9a05f13 100644 --- a/R/utils.R +++ b/R/utils.R @@ -155,8 +155,9 @@ ruler <- function(width = getOption("width")) { get_section_level <- function(section) { class <- xml2::xml_attr(section, "class") - has_level <- grepl("level(\\d+)", class) - ifelse(has_level, as.numeric(gsub(".*section level(\\d+).*", '\\1', class)), 0) +level <- as.numeric(re_match(class, "level(\\d+)")[[1]]) + level[is.na(level)] <- 0 + level } section_id <- function(section) { @@ -188,8 +189,10 @@ write_yaml <- function(x, path) { # Helpers for testing ----------------------------------------------------- -xpath_xml <- function(x, xpath) { - x <- xml2::xml_find_all(x, xpath) +xpath_xml <- function(x, xpath = NULL) { + if (!is.null(xpath)) { + x <- xml2::xml_find_all(x, xpath) + } structure(x, class = c("pkgdown_xml", class(x))) } xpath_contents <- function(x, xpath) { diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R index c3f89ac5a..c4bb22a47 100644 --- a/tests/testthat/helper.R +++ b/tests/testthat/helper.R @@ -1,5 +1,8 @@ skip_if_no_pandoc <- function(version = "1.12.3") { - testthat::skip_if_not(rmarkdown::pandoc_available(version)) + skip_if_not(rmarkdown::pandoc_available(version), "pandoc not available") +} +skip_if_no_quarto <- function() { + skip_if(is.null(quarto::quarto_path()), "quarto not available") } # Simulate a package -------------------------------------------------------- @@ -79,6 +82,7 @@ pkg_vignette <- function(..., title = "title") { c("---", yaml, "---", contents) } + r_code_block <- function(...) c("```{r}", ..., "```") # Simulate a template package ------------------------------------------------ diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R new file mode 100644 index 000000000..6aa91f9a1 --- /dev/null +++ b/tests/testthat/test-build-quarto-articles.R @@ -0,0 +1,53 @@ +test_that("can build all quarto article", { + pkg <- local_pkgdown_site() + pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") + pkg <- pkg_add_file(pkg, "vignettes/vig2.qmd") + + suppressMessages(build_articles(pkg)) + + expect_true(file_exists(path(pkg$dst_path, "articles/vig1.html"))) + expect_true(file_exists(path(pkg$dst_path, "articles/vig2.html"))) +}) + +test_that("can build a single quarto article", { + pkg <- local_pkgdown_site() + pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") + pkg <- pkg_add_file(pkg, "vignettes/vig2.qmd") + + suppressMessages(build_article("vig1", pkg)) + + expect_true(file_exists(path(pkg$dst_path, "articles/vig1.html"))) + expect_false(file_exists(path(pkg$dst_path, "articles/vig2.html"))) +}) + +test_that("doesn't do anything if no quarto articles", { + pkg <- local_pkgdown_site() + expect_no_error(suppressMessages(build_quarto_articles(pkg))) +}) + +test_that("can render a pdf qmd", { + pkg <- local_pkgdown_site() + pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd", pkg_vignette( + format = list(pdf = list(toc = TRUE)) + )) + + expect_equal(pkg$vignettes$type, "qmd") + expect_equal(pkg$vignettes$file_out, "articles/vig1.pdf") + + suppressMessages(build_article("vig1", pkg)) + expect_true(file_exists(path(pkg$dst_path, "articles/vig1.pdf"))) +}) + +test_that("auto-adjusts heading levels", { + pkg <- local_pkgdown_site() + pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd", pkg_vignette( + "# Heading 1", + "# Heading 2" + )) + + suppressMessages(build_article("vig1", pkg)) + + html <- xml2::read_html(path(pkg$dst_path, "articles/vig1.html")) + expect_equal(xpath_text(html, "//h1"), "title") + expect_equal(xpath_text(html, "//h2"), c("Heading 1", "Heading 2")) +}) From d66fa7cf8174d6769cb5ea60b9f49ef49ba52eba Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Wed, 12 Jun 2024 17:28:04 -0500 Subject: [PATCH 34/49] Capture quarto styles --- R/build-quarto-articles.R | 40 +++++++++++-------- .../testthat/_snaps/build-quarto-articles.md | 19 +++++++++ tests/testthat/test-build-quarto-articles.R | 10 +++++ 3 files changed, 52 insertions(+), 17 deletions(-) create mode 100644 tests/testthat/_snaps/build-quarto-articles.md diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index fb2570bbf..c94de884a 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -17,12 +17,6 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { old_digest <- purrr::map_chr(path(pkg$dst_path, qmds$file_out), file_digest) - # Override default quarto format - metadata_path <- withr::local_tempfile( - fileext = ".yml", - pattern = "pkgdown-quarto-metadata-" - ) - write_yaml(quarto_format(pkg), metadata_path) # If needed, temporarily make a quarto project so we can build entire dir if (is.null(article)) { @@ -38,17 +32,8 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { } else { src_path <- path(pkg$src_path, qmds$file_in) } - - output_dir <- withr::local_tempdir("pkgdown-quarto-") - quarto::quarto_render( - src_path, - metadata_file = metadata_path, - execute_dir = output_dir, - quarto_args = c("--output-dir", output_dir), - quiet = quiet, - as_job = FALSE - ) - + output_dir <- quarto_render(src_path, quiet = quiet) + # Read generated data from quarto template and render into pkgdown template purrr::walk2(qmds$file_in, qmds$file_out, function(input_file, output_file) { built_path <- path(output_dir, path_rel(output_file, "articles")) @@ -85,6 +70,27 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { invisible() } +quarto_render <- function(path, quiet = TRUE, frame = caller_env()) { + # Override default quarto format + metadata_path <- withr::local_tempfile( + fileext = ".yml", + pattern = "pkgdown-quarto-metadata-" + ) + write_yaml(quarto_format(pkg), metadata_path) + + output_dir <- withr::local_tempdir("pkgdown-quarto-", .local_envir = frame) + quarto::quarto_render( + path, + metadata_file = metadata_path, + execute_dir = output_dir, + quarto_args = c("--output-dir", output_dir), + quiet = quiet, + as_job = FALSE + ) + + output_dir +} + quarto_format <- function(pkg) { list( lang = pkg$lang, diff --git a/tests/testthat/_snaps/build-quarto-articles.md b/tests/testthat/_snaps/build-quarto-articles.md new file mode 100644 index 000000000..bf7b98aee --- /dev/null +++ b/tests/testthat/_snaps/build-quarto-articles.md @@ -0,0 +1,19 @@ +# we find out if quarto styles change + + Code + cat(data$includes$style) + Output + + code{white-space: pre-wrap;} + span.smallcaps{font-variant: small-caps;} + div.columns{display: flex; gap: min(4vw, 1.5em);} + div.column{flex: auto; overflow-x: auto;} + div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} + ul.task-list{list-style: none;} + ul.task-list li input[type="checkbox"] { + width: 0.8em; + margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ + vertical-align: middle; + } + + diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R index 6aa91f9a1..c2ec67004 100644 --- a/tests/testthat/test-build-quarto-articles.R +++ b/tests/testthat/test-build-quarto-articles.R @@ -51,3 +51,13 @@ test_that("auto-adjusts heading levels", { expect_equal(xpath_text(html, "//h1"), "title") expect_equal(xpath_text(html, "//h2"), c("Heading 1", "Heading 2")) }) + +test_that("we find out if quarto styles change", { + pkg <- local_pkgdown_site() + pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") + + output_dir <- quarto_render(path(pkg$src_path, "vignettes", "vig1.qmd")) + + data <- data_quarto_article(pkg, path(output_dir, "vig1.html"), "vig1.qmd") + expect_snapshot(cat(data$includes$style)) +}) From eb952efb5e5e9b75a321b93c7987a32db85abc87 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 07:48:12 -0500 Subject: [PATCH 35/49] Minor tweaks --- R/build-quarto-articles.R | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index c94de884a..15ab8371f 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -10,14 +10,13 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { return() } + # Let user know what's happening + old_digest <- purrr::map_chr(path(pkg$dst_path, qmds$file_out), file_digest) for (file in qmds$file_in) { cli::cli_inform("Reading {src_path(file)}") } cli::cli_inform("Running {.code quarto render}") - old_digest <- purrr::map_chr(path(pkg$dst_path, qmds$file_out), file_digest) - - # If needed, temporarily make a quarto project so we can build entire dir if (is.null(article)) { project_path <- path(pkg$src_path, "vignettes", "_quarto.yaml") From 674d2b8d534937d654e3a83bec98368e6546ace0 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 07:55:03 -0500 Subject: [PATCH 36/49] Test included in index --- R/build-search-docs.R | 2 +- tests/testthat/test-build-quarto-articles.R | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/R/build-search-docs.R b/R/build-search-docs.R index 788d00525..61a197966 100644 --- a/R/build-search-docs.R +++ b/R/build-search-docs.R @@ -144,7 +144,7 @@ file_search_index <- function(path, pkg) { # Get contents minus logo node <- xml2::xml_find_all(html, ".//main") xml2::xml_remove(xml2::xml_find_first(node, ".//img[contains(@class, 'pkg-logo')]")) - sections <- xml2::xml_find_all(node, ".//div[contains(@class, 'section')]") + sections <- xml2::xml_find_all(node, ".//div[contains(@class, 'section')]|.//section") purrr::pmap( list( diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R index c2ec67004..f0e298617 100644 --- a/tests/testthat/test-build-quarto-articles.R +++ b/tests/testthat/test-build-quarto-articles.R @@ -61,3 +61,18 @@ test_that("we find out if quarto styles change", { data <- data_quarto_article(pkg, path(output_dir, "vig1.html"), "vig1.qmd") expect_snapshot(cat(data$includes$style)) }) + +test_that("quarto articles are included in the index", { + pkg <- local_pkgdown_site() + pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd", pkg_vignette( + "## Heading 1", + "Some text" + )) + + suppressMessages(build_article("vig1", pkg)) + index <- build_search_index(pkg) + + expect_equal(index[[1]]$path, "/articles/vig1.html") + expect_equal(index[[1]]$what, "Heading 1") + expect_equal(index[[1]]$text, "text") # some is a stop word +}) From b8fc296eeb6a504edca519208dff7ce9dde89042 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 08:16:55 -0500 Subject: [PATCH 37/49] Check anchors are tweaked correctly --- DESCRIPTION | 2 +- R/tweak-tags.R | 8 ++++++-- tests/testthat/test-build-quarto-articles.R | 15 ++++++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 88a290f38..ba804ff60 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -70,7 +70,7 @@ Config/Needs/website: usethis, servr Config/potools/style: explicit Config/testthat/edition: 3 Config/testthat/parallel: true -Config/testthat/start-first: build-article, build-reference +Config/testthat/start-first: build-article, build-quarto-article, build-reference Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.1 diff --git a/R/tweak-tags.R b/R/tweak-tags.R index 064e2e054..1e6f5a05b 100644 --- a/R/tweak-tags.R +++ b/R/tweak-tags.R @@ -2,15 +2,19 @@ tweak_anchors <- function(html) { headings <- xml2::xml_find_all(html, ".//h1|.//h2|.//h3|.//h4|.//h5|.//h6") # Find all headings that are contained in a div with an id and # have class 'section' + is_ok <- xml2::xml_find_lgl(headings, - "boolean(parent::div[contains(@class, 'section') and @id])" + "boolean( + (parent::div[contains(@class, 'section') and @id]) or + (parent::section[@id]) + )" ) headings <- headings[is_ok] if (length(headings) == 0) { return(invisible()) } - id <- xml2::xml_find_chr(headings, "string(parent::div/@id)") + id <- xml2::xml_find_chr(headings, "string(parent::div/@id|parent::section/@id)") # Update ids: dot in the anchor breaks scrollspy and rd translation # doesn't have enough information to generate unique ids diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R index f0e298617..ffe557d48 100644 --- a/tests/testthat/test-build-quarto-articles.R +++ b/tests/testthat/test-build-quarto-articles.R @@ -49,7 +49,7 @@ test_that("auto-adjusts heading levels", { html <- xml2::read_html(path(pkg$dst_path, "articles/vig1.html")) expect_equal(xpath_text(html, "//h1"), "title") - expect_equal(xpath_text(html, "//h2"), c("Heading 1", "Heading 2")) + expect_equal(xpath_text(html, "//h2"), c("Heading 1\n", "Heading 2\n")) }) test_that("we find out if quarto styles change", { @@ -76,3 +76,16 @@ test_that("quarto articles are included in the index", { expect_equal(index[[1]]$what, "Heading 1") expect_equal(index[[1]]$text, "text") # some is a stop word }) + +test_that("quarto headings get anchors", { + pkg <- local_pkgdown_site() + pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd", pkg_vignette( + "## Heading 1", + "### Heading 2" + )) + + suppressMessages(build_article("vig1", pkg)) + html <- xml2::read_html(path(pkg$dst_path, "articles/vig1.html")) + headings <- xpath_xml(html, "//h2|//h3") + expect_equal(xpath_attr(headings, "./a", "href"), c("#heading-1", "#heading-2")) +}) From 5c3fc0ed75e50a105ab7abbe5ef11f942a7a0363 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 08:51:21 -0500 Subject: [PATCH 38/49] Fix silly mistake --- R/build-quarto-articles.R | 4 ++-- tests/testthat/test-build-quarto-articles.R | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 15ab8371f..527fdbb1d 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -31,7 +31,7 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { } else { src_path <- path(pkg$src_path, qmds$file_in) } - output_dir <- quarto_render(src_path, quiet = quiet) + output_dir <- quarto_render(pkg, src_path, quiet = quiet) # Read generated data from quarto template and render into pkgdown template purrr::walk2(qmds$file_in, qmds$file_out, function(input_file, output_file) { @@ -69,7 +69,7 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { invisible() } -quarto_render <- function(path, quiet = TRUE, frame = caller_env()) { +quarto_render <- function(pkg, path, quiet = TRUE, frame = caller_env()) { # Override default quarto format metadata_path <- withr::local_tempfile( fileext = ".yml", diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R index ffe557d48..50385d4a0 100644 --- a/tests/testthat/test-build-quarto-articles.R +++ b/tests/testthat/test-build-quarto-articles.R @@ -56,7 +56,7 @@ test_that("we find out if quarto styles change", { pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") - output_dir <- quarto_render(path(pkg$src_path, "vignettes", "vig1.qmd")) + output_dir <- quarto_render(pkg, path(pkg$src_path, "vignettes", "vig1.qmd")) data <- data_quarto_article(pkg, path(output_dir, "vig1.html"), "vig1.qmd") expect_snapshot(cat(data$includes$style)) From c7c694a060a21e076f0cd2523f26d42a118b120d Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 08:57:10 -0500 Subject: [PATCH 39/49] Need to skip tests on no-pandoc runner --- tests/testthat/test-build-quarto-articles.R | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R index 50385d4a0..2caa1e781 100644 --- a/tests/testthat/test-build-quarto-articles.R +++ b/tests/testthat/test-build-quarto-articles.R @@ -1,4 +1,6 @@ test_that("can build all quarto article", { + skip_if_no_quarto() + pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") pkg <- pkg_add_file(pkg, "vignettes/vig2.qmd") @@ -10,6 +12,8 @@ test_that("can build all quarto article", { }) test_that("can build a single quarto article", { + skip_if_no_quarto() + pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") pkg <- pkg_add_file(pkg, "vignettes/vig2.qmd") @@ -26,6 +30,8 @@ test_that("doesn't do anything if no quarto articles", { }) test_that("can render a pdf qmd", { + skip_if_no_quarto() + pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd", pkg_vignette( format = list(pdf = list(toc = TRUE)) @@ -39,6 +45,8 @@ test_that("can render a pdf qmd", { }) test_that("auto-adjusts heading levels", { + skip_if_no_quarto() + pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd", pkg_vignette( "# Heading 1", @@ -53,6 +61,8 @@ test_that("auto-adjusts heading levels", { }) test_that("we find out if quarto styles change", { + skip_if_no_quarto() + pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") @@ -63,6 +73,8 @@ test_that("we find out if quarto styles change", { }) test_that("quarto articles are included in the index", { + skip_if_no_quarto() + pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd", pkg_vignette( "## Heading 1", @@ -78,6 +90,8 @@ test_that("quarto articles are included in the index", { }) test_that("quarto headings get anchors", { + skip_if_no_quarto() + pkg <- local_pkgdown_site() pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd", pkg_vignette( "## Heading 1", From 99add6229933d17a0bac8c17ef4fdfe6489ddbd8 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 08:59:49 -0500 Subject: [PATCH 40/49] Get more debugging info --- tests/testthat/test-build-quarto-articles.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R index 2caa1e781..d6bee143b 100644 --- a/tests/testthat/test-build-quarto-articles.R +++ b/tests/testthat/test-build-quarto-articles.R @@ -40,7 +40,7 @@ test_that("can render a pdf qmd", { expect_equal(pkg$vignettes$type, "qmd") expect_equal(pkg$vignettes$file_out, "articles/vig1.pdf") - suppressMessages(build_article("vig1", pkg)) + build_article("vig1", pkg, quiet = FALSE) expect_true(file_exists(path(pkg$dst_path, "articles/vig1.pdf"))) }) From f208a772491f125fc180356ff17942a422d79ef3 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 09:07:54 -0500 Subject: [PATCH 41/49] Actually pass quiet along --- R/build-article.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/build-article.R b/R/build-article.R index ccc42105a..7e6d3e8b6 100644 --- a/R/build-article.R +++ b/R/build-article.R @@ -51,7 +51,7 @@ build_article <- function(name, quiet = quiet ) } else { - build_quarto_articles(pkg = pkg, article = name) + build_quarto_articles(pkg = pkg, article = name, quiet = quiet) } } From 8a0b2f8150aeffce8181cdfef42000b7da73b184 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 09:18:25 -0500 Subject: [PATCH 42/49] Need tinytex for pdf --- .github/workflows/R-CMD-check.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 414d29cd6..f51be379d 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -45,6 +45,8 @@ jobs: - uses: actions/checkout@v4 - uses: quarto-dev/quarto-actions/setup@v2 + with: + tiny-tex: true - uses: r-lib/actions/setup-pandoc@v2 From 060c75943f8cce1d7e44cbec1318f150b6fdbf2a Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 09:23:49 -0500 Subject: [PATCH 43/49] Actually install tinytex --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index f51be379d..72bd162bc 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -46,7 +46,7 @@ jobs: - uses: quarto-dev/quarto-actions/setup@v2 with: - tiny-tex: true + tinytex: true - uses: r-lib/actions/setup-pandoc@v2 From 6acbda37907b3e33f8b1e479e0f7968ed1c6ba1a Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 10:08:25 -0500 Subject: [PATCH 44/49] More debugging --- tests/testthat/helper.R | 4 ++-- tests/testthat/test-build-quarto-articles.R | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R index c4bb22a47..f2affc4e7 100644 --- a/tests/testthat/helper.R +++ b/tests/testthat/helper.R @@ -13,7 +13,7 @@ local_pkgdown_site <- function(path = NULL, env = caller_env()) { check_string(path, allow_null = TRUE) - dst_path <- withr::local_tempdir(.local_envir = env) + dst_path <- path_real(withr::local_tempdir(.local_envir = env)) # Simulate init_site() so we only have to run it if we care about file_create(path(dst_path, "pkgdown.yml")) dir_create(path(dst_path, "deps")) @@ -22,7 +22,7 @@ local_pkgdown_site <- function(path = NULL, meta <- modify_list(meta, list(destination = dst_path)) if (is.null(path)) { - path <- withr::local_tempdir(.local_envir = env) + path <- path_real(withr::local_tempdir(.local_envir = env)) description <- desc::desc("!new") description$set("Package", "testpackage") diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R index d6bee143b..42d337487 100644 --- a/tests/testthat/test-build-quarto-articles.R +++ b/tests/testthat/test-build-quarto-articles.R @@ -18,7 +18,12 @@ test_that("can build a single quarto article", { pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") pkg <- pkg_add_file(pkg, "vignettes/vig2.qmd") + message() + message("Dest:", pkg$dst_path) + message("Src :", pkg$src_path) + suppressMessages(build_article("vig1", pkg)) + message("Out :", paste0(dir_ls(path(pkg$dst_path, "articles")), collapse = ", ")) expect_true(file_exists(path(pkg$dst_path, "articles/vig1.html"))) expect_false(file_exists(path(pkg$dst_path, "articles/vig2.html"))) @@ -40,7 +45,7 @@ test_that("can render a pdf qmd", { expect_equal(pkg$vignettes$type, "qmd") expect_equal(pkg$vignettes$file_out, "articles/vig1.pdf") - build_article("vig1", pkg, quiet = FALSE) + suppressMessages(build_article("vig1", pkg)) expect_true(file_exists(path(pkg$dst_path, "articles/vig1.pdf"))) }) From 4ffd69c1662a4e7e4514d9176db8da27a582b999 Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 10:14:21 -0500 Subject: [PATCH 45/49] Try disabling parallelism --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index ba804ff60..b99da294a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -69,7 +69,7 @@ VignetteBuilder: Config/Needs/website: usethis, servr Config/potools/style: explicit Config/testthat/edition: 3 -Config/testthat/parallel: true +Config/testthat/parallel: false Config/testthat/start-first: build-article, build-quarto-article, build-reference Encoding: UTF-8 Roxygen: list(markdown = TRUE) From 48b3842e720d64a28359e96ef0ccee2abded471c Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 11:46:08 -0500 Subject: [PATCH 46/49] More debugging --- R/build-quarto-articles.R | 10 +++++++--- tests/testthat/helper.R | 9 +++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index 527fdbb1d..f5545cf35 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -32,10 +32,14 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { src_path <- path(pkg$src_path, qmds$file_in) } output_dir <- quarto_render(pkg, src_path, quiet = quiet) + if (!dir_exists(output_dir)) cli::cli_abort("No output directory found") # Read generated data from quarto template and render into pkgdown template - purrr::walk2(qmds$file_in, qmds$file_out, function(input_file, output_file) { + unwrap_purrr_error(purrr::walk2(qmds$file_in, qmds$file_out, function(input_file, output_file) { built_path <- path(output_dir, path_rel(output_file, "articles")) + if (!file_exists(built_path)) { + cli::cli_abort("No output file found for {.file {input_file}}") + } if (path_ext(output_file) == "html") { data <- data_quarto_article(pkg, built_path, input_file) render_page(pkg, "quarto", data, output_file, quiet = TRUE) @@ -44,7 +48,7 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { } else { file_copy(built_path, path(pkg$dst_path, output_file), overwrite = TRUE) } - }) + })) # Report on which files have changed new_digest <- purrr::map_chr(path(pkg$dst_path, qmds$file_out), file_digest) @@ -73,7 +77,7 @@ quarto_render <- function(pkg, path, quiet = TRUE, frame = caller_env()) { # Override default quarto format metadata_path <- withr::local_tempfile( fileext = ".yml", - pattern = "pkgdown-quarto-metadata-" + pattern = "pkgdown-quarto-metadata-", ) write_yaml(quarto_format(pkg), metadata_path) diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R index f2affc4e7..2362b0ae1 100644 --- a/tests/testthat/helper.R +++ b/tests/testthat/helper.R @@ -2,6 +2,7 @@ skip_if_no_pandoc <- function(version = "1.12.3") { skip_if_not(rmarkdown::pandoc_available(version), "pandoc not available") } skip_if_no_quarto <- function() { + skip_on_os("windows") # quarto set up currently broken? skip_if(is.null(quarto::quarto_path()), "quarto not available") } @@ -13,7 +14,9 @@ local_pkgdown_site <- function(path = NULL, env = caller_env()) { check_string(path, allow_null = TRUE) - dst_path <- path_real(withr::local_tempdir(.local_envir = env)) + dst_path <- path_real( + withr::local_tempdir(.local_envir = env, pattern = "pkgdown-dst") + ) # Simulate init_site() so we only have to run it if we care about file_create(path(dst_path, "pkgdown.yml")) dir_create(path(dst_path, "deps")) @@ -22,7 +25,9 @@ local_pkgdown_site <- function(path = NULL, meta <- modify_list(meta, list(destination = dst_path)) if (is.null(path)) { - path <- path_real(withr::local_tempdir(.local_envir = env)) + path <- path_real( + withr::local_tempdir(.local_envir = env, pattern = "pkgdown-src") + ) description <- desc::desc("!new") description$set("Package", "testpackage") From 22698a1d99da57ca4d21c75530561704c609005a Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 11:51:00 -0500 Subject: [PATCH 47/49] Do need GITHUB_TOKEN --- .github/workflows/R-CMD-check.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 72bd162bc..7cfe84536 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -47,6 +47,8 @@ jobs: - uses: quarto-dev/quarto-actions/setup@v2 with: tinytex: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: r-lib/actions/setup-pandoc@v2 From f92013169b59cd4804dc75369b0c10cca073eb1b Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 12:28:41 -0500 Subject: [PATCH 48/49] Need pre-release quarto? --- .github/workflows/R-CMD-check.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 7cfe84536..cd91a64f1 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -46,6 +46,7 @@ jobs: - uses: quarto-dev/quarto-actions/setup@v2 with: + version: pre-release tinytex: true env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From e6e52220c1355b54d559da64f9b000dc4b2cd52e Mon Sep 17 00:00:00 2001 From: Hadley Wickham Date: Thu, 13 Jun 2024 15:08:33 -0500 Subject: [PATCH 49/49] Revert various debugging changes --- .github/workflows/test-coverage.yaml | 5 +++++ DESCRIPTION | 2 +- R/build-article.R | 1 + R/build-quarto-articles.R | 3 +-- tests/testthat/test-build-quarto-articles.R | 5 ----- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 4d9c2aa2a..2565dbe3e 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -22,6 +22,11 @@ jobs: - uses: r-lib/actions/setup-pandoc@v2 - uses: quarto-dev/quarto-actions/setup@v2 + with: + version: pre-release + tinytex: true + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: r-lib/actions/setup-r@v2 with: diff --git a/DESCRIPTION b/DESCRIPTION index b99da294a..ba804ff60 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -69,7 +69,7 @@ VignetteBuilder: Config/Needs/website: usethis, servr Config/potools/style: explicit Config/testthat/edition: 3 -Config/testthat/parallel: false +Config/testthat/parallel: true Config/testthat/start-first: build-article, build-quarto-article, build-reference Encoding: UTF-8 Roxygen: list(markdown = TRUE) diff --git a/R/build-article.R b/R/build-article.R index 7e6d3e8b6..c5dc5713a 100644 --- a/R/build-article.R +++ b/R/build-article.R @@ -123,6 +123,7 @@ build_rmarkdown_article <- function(pkg, } invisible(path) + } diff --git a/R/build-quarto-articles.R b/R/build-quarto-articles.R index f5545cf35..b50aa4171 100644 --- a/R/build-quarto-articles.R +++ b/R/build-quarto-articles.R @@ -32,13 +32,12 @@ build_quarto_articles <- function(pkg = ".", article = NULL, quiet = TRUE) { src_path <- path(pkg$src_path, qmds$file_in) } output_dir <- quarto_render(pkg, src_path, quiet = quiet) - if (!dir_exists(output_dir)) cli::cli_abort("No output directory found") # Read generated data from quarto template and render into pkgdown template unwrap_purrr_error(purrr::walk2(qmds$file_in, qmds$file_out, function(input_file, output_file) { built_path <- path(output_dir, path_rel(output_file, "articles")) if (!file_exists(built_path)) { - cli::cli_abort("No output file found for {.file {input_file}}") + cli::cli_abort("No built file found for {.file {input_file}}") } if (path_ext(output_file) == "html") { data <- data_quarto_article(pkg, built_path, input_file) diff --git a/tests/testthat/test-build-quarto-articles.R b/tests/testthat/test-build-quarto-articles.R index 42d337487..2caa1e781 100644 --- a/tests/testthat/test-build-quarto-articles.R +++ b/tests/testthat/test-build-quarto-articles.R @@ -18,12 +18,7 @@ test_that("can build a single quarto article", { pkg <- pkg_add_file(pkg, "vignettes/vig1.qmd") pkg <- pkg_add_file(pkg, "vignettes/vig2.qmd") - message() - message("Dest:", pkg$dst_path) - message("Src :", pkg$src_path) - suppressMessages(build_article("vig1", pkg)) - message("Out :", paste0(dir_ls(path(pkg$dst_path, "articles")), collapse = ", ")) expect_true(file_exists(path(pkg$dst_path, "articles/vig1.html"))) expect_false(file_exists(path(pkg$dst_path, "articles/vig2.html")))