Introducing js: tools for working with JavaScript in R

February 17, 2015


A new package has appeared on CRAN called js. This package implements bindings to several popular JavaScript libraries for validating, reformatting, optimizing and analyzing JavaScript code. It builds on the V8 engine, the fully standalone JavaScript engine in R.

Syntax Validation

Several R packages allow the user to supply JavaScript code to be used as callback function or configuration object within a visualization or web application. By validating in R that the JavaScript code is syntactically correct and of the right type before actually inserting it in the HTML, we can avoid many annoying bugs.

The js_typeof function simply calls the typeof operator on the given code. If the code is syntactically invalid, a SyntaxError will be raised.

callback <- 'function(x, y){
  var z = x*y ;
  return z;
}'
js_typeof(callback)
# [1] "function"

Same for objects:

conf <- '{
  foo : function (){},
  bar : 123
}'
js_typeof(conf)
# [1] "object"

Catch JavaScript typos:

js_typeof('function(x,y){return x + y}}')
# Error in eval(expr, envir, enclos): SyntaxError: Unexpected token }

Script Validation

A JavaScript program typically consists of script with a collection of JavaScript statements. The js_validate_script function can be used to validate an entire script.

jscode <- readLines(system.file("js/uglify.min.js", package="js"), warn = FALSE)
js_validate_script(jscode)
# [1] TRUE

Note that JavaScript does not allow for defining anonymous functions in the global scope:

js_validate_script('function(x, y){return x + y}', error = FALSE)
# [1] FALSE

To validate individual functions or objects, use the js_typeof function.

Uglify: reformatting and optimization

One of the most popular and powerful libraries for working with JavaScript code is uglify-js. This package provides an extensive toolkit for manipulating the syntax tree of a piece of JavaScript code.

The uglify_reformat function parses a string with code and then feeds it to the uglify code generator which converts it back to a JavaScript text, with custom formatting options such as fixing whitespace, semicolons, etc.

code <- "function test(x, y){ x = x || 1; y = y || 1; return x*y;}"
cat(uglify_reformat(code, beautify = TRUE, indent_level = 2))
# function test(x, y) {
#   x = x || 1;
#   y = y || 1;
#   return x * y;
# }

The more impressive part of uglify-js is the compressor which refactors the entire syntax tree, effectively rewriting your code into a more compact but equivalent program. The uglify_optimize function in R is a simple wrapper which parses code and then feeds it to the compressor.

cat(code)
# function test(x, y){ x = x || 1; y = y || 1; return x*y;}
cat(uglify_optimize(code))
# function test(x,y){return x=x||1,y=y||1,x*y}

You can pass compressor options to uglify_optimize to control the various uglify optimization techniques.

JSHint: code analysis

JSHint will automatically detect errors and potential problems in JavaScript code. The jshint function is R will return a data frame where each row is a problem detected by the library (type, line and reason of error):

code <- "var foo = 123"
jshint(code)
#
#       id                raw code      evidence line character  scope             reason
# 1 (error) Missing semicolon. W033 var foo = 123    1        14 (main) Missing semicolon.

JSHint has many configuration options to control which types of code propblems it will report on.