Skip to content

Commit

Permalink
Merge pull request #20 from RinteRface/esbuild-improve
Browse files Browse the repository at this point in the history
Handle Sass
  • Loading branch information
DivadNojnarg authored Nov 9, 2022
2 parents 468cf6e + 8b35a3e commit 1fc43aa
Show file tree
Hide file tree
Showing 16 changed files with 219 additions and 55 deletions.
4 changes: 1 addition & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: charpente
Title: Seamlessly design robust 'shiny' extensions
Version: 0.1.0.9000
Version: 0.5.0.9000
Authors@R: c(
person(
given = "David",
Expand Down Expand Up @@ -28,7 +28,6 @@ Description: 'charpente' eases the creation of 'shiny' extensions like 'shinydas
more recently 'golem', that is make ('shiny') developer's life much easier.
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.1
Imports:
Expand All @@ -52,7 +51,6 @@ Imports:
desc,
gh
Remotes:
ThinkR-open/golem@dev,
JohnCoene/npm
URL: https://github.com/RinteRface/charpente
BugReports: https://github.com/RinteRface/charpente/issues
Expand Down
2 changes: 1 addition & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ export("%>%")
export(build_js)
export(charpente_options)
export(create_charpente)
export(create_css)
export(create_custom_handler)
export(create_dependency)
export(create_input_binding)
export(create_js)
export(create_manifest)
export(create_output_binding)
export(create_pwa_dependency)
export(create_scss)
export(get_dependency_assets)
export(get_dependency_versions)
export(get_installed_dependency)
Expand Down
21 changes: 20 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# charpente 0.0.0.9000
# charpent 0.5.0.9000

## Breaking change:
- Include Sass handling. SCSS files are sorted in the `/styles`folder and esbuild
has new Sass modules to treat them and generate CSS.
`package.json` now calls `node esbuild.dev.js` or `node esbuild.prod.js`,
depending on the selected mode, that is production or development.
`esbuild.**.js` is a new script which will be processed at run time in your project, before being called by `node` in `package.json`. If you come from an older
`{charpente}` version, `build_js()` will first try to create a `/styles` folder (which you normally don't have) and install missing Sass dependencies next to esbuild (`esbuild-sass-plugin`, `postcss`, `autoprefixer`).
- `get_dependency_assets()` leverages the new jsdlivr algorithm to infer the best entry point scripts for JS and CSS files, when downloading dependencies within `create_dependency()`.
This likely will change your vendors dependencies scripts names but should not change the features.
- `charpente_options()` will likely be deprecated because of the previous point. It still
contains the local option to either point to external CDN or copy external vendore files
into the local project.
- `create_css()` has been replaced by `create_scss()` (you can still use `golem::create_css`).
There is one main SCSS file created at project setup. Other SCSS files are referenced into this main `styles/main.scss` using `@import path`, which will allow you to have modular
Sass code.
- Remove `entry_point` param from `build_js()` as it was not used anyway...

# charpente 0.1.0

* Added a `NEWS.md` file to track changes to the package.
12 changes: 11 additions & 1 deletion R/charpente.R
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,17 @@ create_charpente <- function(path, remote = NULL, private = FALSE, license) {
# Add mocha for tests
set_mocha()
# Ignore files/folders: srcjs, node_modules, ...
use_build_ignore(c("srcjs", "node_modules", "package.json", "package-lock.json"))
use_build_ignore(
c(
"srcjs",
"node_modules",
"package.json",
"package-lock.json",
"styles",
"esbuild.dev.json",
"esbuild.prod.json"
)
)
use_git_ignore("node_modules")

# version control
Expand Down
44 changes: 29 additions & 15 deletions R/create_files.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#' @export
#' @rdname create_file
create_input_binding <- function(name, pkg = ".", dir = "srcjs", open = TRUE,
dir_create = TRUE, initialize = FALSE, dev = FALSE,
initialize = FALSE, dev = FALSE,
events = list(name = "click", rate_policy = FALSE),
add_reference = TRUE) {
golem::add_js_input_binding(
Expand All @@ -30,7 +30,7 @@ create_input_binding <- function(name, pkg = ".", dir = "srcjs", open = TRUE,
#' @export
#' @rdname create_file
create_output_binding <- function(name, pkg = ".", dir = "srcjs", open = TRUE,
dir_create = TRUE, add_reference = TRUE) {
add_reference = TRUE) {
golem::add_js_output_binding(
name,
pkg,
Expand All @@ -56,7 +56,6 @@ create_custom_handler <- function(
pkg = ".",
dir = "srcjs",
open = TRUE,
dir_create = TRUE,
add_reference = TRUE
) {

Expand All @@ -66,7 +65,7 @@ create_custom_handler <- function(
pkg,
dir,
open,
dir_create,
dir_create = FALSE,
template = charpente::js_handler_template(
path = sprintf(paste0(dir, "/%s.js"), name),
name = name
Expand Down Expand Up @@ -104,14 +103,15 @@ create_custom_handler <- function(
#' @param add_reference Whether to add an import statement in main.js. Defaults to TRUE.
#' @export
#' @rdname create_file
create_js <- function(name, pkg = ".", dir = "srcjs", open = TRUE,
dir_create = TRUE, with_doc_ready = TRUE, template = golem::js_template,
create_js <- function(name, dir = "srcjs", open = TRUE,
with_doc_ready = FALSE,
template = golem::js_template,
..., add_reference = TRUE) {
# Create JS file
golem::add_js_file(
name,
pkg,
dir ,
pkg = ".",
dir,
open,
dir_create = FALSE,
with_doc_ready,
Expand All @@ -123,13 +123,27 @@ create_js <- function(name, pkg = ".", dir = "srcjs", open = TRUE,
if (add_reference) reference_script(name)
}

#' Create a css file
#' Create a scss file
#'
#' @inheritParams golem::add_css_file
#' @inheritParams golem::add_sass_file
#' @export
#' @rdname create_file
create_css <- partial(
golem::add_css_file,
pkg = ".",
dir = "inst"
)
create_scss <- function(name, dir = "styles", open = TRUE,
template = golem::sass_template,
..., add_reference = TRUE) {
# Create JS file
golem::add_sass_file(
name,
pkg = ".",
dir ,
open,
dir_create = FALSE,
template,
...
)

file.rename(sprintf("%s/%s.sass", dir, name), sprintf("%s/%s.scss", dir, name))

# Import into main.scss
if (add_reference) reference_style(name)
}
11 changes: 4 additions & 7 deletions R/deps.R
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ create_dependency <- function(name, tag = NULL, open = interactive(), options =
#' @keywords Internal
create_custom_dependency <- function(name, version, open = interactive(), mode) {

stylesheet <- NULL # remove when Sass workflow is supported!
script <- sprintf("js/%s%s.js", name, mode)
stylesheet <- sprintf("%s%s.css", name, mode)
script <- sprintf("%s%s.js", name, mode)

# need to overwrite path which was used before
path <- sprintf("R/%s-dependencies.R", name)
Expand Down Expand Up @@ -232,11 +232,8 @@ create_custom_dependency <- function(name, version, open = interactive(), mode)
write_there(sprintf(' name = "%s",', name))
write_there(sprintf(' version = "%s",', version))
write_there(sprintf(' src = c(file = "%s-%s"),', name, version))
write_there(sprintf(' script = "%s",', script))
if (!is.null(stylesheet)) {
stylesheet <- sprintf("css/%s%s.css", name, mode)
write_there(sprintf(' stylesheet = "%s",', stylesheet))
}
write_there(sprintf(' script = "dist/%s",', script))
write_there(sprintf(' stylesheet = "dist/%s",', stylesheet))
write_there(sprintf(' package = "%s",', name))
# end deps
write_there(" )")
Expand Down
3 changes: 1 addition & 2 deletions R/jstools.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
#' @param mode Production or development mode. Choose either "prod" or "dev".
#' "prod" bundles, aggregates and minifyies files. "dev" only bundles the code.
#' Modules follow the ES6 format (import/export).
#' @param entry_point Required internally to setup the esbuild config.
#' @export
#' @importFrom utils tail packageVersion
build_js <- function(dir = "srcjs", mode = c("prod", "dev"), entry_point = "main.js") {
build_js <- function(dir = "srcjs", mode = c("prod", "dev")) {

mode <- match.arg(mode)
pkg_desc <- desc::description$new("./DESCRIPTION")$get(c("Package", "Version", "License"))
Expand Down
71 changes: 64 additions & 7 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,12 @@ process_template <- function(template, ..., where = system.file("utils", package
temp <- glue::glue(
readr::read_file(get_template(template, where)),
name = pars$name,
split_version = paste(head(strsplit(pars$version, ".", fixed = TRUE)[[1]], -1), collapse = "."),
split_version = if (!is.null(version)) {
paste(
head(strsplit(pars$version, ".", fixed = TRUE)[[1]], -1),
collapse = "."
)
},
version = pars$version,
entry_point = "main.js",
license = pars$license,
Expand All @@ -103,7 +108,7 @@ process_template <- function(template, ..., where = system.file("utils", package
)

write_there <- function(...) {
write(..., file = "./package.json", append = FALSE)
write(..., file = template, append = FALSE)
}
write_there(temp)
}
Expand All @@ -125,16 +130,34 @@ reference_script <- function(name) {
ui_done("Script successfuly added to JS entry point!")
}


#' Insert import into main SCSS file
#'
#' Useful to reference scss files into the main one.
#'
#' @param path Could be file.name or folder/file.name
#'
#' @keywords internal
reference_style <- function(path) {
write(
sprintf("@import \"%s\";", path),
file = "./styles/main.scss",
append = TRUE
)
ui_done("Style file successfuly added to Sass entry point!")
}


#' Setup esbuild
#'
#' Installs esbuild for the local project
#'
#' @param light Used to only install Sass plugins. This is
#' to workaround a breaking change in charpente where styles does
#' not exist in old versions.
#'
#' @return Installs esbuild in node_modules (dev scope), creates srcjs + srcjs/main.js
#' @keywords internal
set_esbuild <- function() {
set_esbuild <- function(light = FALSE) {

pkg_desc <- desc::description$
new("./DESCRIPTION")$
Expand All @@ -148,9 +171,25 @@ set_esbuild <- function() {
license = pkg_desc[3]
)

npm::npm_install("esbuild", scope = "dev")
dir.create("srcjs")
file.create("./srcjs/main.js")
npm::npm_install(
c(
# Don't re-install esbuild. This will install
# only missing Sass dependencies.
if (!light) "esbuild",
"esbuild-sass-plugin",
"postcss",
"autoprefixer"
),
scope = "dev"
)
# If light, we don't want to recreate srcjs folder
# which has always been in charpente since the first release.
if (!light) {
dir.create("srcjs")
write("import \"../styles/main.scss\";", "./srcjs/main.js")
}
dir.create("styles")
file.create("styles/main.scss")
}


Expand Down Expand Up @@ -227,6 +266,24 @@ copy_charpente_utils <- function(pkg_name) {
#' @param outputDir Output directory
#' @keywords internal
run_esbuild <- function(mode, outputDir) {
# styles did not exist in previous {charpent} versions
if (!dir.exists("styles")) {
# Only add missing pieces ...
set_esbuild(light = TRUE)
}

# Get pkg info
pkg_desc <- desc::description$
new("./DESCRIPTION")$
get(c("Package", "Version"))

# Recreate esbuild.prod/dev.js each time
process_template(
sprintf("esbuild.%s.js", mode),
name = pkg_desc[[1]],
version = pkg_desc[[2]]
)

npm::npm_run(sprintf("run build-%s", mode))
ui_warn(sprintf("%s folder created ...", outputDir))
ui_done("JavaScript successfully processed!")
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ The goal of `{charpente}` is to significantly reduce the complexity of creating
- `{charpente}` automatically import dependencies from [jsdelivr](https://www.jsdelivr.com/), so that you don't have to do it by hand!
- `{charpente}` eases the conversion from HTML to R.
- `{charpente}` offers multiple R and JS boilerplate for `{shiny}` input bindings, `{shiny}` message handlers, ...
- `{charpente}` enable seamless JavaScript code management (powered by [esbuild](https://esbuild.github.io/)): concat, compress, mangle, bundle, minify, ...
- `{charpente}` enables seamless JavaScript code management (powered by [esbuild](https://esbuild.github.io/)): concat, compress, mangle, bundle, minify, ... for JS and
Sass code.


## Installation
Expand Down Expand Up @@ -59,7 +60,7 @@ create_input_binding("myinput")
# Create output binding
create_output_binding("myoutput")

# Compress JS for production
# Compress JS and CSS (Sass) for production
build_js()
devtools::load_all()
```
Expand Down
23 changes: 23 additions & 0 deletions inst/utils/esbuild.dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import esbuild from "esbuild";
import {sassPlugin} from 'esbuild-sass-plugin';
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';

esbuild
.build({
entryPoints: ["./srcjs/main.js"],
outfile: "inst/<<name>>-<<version>>/dist/<<name>>.js",
bundle: true,
format: "esm",
minify: false, // dev
plugins: [
sassPlugin({
async transform(source) {
const { css } = await postcss([autoprefixer]).process(source);
return css;
},
}),
]
})
.then(() => console.log("⚡ Build complete! ⚡"))
.catch(() => process.exit(1));
24 changes: 24 additions & 0 deletions inst/utils/esbuild.prod.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import esbuild from "esbuild";
import {sassPlugin} from 'esbuild-sass-plugin';
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';

esbuild
.build({
entryPoints: ["./srcjs/main.js"],
outfile: "inst/<<name>>-<<version>>/dist/<<name>>.min.js",
bundle: true,
format: "esm",
minify: true, // prod
sourcemap: "external", // prod
plugins: [
sassPlugin({
async transform(source) {
const { css } = await postcss([autoprefixer]).process(source);
return css;
},
}),
]
})
.then(() => console.log("⚡ Build complete! ⚡"))
.catch(() => process.exit(1));
Loading

0 comments on commit 1fc43aa

Please sign in to comment.