Skip to content

Commit

Permalink
Merge pull request #67 from Appsilon/develop
Browse files Browse the repository at this point in the history
Release v0.3.0
  • Loading branch information
kamilzyla authored Feb 21, 2022
2 parents 1552be6 + 6878c81 commit 851d1f0
Show file tree
Hide file tree
Showing 38 changed files with 5,822 additions and 122 deletions.
3 changes: 2 additions & 1 deletion .lintr
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
linters: with_defaults(
linters:
with_defaults(
line_length_linter = line_length_linter(100),
infix_spaces_linter = NULL,
object_usage_linter = NULL
Expand Down
19 changes: 11 additions & 8 deletions 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: 0.2.0
Version: 0.3.0
Authors@R:
c(
person(given = "Kamil", family = "Zyla", role = "aut", email = "[email protected]"),
Expand All @@ -18,12 +18,15 @@ Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.1.2
Imports:
fs,
cli,
renv,
withr
Suggests:
lintr (>= 2.0.0),
testthat (>= 3.0.0)
cli,
glue,
fs,
lintr (>= 2.0.0),
renv,
sass,
styler,
testthat (>= 3.0.0),
withr,
yaml
Config/testthat/edition: 3
Config/testthat/parallel: true
14 changes: 8 additions & 6 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Generated by roxygen2: do not edit by hand

export(build_js)
export(build_sass)
export(format_r)
export(init)
importFrom(cli,cli_alert_success)
importFrom(fs,dir_copy)
export(lint_js)
export(lint_r)
export(lint_sass)
export(test_e2e)
export(test_r)
importFrom(fs,dir_create)
importFrom(fs,file_copy)
importFrom(fs,path)
importFrom(fs,path_package)
importFrom(renv,init)
importFrom(withr,with_dir)
86 changes: 16 additions & 70 deletions R/init.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,89 +5,35 @@
#'
#' @export
init <- function(dir = ".", github_actions_ci = TRUE) {
init_setup(dir)

create_app_structure(dir)

create_unit_tests_structure(dir)
create_e2e_tests_structure(dir)
if (isTRUE(github_actions_ci)) add_github_actions_ci(dir)

init_renv(dir)
}

#' @importFrom fs dir_create
#' @importFrom cli cli_alert_success
init_setup <- function(dir) {
dir_create(dir)
cli_alert_success("Application directory created")
}

#' @importFrom fs dir_copy file_copy path
#' @importFrom cli cli_alert_success
create_app_structure <- function(dir) {
file_copy(
path = path_rhino("app_structure", "app.R"),
new_path = dir
)

file_copy(
path = path_rhino("app_structure", "Rprofile"),
new_path = path(dir, ".Rprofile")
)

file_copy(
path = path_rhino("app_structure", "src.Rproj2"),
new_path = path(dir, "src.Rproj")
)

dir_copy(
path = path_rhino("app_structure", "app"),
new_path = dir
)

cli_alert_success("Application structure created")
copy_template("app_structure", dir)
cli::cli_alert_success("Application structure created")
}

#' @importFrom fs dir_create dir_copy path
#' @importFrom cli cli_alert_success
add_github_actions_ci <- function(dir) {
github_path <- path(dir, ".github")
dir_create(github_path)
dir_copy(
path = path_rhino("github_ci", "workflows"),
new_path = github_path
)

cli_alert_success("Github Actions CI added")
copy_template("github_ci", dir)
cli::cli_alert_success("Github Actions CI added")
}

#' @importFrom fs file_copy
#' @importFrom renv init
#' @importFrom withr with_dir
#' @importFrom cli cli_alert_success
init_renv <- function(dir) {
file_copy(
path = path_rhino("renv", "renvignore"),
new_path = path(dir, ".renvignore")
)

file_copy(
path = path_rhino("renv", "dependencies.R"),
new_path = path(dir)
)

with_dir(
dir,
renv::init(restart = FALSE)
)
copy_template("renv", dir)
withr::with_dir(dir, renv::init(restart = FALSE))
cli::cli_alert_success("renv initialized")
}

cli_alert_success("renv initiated")
create_unit_tests_structure <- function(dir) {
copy_template("unit_tests", dir)
cli::cli_alert_success("Unit tests structure created")
}

#' @importFrom fs path_package
path_rhino <- function(...) {
path_package(
"rhino",
"templates",
...
)
create_e2e_tests_structure <- function(dir) {
copy_template("e2e_tests", dir)
cli::cli_alert_success("E2E tests structure created")
}
25 changes: 25 additions & 0 deletions R/node.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
system_yarn <- function(...) {
status <- system2(
command = "yarn",
args = c("--cwd", shQuote(node_path()), ...)
)
if (status != 0) {
stop(glue::glue("yarn failed with exit status {status}"), call. = FALSE)
}
}

add_node <- function() {
copy_template("node", node_path())
fs::link_create(
path = fs::path("..", ".."),
new_path = fs::path(node_path(), "root")
)
}

yarn <- function(...) {
if (!fs::dir_exists(node_path())) {
add_node()
system_yarn("install")
}
system_yarn(...)
}
45 changes: 45 additions & 0 deletions R/rhino.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
read_config <- function() {
yaml::read_yaml("rhino.yml")
}

template_path <- function(...) {
fs::path_package("rhino", "templates", ...)
}

node_path <- function(...) {
fs::path(".rhino", "node", ...)
}

rename_template_path <- function(path) {
path <- fs::path_split(path)[[1]]
path <- sub("^dot\\.", ".", path)
path <- sub("\\.template$", "", path)
fs::path_join(path)
}

# Copy template from source path (relative to `inst/templates`) to destination
# with some renaming applied to the names of files and directories:
# 1. Leading `dot.` is replaced with `.`.
# 2. Trailing `.template` is removed.
copy_template <- function(src, dst) {
src <- template_path(src)
target <- function(path) {
path <- fs::path_rel(path, start = src)
path <- rename_template_path(path)
fs::path(dst, path)
}

fs::dir_create(dst)
fs::dir_walk(
path = src,
recurse = TRUE,
type = "directory",
fun = function(dir) fs::dir_create(target(dir))
)
fs::dir_walk(
path = src,
recurse = TRUE,
type = "file",
fun = function(file) fs::file_copy(file, target(file))
)
}
131 changes: 131 additions & 0 deletions R/tools.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#' Run R unit tests
#'
#' @importFrom fs path
#' @export
test_r <- function() {
testthat::test_dir(path("tests", "testthat"))
}

#' Lint R
#'
#' @param accepted_errors Number of accepted style errors.
#'
#' @export
lint_r <- function(accepted_errors = 0) {
lints <- c(
lintr::lint("app.R"),
lintr::lint_dir("app"),
lintr::lint_dir(path("tests", "testthat"))
)

style_errors <- length(lints)

if (style_errors > accepted_errors) {
print(lints)
stop(sprintf("Number of style errors: %s", style_errors))
}
}

#' Format R
#'
#' @param path File or directory to format
#'
#' @export
format_r <- function(path) {
if (fs::is_dir(path)) {
styler::style_dir(path)
} else {
styler::style_file(path)
}
}

#' Build JavaScript
#'
#' Builds the `app/js/index.js` file into `app/static/js/app.min.js`.
#' The code is transformed and bundled
#' using [Babel](https://babeljs.io) and [Webpack](https://webpack.js.org),
#' so the latest JavaScript features can be used
#' (including ECMAScript 2015 aka ES6 and newer standards).
#'
#' Functions/objects defined in the global scope do not automatically become `window` properties,
#' so the following JS code:
#' ```js
#' function sayHello() { alert('Hello!'); }
#' ```
#' won't work as expected if used in R like this:
#' ```R
#' tags$button("Hello!", onclick = 'sayHello()');
#' ```
#'
#' Instead you should explicitly export functions:
#' ```js
#' export function sayHello() { alert('Hello!'); }
#' ```
#' and access them via the global `App` object:
#' ```R
#' tags$button("Hello!", onclick = "App.sayHello()")
#' ```
#'
#' @export
build_js <- function() {
yarn("build-js")
}

# nolint start
#' Lint JavaScript
#'
#' Runs [ESLint](https://eslint.org) on the JavaScript sources in the `app/js` directory.
#'
#' If your code uses global objects defined by other JS libraries or R packages,
#' you'll need to let the linter know or it will complain about undefined objects.
#' For example, the `{leaflet}` package defines a global object `L`.
#' To access it without raising linter errors, add `/* global L */` comment in your JS code.
#'
#' You don't need to define `Shiny` and `$` as these globals are defined by default.
#'
#' If you find a particular ESLint error unapplicable to your code,
#' you can disable a specific rule for the next line of code with a comment like:
#' ```js
#' // eslint-disable-next-line no-restricted-syntax
#' ```
#' See the [ESLint documentation](https://eslint.org/docs/user-guide/configuring/rules#using-configuration-comments-1)
#' for full details.
#'
#' @export
# nolint end
lint_js <- function() {
yarn("lint-js")
}

#' Build Sass
#'
#' @importFrom fs dir_create path
#' @export
build_sass <- function() {
config <- read_config()$sass
if (config == "node") {
yarn("build-sass")
} else if (config == "r") {
output_dir <- path("app", "static", "css")
dir_create(output_dir)
sass::sass(
input = sass::sass_file(path("app", "styles", "main.scss")),
output = path(output_dir, "app.min.css"),
cache = FALSE
)
}
}

#' Lint Sass
#'
#' @export
lint_sass <- function() {
yarn("lint-sass")
}

#' Run Cypress end-to-end tests
#'
#' @export
test_e2e <- function() {
yarn("test-e2e")
}
Empty file.
4 changes: 3 additions & 1 deletion inst/templates/app_structure/app/r/main.R
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ ui <- function(id) {
ns <- NS(id)
bootstrapPage(
tags$head(
tags$link(rel = "icon", href = "static/favicon.ico", sizes = "any")
tags$link(rel = "icon", href = "static/favicon.ico", sizes = "any"),
tags$link(rel = "stylesheet", type = "text/css", href = "static/css/app.min.css"),
tags$script(src = "static/js/app.min.js")
),
tags$h3(
textOutput(ns("message"))
Expand Down
Empty file.
File renamed without changes.
6 changes: 6 additions & 0 deletions inst/templates/app_structure/dot.lintr
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
linters:
with_defaults(
line_length_linter = line_length_linter(100),
infix_spaces_linter = NULL,
object_usage_linter = NULL
)
1 change: 1 addition & 0 deletions inst/templates/app_structure/rhino.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sass: node
File renamed without changes.
5 changes: 5 additions & 0 deletions inst/templates/e2e_tests/tests/cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"baseUrl": "http://localhost:3333",
"pluginsFile": false,
"supportFile": false
}
2 changes: 2 additions & 0 deletions inst/templates/e2e_tests/tests/cypress/dot.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/screenshots/
/videos/
Loading

0 comments on commit 851d1f0

Please sign in to comment.