diff --git a/packages/webr/NAMESPACE b/packages/webr/NAMESPACE index f9ca3f0f..25c1ee09 100644 --- a/packages/webr/NAMESPACE +++ b/packages/webr/NAMESPACE @@ -14,6 +14,7 @@ export(mount) export(pager_install) export(require_shim) export(shim_install) +export(syncfs) export(test_package) export(unmount) useDynLib(webr, .registration = TRUE) diff --git a/packages/webr/R/library.R b/packages/webr/R/library.R index ba1861ef..3ebb7740 100644 --- a/packages/webr/R/library.R +++ b/packages/webr/R/library.R @@ -21,8 +21,10 @@ #' `show_menu` argument. By default, if no global option is set and no argument #' is provided, the menu will not be shown. #' +#' @param pkg Character vector of package names #' @param show_menu Show a menu asking the user if they would like to install #' the package if it is missing. Defaults to `getOption("webr.show_menu")`. +#' @param ... Other arguments to be passed to `library` and `require`. #' #' @export library_shim <- function(pkg, ..., show_menu = getOption("webr.show_menu")) { diff --git a/packages/webr/R/mount.R b/packages/webr/R/mount.R index 5a790ab6..fc882cf1 100644 --- a/packages/webr/R/mount.R +++ b/packages/webr/R/mount.R @@ -17,10 +17,11 @@ #' under Node. #' #' When mounting an Emscripten "idbfs" type filesystem, files will be persisted -#' or populated from/to a browser-based IndexedDB database when the JavaScript -#' function `Module.FS.syncfs` is invoked. See the Emscripten `IDBFS` +#' to or populated from a browser-based IndexedDB database whenever the +#' JavaScript function `Module.FS.syncfs` is invoked. See the Emscripten `IDBFS` #' documentation for more information. This filesystem type can only be used -#' when webR is running in a web browser. +#' when webR is running in a web browser and using the `PostMessage` +#' communication channel. #' #' @param mountpoint a character string giving the path to a directory to mount #' onto in the Emscripten virtual filesystem. @@ -52,3 +53,19 @@ mount <- function(mountpoint, source, type = "workerfs") { unmount <- function(mountpoint) { invisible(.Call(ffi_unmount, mountpoint)) } + +#' Synchronise the Emscripten virtual filesystem +#' +#' @description +#' Uses the Emscripten filesystem API to synchronise all mounted virtual +#' filesystems with their backing storage, where it exists. The `populate` +#' argument controls the direction of the synchronisation between Emscripten's +#' internal data and the file system's persistent store. +#' +#' @param populate A boolean. When `true`, initialises the filesystem with data +#' from persistent storage. When `false`, writes current filesystem data to +#' the persistent storage. +#' @export +syncfs <- function(populate) { + invisible(.Call(ffi_syncfs, populate)) +} diff --git a/packages/webr/man/library_shim.Rd b/packages/webr/man/library_shim.Rd index a1fe01a6..f5a33163 100644 --- a/packages/webr/man/library_shim.Rd +++ b/packages/webr/man/library_shim.Rd @@ -10,6 +10,10 @@ library_shim(pkg, ..., show_menu = getOption("webr.show_menu")) require_shim(pkg, ..., show_menu = getOption("webr.show_menu")) } \arguments{ +\item{pkg}{Character vector of package names} + +\item{...}{Other arguments to be passed to \code{library} and \code{require}.} + \item{show_menu}{Show a menu asking the user if they would like to install the package if it is missing. Defaults to \code{getOption("webr.show_menu")}.} } diff --git a/packages/webr/man/mount.Rd b/packages/webr/man/mount.Rd index 7a6d77f6..956d1de6 100644 --- a/packages/webr/man/mount.Rd +++ b/packages/webr/man/mount.Rd @@ -36,8 +36,9 @@ will be mapped into the virtual filesystem and mounted onto the directory under Node. When mounting an Emscripten "idbfs" type filesystem, files will be persisted -or populated from/to a browser-based IndexedDB database when the JavaScript -function \code{Module.FS.syncfs} is invoked. See the Emscripten \code{IDBFS} +to or populated from a browser-based IndexedDB database whenever the +JavaScript function \code{Module.FS.syncfs} is invoked. See the Emscripten \code{IDBFS} documentation for more information. This filesystem type can only be used -when webR is running in a web browser. +when webR is running in a web browser and using the \code{PostMessage} +communication channel. } diff --git a/packages/webr/man/syncfs.Rd b/packages/webr/man/syncfs.Rd new file mode 100644 index 00000000..4d95fc13 --- /dev/null +++ b/packages/webr/man/syncfs.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/mount.R +\name{syncfs} +\alias{syncfs} +\title{Synchronise the Emscripten virtual filesystem} +\usage{ +syncfs(populate) +} +\arguments{ +\item{populate}{A boolean. When \code{true}, initialises the filesystem with data +from persistent storage. When \code{false}, writes current filesystem data to +the persistent storage.} +} +\description{ +Uses the Emscripten filesystem API to synchronise all mounted virtual +filesystems with their backing storage, where it exists. The \code{populate} +argument controls the direction of the synchronisation between Emscripten's +internal data and the file system's persistent store. +} diff --git a/packages/webr/src/init.c b/packages/webr/src/init.c index 1839a826..e1dcf96a 100644 --- a/packages/webr/src/init.c +++ b/packages/webr/src/init.c @@ -11,6 +11,7 @@ extern SEXP ffi_dev_canvas_destroy(SEXP); extern SEXP ffi_mount_workerfs(SEXP, SEXP); extern SEXP ffi_mount_nodefs(SEXP, SEXP); extern SEXP ffi_mount_idbfs(SEXP); +extern SEXP ffi_syncfs(SEXP); extern SEXP ffi_unmount(SEXP); static @@ -25,6 +26,7 @@ const R_CallMethodDef CallEntries[] = { { "ffi_mount_workerfs", (DL_FUNC) &ffi_mount_workerfs, 2}, { "ffi_mount_nodefs", (DL_FUNC) &ffi_mount_nodefs, 2}, { "ffi_mount_idbfs", (DL_FUNC) &ffi_mount_idbfs, 1}, + { "ffi_syncfs", (DL_FUNC) &ffi_syncfs, 1}, { "ffi_unmount", (DL_FUNC) &ffi_unmount, 1}, { NULL, NULL, 0} }; diff --git a/packages/webr/src/mount.c b/packages/webr/src/mount.c index c2f2bfa1..9289a646 100644 --- a/packages/webr/src/mount.c +++ b/packages/webr/src/mount.c @@ -15,6 +15,14 @@ Rf_error("`" #arg "` can't be `NA`."); \ } +#define CHECK_LOGICAL(arg) \ + if (!Rf_isLogical(arg) || LENGTH(arg) != 1) { \ + Rf_error("`" #arg "` must be a logical."); \ + } \ + if (LOGICAL(arg)[0] == NA_LOGICAL){ \ + Rf_error("`" #arg "` can't be `NA`."); \ + } + SEXP ffi_mount_workerfs(SEXP source, SEXP mountpoint) { #ifdef __EMSCRIPTEN__ CHECK_STRING(source); @@ -105,6 +113,16 @@ SEXP ffi_mount_idbfs(SEXP mountpoint) { #endif } +SEXP ffi_syncfs(SEXP populate) { +#ifdef __EMSCRIPTEN__ + CHECK_LOGICAL(populate); + EM_ASM({ Module.FS.syncfs($0, () => {}) }, LOGICAL(populate)[0]); + return R_NilValue; +#else + Rf_error("Function must be running under Emscripten."); +#endif +} + SEXP ffi_unmount(SEXP mountpoint) { #ifdef __EMSCRIPTEN__ CHECK_STRING(mountpoint);