Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

Revision df465aa02367bc8bf2fa3f2e3827be5f8713569a authored by Hadley Wickham on 03 December 2021, 09:00:02 UTC, committed by cran-robot on 03 December 2021, 09:00:02 UTC
version 2.0.1
1 parent be39bf7
  • Files
  • Changes
  • 2000f0b
  • /
  • R
  • /
  • build-articles.R
Raw File Download
Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • revision
  • directory
  • content
revision badge
swh:1:rev:df465aa02367bc8bf2fa3f2e3827be5f8713569a
directory badge Iframe embedding
swh:1:dir:367dbd97ccdd73e773f4be611a8fbfb5f743c97f
content badge Iframe embedding
swh:1:cnt:8ba2a9859dc8ef04150d27fede1bdb0cdced739a
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • revision
  • directory
  • content
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
build-articles.R
#' Build articles section
#'
#' @description
#' `build_articles()` renders each R Markdown file underneath `vignettes/` and
#' saves it to `articles/`. There are two exceptions:
#'
#' * Files that start with `_` (e.g., `_index.Rmd`) are ignored,
#'   enabling the use of child documents in
#'   [bookdown](https://bookdown.org/yihui/bookdown/)
#'
#' * Files in `vignettes/tutorials` are handled by [build_tutorials()]
#'
#' Vignettes are rendered using a special document format that reconciles
#' [rmarkdown::html_document()] with the pkgdown template. This means articles
#' behave slightly differently to vignettes, particularly with respect to
#' external files, and custom output formats. See below for more details.
#'
#' Note that when you run `build_articles()` directly (outside of
#' [build_site()]) vignettes will use the currently installed version of the
#' package, not the current source version. This makes iteration quicker when
#' you are primarily working on the text of an article.
#'
#' @section Index and navbar:
#' You can control the articles index and navbar with a `articles` section in
#' your `_pkgdown.yaml`. It defines a list of sections, each of which
#' can contain four fields:
#'
#' * `title` (required): title of section, which appears as a heading on the
#'   articles index.
#'
#' * `desc` (optional): An optional markdown description displayed underneath
#'   the section title.
#'
#' * `navbar` (optional): A couple of words used to label this section in
#'   the navbar. If omitted, this section of vignettes will not appear in the
#'   navbar.
#'
#' * `contents` (required): a list of article names to include in the
#'   section. This can either be names of individual vignettes or a
#'   call to `starts_with()`. The name of a vignette includes its
#'   path under `vignettes` without extension so that the name of the vignette
#'   found at `vignettes/pizza/slice.Rmd` is `pizza/slice`.
#'
#' The title and description of individual vignettes displayed on the index
#' comes from `title` and `description` fields of the YAML header in the Rmds.
#'
#' For example, this yaml might be used for some version of dplyr:
#'
#' ```yaml
#' articles:
#' - title: Main verbs
#'   navbar: ~
#'   contents:
#'   - one-table
#'   - two-table
#'   - rowwise
#'   - colwise
#'
#' - title: Developer
#'   desc: Vignettes aimed at package developers
#'   contents:
#'   - programming
#'   - packages
#' ```
#'
#' Note the use of the `navbar` fields. `navbar: ~` means that the "Main verbs"
#' will appear in the navbar without a heading; the absence of the `navbar`
#' field in the developer vignettes means that they will only be
#' accessible via the articles index.
#'
#' The navbar will include a link to the articles index if one or more
#' vignettes are not available through the navbar. If some vignettes appear
#' in the navbar drop-down list and others do not, the list will automatically
#' include a "More ..." link at the bottom; if no vignettes appear in the
#' the navbar, it will link directly to the articles index instead of
#' providing a drop-down.
#'
#' @section Get started:
#' Note that a vignette with the same name as the package (e.g.,
#' `vignettes/pkgdown.Rmd` or `vignettes/articles/pkgdown.Rmd`) automatically
#' becomes a top-level "Get started" link, and will not appear in the articles
#' drop-down.
#'
#' (If your package name includes a `.`, e.g. `pack.down`, use a `-` in the
#' vignette name, e.g. `pack-down.Rmd`.)
#'
#' @section External files:
#' pkgdown differs from base R in its handling of external files. When building
#' vignettes, R assumes that vignettes are self-contained (a reasonable
#' assumption when most vignettes were PDFs) and only copies files explicitly
#' listed in `.install_extras`. pkgdown takes a different approach based on
#' [rmarkdown::find_external_resources()], and it will also copy any images that
#' you link to. If for some reason the automatic detection doesn't work, you
#' will need to add a `resource_files` field to the yaml metadata, e.g.:
#'
#' ```yaml
#' ---
#' title: My Document
#' resource_files:
#'   - data/mydata.csv
#'   - images/figure.png
#' ---
#' ```
#'
#' Note that you can not use the `fig.path` to change the output directory of
#' generated figures as its default value is a strong assumption of rmarkdown.
#'
#' @section Embedding Shiny apps:
#' If you would like to embed a Shiny app into an article, the app will have
#' to be hosted independently, (e.g. <https://www.shinyapps.io>). Then, you
#' can embed the app into your article using an `<iframe>`, e.g.
#' `<iframe src = "https://gallery.shinyapps.io/083-front-page" class="shiny-app">`.
#'
#' See <https://github.com/r-lib/pkgdown/issues/838#issuecomment-430473856> for
#' some hints on how to customise the appearance with CSS.
#'
#' @section Output formats:
#' By default, pkgdown builds all articles using the
#' [rmarkdown::html_document()] `output` format, ignoring whatever is set in
#' your YAML metadata. This is necessary because pkgdown has to integrate the
#' HTML/CSS/JS from the vignette with the HTML/CSS/JS from rest of the site.
#' Because of the challenges of combining two sources of HTML/CSS/JS, there is
#' limited support for other output formats and you have to opt-in by setting
#' the `as_is` field in your `.Rmd` metadata:
#'
#' ```yaml
#' pkgdown:
#'   as_is: true
#' ```
#'
#' If the output format produces a PDF, you'll also need to specify the
#' `extension` field:
#'
#' ```yaml
#' pkgdown:
#'   as_is: true
#'   extension: pdf
#' ```
#'
#' To work with pkgdown, the output format must accept `template`, `theme`, and
#' `self_contained` arguments, and must work without any additional CSS or
#' JSS files. Note that if you use
#' [`_output.yml`](https://bookdown.org/yihui/rmarkdown/html-document.html#shared-options)
#' or [`_site.yml`](https://rmarkdown.rstudio.com/docs/reference/render_site.html)
#' you'll still need to add `as_is: true` to each individual vignette.
#'
#' Additionally, htmlwidgets do not work when `as_is: true`.
#'
#' @inheritSection build_reference Figures
#'
#' @section Suppressing vignettes:
#' If you want articles that are not vignettes, either put them in
#' subdirectories or list in `.Rbuildignore`. An articles link will be
#' automatically added to the default navbar if the vignettes directory is
#' present: if you do not want this, you will need to customise the navbar. See
#' [build_site()] details.
#'
#' @inheritParams as_pkgdown
#' @param quiet Set to `FALSE` to display output of knitr and
#'   pandoc. This is useful when debugging.
#' @param lazy If `TRUE`, will only re-build article if input file has been
#'   modified more recently than the output file.
#' @param preview If `TRUE`, or `is.na(preview) && interactive()`, will preview
#'   freshly generated section in browser.
#' @export
build_articles <- function(pkg = ".",
                           quiet = TRUE,
                           lazy = TRUE,
                           override = list(),
                           preview = NA) {
  pkg <- section_init(pkg, depth = 1L, override = override)

  if (nrow(pkg$vignettes) == 0L) {
    return(invisible())
  }

  rule("Building articles")

  build_articles_index(pkg)
  purrr::walk(
    pkg$vignettes$name, build_article,
    pkg = pkg,
    quiet = quiet,
    lazy = lazy
  )

  preview_site(pkg, "articles", preview = preview)
}

#' @export
#' @rdname build_articles
#' @param name Name of article to render. This should be either a path
#'   relative to `vignettes/` without extension, or `index` or `README`.
#' @param data Additional data to pass on to template.
build_article <- function(name,
                           pkg = ".",
                           data = list(),
                           lazy = FALSE,
                           quiet = TRUE) {
  pkg <- as_pkgdown(pkg)

  # Look up in pkg vignette data - this allows convenient automatic
  # specification of depth, output destination, and other parmaters that
  # allow code sharing with building of the index.
  vig <- match(name, pkg$vignettes$name)
  if (is.na(vig)) {
    stop("Can't find article called ", src_path(name), call. = FALSE)
  }

  input <- pkg$vignettes$file_in[vig]
  output_file <- pkg$vignettes$file_out[vig]
  depth <- pkg$vignettes$depth[vig]

  input_path <- path_abs(input, pkg$src_path)
  output_path <- path_abs(output_file, pkg$dst_path)

  if (lazy && !out_of_date(input_path, output_path)) {
    return(invisible())
  }

  local_envvar_pkgdown(pkg)
  local_options_link(pkg, depth = depth)

  front <- rmarkdown::yaml_front_matter(input_path)
  # Take opengraph from article's yaml front matter
  front_opengraph <- check_open_graph(front$opengraph %||% list())
  data$opengraph <- utils::modifyList(
    data$opengraph %||% list(), front_opengraph
  )

  # Allow users to opt-in to their own template
  ext <- purrr::pluck(front, "pkgdown", "extension", .default = "html")
  as_is <- isTRUE(purrr::pluck(front, "pkgdown", "as_is"))

  default_data <- list(
    pagetitle = front$title,
    opengraph = list(description = front$description %||% pkg$package),
    source = repo_source(pkg, path_rel(input, pkg$src_path)),
    filename = path_file(input),
    output_file = output_file,
    as_is = as_is
  )
  data <- utils::modifyList(default_data, data)

  if (as_is) {
    format <- NULL

    if (identical(ext, "html")) {
      data$as_is <- TRUE
      template <- rmarkdown_template(pkg, "article", depth = depth, data = data)
      output <- rmarkdown::default_output_format(input_path)

      # Override defaults & values supplied in metadata
      options <- list(
        template = template$path,
        self_contained = FALSE
      )
      if (output$name != "rmarkdown::html_vignette") {
        # Force to NULL unless overridden by user
        options$theme <- output$options$theme
      }
    } else {
      options <- list()
    }
  } else {
    format <- build_rmarkdown_format(
      pkg = pkg,
      name = "article",
      depth = depth,
      data = data,
      toc = TRUE
    )
    options <- NULL
  }

  render_rmarkdown(
    pkg,
    input = input,
    output = output_file,
    output_format = format,
    output_options = options,
    quiet = quiet
  )
}

build_rmarkdown_format <- function(pkg,
                                   name,
                                   depth = 1L,
                                   data = list(),
                                   toc = TRUE) {

  template <- rmarkdown_template(pkg, name, depth = depth, data = data)

  out <- rmarkdown::html_document(
    toc = toc,
    toc_depth = 2,
    self_contained = FALSE,
    theme = NULL,
    template = template$path,
    anchor_sections = FALSE
  )
  out$knitr$opts_chunk <- fig_opts_chunk(pkg$figures, out$knitr$opts_chunk)

  old_pre <- out$pre_knit
  out$pre_knit <- function(...) {
    options(width = purrr::pluck(pkg, "meta", "code", "width", .default = 80))
    if (is.function(old_pre)) {
      old_pre(...)
    }
  }

  # Surgically eliminate html_dependency_header_attrs() whichs otherwise
  # injects javascript that breaks our HTML anchor system
  pre_process_env <- environment(environment(out$pre_processor)$base)
  pre_process_env$html_dependency_header_attrs <- function() NULL

  attr(out, "__cleanup") <- template$cleanup

  out
}

# Generates pandoc template format by rendering
# inst/template/article-vignette.html
# Output is a path + environment; when the environment is garbage collected
# the path will be deleted
rmarkdown_template <- function(pkg, name, data, depth) {
  path <- tempfile(fileext = ".html")
  render_page(pkg, name, data, path, depth = depth, quiet = TRUE)

  # Remove template file when format object is GC'd
  e <- env()
  reg.finalizer(e, function(e) file_delete(path))

  list(path = path, cleanup = e)
}

# Articles index ----------------------------------------------------------

#' @export
#' @rdname build_articles
build_articles_index <- function(pkg = ".") {
  pkg <- as_pkgdown(pkg)

  dir_create(path(pkg$dst_path, "articles"))
  render_page(
    pkg,
    "article-index",
    data = data_articles_index(pkg),
    path = path("articles", "index.html")
  )
}

data_articles_index <- function(pkg = ".") {
  pkg <- as_pkgdown(pkg)

  meta <- pkg$meta$articles %||% default_articles_index(pkg)
  sections <- meta %>%
    purrr::map(data_articles_index_section, pkg = pkg) %>%
    purrr::compact()

  # Check for unlisted vignettes
  listed <- sections %>%
    purrr::map("contents") %>%
    purrr::map(. %>% purrr::map_chr("name")) %>%
    purrr::flatten_chr() %>%
    unique()
  missing <- !(pkg$vignettes$name %in% listed)

  if (any(missing)) {
    warning(
      "Vignettes missing from index: ",
      paste(pkg$vignettes$name[missing], collapse = ", "),
      call. =  FALSE,
      immediate. = TRUE
    )
  }

  print_yaml(list(
    pagetitle = tr_("Articles"),
    sections = sections
  ))
}

data_articles_index_section <- function(section, pkg) {
  if (!set_contains(names(section), c("title", "contents"))) {
    warning(
      "Section must have components `title`, `contents`",
      call. = FALSE,
      immediate. = TRUE
    )
    return(NULL)
  }

  # Match topics against any aliases
  in_section <- select_vignettes(section$contents, pkg$vignettes)
  section_vignettes <- pkg$vignettes[in_section, ]
  contents <- tibble::tibble(
    name = section_vignettes$name,
    path = path_rel(section_vignettes$file_out, "articles"),
    title = section_vignettes$title,
    description = lapply(section_vignettes$description, markdown_text_block, pkg = pkg),
  )

  list(
    title = section$title,
    desc = markdown_text_block(section$desc, pkg = pkg),
    class = section$class,
    contents = purrr::transpose(contents)
  )
}

# Quick hack: create the same structure as for topics so we can use
# the existing select_topics()
select_vignettes <- function(match_strings, vignettes) {
  topics <- tibble::tibble(
    name = vignettes$name,
    alias = as.list(vignettes$name),
    internal = FALSE
  )
  select_topics(match_strings, topics)
}

default_articles_index <- function(pkg = ".") {
  pkg <- as_pkgdown(pkg)

  print_yaml(list(
    list(
      title = tr_("All vignettes"),
      desc = NULL,
      contents = paste0("`", pkg$vignettes$name, "`")
    )
  ))
}

article_is_intro <- function(name, package) {
  package <- gsub(".", "-", package, fixed = TRUE)
  name %in% c(package, paste0("articles/", package))
}
The diff you're trying to view is too large. Only the first 1000 changed files have been loaded.
Showing with 0 additions and 0 deletions (0 / 0 diffs computed)
swh spinner

Computing file changes ...

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API