Introduction

Figwheel is a tool that compiles your ClojureScript with a focus on providing high quality feedback in a timely manner. The ultimate goal of Figwheel is to get out of your way so that you can just write code.

Figwheel was a pioneer in hot-reloading and provides remarkably fast workflow for a compile-to-JavaScript language, several of its features are now common in other tools.

Features

Figwheel is more than a simple wrapper around the ClojureScript compiler. Figwheel compiles more than 4 years of thought, reflection and user feedback into its codebase.

Figwheel provides a slew of features in the name of providing better ClojureScript experience.

- fast intelligent hot-reloading

- heads up display with compiler feedback

- parsed error messages with code context

- informative configuration errors

- minimal configuration

- great REPL experience

- Nodejs support

- built-in server to get started quickly

- helper application to provide contextual help

- live CSS reloading

- simplicity of only being a library

- minimal dependencies

- grokable codebase

- works equally well with Leiningen and Clojure CLI Tools

Figwheel Main vs. lein figwheel

You are currently looking at documentation for Figwheel Main which is the latest iteration of Figwheel. The original lein-figwheel is still relevant and widely used.

Figwheel Main is a complete re-write of lein-figwheel and the following advantages:

Starts faster

The Figwheel Main codebase is much smaller. A lot of attention was placed on minimizing dependencies and as a result it starts much faster.

REPL connection

Figwheel Main’s REPL evaluates on just one connected JavaScript client whereas lein-figwheel’s REPL evaluated on all connected clients with the same build-id.

Figwheel Main’s REPL will let you select which client environment to use when you have multiple connected clients.

Minimal configuration

Figwheel main makes intelligent choices for your ClojureScript compiler options. This allows you to get started with very little configuration.

Provides a cljs.main CLI interface

Utilizing the same CLI from cljs.main saves you from having to learn yet another way to compile ClojureScript. It’s a design goal to keep the experience consistent between the two tools.

The CLI is also much more expressive than lein-figwheel, which translates into less configuration. The CLI will allow you to create test builds and deployment builds without needing a separate configuration for each build.

Better Nodejs experience

With Figwheel Main you can go from zero to a fully hot reloading Node workflow with very little configuration. Figwheel Main also starts a Node process for you when you launch a build.

Simpler codebase

Figwheel Main has a much smaller codebase. It is more approachable and forgiving when one wants to understand how it works.

much more

ClojureScript, Clojure and the wider ecosystem have all changed over the last 4 years. Another thing that has changed is my understanding of how to write a Clojure tool. Figwheel Main incorporates the influences of all these changes and the result is a fundamentally different codebase with too many improvements to list here.

Why not just incorporate these changes into lein-figwheel?

It would be impossible to incorporate all the changes presented in Figwheel Main while maintaining backwards compatibility with lein-figwheel.

About this Document

This documentation is intended to be a complete guide to using Figwheel Main. It lives in the Figwheel Main Github repository under the docs/docs directory. If you notice any mistakes or errors please submit a PR.

Conventions

For simplicity this document will use Figwheel and Figwheel Main interchangeably. Both will be referring to the subject of this document: Figwheel Main.

Getting Help

Here are some resources you can turn to when you need help.

The is an active community on the Clojurians Slack. There are many Slack channels devoted to ClojureScript and various ClojureScript libraries.

When looking for help with Figwheel and ClojureScript, you should start with the #figwheel-main and #clojurescript channels.

There is also an active ClojureScript Google Group.

ClojureVerse is a very active forum.

Don’t forget to use the ClojureScript website as it has many guides and references.

The ClojureScript cheatsheet is very helpful, as is this list of ClojureScript synonyms

Learning ClojureScript

There is a lot to learn when you are first learning ClojureScript, I recommend that you bite off very small pieces at first. Smaller bites than you would take when learning other languages like JavaScript and Ruby.

There are a couple of common pitfalls that happen to folks when they try to learn ClojureScript.

Trying to learn too much

First, folks try to learn too many things in parallel. They try to learn functional programming, persistent data structures, ClojureScript tooling, hot reloading, using a browser connected REPL, Reactjs, a ClojureScript React wrapper like Reagent, Javascript, Ring (i.e. Rack for Clojure), setting up a Clojure webserver all at the same time.

This layering strategy may be an efficient way to learn when one is learning an imperative programming language like Python, Ruby or JavaScript. It becomes a losing strategy when you start to work with ClojureScript. The biggest reason for this is that the language itself is significantly different than these imperative languages. There are enough differences that you will find it difficult to associate these new patterns with the programming patterns that you are accustomed to. This unfamiliarity is easily compounded when you then add several other paradigm breakers like Reactjs and hot reloading to the mix.

The solution is to keep things as simple as possible when you start out. Choose finite challenges like learning enough of the ClojureScript language to where you can express complex things before you attempt to manipulate a web page. From there attempt to simply manipulate the DOM with the goog.dom API. Once you have a handle on that start exploring React and how to use sablono to create a dynamic web site. Then start exploring Clojure and create a simple webserver with Ring.

Trying to create a sweet dev environment

Another thing that folks do, is they try to duplicate their current development environment. As developers, we become very accustomed to having tools set up just the way we like them. I’m very sympathetic to this.

I however strongly advise that you not invest too much time trying to set up a sweet development environment, and rather invest that time into learning the ClojureScript language with a simple set of tools.

The Clojure landscape currently has a diverse set of tools that is constantly in flux. As a result, it’s very difficult to suss out which ones will actually help you. If you spend a lot of time evaluating all these options it can become very frustrating. If you wait a while, and use simple tools you will have much more fun actually using the language itself and this experience will provide a foundation to make better tooling choices.

If you are new Clojure and ClojureScript I’d advise that you start with a terminal REPL and a decent editor.

Installing Figwheel

Figwheel is just a Clojure library and can be used with several different command line tools. This page will show how to include Figwheel as a dependency with both Leiningen and Clojure CLI tools.

There are two parts to installing Figwheel. First, you will install your Clojure tool of choice. Second, you will create a project that explicitly requires Figwheel.

Choosing Leiningen vs. CLI Tools

Let’s address the question of whether one should choose Leiningen or Clojure Tools to work with Figwheel. This is largely a matter of taste at this point as these tools are quite different.

As of the writing of this document Leiningen is still the dominant Clojure dependency/task tool in the Clojure Ecosystem. Most of the tutorials you find online will be using Leiningen. It has been around for quite a while and there are innumerable plugins to assist you with your workflow. It is very stable, it works very well, and has a vast array of plugins to help you get your work done. That being said it Leiningen takes a batteries included view on tooling, it has a lot of features, and it runs through a bunch of logic to prepare a running Clojure environment for you. This complexity requires more investment to understand how and why Leiningen is doing what it is doing.

Clojure CLI Tools takes the opposite approach. It is minimal and it requires you to add functionality with different libraries as you need it. This simplicity gives CLI Tools the immediate advantage of a fast start up time. This simplicity also means that you can be fairly certain how the Java Environment is created when you run something with CLI Tools, which makes environmental problems easier to understand. The downside of this, is that features that are built into Leiningen need to be added manually as libraries, if they are even available at all. As it is still early for CLI Tools some of these libraries are quite new and not as battle tested.

At the end of the day Figwheel will work absolutely fine with either of these tools, and it is quite likely that you will use it with both of these tools even in one project.

This document is going to prefer CLI Tools because:

- it starts quickly

- it has less configuration options

- it’s very capable doing anything you need to do with figwheel.main

- does not obscure figwheel.main’s usage

On Windows

As of this writing CLI Tools on Windows is in an alpha state. For now, if you are on Windows you may want to use Leiningen.

Install your tool of choice

You will need to install the latest version of Leiningen or CLI Tools.

Install Leiningen

Make sure you have the latest version of Leiningen.

You can check that everything has been installed correctly by running a Clojure REPL. In your terminal application at the shell prompt enter the following:

$ lein repl

You should see a user=> prompt where you can enter Clojure code. Type Control-D to quit out of the Clojure REPL.

Install CLI Tools

First we will want to install the clj and clojure command line tools.

If you are on Mac OSX and you can quickly install the Clojure tools via Homebrew.

In the terminal at the shell prompt enter:

$ brew install clojure

If you’ve already installed Clojure, now is a great time to ensure that you have the latest version installed with:

$ brew upgrade clojure

You can check that everything has been installed correctly by running a Clojure REPL. In your terminal application at the shell prompt enter the following:

$ clj

You should see a user=> prompt where you can enter Clojure code. Type Control-C to quit out of the Clojure REPL.

Adding Figwheel as a dependency

You can add com.bhauman/figwheel-main by itself as a dependency to get started. However, you are better off adding Clojure and the latest version of ClojureScript along with Rebel Readline.

Adding org.clojure/clojure as a dependency will help you ensure that you are using the version of Clojure that you want to be using. Figwheel requires Clojure 1.9 at least because it uses spec to validate configuration. CLI Tools adds Clojure 1.9 by default.

Adding org.clojure/clojurescript as a dependency will allow to ensure that you are getting the latest version of ClojureScript not just the base version that Figwheel will work with.

Adding a com.bhauman/rebel-readline-cljs dependency will make the terminal REPL Figwheel launches much more capable. It is optional but highly recommended.

You will likely need to add other dependencies like Sablono, Reagent or Re-frame. However, these are not needed to work with Figwheel.

Many explanations in this document assume that you are currently in the root directory of a project, which is just a directory that contains all the code and other file assets for the program you are working on.

Dependencies with Leiningen

Leiningen requires project.clj file in the root of your project. In your project.clj file you will need to add com.bhauman/figwheel-main as a dependency.

As a concrete example, in the root directory of your project place a project.clj that at least contains the following configuration:

(defproject example-project "0.1.0-SNAPSHOT"

:dependencies [[org.clojure/clojure "1.9.0"]]

:profiles

{:dev

{:dependencies [[org.clojure/clojurescript "1.10.773"]

[com.bhauman/figwheel-main "0.2.14"]

;; optional but recommended

[com.bhauman/rebel-readline-cljs "0.1.4"]]}})

We added all the dependencies to work with ClojureScript and Figwheel into the :dev profile which is enabled by default when you are working with Leiningen, these dependencies won’t be included when you create an artifact for deployment like a jar or an uberjar.

You can verify this worked by launching a generic figwheel.main ClojureScript REPL.

$ lein trampoline run -m figwheel.main

When using Leiningen with Rebel Readline you will have to use trampoline.

A browser window should pop open and back in the terminal you should see a REPL with a cljs.user=> prompt waiting to evaluate ClojureScript code.

You can quit the REPL by entering :cljs/quit or with Control-D.

Dependencies with CLI Tools

In order to work with Clojure CLI tools you will need a deps.edn file in the root directory of your project.

As an example, in the root directory place a deps.edn file with the following contents:

{:deps {org.clojure/clojure {:mvn/version "1.9.0"}

org.clojure/clojurescript {:mvn/version "1.10.773"}

com.bhauman/figwheel-main {:mvn/version "0.2.14"}

;; optional but recommended

com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}}}

You can verify this worked by launching a generic figwheel.main ClojureScript REPL.

$ clojure -m figwheel.main

A browser window should pop open and back in the terminal you should see a REPL with a cljs.user=> prompt waiting to evaluate ClojureScript code.

You can quit the REPL by entering :cljs/quit or with Control-D.

Aliases

Both Leiningen and Clojure CLI tools supply ways that you can provide command line options to start your Clojure processes.

Aliases will play a helpful role when working with figwheel.main. You will use them as abbreviations to start build processes, to run tests, and to build a final minimized production build.

Aliases with Leiningen

As we noticed above to get a plain figwheel.main REPL we have to use the following command in the shell:

$ lein trampoline run -m figwheel.main

That’s a bit long considering how often we are going to want to use figwheel.main.

You can create a shorter fig alias for these command line options by configuring an :aliases key like so:

(defproject example-project "0.1.0-SNAPSHOT"

...

:aliases {"fig" ["trampoline" "run" "-m" "figwheel.main"]}

...

)

With that configuration you can now start a Figwheel REPL by invoking:

$ lein fig

One thing that is very important to remember is that if you want to pass additional Figwheel CLI options to the lein fig command above you will have to add a -- before the additional arguments.

Let’s display the Figwheel help documentation with the -h (or --help) option:

$ lein fig -- -h

That should display the help documentation while the lein fig -h command will not.

Why am I using using aliases here instead of creating a lein plugin? The first reason I’m not using a plugin here is that Leiningen boots much faster when it doesn’t have to dynamically load/compile plugin code. Another reason is that figwheel.main’s command line options are much more expressive than lein-figwheel’s and lein aliases are better positioned to leverage that expressiveness.

Aliases with CLI tools

Let’s look at how to add aliases with Clojure CLI tools. As an example let’s use the deps.edn configuration we started above and add a way to launch a REPL with figwheel.main.

{:deps {org.clojure/clojure {:mvn/version "1.9.0"}

org.clojure/clojurescript {:mvn/version "1.10.773"}

com.bhauman/figwheel-main {:mvn/version "0.2.14"}

;; optional but recommended

com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}}

:aliases {:fig {:main-opts ["-m" "figwheel.main"]}}}

You can use this alias from the project root directory like so:

$ clj -A:fig

You can also add additional flags as you wish. Let’s look at the help documentation with the -h (or --help) option:

$ clj -A:fig -h

These are brief instructions to help you to start being productive with these Clojure tools. You will benefit greatly by learning more about the tools you are using. Please take time to explore the documentation and features for Leiningen and/or CLI Tools. It will pay off tremendously.

It is also important to note that you can also use Maven and just plain java to work with Figwheel.

The Classpath and Project Layout

The Classpath is integral to how Clojure and the ClojureScript compiler finds dependencies and source files.

It will help if we take some time and solidify our knowledge of the Classpath and how it relates to a common ClojureScript project layout.

The JVM classpath

When I was first introduced to the JVM in college, it constantly seemed like I was always having problems with the Classpath. Even though these problems were constantly popping up I never really took the time to understand it. Luckily when you are working Clojure and ClojureScript you can get by with a simple mental model of the Classpath for 99% of the work you do.

The primary purpose of Leiningen and CLI Tools is to fetch the needed dependencies, and then invoke java with a classpath to create an environment where these dependencies are available. Tools like this make the classpath problems that I used to experience mostly a thing of the past.

First let’s take a look at a Classpath. As an example let’s use the project.clj or the deps.edn we created in the Installation chapter.

Printing the Classpath with Leiningen

Assuming you are in the root directory of your project using a project.clj file as detailed in the last chapter. You can examine the classpath with the following shell command:

$ lein classpath

And you should get this lengthy result:

src:target:/Users/bhauman/.m2/repository/com/cognitect/transit-java/0.8.332/transit-java-0.8.332.jar:/Users/bhauman/.m2/repository/org/clojure/data.json/0.2.6/data.json-0.2.6.jar:/Users/bhauman/.m2/repository/org/clojure/clojure/1.9.0/clojure-1.9.0.jar:/Users/bhauman/.m2/repository/joda-time/joda-time/2.8.2/joda-time-2.8.2.jar:/Users/bhauman/.m2/repository/commons-codec/commons-codec/1.10/commons-codec-1.10.jar:/Users/bhauman/.m2/repository/com/bhauman/figwheel-repl/0.1.4/figwheel-repl-0.1.4.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-servlet/9.2.21.v20170120/jetty-servlet-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/ring/ring-devel/1.6.3/ring-devel-1.6.3.jar:/Users/bhauman/.m2/repository/com/google/errorprone/error_prone_annotations/2.0.18/error_prone_annotations-2.0.18.jar:/Users/bhauman/.m2/repository/org/clojure/core.specs.alpha/0.1.24/core.specs.alpha-0.1.24.jar:/Users/bhauman/.m2/repository/co/deps/ring-etag-middleware/0.2.0/ring-etag-middleware-0.2.0.jar:/Users/bhauman/.m2/repository/expound/expound/0.7.0/expound-0.7.0.jar:/Users/bhauman/.m2/repository/org/clojure/spec.alpha/0.1.143/spec.alpha-0.1.143.jar:/Users/bhauman/.m2/repository/commons-fileupload/commons-fileupload/1.3.3/commons-fileupload-1.3.3.jar:/Users/bhauman/.m2/repository/com/bhauman/figwheel-main/0.1.4/figwheel-main-0.1.4.jar:/Users/bhauman/.m2/repository/ring/ring-ssl/0.3.0/ring-ssl-0.3.0.jar:/Users/bhauman/.m2/repository/org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-http/9.2.21.v20170120/jetty-http-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-util/9.2.21.v20170120/jetty-util-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/net/incongru/watchservice/barbary-watchservice/1.0/barbary-watchservice-1.0.jar:/Users/bhauman/.m2/repository/ring-cors/ring-cors/0.1.12/ring-cors-0.1.12.jar:/Users/bhauman/.m2/repository/org/jline/jline-terminal-jansi/3.5.1/jline-terminal-jansi-3.5.1.jar:/Users/bhauman/.m2/repository/com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1.jar:/Users/bhauman/.m2/repository/ring/ring-defaults/0.3.1/ring-defaults-0.3.1.jar:/Users/bhauman/.m2/repository/org/clojure/google-closure-library/0.0-20170809-b9c14c6b/google-closure-library-0.0-20170809-b9c14c6b.jar:/Users/bhauman/.m2/repository/com/bhauman/rebel-readline/0.1.4/rebel-readline-0.1.4.jar:/Users/bhauman/.m2/repository/cljfmt/cljfmt/0.5.7/cljfmt-0.5.7.jar:/Users/bhauman/.m2/repository/rewrite-clj/rewrite-clj/0.5.2/rewrite-clj-0.5.2.jar:/Users/bhauman/.m2/repository/org/clojure/clojurescript/1.10.339/clojurescript-1.10.339.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-api/9.2.21.v20170120/websocket-api-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/ring/ring-jetty-adapter/1.6.3/ring-jetty-adapter-1.6.3.jar:/Users/bhauman/.m2/repository/commons-io/commons-io/2.5/commons-io-2.5.jar:/Users/bhauman/.m2/repository/org/clojure/tools.namespace/0.2.11/tools.namespace-0.2.11.jar:/Users/bhauman/.m2/repository/com/google/jsinterop/jsinterop-annotations/1.0.0/jsinterop-annotations-1.0.0.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-client/9.2.21.v20170120/websocket-client-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.8.7/jackson-core-2.8.7.jar:/Users/bhauman/.m2/repository/clj-time/clj-time/0.11.0/clj-time-0.11.0.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-servlet/9.2.21.v20170120/websocket-servlet-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-security/9.2.21.v20170120/jetty-security-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/mozilla/rhino/1.7R5/rhino-1.7R5.jar:/Users/bhauman/.m2/repository/org/fusesource/jansi/jansi/1.16/jansi-1.16.jar:/Users/bhauman/.m2/repository/ring/ring-headers/0.3.0/ring-headers-0.3.0.jar:/Users/bhauman/.m2/repository/org/clojure/google-closure-library-third-party/0.0-20170809-b9c14c6b/google-closure-library-third-party-0.0-20170809-b9c14c6b.jar:/Users/bhauman/.m2/repository/hiccup/hiccup/1.0.5/hiccup-1.0.5.jar:/Users/bhauman/.m2/repository/binaryage/env-config/0.2.2/env-config-0.2.2.jar:/Users/bhauman/.m2/repository/com/bhauman/rebel-readline-cljs/0.1.4/rebel-readline-cljs-0.1.4.jar:/Users/bhauman/.m2/repository/com/google/javascript/closure-compiler-externs/v20180610/closure-compiler-externs-v20180610.jar:/Users/bhauman/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar:/Users/bhauman/.m2/repository/org/clojure/java.classpath/0.2.3/java.classpath-0.2.3.jar:/Users/bhauman/.m2/repository/ns-tracker/ns-tracker/0.3.1/ns-tracker-0.3.1.jar:/Users/bhauman/.m2/repository/com/google/guava/guava/22.0/guava-22.0.jar:/Users/bhauman/.m2/repository/binaryage/devtools/0.9.10/devtools-0.9.10.jar:/Users/bhauman/.m2/repository/org/msgpack/msgpack/0.6.12/msgpack-0.6.12.jar:/Users/bhauman/.m2/repository/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar:/Users/bhauman/.m2/repository/com/cognitect/transit-clj/0.8.309/transit-clj-0.8.309.jar:/Users/bhauman/.m2/repository/args4j/args4j/2.33/args4j-2.33.jar:/Users/bhauman/.m2/repository/crypto-random/crypto-random/1.2.0/crypto-random-1.2.0.jar:/Users/bhauman/.m2/repository/ring/ring-codec/1.0.1/ring-codec-1.0.1.jar:/Users/bhauman/.m2/repository/ring/ring-anti-forgery/1.1.0/ring-anti-forgery-1.1.0.jar:/Users/bhauman/.m2/repository/com/bhauman/spell-spec/0.1.0/spell-spec-0.1.0.jar:/Users/bhauman/.m2/repository/crypto-equality/crypto-equality/1.0.0/crypto-equality-1.0.0.jar:/Users/bhauman/.m2/repository/net/java/dev/jna/jna/3.2.2/jna-3.2.2.jar:/Users/bhauman/.m2/repository/com/bhauman/figwheel-core/0.1.4/figwheel-core-0.1.4.jar:/Users/bhauman/.m2/repository/cljs-tooling/cljs-tooling/0.2.0/cljs-tooling-0.2.0.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-io/9.2.21.v20170120/jetty-io-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/clojure/tools.reader/1.3.0-alpha3/tools.reader-1.3.0-alpha3.jar:/Users/bhauman/.m2/repository/compliment/compliment/0.3.6/compliment-0.3.6.jar:/Users/bhauman/.m2/repository/org/jline/jline-terminal/3.5.1/jline-terminal-3.5.1.jar:/Users/bhauman/.m2/repository/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar:/Users/bhauman/.m2/repository/org/jline/jline-reader/3.5.1/jline-reader-3.5.1.jar:/Users/bhauman/.m2/repository/clj-stacktrace/clj-stacktrace/0.2.8/clj-stacktrace-0.2.8.jar:/Users/bhauman/.m2/repository/com/google/javascript/closure-compiler-unshaded/v20180610/closure-compiler-unshaded-v20180610.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-server/9.2.21.v20170120/websocket-server-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-common/9.2.21.v20170120/websocket-common-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/com/google/protobuf/protobuf-java/3.0.2/protobuf-java-3.0.2.jar:/Users/bhauman/.m2/repository/ring/ring-servlet/1.6.3/ring-servlet-1.6.3.jar:/Users/bhauman/.m2/repository/hawk/hawk/0.2.11/hawk-0.2.11.jar:/Users/bhauman/.m2/repository/ring/ring/1.6.3/ring-1.6.3.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-server/9.2.21.v20170120/jetty-server-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar:/Users/bhauman/.m2/repository/ring/ring-core/1.6.3/ring-core-1.6.3.jar:/Users/bhauman/.m2/repository/rewrite-cljs/rewrite-cljs/0.4.3/rewrite-cljs-0.4.3.jar:/Users/bhauman/.m2/repository/com/google/code/gson/gson/2.7/gson-2.7.jar

Printing the Classpath with Clojure CLI Tools

Assuming you are in the root directory of your project which has a deps.edn file as detailed in the last chapter. You can examine the classpath with the following shell command:

$ clj -Spath

And you should get this lengthy result:

src:/Users/bhauman/.m2/repository/com/cognitect/transit-java/0.8.332/transit-java-0.8.332.jar:/Users/bhauman/.m2/repository/org/clojure/data.json/0.2.6/data.json-0.2.6.jar:/Users/bhauman/.m2/repository/org/clojure/clojure/1.9.0/clojure-1.9.0.jar:/Users/bhauman/.m2/repository/joda-time/joda-time/2.8.2/joda-time-2.8.2.jar:/Users/bhauman/.m2/repository/commons-codec/commons-codec/1.10/commons-codec-1.10.jar:/Users/bhauman/.m2/repository/com/bhauman/figwheel-repl/0.1.4/figwheel-repl-0.1.4.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-servlet/9.2.21.v20170120/jetty-servlet-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/ring/ring-devel/1.6.3/ring-devel-1.6.3.jar:/Users/bhauman/.m2/repository/com/google/errorprone/error_prone_annotations/2.0.18/error_prone_annotations-2.0.18.jar:/Users/bhauman/.m2/repository/org/clojure/core.specs.alpha/0.1.24/core.specs.alpha-0.1.24.jar:/Users/bhauman/.m2/repository/co/deps/ring-etag-middleware/0.2.0/ring-etag-middleware-0.2.0.jar:/Users/bhauman/.m2/repository/expound/expound/0.7.0/expound-0.7.0.jar:/Users/bhauman/.m2/repository/org/clojure/spec.alpha/0.1.143/spec.alpha-0.1.143.jar:/Users/bhauman/.m2/repository/commons-fileupload/commons-fileupload/1.3.3/commons-fileupload-1.3.3.jar:/Users/bhauman/.m2/repository/com/bhauman/figwheel-main/0.1.4/figwheel-main-0.1.4.jar:/Users/bhauman/.m2/repository/ring/ring-ssl/0.3.0/ring-ssl-0.3.0.jar:/Users/bhauman/.m2/repository/org/codehaus/mojo/animal-sniffer-annotations/1.14/animal-sniffer-annotations-1.14.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-http/9.2.21.v20170120/jetty-http-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-util/9.2.21.v20170120/jetty-util-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/net/incongru/watchservice/barbary-watchservice/1.0/barbary-watchservice-1.0.jar:/Users/bhauman/.m2/repository/ring-cors/ring-cors/0.1.12/ring-cors-0.1.12.jar:/Users/bhauman/.m2/repository/org/jline/jline-terminal-jansi/3.5.1/jline-terminal-jansi-3.5.1.jar:/Users/bhauman/.m2/repository/com/googlecode/json-simple/json-simple/1.1.1/json-simple-1.1.1.jar:/Users/bhauman/.m2/repository/ring/ring-defaults/0.3.1/ring-defaults-0.3.1.jar:/Users/bhauman/.m2/repository/org/clojure/google-closure-library/0.0-20170809-b9c14c6b/google-closure-library-0.0-20170809-b9c14c6b.jar:/Users/bhauman/.m2/repository/com/bhauman/rebel-readline/0.1.4/rebel-readline-0.1.4.jar:/Users/bhauman/.m2/repository/cljfmt/cljfmt/0.5.7/cljfmt-0.5.7.jar:/Users/bhauman/.m2/repository/rewrite-clj/rewrite-clj/0.5.2/rewrite-clj-0.5.2.jar:/Users/bhauman/.m2/repository/org/clojure/clojurescript/1.10.339/clojurescript-1.10.339.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-api/9.2.21.v20170120/websocket-api-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/ring/ring-jetty-adapter/1.6.3/ring-jetty-adapter-1.6.3.jar:/Users/bhauman/.m2/repository/commons-io/commons-io/2.5/commons-io-2.5.jar:/Users/bhauman/.m2/repository/org/clojure/tools.namespace/0.2.11/tools.namespace-0.2.11.jar:/Users/bhauman/.m2/repository/com/google/jsinterop/jsinterop-annotations/1.0.0/jsinterop-annotations-1.0.0.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-client/9.2.21.v20170120/websocket-client-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.8.7/jackson-core-2.8.7.jar:/Users/bhauman/.m2/repository/clj-time/clj-time/0.11.0/clj-time-0.11.0.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-servlet/9.2.21.v20170120/websocket-servlet-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-security/9.2.21.v20170120/jetty-security-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/mozilla/rhino/1.7R5/rhino-1.7R5.jar:/Users/bhauman/.m2/repository/org/fusesource/jansi/jansi/1.16/jansi-1.16.jar:/Users/bhauman/.m2/repository/ring/ring-headers/0.3.0/ring-headers-0.3.0.jar:/Users/bhauman/.m2/repository/org/clojure/google-closure-library-third-party/0.0-20170809-b9c14c6b/google-closure-library-third-party-0.0-20170809-b9c14c6b.jar:/Users/bhauman/.m2/repository/hiccup/hiccup/1.0.5/hiccup-1.0.5.jar:/Users/bhauman/.m2/repository/binaryage/env-config/0.2.2/env-config-0.2.2.jar:/Users/bhauman/.m2/repository/com/bhauman/rebel-readline-cljs/0.1.4/rebel-readline-cljs-0.1.4.jar:/Users/bhauman/.m2/repository/com/google/javascript/closure-compiler-externs/v20180610/closure-compiler-externs-v20180610.jar:/Users/bhauman/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar:/Users/bhauman/.m2/repository/org/clojure/java.classpath/0.2.3/java.classpath-0.2.3.jar:/Users/bhauman/.m2/repository/ns-tracker/ns-tracker/0.3.1/ns-tracker-0.3.1.jar:/Users/bhauman/.m2/repository/com/google/guava/guava/22.0/guava-22.0.jar:/Users/bhauman/.m2/repository/binaryage/devtools/0.9.10/devtools-0.9.10.jar:/Users/bhauman/.m2/repository/org/msgpack/msgpack/0.6.12/msgpack-0.6.12.jar:/Users/bhauman/.m2/repository/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar:/Users/bhauman/.m2/repository/com/cognitect/transit-clj/0.8.309/transit-clj-0.8.309.jar:/Users/bhauman/.m2/repository/args4j/args4j/2.33/args4j-2.33.jar:/Users/bhauman/.m2/repository/crypto-random/crypto-random/1.2.0/crypto-random-1.2.0.jar:/Users/bhauman/.m2/repository/ring/ring-codec/1.0.1/ring-codec-1.0.1.jar:/Users/bhauman/.m2/repository/ring/ring-anti-forgery/1.1.0/ring-anti-forgery-1.1.0.jar:/Users/bhauman/.m2/repository/com/bhauman/spell-spec/0.1.0/spell-spec-0.1.0.jar:/Users/bhauman/.m2/repository/crypto-equality/crypto-equality/1.0.0/crypto-equality-1.0.0.jar:/Users/bhauman/.m2/repository/net/java/dev/jna/jna/3.2.2/jna-3.2.2.jar:/Users/bhauman/.m2/repository/com/bhauman/figwheel-core/0.1.4/figwheel-core-0.1.4.jar:/Users/bhauman/.m2/repository/cljs-tooling/cljs-tooling/0.2.0/cljs-tooling-0.2.0.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-io/9.2.21.v20170120/jetty-io-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/clojure/tools.reader/1.3.0-alpha3/tools.reader-1.3.0-alpha3.jar:/Users/bhauman/.m2/repository/compliment/compliment/0.3.6/compliment-0.3.6.jar:/Users/bhauman/.m2/repository/org/jline/jline-terminal/3.5.1/jline-terminal-3.5.1.jar:/Users/bhauman/.m2/repository/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar:/Users/bhauman/.m2/repository/org/jline/jline-reader/3.5.1/jline-reader-3.5.1.jar:/Users/bhauman/.m2/repository/clj-stacktrace/clj-stacktrace/0.2.8/clj-stacktrace-0.2.8.jar:/Users/bhauman/.m2/repository/com/google/javascript/closure-compiler-unshaded/v20180610/closure-compiler-unshaded-v20180610.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-server/9.2.21.v20170120/websocket-server-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/websocket/websocket-common/9.2.21.v20170120/websocket-common-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/com/google/protobuf/protobuf-java/3.0.2/protobuf-java-3.0.2.jar:/Users/bhauman/.m2/repository/ring/ring-servlet/1.6.3/ring-servlet-1.6.3.jar:/Users/bhauman/.m2/repository/hawk/hawk/0.2.11/hawk-0.2.11.jar:/Users/bhauman/.m2/repository/ring/ring/1.6.3/ring-1.6.3.jar:/Users/bhauman/.m2/repository/org/eclipse/jetty/jetty-server/9.2.21.v20170120/jetty-server-9.2.21.v20170120.jar:/Users/bhauman/.m2/repository/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar:/Users/bhauman/.m2/repository/ring/ring-core/1.6.3/ring-core-1.6.3.jar:/Users/bhauman/.m2/repository/rewrite-cljs/rewrite-cljs/0.4.3/rewrite-cljs-0.4.3.jar:/Users/bhauman/.m2/repository/com/google/code/gson/gson/2.7/gson-2.7.jar

The Classpath as a search path

If you look at the classpaths above you you will see a list of paths separated by a : character. If you have ever looked at the $PATH variable in your shell environment this should look familiar.

The classpath is a list of paths that will be used to search for various artifacts. For instance Clojure uses the classpath to find and load Clojure namespaces. ClojureScript does the same to find and compile ClojureScript source files.

Another thing to notice about the classpaths above is that the classpath starts with several local directories and then continues with a quite of number of .jar files that represent our dependencies.

If we format the classpath that CLI tools generated we can see that it starts with a single local src directory.

$ clj -Spath |  sed -e 's/:/\'$'\n/g' | head

src

/Users/bhauman/.m2/repository/com/cognitect/transit-java/0.8.332/transit-java-0.8.332.jar

/Users/bhauman/.m2/repository/org/clojure/data.json/0.2.6/data.json-0.2.6.jar

/Users/bhauman/.m2/repository/org/clojure/clojure/1.9.0/clojure-1.9.0.jar

/Users/bhauman/.m2/repository/joda-time/joda-time/2.8.2/joda-time-2.8.2.jar

/Users/bhauman/.m2/repository/commons-codec/commons-codec/1.10/commons-codec-1.10.jar

...

This local src directory is a default that is added to the classpath by CLI Tools if no other paths are configured in the deps.edn.

Looking up a source file on the Classpath

When the ClojureScript compiler is looking for a namespace to analyze or compile, it will utilize the classpath to find the file.

For example if a ClojureScript namespace hello-world.core is required the ClojureScript compiler is going to utilize the classpath to look for a file named hello_world/core.cljs. If our only local path is src then hello_world/core.cljs is going to have to be in the src directory in order to be found.

./

├── deps.edn

└── src

└── hello_world

└── core.cljs

Here I’m adding an example.trigger namespace to show where it would need to be placed.

./

├── deps.edn

└── src

├── example

│   └── trigger.cljs

└── hello_world

└── core.cljs

All of your ClojureScript and Clojure source files are going to need to have namespace based paths that are rooted in a path on the classpath.

Adding additional paths to the Classpath

When you set up your project you will often have one or more source directories. Some folks prefer to have a cljs-src directory that only holds files relevant to a local ClojureScript codebase (i.e. a front-end application).

Adding the cljs-src path with CLI Tools

Let’s add the cljs-src path to our classpath with CLI Tools. In your deps.edn file:

{:deps {org.clojure/clojure {:mvn/version "1.9.0"}

org.clojure/clojurescript {:mvn/version "1.10.773"}

com.bhauman/figwheel-main {:mvn/version "0.2.14"}

com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}}

:aliases {:fig {:main-opts ["-m" "figwheel.main"]}}

;; define paths here

:paths ["src" "cljs-src"]}

Above we added :paths key with the value ["src" "cljs-src"]. We have to explicitly add "src" to the paths key because once it is defined the src is no longer added implicitly.

Adding the cljs-src path with Leiningen

Let’s add the cljs-src path to our classpath with CLI Tools. In your project.clj file:

(defproject example-project "0.1.0-SNAPSHOT"

...

:source-paths ["src" "cljs-src"]

...

)

Above we added :source-paths key with the value ["src" "cljs-src"]. We have to explicitly add "src" to the paths key because once it is defined the src is no longer added implicitly.

Unlike CLI Tools, Leiningen has several keys to add paths to the classpath depending on the type of path it is. This is because Leiningen provides jar bundling built-in and needs to know which paths to bundle. All of the following project.clj configuration keys add paths to the classpath.

- :source-paths - is for paths that hold source files

- :resource-paths - holds paths to assets that you want

- :target-paths - for paths that hold output files that can be safely deleted

Inspecting the new classpath

Now that we’ve added a new path let’s verify that its working the way we want it to.

$ clj -Spath | sed -e 's/:/\'$'\n/g' | head -n 5

src

cljs-src

/Users/bhauman/.m2/repository/com/cognitect/transit-java/0.8.332/transit-java-0.8.332.jar

/Users/bhauman/.m2/repository/org/clojure/data.json/0.2.6/data.json-0.2.6.jar

/Users/bhauman/.m2/repository/org/clojure/clojure/1.9.0/clojure-1.9.0.jar

and there we see cljs-src is now on the classpath.

Checkpoint: Project layout

We’ve been trying and learning different things so far. Let’s apply this to a project with a hello_world.core ClojureScript namespace that looks like this:

Place this code in src/hello_world/core.cljs:

(ns hello-world.core)

(js/console.log "Hello World!")

Ensure that src is on the classpath. For this example your project directory file tree should look like this:

./

├── deps.edn # or project.clj

└── src

└── hello_world

└── core.cljs

We will continue to use this example from here on out.

Using the classpath to find web assets

Once we start to understand the classpath we can use it to our advantage. We can bundle our web assets (HTML, CSS, Javascript files) in a jar and easily access them from inside our web process.

A common pattern in Clojure web development is to serve our public web assets from a public directory that is on the classpath. This is the default for the Jetty server Figwheel provides to deliver compiled ClojureScript files and other assets.

It is also a Clojure/Java idiom to place a resources directory on the classpath to hold file assets that are not source files that we will want to bundle and deploy with our application or library.

Concretely, we want to create a local resources directory and add it to the classpath:

$ mkdir resources

Add resources to the classpath with CLI Tools

Edit deps.edn:

{:deps {org.clojure/clojure {:mvn/version "1.9.0"}

org.clojure/clojurescript {:mvn/version "1.10.773"}

com.bhauman/figwheel-main {:mvn/version "0.2.14"}

com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}

:aliases {:fig {:main-opts ["-m" "figwheel.main"]}

;; add "resources" path here

:paths ["src" "resources"]}

With Leiningen, there is no need to add resources to the classpath as Leiningen already adds it as a default. Extra Credit: check your Leiningen classpath and verify this for yourself.

Now that we have a resources directory on the classpath we will need to place a public directory inside of it.

$ mkdir resources/public

Let’s add a minimal robots.txt file as an example static file that we may want to serve from the root of our webserver.

Create a resources/public/robots.txt with the following contents:

User-agent: *

Disallow: /

So now we have a project directory that looks like this:

./

├── deps.edn # or project.clj

├── resources

│   └── public

│       └── robots.txt

└── src

└── hello_world

└── core.cljs

It will be helpful to learn how to get at this file from Clojure.

The following REPL session demonstrates how you can test that files are accessible from the classpath.

# start a REPL in the project root

$ clj

Clojure 1.9.0

user=> (require '[clojure.java.io :as io])

nil

user=> (slurp (io/resource "public/robots.txt"))

"User-agent: *\nDisallow: /\n"

user=> # control-C to exit

We were able to find the public/robots.txt file on the classpath, this means our Webserver will be able to find it as well.

So now we have learned how to set the classpath so that the ClojureScript compiler can find our source files and so that the Webserver can find our static files. There is one more thing to consider when setting up our project.

The Target Path

When we compile our ClojureScript source code the resulting JavaScript output files will need a place to go and that place will need to be on the classpath so that the webserver can serve them.

An earlier convention, and one that you can still use, was to place the output files in the resources/public directory alongside all of your other web assets.

While that works, figwheel.main by default places output files in the target/public directory. Using the target directory for output files is a convention that is already used by Leiningen. The major reason for this is that compiled files are temporary and need to be deleted from time to time. Leiningen has a lein clean task that deletes the target directory and thus cleans out all compiled and cached assets allowing us to start fresh, and perhaps eliminate stale files that are causing problems.

Because our output files are going to be placed in the target/public directory by default and we want them to be served as static assets by the webserver we will need to add the target directory (NOT target/public) to our classpath.

Adding the target directory with Leiningen

Unfortunately I’m not going to fully explain the changes to your project.clj file as it would require introducing quite a few ideas.

The following config is going to add a path to your classpath in development mode so that the files don’t get added to your deployed uberjar. It’s going to use the :resource-paths because it seems to be the most appropriate considering how we are using it. We are also making an adjustment so that we can call lein clean without violating a Leiningen fail-safe.

(defproject example-project "0.1.0-SNAPSHOT"

...

:profiles {:dev {:resource-paths ["target"]

:clean-targets ^{:protect false} ["target"]}}

...

)

If you call lein classpath you will now see that target is indeed on the classpath.

Learn more about Leiningen profiles here or with the lein command lein help profiles

Adding the target directory with CLI Tools

This is just a simple matter as we just need to add "target" to the :paths key.

{:deps {org.clojure/clojure {:mvn/version "1.9.0"}

org.clojure/clojurescript {:mvn/version "1.10.773"}

com.bhauman/figwheel-main {:mvn/version "0.2.14"}

com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}

:aliases {:fig {:main-opts ["-m" "figwheel.main"]}

;; add "target" path here

:paths ["src" "resources" "target"]}

If you now call clj -Spath you will see the local target directory listed.

When working with CLI Tools we are also going to want to create the target directory because paths on the classpath that we want to resolve files in need to exist before the JVM starts, or file resolution will not work.

Final classpath and project layout

We have completed a rather long explanation of what paths need to be on the classpath and why.

In summary, we need the following paths on our classpath to work with figwheel.main:

- src and other CLJS source directories - so the compiler can find source files

- resources - so the webserver can serve static assets

- target - so the webserver can serve our compiled ClojureScript

Thus, our general project layout will look like this:

./

├── deps.edn # or project.clj

├── resources

│   └── public

│       # web assets HTML, CSS, images, etc

├── src

│   ├── hello_world

│   │   └── core.cljs

│   │       # other source files

└── target

└── public

# compiled ClojureScript files

Create a Build

In order to start using Figwheel's hot reloading based workflow we will need to create a build. A build defines a compilation process, and is going to be your main unit of configuration.

figwheel.main has a CLI that is fairly expressive. However, most folks who work with it are going to want to define a watch/compile process with a hot-reloading workflow to get the bulk of their work done.

Now that we know how to add our dependencies and set up our project and classpath, let’s start using Figwheel to compile and reload our ClojureScript code.

We’ll start with a project setup as described in the previous chapters.

Your project layout should look like this:

./

├── deps.edn # or project.clj

├── resources

│   └── public

├── src

│   └── hello_world

│       └── core.cljs

└── target

The contents of the deps.edn file should be:

{:deps {org.clojure/clojure {:mvn/version "1.9.0"}

org.clojure/clojurescript {:mvn/version "1.10.773"}

com.bhauman/figwheel-main {:mvn/version "0.2.14"}

com.bhauman/rebel-readline-cljs {:mvn/version "0.1.4"}}

:aliases {:fig {:main-opts ["-m" "figwheel.main"]}}

:paths ["src" "resources" "target"]}

If you’re using Leiningen your project.clj should be:

(defproject example-project "0.1.0-SNAPSHOT"

:dependencies [[org.clojure/clojure "1.9.0"]]

:profiles

{:dev

{:dependencies [[org.clojure/clojurescript "1.10.773"]

[com.bhauman/figwheel-main "0.2.14"]

;; optional but recommended

[com.bhauman/rebel-readline-cljs "0.1.4"]]

:resource-paths ["target"]

:clean-targets ^{:protect false} ["target"]}}

:aliases {"fig" ["trampoline" "run" "-m" "figwheel.main"]})

The contents of the src/hello_world/core.cljs file should be:

(ns hello-world.core)

(js/console.log "Hello there world!")

Configuring a build

The ClojureScript compiler can take a fairly extensive set of configuration options. Figwheel provides sane defaults for several important compiler options. This will allow us to configure a compile process by simply defining the :main option.

We are going to pass this option to Figwheel via a build file. A build file is a named set of compiler configuration options. Figwheel utilizes this name to isolate a particular build’s REPL connection and output files from all the other builds.

The build file will sit in our project root directory and the build file’s name will take the form [build-name].cljs.edn where you will substitute [build-name] with a name of your choosing.

Let’s create a build called dev. Create a dev.cljs.edn file with the following content:

{:main hello-world.core}

The above :main option defines a root namespace for our build. When we include the compiled artifact on a webpage it will pull in all the code that our :main namespace depends on.

Figwheel will only be able to start a REPL and hot reload if :optimizations level is at its default setting of :none as it is in the above configuration. The other :optimization levels are intended to be used for deployment.

Running a build

At this point we have everything we need to start compiling and editing our code with a hot reloading workflow.

Start a build with CLI Tools

Run the following in the root directory of the project:

$ clojure -m figwheel.main --build dev --repl

We can also use the shorter -b and -r flags

$ clojure -m figwheel.main -b dev -r

or with the defined alias:

$ clojure -A:fig -b dev -r

Start a build with Leiningen

Let’s use our defined alias:

$ lein fig -- --build dev --repl

We can also use the shorter -b and -r flags

$ lein fig -- -b dev -r

Once you’ve started Figwheel

When you start Figwheel you should see a browser pop open:

default dev page

The green Connected animation that appears next to the CLJS logo indicates the browser environment has successfully connected back to the Figwheel server. If you open your browser’s devtools for the current page you will see Hello there world! (remember that the code that prints this is in our hello_world.core namespace).

devtools open

You should now be able to return to the terminal where you launched Figwheel from to see a working REPL that is ready for you to evaluate some ClojureScript Code.

screen shot 2018-07-24 at 4 44 25 pm

You can now try out some ClojureScript at the REPL.

As an example try entering (js/alert "Crocodile Rock!") at the cljs.user=> prompt.

You can also return to the src/hello_world/core.cljs file and edit the "Hello there world!" string so that it reads "Live edit!!!!!" and then save the file.

You should notice that Figwheel reports loading the new file in the console and you should see Live edit!!!!! printed in the console as well.

Congrats!

You have successfully set up a Figwheel hot-reloading compile process for a ClojureScript project.

If you have never built an application with ClojureScript this is a good place to start building from.

If you are building an application that works with the DOM there is a <div id="app"></div> already on the Figwheel Default Dev Page that you can override. If you clear it’s contents all of the CSS and HTML for the content of the Dev Page will removed.

Adding Figwheel configuration to the build

Figwheel Main has several options to configure how your build process works.

You can add these configuration options straight to your dev.cljs.edn build file. As an example you can turn off hot reloading by adding :hot-reload-cljs false to your build file like this:

^{:hot-reload-cljs false}

{:main hello-world.core}

The ^ character is not a typo, it’s a reader macro that in this case is adding metadata to the map that follows it. Figwheel examines the metadata of your build config for its own configuration options.

You can also specify a map of Figwheel configuration in a figwheel-main.edn file in your project root directory. See the configuration for more details.

Host Page

A host page is the HTML page that includes your ClojureScript program. Figwheel provides a default host page, but sooner or later you will want to supply your own.

The Figwheel server

Figwheel starts a server when it launches a build and/or a REPL. The primary purpose of this server is to provide websocket communication between the REPL and the client environment. Figwheel not only uses this connection to evaluate compiled REPL expressions, it also uses it to communicate hot reloads, compile errors, and other things.

The secondary use of this server is as an initial development HTTP server. It can serve static assets (HTML, images, CSS, etc.) from the classpath (this is described in Classpaths). It also lets you supply a ring handler (via :ring-handler config option) to embed your own HTTP endpoints in the server.

The instructions on this page only apply if you are using the Figwheel server to serve your application.

It is important to note that you can and most likely should supply your own server to run alongside of Figwheel’s server as the needs of your project outgrow what the dev server provides. Figwheel is designed to handle the cross-origin communication necessary to make a connection to a page served by your own server.

Default Host Page

By default when Figwheel launches it navigates a browser to the root of the server (usually http://locahost:9500). In response, the Figwheel server provides a default dev page if the root route (/) fails and no public/index.html file is found on the classpath.

Including your compiled ClojureScript

When you supply HTML content for a host page, it is important to correctly include your compiled CLJS code.

By default, Figwheel will direct the compiler to output your compiled CLJS to target/public/cljs-out/[build]-main.js (substituting your build name for [build]).

If you have doubts about where the compiler is placing your output file, you can examine Figwheel’s start-up messages.

showing output file

As you can see in the above start-up messages, Figwheel is compiling the build to target/public/cljs-out/dev-main.js.

In order to include this file in our host page, we will utilize the path to the output file minus the classpath based webroot target/public. This leaves us with /cljs-out/dev-main.js as the path to our compiled ClojureScript.

You should take a moment to look at the content of this file. You will see that it is actually a bootstrap script which itself requires all of the necessary files for the program. This is not the case when you use a different :optimizations setting such :simple, :whitespace or :advanced. When these settings are used the output file will be a single file. You may be tempted to use these settings for development, but for the REPL and Figwheel to work the default :optimizations setting of :none is required.

When you include the output file on your HTML page you will want to place the <script> tag as the last tag in your <body> content. This is the convention for Google Closure compiled projects.

An example host page:

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

<link href="css/style.css" rel="stylesheet" type="text/css">

<link rel="icon" href="https://clojurescript.org/images/cljs-logo-icon-32.png">

</head>

<body>

<div id="app"></div>

<!-- include your ClojureScript at the bottom of body like this -->

<script src="/cljs-out/dev-main.js" type="text/javascript"></script>

</body>

</html>

The css/style.css file will need to be available on the classpath. In our running example, a good place for this file would be at resources/public/css/style.css.

Providing your own page

There are several ways to override the default dev page and provide your own page:

- supply a static index.html file in a public directory on the classpath

- use a page other than index.html and change the :open-url config option to point to it

- create a Ring handler that handles the root route / and supply it to the :ring-handler config option

Supply a static index.html

The most common way to use your HTML content to host your app is to place an index.html file in a public directory on the classpath. The most common place for this is resources/public/index.html.

Using a page other than server root

Another way is to provide a static HTML page other than the index.html root page. This pattern is very helpful if you need to have several different host pages.

As an example, let’s say we want our build to use admin.html as the host page. First, we will create a resources/public/admin.html file and ensure that we are requiring our compiled ClojureScript correctly.

Once that is done we will want to make our build pop open the admin.html url in our browser. We can use the :open-url config option to do this.

In our build file we’ll set :open-url like so:

^{:open-url "http://localhost:[[server-port]]/admin.html"}

{:main example.core}

Create a Ring handler that handles the root route

It’s surprisingly easy to start building out server side actions in Figwheel. Let’s create a handler that responds to the root route and delivers the HTML content to host our app.

You can create a server.clj file right next to CLJS source files. In our example hello-world app the handler would be at src/hello_world/server.clj.

This file will be a Clojure source file rather than a ClojureScript source file and it will be running in the Clojure server process.

We are about to introduce you to some Clojure code. If you have never worked with Clojure up to now, I highly recommend you understand how to work with Clojure at the REPL before starting this. If you do want to go ahead with this example, first start your hello-world.core example build running with clojure -m figwheel.main -b dev -r and keep your the browser page open and in view while you code it, as this will give you important feedback about the correctness of the Clojure code as you work. Only configure the :ring-handler option when you are sure the code is correct and loadable.

Without further ado here is our fancy handler to serve our HTML host page.

(ns hello-world.server)

;; define index content

(def home

"<!DOCTYPE html>

<html>

<head>

<meta charset=\"UTF-8\">

<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">

<link href=\"css/style.css\" rel=\"stylesheet\" type=\"text/css\">

<link rel=\"icon\" href=\"https://clojurescript.org/images/cljs-logo-icon-32.png\">

</head>

<body>

<div id=\"app\"></div>

<script src=\"/cljs-out/dev-main.js\" type=\"text/javascript\"></script>

</body>

</html>")

(defn handler [request]

(if (and (= :get (:request-method request))

(= "/"  (:uri request)))

{:status 200

:headers {"Content-Type" "text/html"}

:body home}

{:status 404

:headers {"Content-Type" "text/plain"}

:body "Not Found"}))

Fancy eh?

Now we’ll configure Figwheel to embed the handler with the :ring-handler config option in our build file.

Let’s configure the ring handler in our example dev.cljs.edn:

^{:ring-handler hello-world.server/handler}

{:main hello-world.core}

Now when you start Figwheel and the browser opens up to display the root server route it will be served from your handler.

If you are creating an SPA with pushState routing you may want every route or a small subset of routes to return the home page. As you can see it would be pretty straight forward to modify the above handler to return the home page when needed.

Live CSS reloading

Figwheel will hot reload your CSS files as you edit them.

You need to do four things for this to work:

- ensure that you are providing your own HTML host page

- include a link to your CSS in your host page HTML

- ensure your CSS is in a public directory on the classpath

- configure the :css-dirs config option

Including link to CSS file

Include a link tag to your CSS on your HTML host page.

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<!-- we are including the CSS here -->

<link href="css/style.css" rel="stylesheet" type="text/css">

</head>

<body>

<div id="app">

</div>

<script src="cljs-out/dev-main.js" type="text/javascript"></script>

</body>

</html>

Ensuring your CSS file can be served

The above example will work if you place your CSS file in a public/css directory on the classpath. Since the resources directory is normally on the classpath by convention we can place our CSS files resources/public/css.

Tell Figwheel to watch and reload CSS

You will use the :css-dirs config key to tell Figwheel to which directories to watch for CSS file changes.

You can do this one of two ways: in the build file or in the figwheel-main.edn file.

As an example let’s assume a dev.cljs.edn build file. You can add :css-dirs config to the metadata in the build file like so:

^{:css-dirs ["resources/public/css"]}

{:main example.core}

Or you can set it for all builds and compiles in the figwheel-main.edn:

{:css-dirs ["resources/public/css"]

;; rest of the config

}

Then you restart your build:

clojure -m figwheel.main -b dev -r

Now you should be able to edit and save the resources/public/css/style.css file and see the changes rendered live in your application.

Compile Configuration

The ClojureScript compiler has a dizzying number of configuration options. However, there are really only a few key ones you need to understand to work effectively.

Figwheel handles setting up several compilation options for you, but it’s very important to understand these options, and how Figwheel uses them to inject itself into a build. Having this knowledge will empower you to understand how your build is working and how to adjust the options so that they serve you better.

Here are the compiler options we’ll cover:

- --print-config

- :output-to

- :output-dir

- :target

- :optimizations

- :main

- :asset-path

- :preloads

- :closure-defines

The --print-config option

You can use the figwheel.main CLI option --print-config (or -pc) to examine the compile options that Figwheel is sending to the ClojureScript compiler. The --print-config CLI flag will only print out the configuration, it will not run the command.

It’s important to note that order matters for figwheel.main CLI options. The -pc flag is considered an init option and as such it must always come before a main option (-c, -b, -r, -m are all main options). In the commands that we’ve been using so far in this documentation, this means it has to come before the -b option.

Keeping with our hello-world.core example let’s print out the configuration.

$ clojure -m figwheel.main -pc -b dev -r

The above command should print out something like the following:

[Figwheel] :pprint-config true - printing config:

---------------------- Figwheel options ----------------------

{:pprint-config true, :mode :repl, :watch-dirs ("src")}

---------------------- Compiler options ----------------------

{:main hello-world.core,

:output-to "target/public/cljs-out/dev-main.js",

:output-dir "target/public/cljs-out/dev",

:asset-path "cljs-out/dev",

:preloads

[figwheel.core

figwheel.main

figwheel.repl.preload

devtools.preload

figwheel.main.evalback],

:aot-cache false,

:closure-defines

{figwheel.repl/connect-url

"ws://localhost:9500/figwheel-connect?fwprocess=b96800&fwbuild=dev"},

:repl-requires

([figwheel.repl :refer-macros [conns focus]]

[figwheel.main

:refer-macros

[stop-builds start-builds build-once reset clean status]])}

As you can see there are two sections; one displaying the current config options for Figwheel and one displaying the final compile options to be sent to the ClojureScript compiler.

Looking at the compiler options you will see that while we only supplied {:main hello-world.core} in our dev.cljs.edn, Figwheel added a few more options. We will focus on explaining these options.

The :output-to option

The :output-to option is “the path to the JavaScript file that will be output”. This is simple enough. You provide the :output-to option a path to a file where the compiled ClojureScript should be output. This file will always represent your main compiled artifact.

However, the contents of the file and how we load this file will vary based on the :optimizations, :main, and :target options.

The most important thing to remember when working with the Figwheel server is that the :output-to option needs to point to a path that is basically the classpath + public.

The Figwheel default for this path is in the target/public directory because we want to separate files that are compiled (and thus temporary) from files that we edit and keep in version control like HTML and CSS.

The Figwheel default as mentioned before uses the build name and looks like

target/public/cljs-out/[build-name]-main.js

If you don’t use a build file then the :output-to path may look like

target/public/cljs-out/main.js

This can lead to problems if you are using different command line options to obtain different compilation results.

The :output-dir option

The :output-dir sets the output directory for temporary files used during compilation.

Regardless of the :optimizations level, the compiler will output a file for each ClojureScript “namespace” in your project and all of its dependencies, as well as the Google Closure libraries and foreign libraries that are utilized.

If you have a hello-world.core example you can examine the :output-dir after a compile and see the temporary files:

target/public/cljs-out/dev

├── cljs

│   ├── core.cljs

│   ├── core.js

│   ├── core.js.map

│   ├── pprint.cljs

│   ├── pprint.cljs.cache.json

│   ├── pprint.js

│   ├── pprint.js.map

│   ├── stacktrace.cljc

│   ├── stacktrace.cljc.cache.json

│   ├── stacktrace.js

│   ├── stacktrace.js.map

... a lot more

These files also serve as a cache for the compiler which enables incremental compiles. If your source file has a timestamp newer than the file in the output directory, then the compiler will compile the source file. Caches can get stale however and you will want to delete your output directory on a regular basis.

When no :output-dir is defined, Figwheel will provide a default :output-dir which will have the form:

target/public/cljs-out/[build-name]

For example the default :output-dir for our dev.cljs.edn build file will be target/public/cljs-out/dev.

You may notice that the :output-dir path is placed so that its contents are available via the classpath. The reason for this is because when we use the default :optimizations level :none many of these “temporary files” are directly loaded into our client environment. For example, in a web environment when we load our :output-to file, the Google Closure base code will calculate the files that need loading and then load them in order. Hence the browser needs to serve these files and this is why they need to be available on the classpath.

So if you choose to customize the :output-dir path keep in mind that they will need to be found by the client environment if you are using :optimizations :none.

But … you should never put the :output-dir directly on the classpath. This means if target is in the classpath, you should never set :output-dir to the target directory. If you do this you are making your compiled artifacts resolvable by the CLJS compiler. This means that when the CLJS compiler looks for a required CLJS file it will possibly find it in the target directory and this will cause major problems.

The :optimizations option

The :optimizations compiler option designates the optimizations level that the Google Closure compiler should use. The available optimization levels are:

- :none - the default level, no code optimizations

- :whitespace - basically just removes whitespace and comments

- :simple - removes whitespace and shortens local variable names

- :advanced - more aggressive renaming, dead code removal, global inlining

The Figwheel --build option and the ClojureScript REPL both require :optimizations to be set to :none. :none is the default so it does not need to be specified in the configuration options map.

As we noted before the :none level does not produce a single self-contained compiled artifact, but rather creates an artifact that loads all of the separately compiled namespaces.

All the other levels (:whitespace, :simple, and :advanced) produce a single compiled artifact to the :output-to file. These are often used for producing your final deployable asset when you use these optimizations settings.

The :advanced level provides absolutely amazing compression and optimization but it is also the most finicky. This guide can be helpful.

The :main option

The :main option specifies an entry point (root namespace) for your compiled artifact. When :optimizations is :none the :main option will cause the compiler to generate a file that will in turn load all the files that are needed by the :main namespace. Actually, it does more than this. It adds all the :closure-defines that we have specified in our configuration, and requires all of the :preloads.

The :main option bootstraps your client environment and as such it behaves differently depending on the :target option.

Figwheel does not have a default for the :main option and requires that you provide a namespace value.

You can provide a symbol or a string, and it needs to be a namespace that is available in your source path directories that are on the classpath.

The example we have been using in this documentation has been:

:main hello-world.core

The :asset-path option

This only affects the build if you are using the :main option with :optimizations :none. The generated bootstrap script in the :output-to file needs to know the path to your temporary files in :output-dir. This path is relative to your webroot.

As you can see above in the compile options for our example hello-world.core project our :asset-path is this:

:asset-path "cljs-out/dev"

Basically you can think of the :asset-path as your :output-dir minus your webroot directory.

So in our case:

"target/public/cljs-out/dev" - "target/public/" => "cljs-out/dev"

If your application can’t seem to find the files it needs to load, it normally means you have a misconfigured :asset-path.

The :target option

There are three values for the :target option. Actually there are two values and the absence of a :target option. If you don’t specify a :target then it will be assumed that our client environment is the Browser.

The other valid targets are :nodejs and :webworker.

The :target option will change the output of the :main bootstrap script which gets output to the :ouput-to file. The script will handle the environmental needs of loading your ClojureScript code for that particular environment.

The :target option will also change the output code based on what the environment needs.

Figwheel does not add or change the :target option. It will respond to it and ensure that it starts a Node backed REPL if it needs to.

The :preloads option

When we load a ClojureScript application we start at the :main namespace to find all the files that need to be loaded. Sometimes we want to inject functionality (like REPL support) into our applications.

The :preloads option allows you to inject namespaces into your runtime environment.

Figwheel takes advantage of this to inject its code into your environment. You can see this in the hello-world.core example project above. Figwheel appended several :preloads to the configuration:

:preloads

[figwheel.core

figwheel.main

figwheel.repl.preload

devtools.preload

figwheel.main.evalback]

All of these namespaces add additional functionality to your application. Notice that this is how we add devtools and the figwheel.repl connection to your build.

You can add pre-loads as well to add behavior to your development or production builds.

The :closure-defines option

The :closure-defines option is especially powerful. It allows us to define namespaced constants with goog-define and configure their values in the :closure-defines map.

Let’s say we want to have DEBUG and LOCALE variables that we set at compile time.

(ns hello-world.core)

(goog-define DEBUG false)

(goog-define LOCALE "en")

Important: goog-define only works with String, Booolean, and Number values.

So we’ve defined these constants along with their default values. We can override these default values in our configuration with the :closure-defines option like so:

:closure-defines {hello-world.core/DEBUG true

hello-world.core/LOCALE "fr"}

Figwheel uses :closure-defines to supply the connection URL to the figwheel.repl connection code.

You can see in our example above that its value is:

:closure-defines

{figwheel.repl/connect-url

"ws://localhost:9500/figwheel-connect?fwprocess=b96800&fwbuild=dev"}

Hot Reloading

Figwheel's dominating feature is a very fast hot reloaded workflow.

When you start a build process with the --build option and your :optimizations level is the default :none, Figwheel starts a hot-reloaded workflow by default.

Working in a hot-reloaded environment can initially take some getting used to, but once you get the hang of it you will experience a noted increase in productivity and enjoyment in your coding.

Among the challenges of working in a hot reloaded environment the biggest one is ensuring that you are aware of the load time side-effects of your code.

Here we are going to cover the tools available to you to tweak the hot reloading behavior of Figwheel.

Configuring which directories to watch

Of all the Figwheel config options the one you will use the most often is the :watch-dirs option. This option determines which directories Figwheel will watch for file changes. While Figwheel will guess which directory to watch based on your :main namespace, it is better to configure this explicitly. Please take a moment to read the :watch-dirs documentation. It is very important to understand that watched directories need to also be in the list of paths on the classpath.

Note that you will normally configure this on a per build basis, for example in our dev.cljs.edn:

^{:watch-dirs ["src"]}

{:main hello-world.core}

Reload a file on every save

Sometimes there is a file that you want to reload every time a file is saved. You can accomplish this by adding :figwheel-always to the namespace of the file.

For example:

(ns ^:figwheel-always hello-world.core)

(println "hello")

There is rarely a need to use this because Figwheel will reload all the files that are depending on the changed file that initiated the reload. However, in some cases you may want to reload a file on every save when it doesn’t depend on your current file.

Using :figwheel-always may make more sense when you disable the reloading of dependents.

Never reload a given file

There are files that once they’ve been loaded you don’t want them to reload again. This can be accomplished by adding :figwheel-no-load to the namespace of the file that you don’t want to be reloaded.

For example:

(ns ^:figwheel-no-load hello-world.core)

(println "hello")

This will prevent hello-world.core from ever being re-loaded. It will load the first time but will not be reloaded after that.

Force a file to be loaded

Figwheel only reloads files that have been required by your application. Sometimes you are working on a file that hasn’t been required by your application and you want to load the file into your application. You could go to the REPL and require it, but that would take your focus out of the file you are editing. You can force a file to be loaded by adding :figwheel-load metadata to the namespace.

For example:

(ns ^:figwheel-load hello-world.core)

(println "hello")

This will force hello-world.core to be loaded for the first time. Once it’s been loaded, you can safely remove the :figwheel-load metadata because the namespace is now noted as a dependency in your client environment and will be reloaded on change like all the other required files.

:figwheel-load is intended to be a development time tool so you can load files that help with your dev process without having to explicitly require them.

Reload hooks

It is common to want to provide callbacks to do some housekeeping before or after a hot reload has occurred.

You can conveniently configure reload callbacks at runtime by first adding :figwheel-hooks metadata to the namespace that contains functions that you want called on reload.

Once the namespace is marked you will then need to add :before-load and :after-load metadata to the functions that you want called on every reload.

Here is an example of using reload hooks:

;; first notify figwheel that this ns has callbacks defined in it

(ns ^:figwheel-hooks example.core)

(defn ^:before-load my-before-reload-callback []

(println "BEFORE reload!!!"))

(defn ^:after-load my-after-reload-callback []

(println "AFTER reload!!!"))

Re-rendering UI after saving a file

Most ClojureScript UI libraries like Reagent, Rum, Re-frame or Om only render when some managed state changes. Most of the time, that state is defined using defonce, so while Figwheel will compile and install new versions of the various components, the DOM will stay the same so the UI library doesn’t have any reason to re-render. This is where the aforementioned reload hooks are useful:

First, don’t forget to add the ^:figwheel-hooks annotation to the namespace:

(ns ^:figwheel-hooks example.core)

Then add an ^:after-load marked function that will render the UI. In most cases you can reuse the “mount” function that rendered the UI for the first time. For example, to get Reagent to re-render you’d write:

;; this is what you call for the first mount

(defn mount []

(r/render [my-main-component]

(js/document.getElementById "app")))

;; and this is what figwheel calls after each save

(defn ^:after-load re-render []

(mount))

;; this only gets called once

(defonce start-up (do (mount) true))

Reloading Clojure code

If Clojure code is on a watched path Figwheel will reload it when it changes. It does this because ClojureScript macros are defined in Clojure not ClojureScript and if we want to pick up changes to our macros as we save them, we need to reload the changed Clojure code and then recompile/reload the ClojureScript files that depend on it.

Reloading changed Clojure code can also be helpful while you work on Clojure files that don’t have macros because Figwheel will report any load time syntax errors in the heads-up display, allowing you to catch errors sooner.

You may want to disable the reloading of Clojure files if your Clojure code has load time side-effects that you don’t want to manage. Another good reason to do this is when reloading Clojure code is causing a mostly ClojureScript application to recompile/reload and thus slowing down your workflow.

Use the :reload-clj-files option to disable the reloading of watched Clojure code.

Setting :reload-clj-files to false will stop .clj files and .cljc files from being reloaded.

If you only want to reload .cljc files set :reload-clj-files to the vector [:cljc]

If you only want to reload .clj files set :reload-clj-files to the vector [:clj]

Figwheel does have limited support for recompiling ClojureScript files which depend on Clojure files. It will only recompile and reload ClojureScript files which are direct dependents of a changed Clojure file.

Disabling hot reloading

There are plenty of situations where you will want to disable ClojureScript hot reloading and go back to reloading the browser after you make changes. This can be done with the :hot-reload-cljs option. By setting :hot-reload-cljs to false you will stop files from being reloaded when you change them.

Setting :hot-reload-cljs to false does not stop files from being recompiled on change and will still allow you to get feedback from the compile process in the heads-up display.

Disable reloading dependent namespaces

Figwheel by default finds all the files that depend on a changed file and reloads them in correct dependency order after reloading the changed file itself.

If you are working in a situation where you don’t want these dependent files reloaded set the :reload-dependents option to false

Disable recompiling of dependent namespaces

So far we have been talking about Figwheel options but there is a ClojureScript compiler option that you should be aware of in how it affects recompiles.

By default the ClojureScript compiler recompiles all the dependents along with a changed file. Depending on the size of your dependency tree and the compile time of all the dependent files this can significantly slow the compile time after each file save.

You can disable the recompiling of dependent namespaces by setting :recompile-dependents in your compile options to false.

As we have been talking about Figwheel options so far in on this page, I want to clarify that this is a ClojureScript compile option and you would place it in your [build-name].cljs.edn file like so:

{:main hello-world.core

:recompile-dependents false}

The :recompile-dependents CLJS option is completely independent of the :reload-dependents Figwheel option. It can still make sense to reload your dependent files even if they are not recompiled. Load time side effects are the reason. Reloading dependents is very fast compared to recompiling dependents. I recommend that you continue to reload dependent files even if you stop recompiling dependents.

Slowing reloads down

Depending on your environment Figwheel may reload files too quickly. If it does reload too soon it may be reloading the old file before it has changed. This is not an ideal experience.

For example you may be working with a Jekyll site where Jekyll copies all the assets of the site to a _site directory and serves the site from there. Jekyll currently doesn’t copy the files fast enough to beat Figwheel’s reload time.

You can slow down this reload time with the :wait-time-ms option. It defaults to 50 milliseconds.

You can make each reload wait a second after each compile by setting :wait-time-ms to 1000.

Reloads are broadcast

There can be any number of clients (browser tabs) with valid connections to the Figwheel websocket server. For sanity the Figwheel REPL only communicates with one of these clients. However, reloads are broadcast to all clients that are connected to the current build-id.

The reason it is done this way is to facilitate development across multiple clients types. I.E. You want to work on your web app and view the live reloaded changes on your phone, tablet and laptop at the same time.

If you want to disable this you can set the :broadcast-reloads option to false.

Figwheel is careful about which clients are able to receive reload and compile time messages. When you start Figwheel it assigns itself a unique identifier. When a client connects to the Figwheel websocket it can supply this process-id along with the build-id. Figwheel only sends messages to clients with the correct identifiers. This prevents stale clients and clients with other build-ids from getting the wrong reload messages and REPL evals.

Embed a Ring Handler

You can quickly get started creating a back-end for your application by embedding a :ring-handler in the Figwheel server.

The Figwheel server is primarily needed to provide a connection for REPL communication. However, it is a server so it’s handy to allow developers to leverage this server so they can concentrate on the problems they are actually trying to solve.

Figwheel allows you to specify the name of a function in your Figwheel configuration options in figwheel-main.edn or in the metadata of your build file under the key :ring-handler.

A quick example.

In src/hello_world/app_server.clj

(ns hello-world.app-server)

(defn handler [req]

{:status 404

:headers {"Content-Type" "text/html"}

:body "Yep the server failed to find it."})

And in your figwheel-main.edn file add a :ring-handler key:

{...

:ring-handler hello-world.app-server/handler

...}

Now when you start your figwheel.main build when you try to access an endpoint that doesn’t exist you will see the Yep the server failed to find it. message.

You can learn more about Ring by exploring the Wiki and API. This wiki document on Ring concepts provides a good summary of how Ring works.

The last handler

The Figwheel server has a chain of middleware that helps it do its job, this chain of middleware is composed on top of the popular ring-defaults middleware stack.

The function you specify in :ring-handler will be the last handler in the Figwheel middleware chain. This means it will only be called if the request isn’t handled by other middleware.

This can cause confusion if you have an index.html file present and you are trying to respond to a request for the root path / in your handler. In this case the Figwheel middleware will handle the root path returning the index.html file before the request has a chance to make it to your handler.

Returning index.html at specific routes for an SPA

If you are doing pushState history based routing, which is common in Single Page Applications, you will probably want to return the index.html for a given set of routes.

Here’s an example Ring handler that does this.

In src/hello_world/app_server.clj:

(ns hello-world.app-server

(:require

[ring.util.response :refer [resource-response content-type not-found]]))

;; the routes that we want to be resolved to index.html

(def route-set #{"/" "/contact" "/menu" "/about"})

(defn handler [req]

(or

(when (route-set (:uri req))

(some-> (resource-response "index.html" {:root "public"})

(content-type "text/html; charset=utf-8")))

(not-found "Not found")))

A more advanced Ring Handler

A quick example of a advanced Ring handler that uses higher level Clojure web libraries.

You will need to add compojure and hiccup to your dependencies for the following :ring-handler to work.

(ns hello-world.app-server

(:require

[compojure.core :refer [defroutes GET]]

[compojure.route :as route]

[hiccup.page :refer [html5 include-js include-css]]))

(defn index-html []

(html5

[:head

[:meta {:charset "UTF-8"}]

[:meta {:name "viewport"

:content "width=device-width, initial-scale=1"}]

(include-css "/css/style.css")]

[:body

[:div {:id "app"}]

(include-js "/cljs-out/dev-main.js")]))

(defroutes handler

(GET "/" [] (index-html))

(route/not-found "<h1>Page not found</h1>"))

The defroutes macro above creates a ring handler function that will work as a :ring-handler parameter.

ring-defaults

It’s also important to remember that the Figwheel’s server already uses ring-defaults middleware.

When you create your own server, this middleware will not be present and you will have to supply it by wrapping your handler via (ring.middleware.defaults/wrap-defaults your-app-handler ring.middleware.defaults/site-defaults)

Your own server

Injecting a :ring-handler into the Jetty server that Figwheel uses primarily for REPL communication is a convenient way to start building server side functionality. However it is easy to outgrow this built-in server. This guide will detail how to setup a separate HTTP server for your application and still benefit from working with Figwheel.

It normally doesn’t take long to outgrow using :ring-handler to embed server side functionality in Figwheel. If there is a server side component to your application, it is inevitable that you will need to setup a server to serve your app when you deploy it. So in many cases you can’t get by without it.

Requirements for working with Figwheel

Normally there is very little that needs to be done so that you can continue to develop using Figwheel while serving your application from a different server.

You will need to run Figwheel as well as your application server. It can be helpful to think of Figwheel as a separate tooling process that has its own Websocket server to handle REPL communication.

Figwheel’s tooling process is not required to be in a separate JVM process from your application server but it can be. Your application server can be written in Ruby, Node, Python, C#, Clojure, and Figwheel will still work.

There are only two things you need to ensure to make Figwheel work along side your own application server.

The separate server should:

1. serve an HTML host page that loads your CLJS app correctly

2. serve the compiled ClojureScript artifacts of your application

In most of the cases that is all that is needed to use your own server.

No need to proxy

Many folks worry about Cross Origin problems and believe that they need to proxy an endpoint on their app server to the Figwheel server. This is not needed at all. Figwheel has been setup to handle CORS problems.

How this works

The reason why Figwheel works as a Cross Origin Websocket is the same reason that services like Firebase and Pusher work as Cross Origin services.

In this case, Figwheel is responsible for compiling your ClojureScript into JavaScript artifacts that get loaded by the browser. When it compiles these artifacts it inserts a URL that points to the Figwheel server to establish a Websocket connection. So when your app server serves these compiled artifacts the Figwheel client will call home to the Figwheel server to establish a REPL connection. Since the Figwheel server allows CORS requests this works just fine.

Figwheel was designed to work this way to support the inevitable need to use your own server.

Connecting Remotely

When you are not developing locally and connecting to localhost you will need to set the :connect-url config option so that Figwheel knows which server host to connect back to.

This is something that is required whether you use your own server or not.

Example of using your own server

We will start from the base hello-world example app.

We will create a Ring server and launch it via a script. Create a file at scripts/server.clj with the following content:

(require '[ring.adapter.jetty :refer [run-jetty]])

(require '[ring.middleware.defaults :refer [wrap-defaults site-defaults]])

(require '[ring.util.response :refer [resource-response content-type]])

(defn handler [req]

(or

(when (= "/" (:uri req))

(some-> (resource-response "index.html" {:root "public"})

(content-type "text/html; charset=utf-8")))

{:status 404

:headers {"Content-Type" "text/html"}

:body "Not found"}))

(run-jetty

(wrap-defaults handler site-defaults)

{:port 4000

:join? false})

The above Clojure file defines a Ring handler and runs the server when it is loaded.

If you haven’t defined an index.html file yet, create a file like the following at resources/public/index.html:

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1">

</head>

<body>

<div id="app"></div>

<!-- include your ClojureScript at the bottom of body like this -->

<script src="/cljs-out/dev-main.js" type="text/javascript"></script>

</body>

</html>

If you don’t already have a host page for your application and you are not sure how to create one, please see the documentation on this subject.

Of course we can generate and serve the above HTML directly from the server, but I’m choosing this to keep things simple and consistent with the other guides.

This is all you need to start your own server while you are running a Figwheel tooling process.

You can start the server along with your Figwheel build with the following command:

$ clojure -i scripts/server.clj -m figwheel.main -b dev -r

You can also add these CLI options to an alias in your deps.edn file to make things more concise on the command line.

Using NPM

NPM is the defacto package repository for the JavaScript ecosystem. It holds a tremendous amount of valuable functionality. This guide will show you how to include and consume NPM packages in your ClojureScript codebase.

Npm usage in Figwheel has changed significantly. For reference purposes the original version of this document can be found here

These instructions require org.clojure/clojurescript >= 1.10.773 and com.bhauman/figwheel-main >= 0.2.6.

Quick Reference

Read the rest of this document and come back here for a quick reference.

Set the :target compiler option to :bundle. This will cause the compiler to emit an output file file that can be bundled a JavaScript bundler like webpack.

Optionally set the :bundle-cmd compiler option to

{:none ["npx" "webpack" "--mode=development" "--entry" :output-to

"--output-path" :final-output-dir

"--output-filename" :final-output-filename]}

to ensure the output file is bundled after a compile. Figwheel will fill in :output-to, :final-output-dir and :final-output-filename.

Your host page will need to load the final bundled asset.

You will want to make sure that the :output-to file is in the :output-dir directory so the bundler can resolve the assets it requires.

As a rule, if you are using NPM libraries along with the :bundle target, don’t use :aot-cache true.

If your are porting a project over to use the :bundle target and NPM libs, use the :clean-outputs Figwheel option.

Relevant Figwheel Options

- :auto-bundle

- :bundle-freq

- :final-output-to

What?

NPM is a package repository for the JavaScript ecosystem. Almost all available JavaScript libraries are packaged, stored, and retrieved via NPM.

We want to use these libraries inside our ClojureScript codebase, but there is some friction because ClojureScript embraced the Google Closure Compiler and its method of declaring libraries, which is quite different than NPM’s.

However, with recent changes in the ClojureScript compiler (along with changes in Figwheel) it is now becoming much more straightforward to include NPM modules in your codebase.

The Overview

We are going to utilize a JavaScript bundler like Webpack to bundle up the output of our ClojureScript compiled code to produce a final bundled output file which will contain all the NPM libraries we have required in our ClojureScript code.

So the code is going to go through two steps:

1. compilation by the ClojureScript compiler to an intermediate file

2. bundled together with its NPM dependencies to a final output file that we will load into the browser

It’s important to remember that the under the :bundle target the output of the ClojureScript compiler is not loadable by the browser it has to be bundled first.

During development under optimizations :none the bundled output file will only contain the NPM libraries and small amount of ClojureScript boot code that will in turn load our compiled ClojureScript code. So there exists a bundle file and then a bunch of individually compiled ClojureScript namespaces that the bundled boot script will need to load.

It’s important to remember that during development the bundled file still depends on the other ClojureScript output files and will not work on its own.

When we deploy to production we will be compiling a single ClojureScript artifact using :simple or :advanced optimizations. This single ClojureScript artifact will then be bundled along with its NPM dependencies into a single bundle that can be deployed on its own.

Getting started with NPM libraries into your project

We are going to assume you are starting from the base example.

I’m going to use npm for this example but if you prefer yarn go ahead and use that. It doesn’t really matter for this.

There are four steps that we are going to follow to add some libraries to our hello-world.core project.

1. Initialize NPM in our project by adding a package.json file

2. Install Webpack to bundle the needed dependencies into a single JS file.

3. Add the needed libraries as NPM dependencies.

4. Configure our ClojureScript build to use the bundle generated by Webpack.

Initialize NPM

We will need to initialize npm for our hello-world project.

One way to do this is to use the npm init command. You can of course just create the file from scratch but npm init is faster.

Make sure you are in the root directory of the project and execute:

$ npm init -y

This will create a package.json file in the root directory of your project along side your dev.cljs.edn file.

Install Webpack

We will need webpack to bundle our application along with our moment dependency to make it available to our ClojureScript code.

Install webpack and webpack-cli:

$ npm add --save-dev webpack webpack-cli

This add webpack and webpack-cli as development dependencies in your package.json. It will also download them to a node_modules directory in your project.

Add the needed libraries

Let’s say we want to use the moment library in our application.

We’ll use npm to add a moment dependency to our package.json file in the usual manner:

$ npm add moment

This should download and install the moment library along with its dependencies if it has any.

Configure our ClojureScript build

Everything we have done up until this point is very similar to what we would normally do if we were using NPM and Webpack for a simple JavaScript project.

In the dev.cljs.edn file we’ll add the following config:

{:main hello-world.core

:target :bundle

:bundle-cmd {:none ["npx" "webpack" "--mode=development" "--entry" :output-to

"--output-path" :final-output-dir

"--output-filename" :final-output-filename]}}

Understanding the above configuration is important, so I’m going to explain each part.

The :target compiler option is set to :bundle to instruct the ClojureScript compiler to produce an output file that can be bundled by a JavaScript bundler like Webpack.

The :bundle-cmd compiler option is set to

{:none ["npx" "webpack" "--mode=development" "--entry" :output-to

"--output-path" :final-output-dir

"--output-filename" :final-output-filename]}}

This provides the ClojureScript compiler with a command that it can use to bundle the intermediate output of the compiler into its final bundled form.

Figwheel adds some additional functionality to the :bundle-cmd. It interpolates the keywords :output-to, :final-ouitput-dir and :final-output-filename into the command. In this case the :output-to is going to be replaced by the default :output-to path ./target/public/cljs-out/dev/main.js. The :final-output-dir is replaced by the path part of :output-to which is ./target/public/cljs-out/dev/. The :final-output-filename defaults to the filename of of :output-to with a _bundle added before the extension or main_bundle.js.

Or stated more simply in this case:

- :output-to is replaced with ./target/public/cljs-out/dev/main.js

- :final-output-dir is replaced with ./target/public/cljs-out/dev

- :final-output-filename is replaced with main_bundle.js

If you supply your own :output-to cljs compiler option, it will be used instead of the default.

After the ClojureScript compiler is finished compiling it will call the :bundle-cmd to bundle up the output.

In this case it will call:

$ npx webpack --mode=development --entry ./target/public/cljs-out/dev/main.js --output-path ./target/public/cljs-out/dev --output-filename main_bundle.js

This will bundle up the main.js file and pull in the moment dependency along with it.

Now let’s modify our source file to use moment so that we can make sure that things are working.

Edit the src/hello_world/core.cljs to look like:

(ns hello-world.core

(:require [moment]))

(js/console.log moment)

(println (str "Hello there it's "

(.format (moment) "dddd")))

Run the build

OK now that we’ve setup everything up we can run the build.

$ clojure -m figwheel.main -b dev -r

The browser and REPL should launch as usual:

Repl

Except now you can see one additional line in the output which notifies us that the bundle command was called.

And if you look at the dev tools console of the browser window that just popped open you should see similar output printed as below verifying that we were able to use moment successfully.

Repl

Well we successfully used an NPM package from our ClojureScript code. Now you can npm add other JavaScript NPM packages and use them from ClojureScript

Simplifying with :auto-bundle

If you just need to get up and running quickly the :auto-bundle Figwheel option will set up all the default options that we configured above.

So this configuration is equivalent to the above configuration:

^{:auto-bundle :webpack}

{:main hello-world.core}

When enabled :auto-bundle will set :target to :bundle.

When choosing :webpack it will set :bundle-cmd to:

{:none ["npx" "webpack" "--mode=development" "--entry ":output-to

"--output-path" :final-output-dir

"--output-filename" :final-output-filename]

:default ["npx" "webpack" "--mode=production" "--entry" :output-to

"--output-path" :final-output-dir

"--output-filename" :final-output-filename]}

and when choosing :parcel it will set :bundle-cmd to:

{:none ["npx" "parcel" "build" :output-to

"--out-dir" :final-output-dir

"--out-file" :final-output-filename

"--no-minify"]

:default ["npx" "parcel" "build" :output-to

"--out-dir" :final-output-dir

"--out-file" :final-output-filename]}

These :bundle-cmd configurations are merged with any :bundle-cmd configurations in your .cljs.edn file so that you can override either the default ones.

:auto-bundle also adds an important config to :closure-defines in your compiler options when not using :optimizations :none.

It adds:

{:closure-defines {...

cljs.core/*global "window"}}

Configuration Tips

Don’t specify :output-to

Don’t specify the :output-to compiler option. Figwheel will take care of this for you and normally it’s just an intermediate file. You are probably much more interested in the output of the bundler.

If you must specify where the output of the compiler is sent use :output-dir and Figwheel will specify an [:output-dir]/main.js file for you.

It’s important to remember that all the output-files still have to be accessible from the browser (i.e. served by the web-server) when developing, as these files are not included in the output bundle.

Using :final-output-to

:final-output-to is a Figwheel option, NOT a ClojureScript compiler option. Place it in the metadata section of your build config.

If you don’t want to use the default location and need to specify a specific location for your build’s final bundled asset its probably best to supply a :final-output-to config option.

You don’t need to specify :final-output-to if you are not using the built in Figwheel REPL host page, Extra-Mains, or Auto-testing.

However, its helpful for Figwheel to know where the final bundled asset of your build is located. If the location is known then Figwheel can provide you a REPL without having to create and index.html page. Figwheel can also munge the name of the :final-output-to to create bundles for Extra-Mains and Auto-testing. Both :final-output-dir and :final-output-filename are derived from :final-output-to.

Using :bundle-cmd

The :bundle-cmd compiler option is not required. It specifies what to do with the a compiled ClojureScript output file.

You can skip using :bundle-cmd entirely and this would require that you run the bundler manually AFTER your files have been initially compiled.

This can be unpleasant because it’s rather nice to run a Figwheel build command and have the browser window pop open along with a running REPL.

When you omit the :bundle-cmd you will need to launch figwheel first. The browser will pop open and display your custom index.html application page but it will be broken because the bundled JavaScript isn’t available yet. Next, you will need to run your bundler and reload the browser. At this point, everything should be up and running fine. However, this is not the best experience and why using the :bundle-cmd is helpful.

Another reason the :bundle-cmd is helpful is to provide Figwheel a template of a command that can create bundles for your build. If you create a :bundle-cmd with the keywords :output-to and :final-output-to, Figwheel will be able to reuse that command with different parameters to create slightly different bundles for things like Extra-Mains and Auto-testing.

When filling in the :bundle-cmd template Figwheel also replaces the :final-output-dir and :final-output-filename template keywords (this helps with commands that require them). It obtains these values from the :final-output-to value.

Figwheel by default only runs the :bundle-cmd after the first compile, this avoids the incurring the latency of bundling on every single file change. This can significantly slow down hot-reloading depending on your set up. You can change this with the :bundle-freq Figwheel option.

Using :bundle-freq

The :bundle-freq Figwheel option controls how often a the :bundle-cmd is called.

It has three settings :once, :always, and :smart:

- :once - bundles only once after the initial build compile. This helpful if you want to launch a webpack watch command on the command line after you start your build.

- :always - bundles after every ClojureScript compile. This is fine when you are starting out and bundle times are short. This however increases the total compile time and can slow down your hot-reload time.

- :smart - re-bundles only when your :output-to or npm_deps.js changes thus trying to only bundle when you have included a new node dependency in your CLJS code. This will cover most cases where you need to rebundle but is no where near a complete as launching a webpack watcher.

The default is :once which is the most conservative setting.

I think that using :smart is probably the best choice if you are not using a webpack watcher.

Keep in mind that a hot-reload will NOT pick up changes from a re-bundling. You have to refresh the browser to get those changes. So if you add a node dependency and it gets re-bundled into your JS bundle you won’t see it until you reload the browser.

Using a webpack.config.js

It’s important to note that you can still use a webpack.config.js to specify various configurations for your bundle. The CLI options simply override the configuration in the webpack.config.js file. If an input file is specified on the command line it will simply override any entry supplied in the Webpack config. If an -o is supplied it will any output supplied in the Webpack config as well.

This frees you to configure your Webpack bundle as you need.

How NPM support works

The motivation is that we want to have a JavaScript bundler process our ClojureScript output so that it can bring in and bundle all of our NPM dependencies. So we have two output files the output file from ClojureScript and the final bundled output from a JavaScript bundler like Webpack or Parcel.

When you use the :bundle target, the ClojureScript compiler does just this. We’re going to examine how this is accomplished.

Let’s look at an example default output file when you don’t supply a :target option and thus get the default browser target output.

window.CLOSURE_UNCOMPILED_DEFINES = {"figwheel.repl.connect_url":"ws:\/\/localhost:9500\/figwheel-connect?fwprocess=29ee5f&fwbuild=devy"};

window.CLOSURE_NO_DEPS = true;

if(typeof goog == "undefined") document.write('<script src="/cljs-out/devy/goog/base.js"></script>');

document.write('<script src="/cljs-out/devy/goog/deps.js"></script>');

document.write('<script src="/cljs-out/devy/cljs_deps.js"></script>');

document.write('<script>if (typeof goog == "undefined") console.warn("ClojureScript could not load :main, did you forget to specify :asset-path?");</script>');

document.write('<script>goog.require("figwheel.core");</script>');

document.write('<script>goog.require("figwheel.main");</script>');

document.write('<script>goog.require("figwheel.repl.preload");</script>');

document.write('<script>goog.require("devtools.preload");</script>');

document.write('<script>goog.require("figwheel.main.system_exit");</script>');

document.write('<script>goog.require("process.env");</script>');

document.write('<script>goog.require("hello_world.core");</script>');

In the code above you can see the standard ClojureScript bootstrap code that loads your application into the browser (along with the figwheel preloads).

When we are working with NPM, we want the ClojureScript compiler to resolve all of our references to NPM libraries in our ns declarations and then add them into this file somehow so we can call webpack on it to import them.

For example when we have a namespace like above:

(ns hello-world.core

(:require [moment]))

We want ClojureScript to make a note of that and then emit something that can bundled and that will process and include the moment NPM library.

With this in mind let’s look at the main output-to file when we use the :bundle target.

import {npmDeps} from "./npm_deps.js";

window.CLOSURE_UNCOMPILED_DEFINES = {"figwheel.repl.connect_url":"ws:\/\/localhost:9500\/figwheel-connect?fwprocess=87057f&fwbuild=dev","cljs.core._STAR_target_STAR_":"bundle"};

window.CLOSURE_NO_DEPS = true;

if(typeof goog == "undefined") document.write('<script src="/cljs-out/dev/goog/base.js"></script>');

document.write('<script src="/cljs-out/dev/goog/deps.js"></script>');

document.write('<script src="/cljs-out/dev/cljs_deps.js"></script>');

document.write('<script>if (typeof goog == "undefined") console.warn("ClojureScript could not load :main, did you forget to specify :asset-path?");</script>');

document.write('<script>goog.require("figwheel.core");</script>');

document.write('<script>goog.require("figwheel.main");</script>');

document.write('<script>goog.require("figwheel.repl.preload");</script>');

document.write('<script>goog.require("devtools.preload");</script>');

document.write('<script>goog.require("figwheel.main.system_exit");</script>');

document.write('<script>goog.require("process.env");</script>');

document.write('<script>goog.require("hello_world.core");</script>');

window.require = function(lib) {

return npmDeps[lib];

}

The first line is new. Let’s look at it:

import {npmDeps} from "./npm_deps.js";

OK so we are importing an npm_deps.js file. Let’s look at its contents as well:

module.exports = {

npmDeps: {

"moment": require('moment')  }

};

So, the npm_deps.js file is a JavaScript bundler ready file that was generated by the CLJS compiler. The compiler looked at all of our ns declarations and resolved the NPM libraries by looking at the node_modules directory.

Now when we run a bundler on our main.js output file the bundler will resolve the npm_deps.js file and then will resolve and include all NPM libs we are using.

It’s important to note that the main.js output file is importing the ./npm_deps.js file relatively so both these files need to be in the same directory. ClojureScript will let you break this easily if you supply an :output-to option that isn’t in the output directory.

Now let’s look at the last lines of our main.js output file.

window.require = function(lib) {

return npmDeps[lib];

}

These lines shim require so that it requiring a NPM library in ClojureScript works correctly.

Further is we look at the output of compiling the hello_world/core.cljs file you will see this:

// Compiled by ClojureScript 1.10.775 {:target :nodejs}

goog.provide('hello_world.core');

goog.require('cljs.core');

hello_world.core.node$module$moment = require('moment');

console.log(hello_world.core.node$module$moment);

cljs.core.println.call(null,["Hello there it's ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(hello_world.core.node$module$moment.call(null).format("dddd"))].join(''));

//# sourceMappingURL=core.js.map

The interesting line is where a scoped local reference of the moment is created.

hello_world.core.node$module$moment = require('moment');

The :bundle target uses the current functionality of the :nodejs target which emits these requires. And while this works fine for Node we need to shim require to support it in the browser.

Keep in mind this is all only true when we are developing in :optimizations :none. When we want to create a single compiled artifact to deploy for production, using :simple or :advanced optimization mode ClojureScript will put together a single output file with the requires in it. Then the bundler will resolve and replace these top requires as it normally does.

Problems with cached compiled assets

Looking at the previous example if we hadn’t been using the :bundle target had been using the Cljsjs cljsjs/moment library instead of the NPM based moment library the output of compiling hello_world/core.cljs would have instead been the following:

// Compiled by ClojureScript 1.10.775 {}

goog.provide('hello_world.core');

goog.require('cljs.core');

goog.require('moment');

hello_world.core.global$module$moment = goog.global["moment"];

console.log(hello_world.core.global$module$moment);

cljs.core.println.call(null,["Hello there it's ",cljs.core.str.cljs$core$IFn$_invoke$arity$1(hello_world.core.global$module$moment.call(null).format("dddd"))].join(''));

//# sourceMappingURL=core.js.map

The major difference is one the line:

hello_world.core.global$module$moment = goog.global["moment"];

which when compiled with an NPM moment library looks like this:

hello_world.core.node$module$moment = require('moment');

When transitioning from Cljsjs libraries to NPM based libraries the way that moment is required changes. This can become a problem while we are actively converting our code to use NPM libraries.

The caching of compiled code can wreak havoc on this process. The AOT caching that happens when you set :aot-cache true caches compiled libraries globally.

If you compile a version of reagent the depends on Cljsjs based react and it gets cached to the global AOT-cache, then the compiled reagent lib in the cache will have been specialized to use a Cljsjs based react.

If you then create a new project that has a reagent dependency that tries to meet its react dependency from NPM it will fail if you are using :aot-cache true. It will fail because you aren’t providing the library via Cljsjs but the cached version of reagent is expecting it.

This same caching problem also occurs with code that’s been compiled to your local target directory. A compiled reagent library could be specialized to either NPM or Cljsjs.

This becomes even more confusing if you are accidentally providing both the Cljsjs lib and the NPM lib. It’s possible that your code will appear to work but may potentially be fetching the JavaScript library from the wrong source.

Bottom line

As a rule, if you are using NPM libraries along with the :bundle target don’t use :aot-cache true. The default value of :aot-cache is false in Figwheel. However the default value of :aot-cache is true when you use cljs.main.

Also, if your are porting a project over to use the :bundle target and NPM libs, use the :clean-outputs Figwheel option. The clean outputs option will delete all of your compiled artifacts everytime you start a Figwheel build.

If you run into a situation where your Cljsjs lib or your NPM lib doesn’t resolve you should look at the compiled output file and verify that the file is being required correctly as you may have a caching problem.

Compiling for production

When you want to compile your final output file you will use an :optimizations mode other than :none.

The thing to remember is that the ClojureScript compiler is going to outputing a single JavaScript file and then the bundler is going to resolve all of the JavaScript requires present in that file in order to ultimately create a JavaScript bundle that you can deploy.

This is different process to what happens during development (under :optimizations :none). During developement bundle only resolves the requires in the :output-to file and the npm_deps.js file.

This is an important difference because if there is an errant require in your code that is called conditionally it won’t cause a problem during development because require is shimmed, however the bundle process is resolving requires statically. So if there is a (js/require ...) for a library that isn’t in node_modules or is only available in the Nodejs runtime (but not in the browser) your bundler will throw an error about a missing library.

Keeping this in mind we should remember to not dynamically require NPM libraries that are only available on the backend, or are not currently stored in node_modules.

When compiling for production we should also, make sure that you aren’t including Cljsjs libraries that are being provided for by NPM libraries. We don’t want to be including libraries twice.

For example, we need to exclude react dependencies from reagent to prevent bundling to copies of react into your final application.

An example of excluding react from reagent:

{:deps {org.clojure/clojurescript {:mvn/version "1.10.773"}

reagent {:mvn/version "0.10.0" :exclusions [cljsjs/react cljsjs/react-dom]}}}

We also need to make sure we include cljs.core/*global* "window" in our :closure-defines (I.e.:closure-defines {cljs.core/*global* "window" ...}). This is required when we aren’t compiling in :optimizations :none. Figwheel adds this automatically when using the :auto-bundle option.

Troubleshooting

Bad bundle command

The most likely thing that will happen is you will have a bad :bundle-cmd.

- Please check that the command that is logged is the command that you expect.

- comment out the :bundle-cmd option, run the figwheel build and then run the webpack command from your terminal/shell environment to see what errors are showing up

The relationship between the :output-to file and npm_deps.js

The :output-to file emitted by the :bundle target imports the npm_deps.js file. This can only work when the :output-to file is in the same directory as the npm_deps.js file. I.E. the :output-to file has to be in the :output-dir for the build.

{::comment} TODO

- advanced

- talk about the combination of bundling :once and the launching a watcher

- maybe discouraging the :output-to is a bad idea, perhaps just encourage the “_bundle” convention. {/:comment}

Advanced Compile

If you are going to deploy your compiled ClojureScript to the web you will want to use advanced compilation as it will shrink your compiled code significantly.

The Google Closure Compiler’s advanced compilation may be the best possible way to compress your compiled ClojureScript. It shrinks names globally, it in-lines code, it removes code that is not reachable and just does a fantastic job of compressing your code.

However, there is a downside. It needs to know the variable and function names that it shouldn’t compress. It can be very frustrating when you are finally getting ready to deploy your project and then discover your code has some subtle incompatibilities with advanced compilation.

The way this plays out is that your code will compile without failure or warnings and then it just won’t run. And you will see in the developers console that some randomly compressed function name like zB does not exist.

It is for this reason that you should get in the habit of compiling your project in advanced mode fairly often, at least once a day, to verify that things are working.

How to advanced compile

Given our hello-world.core example project we can advanced compile it very simply by executing the following shell command:

$ clj -m figwheel.main -O advanced -bo dev

The -bo is the short flag for --build-once and it takes a build name just like the --build flag. The -O flag overrides the :optimizations configuration option in the build options. It is important that the -O flag and its options is before the final -bo option.

The -O flag is a capital letter and overrides the :optimizations build option. Not to be confused with the -o flag which overrides the :output-to config option.

The above command should have advanced compiled your code one time. If you examine the target/public/cljs-out/dev-main.js file you will see the compressed output of your program.

Now something to notice here is that the :output-to path target/public/cljs-out/dev-main.js has not changed. This means that the HTML that is hosting this file can still be used to verify that it all works.

You can verify that your advanced compiled code is working two different ways.

The first is to just build it once and start the server like so:

$ clojure -m figwheel.main -O advanced -bo dev -s

or alternatively you can watch and compile the advanced build so that you can have it recompiled after you change it.

$ clojure -m figwheel.main -O advanced -b dev -s

The above method can be very helpful when diagnosing advanced compile problems.

Keep in mind the above command will not hot-load your changes and you will need to manually refresh the browser to see if your fixes have taken. Also, remember that advanced compilation is significantly slower than :optimizations :none compiles.

Troubleshooting

If and when you run into problems please make sure that you have read and understood these guides on the ClojureScript site:

- Externs guide is absolutely essential

- Dependencies guide is written with a focus on advanced compilation

- Advanced compilation guide provides some extra info

- Taming advanced compilation bugs A blog post summarizing methods for avoiding and fixing advanced compilation bugs

Extra Mains

This feature is only available in figwheel-main >= 0.1.8-SNAPSHOT

Extra mains allow you to create extra entry points to your code base. This is helpful for running things like tests, Devcards, and other top level applications in the same codebase.

Extra mains have the advantage of running in the build that declares them. This makes them much lighter and simpler than running background builds. It also means they all share the same broadcast updates when a file changes. In other words when a source file changes all of the extra main apps get the change message, but the namespace is only reloaded when it is in the app’s dependency tree.

The best part of the extra mains feature is that Figwheel provides default web endpoints and you can easily use them without having to create a host page.

Let’s look at an example of using an extra main, so that we have a better idea of what it is.

Simple testing example

I’m going to assume that you already have a project set up. I’m going to continue to use the hello-world.core project that I’ve been using so far in this documentation.

We are going to add some basic tests to our project.

Go ahead and add the following tests/hello-world/core_tests.cljs file to your project tree:

(ns hello-world.core-tests

(:require [cljs.test :refer-macros [deftest is run-tests]]))

(deftest should-not-pass

(is (= 1 20)))

(run-tests)

Now we need to make sure we add the tests directory to your classpath. For example in your deps.edn file:

{...

:paths ["resources" "target" "src" "tests"]

...}

We also need to add tests to your :watch-dirs figwheel option. For example in your dev.cljs.edn file:

^{:watch-dirs ["src" "tests"]}

{:main hello-world.core}

And now for the magic sauce you need to add an :extra-main-files entry in your Figwheel options. For example:

^{:watch-dirs ["src" "tests"]

:extra-main-files {:tests {:main hello-world.core-tests}}}

{:main hello-world.core}

Now when you build the dev build:

$ clojure -m figwheel.main -b dev -r

Everything will start like usual. But if you check your target directory you will have a new output file. In this example it would be target/public/cljs-out/dev-main-tests.js. This output file will work just like the target/public/cljs-out/dev-main.js output file except that it will run the code in the hello-world.core-tests namespace.

At this point you would normally need to create a host page for this new main file but you don’t need to do that because you can visit the /figwheel-extra-main/tests endpoint on your Figwheel server.

In the current example that would be localhost:9500/figwheel-extra-main/tests

If everything went well, when you visit that URL you should see something like this:

extra-mains

If you open the console you will see the test report from cljs.test.

As you can see you now have a different application that is being output from the dev build and it has its very own endpoint.

Why?

Well, if you are working on an application and you want to have the tests visible in another tab of your browser and you want them to re-run after all source code changes, this is an ideal way to do that.

You could do that with a background build but in that case you have two autobuilding processes running in parallel on the same codebase which is much more taxing on your CPU.

This is much lighter, especially if you start to have a lot of extra main entry points. For example, you could have all of the following running at the same time with virtually no extra cost:

- cljs-test-display

- devcards

- backend admin

- tests that run on node (to keep a part of your app node-compatible)

- node server

You don’t have to use all of the main entry points at the same time. But they are there, waiting to do your bidding.

Not only do extra mains lighten the compile load, but because they are all within a single build this also allows for extremely simple REPL focus switching.

Behavior

The application loaded by the extra main entry point will behave the same as the main in terms of its relationship to Figwheel. In other words, the extra main will connect to the Figwheel REPL and get reloads just like the main build.

This feature will only output the additional ClojureScript bootstrap file that you will require on your host page, it will not cause any additional files to be compiled.

You will need to make sure that you have added all the needed source directories to your :watch-dirs and to your classpath.

If you add an extra main entry point but don’t supply its source files to the compiler this feature won’t work.

Default endpoint

You will be able to find the default endpoint for your extra main at /figwheel-extra-main/[extra-main-id]. I.E for :tests the endpoint would be /figwheel-extra-main/tests.

There is a div on the default host page where you can mount your application. The app div on the page does not have an id app but rather app-[extra-main-id] (for :tests that would be app-tests). This is so that you can conditionally mount applications by testing for the presence of a certain div id. I.E. you can belay starting your main application if the app div isn’t there. This would allow you to require the namespace that mounts your main application and test the functions in it even if it has top level code that tries to display itself on the page.

Usage

:extra-main-files will only work under :optimizations level :none.

As you can see from the above example when you configure :extra-main-files you pass it a map of name keys mapped to ClojureScript options maps.

:extra-main-files {:tests    {:main hello-world.core-tests}

:devcards {:main hello-world.devcards}}

These maps are merged with the options map that Figwheel has already modified and may have injected various :preloads and :clojure-defines. This of course creates a problem if you want to add more :preloads or :clojure-defines. :extra-main-files handles this by allowing you to add more :preloads by supplying an :extra-preloads key. You can do the same with any ClojureScript key that has a collection value.

:extra-main-files {:tests    {:main hello-world.core-tests

:extra-preloads [hello-world.dev-methods]}}

The options that you supply do not affect how your code is compiled, they only affect how the final main entry file is generated. The following options are the only ones that affect how this file is generated.

- :main

- :output-to

- :target

- :asset-path

- :closure-defines

- :preloads

You can supply an explicit :output-to path but Figwheel will supply one based on the current :output-to. It will add an "-[extra-main-id]" to the end of the file name before the extension. For example, if there is already an :output-to file named target/public/cljs-out/dev-main.js, Figwheel will rename that to target/public/cljs-out/dev-main-tests.js if the name of the extra mains config is :tests.

Simple REPL focus switching

A major question when you have more than one environment is “How do I get a REPL into my tests?”

The Figwheel REPL allows you to switch focus between environments (think open tabs) as long as they are in the same build.

You can use the figwheel.repl/conns and figwheel.repl/focus macros from the CLJS REPL prompt to do this.

There is a very simple technique for switching the focus of your REPL though. Reload the browser tab if you want the focus of your REPL to switch to that environment. The Figwheel REPL will focus on the last environment to connect to the Websocket. So to switch focus just reload the browser tab where you want to focus.

You verify this with a (js/console.log "focus is here"). You will see that REPL focus follows that last tab to open within a build.

Background Builds

Background builds allow you to run more than one Figwheel hot reloading build process at a time.

Background builds can be started with the --background-build (-bb) Figwheel Main CLI option.

For instance, let’s say you want to run a build that compiles your main application and a build that compiles an admin backend application. The build files for both are respectively named app.cljs.edn and admin.cljs.edn. You can start both builds with the following command:

$ clojure -m figwheel.main -bb admin -b app -r

The above command will run both the admin and app builds and allow you to work on both of them at the same time.

Difference from foreground build

Background builds are not full fledged citizens like the build you supply to the --build option.

In the above example we’ll call the app build the foreground build. The foreground build is different because its configuration is what gets applied to the Figwheel server when it starts. Practically speaking, if both builds have defined their own :ring-handler, only the app build’s ring-handler will be installed. The same goes for all server related options like :ring-server-options etc.

Background builds do not affect the Figwheel server configuration.

Background builds only launch a file watcher/builder and maintain a REPL connection to the client environment to allow Figwheel to send reload and error messages. REPL connections are maintained for each build, but only the foreground build gets an interactive REPL on terminal.

Background builds should always be builds which initiate a running watcher/build process.

Background build requirements

Background builds usually have to define a :watch-dirs Figwheel configuration option.

Background builds also need to define their own host page as both applications need their own client environment.

Background build for Testing

Background builds are an ideal solution if you want to run your test suite in the Browser and you want it to react and run every time you change a file.

Build inputs

Build inputs to the ClojureScript compiler determine what ClojureScript source files get compiled. Figwheel follows a simple heuristic for determining the build inputs based on your configuration.

You can determine the build inputs explicitly with the :build-inputs config option.

Understanding how build inputs change the ClojureScript compiler’s behavior can be very helpful depending on your compilation goals.

Development

When you supply a directory like src to the compiler, it will find all the ClojureScript files (.cljs and .cljc) and use them directly as the initial sources for compilation. All of these initial sources will be compiled regardless of their relationship to the :main. When a namespaces is required but not present in these initial sources, the compiler will then attempt to find it on the classpath.

If you provide a single file to the compiler like src/example/core.cljs, only that file will be in the initial sources and all of its dependencies will be resolved via the classpath.

This behavior has various trade-offs that need to be understood in the context of compiling for development and production.

One could not be blamed for coming to the conclusion that it is always better to supply the compiler with a single build input that represents the root namespace of your application. This way the final code base will only include code that is needed by the application.

It’s a temptingly simple heuristic that works for many cases. But let me make an argument why it is better to pass a directory or set of directories to the compiler during development when you are using a hot reloading workflow.

The biggest reason to have all you files under watch while you are developing them is so that you receive feedback on files that are not currently included in the application. I.E you can work and prototype them and Figwheel will give you feedback on how well they are compiling. This feedback is not to be underestimated as it parallels the feedback that every decent IDE gives you about your code’s syntax.

Another major reason to pass your source directories as your build inputs is to support the extra mains feature. If you are simply compiling from a single root namespace, files that you need for your extra main entry point will possibly not be compiled.

When you are developing with :optimizations level :none, any extra source files that are being worked on but are not required by the running application will not be loaded in the running application. The cost of supplying a directory of source files to the compiler will only be high if the unrequired source files have a long initial compile time. This is a very rare situation. Even if that situation does occur the cost of compiling is paid only once during the initial compile.

It is for the above reasons that both figwheel.main and cljs.main supply the watched directories to the compiler. They are being watched so that there is an expectation of feedback. When there is no feedback, one can mistakenly assume that the code one is writing has no syntax errors.

Production

Now when we leave development mode the priority changes. We normally do not want to include extra source files in a deployed artifact and we have no need for feedback on files that are not getting deployed. When we compile one big artifact with :optimizations level :whitespace we don’t want it to contain unneeded namespaces.

figwheel.main uses the file containing your :main namespace as the single build input when you are not in :optimizations level :none. This will ensure that the deployed bundle of JavaScript only contains code that is being depended on.

This is less of a problem when you use the :advanced level as it will perform dead code elimination from the final artifact.

Overriding build inputs

Now that you understand the trade-offs you can safely use the :build-inputs config option to override the default sources passed to the compiler by Figwheel.

Only sending the main namespace

This will probably be the most common build inputs override.

You can accomplish this with the following config:

:build-inputs [:main]

The :main keyword will be replaced by the main namespaces.

Only sending the watched directories

:build-inputs [:watch-dirs]

The :watch-dirs keyword will be replaced by the watched directories.

Compiling a directory that you don’t want to watch

Assuming that ./src-no-watch is a source directory that you want to compile but don’t want to watch.

:build-inputs [:watch-dirs "src-no-watch"]

A string is assumed to be a file or a directory.

Watched directories and a specific namespace

:build-inputs [:watch-dirs example.tool.extra]

A symbol is assumed to be a ClojureScript namespace.

Scripting API

You can readily start and stop a Figwheel build process from the REPL or a script.

This API is only available for figwheel.main 0.1.6 and higher.

Starting Figwheel from the REPL (or Script)

The scripting API isn’t quite as general as the command line API. It focuses on creating and managing running build processes.

To use the API you will need to ensure that you have the dependencies and the classpaths sorted out to use figwheel.main. Then you will need to require the figwheel.main.api namespace and call the figwheel.main.api/start function.

Let’s assume we have a dev.cljs.edn build file and we want to start Figwheel from the Clojure REPL. You can start the dev build with a REPL as follows:

$ clj

Clojure 1.9.0

user=> (require 'figwheel.main.api)

nil

user=> (figwheel.main.api/start "dev")

;; ... Figwheel startup output ommitted ...

ClojureScript 1.10.238

cljs.user=>

As you can see this starts a Figwheel build process along with a ClojureScript REPL.

If you want to start a Figwheel build without a REPL you will need to ensure that the :mode option is :serve. You can do this in the metadata in the build file or you can supply a replacement for the figwheel-main.edn config options like so:

$ clj

user=> (figwheel.main.api/start {:mode :serve} "dev")

[Figwheel] Compiling build dev to "target/public/cljs-out/dev-main.js"

[Figwheel] Successfully compiled build dev to "target/public/cljs-out/dev-main.js" in 0.782 seconds.

[Figwheel] Watching and compiling paths: ("src" "devel") for build - dev

[Figwheel] Starting Server at http://localhost:9500

2018-08-06 17:53:26.155:INFO::main: Logging initialized @15707ms

Opening URL http://localhost:9500

nil

user=>

As you can see this starts the Figwheel build process, launches a server and does not launch a ClojureScript REPL, instead it returns you to the Clojure REPL so you can continue interacting with your Clojure process.

Now that you have the dev Figwheel build running in the background, you can now use the rest of the Scripting API. For example, you can launch a ClojureScript REPL attached to the running dev build process like so:

;; in the Clojure REPL after you have started the "dev" build

user=> (figwheel.main.api/cljs-repl "dev")

This will start a REPL into the running dev build. You can quit the REPL via :cljs/quit and then restart it by calling figwheel.main.api/cljs-repl again.

REPL switching example

For this example to work, one will need to set up the normal Figwheel dependencies along with com.bhauman/rebel-readline-cljs.

We’ll start off by starting a Rebel Readline Clojure REPL.

$ clojure -m rebel-readline.main

user=>

Assuming that we already have a build set up in dev.cljs.edn and a background build defined in admin.cljs.edn.

We can start both of these builds running with:

user=> (require '[figwheel.main.api :as fig])

nil

user=> (fig/start {:mode :serve} "dev" "admin")

Now we’ll start a REPL for the dev build:

user=> (fig/cljs-repl "dev")

;; .. figwheel REPL startup output omitted ...

cljs.user=> (js/console.log "hey")

nil

There are a couple of things to notice at this point. If you check the console of the browser window that the dev application is running in you will notice that the word "hey" is printed out. You should also notice that your ClojureScript REPL is utilizing Rebel Readline.

You can now quit the dev REPL and launch a REPL into admin.

cljs.user=> :cljs/quit

nil

;; returns us to the Clojure REPL prompt

user=> (fig/cljs-repl "admin")

;; .. figwheel REPL startup output omitted ...

cljs.user=>

We’ve successfully switched between ClojureScript REPLs for our builds all the while staying in a Rebel Readline environment.

API Docs

figwheel.main.api/start

Args: ([build] [figwheel-options-or-build build & background-builds])

Starts a Figwheel build process.

Has two arities:

(start build) (start figwheel-config-or-build build & backgound-builds)

You can call start with any number of build arguments. The first one will be the foreground build and any builds that follow will be background builds. When you provide more than one argument to start the first argument can optionally be a map of Figwheel Main options.

A build arg can be either:

- the name of a build like “dev” (described in a .cljs.edn file)

- a map describing a build with the following form

{

:id      "dev"                   ; a required string build id

:options {:main 'hello-world.core} ; a required map of cljs compile options

:config  {:watch-dirs ["src"]}   ; an options map of figwheel.main config options

}

If the :options map has Figwheel options metadata, it will be used unless there is a non-nil :config option. The presence of a non-nil :config option map will cause any metadata on the :options map to be ignored.

The figwheel-config-or-build arg can be a build or a map of Figwheel options that will be used in place of the options found in a figwheel-main.edn file if present.

The background-builds is a collection of build args that will be run in the background.

Examples:

; The simplest and most common case. This will start figwheel just like

; `clojure -m figwheel.main -b dev -r`

(start "dev")

; With inline build config

(start {:id "dev"

:options {:main 'example.core}

:config {:watch-dirs ["src"]}})

; With inline figwheel config

(start {:css-dirs ["resources/public/css"]} "dev")

; With inline figwheel and build config:

(start {:css-dirs ["resources/public/css"]}

{:id "dev" :options {:main 'example.core}})

REPL API Usage

Starting a Figwheel build stores important build-info in a build registry. This build data will be used by the other REPL API functions:

- figwheel.main.api/cljs-repl

- figwheel.main.api/repl-env

- figwheel.main.api/stop

If you are in a REPL session the only way you can use the above functions is if you start Figwheel in a non-blocking manner. You can make start not launch a REPL by providing a :mode :serve entry in the Figwheel options.

For example neither of the following will start a REPL:

(start {:mode :serve} "dev")

(start {:id "dev"

:options {:main 'example.core}

:config {:watch-dirs ["src"]

:mode :serve}})

The above commands will leave you free to call the cljs-repl, repl-env and stop functions without interrupting the server and build process.

However once you call start you cannot call it again until you have stopped all of the running builds.

figwheel.main.api/cljs-repl

Args: ([build-id])

Once you have already started Figwheel in the background with a call to figwheel.main.api/start, you can supply a build name of a running build to this function to start a ClojureScript REPL for the running build.

Example:

(figwheel.main.api/cljs-repl "dev")

figwheel.main.api/repl-env

Args: ([build-id])

Once you have already started a build in the background with a call to start, you can supply the build-id of the running build to this function to fetch the repl-env for the running build. This is helpful in environments like vim-fireplace that need the repl-env.

Example:

(figwheel.main.api/repl-env "dev")

The repl-env returned by this function will not open urls when you start a ClojureScript REPL with it. If you want to change that behavior:

(dissoc (figwheel.main.api/repl-env "dev") :open-url-fn)

The REPL started with the above repl-env will be inferior to the REPL that is started by either figwheel.main.api/start and figwheel.main.api/cljs-repl as these will listen for and print out well formatted compiler warnings.

figwheel.main.api/stop

Args: ([build-id])

Takes a build-id and stops the given build from running. This will not work if you have not started the build with start.

figwheel.main.api/stop-all

Args: ([])

Stops all of the running builds.

(defn -main [& args]

(println "ARGS:" (pr-str args)))

You can execute it from the command line like this:

$ clj -m figwheel.main -m example.hello hi there

You will see the command will compile and eventually print out:

ARGS: ("hi" "there")

You may wonder why you might need a feature like this in the first place. Being able to run one-off arbitrary scripts can be a major boon to your tool chain. You will most likely use a main script to run tests, but it could also be useful for running tooling like Webpack from Node.

Being able to accept arbitrary arguments at the end of the command line is why CLI option order is important in clojure.main, cljs.main and figwheel.main. I know that this can be confusing at times, but being able to provide scripts and main scripts with arbitrary options, including ones that could mistakenly be recognized by figwheel.main, is very important for flexible expression. And is the reason why figwheel.main can reuse many of the same CLI args as clojure.main.

Figwheel and compile options when using --main

Since the -m and -b options cannot be used at the same time you may be left wondering how to supply the options that you would normally supply with the --build option to the --main option.

You can do this by providing your [build-id].cljs.edn file to the -co option. Figwheel will pick up both the metadata config along with the CLJS compiler config.

For example the following will allow you to use the config in your dev.cljs.edn file:

$ clj -m figwheel.main -co dev.cljs.edn -m example.hello

Asynchronous execution and non-zero exits

Only available in figwheel.main >= 0.1.9-SNAPSHOT

This --main CLI option behavior is unfortunately hampered in its ability to be useful because many ClojureScript tasks (including running tests) are asynchronous. Not only that, but we’d prefer that if a command line task execution fails in ClojureScript we’d like that failure to be communicated by returning a non-zero exit status from the clj process.

The good news is that currently if you throw a JavaScript Error from synchronous code in your -main function the Clojure process will exit with a non-zero exit status.

This means that when you are running test code that is synchronous, you can determine if the tests failed at the end of the run and throw an error to cause a non-zero exit from the Clojure process.

What do we do when the process is asynchronous?

figwheel.main extends the cljs.main behavior to provide a means of waiting for an asynchronous process to complete or throw an error.

The following 3 tools help you do this:

- the Figwheel Clojure process will block and wait when the -main function returns a vector [:figwheel.main.result/async-wait optional-timeout optional-timeout-value]

- the figwheel.main.result/send ClojureScript function will send back a value to the blocked Figwheel process

- the figwheel.main.result/throw-ex ClojureScript function will send back an exception to the blocked Figwheel process

Figwheel will look at the value returned by the -main function and alter its behavior if it receives something like [:figwheel.main.result/async-wait 5000]. This value will cause the Clojure process to block and wait for a result or timeout after 5 seconds.

For Example:

(ns example.hello)

(defn -main [& args]

[:figwheel.main.async-result/wait 5000])

When you run the above namespace with the following:

$ clj -m figwheel.main -m example.hello

The Clojure process will block for 5 seconds while it waits for some asynchronous result to be sent back. In this example we are not sending back a result value, so the process will throw a timed-out exception which will cause a non-zero exit.

Now let’s modify the above -main function to send back an asynchronous result.

(ns example.hello

(:require [figwheel.main.async-result :as async-result]))

(defn -main [& args]

(js/setTimeout #(async-result/send args) 3000)

[:figwheel.main.async-result/wait 5000])

Again run the -main function with:

$ clj -m figwheel.main -m example.hello

Now the Clojure process will block for 3 seconds and ultimately print out the value of args sent by the figwheel.main.async-result/send function.

Let’s modify the namespace one more time to send an asynchronous failure:

(ns example.hello

(:require [figwheel.main.async-result :as async-result]))

(defn -main [& args]

(js/setTimeout #(async-result/throw-ex (ex-info "This failed in ClojureScript!" {})) 3000)

[:figwheel.main.async-result/wait 5000])

After you run the above example, the Clojure process will block for 3 seconds and then fail with an exception.

More details

The figwheel.main.async-result/send and figwheel.main.async-result/throw-ex functions are single-use actions that are only intended to be used in a --main script. The first asynchronous value or exception that is received will unblock the process.

The returned :figwheel.main.async-result/wait message has two optional parameters.

- timeout defaults to 5000 milliseconds

- timeout-value defaults to :figwheel.main.async-result/timed-out

When Figwheel runs a -main script it prints the final result, cljs.main does not do this.

figwheel.main.api/start-join

Args: ([& args])

Takes the same arguments as start.

Starts figwheel and blocks. Useful when you want Figwheel to block on the server it starts when using :mode :serve. You would normally use this in a script that would otherwise exit prematurely.

Main Scripts

Just like cljs.main, the Figwheel allows you to run a -main function in a namespace from the command line. Figwheel provides additional functionality to facilitate asynchronous execution and process failure.

If you have a ClojureScript namespace on the classpath with a -main function in it, you can execute that function from the command line.

For example if you have the following namespace:

(ns example.hello)

(defn -main [& args]

(println "ARGS:" (pr-str args)))

You can execute it from the command line like this:

$ clj -m figwheel.main -m example.hello hi there

You will see the command will compile and eventually print out:

ARGS: ("hi" "there")

You may wonder why you might need a feature like this in the first place. Being able to run one-off arbitrary scripts can be a major boon to your tool chain. You will most likely use a main script to run tests, but it could also be useful for running tooling like Webpack from Node.

Being able to accept arbitrary arguments at the end of the command line is why CLI option order is important in clojure.main, cljs.main and figwheel.main. I know that this can be confusing at times, but being able to provide scripts and main scripts with arbitrary options, including ones that could mistakenly be recognized by figwheel.main, is very important for flexible expression. And is the reason why figwheel.main can reuse many of the same CLI args as clojure.main.

Figwheel and compile options when using --main

Since the -m and -b options cannot be used at the same time you may be left wondering how to supply the options that you would normally supply with the --build option to the --main option.

You can do this by providing your [build-id].cljs.edn file to the -co option. Figwheel will pick up both the metadata config along with the CLJS compiler config.

For example the following will allow you to use the config in your dev.cljs.edn file:

$ clj -m figwheel.main -co dev.cljs.edn -m example.hello

Asynchronous execution and non-zero exits

Only available in figwheel.main >= 0.1.9-SNAPSHOT

This --main CLI option behavior is unfortunately hampered in its ability to be useful because many ClojureScript tasks (including running tests) are asynchronous. Not only that, but we’d prefer that if a command line task execution fails in ClojureScript we’d like that failure to be communicated by returning a non-zero exit status from the clj process.

The good news is that currently if you throw a JavaScript Error from synchronous code in your -main function the Clojure process will exit with a non-zero exit status.

This means that when you are running test code that is synchronous, you can determine if the tests failed at the end of the run and throw an error to cause a non-zero exit from the Clojure process.

What do we do when the process is asynchronous?

figwheel.main extends the cljs.main behavior to provide a means of waiting for an asynchronous process to complete or throw an error.

The following 3 tools help you do this:

- the Figwheel Clojure process will block and wait when the -main function returns a vector [:figwheel.main.result/async-wait optional-timeout optional-timeout-value]

- the figwheel.main.result/send ClojureScript function will send back a value to the blocked Figwheel process

- the figwheel.main.result/throw-ex ClojureScript function will send back an exception to the blocked Figwheel process

Figwheel will look at the value returned by the -main function and alter its behavior if it receives something like [:figwheel.main.result/async-wait 5000]. This value will cause the Clojure process to block and wait for a result or timeout after 5 seconds.

For Example:

(ns example.hello)

(defn -main [& args]

[:figwheel.main.async-result/wait 5000])

When you run the above namespace with the following:

$ clj -m figwheel.main -m example.hello

The Clojure process will block for 5 seconds while it waits for some asynchronous result to be sent back. In this example we are not sending back a result value, so the process will throw a timed-out exception which will cause a non-zero exit.

Now let’s modify the above -main function to send back an asynchronous result.

(ns example.hello

(:require [figwheel.main.async-result :as async-result]))

(defn -main [& args]

(js/setTimeout #(async-result/send args) 3000)

[:figwheel.main.async-result/wait 5000])

Again run the -main function with:

$ clj -m figwheel.main -m example.hello

Now the Clojure process will block for 3 seconds and ultimately print out the value of args sent by the figwheel.main.async-result/send function.

Let’s modify the namespace one more time to send an asynchronous failure:

(ns example.hello

(:require [figwheel.main.async-result :as async-result]))

(defn -main [& args]

(js/setTimeout #(async-result/throw-ex (ex-info "This failed in ClojureScript!" {})) 3000)

[:figwheel.main.async-result/wait 5000])

After you run the above example, the Clojure process will block for 3 seconds and then fail with an exception.

More details

The figwheel.main.async-result/send and figwheel.main.async-result/throw-ex functions are single-use actions that are only intended to be used in a --main script. The first asynchronous value or exception that is received will unblock the process.

The returned :figwheel.main.async-result/wait message has two optional parameters.

- timeout defaults to 5000 milliseconds

- timeout-value defaults to :figwheel.main.async-result/timed-out

When Figwheel runs a -main script it prints the final result, cljs.main does not do this.

aTesting

Testing is an important part of any programming workflow. Having tests that run after every code change is essential and should not be hard to set up.

Figwheel now provides automatic test discovery and display, facilitates custom testing with Extra Main entry points, complements build processes and CI with non-zero test run failures, and provides a flexible way to configure an alternative JS environment to run your tests in.

Only available in figwheel.main >= 0.1.9-SNAPSHOT

Testing ClojureScript

If you haven’t written tests in ClojureScript yet, see the testing guide over at clojurescript.org.

Auto testing

The easiest way to start working with tests in Figwheel is to write some tests in a namespace that is on your classpath, and set the :auto-testing config option to true. Figwheel will automatically discover your tests and display them with cljs-test-display at the HTTP endpoint /figwheel-extra-main/auto-testing. No test runner is required and the tests will be re-run every time you save a watched source code file.

The :auto-testing config option can also take a map with the following keys:

- :cljs-test-display can have a boolean value indicating if you want to display the tests

- :namespaces takes a vector of namespaces that you want to test

Custom testing with :extra-main-files

If the testing setup provided by :auto-testing doesn’t work for you, the extra mains feature has you covered. We’ll quickly walk through an example of using :extra-main-files to provide a custom testing setup with relatively little effort.

First you would add a test runner namespace of some sort. I’m going to assume we are using cljs.test and cljs-test-display.

(ns example.test-runner

(:require

[cljs-test-display.core]

[cljs.test :refer-macros [run-tests]]

;; require all the namespaces that have tests in them

[example.core-test]

[example.other-test]))

(run-tests (cljs-test-display.core/init! "app-testing")

'example.core-test

'example.other-test)

Now we’ll configure this as an extra main entry point in our dev.cljs.edn file:

^{:extra-main-files {:testing {:main example.test-runner}}}

{:main example.core}

Now when you start your build with the standard clojure -m figwheel.main -b dev -r command you will now be able to see your custom testing at the /figwheel-extra-main/testing endpoint.

Please note that the "app-testing" above refers to the DOM id of the DIV that is available by default on the page served by the extra main endpoint. This id needs to correspond to the name of your extra main. In this case our extra main is named :testing. If your extra main was named :tests you would need to use "app-tests" in the cljs-test-display/init! call.

You can also utilize the figwheel.main.testing/run-tests macro that will automatically find all the testing namespaces in your source files.

(ns example.test-runner

(:require

[cljs-test-display.core]

[figwheel.main.testing :refer-macros [run-tests]]

;; require all the namespaces that have tests in them

[example.core-test]

[example.other-test]))

(run-tests (cljs-test-display.core/init! "app-testing"))

And as usual you can always create your own HTML host page for your extra main. In this case you would have to require the target/public/cljs-out/dev-main-testing.js bootstrap script.

Running tests from the command line

Running tests from the command line is an important feature that allows you to confirm that your tests are passing without having to boot up and kill a REPL. Being able to run tests from the command line in a process that will non-zero exit on test failure is an essential feature that allows us to integrate testing into modern CI and cloud testing services.

Up until now most CLJS developers have had to rely on using a Node process to drive a headless JavaScript environment to run their ClojureScript tests. This is a shame considering that we are already using a mediating process when we run Clojure to start our ClojureScript REPLs and run our scripts.

Figwheel can now run an asynchronous main script and report failures with a non-zero exit status.

Here is an example test runner that uses the new asynchronous main script functionality.

(ns example.test-runner

(:require

[cljs.test :refer-macros [run-tests] :refer [report]]

[figwheel.main.async-result :as async-result]

;; require all the namespaces that have tests in them

[example.core-test]

[example.other-test]))

;; tests can be asynchronous, we must hook test end

(defmethod report [:cljs.test/default :end-run-tests] [test-data]

(if (cljs.test/successful? test-data)

(async-result/send "Tests passed!!")

(async-result/throw-ex (ex-info "Tests Failed" test-data))))

(defn -main [& args]

(run-tests 'example.core-test 'example.other-test)

;; return a message to the figwheel process that tells it to wait

[:figwheel.main.async-result/wait 5000])

This is just an example but given that the test namespaces are on the classpath, you can execute a test runner main script like the one above on the command line as follows:

clj -m figwheel.main -m example.test-runner

The above example is a bit verbose so Figwheel has provided a helper that will find all your testing namespaces and execute them taking advantage of the new asynchronous -main support.

Let’s repeat the above example using the figwheel.main.testing/run-tests-async helper.

(ns example.test-runner

(:require

[figwheel.main.testing :refer-macros [run-tests-async]]

;; require all the namespaces that have tests in them

[example.core-test]

[example.other-test]))

(defn -main [& args]

;; this needs to be the last statement in the main function so that it can

;; return the value `[:figwheel.main.async-result/wait 10000]`

(run-tests-async 10000))

If by any chance your tests are synchronous please feel free to use the figwheel.main.testing/run-tests helper which will throw an exception if tests fail.

Running tests in a headless environment

CI and testing environments almost always require being able to run tests in a headless JavaScript environment.

Figwheel now has the :launch-js configuration option which should allow you to launch an arbitrary JavaScript environment.

:launch-js allows three different ways to launch a JavaScript environment.

- a string representing a shell script that will be supplied a URL or a path to your compiled ClojureScript. Example: "headless-chrome"

- a vector representing a shell command. Example: ["headless-chrome" :open-url]

- a namespaced symbol that represents a Clojure function that will be supplied a map. Example: user/run-test-env

I’m going to show how you can use the :launch-js config option to launch a headless Chrome environment to run your tests using all 3 different methods.

Shell Script method

On my Mac in my ~/bin directory I have an executable shell script named headless-chrome that has the following content:

#!/bin/sh

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --disable-gpu --repl "$@"

The above script takes a single URL argument.

Now we just have to add :launch-js "headless-chrome" to our Figwheel options. We can do this on the command line while we run our tests like so:

clojure -m figwheel.main -fwo '{:launch-js "headless-chrome"}'  -m example.test-runner

We can also add the option in a tests.cljs.edn build file if we are using it:

^{:launch-js "headless-chrome"}

{:main example.normal-test-runner}

And then use it like this:

clojure -m figwheel.main -co tests.cljs.edn  -m example.test-runner

The command vector method

The command vector method is pretty much the same as the shell script method but it allows you to execute arbitrary shell commands without needing to write a script.

The following will launch headless Chrome on my system:

:launch-js ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

"--headless" "--disable-gpu" "--repl" :open-url]

Using a Clojure function

This time let’s use a Clojure function to launch headless Chrome. First we’ll place a headless-js-env function in our user.clj file.

(ns user

(:require [clojure.java.shell :as shell]))

(defn headless-js-env [{:keys [open-url]}]

(shell/sh "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"

"--headless" "--disable-gpu" "--repl" open-url))

Now we will configure :launch-js to take a user/headless-js-env symbol:

clojure -m figwheel.main -fwo '{:launch-js user/headless-js-env}'  -m example.test-runner

Editor Integration

Undoubtedly one of the major advantages of using Clojure (and Lisps in general) is that S Expressions and REPLs make it trivial for editors to evaluate code that is present in an editor buffer.

Experiencing the interactive workflow that results from being able to evaluate Clojure expressions that are present in your editor’s buffer frame is enlightening. When you have this ability to quickly verify behavior without the ceremony of restarting an application, you will start to move forward with more confidence. You have the ability to check that a function works against glaring edge cases in seconds. This allows you to think more clearly about what your code is actually doing, and more clearly about what you actually need.

Many editors provide support for evaluating ClojureScript code via a REPL connection, however the process for setting this up can seem complex, mysterious and convoluted.

Why does this complexity exist? The main reason that there is complexity in connecting a ClojureScript REPL to your editor is that the environment is just well … complex.

The typical path of code that will ultimately evaluated in a Browser and eventually returned to an editor:

Editor Buffer

|

nREPL server                    - Clojure remote REPL network interface

|

Piggieback nREPL Middleware     - re-directs nREPL evals to ClojureScript Compiler

|

ClojureScript REPL Code         - compiles ClojureScript to JavaScript

|

Figwheel REPL Websocket Server  - pushes JavaScript to Browser

|

Browser                         - Figwheel REPL Client code evaluates JavaScript

|

... and back through all the layers

The main problem here is that a lot is going on. When we evaluate Clojure remotely the message just needs to travel to the Clojure runtime and back. When we are using ClojureScript the code has to travel to the compiler and then to the browser and back.

The Major Players

Because of this complexity it is best if we start and understand the pieces. In practice you will not often have to set these up yourself but things can become very confusing very quickly if you don’t understand what they are.

nREPL

nREPL is server that implements a protocol that supports evaluating Clojure code remotely.

It is common for Clojure developers to start an nREPL server inside an application that is running on a remote server so they can interactively debug particularly thorny problems.

nREPL provides the server endpoint that most editors integrations talk to when they want to evaluate Clojure/ClojureScript code.

The simplest way to explain how nREPL works is that it understands a set of messages. The most important one for our purposes is the :eval message that includes a :code attribute.

nREPL is extensible via middleware that can receive and handle these messages.

Piggieback

Piggieback is nREPL middleware that intercepts :eval messages and re-directs them to a ClojureScript REPL for evaluation. So Piggieback is an extension of nREPL and we have to extend nREPL because it knows nothing about ClojureScript.

There are two main parts to using Piggieback. First, it has to be inserted into the middleware of the nREPL server that you are going to be using. Second, you have to start your ClojureScript REPL with a call to cider.piggieback/cljs-repl. You have to start your REPL this way so that it can do the bookkeeping necessary to re-direct :eval messages to the ClojureScript REPL.

ClojureScript REPL

The ClojureScript REPL is written in Clojure and is what allows us to evaluate ClojureScript code in the first place.

You can run a ClojureScript REPL locally and this is what happens when you run clojure -m figwheel.main -b dev -r. This is pretty simple to accomplish. However, it is when you wan to run it remotely over nREPL that things get more difficult.

The ClojureScript REPL is responsible for compiling ClojureScript code and sending it to a JavaScript environment for evaluation. The ClojureScript REPL’s implementation is independent of the various JavaScript environments that are available to evaluate JavaScript. For this reason the ClojureScript REPL needs to be provided a Connection to a JavaScript environment.

Figwheel Websocket Server

The Figwheel provides a Websocket server that the Browser or Node can connect back to. It is over this connection that the ClojureScript REPL sends JavaScript code for evaluation. And it is also over this connection that the ClojureScript REPL receives the response as a result of the evaluation.

Editors and nREPL

Almost all editor integrations in the ClojureScript ecosystem can utilize nREPL for communication. This is why understanding it and its role as a REPL server is important.

There are other ways for editors to connect to Clojure remotely but currently nREPL integrations tend to provide the most editor functionality (i.e. documentation, auto-complete, jump to definition). You can opt to connect to a REPL by another means but the nREPL integrations are by far the most used.

Editors and Stream REPLs

A traditional way for editors to communicate with a REPL is to pipe forms (Clojure expressions in this case), to the STDIN of the REPL and capture the STDOUT as a response. This form of communication is much simpler.

Currently both Emacs and Cursive support this type of communication. Emacs has its inf-clojure. Cursive has particularly good support for this type of REPL communication when you create a clojure.main REPL.

Editors integrations

There are two major ClojureScript editor integrations, CIDER for Emacs and Cursive for IntelliJ.

Don’t worry if neither of those are your favorite editors as there are many other integrations out there. But you should know that CIDER and Cursive account for a whopping 80% of Clojure editor use and this is not by accident as both CIDER and Cursive are under intense active development.

10% of Clojurists use Vim Fireplace. The Vim support for Clojure/Script is nowhere near as extensive as the previous two, I’ll chalk its high rate of adoption up to the sheer popularity of Vi among the hacker crowd.

Atom, VSCode and Sublime Text all have support for evaluating ClojureScript code from your editor as well.

Vi users

I know that Emacs is the other text editor but I highly recommend that Vi users use Emacs with evil-mode, many Clojurists who are come from a Vi background have switched and are happy about it. The main reason is that Emacs Clojure integration via CIDER is heads and shoulders above the currently available Vi integration.

See this blog post for an experience report on switching to Emacs.

Emacs

Regardless of how you feel about using Emacs as an editor, it is one of the oldest actively developed text editors. Emacs is also LISP centric and because of this its native features already support the interactions we want when we are working on Clojure code.

Learn more about why Emacs is sexy.

Emacs can support LISP editing and REPL interaction natively, without installing any extra packages or libraries, but the experience is sub optimal. The CIDER library provides a comprehensive set of features to help us edit Clojure code. I’m going to focus on setting up CIDER from scratch in this guide.

Installing Emacs

You can follow the instructions on the Emacs site to download and install Emacs.

If you are on MacOS don’t miss the Emacs for OSX link at the bottom of the page as downloading the pre-compiled binary is by far the easiest way to install Emacs on OSX.

Emacs is universally available in most package managers and if you can’t find an expedient way to install it on your current OS, I would be very very surprised.

Learning to use Emacs

There is a lot to know about Emacs but you can get by with a relatively small amount of information at the start.

When Emacs starts it will display a link to a tutorial. Click on that link and learn enough so that you can get around in the buffer and execute some commands. Also, make sure that you know how to open and save files and how to switch from one editor buffer to another. With this small bit of information you will be able to do a lot of editing.

Some good visual guides on how to learn Emacs.

Emacs rocks is a great resource of helpful videos once you want to do more advanced editing.

Personally, I am not an Emacs expert, I tend to use a very small set of commands when I’m using Emacs. I tend to use the commands that are available universally in text buffers and shell line readers. I do tend to use more commands when I’m creating Emacs macros to automate an otherwise repetitive operation to transform some text.

Installing CIDER

Now we are going to start installing some Emacs packages. We will use package.el to install some Emacs lisp packages.

First we need to add a snippet to the top of our ~/.emacs.d/init.el file. If you don’t have an ~/.emacs.d/init.el you should create one and make sure that it has the following code at the top.

(require 'package)

(add-to-list 'package-archives

'("melpa-stable" . "https://stable.melpa.org/packages/") t)

(package-initialize)

This will initialize the package system and add the melpa-stable package repository to the list of repositories to fetch packages from.

If you are editing init.el in Emacs you can now either restart Emacs or you can call the eval-last-sexp with Ctrl-x Ctrl-e at the end of each of these expressions one at a time. Either way will work to execute the code.

Now that we have initialized the package system we can install and setup some packages.

If you are using MacOS you will want to install a package to import and use the PATH of your terminal environment in Emacs.

Type M-x (meta x) then package-install and hit ENTER. You will be prompted for a package name. At the prompt type the name exec-path-from-shell and hit ENTER.

This will quickly download and install the exec-path-from-shell library.

Almost every Emacs package you install will contain instructions that provide some code to add to your init.el file. You can normally find these instructions in the comments at the top of the .el file.

You can find the source code of the installed packages in the ~/.emacs.d/elpa directory. With my current system I can see the docs for exec-path-from-shell in ~/.emacs.d/elpa/exec-path-from-shell-1.11/exec-path-from-shell.el

When I look there I learn that I should add the following snippet to my init.el:

(require 'package)

(add-to-list 'package-archives

'("melpa" . "https://melpa.org/packages/") t)

(package-initialize)

;; setup exec-path-from-shell here

(when (memq window-system '(mac ns))

(exec-path-from-shell-initialize))

Now let’s install CIDER following a similar procedure.

Type M-x then package-install then ENTER then cider and finally hit ENTER. This will install cider and clojure-mode.

Using CIDER from ClojureScript

To prepare to use CIDER in Emacs for the first time you will want to make sure all the libraries you need to start Figwheel are available when you call lein repl or clojure to start a REPL.

If you are using Clojure CLI tools all the libraries you need to compile and start your Figwheel build should be in the top level :deps map, don’t place your com.bhauman/figwheel-main and other libraries in an alias.

In a simple hello-world example this means that your deps.edn file would look like this:

{:deps {org.clojure/clojure {:mvn/version "1.9.0"}

org.clojure/clojurescript {:mvn/version "1.10.773"}

com.bhauman/figwheel-main {:mvn/version "0.2.14"}}

:paths ["src" "resources" "target"]}

For Leiningen there is less of a problem because folks normally put development time dependencies in the :dev profile which will be available on the classpath when we run lein repl. Just make sure that when you run lein repl that you can require (require 'figwheel.main.api) and run your build via (figwheel.main.api/start %build-name).

To start editing ClojureScript with CIDER integration, you should navigate (in Emacs) to a ClojureScript source file in one of your ClojureScript source directories (For example src/hello_world/core.cljs). Now we will start our Figwheel build from inside Emacs, with our cursor in the ClojureScript source code buffer type M-x cider-jack-in-cljs. When you type this it will prompt you for the type of tool you want to start a Clojure REPL with. If you are using Leiningen type lein if you are using the Clojure tools type clojure.

Cider will now ask you what type of ClojureScript REPL you want to start. You should answer figwheel-main.

Next it will ask you for the name of your build. It should list build configurations available (such as “dev” build corresponding to the dev.cljs.edn file) with one selected by default so you can just hit enter if its correct.

At this point you will see Figwheel start up in a REPL buffer in Emacs.

Now you can use this REPL buffer like you would any other REPL. This is handy in itself because you will be able to copy and paste code from an editor buffer to the REPL quite easily.

The real magic happens when you experience evaluating ClojureScript code from a buffer that holds a ClojureScript source file.

If you return to the buffer that contains the ClojureScript file (where you launched Figwheel from). You can for example at the bottom of the file you can add a line (+ 1 2 3) and then put your cursor at the end of the line and hit Ctrl-x Ctrl-e to evaluate it. You should see an inline evaluation of you code appear at the end of the line.

CIDER can provides many features to help you as a Clojure/Script developer. You should take some time to read the documentation especially the parts about interactive programming and using the REPL.

Paredit

Structural editing makes editing LISP based languages a breeze and in my opinion much better than editing languages that have an irregular approach to delimiting expressions or that favor statements over expressions.

Learning Paredit is an essential part of understanding the LISP programming experience.

You will want to install paredit just like you installed cider above.

Type M-x package-install hit enter then type paredit. Once it is installed you want to make it start whenever you are editing a Clojure/Script file. To do this add the following line to your .emacs.d/init.el file after the (package-initialize) line:

(add-hook 'clojure-mode-hook #'paredit-mode)

Checkout this great animated guide to using Paredit

Troubleshooting

CIDER is under active development and it’s not uncommon to run into troubles when you try to set this up.

You will find helpful folks on the #cider and #emacs channels on the Clojurians Slack. You can sign up for the Clojurians Slack here.

If you are just starting to use Emacs my best advice is to keep your configuration simple at first. Trying to add a ton of functionality to Emacs without understanding the ramifications of what your are doing will most likely lead to thrashing about and not having anything work.

At first focus on getting things to work, not on getting them to work perfectly.

Cursive

Once you have become comfortable with figwheel-main, you may want to put some effort into integrating it with your preferred editor. This document will discuss how to get your project working nicely with Cursive for a few different configurations.

CLJS Only

Support for deps.edn and the CLI is currently only available in the Early Access versions of Cursive and it is not yet able to run figwheel-main directly, so for now we will focus on Leiningen.

We’re going to start with the same build that we used in the create-a-build documentation. Set up your project as described there and confirm that you can run it using

$ lein fig -- -b dev -r

These instructions should work for any project that is able to run Figwheel from the command line using a lein run command

Add a dev folder to the root of your project, then add a user.clj file in that folder and copy this code into it

(require '[figwheel.main.api :as fig])

(fig/start "dev")

Now we can get our REPL configured. In the Cursive menu, follow these steps

- Navigate to Run -> Edit Configurations

- Click the + icon and select Clojure REPL -> Local

- Select Use clojure.main in normal JVM process

- Enter dev/user.clj into the parameters field.

- Click OK to save the config.

Run your REPL and enjoy figwheel-main in Cursive! Note that this will be a ClojureScript REPL - we will see how to get things working nicely with a combined Clojure + ClojureScript project next.

Code Splitting

ClojureScript lets you break down a larger application into modules that can be dynamically loaded into your host environment at runtime.

This feature is covered in the Code Splitting Guide. I’m going to discuss how to use Figwheel with a build where you are utilizing code splitting.

Skip :main and :output-to

The first thing to note is that both the :main and the top-level :output-to compile options have no meaning and are ignored when you configure a code splitting build with :modules.

Previously, we have said that to use the --build Figwheel flag requires declaring a :main at the very least. This doesn’t apply when you configure a code splitting build.

Figwheel Code Splitting Guide

This requires figwheel-main version 0.1.5 or greater.

This is going to be a brief version of the official Code Splitting Guide with adjustments for working with Figwheel. You should read and understand that document first.

Make a Simple Project

Create a project folder:

mkdir -p hello-modules

cd hello-modules

mkdir src

Create a deps.edn file to work with Figwheel.

touch deps.edn

Edit this script to look like the following:

{:deps {com.bhauman/figwheel-main {:mvn/version "0.2.14"}}

:paths ["src" "resources" "target"]}

The Sources

Create the foo.core namespace:

mkdir -p src/foo

touch src/foo/core.cljs

Edit this file to look like the following:

(ns ^:figwheel-load foo.core

(:require [goog.dom :as gdom]

[goog.events :as events]

[cljs.loader :as loader])

(:import [goog.events EventType]))

(println "I'm foo!")

(defn listen-to-button []

(let [app (gdom/getElement "app")

button (gdom/createDom

"button"

(gdom/createTextNode "Load Bar!"))]

(gdom/removeChildren app)

(gdom/append app button)

(events/listen button EventType.CLICK

(fn [e]

(loader/load :bar

(fn []

((resolve 'bar.core/woz))))))))

(defonce init (listen-to-button))

(loader/set-loaded! :foo)

I altered the original example to make it reloadable and so that it works with the DOM on the Figwheel default dev page. I also added ^:figwheel-load metadata to the namespace so that I can easily load the file into my environment by saving it.

Create the bar.core namespace:

mkdir -p src/bar

touch src/bar/core.cljs

(ns bar.core

(:require [cljs.loader :as loader]))

(println "I'm bar!")

(defn woz []

(println "WOZ!"))

(loader/set-loaded! :bar)

Note I removed the (enable-console-print!) from the original examples. Figwheel already enables printing to the console by default. See the :client-print-to option.

Configure your build file

Let’s make our Figwheel build file support a :modules based build.

In a dev.cljs.edn file place the following:

^{:watch-dirs ["src"]}

{:modules {:foo {:entries #{foo.core}}

:bar {:entries #{bar.core}}}}

This is all the compile configuration that’s needed for Figwheel to start building and hot reloading a build that is using code splitting.

The :watch-dirs config option is required when working with :modules.

Note that Figwheel will supply the :output-to config parameter for each of your modules if one doesn’t exist already.

If you do want to supply your own :output-to parameters to each module you will want to heed the advice that is true for the top level :output-to option. It needs to be on a path that the client can load and thus should normally be on the classpath under a public directory.

If you want to choose your :output-to you will probably also want to configure your top level :output-dir and :asset-path options as well.

You can learn more about the configuration params that Figwheel provides by using the -pc option with the above minimal configuration.

For example if you run:

clojure -m figwheel.main -pc -b dev -r

It will show compile configuration to be this:

---------------------- Compiler options ----------------------

{:modules

{:foo

{:entries #{foo.core},

:output-to "target/public/cljs-out/dev-foo.js"},

:bar

{:entries #{bar.core},

:output-to "target/public/cljs-out/dev-bar.js"}},

:preloads

[figwheel.core

figwheel.main

figwheel.repl.preload

devtools.preload],

:output-dir "target/public/cljs-out/dev",

:asset-path "/cljs-out/dev",

:aot-cache false,

:closure-defines

#:figwheel.repl{connect-url

"ws://localhost:9500/figwheel-connect?fwprocess=279383&fwbuild=dev"},

:repl-requires

([figwheel.repl :refer-macros [conns focus]]

[figwheel.main

:refer-macros

[stop-builds start-builds build-once reset clean status]]

[cljs.pprint :refer [pprint] :refer-macros [pp]]

[cljs.repl :refer-macros [source doc find-doc apropos dir pst]])}

The main difference here and an ordinary build is that there is neither a :main option nor a top-level :output-to option.

Build and start a REPL

Note that we haven’t created a host HTML page for our build yet. The Figwheel default dev page should work and host a REPL for your :modules build.

To begin just start the build as usual:

clojure -m figwheel.main -b dev -r

You will see the default dev page open up and a REPL start.

It is important to note that neither of the modules has been loaded yet. You can require (or use :figwheel-load metadata) to load them in to the client environment and start working on them in the normal hot reloaded workflow.

Since we already included ^:figwheel-load metadata on the foo.core namespace you can make a small whitespace change to the src/foo/core.cljs file and you will see a Load Bar! button replace all the current content of the page. Open the development console and press the button so you can see the dynamic loading at work.

Host page

We’re going to make a Host page now. Almost every thing that we covered on the Host Page doc still applies here. The only difference is that we need to load a cljs_base.js file along with any modules we want to initialize the page with.

Now make a resources/public/index.html file:

<html>

<body>

<div id="app"></div>

<!-- include the cljs_base.js target file -->

<script src="target/cljs-out/dev/cljs_base.js" type="text/javascript"></script>

<!-- You will normally want to include at least one module otherwise nothing will happen -->

<script src="target/cljs-out/dev-foo.js" type="text/javascript"></script>

</body>

</html>

Note the name of the default :output-to file for the :foo module is [build-id]-[module-name].js.

If you customize the :output-to’s for the modules you will need to alter the paths on your host page accordingly.

Build the Project

Now if we return to the shell and build the project again:

clojure -m figwheel.main -b dev -r

We will see the page with our button on it and get a working REPL. We are now in a normal Figwheel hot reloaded workflow.

Release Compile

The release compile is the same as other advanced builds:

clojure -m figwheel.main -O advanced -bo dev

The advanced compile still outputs a cljs_base.js file and a compressed output file for each module so your HTML host page will still work just fine.

Jetty Conflicts

This should be fixed if you upgrade to figwheel-main 0.2.1-SNAPSHOT or later.

Figwheel currently uses the same Jetty webserver version that the Ring Jetty Adapter uses. Unfortunately Jetty is packaged in a way that is prone to version conflicts.

Moving to Jetty means that Figwheel is using the same server that many Clojure applications are already using. While this can be helpful in terms of consistent behavior, supporting HTTPS and minimizing dependencies, it unfortunately also leads to version conflicts because of the way Jetty is packaged. The most common conflict that folks seem to experience is when they include the Datomic Cloud client.

This also can happen if you update your version of Ring to 1.7.1.

Fixing the conflict by specifying explicit Jetty deps

Currently if I add com.datomic/client-cloud {:mvn/version "0.8.71"} to a deps.edn file along with com.bhauman/figwheel-main, trying to start a build with figwheel.main with fail with:

java.lang.NoClassDefFoundError: org/eclipse/jetty/http/HttpParser$ProxyHandler

This is due to a dependency mismatch between Jetty deps.

The most sure fire way to fix this error is to explicitly specify all the Jetty dependencies in your project and ensure they are all the same version.

In your deps.edn this would look like this:

{:deps {org.clojure/clojurescript {:mvn/version "1.10.773"}

com.bhauman/figwheel-main {:mvn/version "0.2.14"}

com.datomic/client-cloud {:mvn/version "0.8.71"}

;; directly specify all jetty dependencies

;; ensure all the dependencies have the same version

org.eclipse.jetty/jetty-server                {:mvn/version "9.4.12.v20180830"}

org.eclipse.jetty.websocket/websocket-servlet {:mvn/version "9.4.12.v20180830"}

org.eclipse.jetty.websocket/websocket-server  {:mvn/version "9.4.12.v20180830"}

}}

The next time you start Figwheel the error should be gone.

Conflicts in general

You can detect these conflicts by looking at the dependency tree of your application.

Leiningen has excellent support for pointing out these conflicts.

If you are using Leiningen then you can run the lein deps :tree command.

Take some time and carefully read the output. It will print out dependency conflicts and make suggestions as to how you can fix them.

It also prints out the dependency tree that it is using, so take some time to examine that as well.

with Clojure CLI tools

Clojure CLI tools doesn’t have the helpful output that Leiningen currently supplies. It can print out the dependency tree:

$ clj -S:tree

Writing reloadable code

Figwheel relies on having files that can be reloaded.

Reloading works beautifully on referentially transparent code and code that only defines behavior without bundling state with the behavior.

Let’s face it though, we are in JavaScript’s domain and mutating state is just the way stuff gets done.

Load Time Side-Effects

Load time side-effects are the side-effects that change the state of our JavaScript environment when a file is loaded. As an illustration: when a file with code in it is loaded, it will be changing the state by importing that code into the environment. That is a side-effect of loading the file but it is exactly the side-effect we want when we load a file.

If you are mutating the DOM or your program state at the top-level of your file so that these state changes will occur when the file is loaded, we are going to have to put some thought into how we can safely make these things happen so that we can protect the state of our running program.

Reloadable code is code where load time side-effects will not interact with our running program in a destructive way.

The good news:

- it is idiomatic in ClojureScript to write our functions so that they don’t exhibit bad load time side-effects

- ReactJS makes it trivial to work with the DOM in a declarative way that is very reloadable

The upshot is: if you are using React or one of the ClojureScript libraries that wraps it, it’s not hard to write reloadable code, in fact you might be doing it already.

Potential Problems

There are several coding patterns to look out for when writing reloadable code.

Top-level mutable state

One problematic pattern is top-level definitions that have local state.

(def state (atom {}))

The state definition above is holding program state in an atom. Every time this definition gets reloaded, the definition will be redefined and reset back to the empty map {}. This is normally not desirable.

The way to fix this is to use defonce

(defonce state (atom {}))

This will fix most situations where you have a top-level definition. It is important to remember that if you change code in a defonce you won’t see the changes after a reload because it won’t be evaluated after the first load.

Page initialization

It is not uncommon to initialize and launch your program from the top-level of a file. However, you normally won’t want to run this initialization every time this file gets reloaded.

We can solve this problem by using defonce again. For example:

(defonce initialize-block

(do

(initialize-dom)

(add-body-listeners)

true))

Above, we use the name initialize-block but you can use any name because it will probably never be referenced in your program. We use a do block because defonce can only take one form besides the name. At the end of the do block we provide a value true so that the defonce is only invoked the first time it is evaluated. If add-body-listeners returns a truthy value we don’t have to provide the true at the end.

Running your initialization code the first time the page loads can also be accomplished by registering a load handler on window.

(ns example.core

(:require

[goog.events :as events]))

(events/listen js/window "load"

(fn [_]

(initialize-dom)

(add-body-listeners)))

Setup and teardown pattern

If you are working on a page where you are mainly altering the DOM directly but you still want to use hot reloading, you can normally manage almost any complex state with a setup and teardown pattern.

Create a couple of functions named setup and teardown to help reset your application on every reload.

A trivial example is to add a listener in setup and remove it in teardown.

(ns ^:figwheel-hooks example.core

(:require

[goog.dom :as dom]

[goog.events :as events]))

(defn ^:after-load setup []

(events/listen

(dom/getElementByTagNameAndClass "a" "submit-btn")

"click"

(fn [e] (handle-submit)))))

(defn ^:before-load teardown []

(events/removeAll

(dom/getElementByTagNameAndClass "a" "submit-btn"))

;; and we'll want to call setup on the first initial load as well.

(defonce init-block (setup))

The above is taking advantage of Figwheel’s metadata markers to specify functions to call before and after a reload. The ^:figwheel-hooks marker is required to let Figwheel know that there are reload hooks in the namespace.

Use ReactJS

When you use ReactJS, state is mediated and managed for you. Your code describes what should be there and then React takes care of all the state changes needed to bring the DOM inline with your description. For this reason React is a prime candidate for writing reloadable code.

Conclusion

There are many ways to make your code reloadable. Once you start paying attention to load time side-effects, the exercise of writing reloadable code becomes fairly straight forward.

Since a great deal of programming complexity stems from complex interactions (side-effecting events) between things that have local state, it is my belief that being more attentive to load time side-effects, leads developers to reduce the amount of state and stateful objects that are strewn throughout their programs and take a more intentional approach to where state is stored and how it is transformed.

Reloadable code is often simply better code.

aaaaaaaaaa