--- title: "Mounting filesystem images" output: rmarkdown::html_document vignette: > %\VignetteIndexEntry{Mounting filesystem images} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- The Emscripten WebAssembly (Wasm) environment provides a virtual filesystem (VFS) which supports the concept of *mounting*. With this, an entire file and directory structure can be packaged into a filesystem image, efficiently making individual files or entire R package libraries available for use in webR. ## Create filesystem images ### Emscripten's `file_packager` tool The [`file_packager`](https://emscripten.org/docs/porting/files/packaging_files.html#packaging-using-the-file-packager-tool) tool, provided by Emscripten, takes in a directory structure as input and produces a webR compatible filesystem image as output. The [`file_packager`](https://emscripten.org/docs/porting/files/packaging_files.html#packaging-using-the-file-packager-tool) tool may be invoked from the [rwasm](https://r-wasm.github.io/rwasm/) package: ```{r eval=FALSE} > rwasm::file_packager("./input", out_dir = ".", out_name = "output") ``` It can also be invoked directly using its CLI^[See the [`file_packager`](https://emscripten.org/docs/porting/files/packaging_files.html#packaging-using-the-file-packager-tool) Emscripten documentation for details.], if you prefer: ```bash $ file_packager output.data --preload ./input@/ \ --separate-metadata --js-output=output.js ``` In the above examples, the files in the directory `./input` are packaged and an output filesystem image is created^[When using the `file_packager` CLI, a third file named `output.js` will also be created. If you only plan to mount the image using webR, this file may be discarded.] consisting of a data file, `output.data`, and a metadata file, `output.js.metadata`. To prepare for mounting the filesystem image with webR, ensure that both files have the same basename (in this example, `output`). The resulting URLs or relative paths for the two files should differ only by the file extension. #### Compression Filesystem image `.data` files may optionally be `gzip` compressed prior to deployment. The file extension for compressed filesystem images should be `.data.gz`, and compression should be indicated by setting the property `gzip: true` on the metadata JSON stored in the `.js.metadata` file. **NOTE**: Loading compressed VFS images requires at least version 0.4.1 of webR. ### Mount `.tar` archives as a filesystem image Archives in `.tar` format, optionally gzip compressed as `.tar.gz` or `.tgz` files, can also be used as filesystem images by pre-processing the `.tar` archive using the `rwasm::add_tar_index()` function. The function reads archive contents and appends the required filesystem metadata to the end of the `.tar` archive data in a way that is understood by webR. For further information about the format see the [Technical details for .tar archive metadata](tar-metadata.html) article. ```{r eval=FALSE} > rwasm::add_tar_index("./path/to/archive.tar.gz") # Appending virtual filesystem metadata for: ./path/to/archive.tar.gz ``` Once processed by `rwasm::add_tar_index()`, the `.tar` archive can be deployed and used directly as a filesystem image. ## Mounting filesystem images When running in a web browser, the [`webr::mount()`](https://docs.r-wasm.org/webr/latest/api/r.qmd#mount) function downloads and mounts a filesystem image from a URL source, using the `WORKERFS` filesystem type. ```{r eval=FALSE} webr::mount( mountpoint = "/data", source = "https://example.com/output.data" ) ``` Filesystem images should be deployed to static file hosting^[e.g. GitHub Pages, Netlify, AWS S3, etc.] and the resulting URL provided as the source argument. The image will be mounted in the virtual filesystem under the path given by the `mountpoint` argument. If the `mountpoint` directory does not exist, it will be created prior to mounting. When running under Node.js, the source may also be provided as a relative path to a filesystem image on disk. To test filesystem images before deployment, serve them using a local static webserver. See the Local Testing section below for an example using `httpuv::runStaticServer()` in R. ## Building an R package library image A collection of R packages can be collected and bundled into a single filesystem image for mounting. To build an R package library image we must first build one or more Wasm R packages using `add_pkg()`. As an example, let's build a package with a few hard dependencies. Ensure that you are running R in an environment with access to Wasm development tools^[See the "Setting up the WebAssembly toolchain" section in `vignette("rwasm")` for further details.], then run: ```{r eval=FALSE} rwasm::add_pkg("dplyr") ``` After the build process has completed, the new `repo` directory contains a CRAN-like package repository with R packages build for Wasm. Next, run the following to build an Emscripten VFS image: ```{r eval=FALSE} rwasm::make_vfs_library() ``` By default, this function will create a new directory named `vfs` if it does not exist. The files `vfs/library.data` and `vfs/library.js.metadata` together form an Emscripten filesystem image containing an R package library consisting of all the packages previously added to the CRAN-like repository in `repo` using `add_pkg()`. ### Local testing The following R command starts a local web server to serve your filesystem image for testing^[Ensure that the latest version of the `httpuv` package is installed so that the `?httpuv::runStaticServer` function is available.]. When serving your files locally, be sure to include the `Access-Control-Allow-Origin: *` HTTP header, required for downloading files from a cross-origin server by the [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) mechanism. ```r httpuv::runStaticServer( dir = ".", port = 9090, browse = FALSE, headers = list("Access-Control-Allow-Origin" = "*") ) ``` Once the web server is running start a webR session in your browser, such as the console at . Use `webr::mount()` to make the R library image available somewhere on the VFS^[You might need to adjust the path portion of the URL, depending on your set-up. If it does not work, you could also try `"http://127.0.0.1:9090"` or `"http://127.0.0.1:9090/output/vfs"`]: ```{r eval=FALSE} webr::mount("/my-library", "http://127.0.0.1:9090/vfs/library.data") ``` Once mounted, the contents of the filesystem image are available at `/my-library` in the virtual filesystem. ```{r eval=FALSE} list.files("/my-library") #> [1] "R6" "cli" "dplyr" "fansi" "generics" "glue" #> [7] "lifecycle" "magrittr" "pillar" "pkgconfig" "rlang" "tibble" #> [13] "tidyselect" "utf8" "vctrs" "withr" ``` This new directory should be added to R's `.libPaths()`, so that R packages may be loaded from the new library. ```{r eval=FALSE} .libPaths(c(.libPaths(), "/my-library")) library(dplyr) #> Attaching package: ‘dplyr’ #> #> The following objects are masked from ‘package:stats’: #> #> filter, lag #> #> The following objects are masked from ‘package:base’: #> #> intersect, setdiff, setequal, union ```