Cheat your way to running CLJS on Node

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:

rm index.html

Modify the project file for running on node:

Unfortunately we can't completely avoid touching the project file. Make the following changes to project.clj:

  • Add :target :nodejs to the :compiler map.
  • Change :optimizations from :none to :simple.
  • Remove the :sourcemaps line from the :compiler map.

After the changes your project.clj should look something like this:

Compile your ClojureSript

Run the lein-cljsbuild plugin in order to automatically compile your ClojureScript code to JavaScript whenever the source files change:

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

Open 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, Ctrl-C the lein-cljsbuild process, run lein cljsbuild clean and finally rerun lein cljsbuild auto).

Run the program

Once lein-cljsbuild has finished compiling the code, run node in another terminal window:

node node_demo.js

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.

Optimizations

The mies template sets :optimizations to :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 goog symbol.

The easiest solution is to change :optimizations to :simple which will compile our CLJS and the Closure JS into one file, node_demo.js, thus allowing node to load all required code.

Source maps

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.

The cljs.nodejs namespace

The 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*

The *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!

Update

I've written a Leiningen template for generating a node.cljs project skeleton as described above.


Want to read more about ClojureScript on Node?
Sign up for our mailing list!

Unsubscribe at any time. No spam, ever.
comments powered by Disqus