Learning ClojureScript

4.8 (4 reviews total)
By W. David Jarvis , Rafik Naccache , Allen Rohner
    What do you get with a Packt Subscription?

  • Instant access to this title and 7,500+ eBooks & Videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Free Chapter
    Getting Ready for ClojureScript Development
About this book

Clojure is an expressive language that makes it possible to easily tackle complex software development challenges. Its bias toward interactive development has made it a powerful tool, enabling high developer productivity.

In this book, you will first learn how to construct an interactive development experience for ClojureScript.. You will be guided through ClojureScript language concepts, looking at the basics first, then being introduced to advanced concepts such as functional programming or macro writing. After that, we elaborate on the subject of single page web applications, showcasing how to build a simple one, then covering different possible enhancements. We move on to study more advanced ClojureScript concepts, where you will be shown how to address some complex algorithmic cases. Finally, you'll learn about optional type-checking for your programs, how you can write portable code, test it, and put the advanced compilation mode of the Google Closure Compiler to good use.

Publication date:
June 2016
Publisher
Packt
Pages
320
ISBN
9781785887635

 

Chapter 1. Getting Ready for ClojureScript Development

ClojureScript's promise is to bring the expressiveness and agility of the Clojure programming language to JavaScript developers. Having such power at hands means that teams working on single page applications—and on Node.js services as well—more productivity and less frustration.

But to be able to take complete advantage of this platform, we must grasp its inner mechanisms and, sometimes patiently, work our way towards the perfect ClojureScript live-coding environment. In this chapter, we'll cover the material necessary to achieve this objective.

We'll begin by studying the ClojureScript ecosystem, focusing on its compiler internals and talking about the Read-Eval-Print-Loop (REPL) it offers. We'll then present some alternative building blocks that make it possible to expose interactive ClojureScript development work-flows through third-party tools. We'll finally leverage all of this knowledge in order to build full-fledged, integrated, and interactive ClojureScript development environments. To get started with adopting this approach, we'll discuss the following:

  • Getting familiar with the ClojureScript ecosystem

  • Live coding ClojureScript on top of nREPL with PiggieBack and Weasel

  • Live coding ClojureScript with Figwheel

  • Setting up Emacs for ClojureScript development

 

Getting familiar with the ClojureScript ecosystem


At the heart of the ClojureScript's ecosystem lies the compiler. In this section, we'll gain an insight into its internals: what is its underlying architecture, how does it work, and how can its functioning be tweaked in order to allow for leaner ClojureScript development?

Inside the ClojureScript compiler

The ClojureScript compiler is a piece of Clojure software packaged as a JAR along with Clojure itself, so the package is self-contained and can be manipulated easily. As such, the ClojureScript compiler requires the JVM for its operation. Currently, as ClojureScript developers have baked in the compiler, among many other things, an integration mode for Nashorn, the Java 8 embedded JavaScript engine, they recommend using the same version of the JVM. But, Java 7 is sufficient for the sole operation of the compiler.

Note

A bootstrapped version of ClojureScript, that is, one that uses pure ClojureScript for compilation, has recently been released. Cljs-bootstrap ( https://github.com/swannodette/cljs-bootstrap ), at the time of writing this, is still a work in progress, and offers worse performance than the JVM mainstream compiler. Besides, bootstrapped ClojureScript does not allow for the advanced compilation flags that its JVM counterpart offers.

At its most stripped down definition, the compiler accepts ClojureScript code, that is, mainly s-expressions obeying some Clojure subset semantics, and emits JavaScript artifacts that are passed on to the Google Closure Library ( https://developers.google.com/closure/ ) in order to get it "polished".

The Google Closure Library is a set of JavaScript optimizing tools open sourced by Google, which it uses to support the development of its JavaScript-rich applications, such as Gmail or Maps. Using this library has the following benefits:

  • It abstracts away the effort of managing inconsistencies across the many JavaScript engines of the market.

  • It takes advantage of the Google Closure's complete program optimization with features such as JavaScript minification or dead code elimination.

  • It exposes the namespace functionality, which is otherwise unavailable in Vanilla JavaScript. Actually, a ClojureScript namespace maps to a Google Closure namespace.

Now, let's see the ClojureScript compiler in action. To understand its fundamentals, we won't use any build-automation tooling such as Leiningen for the moment, though we'll for sure need it to construct our tooling later on. Let's begin by downloading the latest release of the compiler (1.7.48 as of the time of writing):

Note

Throughout this book, we assume that you are working on a POSIX compatible system, such as Linux or Mac OS X.

Create a new directory for your first project, label it cljs_first_project, and then download into it the compiler JAR:

mkdir cljs_first_project; cd cljs_first_project
wget \
https://github.com/clojure/clojurescript/releases/download/r1.7.48/cljs.jar

Once the compiler JAR is downloaded, you'll need to create a source directory and the path for your first namespace inside the project folder:

mkdir -p src/cljs_first_project; cd src/cljs_first_project

Now it's time to write your first ClojureScript namespace, which must conform to the path we're currently in (note that you should replace the underscores with dashes in the directory name). Type the following code in a file named core.cljs:

(ns cljs-first-project.core) 
(js/alert "Hello world!") 

This code just declares a namespace and the only operation that our program does is showing an alert popup with the familiar "Hello World!" greeting.

Now with the ClojureScript compiler being a Clojure library, we must write some Clojure code in order to trigger the building of the ClojureScript code we just wrote. Create a Clojure file at the root of our project (at the same level as the /src directory) and label it build.clj with the following Clojure code in it:

(require 'cljs.build.api) 
(cljs.build.api/build "src" {:output-to "out/main.js"}) 

Building ClojureScript is a matter of requiring the cljs.build.api namespace and then launching the build function that takes two arguments. The first argument is where to look for the ClojureScript source code to build, that is the src directory in our case, and second one is where to output the result JavaScript; out/main.js as far as this example is concerned.

With this helper Clojure program under our belt, we can launch the ClojureScript compilation process. It is about launching the embedded Clojure from the JAR we downloaded and passing to it the build program we just wrote. When we run Clojure this way, we make sure that the ClojureScript facilities are loaded, especially the cljs.build.api namespace. To be able to achieve this, we'll have to add the JAR we downloaded as well as the src directory to the classpath when we invoke Clojure with the help of the java command:

java -cp cljs.jar:src clojure.main ./src/build.clj

After you've run this command, you'll notice that an out directory containing our target main.js file has just been created. In order to launch the output JavaScript artifact, we'll need an HTML page, which when loaded into our browser will greet us with a popup. On our HTML page, we must surely load the generated main.js file, but we must also bootstrap the Google Closure Library by loading the out/goog/base.js script.

Also, note that the main.js file only contains a description of the different namespaces' dependencies managed by the Google Closure Library and no logic of execution. So, we must explicitly set an entry point to our program by telling the Google Closure Library to require a namespace to start with, and that's our cljs_first_project.core namespace (note how the dashes got transformed to underscores in the HTML page). Here's what the HTML page, which we'll store under the greet.html file, at the root folder of your project, looks like:

<html> 
  <body> 
    <script type="text/javascript" src="out/goog/base.js"></script> 
    <script type="text/javascript" src="out/main.js"></script> 
    <script type="text/javascript"> 
      goog.require("cljs_first_project.core"); 
    </script> 
  </body> 
</html> 

Accessing this page from your browser greets you with a JavaScript alert popup. Congratulations! You've successfully written and compiled your first ClojureScript program!

There are more advanced ways to work with the architecture of the build process. For example, to get rid of all the goog requires in your HTML page, you can tell the compiler in your Clojure build program which namespace should be set as an entry point, as follows:

(require 'cljs.build.api) 
(cljs.build.api/build "src" 
  {:main 'cljs-first-project.core 
    :output-to "out/main.js"}) 

This lets you strip the necessary script declarations in your greet.html page down to the following:

<html> 
  <body> 
    <script type="text/javascript" src="out/main.js"></script> 
  </body> 
</html> 

Another way to optimize the build process is to set the auto-build of your ClojureScript code on. The compiler can be triggered to be on the watch mode, thus recompiling the output JavaScript as soon as it observes any changes in the src directory. Set your Clojure build program to use the watch function instead of build, as shown here:

(require 'cljs.build.api) 
 
(cljs.build.api/watch "src" 
  {:main 'first-cljs-project.core 
    :output-to "out/main.js"}) 

We've taken quite a deep dive inside the compiler. But, to be able to keep the promise of bringing agile development to JavaScript land, ClojureScript ought to offer a REPL to its users, as any decent lisp would do. Let's discover how ClojureScript addresses this matter in the next section.

Working with the ClojureScript REPL

ClojureScript comes bundled with REPL support for the browser, Node.js, Rhino, and Nashorn. The REPL functionality can be triggered through a call to the repl function from the cljs.repl namespace present in the ClojureScript JAR. Just as we did for the building process, we must create a REPL launching Clojure program. In this program, we begin by building our project and then launch the interactive REPL session. Create a repl.clj Clojure program containing the following listing:

(require 'cljs.repl) 
(require 'cljs.build.api) 
(require 'cljs.repl.browser) 
 
(cljs.build.api/build "src" 
  {:main 'cljs-first-project.core 
    :output-to "out/main.js" 
    :verbose true}) 
 
(cljs.repl/repl (cljs.repl.browser/repl-env) 
  :watch "src" 
  :output-dir "out") 

Here, we'll build a REPL with evaluation on the browser, as we've used the cljs.repl.browser namespace. Note how we set the :watch option, so our REPL automatically gets fresh versions of the output JavaScript, providing for interactive ClojureScript code evaluation. The :output-dir directive tells the REPL where to look for generated artifacts so that they can be loaded into the relevant evaluation environment. As the interactive evaluation session goes, output of the compilation goes into out/watch.log, so we can follow along what's going on while the code interacts with the REPL.

Now, you must set a connection to the REPL inside your ClojureScript program, core.cljs. Once built, the resulting JavaScript program will stay in tune with the REPL environment, by pushing to the browser any changes made to the ClojureScript source:

(ns cljs-first-project.core 
  (:require [clojure.browser.repl :as repl])) 
 
(defonce conn 
  (repl/connect "http://localhost:9000/repl")) 
 
(js/alert "Hello world!") 

The connection has been defined with the defonce parameter to make sure that the same connection is used across the many builds that will occur while the user interacts with the REPL and triggers a new build per evaluation.

Now, launch the REPL, preferably using the rlwrap command, so the display on the terminal is properly rendered:

rlwrap java -cp cljs.jar:src clojure.main repl.clj

Be patient while the first build, involving the construction of the connection to the REPL, is completed. When it completes, you'll see the Waiting for browser to connect message in your terminal. Once you see this message, point your browser to the HTML page we prepared before (greet.html) now through http://localhost:9000/greet.html. Accept the first greeting popup and go back to your terminal; you'll see the following output:

Watch compilation log available at: out/watch.log 
To quit, type: :cljs/quit 
cljs.user=>  

Type another greeting to see if it gets automatically executed in your browser. Type in your REPL the following:

cljs.user=> (js/alert "Hello World From REPL!") 

You'll see new greetings from the REPL interactively popping up without hitting refresh on your browser:

So far, were able to come up with a ClojureScript REPL that empowered us to interact with the browser. But, we are far from having a full-fledged development environment yet; the terminal through which we are coding is quite limited, and we lack several essential features such as code completion, syntax coloring, source code exploration, refactoring, or version control management to name a few. We need a much more complete and fluid coding experience and that's what we will strive to achieve in the next sections. We'll begin by exploring the two most promising facilities that permit text editors or integrated development environments to take advantage from the ClojureScript REPL. Then, we'll showcase two Emacs setups based on those facilities-one based on CIDER and another one backed byinf-clojure.

 

Live coding ClojureScript on top of nREPL with Piggieback and Weasel


Network REPL (nREPL) ( https://github.com/clojure/tools.nrepl ) is a Clojure library designed primarily to offer remote Clojure code evaluation. It follows a client-server architecture in which the server exposes the REPL functionality by responding to client code evaluation queries. nREPL has been created to offer a means to the makers of development tools to connect and explore running Clojure environments in a way that is agnostic to the platform these tools may be running on.

Many prominent Clojure development tools rely on nREPL to implement their functionality. In fact, CIDER on Emacs or Cursive on Intellij IDEA, the most used tools for developing Clojure at the time of writing this, both rely on nREPL (CIDER relying more on nREPL for code introspection than Cursive).

Naturally, ClojureScript also benefits from nREPL.

Piggieback ( https://github.com/cemerick/piggieback ) is an nREPL middleware that seizes this opportunity. Piggieback hooks into nREPL and changes its operation so it can evaluate and load ClojureScript code, while being understandable to the vast majority of existing Clojure tooling. In the next section, we'll take a closer look at Piggieback.

Working with Piggieback

Piggieback changes the nREPL behavior to turn it into a ClojureScript remote evaluation environment. It does this by functioning as a middleware layer on top of nREPL. Let's see how to do this using Leiningen.

Note

Leiningen, the Clojure build tool, can be downloaded from  http://leiningen.org/ or https://github.com/technomancy/leiningen . You can also find detailed instructions and documentation on how to install and get started with Leiningen on both the sites. We'll be using Leiningen a lot in this book, so you'll definitely want to install it.

We'll use this project management tool to create a new Clojure project:

lein new piggieback_project

Then, we'll have to modify the project.clj file (where project-specific configuration is kept) in order to turn our project into a ClojureScript one, adding ClojureScript as a dependency:

(defproject piggieback_project "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.8.0"]
                 [org.clojure/clojurescript "1.8.51"]])7.228"]]) 

Now, it is time to add Piggieback into the mix. Add its dependencies and its middleware to the project.clj file:

(defproject piggieback_project "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.8.0"]
                 [org.clojure/clojurescript "1.7.228"]] 
  :profiles {:dev {:dependencies [[com.cemerick/piggieback"0.2.1"]

                                 [org.clojure/tools.nrepl"0.2.10"]]
  :repl-options {:nrepl-middleware
                 [cemerick.piggieback/wrap-cljs-repl]}}})

Piggieback is just an entry point to ClojureScript REPLs. Once we hook into our nREPL, we can operate in the same manner as we did when we were working with the REPL bundled with the compiler. Once again, we'll need to prepare a ClojureScript namespace that can define the connection to the REPL, and an HTML page that we can load into our browser. Create a core.cljs file under the src/piggieback_project folder:

(ns piggieback-project.core 
  (:require [clojure.browser.repl :as repl])) 
(defonce conn 
  (repl/connect "http://localhost:9000/repl")) 

Next, copy the greet.html file we wrote previously into the root of your new project.

We can now start an nREPL session. Type the following at the root of your project:

lein repl

In order to launch interactive code evaluation and loading in the browser, we must use some ClojureScript namespaces as to be able to hook Piggieback into our running nREPL session. We'll need to build the ClojureScript program we wrote at least once, so first we'll have to load the compiled JavaScript code to connect to the nREPL. Issue the following commands at the running REPL prompt:

user=> (require 'cljs.build.api)
user=> (cljs.build.api/build "src"
  #_=>   {:main 'piggieback-project.core
  #_=>    :output-to "out/main.js"
  #_=>    :verbose true})
user=> (require 'cljs.repl.browser)

We are now ready to hand over the code evaluation responsibility to Piggieback. Type the following:

user=>  (cemerick.piggieback/cljs-repl (cljs.repl.browser/repl-
      env))

You'll see that a JavaScript compilation process has been launched. It is our core.cljs file being compiled, and constructing a connection to the REPL. This will be our nREPL session—accessed through the JavaScript artifacts loaded via the greet.html page.

Once this operation has finished, you'll get the Waiting for browser to connect message that we previously encountered during our interaction with the REPL built by the ClojureScript compiler. As soon as you point your browser to http://localhost:9000/greet.html, you'll notice that the prompt has changed; it now shows the following:

cljs.user=>_

This means that the nREPL session has started accepting code to be compiled. The compiled JavaScript will automatically be evaluated on the connected browser. Try to generate a Hello World! popup from the browser from Piggieback this time:

cljs.user=> (js/alert "Hello World from Piggieback!")

And your browser says it with a popup, from your nREPL/Piggieback session.

We've now seen how we can change the nREPL's behavior so that it is turned into an entry point to the ClojureScript REPL. Code passed to the REPL executed on into the JavaScript environment-the browser in our context-via JavaScript files that are regenerated after each and every operation.

We can make this workflow even leaner by hot loading the JavaScript artifacts into the browser with websockets. This is what we will see in the next section, with the Weasel library.

Setting up Weasel with PiggieBack for browser live coding

Weasel ( https://github.com/tomjakubowski/weasel ) sets a up realtime communication channel using a websocket between a ClojureScript REPL and the JavaScript evaluation environment. The authors of this library say that choosing websockets as a means for delivering compiled JavaScript to runtime environments made it possible for them to profit from a simple and reliable transport. It also empowered them to reach a much wider range of JavaScript engines, especially those that don't properly support, the <iframe> tag (the main technology behind the vanilla ClojureScript browser REPL). Let's now add Weasel on top of the Piggieback-enabled nREPL environment that we've set up in the previous sections.

Let's first modify the piggieback_project file we worked on earlier to support Weasel by adding its dependency to the project.clj file:

(defproject piggieback_project "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.8.0"] 
    [org.clojure/clojurescript "1.7.228"] 
    [weasel "0.7.0" :exclusions
      [org.clojure/clojurescript]]]
    :profiles {:dev {:dependencies
      [[com.cemerick/piggieback"0.2.1"] 
    [org.clojure/tools.nrepl "0.2.10"]] 
    :repl-options {:nrepl-middleware
      [cemerick.piggieback/wrap-     cljs-repl]}}})

Let's modify our ClojureScript code so that it can connect to the websocket opened by Weasel:

(ns piggieback-project.core 
  (:require [weasel.repl :as repl])) 
(when-not (repl/alive?) 
  (repl/connect "ws://localhost:9001")) 

Just like we did in the previous section, we'll need to connect to our nREPL and plug Piggieback in on top of it, but this time, we'll run it with a Weasel browser. After making sure that you are at the root of the project folder, type in a terminal:

lein repl

First, let's compile our ClojureScript source so we have our JavaScript ready to connect to the format as code websocket. Run the following from your nREPL session:

user=> (require 'cljs.build.api)
user=> (cljs.build.api/build "src"
   #_=>   {:main 'piggieback-project.core
   #_=>    :output-to "out/main.js"
   #_=>    :verbose true})

Next, we require the Weasel REPL namespace:

user=> (require 'weasel.repl.websocket)

We hook Piggieback onto the current session and ask it to use the Weasel websocket as the REPL environment (we also set the IP address and the port we want our websocket to be listening to):

user=> (cemerick.piggieback/cljs-repl
  #_=> (weasel.repl.websocket/repl-env :ip "0.0.0.0" :port
      9001))

After Piggieback is launched, Weasel shows a message to inform us that it is awaiting incoming connections:

<< waiting for client to connect ...

Open the greet.html file in your browser (you need to open it from the files manager on your OS, as we are not serving it over HTTP this time!). As soon as the file opens, you see the following message in your nREPL session:

<< waiting for client to connect ...  connected! >>

The prompt in your nREPL session should change to the following:

cljs.user=> _

As usual, let's salute the world and see if our greeting pops up in the browser:

cljs.user=> (js/alert "Hello World from Weasel!")

Your browser should now emit an alert with a greeting message on it.

Until this point, we have been able to put together a powerful environment for developing ClojureScript. Thanks to Piggieback being hooked on nREPL, we can benefit from the integrated development tools that already exist for Clojure. Before we go too much further on this subject, let's see how we can write ClojureScript programs that target other JavaScript environments. In the following section, we'll focus on setting up a Piggieback powered nREPL that evaluates code on the Node.js platform.

ClojureScript REPLs on Node.js with Piggieback

Setting up Node.js REPLs is simpler than targeting the browser. You don't need to set up connections from the REPL process to the browser with the help of some vanilla JavaScript. All you have to do is make sure that the source is compiled, and set the REPL target to Node.js, so that the compiled output is handed over to that evaluation environment for running.

First, make sure that Node.js is installed. Under piggieback_project, change the core.cljs file so that it looks like the following:

(ns piggieback-project.core 
  (:require [cljs.nodejs :as nodejs])) 
 
(nodejs/enable-util-print!) 
 
(defn -main [& args] 
  (println "Hello world from Node.js!")) 
 
(set! *main-cli-fn* -main) 

Launch an nREPL session for your project by typing the following:

lein repl

We then require the namespaces necessary for the launch of our Node.js REPL:

user=>(require 'cljs.build.api)
user=> (require 'cljs.repl.node)

Now, launch the first build of our ClojureScript core.cljs source:

user=> (cljs.build.api/build "src"
  #_=>   {:main 'piggieback-project.core
  #_=>    :output-to "out/main.js"
  #_=>    :verbose true})

We can now hook a Piggieback REPL into this running nREPL session. Issue the following command:

(cemerick.piggieback/cljs-repl (cljs.repl.node/repl-env))

The nREPL session responds with a message saying that a Node.js REPL has been launched:

ClojureScript Node.js REPL server listening on 49449

And as usual, the prompt has been changed so as to notify us that we have successfully launched a ClojureScript REPL on top of our nREPL session:

cljs.user=> _

At this point, we can implement a program similar to the Node.js "Hello World," a little HTTP server that greets the browser that queries it. Type the following in your, now, ClojureScript-enabled nREPL session (we'll elaborate more on the syntax later on):

cljs.user=> (def http (js/require "http"))
cljs.user=> (.listen (.createServer http
       #_=>                       (fn [req res]
       #_=>                       (do
       #_=>                       (.writeHead res
       #_=>                       200

       #_=>                       (js-obj
       #_=>                       "Content-Type" "text/plain"))
       #_=>                       (.end res
       #_=>                       "Hello World from Node.js
                                        http server!"))))
       #_=>                       1337
       #_=>                       "127.0.0.1")

If you navigate your browser to  http://127.0.0.1:1337, you should see the greeting from your Node.js server.

In the next section, we'll elaborate on a new contender in the realm of interactive ClojureScript coding environments: Figwheel, the new kid on the block that gets you to a ClojureScript browser REPL quickly.

 

Live coding ClojureScript with Figwheel


Figwheel ( https://github.com/bhauman/lein-figwheel ) is a Leiningen plugin that builds ClojureScript programs and delivers them to the browser for interactive evaluation. In contrast with nREPL-based work-flows, Figwheel does not rely on third-party REPLs. It is a self-contained library with its own ClojureScript REPL that relies on websockets to push your work to the browser as you edit your ClojureScript code. Figwheel also supports CSS live reloading in the browser, hence providing for a completely interactive web development experience. In this next section, we'll use Figwheel to get set up a ClojureScript live-coding experience on the browser.

Setting up Figwheel for browser live coding

Figwheel comes as a self-contained library that automatically builds and loads the generated JavaScript into the browser. This means we won't have to manually build the JavaScript that'll be pushed to the browser in order to establish the connection to the Figwheel REPL. Everything will be handled for us.

Let's begin by creating a new project that we will use to experiment with Figwheel:

lein new figwheel-project

We'll now need to change our project.clj file so that our project is aware of the lein-figwheel plugin:

(defproject figwheel-project "0.1.0-SNAPSHOT" 
  :dependencies [[org.clojure/clojure "1.8.0"] 
    [org.clojure/clojurescript "1.8.51"]] 
  :plugins [[lein-figwheel "0.5.1"]] 
  :clean-targets [:target-path "out"] 
  :cljsbuild { 
    :builds [{:id "dev" 
      :source-paths ["src"] 
      :figwheel true 
      :compiler {:main "figwheel-project.core"}}]})
        :asset-path "js/out"
        :output-to "resources/public/js/main.js"
        :output-dir "resources/public/js/out"}}]})

Create a file named core.cljs under src/figwheel_project/ so we can have a ClojureScript program that will be built and pushed to the browser. Code changes will be pushed to the browser automatically later on via this loaded file:

(ns figwheel-project.core) 
 
(js/alert "Hello from Figwheel!") 

As before, in order to load the compiled JavaScript that'll connect our browser to the running Figwheel process we need to have an HTML page. Since we're still compiling ClojureScript to the main.js file, we must load this file in order to get it evaluated in the browser. Create a greet.html file that you'll put in the root of your project. This page will contain the following:

<html> 
  <body> 
    <script type="text/javascript" src="main.js"></script> 
  </body> 
</html> 

Let's launch Figwheel. Note how this is done as a Leiningen plugin, and how we don't need to load a specific ClojureScript on top of an nREPL as we did with Piggieback:

lein figwheel

Your terminal should show a message that states that it is awaiting the client connection:

Prompt will show when figwheel connects to your application

For this, we are going to simply use the web server that comes embedded within Figwheel. Provided that we’ve put the previous greet.html in our browser. Open that HTML page as a regular file under the public/resources folder visit the following URL, http://localhost:3449/greet.html. As soon as the page loads, you'll see the greeting we programmed to show in the ClojureScript file, and once you've clicked on the OK button, you'll notice that the Figwheel invite is now showing a prompt accepting user requests for ClojureScript evaluation:

cljs.user=> _

Let's try to evaluate something in the browser. Type the following:

cljs.user=> (js/alert "Hi from Figwheel Again!")

Once again, this new greeting should pop up in your browser!

We've seen how it was easy to set up a browser live-coding session with Figwheel. In the next section, we'll experiment with Node.js evaluations.

Node.js interactive development workflows with Figwheel

Figwheel is mainly intended for the browser, and as such, configuring it to connect to Node.js is a bit trickier than what we just did. Since Figwheel does not rely on the core ClojureScript REPL or nREPL, and hence, there are some actions that need to be taken in order to add Node.js support to its default stack.

Getting Figwheel to provide a Node.js REPLs is a matter of preparing a special JavaScript artifact that, when run with Node.js, will implement a server that connects via websocket to a running Figwheel session. This server will evaluate the compiled JavaScript from the Figwheel REPL via the WebSocket connection. Let's implement this.

First create a new Clojure project and name it figwheel_node. Next, prepare the ClojureScript Node.js script that, once launched, will connect via a WebSocket to the REPL served by Figwheel:

(ns ^:figwheel-always figwheel-node-repl.core 
  (:require [cljs.nodejs :as nodejs])) 
(nodejs/enable-util-print!) 
(def -main (fn [] nil)) 
(set! *main-cli-fn* -main) 

Next, let's modify our project.clj file to target the Node.js runtime using the relevant bootstrapping library. We could configure the bootstrapping library ourselves, but instead we'll use a popular Leiningen plugin, Cljsbuild (https://github.com/emezeske/lein-cljsbuild), to automate this process for us. Let's add and configure it by editing your project.clj as follows:

(defproject figwheel-node "0.1.0-SNAPSHOT" 
  :dependencies [[org.clojure/clojure "1.7.0"] 
    [org.clojure/clojurescript "1.7.122"]] 
  :plugins [[lein-cljsbuild "1.1.0"] 
    [lein-figwheel "0.4.0"]] 
  :clean-targets ^{:protect false} ["out"] 
  :cljsbuild { 
    :builds [{:id "server-dev" 
      :source-paths ["src"] 
      :figwheel true 
      :compiler {:main figwheel-node-repl.core
                 :output-to "out/figwheel_node_repl.js"
                 :output-dir "out"
                 :target :nodejs
                 :optimizations :none
                 :source-map true}}]}
  :figwheel {})

Note that setting the figwheel-node-repl.core namespace as a main entry point will ensure that all the necessary imports are added to our compiled output before we execute any of the program logic via websockets. This way, the script can be painlessly run by Node.js.

Next, let's install the Node.js websockets client library, ws, so that our script can connect to the Figwheel session:

npm install ws

As we've done with the browser setup, launch the Figwheel REPL:

lein figwheel server-dev

As usual, you will see a prompt telling you that the Figwheel environment is awaiting a client connection. This time, the client will be the Node.js script we just developed. Launch it in a different terminal window from the one currently running our Figwheel server:

node out/figwheel_node_repl.js

At this point, you have two running environments: the Figwheel REPL, which now shows the cljs.user=> prompt and the Node.js process, which is actively evaluating the compiled JavaScript that is being pushed to it by Figwheel.

Let's evaluate, on the Figwheel REPL, the HTTP server we used in the previous sections:

cljs.user=> (def http (js/require "http"))
cljs.user=> (.listen (.createServer http
       #_=>                         (fn [req res]
       #_=>                         (do
       #_=>                         (.writeHead res
       #_=>                         200
       #_=>                         (js-obj
       #_=>                         "Content-Type"
                                          "text/plain"))
       #_=>                         (.end res
       #_=>                         "Hello World from
                                          Node.js http server!"))))
       #_=>                         1337
       #_=>                         "127.0.0.1")

If you visit the URL exposed by this HTTP server, http://127.0.0.1:1337, you should see a greeting from Node.js, meaning that the ClojureScript you typed in the Figwheel REPL has been successfully compiled to JavaScript and evaluated by the running Node.js process.

We've studied two alternatives for exposing ClojureScript REPLs-one of them based on nREPL with Piggieback and the other using a standalone REPL environment based on Figwheel. In the next sections, we'll talk about how to set up development environments for ClojureScript on Emacs.

 

Setting integrated development environments for ClojureScript


Now that we've got the basic lay of the land, we can now focus on configuring a proper Integrated Development Environment. We'll also discuss how we can make it ClojureScript-aware using the REPLs we've covered so far.

In doing so, we strive to profit from the many ways IDEs can assist us. IDEs are powerful tools, exposing functionalities such as code completion, syntax highlighting, program structure introspection, and navigation.

We'll focus on Emacs, its Clojure development environment, CIDER, as well as another simpler package, inf-clojure. The reason why we chose these Emacs-based tools is that they are the most used IDEs for most Clojurists, and are actively maintained by a vibrant community of open source enthusiasts.

Working on Emacs with Piggieback and Weasel on CIDER

CIDER, or the Clojure Interactive Development Environment that Rocks for Emacs ( https://github.com/clojure-emacs/cider ), is an open source Emacs Library for working with Clojure on Emacs. Originally called nrepl.el, it is stable, feature-rich, and an active project that is highly beneficial to Clojure and ClojureScript developers.

If you're going to use CIDER, its authors have stated that they expect ClojureScript developers to use Piggieback and Weasel as their default toolkit.

Let's assume that you can launch a Piggieback/Weasel-enabled nREPL session for your ClojureScript project (refer to the Live-coding ClojureScript on top of nREPL with PiggieBack and Weasel section). We'll now focus on how CIDER empowers you to develop ClojureScript with Emacs.

Installing CIDER

Installing Cider is a matter of getting the relevant library from package.el (using MELPAMELPA Stable, or Marmalade repositories) and issuing the following command (in Emacs):

M-x package-install [RET] cider [RET]

Alternatively, add the following lines to your Emacs configuration file:

(unless (package-installed-p 'cider) 
  (package-install 'cider)) 

You'll also need to hook up the CIDER middleware into our nREPL. To do this, add the following to the :user section in your ~/.lein/profiles.clj file:

:plugins [[cider/cider-nrepl "x.y.z"]] 

We haven't spoken about Leiningen profiles too much prior to now. To learn more about how the profiles.clj file works, check out the Leiningen documentation at https://github.com/technomancy/leiningen/blob/master/doc/PROFILES.md#declaring-profiles. Take care, the "x.y.z" version number in cider-nrepl must match the CIDER version, otherwise you'll get a warning when trying to connect to a project's REPL.

Working with Clojure and ClojureScript REPLs on CIDER

At this point, you're free to tweak CIDER's configuration to add features like different autocompletion providers or syntax-highlighting behavior. Whether you choose to or not, you should have everything you need to get CIDER up and running at this point.

Once you have installed CIDER and its nREPL middleware, you can open a Clojure file (even an empty buffer to experiment in), launch an REPL on it and begin to work interactively. Most Clojure developers go back and forth between editing and sending code to the REPL for evaluation. To launch a Clojure nREPL session from Emacs use the following command:

M-x cider-jack-in

Now, how can we get this set up to work with ClojureScript? Let's go back to our piggieback_project from earlier in this chapter and get CIDER working for it.

First, we'll need to tell CIDER which ClojureScript evaluation environment we are going to use. CIDER defaults to Rhino, so for our case we'll need to tell CIDER to use Weasel. Customize the cider-cljs-repl file to set it to Weasel:

M-x customize-variable RET cider-cljs-repl RET Weasel RET 

Make certain that your ClojureScript file contains the following connection code:

(ns piggieback-project.core 
  (:require [weasel.repl :as repl])) 
 (when-not (repl/alive?) 
  (repl/connect "ws://localhost:9001")) 

Tip

Make sure that this ClojureScript code has been successfully compiled at least once. Otherwise, we won't be able to load the websocket client.

Next, we'll use the lein-cljsbuild package. To activate this plugin make sure that your project.clj file looks as follows:

(defproject piggieback_project "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"} 
  :plugins [[lein-cljsbuild "1.1.0"]] 
  :cljsbuild { 
    :builds [{:source-paths ["src"] 
    :compiler {:main piggieback-project.core 
    :output-to "out/main.js" 
    :output-dir "out" 
    :optimizations :none}}]} 
  :dependencies [[org.clojure/clojure "1.7.0"] 
    [org.clojure/clojurescript "1.7.122"] 
    [weasel "0.7.0" :exclusions [org.clojure/clojurescript]]] 
  :profiles {:dev {:dependencies [[com.cemerick/piggieback
    "0.2.1"] 
    [org.clojure/tools.nrepl "0.2.10"]] 
    :repl-options {:nrepl-middleware
      [cemerick.piggieback/wrap-cljs-repl]}}})

Now we'll  build the code responsible for creating the connection. While in the project directory, type the following:

lein cljsbuild once

Now open any ClojureScript file in Emacs, and launch the nREPL session with:

M-x cider-jack-in-clojurescript

You'll see two REPLs, one for Clojure and the other for ClojureScript. The ClojureScript REPL should notify you that it is waiting for the client to connect.

Connect your browser to the Weasel session by opening greet.html. You should get the following screen in Emacs:

Now, switch to the window containing the Clojure REPL and set it to show the buffer containing your test.cljs ClojureScript file.

Load the content of the file in your REPL using the following Emacs command:

C-c C-k

Then, set the namespace of the REPL to be the one declared by the current source file:

C-c M-n

Switch now to your ClojureScript REPL:

C-c C-z

Start typing the name of the function. You'll notice that code completion should be working. In the minibuffer, Emacs should also help you with the signature of your function.

Now let's evaluate some ClojureScript in our REPL:

piggieback-project.test> (defn test-fn [your-name] (js/alert (+
      "hello " your-name)))
piggieback-project.test> (test-fn "Rafik")

And a popup should happily greet you from your connected browser.

Working on Emacs with Figwheel and inf-clojure

In order to use Figwheel with Emacs, we'll need to use inf-clojure, an Emacs package offering basic interaction with a running Clojure subprocess. In conjunction with clojure-mode, this setup will make sure that we benefit from static code analysis features. inf-clojure is not as feature rich as CIDER, which is worth keeping in mind. It is nevertheless, able to load files, switch namespaces, evaluate expressions, show documentation and source of symbols, and do macro-expansion.

Installing inf-clojure

Type the following in your Emacs:

M-x package-install [RET] inf-clojure [RET]

You may also want to add the following snippet to your Emacs configuration file:

(unless (package-installed-p 'inf-clojure) 
  (package-install 'inf-clojure)) 

To enable inf-clojure whenever you visit a Clojure or ClojureScript file, add the following to your Emacs configuration file:

(add-hook 'clojure-mode-hook #'inf-clojure-minor-mode) 

Configuring inf-clojure to run Figwheel as a Clojure subprocess

In order for Emacs to know that we want to use Figwheel as our REPL environment, we'll need to configure it explicitly. To do this, add the following to your Emacs configuration file (usually ~/.emacs):

(defun figwheel-repl () 
  (interactive) 
  (run-clojure "lein figwheel")) 

Restart Emacs or re-evaluate the the configuration file buffer for the modifications to take effect. Next, let's open a file in the source directory of the figwheel_project we've set up for the browser, say core.cljs, and launch the ClojureScript powered inf-clojure session by typing the following:

M-x figwheel-repl

You'll end up to a configuration like the one shown in the following screenshot, where Figwheel, run from inf-clojure, is awaiting connection from the browser:

As soon as you open the greet.html file within your browser, the user prompt should change to notify you that the evaluation environment is successfully connected to the Figwheel REPL:

Let's evaluate some ClojureScript. Split your window in two using the following:

C-x 2

And load the buffer containing core.cljs:

C-x b  core.cljs [RET]

You should end up with something like the following:

Move the cursor to the (js-alert...) form and evaluate it by hitting the following:

C-c C-c

You should see the greeting showing up in the browser.

We've now seen two different possible setups on Emacs: one based on Piggieback/Weasel, which is harder to set up but offers a fully-fledged Clojure development environment—CIDER, and another using Figwheel but offering less integrated development functionality. Which one you choose to use relies pretty much on personal taste, and how much effort you feel like putting in.

 

Summary


This concludes our section on getting started with ClojureScript development. We introduced you to the ClojureScript compiler, demonstrating how it runs on the JVM and leverages the Google Closure Library to optimize compiled JavaScript and provide namespace functionality.

We learned how the ClojureScript compiler can be used to build ClojureScript programs into JavaScript artifacts, how to access these artifacts and, finally, how to interact with them via a REPL targeting different JavaScript environments.

We covered how to use Piggieback in order to expose a JavaScript-enabled nREPL session, how to push the compiled JavaScript via websockets to the browser thanks to Weasel, and how to target Node.js using this setup.

After that, we saw how we could use Figwheel to get a single self-contained Leiningen plugin for developing with the browser. We also learned how to target Node.js using Figwheel.

Finally, we saw how one can use one of these setups with an integrated development environment based on CIDER or inf-clojure.

Now that you have your computer configured for ClojureScript development, lets tackle the language properly. In the next chapter, we'll dig into the core of the ClojureScript language.

About the Authors
  • W. David Jarvis

    W. David Jarvis is a software engineer living in San Francisco, California. In his spare time, he enjoys hiking, gardening, playing pool in dive bars, and overthinking everything. He is active in the open source Clojure and ClojureScript communities, and software authored by him has been downloaded over 10,000 times.

    David has worked for a number of companies now living or dead, including Aggregate Knowledge, CircleCI, Standard Treasury, and Airbnb. He is currently responsible for the build, test, and deployment infrastructure at Airbnb.

    The "W" is for "William" (now you know!).

    While David has made the unfortunate mistake of exposing his previous scribblings to the world, this is his first actual book.

    Browse publications by this author
  • Rafik Naccache

    Rafik Naccache is a Tunisian experienced software architect and emergent technologies enthusiast. He earned his bachelors degree in computer science engineering from Tunis University of Science in 2001. Rafik fell in love with Clojure back in 2012, and he has been developing it professionally since 2013. He has occupied various positions in telecoms and banking, and he has launched some innovative internet startups in which he has been able to deploy Clojure apps. He also founded the Tunisian Clojure users community. He contributes to Open Source projects, such as Cryogen (https://github.com/cryogen-project/cryogen/graphs/contributors), Milestones (https://github.com/automagictools/milestones), and Scheje (https://github.com/turbopape/scheje). You can reach him as @turbopape on GitHub and Twitter.

    Browse publications by this author
  • Allen Rohner

    Allen Rohner is a software engineer and entrepreneur living in Austin, Texas. He is the founder of numerous startups. A few, including CircleCI, have even been successful.

    Allen has been using Clojure and ClojureScript professionally since 2009, with commitments in Clojure core and dozens of other open source libraries. He has given multiple talks at Clojure/West and Clojure/conj.

    Currently, Allen is working on a startup called Rasterize (https://rasterize.io), which helps companies improve conversion rate by optimizing website load times. While Allen has occasionally had blog posts go to #1 on Hacker News, this is the first 'real' book that he's collaborated on.

    Browse publications by this author
Latest Reviews (4 reviews total)
A good overview of a fast-moving subject that needs better documentation.
Only ordered a digital book with great discount. The quality of the PDF was great and the contents, as far as I've read, is very good. Would buy again if such a huge discount appears.
Good, but it has at least one pretty badly-mangled code example in the section on different ways to get a ClojureScript REPL; it took a bit of work to figure out how the project.clj was actually supposed to look, and I wouldn't expect many people to whom Clojure or ClojureScript is new to be able to do that. Also, in that section, it seems to say that Figwheel can't be used with CIDER; but research on the web shows that the two can work together. I don't know if that's a new development since the writing of the book or not, but since the book was just recently published I would expect it to be up to date.
Learning ClojureScript
Unlock this book and the full library FREE for 7 days
Start now