# install.packages('pak')
::pak('mjfrigaard/shinypak') pak
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:
install.packages('pak')
::pak("devOpifex/leprechaun")
paklibrary(leprechaun)
::pak("JohnCoene/packer")
paklibrary(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.9000 | 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. |
24.1 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)
::run() lap
Launch app with the shinypak
package:
launch('24_leprechaun')
24.2 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 files
24.3 Development
The sections below outline the steps for creating the sap
movies review app from scratch using leprechaun
.
24.3.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_*.R
file (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
<- function(id){
varsUI <- NS(id)
ns
tagList(
h2("vars")
)
}
#' vars Server
#'
#' @param id Unique id for module instance.
#'
#' @keywords internal
<- function(id){
vars_server moduleServer(
id,function(
input,
output,
session
){
<- session$ns
ns <- make_send_message(session)
send_message
# your code here
}
)
}
# UI
# varsUI('id')
# server
# vars_server('id')
Run
R/run.R
provides 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.R
file 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.3.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.3.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.png
Add file path to the UI:
::card_header(
bslib$h4(tags$em("Brought to you by ",
tags$img(
tagssrc = "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
::run() lap
24.3.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.3.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.R
Assign the output from make_send_message()
to send_message()
in R/server.R
, then pass the msgId
and text
of the message:
<- make_send_message(session)
send_message 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:
::run() lap
24.3.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 files
The 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
::build()
leprechaun
✔ Running packer.R
✔ Bundled ✔ Running sass.R
Once again, load, document(), and install lap
, then launch the application with run()
:
Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B
::run() lap
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.js
These folders are necessary for adding JavaScript/SCSS functionality, so don’t remove them (just make sure they’re added to the .Rbuildignore
/.gitignore
)
24.3.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.3.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.3.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.3.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.3.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
::load_all(
pkgloadreset = 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()
::build()
leprechaun
::load_all(
pkgloadpath = "../../",
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
testthat
andshinytest2
can 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 theDESCRIPTION
file (or edit this manually).↩︎leprechaun::scaffold()
has arguments forui
(fluidPage
ornavbarPage
)bs_version
(bootstrap version) andoverwrite
(if you need to start over).↩︎R/zzz.R
files 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.js
andwebpack.prod.js
are added to the.Rbuildignore
(andnode_modules/
is added to the.gitignore
).↩︎The
scss
folder in the root directory is used byleprechaun::build()
to createinst/tidy-data/sass.R
.↩︎