Have you ever wanted to run ClojureScript on node? How do you start? Can you use Leiningen? Start with
lein new node-demo and then what? Does Leiningen even know how to compile ClojureScript?
Setting up your project file manually
As it turns out, Leiningen is perfectly capable of dealing with CLJS projects. You just have to pull in ClojureScript as a dependency and setup the lein-cljsbuild plugin for compiling CLJS to JS.
But having to hack on
project.clj manually is tedious and error prone, and the
lein-cljsbuild configuration is especially difficult to write from scratch. Fortunately there's a way to cheat your way to a node-compatible project file...
(Ab)using a template
David Nolen has written a nice little Leiningen template, called mies, for kickstarting a minimal client side CLJS project. David also has a great post on using mies to get started with CLJS on the browser.
While mies is intended for client side dev and includes an
index.html file among some other browser-related features, we can take advantage of the project file generation and ignore the bits that don't apply to running on node.
Using mies with node
Let's get started.
Create a new project with mies:
lein new mies node-demo cd node-demo
Delete the unwanted html file:
Modify the project file for running on node:
Unfortunately we can't completely avoid touching the project file. Make the following changes to
:target :nodejsto the
- Remove the
:sourcemapsline from the
After the changes your
project.clj should look something like this:
Compile your ClojureSript
lein cljsbuild auto
The output looks something like this:
$ lein cljsbuild auto Compiling ClojureScript. Compiling "node_demo.js" from ["src"]... Successfully compiled "node_demo.js" in 7.911 seconds.
After the initial compilation
lein-cljsbuild will watch the CLJS source files and recompile as necessary.
When you first kick off the auto-build, it will take some time for the compiler to start. The initial compilation takes almost 8 seconds on my MacBook Air. But the subsequent compilations will be much faster (about 2 seconds on my machine).
Let's write some code
src/node_demo/core.cljs in your favourite editor.
Again the mies template has generated some code for us that isn't quite right for node.
Edit the file to look like this:
When you save the file
lein-cljsbuild should pick up the file modification and rerun the compiler for you (if not,
lein-cljsbuild process, run
lein cljsbuild clean and finally rerun
lein cljsbuild auto).
Run the program
lein-cljsbuild has finished compiling the code, run node in another terminal window:
And you should see
Hello world! on your console!
What just happened?
I'll try to briefly explain the magic incantations that we just did above.
The mies template sets
:none by default. This setting results in our CLJS code and the Google Closure files all being compiled into their own JS files. This is fine on the browser where we can use multiple
<script> tags to load any number of JS files, but on node we can't do that and running our compiled JS results in an error about a missing
The easiest solution is to change
:simple which will compile our CLJS and the Closure JS into one file,
node_demo.js, thus allowing node to load all required code.
Node doesn't support source maps by default, so we'll turn the source map generation off. Notice that the CLJS compiler is about five times faster when it's not generating source maps.
cljs.nodejs namespace is provided by ClojureScript standard library and provides some node-related helpers.
You can read the code for that namespace in
out/cljs/nodejs.cljs, but there's not much to it really: the namespace provides references to some node globals and sets up the ClojureScript print functions to use node's
sys.print facility for printing to standard out.
*main-cli-fn* var is used to define the main entry point for our program. More specifically, when we run out compiled JS with node, the CLJS runtime calls the function that
*main-cli-fn* is set to.
Go forth and hack!
I hope this short intro gets you started on running CLJS on node. There are a large number of topics that I didn't cover yet, but hopefully I'll get to them in future posts. Watch this space!
I've written a Leiningen template for generating a node.cljs project skeleton as described above.