The official docs already have very good getting started instructions,
refer to them for details. On OSX:
brew install clojure
You will need some cljs/cljc sources in src/my_app_ns/
. Create a file in the
root of your project called deps.edn
, and fill it with:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.10.439"}}
:aliases {:dev {:extra-paths ["resources"]
:extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}}}}}
Figwheel uses dev.cljs.edn
to configure the ClojureScript compiler, and
optionally, figwheel itself as well. Here's a minimal version:
{:main my-app-ns.core
:optimizations :none
:pretty-print true
:source-map true
:asset-path "/js/dev"
:output-to "resources/public/js/dev.js"
:output-dir "resources/public/js/dev"}
Now you can launch a figwheel server from a shell:
clojure -A:dev -m figwheel.main -b dev -r
This will pop up a browser window and drop you into a shell. NB! While you
can add the command line arguments to :main-opts
I strongly suggest you
don't until you've read through the Emacs section.
The dev.cljs.edn
file creates a figwheel build, called dev
- the name of the
configuration file, excluding the suffix. If you want to have a more optimized
production build, just create another *.cljs.edn
file, and invoke
figwheel.main
with the -b
(that's "build") or --build-once
option pointing
to the file you created. Example:
{:main my-app-ns.core
:optimizations :advanced
:pretty-print false
:source-map "resources/public/js/prod.js.map"
:asset-path "/js/prod"
:output-to "resources/public/js/prod.js"
:output-dir "resources/public/js/prod"}
Then build the file:
clojure -A:dev -m figwheel.main --build-once prod
NB! It is not recommended to share :output-dir
between different
builds with different optimization settings, as you will end up with builds that
simply do not work.
figwheel-main supports multiple mains from the same build, which means that you
can serve both your development build and devcards from the same figwheel
process. It's amazing. In the root of your project, create the
devcards/my_app_ns
directory, and add the following to
devcards/my_app_ns/cards.cljs
:
(ns ^:figwheel-hooks my-app-ns.cards
(:require [devcards.core]
[my-app-ns.cards.my-first-devcard]))
(enable-console-print!)
(defn render []
(devcards.core/start-devcard-ui!))
(defn ^:after-load render-on-reload []
(render))
(render)
You will need to require all your devcards into this namespace.
Next, add the Devcards dependency and the extra directory to your deps.edn
file:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.10.439"}}
:aliases {:dev {:extra-paths ["resources" "devcards"]
:extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}
devcards {:mvn/version "0.2.6"}}}}}
Then, crucially, enable devcards in dev.cljs.edn
, otherwise devcards will be
in noop mode. We also need to tell figwheel about the new build we're exposing.
Edit dev.cljs.edn
:
^{:watch-dirs ["src" "devcards"]
:extra-main-files {:devcards {:main my-app-ns.cards}}} {:main my-app-ns.core
:devcards true :optimizations :none
:pretty-print true
:source-map true
:asset-path "/js/dev"
:output-to "resources/public/js/dev.js"
:output-dir "resources/public/js/dev"}
Restart the figwheel process. Now http://localhost:9500
will serve your dev build, and
http://localhost:9500/figwheel-extra-main/devcards
will serve your devcards.
If you need to serve some CSS files, or otherwise want to tweak the HTML page
that your devcards is served from, add resources/public/devcards.html
and make
sure it includes /js/dev-devcards.js
. Then view devcards from
http://localhost:9500/devcards.html
instead.
To run it all from Emacs, add a .dir-locals.el
file to the root of your
project with the following contents:
((nil
(cider-clojure-cli-global-options . "-A:dev")
(cider-default-cljs-repl . figwheel-main)
(cider-figwheel-main-default-options . ":dev")))
This will tell CIDER to use your :dev
tools.deps profile, to start a
figwheel-main REPL, and to use the :dev
figwheel build (e.g. dev.cljs.edn
).
Pitfall warning: These vars will be set when you open new files, they will
not apply when you revisit existing buffers. To avoid frustration, kill the
deps.edn
buffer, reopen it, and continue from there.
Start a fighweel-main REPL from inside Emacs (remember to close any existing
figwheel process running in a shell) with C-c M-C-j
, or cider-jack-in-cljs
.
It's crucial that you use the cljs
version, and not the clj
version (note
the added meta in C-c M-C-j
). I've already wasted enough time running the
wrong jack-in for everyone, no need to repeat this mistake...
You now have the ability to run figwheel-main with Devcards from both Emacs and
the shell. However, your shell users are stuck with a pretty long incantation.
It can be improved by putting the main options into your deps.edn
file.
However, do not put :main-opts
in the profile you intend to use with
CIDER. It will not work. It will start a figwheel process, and things will
seem to be almost working, but CIDER will just hang. To avoid this problem,
add a second profile for shell users. As a bonus, throw in
rebel-readline for a massively
improved REPL experience on the shell:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.10.439"}}
:aliases {:dev {:extra-paths ["resources" "devcards"]
:extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}
devcards {:mvn/version "0.2.6"}}}
:repl {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}}}
Now you can launch a figwheel REPL on the shell with:
clojure -A:dev -A:repl
These days I like running tests in two ways: using Bruce Hauman's (yes, him
again, thanks Bruce!)
cljs-test-display to view test
results in a browser (covered here), and
Kaocha, a command-line test runner
(next section).
To use the visual test display, add the dependency and the test
directory to
deps.edn
:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.10.439"}}
:aliases {:dev {:extra-paths ["resources" "devcards" "test"]
:extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}
devcards {:mvn/version "0.2.6"}
com.bhauman/cljs-test-display {:mvn/version "0.1.1"}}}
:repl {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}}}
Then define another main in dev.cljs.edn
, and optionally, include a
:closure-defines
map to
configure the test
display:
^{:watch-dirs ["src" "test" "devcards"]
:extra-main-files {:tests {:main my-app-ns.test-runner}
:devcards {:main my-app-ns.cards}}}
{:main my-app-ns.core
:devcards true
:optimizations :none
:pretty-print true
:source-map true
:asset-path "/js/dev"
:output-to "resources/public/js/dev.js"
:output-dir "resources/public/js/dev"
:closure-defines {cljs-test-display.core/notifications false
cljs-test-display.core/printing false}}
The test runner needs to load the library and all your tests. Add
test/my_app_ns/test_runner.cljs
with the following content:
(ns ^:figwheel-hooks yahtzee.test-runner
(:require [my-app-ns.core-test]
[cljs.test :as test]
[cljs-test-display.core :as display]))
(enable-console-print!)
(defn test-run []
(test/run-tests
(display/init! "app-tests")
'my-app-ns.core-test))
(defn ^:after-load render-on-reload []
(test-run))
(test-run)
Make sure to require all your tests, and remember to add new ones in here as you
add more. After restarting fighweel, you should see a nice test report on
http://localhost:9500/figwheel-extra-main/tests.
Running Kaocha with ClojureScript is
almost as easy as running it with Clojure - you just need one additional dep. It
is also convenient to add :main-opts
to the same profile to make it easy to
run tests:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.10.439"}}
:aliases {:dev {:extra-paths ["test" "resources" "devcards"]
:extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}
devcards {:mvn/version "0.2.6"}
com.bhauman/cljs-test-display {:mvn/version "0.1.1"}}}
:repl {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}
:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "0.0-367"}
lambdaisland/kaocha-cljs {:mvn/version "0.0-16"}}
:main-opts ["-m" "kaocha.runner" "unit-cljs"]}}}
To configure Kaocha, you can (but don't have to) provide a tests.edn
file in
the root of your project:
#kaocha/v1
{:tests [{:id :unit-cljs
:type :kaocha.type/cljs
:test-paths ["test"]
:source-paths ["src"]
}]
:reporter [kaocha.report/documentation]}
Now you can run tests with Kaocha with:
clojure -A:test
The kaocha-cljs docs has more
information on how to configure and run it.
To release a ClojureScript package to Clojars, you need to
- Provide a
pom.xml
- Package the sources in a jar
- Deploy to Clojars with your username and password
tools.deps can create pom.xml
for you:
clojure -Spom
Take a look at the generated pom.xml
, and adjust as appropriate (version,
description, package name etc).
There are many ways to create a jar. I recommend using
pack. Edit deps.edn
:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.10.439"}}
:aliases {:dev {:extra-paths ["test" "resources" "devcards"]
:extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}
devcards {:mvn/version "0.2.6"}
com.bhauman/cljs-test-display {:mvn/version "0.1.1"}}}
:repl {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}
:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "0.0-367"}
lambdaisland/kaocha-cljs {:mvn/version "0.0-16"}}
:main-opts ["-m" "kaocha.runner" "unit-cljs"]}
:jar {:extra-deps {pack/pack.alpha {:git/url "https://github.com/juxt/pack.alpha.git"
:sha "90a84a01c365fdac224bf4eef6e9c8e1d018a29e"}}
:main-opts ["-m" "mach.pack.alpha.skinny" "--no-libs" "--project-path" "my-app.jar"]}}}
You should check whatever the latest stable commit is for
pack and use the corresponding sha. Also
change my-app.jar
to a suitable name. You can now create a jar with:
clojure -A:jar
We will use deps-deploy to deploy the
jar to Clojars:
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/clojurescript {:mvn/version "1.10.439"}}
:aliases {:dev {:extra-paths ["test" "resources" "devcards"]
:extra-deps {com.bhauman/figwheel-main {:mvn/version "0.2.0-SNAPSHOT"}
devcards {:mvn/version "0.2.6"}
com.bhauman/cljs-test-display {:mvn/version "0.1.1"}}}
:repl {:extra-deps {com.bhauman/rebel-readline {:mvn/version "0.1.4"}}
:main-opts ["-m" "figwheel.main" "-b" "dev" "-r"]}
:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "0.0-367"}
lambdaisland/kaocha-cljs {:mvn/version "0.0-16"}}
:main-opts ["-m" "kaocha.runner" "unit-cljs"]}
:jar {:extra-deps {pack/pack.alpha {:git/url "https://github.com/juxt/pack.alpha.git"
:sha "90a84a01c365fdac224bf4eef6e9c8e1d018a29e"}}
:main-opts ["-m" "mach.pack.alpha.skinny" "--no-libs" "--project-path" "my-app.jar"]}}
:deploy {:extra-deps {deps-deploy {:mvn/version "0.0.9"}}
:main-opts ["-m" "deps-deploy.deps-deploy" "deploy" "my-app.jar"]}}
Now you can deploy the jar with:
env CLOJARS_USERNAME=username CLOJARS_PASSWORD=password clj -a:deploy
If you add "true"
as the last :main-opts
, deps-deploy will sign your release
with GPG as well (provided you have a working GPG configuration):
["-m" "deps-deploy.deps-deploy" "deploy" "my-app.jar"]
tools.deps isn't really a build-system, although its :main-opts
will allow you
to solve most use-casess with just your deps.edn
file. However, I usually top
the whole thing off with a Makefile, which allows us to define dependencies
between targets, refresh builds when source files change, and much more.
Explaining it is outside the scope of this article though, so I'll just provide
an example for you to look at, and encourage you to learn more about this
fascinating tool if you
aren't already using it:
test:
clojure -A:test
my-app.jar: src/**/*
clojure -A:jar
pom.xml:
clojure -Spom
deploy: pom.xml test my-app.jar
clj -a:deploy
.PHONY: test deploy