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.