Extract and visualize dependencies
- Dominant language
- R
- Stars
- 979
- Forks
- 49
- PR merge metrics
- No merged PRs in 30d
Description
### Please describe your feature request
Great package Konrad! I love it and find it very useful for complicated projects.
One thing that I was missing every now and then was a way to extract and maybe visualize dependencies of a file/function.
Do you think something like this is worth adding to `box`?
I'd be happy to create a proper draft PR (with less dependencies, cleaner code, tests, etc).
# Quick Example
A quick-and-dirty function I threw together looks like this: `get_box_dependencies ` which either takes a file or a text and reports its dependencies:
```R
library(stringr)
# extracts the box dependencies from a file or a text string
get_box_dependencies <- function(file = NULL, text = NULL) {
stopifnot(
"Either file or text must be provided but not both" = xor(is.null(file),
is.null(text)))
if (!is.null(file)) text <- paste(readLines(file), collapse = "\n")
# remove commented code
x <- text |> str_replace_all("#[^\\n]+", "")
# extract box dependencies
box_txt <- str_extract_all(x, "(?<=box::use\\()[^\\)]*") |>
unlist() |>
paste(collapse = "\n") |>
str_replace_all("\\n", " ")
if (nchar(box_txt) == 0) {
return(tibble::tibble(
type = character(0),
dependency = character(0),
exports = list(),
exports_agg = character(0)
))
}
# [a-zA-Z0-9\\.\\/]+ Name regex for the packages/files
# (\\[[^\\]]+\\])? Maybe followed by text in []
deps <- str_extract_all(box_txt, "[a-zA-Z0-9_\\.\\/]+(\\[[^\\]]+\\])?")[[1]]
is_file <- str_detect(deps, "\\/")
reps_res <- deps |> str_replace_all("\\[[^\\]]+\\]", "")
export_names <- deps |>
str_extract_all("(?<=\\[)[^\\]]+(?=\\])") |>
sapply(str_split, pattern = ", *")
tibble::tibble(
type = ifelse(is_file, "file", "package"),
dependency = ifelse(!endsWith(reps_res, ".R") & is_file,
paste0(reps_res, ".R"), reps_res),
exports = export_names,
exports_agg = sapply(export_names, \(x) paste(unlist(x), collapse = " "))
)
}
```
An example of this function is this
```R
test_string <- "
box::use(
pkg1[fun1, fun2,
fun3, fun4],
pkg2[...],
pkg3
# pkg4[fun5] not used
)
box::use(
./file1 ,
./file2[ffun1, ffun2,
ffun3],
folder/file3[...]
)
"
get_box_dependencies(text = test_string)
#> # A tibble: 6 × 4
#> type dependency exports exports_agg
#>
#> 1 package pkg1 "fun1 fun2 fun3 fun4"
#> 2 package pkg2 "..."
#> 3 package pkg3 ""
#> 4 file ./file1.R ""
#> 5 file ./file2.R "ffun1 ffun2 ffun3"
#> 6 file folder/file3.R "..."
```
A larger (but non-reproducible) example output is this code which lists all dependencies and its connections in a directory `app/`
```R
files <- list.files("app", pattern = "\\.R$", full.names = TRUE, recursive = TRUE)
> res <- purrr::map_dfr(files, get_box_dependencies, .id = "file") |>
+ dplyr::mutate(file = files[as.numeric(file)])
> res
# A tibble: 116 × 5
file type dependency exports exports_agg
1 app/logic/misc.R package shiny div
2 app/logic/misc.R package shinyWidgets downloadBttn
3 app/logic/misc.R package data.table ...
4 app/logic/misc.R package dplyr group_by
5 app/main.R package shiny div h1 NS tagList moduleServer sliderInput i…
```
**Caveats** to the solution above:
- this does not work when `box` is used within a function. Eg the following `bar` dependency is picked up but is not reported to belong to `foo`
```r
#' @export
foo <- function(...) {
box::use(bar[baz])
}
```
- At the moment the function depends on `stringr` and `tibble` (to a lesser degree) and lots of regex. This can be brought down if needed.
Contributor guide
Research direction
No repository files or tests are named; start by reading the issue's prototype get_box_dependencies and the box::use syntax it parses. Define the supported extraction and visualization scope, including files, packages, exports, and function-scoped use, then validate the result against the examples and caveats in the issue.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- r
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100