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:
- 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.
- They are outdated.
- They are not well explained. For sake of simplicity, they assume a lot of things. But I want to know what I’m writing!
- 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:
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.
Well, now we can start to write some sweet HTML.
Step 4: Writing HTMLIt's time! Go in the resources/public folder and write a small index.html file!
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:
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
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.
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.