install.packages('pak')
pak::pak("devOpifex/leprechaun")
library(leprechaun)24 leprechaun
This chapter walks through building a version of the sap with the leprechaun framework. The resulting app-package (lap) is in the 24_leprechaun branch. For more information on buidling an application with the leprechaun framework, see my supplemental Shiny frameworks website.
Install leprechaun and packer to get started:
pak::pak("JohnCoene/packer")
library(packer)Iβve included the version/description of both the leprechaun and packer packages weβll use for the app-package:
| Package | Version | Title | Description |
|---|---|---|---|
| leprechaun | 1.0.0.9001 | Create Simple 'Shiny' Applications as Packages | Code generator for robust dependency-free 'Shiny' applications in the form of packages. It includes numerous convenience functions to create modules, include utility functions to create common 'Bootstrap' elements, setup a project from the ground-up, and much more. |
| packer | 0.1.3 | An Opinionated Framework for Using 'JavaScript' | Enforces good practice and provides convenience functions to make work with 'JavaScript' not just easier but also scalable. It is a robust wrapper to 'NPM', 'yarn', and 'webpack' that enables to compartmentalize 'JavaScript' code, leverage 'NPM' and 'yarn' packages, include 'TypeScript', 'React', or 'Vue' in web applications, and much more. |
lap (a leprechaun app-package)
After checking out the 24_leprechaun branch, be sure to load, document, and install the application.
Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B
lap exports the movies data and the standalone app function, run().
library(lap)
lap::run()Launch app with the shinypak package:
launch('24_leprechaun')24.1 Set up
Creating a new leprechaun app-package can be done with usethis::create_package()1 (which we covered in the packages chapter). After your app-package is created, leprechaun::scaffold() builds the core app files:2
lap/
βββ DESCRIPTION
βββ NAMESPACE
βββ R/
β βββ _disable_autoload.R
β βββ assets.R
β βββ input-handlers.R
β βββ leprechaun-utils.R
β βββ run.R
β βββ server.R
β βββ ui.R
β βββ zzz.R
βββ inst/
β βββ assets
β βββ dev
β βββ img
β βββ run
β βββ app.R
βββ lap.Rproj
7 directories, 12 files24.2 Development
The sections below outline the steps for creating the sap movies review app from scratch using leprechaun.
24.2.1 The R/ folder
All leprechaun apps come with primary app UI and server functions (R/ui.R and R/server.R) and a standalone app function (R/run.R.).
UI
ui()defines the overall UI structure usingnavbarPage().assets()combines the output ofserveAssets()(which gathers JS and CSS frominst/assets)
Server
server()contains the server logic for the application.send_message <- make_send_message(session)sets up a utility that lets you easily send messages from R to JavaScript
Modules
Create new modules with
leprechaun::add_module().- This creates a new
R/module_*.Rfile (see example below).
- This creates a new
show/hide R/module_vars.R file
#' vars UI
#'
#' @param id Unique id for module instance.
#'
#' @keywords internal
varsUI <- function(id){
ns <- NS(id)
tagList(
h2("vars")
)
}
#' vars Server
#'
#' @param id Unique id for module instance.
#'
#' @keywords internal
vars_server <- function(id){
moduleServer(
id,
function(
input,
output,
session
){
ns <- session$ns
send_message <- make_send_message(session)
# your code here
}
)
}
# UI
# varsUI('id')
# server
# vars_server('id')Run
R/run.Rprovides functions to launch your Shiny application in different modes:run()is the main function that users will call to launch your application in normal mode.run_dev()is for development purposes and launches a development version of the app.
Assets
The
R/assets.Rfile contains the following functions:serveAssets(): scans your package for JavaScript and CSS files, separates regular JavaScript files from βmoduleβ type JavaScript files, and bundles everything as proper HTML dependencies that Shiny can use.remove_modules(): filters out the module files from the list of all JavaScript files.get_modules(): extracts only the module files from the list of all JavaScript files.collapse_files(): creates a regular expression pattern to match specific files.
Weβll use the functions in R/assets.R via the assets() function in the R/ui.R file, which calls serveAssets() to include all any front-end resources in our Shiny app.
Input handlers
R/input-handlers.R sets up custom data handling between your JavaScript front-end and R back-end.
leprechaun utilities
The R/leprechaun-utils.R file provides a utility function (make_send_message()) β a helper to send custom messages from the server to the front-end (JavaScript) in a namespace-aware and modular way.
R/zzz.R
.onLoad() creates a static file server path for images and makes inst/img accessible to the web browser.3
24.2.2 The inst/ folder
leprechaun::scaffold() creates the following folders in inst/:
inst/assets/
Contains any JavaScript and CSS files that are part of the application frontend.
inst/dev/
Optional directory used for custom dev scripts, build tools, or staging.
inst/img/
Holds static image files like logos, icons, or other assets we want to display in the UI.
inst/run
Contains the app.R file used for development mode (i.e., with lap::run_dev()).
24.2.2.1 Adding images
Adding images is simplified with the .onLoad() function, which we can demonstrate by adding an image file to the UI function.
Place leprechaun-logo.png in inst/img/:
inst/
βββ img/
βββ leprechaun-logo.pngAdd file path to the UI:
bslib::card_header(
tags$h4(tags$em("Brought to you by ",
tags$img(
src = "img/leprechaun-logo.png",
height = 100,
width = 100,
style = "margin:10px 10px"
)
)
)
)Load, document, and install the package, then launch the application with run():
Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B
lap::run()24.2.2.2 use and build
The leprechaun workflow involves a combination of use_ functions that are combined with leprechaun::build() to add various functionality to your application. Iβll demonstrate adding JavaScript and SCSS below.
24.2.2.3 Use JavaScript
Using packer
Weβll use the
make_send_message()from the JavaScript example on the package website:- Donβt be alarmed when
scaffold_leprechaun()downloads multiple folders into the root directory. 4
Run
leprechaun::use_packer()
- Donβt be alarmed when
This creates inst/dev/packer.R and inst/assets/index.js.
inst
βββ assets
β βββ index.js
βββ dev
β βββ packer.R
βββ extdata
β βββ movies.RData
βββ img
βββ run
βββ app.RAssign the output from make_send_message() to send_message() in R/server.R, then pass the msgId and text of the message:
send_message <- make_send_message(session)
send_message("show-packer",
text = "a message from R/server.R")- Create
send_message()
- Use
send_message()to send a message the UI.
After loading, documenting, and installing your app-package:
Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B
Run the application:
lap::run()24.2.2.4 Use Sass
To add Sass styling, we can combine leprechaunβs use_sass() and build() functions.
leprechaun::use_sass() adds scss/ folder with _core.scss and main.scss.
scss
βββ _core.scss
βββ main.scss
1 directory, 2 filesThe original _core.scss file:
html{
.error {
color: red
}
}Change the color to green using $accent: #38B44A;
$accent: #38B44A;
html{
h4 {
color: $accent;
}
}Run leprechaun::build():5
leprechaun::build()
β Running packer.R
β Bundled
β Running sass.ROnce again, load, document(), and install lap, then launch the application with run():
Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B
lap::run()Itβs important to note that adding features to your leprechaun app-package will add non-standard directories to the root folder:
βββ node_modules/ <- too many sub-folders to list here!
βββ package-lock.json
βββ package.json
βββ scss/
β βββ _core.scss
β βββ main.scss
βββ srcjs/
β βββ config
β βββ index.js
β βββ modules
βββ webpack.common.js
βββ webpack.dev.js
βββ webpack.prod.jsThese folders are necessary for adding JavaScript/SCSS functionality, so donβt remove them (just make sure theyβre added to the .Rbuildignore/.gitignore)
24.2.3 Data
We can add data to lap the same way weβd add data to any R package.
inst/extdata/
Add the movies.RData file to inst/extdata/
data-raw/
Create the movies.R file in data-raw/ with usethis::use_data_raw("movies")
data/
Load the movies.RData in data-raw/movies.R and export the file to data/movies.rda using usethis::use_data().
R/
Document the dataset in R/data.R.
24.2.4 Documentation
leprechaun modules and functions include @keywords internal, which can be used in combination with a {pkgname}-package.R file. We can create this file using usethis::use_package_doc():
Add the following to lap-package.R.
#' @keywords internal
"_PACKAGE"The documentation for lap is intentionally sparce:
By default, run() is the only leprechaun funtion available in the package index (Iβve exported the theme, data, and utility functions).
24.2.5 Dependencies
leprechaun lists bslib, htmltools, and shiny when the app-package is initially buit. If using packer, this application is also added to the DESCRIPTION file with leprechaun::use_packer().
To keep track of the add-on packages, I recommend using the attachment package.
24.2.6 Tests
Unit tests in leprechaun apps should be written using the same process/tools as a standard R package/Shiny app.
Unit tests
Use testthat to set up the test suite and use_test() to create and open test files.
Integration tests
Use testServer to create tests for module server functions.
System tests
Use shinytest2 for system tests.
24.2.7 Deployment
To launch our app locally (or using rsconnect::deployApp()), leprechaun has a handy leprechaun::add_app_file() function that creates an app.R file in the root folder with the following contents:
# Launch the ShinyApp
# do not remove to keep push deploy button
# from RStudio
pkgload::load_all(
reset = TRUE,
helpers = FALSE
)
run()This file shouldnβt be confused with inst/run/app.R:
# do not deploy from this file
# see leprechaun::add_app_file()
leprechaun::build()
pkgload::load_all(
path = "../../",
reset = TRUE,
helpers = FALSE
)
run()The inst/run/app.R file is specifically used by the run_dev() function.
Recap
leprechaun app-packages have an unopinionated structure and extensive JavaScript frontend support.
Building modules is made easier with the
add_module()function (although the naming convention could be more consistent).The application is split into separate UI, server, and standalone app functions/files.
Testing support is not included (but
testthatandshinytest2can be easily implemented).
The leprechaun framework is ideal for any developer who is comfortable with JavaScript frameworks (e.g., Webpack, Babel, etc.) and want to build custom UIs with Shiny as a backend.
Remember to provide a list of fields to
usethis::create_package()for theDESCRIPTIONfile (or edit this manually).β©οΈleprechaun::scaffold()has arguments forui(fluidPageornavbarPage)bs_version(bootstrap version) andoverwrite(if you need to start over).β©οΈR/zzz.Rfiles are traditionally used for setup functions that run during package loading. The name ensures itβs loaded last alphabetically (though modern R doesnβt strictly require this naming convention anymore).β©οΈsrcjs/,package-lock.json,package.json,webpack.common.js,webpack.dev.jsandwebpack.prod.jsare added to the.Rbuildignore(andnode_modules/is added to the.gitignore).β©οΈThe
scssfolder in the root directory is used byleprechaun::build()to createinst/tidy-data/sass.R.β©οΈ





