---
title: "Using NPM packages in V8"
author: "Jeroen Ooms"
date: "`r Sys.Date()`"
vignette: >
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteIndexEntry{Using NPM packages in V8}
  \usepackage[utf8]{inputenc} 
output:
  knitr:::html_vignette:
    toc: yes
---

```{r, echo = FALSE, message = FALSE}
knitr::opts_chunk$set(comment = "")
library(V8)
```

## What is V8 (not)

V8 is Google's JavaScript engine that is used in Chrome, Node and other tools. However each of these programs implements most JavaScript functionality on top of V8. The naked V8 engine only provides basic ECMAscript, which does not include a lot of things that you might be used to. There is no I/O (network/disk), no event-loop and no DOM (window).

|         | JS Engine | Evented  | Network | Disk | DOM |
|---------|--------|-----------|------|---------|-----|
| Browser | ✔      | ✔        | ✔    | -     | ✔   |
| Node    | ✔      | ✔        | ✔    | ✔       | -   |
| V8      | ✔      | -         | -    | -       | -   |

Currently the R package is just V8. Perhaps more will be added later, although both IO and DOM would require an event-loop, which disqualifies the simple eval-and-return pattern. In that case it would work more like a server (similar to the httpuv package). But for now we will be working with plain V8 and rely on R for I/O.

## Can we use NPM?

Yes! But not all packages will work. Most libraries in [npm](https://www.npmjs.com) are primarely written for Node or the browser. Obviously, anything that requires internet access, graphics or files is not going to work in plain V8. But there is quite a lot of stuff that does work.

Some general purpose libraries like underscore or crossfilter will work natively in V8:

```{r}
ct <- v8()
ct$source(system.file("js/underscore.js", package="V8"))
ct$call("_.filter", mtcars, JS("function(x){return x.mpg < 15}"))
```

However NPM assumes disk access to resolve dependencies. How is that going to work?

## Browserify to the rescue

![browserify logo](http://jeroenooms.github.io/V8/browserify.png)

[Browserify](http://browserify.org/) is a brilliant tool to bundle an npm package with all of its dependencies into a single js file that does not require disk access. It is mainly designed to make npm packages suitable for use on a webpage (duh) but it is useful with embedded V8 as well. To install it run:

```bash
npm install -g browserify
```

## Example: beautify-js

[Beautify-js](https://www.npmjs.com/package/js-beautify) is a simple npm package to fix linebreaks and indentation in JavaScript, HTML or CSS code. To bundle it up, run these three lines in a shell:

```bash
npm install js-beautify
echo "global.beautify = require('js-beautify');" > in.js
browserify in.js -o bundle.js
```

The first line will install js-beautify in a the current dir under `node_modules`.
The second line creates the input file for browserify. In this case it consists of only
one line that imports the js-beautify library and exports it to the global environment.
The third line runs browserify and saves the output to a new file `bundle.js`.

We now have a file that we can load in V8. Assuming you ran the above commands in your Desktop directory:

```{r eval=FALSE}
ct <- v8()
ct$source("~/Desktop/bundle.js")
```

```{r echo=FALSE, results='hide'}
ct <- v8()
ct$source("beautify.js")
```

Let's see whats in our global environment now:

```{r}
ct$get(JS('Object.keys(global)'))
```

The `beautify` library is available now.

## Lets beautify stuff

To beautify JavaScript we need to use the `js_beautify` function. See the [package homepage](https://www.npmjs.com/package/js-beautify) for a full list of options.

```{r}
test <- "(function(x,y){x = x || 1; y = y || 1; return y * x;})(4, 9)"
pretty_test <- ct$call("beautify.js_beautify", test, list(indent_size = 2))
cat(pretty_test)
```

The package also includes functions to beautify css and html:

```{r}
html <- "<ul><li>one</li><li>two</li><li>three</li></ul>"
cat(ct$call("beautify.html_beautify", html))
```

