Using shadow-cljs

shadow-cljs is one of the most popular toolchain for doing ClojureScript development these days. In this section we’ll discuss how to set it up and use it together with CIDER.

Setting up shadow-cljs

This section assumes you’ve already installed node.js.

Installing shadow-cljs is pretty straight-forward. You can do it via npm or yarn:

$ npm install -g shadow-cljs
$ yarn global add shadow-cljs

While it’s not necessary to do a global installation that’s generally the recommended approach.

Starting a shadow-cljs REPL

Using cider-jack-in-cljs

Provided you’ve configured your project correctly, you can simply use cider-jack-in-cljs:

  • Press C-c C-x j s (or do M-x cider-jack-in-cljs)

  • When prompted for the ClojureScript REPL type to start, select shadow

This will automatically start the shadow-cljs server and connect to it. You’ll also be prompted for the shadow-cljs build to use. Select your desired build (e.g. app) and you should see something like:

shadow.user> To quit, type: :cljs/quit
[:selected :app]
cljs.repl>
CIDER will extract the list of available builds automatically from the shadow-cljs.edn file in the root of the current project.

You can get rid of the prompts for the REPL type and the target build by creating a .dir-locals.el file with the following contents in the root of your project.

((nil . ((cider-default-cljs-repl . shadow)
         (cider-shadow-default-options . "<your-build-name-here>")
         (cider-shadow-watched-builds . ("<first-build>" "<other-build>")))))

Using cider-connect-cljs

Alternatively you can start the server manually with something like:

$ shadow-cljs watch app

And connect to it with cider-connect.

…​For that to work, shadow-cljs.edn contents like the following are assumed:

 :dependencies [[cider/cider-nrepl "0.39.1"] ;; mandatory (unless it's inherited from deps.edn or otherwise present in the classpath of shadow-cljs's JVM process)
                [refactor-nrepl/refactor-nrepl "3.9.0"]] ;; refactor-nrepl is optional

 :nrepl {:middleware [cider.nrepl/cider-middleware ;; it's advisable to explicitly add this middleware. It's automatically added by shadow-cljs (if available in the classpath), unless `:nrepl {:cider false}`
                      refactor-nrepl.middleware/wrap-refactor] ;; refactor-nrepl is optional
         :port 50655} ;; optional - if not specified, a random free port will be used
If cider-nrepl isn’t in your classpath you should make sure it’s there. You can do this by correctly filling the shadow-cljs.edn configuration file residing in the root of your project, as described above. Alternatively you can set cider-repl-auto-detect-type to nil, as the auto-detection of REPL types doesn’t work without cider-nrepl.

If you already have a running server watching a build (for instance you have already run npx shadow-cljs watch :dev), you can use the shadow-select CLJS REPL and specify :dev when prompted.

Using shadow-cljs with deps.edn and custom repl initialization

In case you want to manage your dependencies via deps.edn, you can use a custom cljs-repl init form. This supposes you have shadow-cljs in your deps.edn dependencies.

{:paths ["src"]
 :deps {...
        thheller/shadow-cljs       {:mvn/version "2.15.6"}
        ...
        }
 :aliases {:dev {:extra-paths ["dev"]}}}

Create a :dev alias with an extra source path of "dev" and add the following namespace

(ns user
  (:require [shadow.cljs.devtools.api :as shadow]
            [shadow.cljs.devtools.server :as server]))

(defn cljs-repl
  "Connects to a given build-id. Defaults to `:app`."
  ([]
   (cljs-repl :app))
  ([build-id]
   (server/start!)
   (shadow/watch build-id)
   (shadow/nrepl-select build-id)))

Supposing your build-id is :app, add the following to your .dir-locals.el

((nil . ((cider-clojure-cli-aliases        . ":dev")
         (cider-preferred-build-tool       . clojure-cli)
         (cider-default-cljs-repl          . custom)
         (cider-custom-cljs-repl-init-form . "(do (user/cljs-repl))")
         (eval . (progn
                   (make-variable-buffer-local 'cider-jack-in-nrepl-middlewares)
                   (add-to-list 'cider-jack-in-nrepl-middlewares "shadow.cljs.devtools.server.nrepl/middleware"))))))

cider-jack-in-cljs should then work out of the box.

Configuration

You can tweak the command used by cider-jack-in-cljs to start the shadow-cljs server via the following configuration variables:

  • cider-shadow-cljs-command (its default value is npx shadow-cljs)

  • cider-shadow-cljs-parameters (its default value is server)

All of this results in the following default command to start the shadow-cljs server:

$ npx shadow-cljs server

The command is visible in the minibuffer when you’re doing cider-jack-in-cljs.

As noted earlier you can also set a default build via cider-shadow-default-options:

(setq cider-shadow-default-options "app")

Additional Resources

Official shadow-cljs Documentation

Here are a few useful sections from shadow-cljs's own documentation: