At SourceNinja we recently expanded our offering to track Node.js, and as part of that we released a Node.js module. I love Clojure, and used this opportunity to finally take a look at ClojureScript, and specifically using Node.js as a deploy target.
There is some documenation for running ClojureScript on top of Node.js, but so far no documentation for deploying a module written in ClojureScript to npm. Think of this post as a report describing what worked, what didn’t and where we found gaps. I’m going to assume you know Clojure, and that you have installed Node.js. If you haven’t installed Node.js yet I highly recommend you use nvm, it’s the bee’s knees.
Let’s get started. Create a new project directory. You’re going to need two sub-folders, one named src, and another named lib. src is for your raw ClojureScript, the lib direcory will contain the code generated by the ClojureScript compiler.
Start out with a simple ClojureScript program
As stated in other guides, in order to make this program run with Node.js you need to specify a main by setting the value of
*main-cli-fn* to some function.
To compile the ClojureScript we use the incantation below. cljsc will take all
*.cljs files found in the src directory, compile them to js and spit the result out via stdout. We pipe the output to a js file matching the module name in the lib directory. The fact that we use the lib directory is important, because that’s where Node.js is going to look for code.
The only option which is stricly neccessary is
:target which should be set to
:nodejs. This option generates a main functions which in turn calls the function specified by
*main-cli-fn*. While the other are not required they will make this whole process go much smoother. We found that setting the optimizations level to anything other than
:simple produced unreliable code which was difficult to debug. For our purposes heavy optimization wasn’t neccessary so I didn’t invest much effort in getting it to work, give it a shot, your millage may vary.
Most of your debugging time is going to be tracing through the generated JS code, for this reason we always enable :pretty-print which results in human-readable code being generated.
At this point we have a fully functional Node.js program. When we run the generated code we should see the following.
Great it works, but every time your module is included it’s going to run whatever function is bound to
*main-cli-fn*. Leaving it unbound isn’t an option so we need to bind it to a noop function. The final form is below.
To turn this program into a module we need a couple small additions. The first is that we need to export/extern the public functions so that they are visible to node. We do this by adding a file
require("./sample.js"). The actual export is done by the next line
exports.core = sample.sample.core, this makes the sample.core namespace defined by our ClojureScript program visible to node.js. All functions declared by your clojure namespace should now be accessible.
Publishing the Module
The last addition is the
packages.json file which lives at your project root. You can find lots of documentation for this file, so I won’t spends lots of time on it, I’ll just emphasise a couple points.
Spend some time verifying which version of Node.js introduced the functionality you require. For instance we use npm programatily, but the functionality wasn’t introduced until 6.0 which is newer than the verion deployed by Heroku. Node.js is very young, and is a bit of a moving target, spending a little time up front will prevent you from being burned. Using nvm becomes invaluable when attempting to validate your module against different versions of node. Oh, and please populate the repository and license fields, it’s just part of being a good open source citizen.
At this point from your project root you can execute
npm publish and npm will walk you through creating an account and publish your module to npm.
Unit testing can be done using the built-in clojure.test API, and that seems to be the recommended method. It should be possible to test functionality via one of the Node.js frameworks such as vows, but that’s another blog post. If you want to test the integration between other Node.js programs and your newly minted module you can create a test program which requires your module and then copy your module to the
node_modules sub-directory which allows you to do pre-publish testing. This is what we do as part of our last round of testing before we publish.