Package Development

Use pkg::fun() inside package code (or @importFrom pkg fun); never call library() in a package.

44.1 Create a package

install.packages(c("devtools", "usethis", "roxygen2", "testthat"))
usethis::create_package("~/mypkg")
usethis::use_git(); usethis::use_github()
usethis::use_mit_license("Your Name")
usethis::use_r("greet")                 # creates R/greet.R
usethis::use_test("greet")              # creates tests/testthat/test-greet.R
usethis::use_package("dplyr")           # add to Imports in DESCRIPTION
usethis::use_readme_rmd(); usethis::use_news_md(); usethis::use_vignette("intro")
usethis::use_pkgdown(); usethis::use_github_action("check-standard")
usethis::use_data(my_dataset)           # bundle data

44.2 Package structure

mypkg/
├── DESCRIPTION        # metadata, dependencies (Imports, Suggests), version
├── NAMESPACE          # exports/imports (generated by roxygen2)
├── R/                 # R code
├── man/               # documentation (generated)
├── tests/testthat/    # unit tests
├── vignettes/         # long-form docs
├── data/              # exported datasets
├── inst/              # extra files (inst/extdata)
├── src/               # C/C++/Rust code
└── README.Rmd, NEWS.md, LICENSE

44.3 Documentation with roxygen2

Use pkg::fun() inside package code (or @importFrom pkg fun); never call library() in a package.

#' Greet someone
#'
#' @param name A character string with the person's name.
#' @param punct Punctuation to append. Defaults to "!".
#' @return A greeting string.
#' @examples
#' greet("Ann")
#' @export
greet <- function(name, punct = "!") {
  stopifnot(is.character(name))
  paste0("Hello, ", name, punct)
}
devtools::document()     # generate man/ and NAMESPACE

44.4 Testing with testthat

test_that("greet works", {
  expect_equal(greet("Ann"), "Hello, Ann!")
  expect_type(greet("x"), "character")
  expect_error(greet(1))
  expect_snapshot(greet("Bob", "?"))
  expect_true(nchar(greet("A")) > 0)
  expect_warning(as.numeric("x"))
  expect_length(letters, 26)
})
devtools::test(); covr::package_coverage()

44.5 The development loop

devtools::load_all()     # Ctrl+Shift+L — load the package without installing
devtools::document()     # Ctrl+Shift+D
devtools::test()         # Ctrl+Shift+T
devtools::check()        # Ctrl+Shift+E — R CMD check (must pass with 0 errors/warnings/notes for CRAN)
devtools::install()
devtools::build()
pkgdown::build_site()    # documentation website

44.6 Publishing

CRAN: follow the CRAN policies, devtools::check(remote = TRUE, manual = TRUE), rhub::rhub_check(), devtools::check_win_devel(), usethis::use_release_issue(), devtools::submit_cran().

R-universe — build & host packages from GitHub.

Bioconductor — its own review process.

GitHub — pak::pak("user/repo").

Read *R Packages* (Wickham & Bryan, free online) for the full process.