Foreign Libs Processing in REPL

December 22, 2015

Previously I had written about some experimental hacking I had done in Ambly to make it so that foreign libs processing could occur when launching the Ambly REPL. Now, via recently landed CLJS-1313, this capability will be available for all ClojureScript REPLs.




Here is a detailed example illustrating the capability.

Let's say you have a libs dir containing a couple of CommonJS modules:

german.js:

exports.hello = function() {
   return "Hallo";
};

greeting.js:

var german = require("./german");

exports.hello = function(name) {
   return german.hello() + ", " + name;
};

For generality, let's make use of these in the Nashorn ClojureScript REPL. (This works in the Node REPL as well, but since the Nashorn JavaScript environment doesn't intrinsically support CommonJS, I hope you'll find the illustration even more convincing!)

First, fire up a Clojure REPL and load the ClojureScript REPL code:

(require
  '[cljs.repl :as repl]
  '[cljs.repl.nashorn :as nashorn])

Now, before starting the Nashorn REPL, let's define a :foreign-libs compiler option.

(def foreign-libs
  [{:file        "libs/greeting.js"
    :provides    ["greeting"]
    :module-type :commonjs}
   {:file        "libs/german.js"
    :provides    ["german"]
    :module-type :commonjs}])

This would normally be specified in lein's project.clj or perhaps in the options to cljs.build.api/build.

But—and this is the important bit—CLJS-1313 is about letting you just make use of these foreign libs directly from the REPL like you would any other ClojureScript code, without having to do an explicit build step first. CLJS-1313 essentially allows us to now pass :foreign-libs (and :libs) compiler options to the REPL when launching it:

(cljs.repl/repl (nashorn/repl-env)
  :foreign-libs foreign-libs)

Upon doing this, the foreign-libs processing occurs, and in particular these CommonJS foreign libs are converted to Google Closure native libs. (You'll see this if you peek at the german.js and greetings.js files that get generated in .cljs_nashorn_repl.)

A consequence is, voilà, you can now make use of the foreign libs:

(require '[greeting :refer [hello]])

Then

(hello "Welt!")

yields

"Hallo, Welt!"

The original inspiration and motivation for this was a technicality of the way the Ambly REPL operates (REPL remote compilation precludes a pre-REPL-launch build step in order to do foreign libs processing). Nevertheless, this change is great because it makes the use of foreign libs that much more like regular ClojureScript libs when it comes to just loading code and making use of it directly in the REPL.

Tags: ClojureScript