R/add_dependencies.R

Defines functions add_dependencies

Documented in add_dependencies

#' Add dependencies in DESCRIPTION
#' 
#' @description 
#' This function detects external dependencies used in `R/`, `NAMESPACE`, and 
#' `@examples` sections of `roxygen2` headers and automatically adds these 
#' dependencies in the `Imports` section of the `DESCRIPTION` file.
#' 
#' In the `NAMESPACE` this function detects dependencies mentioned as 
#' `import(pkg)` and `importFrom(pkg,fun)`.
#' 
#' In the `R/` folder it detects functions called as `pkg::fun()` in the code
#' of each R files. In `@examples` sections it also detects packages attached
#' by `library()` or `require()`.
#' 
#' The `vignettes/` folder is also inspected and detected dependencies 
#' (`pkg::fun()`, `library()` or `require()`) are added to the `Suggests` 
#' field of the `DESCRIPTION` file (in addition to the packages 
#' [`knitr`](https://yihui.org/knitr/) and 
#' [`rmarkdown`](https://rmarkdown.rstudio.com/)).
#' 
#' If the project is a research compendium user can also inspect additional 
#' folder(s) with the argument `compendium` to add dependencies to the 
#' `Imports` section of the `DESCRIPTION` file. The detection process is the 
#' same as the one used for `vignettes/`. 
#' 
#' The `tests/` folder is also inspected and detected dependencies 
#' (`pkg::fun()`, `library()` or `require()`) are added to the `Suggests` 
#' field of the `DESCRIPTION` file (in addition to the package 
#' [`testthat`](https://testthat.r-lib.org)).
#' 
#' @param compendium A character of length 1. The name of the folder to 
#'   recursively detect dependencies to be added to the `Imports` field of 
#'   `DESCRIPTION` file. It can be `'analysis/'` (if additional folders, i.e. 
#'   `data/`, `outputs/`, `figures/`, etc. have been created in this folder), 
#'   `'.'` (if folders `data/`, `outputs/`, `figures/`, etc. have been created 
#'   at the root of the project), etc. 
#'   See [new_compendium()] for further information.
#'   
#'   Default is `compendium = NULL` (i.e. no additional folder are inspected 
#'   but `R/`, `NAMESPACE`, `vignettes/`, and `tests/` are still inspected).
#' 
#' @return No return value.
#'   
#' @export
#' 
#' @family development functions
#'
#' @examples
#' \dontrun{
#' add_dependencies()
#' }

add_dependencies <- function(compendium = NULL) {


  ## Checks ----

  if (!is.null(compendium))  stop_if_not_string(compendium)

  is_package()
  path <- path_proj()
  
  
  ## If no R function ----
  
  # if (!dir.exists(file.path(path, "R"))) stop("No 'R/' folder found.")
  
  
  ## Update Documentation & NAMESPACE ----
  
  # suppressMessages(devtools::document(quiet = TRUE))
  
  
  ## Detect Dependencies in NAMESPACE ----
  
  deps_in_namespace <- get_deps_in_namespace()
  
  
  ## Detect Dependencies in R/ ----
  
  deps_in_functions <- get_deps_in_functions_r()
  
  
  ## Detect Dependencies in compendium ----
  
  deps_extra <- get_deps_extra(compendium)
  
  
  ## Merge in-functions ----
  
  deps_in_functions <- c(deps_in_functions, deps_extra)
  deps_in_functions <- sort(unique(deps_in_functions))
  
  
  ## Detect duplicates ----
  
  duplicated_deps <- deps_in_functions[which(deps_in_functions %in% 
                                             deps_in_namespace)]
  
  if (length(duplicated_deps)) {
    
    duplicated_deps <- sort(duplicated_deps)
    duplicated_deps <- paste0(duplicated_deps, "()")
    
    ui_info(paste0("The following function(s) are already present in ",
                   "{ui_value('NAMESPACE')}:"))
    ui_line("{ui_code(duplicated_deps)}")
    ui_todo("You can omit package name when calling these functions")
    cli::cat_line()
  }

  
  ## Detect Dependencies in @examples ----
  
  deps_in_examples <- get_deps_in_examples()
  
  
  ## Merge Dependencies ----
  
  deps_in_package <- c(deps_in_namespace, deps_in_functions, deps_in_examples)
  
  if (length(deps_in_package)) {
    
    deps_in_package <- unlist(lapply(strsplit(deps_in_package, "::"), 
                                     function(x) x[1]))
  }
  
  deps_in_package <- sort(unique(deps_in_package))
  
  
  ## Detect Dependencies VIGNETTES ----
  
  deps_suggest <- get_deps_in_vignettes()
  
  if (length(deps_suggest)) {
    
    deps_suggest <- unlist(lapply(strsplit(deps_suggest, "::"), 
                                  function(x) x[1]))
  }
  
  deps_suggest <- sort(unique(deps_suggest))
  
  
  ## Detect Dependencies TESTS ----
  
  deps_test <- get_deps_in_tests()
  
  if (length(deps_test)) {
    
    deps_test <- unlist(lapply(strsplit(deps_test, "::"), function(x) x[1]))
    
  }
  
  deps_suggest <- sort(unique(c(deps_suggest, deps_test)))
  
  
  ## Remove Package Name ----
  
  package_name <- get_package_name()
  
  deps_in_package <- deps_in_package[!(deps_in_package %in% package_name)]
  deps_suggest    <- deps_suggest[!(deps_suggest %in% package_name)]
  
  
  ## Remove Base packages ----
  
  deps_in_package <- deps_in_package[!(deps_in_package %in% "base")]
  deps_suggest    <- deps_suggest[!(deps_suggest %in% "base")]
  
  
  ## Remove duplicates ----
  
  if (length(deps_suggest)) {
    deps_suggest <- deps_suggest[!(deps_suggest %in% deps_in_package)]
    deps_suggest <- sort(unique(deps_suggest))
  }
  
  deps_in_package <- sort(unique(deps_in_package))

  
  
  ## Read DESCRIPTION File ----
  
  descr <- read_descr()
  
  
  ## Dependencies in DEPENDS ----
  
  pkgs_in_depends <- get_deps_in_depends()
  
  ui_done("Scanning {ui_value('Depends')} dependencies")
  
  if (!length(pkgs_in_depends)) {
  
    ui_line("  {cli::symbol$radio_on} No package found")
  
  } else {
  
    ui_line(paste0("  {cli::symbol$radio_on} Found ", 
                   "{ui_value(length(pkgs_in_depends))} package(s)"))
  }
  
  
  ## Dependencies in IMPORTS ----
  
  ui_done("Scanning {ui_value('Imports')} dependencies")
  
  pkgs_in_imports <- sort(deps_in_package[!(deps_in_package %in% 
                                              pkgs_in_depends)])
  
  if (length(pkgs_in_imports)) {
    
    # keep packages versions
    pkgs_with_version <- c(get_deps_in_imports(), get_deps_in_suggests())
    pkgs_with_version <- pkgs_with_version[grep("\\(.*\\)", pkgs_with_version)]
    
    if (length(pkgs_with_version)) {
      
      pkgs <- unlist(lapply(strsplit(pkgs_with_version, "\\s\\("), 
                            function(x) x[1]))
      for (i in seq_len(length(pkgs))) {
        pos <- which(pkgs_in_imports == pkgs[i])
        if (length(pos)) pkgs_in_imports[pos] <- pkgs_with_version[i]
      }
    }
    
    # Message
    
    ui_line(paste0("  {cli::symbol$radio_on} Found ", 
                   "{ui_value(length(pkgs_in_imports))} package(s)")) 
    
    msg <- paste0("Imports: ", paste0(pkgs_in_imports, collapse = ", "))
    ui_line(paste0("  {cli::symbol$radio_on} Adding the following ", 
                   "line in {ui_value('DESCRIPTION')}: {ui_code(msg)}"))

    pkgs_in_imports <- paste0(pkgs_in_imports, collapse = ",\n    ")
    descr$"Imports" <- paste0("\n    ", pkgs_in_imports)

  } else {
    
    ui_line("  {cli::symbol$radio_on} No package found")
    
    pos <- which(colnames(descr) == "Imports")
    if (length(pos)) descr <- descr[ , -pos]
  }
  
  
  
  ## Dependencies in SUGGESTS ----
  
  ui_done("Scanning {ui_value('Suggests')} dependencies")
  
  pkgs_in_suggests <- sort(deps_suggest[!(deps_suggest %in% pkgs_in_depends)])
  
  if (length(pkgs_in_suggests)) {
    
    # keep packages versions
    pkgs_with_version <- c(get_deps_in_imports(), get_deps_in_suggests())
    pkgs_with_version <- pkgs_with_version[grep("\\(.*\\)", pkgs_with_version)]
    
    if (length(pkgs_with_version)) {
      
      pkgs <- unlist(lapply(strsplit(pkgs_with_version, "\\s\\("), 
                            function(x) x[1]))
      for (i in seq_len(length(pkgs))) {
        pos <- which(pkgs_in_suggests == pkgs[i])
        if (length(pos)) pkgs_in_suggests[pos] <- pkgs_with_version[i]
      }
    }
    
    
    # Message
    
    ui_line(paste0("  {cli::symbol$radio_on} Found ", 
                   "{ui_value(length(pkgs_in_suggests))} package(s)"))
    
    msg <- paste0("Suggests: ", paste0(pkgs_in_suggests, collapse = ", "))
    ui_line(paste0("  {cli::symbol$radio_on} Adding the following ", 
                   "line in {ui_value('DESCRIPTION')}: {ui_code(msg)}"))
    
    
    pkgs_in_suggests <- paste0(pkgs_in_suggests, collapse = ",\n    ")
    descr$"Suggests" <- paste0("\n    ", pkgs_in_suggests)
    
  } else {
    
    ui_line("  {cli::symbol$radio_on} No package found")
    
    pos <- which(colnames(descr) %in% c("Suggests", "VignetteBuilder"))
    if (length(pos)) descr <- descr[ , -pos]
  }
  
  
  ## Rewrite DESCRIPTION ----
  
  write_descr(descr)
  
  invisible(NULL)
}
FRBCesab/rcompendium documentation built on Nov. 15, 2024, 9:38 p.m.