When I just started to learn ClojureScript, I had hard time trying to find some tutorials which could explain how to set up a nice dev environment for ClojureScript. The good environment is extremely important especially when you just started to play around with the language, it is very useful to get immediate feedback to your changes in the code. But it’s also helpful when you already know what you do, and allows you to iterate quickly on your project.
Later, while working on Textik (check it out, or check the sources), I was able to build something close enough to the ideal environment, which could allow me to iterate very quickly. To simplify life of ClojureScript beginners, here’s a tutorial how to set up that kind of environment.
So, here is a detailed explanation what you need to do from the scratch, or you could just go and clone/fork the skeleton project on Github, if you feel more like tl;dr.
I used Vim for development, but most of this tutorial (except the Vim part) is IDE-agnostic.
To start, let’s create a new Clojure project, called ‘perfection’:
1
$ lein new perfection
It will create a skeleton of a Clojure project. Then, open project.clj file, it should look something like that:
123456
(defproject perfection"0.1.0-SNAPSHOT":description"FIXME: write description":url"http://example.com/FIXME":license{:name"Eclipse Public License":url"http://www.eclipse.org/legal/epl-v10.html"}:dependencies[[org.clojure/clojure"1.5.1"]])
Add some lines to turn the project into a ClojureScript project:
123456789101112131415161718192021222324252627
(defproject perfection"0.1.0-SNAPSHOT":description"FIXME: write description":url"http://example.com/FIXME":license{:name"Eclipse Public License":url"http://www.eclipse.org/legal/epl-v10.html"}:dependencies[[org.clojure/clojure"1.5.1"][org.clojure/clojurescript"0.0-2277"]];; <- Adding ClojureScript:jvm-opts["-Xmx1G"];; <- Sometimes ClojureScript compilation fails because available;; JVM heap space it too low. This line gives it more space:plugins[[lein-cljsbuild"1.0.3"]];; <- While you can build ClojureScript just by running;; 'cljsc', there is a waaay more convenient way -;; using cljsbuild lein plugin;; This is the list of builds 'cljsbuild' will use to compile our ClojureScript;; code into JavaScript. Here we add first build "dev", it will put resulting main;; entry for JavaScript code to resources/public/perfection.js, and all dependencies;; for it to resources/public/out:cljsbuild{:builds[{:id"dev":source-paths["src/perfection"]:compiler{:output-to"resources/public/perfection.js":output-dir"resources/public/out":optimizations:none:source-maptrue}}]})
Now, run
1
$ lein deps
And lein will install all the new dependencies we just added.
Next, delete the file src/perfection/core.clj, create a new file src/perfection/core.cljs, and put simple “Hello World” code into it:
Now we need something to run our compiled JavaScript code. Let’s create the index.html file, which will include the generated JavaScript file. Create resources/public/index.html file and add something like this to it:
1234567891011
<html><head><title>Perfection - perfect dev environment for ClojureScript</title><linkhref="css/style.css"rel="stylesheet"type="text/css"/></head><body><script src="out/goog/base.js"type="text/javascript"></script><script src="perfection.js"type="text/javascript"></script><script type="text/javascript">goog.require("perfection.core");</script></body></html>
Let’s also add some styles, create the resources/public/css/styles.css CSS file with this content:
123
body{background:silver;}
Let’s try to run compilation, via ‘cljsbuild’ lein plugin:
1234
$ lein cljsbuild auto dev
Compiling ClojureScript.
Compiling "resources/public/perfection.js" from ["src"]...
Successfully compiled "resources/public/perfection.js" in 6.829 seconds.
Now we need some server to serve our files. I usually prefer to use Python’s SimpleHTTPServer:
1
$ python -m SimpleHTTPServer 8000
Next, open a browser and go to http://localhost:8000/resources/public/index.html. Check the console, you should see “Hello World!” there.
Figwheel
Now, you have some running ClojureScript environment, which will automatically and instantly (less than for a second) recompile your files when you change any files in ‘src/perfection’ directory. Sweet. But we want more.
Meet lein-figwheel, amazing lein plugin, which will reload the changed JavaScript files after recompilation in the browser without reloading the page! That is so cool. Let’s add it to our project:
(defproject perfection"0.1.0-SNAPSHOT":description"FIXME: write description":url"http://example.com/FIXME":license{:name"Eclipse Public License":url"http://www.eclipse.org/legal/epl-v10.html"}:dependencies[[org.clojure/clojure"1.5.1"][org.clojure/clojurescript"0.0-2277"][figwheel"0.1.3-SNAPSHOT"]]);; <- Adding Figwheel:jvm-opts["-Xmx1G"]:plugins[[lein-cljsbuild"1.0.3"][lein-figwheel"0.1.3-SNAPSHOT"]];; <- Adding also a lein plugin for it;; Figwheel settings. We are going to use 'resource/public' directory as our root;; directory for serving the files by the figwheel server, and also we specify;; where our CSS styles live, so Figwheel could reload them without reloading the;; page when we change them as well!:figwheel{:http-server-root"public":port3449:css-dirs["resources/public/css"]}:cljsbuild{:builds[{:id"dev":source-paths["src/perfection""src/figwheel"];; <- adding source path where;; figwheel code will live:compiler{:output-to"resources/public/perfection.js":output-dir"resources/public/out":optimizations:none:source-maptrue}}]})
We will place the client code for figwheel not in src/perfection, but in a separate dir, because we don’t want other builds (like ‘test’ or ‘release’) to include it.
Now, create the file src/figwheel/perfecton_figwheel.cljs, and put this code there:
<html><head><title>Perfection - perfect dev environment for ClojureScript</title><linkhref="css/style.css"rel="stylesheet"type="text/css"/></head><body><script src="out/goog/base.js"type="text/javascript"></script><script src="perfection.js"type="text/javascript"></script><script type="text/javascript">goog.require("perfection.core");</script><!-- Requiring figwheel --><script type="text/javascript">goog.require("perfection_figwheel");</script></body></html>
After that, let’s shut down our python server, but run
1
$ lein figwheel dev
instead, then open localhost:3449/index.html in the browser, and check the console. It should look like:
123
Hello world!
Figwheel: trying to open cljs reload socket
Figwheel: socket connection established
Now go to src/perfection/core.cljs, and change it to something else, e.g. change “Hello World!” to “Hello World 2!”. You should see in the console, that we reloaded our files, and the new “Hello World 2!” output:
1234
Figwheel: loading files
Hello world 2!
Figwheel: loaded these files
("/out/perfection/core.js")
Figwheel will also track the changes in CSS and also reload them. Change the background in the CSS file from silver to blue (resources/public/css/style.css file):
123
body{background:blue;}
and you’ll see in the browser console:
12
Figwheel:loadedCSSfiles("/css/style.css")
Tests
I like unit tests. They give me confidence. But it really sucks to manually run them every time, and it especially sucks to run them in Clojure/ClojureScript, because you have to wait for the full JVM start, which takes several seconds. Not really perfect instant experience we are looking for. Thankfully, you can run it once, and then restart the tests on every change of the source or test files, via the same ‘lein clsbuild auto’ mechanism.
First thing, make sure you have PhantomJS installed. If not, install it with HomeBrew:
1
$ brew install phantomjs
Then, let’s add the ‘test’ build for cljsbuild, in project.clj:
(defproject perfection"0.1.0-SNAPSHOT":description"FIXME: write description":url"http://example.com/FIXME":license{:name"Eclipse Public License":url"http://www.eclipse.org/legal/epl-v10.html"}:dependencies[[org.clojure/clojure"1.5.1"][org.clojure/clojurescript"0.0-2277"][figwheel"0.1.3-SNAPSHOT"]]):jvm-opts["-Xmx1G"]:plugins[[lein-cljsbuild"1.0.3"][lein-figwheel"0.1.3-SNAPSHOT"][com.cemerick/clojurescript.test"0.3.1"]];; <- Adding lein plugin for tests:figwheel{:http-server-root"public":port3449:css-dirs["resources/public/css"]}:cljsbuild{:builds[{:id"dev":source-paths["src/perfection""src/figwheel"]:compiler{:output-to"resources/public/perfection.js":output-dir"resources/public/out":optimizations:none:source-maptrue}};; Adding the new build 'test'. Note the :notify-command setting,;; it will run the tests when the files change{:id"test":source-paths["src/perfection""test"]:notify-command["phantomjs":cljs.test/runner"perfection_test.js"]:compiler{:output-to"perfection_test.js":optimizations:whitespace}}]})
Now, let’s add some function to test (src/perfection/core.cljs):
1234567
(ns perfection.core)(.logjs/console"Hello world 2!");; Our new function(defn add[ab](+ ab))
Then, remove the currently existing test/perfection/core_test.clj, and create a new file test/perfection/core_test.cljs, which will contain our new first failing test:
Change (is (= (pn/add 1 2) 4)) to (is (= (pn/add 1 2) 3)), then save the file, and the test should rerun automatically, and now it should pass:
123456789101112
Compiling "perfection_test.js" from ["src/perfection""test"]...
SyntaxError: Parse error
Testing perfection.core-test
Ran 1 tests containing 1 assertions.
Testing complete: 0 failures, 0 errors.
Ran 1 tests containing 1 assertions.
Testing complete: 0 failures, 0 errors.
Successfully compiled "perfection_test.js" in 0.719 seconds.
W00t! Now we have an autorunning test in our app.
Some notes: PhantomJS is pretty old, it lacks a lot of features existing in modern browsers, like Function.bind or requestAnimationFrame. So, for tests you may need shims, you can write them in separate JS files, and then add to :notify-command in project.clj for the “test” build, e.g. like that:
We already have a lot, but we are not going to stop :). We would also want to quickly search docs for the functions we use from Vim, be able to jump to definitions of the functions, and even execute some code right in the browser’s context, right in the context of our running application, straight from Vim. Oh yes, that would be super cool. And that is totally possible, all thanks to famous Vim master Tim Pope and his beautiful ‘vim-fireplace’ plugin.
So, let’s first configure our app so it could accept connections from repl. We need to install brepl to it. As usual, we first need to add some changes to project.clj:
(defproject perfection"0.1.0-SNAPSHOT":description"FIXME: write description":url"http://example.com/FIXME":license{:name"Eclipse Public License":url"http://www.eclipse.org/legal/epl-v10.html"}:dependencies[[org.clojure/clojure"1.5.1"][org.clojure/clojurescript"0.0-2277"][figwheel"0.1.3-SNAPSHOT"]]):jvm-opts["-Xmx1G"]:plugins[[lein-cljsbuild"1.0.3"][lein-figwheel"0.1.3-SNAPSHOT"][com.cemerick/clojurescript.test"0.3.1"][com.cemerick/austin"0.1.4"]];; <- Adds nice support for ClojureScript REPL.;; Also has Piggieback as a dependency, which;; we need for vim-fireplace:figwheel{:http-server-root"public":port3449:css-dirs["resources/public/css"]}:cljsbuild{:builds[{:id"dev":source-paths["src/perfection""src/figwheel""src/brepl"];; <- adding source path;; where brepl code;; will live:compiler{:output-to"resources/public/perfection.js":output-dir"resources/public/out":optimizations:none:source-maptrue}}{:id"test":source-paths["src/perfection""test"]:notify-command["phantomjs":cljs.test/runner"perfection_test.js"]:compiler{:output-to"perfection_test.js":optimizations:whitespace}}]})
Same thing as for figwheel – we will place the client code for brepl not in src/perfection, but in a separate dir, because we don’t want other builds (like ‘test’ or ‘release’) to include it.
And add its requiring to resources/public/index.html:
1234567891011121314
<html><head><title>Perfection - perfect dev environment for ClojureScript</title><linkhref="css/style.css"rel="stylesheet"type="text/css"/></head><body><script src="out/goog/base.js"type="text/javascript"></script><script src="perfection.js"type="text/javascript"></script><script type="text/javascript">goog.require("perfection.core");</script><script type="text/javascript">goog.require("perfection_figwheel");</script><!-- Requiring brepl --><script type="text/javascript">goog.require("perfection_brepl");</script></body></html>
After that, restart lein figwheel dev, then run lein repl.
Now, it’s time to install the vim-fireplace plugin for Vim. I use pathogen, so I just simply put the plugin to ~/.vim/bundle/vim-fireplace.
Next, restart Vim, open src/perfection/core.cljs, and in Vim run :Piggieback 9000. It will hang for several seconds.
Then reload localhost:3449/index.html in browser.
You may see the exception in the browser console, like:
123
Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement':
Blocked a frame with origin "http://localhost:3449" from accessing a frame with origin
"http://localhost:9000". Protocols, domains, and ports must match.
but that’s totally fine, and will work anyway.
Go back to src/perfection/core.cljs in Vim, and try to run something like :Eval (js/alert 123). The alert window should appear right in the browser. Cool, huh?
You can do a lot of things with vim-fireplace, just check the docs. It converts Vim to a pretty solid ClojureScript IDE, which now is quite close even to LightTable, but with all that well-known Vim stuff you and me love so much :)
Release
Now we have everything we need for comfortable development process, but I highly recommend that you also add another build, which will generate the “release” version of the app, which will eventually go to production. It is very important to do that right from the beginning, because sometimes there are issues with “advanced” optimizations of Google Closure (which ClojureScript uses under the hood for compilation). You should really always check that your app works with :advanced optimizations, because debugging may be difficult there.
(defproject perfection"0.1.0-SNAPSHOT":description"FIXME: write description":url"http://example.com/FIXME":license{:name"Eclipse Public License":url"http://www.eclipse.org/legal/epl-v10.html"}:dependencies[[org.clojure/clojure"1.5.1"][org.clojure/clojurescript"0.0-2277"][figwheel"0.1.3-SNAPSHOT"]]):jvm-opts["-Xmx1G"]:plugins[[lein-cljsbuild"1.0.3"][lein-figwheel"0.1.3-SNAPSHOT"][com.cemerick/clojurescript.test"0.3.1"][com.cemerick/austin"0.1.4"]]:figwheel{:http-server-root"public":port3449:css-dirs["resources/public/css"]}:cljsbuild{:builds[{:id"dev":source-paths["src/perfection""src/figwheel""src/brepl"]:compiler{:output-to"resources/public/perfection.js":output-dir"resources/public/out":optimizations:none:source-maptrue}}{:id"test":source-paths["src/perfection""test"]:notify-command["phantomjs":cljs.test/runner"perfection_test.js"]:compiler{:output-to"perfection_test.js":optimizations:whitespace}};; Our release build. The main difference - it uses :optimizations :advanced.;; It also doesn't include src/figwheel or src/brepl - we don't need that in production{:id"release":source-paths["src/perfection"]:compiler{:output-to"resources/public/perfection_prod.js":output-dir"resources/public/prod-out":optimizations:advanced:pretty-printfalse:source-map"resources/public/perfection_prod.js.map"}}]})
Now create resources/public/index_prod.html, it’s going to be way shorter than our dev one:
123456789
<html><head><title>Perfection - perfect dev environment for ClojureScript</title><linkhref="css/style.css"rel="stylesheet"type="text/css"/></head><body><script src="perfection_prod.js"type="text/javascript"></script></body></html>
And run:
1
$ lein cljsbuild auto release
Now you can open http://localhost:3449/index_prod.html, and make sure the site still works (by checking for “Hello World 2!” in the console.
BTW, now if you open resources/public/perfection_prod.js, all you will see there is:
12345
;(function(){console.log("Hello world 2!");})();//# sourceMappingURL=perfection_prod.js.map
That’s the power of :advanced optimizations. It got rid of everything which is not used (all the ClojureScript libs, Google Closure libs, thousands and thousands of lines of code), and left only what matters.
Summary
So, I usually have 4 things running simultaneously:
And whenever I make a change in the source code, I’ll get updated version of the app in the browser, without page refresh, the tests will run, and the release will be built. All automagically.
That’s what I call “the perfect dev environment”.
Tips & Tricks
Paredit
You should use it. Since Clojure is Lisp, and consists of s-expressions, it has a very clear structure. Paredit allows to efficiently edit your code by manipulating s-expressions. This is when all these parentheses, which initially scare Lisp newcomers, will bring power. There is a plugin for Vim, a bit buggy, especially when you copy/paste large structures (especially when they are unbalanced), but it’s still worth it.
p and b
I found it very useful for my development process to create the function p and the macro b for debugging.
p just prints what the value it receives and returns it.
b prints what time did the execution of the nested s-expression take, and also returns the value.
They look like this:
1234
(defn p"Prints given arguments, and then returns the last one"[&values](.logjs/console(apply pr-str values))(last values))
12345678910111213141516
(defn benchmark"Prints the execution time for the given function. Accepts optional string, which will be used as a description"([f](benchmarknilf))([msgf](let [start(.nowjs/Date)result(f)](p(str (when msg(str msg": "))(- (.nowjs/Date)start)"ms"))result)))(defmacro b([f]`(tixi.utils/benchmark(fn []~f)))([msgf]`(tixi.utils/benchmark~msg(fn []~f))))
And then we can use them, like that. Imagine you have a function:
123
(let [foo2bar(+ 3foo)](+ foobar))
And you are curious about what’s the output of (+ 3 foo). So you can do:
123
(let [foo2bar(p(+ 3foo))](+ foobar))
And it will put ‘5’ in the browser console. Curious how long it takes to execute it?
123
(let [foo2bar(b(+ 3foo))](+ foobar))
‘0ms’ (obviously :)) will appear in the browser console.
It starts to really shine especially with Paredit, when you can wrap and unwrap parentheses by one keystroke.
Video
This is a little screencast I recorded how I develop Textik, using the dev environment like that:
Conclusion
I really think ClojureScript so far is the most interesting and productive way to build complex front-end projects on the web. Especially if you use it with React (and some React wrapper, like Om, Reagent or Quiescent). If you didn’t try it yet, you should definitely try, and then hopefully you’ll find this article useful :)