Why ClojureScript

Webpack meets the JVM

Until recently, I have mostly been working with the JVM and I enjoy Clojure and ClojureScript a lot. With regards to the browser target, we have ClojureScript tooling and libraries which I consider even superior to what there is on Node / Webpack today (Figwheel, Google Closure, reagent / re-frame). Linking an npm package in your application generally works well these days and things are improving quickly. However, there are times when you are tempted to pick something from the Node ecosystem which also requires a Node runtime - simply because there is no alternative available on the JVM. Compilation tooling is an example domain.

Besides, Node native tooling such as create-react-app generally delivers a pretty pleasant experience which could be considered "mainstream" these days. And as a developer you should make sure to not be significantly less productive than mainstream. You might run into trouble finding a job otherwise. :)

In this post I am looking at Webpack, but the story is really about the JVM and Node (and not just plain JavaScript) in one process. Webpack just happened to be the detail itching me.

Briding the gap to Webpack

People approaching from the JavaScript side of things might be wondering: Why bother bridging a gap from the JVM to Node and not rewrite all the things for Node? I assume this will be happening, but it takes time even though we already have tooling like lumo - a Node based environment leveraging bootstrapped ClojureScript. The other way (bringing all the things to the JVM natively) may never happen.

Why bother with ClojureScript anyways? Derek Slanger has some answers.

Ways to leverage Webpack

Basic Webpack usage starts by introducing a with dependencies sufficient to generate a bundle and launching the compilation from the command line. Automatically rebuilding things on change is not a big deal either. Aspects such as hot reloading get tricky in case you already have something alien to Node (such as Figwheel) dealing with it.

In ClojureScript land, there are tools trying to bridge the JVM to Node gap already. ClojureScript starter tool and shadow-cljs are two examples. Today, they are both based on separate (Node / JVM) processes. A multi-process setup can already be helpful, but it gets hairy once you need a lot of fine-grained interaction between Node and the JVM.

I was sure it is not possible to use the JVM as a library in Node, but I was aware of solutions going the other way. Oracle has abandoned Avatar, but Ian Bull stepped in and came up with J2V8. I decided to give it a spin.

A proof of concept

First I was wondering about the system boundary. Should cljsbuild call into Node or vice versa?

Since there already are Webpack loaders for various other languages, I chose to try implementing a skeleton of a cljs loader. The code for my experiment webpack-jvm is on Github. At the time of writing, it consists of a loader implemented in Clojure (just a function consuming the source and emitting static JavaScript code) along with a demo. Feel free to try it out.

I have not tried it on Mac or Windows, but I think it should be working on those OSs as well. At least the artifacts are on maven central. That’s also the reason I went down to 4.6.0 of j2v8. I was surprised to see Node 5.9 running under the hood. I don't consider it an issue for now as it is good enough to run current Webpack 2.6, 4.7.0 on Linux comes with Node 7, the project is alive and from what I can tell so far, Node 8 does not introduce serious issues either. However, compiling the native code is still painful - especially if you want to support all those platforms - including even Android.

Where to go next

The loader should be able to kick off a ClojureScript compiler - JVM native or bootstrapped and there are still various details to worry about and sacrifices (Closure / tree-shaking) to expect.

Entering Node land from the ClojureScript compiler (JVM Version) - or the ns-form may be an alternative. Especially if we allow strings instead of symbols as suggested by Thomas Heller CLJS-2061.

Thats all I got for now. Feel free to comment or jump in.