A beginner guide to compile ClojureScript with shadow-cljs

shadow-cljs provides everything you need to compile your ClojureScript code with a focus on simplicity and ease of use.

To install shadow-cljs, use npm:

npm install -g shadow-cljs

shadow-cljs requires Java, make sure you have Java installed. Don’t worry, shadow-cljs handles Java for you. If you still want to learn more about ClojureScript, checkout this list.

Compile to browser

Say you have a project:

.
├── README.md
├── assets
│ └── index.html
├── package.json
├── shadow-cljs.edn
├── src
│ └── app
│ ├── lib.cljs
│ └── main.cljs
└── yarn.lock

assets/index.html(please link it to target/index.html) looks like:

<script src="main.js"></script>

src/app/main.cljs may look like:

(ns app.main)
(def value-a 1)
(defonce value-b 2)
(defn reload! []
(println "Code updated.")
(println "Trying values:" value-a value-b))
(defn main! []
(println "App loaded!"))

The namespace for this project is just app. To compile this project, first create a shadow-cljs.edn file, with configs:

{:source-paths ["src"]
:dependencies []
:builds {:app {:output-dir "target/"
:asset-path "."
:target :browser
:modules {:main {:entries [app.main]}}
:devtools {:after-load app.main/reload!
:http-root "target"
:http-port 8080}}}}

Here’s how it’s configured:

  • :source-paths is where you put source code
  • :dependencies is ClojureScript deps of this projects
  • :builds is where you specify build configurations
  • :app is a build id named by us, we will need it to run the specific compilation
  • :output-dir decides where the code are generated
  • :asset-path it is the path where the code can be found in a web context
  • :target is short for “compilation target”, here it’s :browser
  • :modules specifies the module we want to compile
  • :devtools specifies configurations for development tools
  • :http-port and :http-root tell shadow-cljs to start an HTTP server

During development, we can compile with hot code swapping, watch how we use the build id :app :

shadow-cljs watch app

After running, shadow-cljs would generate code in target/main.js and serve target/ folder on http://localhost:8080. When you open the address, the page will connect to shadow-cljs’ server and listen for updates. As you modify code and save a file, the runtime will update automatically and then call app.main/reload!.

To compile and optimize code:

shadow-cljs release app

For the complete example, please clone this repo:

Compile to Node.js

It’s quite similar steps to compile ClojureScript targeting Node.js , for the project:

.
├── README.md
├── package.json
├── shadow-cljs.edn
├── src
│ └── server
│ └── main.cljs
└── yarn.lock

src/server/main.cljs may look like:

(ns server.main)
(def value-a 1)
(defonce value-b 2)
(defn reload! []
(println "Code updated.")
(println "Trying values:" value-a value-b))
(defn main! []
(println "App loaded!"))

We can use shadow-cljs.edn with configurations:

{:source-paths ["src"]
:dependencies []
:builds {:app {:target :node-script
:output-to "target/main.js"
:main server.main/main!
:devtools {:after-load server.main/reload!}}}}

Now we use :node-script as the compilation :target . And there are two fields:

  • :output-to specifies the output file
  • :main specifies the main function

Like in previous demo, we can start compilation in development mode, and run it with Node.js :

shadow-cljs watch app
# in another terminal
node target/main.js

As Node.js runs, it will connect to shadow-cljs’ WebSocket server listening for updates. Some npm modules(wsand source-map-support) are required, please install them. It also does hot code swapping as well as calling reload! function.

To release Node.js code, it’s the same sub-command:

shadow-cljs release app

To try the full example, clone this repo:

More

There are also examples you can browse in shadow-cljs, like using Macros:

Or like compile code with long term caching:

You can find more in the Wiki and issue list of shadow-cljs. The docs is not all ready, but it’s proved running well for years and now runs in several environments of JavaScript.

Explore more on shadow-cljs and send feedbacks on GitHub :)

Like what you read? Give JiyinYiyong a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.