Since version 0.8.0 of Chlorine, there’s a new way to evaluate ClojureScript code: that’s the Shadow-CLJS Remote API. It is basically a new REPL (not nREPL, no Socket REPL) over WebSockets to try to solve problems when translating other REPLs to ClojureScript. So, to understand why these problems exist, I’ll first introduce the difference between ClojureScript and Clojure.

On Clojure, you’re always inside a JVM. This means that compilation happens on the same JVM that your REPL, and your code is running. If you practice REPL-Driven Development, even your tests are running on the same JVM. In practical terms, it means that when you fire up your REPL, you already have everything ready to run code, compile code, and evaluate forms.

On ClojureScript, the compiler is written in Clojure – that means it’s running on the JVM. So, to produce Javascript code you don’t need a Javascript environment – and that’s when things become confusing, because when exactly will you run the REPL? Let’s try from another angle: if you start the REPL on compilation time, you can’t evaluate code (because there’s no Javascript generated, nor any Javascript engine running). If you start the REPL when you run the compiled code, this REPL can become unusable if you stop the Javascript environment, and also you have to coordinate lots of state and translations between formats.

Why do you have to coordinate things? Now, consider this: the browser target does not have a way to start up any kind of server; in fact, browser targets doesn’t even have a way to connect to sockets – only to websockets (and that’s why it was things like websocketfy exist) so in ClojureScript you have two options: or you open up the websocket in node.js, so you can stay on the JavaScript environment all the time (which is bizarre because now you’ll have three parts to coordinate – the compiler living on the JVM world, the node.js websocket, and the browser code) or you start some kind of socket in the compiler, then expose websockets for browser environments to connect (again because websocket is the only kind of server that every Javascript target understands).

In fact that’s exactly what the REPLs in shadow-cljs and figwheel do! But in shadow, there’s also a different way to expose the REPL: considering that you already have a websocket to coordinate interactions, why not use it for REPL evaluation? The shadow remote API is exactly that – a way to evaluate code in different JavaScript runtimes without any “middleman” or so to speak.

There are also other problems: remember that the output of ClojureScript compiler is a Javascript file that can be run by node, the browser, or by react native. If you, for example, open two web browsers for the same Javascript and evaluate code on the REPL – in which browser will it be evaluated?

So to solve these issues the new API uses two IDs: the :build-id is a keyword with the build target name; :client-id is an identifier for a specific runtime connected into a specific :build-id (it’s the little number on the JS console that appears when you connect a runtime, for example: shadow-cljs: #25 ready!). So, if you want to evaluate, you send a command :cljs-eval, to a specific :client-id, then you’ll get an UUID as an answer that you’ll use to get the results. Quite easy, to be honest, but also powerful: the UUID for the result can be used to get the full result in EDN, or to get partial results. It can also get metadata, datafy, etc… Chlorine still does not support everything that can be done, but it will in the future (and probably this will allow me to remove lots of ugly hacks that I current have, just to work with limitations on the Socket REPL).

What more? Well, you can detect when a runtime is available; you can also detect when it goes away (somebody closed the browser window, for example). You can listen to tap> commands; you can also listen to STDOUT, STDERR, and build failures / warnings (these, Chlorine already have support).

What’s missing? Well… a Clojure port! Seriously, it’s really good and can allow you to develop ClojureScript from inside your browser, without ANY GLUE CODE (because, again – websocket!). Obviously, you can use from Clojure, but you’ll need to develop Clojure code in Shadow-CLJS, but I must be honest – that’s a path I am indeed considering!


Leave a Reply

Your email address will not be published. Required fields are marked *