Mastodon Icon RSS Icon GitHub Icon LinkedIn Icon RSS Icon

The definitive guide to start with ClojureScript

Yesterday, while my working machine was crunching tons of numbers, graphs and maps to produce some (hopefully) meaningful data for a research work, I was looking for a simple guide to build a simple web page with ClojureScript. Unfortunately, I was not able to find something that was at the same time straightforward, simple, minimal and explained! So, I decided to write something by myself to avoid my pain to some future young clojurist. The main problem with the guides I’ve found online were:

  1. They didn’t use Leiningen or any other build system. Now, this can also be good, but Leiningen is, in practice, a standard for Clojure developer, so I’d like to learn how to setup a project as soon as possible.
  2. They are outdated.
  3. They are not well explained. For sake of simplicity, they assume a lot of things. But I want to know what I’m writing!
  4. They are not simple. I don’t want to write a full featured web application with a lot of dependencies and plugins! I want an hello world!

So, I hope to cover all this points. Let’s start!

Step 1: Leiningen

The first thing to do is to check if Leiningen is correctly installed on your system. To do that just write lein –version . If you receive a message with the version number, congratulation, you have already installed Leiningen! Otherwise, you can just follow the super-easy steps on the Leiningen website! Note that those steps are not super-easy on Windows, mostly because you have to get  Wget for windows or Curl for windows, but if you chose Curl you have to install OpenSSL on your system, and all these things are not clearly visible on the homepage, and so… whatever. Anyway! After all this you should be able to run lein self-install  flawlessly and be able to complete your installation! At this point you should be happy, because it’s all downhill from here.

Step 2: Create a new project

Now that we have the power we can create a new “empty” project. According to my personal taste, I think that Leiningen by default tends to generate too many files for an empty project, but ok.  The shamanic command is lein new cljstest  when you can change cljstest  with the desired name of your project. This command will create 8 files and 6 folders, many of which are unnecessary for now. Never mind. Keep going. At the end, you should have something like this:

Step 3: Configure the project

The core of Leiningen is the project.clj file. In this file we can set the project dependencies, the Leiningen’s plugins, the building information (such as optimization level, output folder, etc…), literally everything. As a consequence, you can easily imagine that this is where you have to concentrate all your attention. What we want is a file that looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
(defproject cljstest "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.6.0"]
                 [org.clojure/clojurescript "0.0-2850"]]

  :jvm-opts ["-Xmx1G"] 

  :plugins [[lein-cljsbuild "1.0.4"]] 

  :cljsbuild {
    :builds [{:id "dev"
              :source-paths ["src/cjstest"]
              :compiler {
                :output-to "resources/public/js/test.js"
                :output-dir "resources/public/out"
                :optimizations :none
                :source-map true}}]})

If you copy this file as it is, you can probably be able to run your project BUT you have learned nothing. So, we will look at this file line by line, to understand well how it is done.

Line 1. All the project.clj files start with a defproject directive. This is used to specify the name of the project and its version. Additionally, is possible to specify  a “group-id” for the project, for instance (defproject org.example/sample "1.0.0-SNAPSHOT" ... where org.example  is  the group-id.

Line 2, 3 and 4 are old classics. Project description, URL and license. There is no much to say about it. Then on line 5 we have :dependencies . As you can imagine, this is where you can specify a list of dependencies for our project. For now, there are only two standard dependencies: clojurescript and clojure (of  course).

Line 9: Now we want to increase the max Java Virtual Machine memory. This is a tip I’ve got from “The Ancients”. I have never had this problem but I have also never compiled a big Clojure project, so…

Line 11! The :plugins is used to add plugins to Leiningen. In particular, we want Leiningen to be able to run cljsc  and this is what this plugin does!

Line 13 is where the config files becomes more important! The :cljsbuild  keyword is used to specify how the source code have to be built (and where). For now we will use only a build option, the :dev  one. First we specify an ID and the options, then we start to use put building parameters. The most important one a: :source-paths , who specifies where the source code is, and :output-to , who specifies where the output will be produced. The keyword :output-dir  is where all the builded dependencies will go. I’m not sure if this is needed, but, in any case, let’s use it. We decided to put the output in the resources/public/js/test.js file and the building material in the out folder. A little spoiler: the resource/public folder will be the root of our website. So we can use any folder inside “public” provided that than we use the right path in the HTML.

Important thing! The :source-map is used to let us debug ClojureScript code inside the browser (just as JavaScript). This parameter takes a boolean id optimizations are turned off and a path to a file if optimizations are on! Not really intuitive indeed.

Well, now we can start to write some sweet HTML.

Step 4: Writing HTML

It's time! Go in the resources/public folder and write a small index.html file!
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<html>
  <head>
    <title>Whoooo a ClojurePage!</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <script src="out/goog/base.js" type="text/javascript"></script>
    <script src="js/test.js" type="text/javascript"></script>
    <script type="text/javascript">goog.require("cljstest.core");</script>
  </body>
</html>

The css/style.css is completely optional. You can remove this line if you don’t want to write a CSS file now. Then, there are the JavaScript includes. The first one is Goog, the Google Clojure Library. Why we need it? Well, it provide some fancy stuff to load namespaces and mix together JavaScript and compiled ClojureScript code. I don’t know if it is possible to avoid this inclusion, but for now, keep it. The second include is our library! Wiiii! The third one is Goog invoked over our namespace.

Well, what this page does? This will be specified by our ClojureScript code. Well, it’s time to write our first ClojureScript code.

Step 5. Write the code

Finally ClojureScript code! Quick! Go in the cjstest\core.cljs  file (if you don’t have it, create it) and write two simple lines:

1
2
3
(ns cljstest.core)

(.log js/console "Hello world!")

A boring Hello world! This code will just write “Hello world” on your JavaScript console. Nothing exciting, but it is not time to write fancy stuff, just to make everything works.

Step 6. Compile and Run

It is the  Moment of Truth.  If you did everything correctly (and if you don’t, it is my fault, so please, tell me) you should be able to compile your code. How? Simple:

lein deps
lein cljsbuild once dev

If you are lucky, this will generate all the required files. We can now launch our test server (for instance you can use python with python -m http.server 8080 (or python -m SimpleHTTPServer 8000 for older version of python) from the public folder. Then, we can go to localhost:8080 and see our webpage.  That is completely white! Remember: we are writing on the JavaScript console.

Additional Notes

There are a couple of problem with the magic combination between version numbers. With some unfortunate combination, goog will not be downloaded. Some other times you have to manually delete output files in order to have a successful compilation. I really don’t understand how this works. I’m not a ClojureScript expert, any enlightenment on the topic will be appreciated.

And now?

This is a very basic setup. Next, we want to see how to build a  backend (running over node.js) or more complex plugin and dependencies. But all this is for another time.