Permalink
368 lines (291 sloc) 10.9 KB

Tutorial 1 - The Basics

In this first tutorial we are going to create and configure a minimum ClojureScript (CLJS) project by using the boot build tool.

Requirements

This tutorial requires java and boot to be installed on your computer.

To install java follow the instructions for your operating system. To install boot follow the very easy instructions in the corresponding section of its README.

Test the installation by issuing the boot -h command at the terminal. Then submit the boot -u command to get the latest boot updates.

NOTE 1: I strongly suggest to use Java 8. If you're using Java 7, it might be worth mentioning https://github.com/boot-clj/boot/wiki/JVM-Options#permgen-errors

Create the project structure

A minimum CLJS web project is composed of 3 files:

  • an html page;
  • a CLJS source code;
  • a boot build file to compile CLJS source code.

Even if CLJS does not dictate a specific directory structure, it's a good practice to organize your project in such a way that it will be easy for anyone, even yourself in a few months, to be able to understand the project components and its structure. Moreover, each building tool has its own idiosyncrasies, which they call defaults. The more you adhere to the defaults of the tool at hand, the less pain you will experience while managing the project.

Taking these premises into account, let's create a directory structure for our new project, named modern-cljs, by adhering as much as possible to the boot defaults.

The suggested file layout of the project is the following:

modern-cljs/
├── build.boot
├── html
│   └── index.html
└── src
    └── cljs
        └── modern_cljs
            └── core.cljs
  • modern-cljs is the home directory of the project;
  • src/cljs/ hosts CLJS source files;
  • html hosts html resources;

NOTE 2: Single segment namespace are discouraged in CLJ/CLJS. That's why we created the modern_cljs directory name. Due to Java difficulties in managing hyphen "-" (or other special characters) in package names, we substituted an underscore (_) for any hyphen (-) in corresponding directory names.

NOTE 3: Please note that the filename extension for ClojureScript sources is cljs, not clj.

Issue the following command at the terminal:

mkdir -p modern-cljs/{src/cljs/modern_cljs,html}

Let's now create the three needed files by issuing the folowing command:

cd modern-cljs
touch html/index.html src/cljs/modern_cljs/core.cljs build.boot

Hello World in CLJS

We're now going to write our very first CLJS code. Open the src/cljs/modern_cljs/core.cljs file with your preferred editor and type into it the following CLJS code:

;; create the main project namespace
(ns modern-cljs.core)

;; enable cljs to print to the JS console of the browser
(enable-console-print!)

;; print to the console
(println "Hello, World!")

Every CLJ/CLJS file must start with a namespace declaration matching a path on your disk:

modern-cljs.core <--> modern_cljs/core.cljs

The (enable-console-print!) expression redirects any printed output to the console of the browser in such a way that the (println "Hello, world!") prints Hello, World! to the console.

Minimal build.boot

We now need a way to compile core.cljs to JS and link it to the index.html page.

First, open the file html/index.html and edit it as follows:

<!doctype html>
<html>
  <head>
    <title>Hello, World!</title>
  </head>
  <body>
    <script src="main.js"></script>
  </body>
</html>

Note that there are no references to CLJS files. We only added a <script> tag to link the index.html page to the main.js file. This JS file will be generated by the boot building tool while compiling the above core.cljs source code.

To compile the core.cljs file, we need to configure the boot command by editing the build.boot file, which is just a regular CLJ file with a different extension:

(set-env!
 :source-paths #{"src/cljs"}
 :resource-paths #{"html"}

 :dependencies '[[adzerk/boot-cljs "1.7.228-2"]])

(require '[adzerk.boot-cljs :refer [cljs]])

Pretty minimal! The set-env! function sets :source-paths and :resource-paths options to the corresponding values of the project structure as we have created above. Then it injects the boot-cljs compilation task as the only explicit dependency of the project by adding it to the :dependencies keyword.

Note that even if we did not include any clojure and clojurescript dependencies, boot will be able to automagically download the corresponding releases it knows to work well with it.

Finally, the require form makes the cljs task visible to the boot command.

If you now run the boot -h command from the terminal, you'll see that the cljs task is now available to boot.

boot -h

...

Tasks:   ...
         target                      Writes output files to the given dir...
         ...
         zip                         Build a zip file for the project.

         cljs                        Compile ClojureScript applications.

...

Do `boot <task> -h` to see usage info and TASK_OPTS for <task>.

You now may want to have more information about the cljs task by issuing the following command:

boot cljs -h
Compile ClojureScript applications.
...
Available --optimization levels (default 'none'):
...

As you see, the default optimization directive for the CLJS compiler is none. Another tutorial will explain the different CLJS compilation optimizations (i.e. none, whitespace, simple and advanced). At the moment stay with none, which is commonly used during development cycles. Let's see boot cljs at work:

boot cljs
Writing main.cljs.edn...
Compiling ClojureScript...
• main.js

The cljs task compiled your CLJS code by producing a main.js JS file and you now know why we called the JS file included in the <script> tag of the index.html page main.js: just to adhere to an easy default.

That said, if you look for it the directory structure of the project, you'll be surprised to not find it:

tree
.
├── build.boot
├── html
│   └── index.html
└── src
    └── cljs
        └── modern_cljs
            └── core.cljs

4 directories, 3 files

The fact is that by default boot does not create an output file if you don't explicitely instruct it to do so. Let's see the command line help for the target predefined task:

boot target -h
Writes output files to the given directory on the filesystem.

Options:
  -h, --help      Print this help info.
  -d, --dir PATH  Conj PATH onto the set of directories to write to (target).
  -m, --mode VAL  VAL sets the mode of written files in 'rwxrwxrwx' format.
  -L, --no-link   Don't create hard links.
  -C, --no-clean  Don't clean target before writing project files.

Interesting. The target predefined task of boot is able to write the output of a task (e.g. cljs) to a given directory. The above help doesn't tell you, but if you do not specify a directory it writes the output file to the a default target subdirectory of your project's home directory, as you can verify as follows:

boot cljs target
Writing main.cljs.edn...
Compiling ClojureScript...
• main.js
Writing target dir(s)...
tree
.
├── boot.properties
├── build.boot
├── html
│   └── index.html
├── src
│   └── cljs
│       └── modern_cljs
│           └── core.cljs
└── target
    ├── index.html
    ├── main.js
    └── main.out
        ├── boot
        │   └── cljs
        │       ├── main312.cljs
        │       ├── main312.cljs.cache.edn
        │       ├── main312.js
        │       └── main312.js.map
        ├── cljs
        │   ├── core.cljs
        │   ├── core.js
        │   └── core.js.map
        ├── cljs_deps.js
        ├── goog
        │   ├── array
        │   │   └── array.js
        │   ├── asserts
        │   │   └── asserts.js
        │   ├── base.js
        │   ├── debug
        │   │   └── error.js
        │   ├── deps.js
        │   ├── dom
        │   │   └── nodetype.js
        │   ├── object
        │   │   └── object.js
        │   └── string
        │       ├── string.js
        │       └── stringbuffer.js
        └── modern_cljs
            ├── core.cljs
            ├── core.cljs.cache.edn
            ├── core.js
            └── core.js.map

17 directories, 27 files

A lot of stuff. We're not digging into it right now. At the moment we're only interested in noting a few things:

  • the original directory structure living in html and src is untouched
  • everything, even the index.html resource, has been generated into the new target directory.

Considering that boot is under continuous development, I strongly suggest you to pin the current stable release in your project by creating a new boot.properties file as follows:

boot -V > boot.properties

which should now have the following content:

cat boot.properties
#http://boot-clj.com
#Thu Mar 09 14:56:52 CET 2017
BOOT_CLOJURE_NAME=org.clojure/clojure
BOOT_CLOJURE_VERSION=1.7.0
BOOT_VERSION=2.7.1

Visit index.html

boot uses a pretty neat approach in taking apart the input of the project from the corresponding output generated by its tasks. You'll never see an input file from the :source-paths and the :resource-path original directories be modified by any boot task. Aside from internally generated temporary directories, everything happens in the explicit target directory.

Open a browser and visit the local target/index.html file. Now open the console in your Development Tool (e.g. Chrome Development Tool). If everything went ok, you should see "Hello, World!" printed at the console.

Next Step - Tutorial 2: Immediate Feedback Principle

In the next tutorial we're going to adhere as closely as possible to the Bret Victor Immediate Feedback Principle to build a very interactive development environment.

License

Copyright © Mimmo Cosenza, 2012-2015. Released under the Eclipse Public License, the same as Clojure.