ClojureScript
| CIDER works well with ClojureScript, but ClojureScript’s compile-on-the-JVM, run-in-a-JavaScript-runtime model means a few Clojure features can’t apply to it. See Feature support below for what works, what doesn’t, and what to reach for instead. |
Unlike the Clojure ecosystem, which is dominated by a single REPL, the
ClojureScript ecosystem offers several different REPLs (e.g. browser, node,
weasel, figwheel and shadow-cljs). This section explains how ClojureScript
support is implemented in CIDER and helps you pick one; the subsequent sections
cover launching and configuring the most popular ones.
nREPL and ClojureScript
nREPL doesn’t natively support ClojureScript evaluation, that’s why an additional
middleware is needed. For most REPLs (with the notable exceptions of shadow-cljs, nbb), CIDER relies on the popular Piggieback middleware for its
ClojureScript support.
Piggieback works in the following manner:
-
You start a regular Clojure REPL
-
You run some Clojure code in it, that converts it to a ClojureScript REPL
This means that jacking-in is a two-fold process for ClojureScript, compared to Clojure, as now we have the extra REPL "upgrade" step.
On the bright side - this also means that a single CIDER session can hold both a
Clojure and a ClojureScript REPL at once, sharing one nREPL connection. That’s
the reason commands like cider-jack-in-clj&cljs and the "sibling" connection
commands exist: they set up both REPLs together, and CIDER routes each
evaluation to the right one. See Managing
Connections for how sessions group these REPLs and how to control where code
gets evaluated.
| `shadow-cljs’s REPL is implemented in a very similar fashion, but its mechanism is provided by its own nREPL middleware - not Piggieback. |
Choosing a ClojureScript REPL
You’ll have to decide which ClojureScript REPL to run and how you want CIDER to interact with it. Here’s a quick map of the most common choices:
| REPL | How CIDER drives it | Setup |
|---|---|---|
|
Vanilla ClojureScript REPLs upgraded in place via Piggieback. |
|
A Piggieback-style upgrade, with Figwheel’s hot-reloading and build tooling on top. CIDER checks that Figwheel is on the classpath before upgrading. |
||
Uses shadow-cljs’s own nREPL middleware rather than Piggieback; CIDER prompts for the build to connect to. |
||
A browser-connected REPL over a WebSocket, upgraded via Piggieback. Maintained under the nREPL org, alongside CIDER. |
| Node-based ClojureScript runtimes such as nbb are reached through the same ClojureScript commands, even though they aren’t Piggieback-based. |
Feature support
ClojureScript compiles on the JVM but evaluates in a separate JavaScript runtime (a browser tab, Node, React Native, and so on). Features that read the compiler/analyzer therefore work much like they do for Clojure, while features that need to instrument running code, rebind vars at runtime, or inspect the JVM have nothing to attach to in a JavaScript runtime.
The following work in a ClojureScript REPL:
-
evaluation and loading files
-
code completion
-
eldoc(argument lists) and documentation lookup (cider-doc) -
jump to definition
-
macroexpansion
-
namespace operations (listing namespaces and their vars)
-
the test runner (via
cljs.test) -
code formatting
The following are not available for ClojureScript, and aren’t planned, because they can’t work over nREPL against a JavaScript runtime - reach for the suggested alternative instead:
| Feature | Why, and what to use instead |
|---|---|
Debugger / stepping |
The debugger instruments forms and pauses a JVM thread. For ClojureScript use Cider Storm (time-travel debugging), or your browser/Node DevTools together with source maps. |
Enlighten |
Built on the same instrumentation as the debugger. |
Tracing |
Wraps JVM vars; ClojureScript vars compile to static JavaScript. |
Profiling |
Relies on JVM instrumentation; use the runtime’s own profiler (e.g. the browser’s performance tools). |
|
Runtime introspection of Clojure vars. For static navigation and find-references across ClojureScript, use clojure-lsp alongside CIDER. |
When you invoke one of these in a ClojureScript REPL, CIDER tells you it isn’t available rather than failing obscurely.
For inspecting values, the ClojureScript ecosystem leans on tap>-based
tools such as shadow-cljs Inspect, Portal and
cljs-devtools, which complement CIDER nicely.
|
Piggieback differences with the Standard ClojureScript REPL
While the Piggieback-powered ClojureScript REPLs behave more or less the same as the standard ClojureScript REPL, there are few subtle differences everyone has to be aware of.
Handling of Multiple Forms
Here’s how the standard ClojureScript behaves with multiple input forms:
cljs.user>
(declare is-odd?)
(defn is-even? [n] (if (= n 0) true (is-odd? (dec n))))
(defn is-odd? [n] (if (= n 0) false (is-even? (dec n))))
#'cljs.user/is-odd?
#'cljs.user/is-even?
#'cljs.user/is-odd?
cljs.user> (is-even? 4)
true
And here’s how a Piggieback-powered REPL behaves:
cljs.user>
(declare is-odd?)
(defn is-even? [n] (if (= n 0) true (is-odd? (dec n))))
(defn is-odd? [n] (if (= n 0) false (is-even? (dec n))))
#'cljs.user/is-odd?
cljs.user> (is-even? 4)
Compile Warning <cljs repl> line:1 column:2
Use of undeclared Var cljs.user/is-even?
1 (is-even? 4)
^---
#object[TypeError TypeError: Cannot read property 'call' of undefined]
(<NO_SOURCE_FILE>)
cljs.user>
This difference comes from a performance optimization in Piggieback, which avoids creating different REPLs for each ClojureScript form it evaluates.
| You can learn more about this difference here. |
Dealing with Dependencies
CIDER doesn’t handle automatically ClojureScript REPL dependencies when you’re doing
cider-jack-in-cljs. You’ll have to configure those manually yourselves
as documented in the subsequent sections of this manual.
Actually, CIDER will handle automatically the most important dependency - namely Piggieback.
The problem with the other dependencies, however, is that you might need to install
some external tools (e.g. node, shadow-cljs) and that ClojureScript development
tools like Figwheel and shadow-cljs also require some setup to be useful.
CIDER will try to help you identify missing requirements by running a check, right before attempting to upgrade a Clojure REPL to a ClojureScript REPL. The nature of this check differs for the different REPL types:
-
For a
nodeREPL we check whether thenodebinary is on yourexec-path(Emacs’s version ofPATH) -
For tools like
figwheelwe check whether they are available on the classpath (by trying to require some of their namespaces)
We’ll discuss those checks further in the upcoming sections.
Limitations
CIDER currently doesn’t support self-hosted ClojureScript implementations. The reason for this is that there’s no self-hosted version of nREPL (implemented in ClojureScript) available today.
Another unsupported REPL is Rhino. Supporting in it Piggieback required a lot of ugly hacks and eventually it was decided we were better off without Rhino. Given the abundance of better solutions today, I doubt anyone’s going to miss Rhino anyways.
Additionally, some of CIDER’s features can’t apply to ClojureScript at all - see Feature support above for the details and the recommended alternatives.