ClojureScript is a bit of an awkward one to get into. If Clojure isn't your natural language then you may well find the clojarian setup and the long compilation times a bit of a frustration. Even if you're accustomed to the REPL based development approach that fits so well with Clojure there is still a certain amount of friction to be had with ClojureScript.
Recently a strong desire to play with and better understand core.async
in ClojureScript I decided to try and put together a lightweight workflow. In doing so I ended up producing a Clojure library (primrose) and a Leiningen plugin (lein-cooper) to better support this workflow.
It's probably a sure sign that you are doing something wrong when you feel you need to create a bunch of developer focused tools to support a workflow for an already established ecosystem but you gotta do, what you gotta do.
Here goes.
Starting point
ClojureScript projects are still Clojure projects and so there are few bits and pieces that you need to wire up a basic empty project. You've got your project.clj
, the lein-cljsbuild
plugin for compiling ClojureScript to JavaScript and (at the very least) an index.html
for bringing everything together.
Thankfully David Nolen has you covered. Mies is a very basic ClojureScript project template so getting up and running is a matter of,
lein new mies <project name>
cd <project name>
At this point you are good to go. To compile the ClojureScript you can run,
lein cljsbuild once
If you open index.html
and view the developer console you should see some predictable output. Alternatively if you run,
lein cljsbuild auto
The plugin will watch your project sources and recompile everything when changes are made. This is significantly faster that manually compiling every time as compilation is incremental. So instead of 10s of seconds for each compile you'll be getting sub-second compilations.
Ajax
When you view HTML files via file://
you won't be able to, at least by default, make ajax calls (due to security concerns such as Same-origin policy).
To fix this you'll want to serve your index.html
from a web server. You could go with python -m simplehttpserver
as many often do but if you want to keep everything under the same roof (Clojure/Leiningen) then I'd recommend lein-simpleton
- a Leiningen plugin for serving static files from the current directory. If you add lein-simpleton
to the projects project.clj
file,
:plugins [[lein-cljsbuild "1.0.2"]
[lein-simpleton "1.3.0"]]
You can now run lein simpleton <port>
to start serving static files (your index.html
). Alternatively if you have actual server side code in the same project you can replace lein-simpleton
with lein-ring
.
Two for One
So now if you run both
lein cljsbuild auto
lein simpleton <port>
You have a dynamic(ish) environment for working and testing your ClojureScript work.
Problem is I don't like having multiple windows open for long running processes and we now have 2. Ruby has a nice answer for this which plays very well with Heroku - Foreman. Foreman takes a file (Procfile
) that contains a list of named command line processes and runs them. These processes are supposed to be long running (like simpleton
and cljsbuild auto
) and Foreman merges the output of these files into a single stream that makes development and debugging a bit easier.
Again if you want to keep this sort of thing all under one roof I've thrown together a simple plugin for ingesting a Procfile
and doing what Foreman does - in Leiningen.
lein-cooper
lein-cooper
gives you that Foreman feeling. So lets add this as a plugin.
:plugins [[lein-cljsbuild "1.0.2"]
[lein-simpleton "1.3.0"]
[lein-cooper "0.0.1"]]
Now we can create a Procfile
at the root of our project to run our two commands.
web: lein simpleton
cljs: lein cljsbuild auto
Now if we run lein cooper
we will have single call to start up our dynamic(ish) environment for working with ClojureScript. Once you are done just CTRL-C
the terminal and everything will shutdown cleanly.
Summing Up
So we now have a very basic project setup that will allow us to tinker with ClojureScript, make Ajax calls, edit code and cleanly automate away all the stopping and restarting and problematic debugging. It's not quite the REPL driven style of a typical Clojure project but it's minimal enough to try a few smaller scale things.