Skip to content

Commit

Permalink
Box universal import linter (#533)
Browse files Browse the repository at this point in the history
* box universal import linter

* add box_universal_import_linter to .lintr template

* documentation

* package housekeeping

* removed some lint

* namespace box_universal_import_linter

* reordered Suggests

* update e2etest app files to rhino-box styleguide

* address comments/suggestions in PR

* fixed version number issue. moved xml2 to imports
  • Loading branch information
radbasa authored Jan 26, 2024
1 parent a1b184e commit fea7b83
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 5 deletions.
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: rhino
Title: A Framework for Enterprise Shiny Applications
Version: 1.6.0.9000
Version: 1.6.0.9001
Authors@R:
c(
person("Kamil", "Żyła", role = c("aut", "cre"), email = "[email protected]"),
Expand Down Expand Up @@ -38,12 +38,14 @@ Imports:
testthat (>= 3.0.0),
utils,
withr,
xml2,
yaml
Suggests:
covr,
knitr,
mockery,
rcmdcheck,
rex,
rmarkdown,
shiny.react,
spelling
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated by roxygen2: do not edit by hand

export(app)
export(box_universal_import_linter)
export(build_js)
export(build_sass)
export(diagnostics)
Expand Down
57 changes: 57 additions & 0 deletions R/box_linters.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#' Box library universal import linter
#'
#' Checks that all function imports are explicit. `package[...]` is not used.
#'
#' @return A custom linter function for use with `r-lib/lintr`
#'
#' @examples
#' # will produce lints
#' lintr::lint(
#' text = "box::use(base[...])",
#' linters = box_universal_import_linter()
#' )
#'
#' lintr::lint(
#' text = "box::use(path/to/file[...])",
#' linters = box_universal_import_linter()
#' )
#'
#' # okay
#' lintr::lint(
#' text = "box::use(base[print])",
#' linters = box_universal_import_linter()
#' )
#'
#' lintr::lint(
#' text = "box::use(path/to/file[do_something])",
#' linters = box_universal_import_linter()
#' )
#'
#' @export
box_universal_import_linter <- function() {
lint_message <- "Explicitly declare imports rather than universally import with `...`."

xpath <- "
//SYMBOL_PACKAGE[(text() = 'box' and following-sibling::SYMBOL_FUNCTION_CALL[text() = 'use'])]
/parent::expr
/parent::expr
//SYMBOL[text() = '...']
"

lintr::Linter(function(source_expression) {
if (!lintr::is_lint_level(source_expression, "file")) {
return(list())
}

xml <- source_expression$full_xml_parsed_content

bad_expr <- xml2::xml_find_all(xml, xpath)

lintr::xml_nodes_to_lints(
bad_expr,
source_expression = source_expression,
lint_message = lint_message,
type = "style"
)
})
}
1 change: 1 addition & 0 deletions inst/templates/app_structure/dot.lintr
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
linters:
linters_with_defaults(
line_length_linter = line_length_linter(100),
box_universal_import_linter = rhino::box_universal_import_linter(),
object_usage_linter = NULL # Does not work with `box::use()`.
)
38 changes: 38 additions & 0 deletions man/box_universal_import_linter.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkgdown/_pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ reference:
- diagnostics
- test_e2e

- title: Linters
contents:
- box_universal_import_linter

- title: Data
contents:
- rhinos
4 changes: 2 additions & 2 deletions tests/e2e/app-files/test-hello.R
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
box::use(
shiny[testServer],
testthat[...],
testthat[describe, expect_identical, it],
)
box::use(
app/view/hello[...],
app/view/hello[server],
)

describe("hello$server()", {
Expand Down
4 changes: 2 additions & 2 deletions tests/e2e/app-files/test-say_hello.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
box::use(testthat[...])
box::use(testthat[describe, expect_identical, it], )

box::use(app/logic/say_hello[say_hello])
box::use(app/logic/say_hello[say_hello], )

describe("say_hello()", {
it("should say hello with the correct name", {
Expand Down
73 changes: 73 additions & 0 deletions tests/testthat/test-linter_box_universal_import_linter.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
test_that("box_universal_count_linter skips allowed package import usage", {
linter <- box_universal_import_linter()

good_package_imports <- "box::use(
dplyr[select, mutate, ],
stringr[str_sub, str_match, ],
)
"

lintr::expect_lint(good_package_imports, NULL, linter)
})

test_that("box_universal_count_linter skips allowed module import usage", {
linter <- box_universal_import_linter()

good_module_imports <- "box::use(
path/to/file1[do_something, do_another, ],
path/to/file2[find_x, find_y, ],
)
"

lintr::expect_lint(good_module_imports, NULL, linter)
})

test_that("box_universal_count_linter blocks disallowed package import usage", {
linter <- box_universal_import_linter()

bad_package_imports <- "box::use(
dplyr[...],
stringr[str_sub, str_match, ],
)
"

lint_msg <- rex::rex("Explicitly declare imports rather than universally import with `...`.")

lintr::expect_lint(bad_package_imports, list(message = lint_msg), linter)
})

test_that("box_universal_count_linter blocks disallowed module import usage", {
linter <- box_universal_import_linter()

bad_module_imports <- "box::use(
path/to/file1[...],
path/to/file2[find_x, find_y, ],
)
"

lint_msg <- rex::rex("Explicitly declare imports rather than universally import with `...`.")

lintr::expect_lint(bad_module_imports, list(message = lint_msg), linter)
})

test_that("box_universal_count_linter skips three dots in function declarations and calls", {
linter <- box_universal_import_linter()

function_with_three_dots <- "some_function <- function(...) {
sum(...)
}
"

lintr::expect_lint(function_with_three_dots, NULL, linter)
})

test_that("box_universal_count_linter respects #nolint", {
linter <- box_universal_import_linter()

no_lint <- "box::use(
shiny[...], # nolint
)
"

lintr::expect_lint(no_lint, NULL, linter)
})

0 comments on commit fea7b83

Please sign in to comment.