cb codes

Setting Up A Node.cljs Project With Leiningen and NPM

Before we begin, make sure you can start a node.cljs repl first

One important thing I want to highlight is that you should have a stable version of node 0.12.X as the linked wiki page says. Additionally, you want to make sure you have npm installed so you can install node packages later on. Finally, I'm also assuming that you already have Clojure and Clojurescript setup with Leiningen.

Let's begin.

Start by making a new leiningen project. 

lein new node_project

And then move into that directory.

Start by editing your project.clj file to look something like this:

(defproject node_test "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.7.0-RC1"]
                 [org.clojure/clojurescript "0.0-3269"]
                 [org.clojure/core.async "0.1.346.0-17112a-alpha"]]
  :profiles {:dev
             {:dependencies [[com.cemerick/piggieback "0.2.1"]
                             [org.clojure/tools.nrepl "0.2.10"]]
              :plugins [[lein-cljsbuild "1.0.5"]]
              :source-paths ["src" "dev"]
              :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}}}
  :clean-targets
  [[:cljsbuild :builds 0 :compiler :output-to]
   :target-path
   :compile-path]
  :cljsbuild {:builds
              [{:id "dev"
                :source-paths ["src"]
                :compiler {:output-dir "out"
                           :output-to "index.js"
                           :optimizations :none
                           :source-map "src-maps"
                           :target :nodejs}}]})

It's very important that you use versions of Clojure and Clojurescript greater than or equal to the ones I've listed. I've ran into a lot of issues trying a setup like this before finding one that worked. It was mostly due to old versions of piggieback and Clojurescript.

I added the piggieback related dependencies in dev for the node.js REPL we're going to be using later. The clean-targets are for the command 

lein clean

Because cljsbuild deprecated its clean function.

A few more things to note:

We're using no optimizations and targeting node.js. Having optimizations set to :simple, I ran into issues with core.async macros. 

Next, we need to setup the node side of the project. Inside the same project folder, run:

npm init

You can pretty much press enter for every single prompt, except the entry point one. There you want it to be named app.js, not index.js as we specified in the project.clj. We can't run index.js directly due to optimizations being set to none, so cljsbuild will output multiple files.

Next, you can install a web framework using npm like express:

npm install --save express

Now that we have Express.js installed, we can just make a simple hello world web application in clojurescript.

Rename src/node_test/core.clj to src/node_test/core.cljs.

Then in the file put this:

(ns node-test.core
  (:require [cljs.nodejs :as node]))

(def express (node/require "express"))

(def app (new express))

(defn -main
  []
  (.listen app
           3000
           (fn []
             (js/console.log "App Started at http://localhost:3000"))))

(set! *main-cli-fn* -main)

Then you want to compile your clojurescript to javascript with:

lein cljsbuild auto

Now that we have our index.js file, we need to make the file for node to run the application.

Make a file called app.js in the root of your project folder with this as its contents:

// Bootstrap node.js
require('./out/goog/bootstrap/nodejs');

// Our app compiled by cljsbuild
require('./index.js');

// The core of our code
require('./out/node_test/core');

// The core of cljs
require('./out/cljs/core');

// Run main
// NOTE: Dashes in namespaces are replaced by underscores.
node_test.core._main();

If you've changed anything, replace the paths with the correct one.

Now, you can start the app with node.js:

$ node app.js
App Started at http://localhost:3000

And if you visit that url, you'll receive an error message about not being able to GET /. That's fine, we didn't specify any routes.