Here’s what you need to set up a pretty nice ClojureScript development environment.
Editor
I’m using Emacs. I actually have it set up through Clojure before I started playing with ClojureScript. There’s a tutorial on the main site:
- https://github.com/clojure/clojurescript/wiki/Emacs-%26-inferior-lisp-mode
where you can also find tutorials for other editors:
- https://github.com/clojure/clojurescript/wiki#tools
Lein + lein-cljsbuild
Install Leiningen, make a new project:
$ lein new com.icyrock.clojurescript Generating a project called com.icyrock.clojurescript based on the 'default' template. To see other templates (app, lein plugin, etc), try `lein help new`.
and add lein-cljsbuild plugin to your project.clj
:
(defproject com.icyrock.clojurescript "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.4.0"]] :plugins [[lein-cljsbuild "0.3.0"]] :cljsbuild {:builds [{:source-paths ["src-cljs"] :compiler {:output-to "resources/public/js/cljsbuild-main.js" :optimizations :whitespace :pretty-print true}}]})
The above has some default configuration for the plugin:
- The source folder for ClojureScript files is
src-cljs
- Turn some basic optimization and pretty-print the output
- The output goes to
resources/public/js/cljsbuild-main.js
Usually, you’d run cljsbuild
in the background with:
$ lein cljsbuild auto
which will watch for the files in the source path for changes and then perform the necessary steps (compile, optimize, bundle) to get you the target .js
file.
Ring
Ring is a Web server. It’s very nice for development, as it compiles the Clojure files on the fly. There’s also lein-ring which is a helper plugin to simplify usage when used with lein
. Here’s the expanded project.clj
:
(defproject com.icyrock.clojurescript "0.1.0-SNAPSHOT" :description "icyrock.com ClojureScript" :url "https://icyrock.com/clojure-script" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.5.1"] [ring/ring-core "1.1.8"]] :plugins [[lein-cljsbuild "0.3.0"] [lein-ring "0.8.3"]] :ring {:handler com.icyrock.clojure.ring-main/handler :port 3000} :cljsbuild {:builds [{:source-paths ["src-cljs"] :compiler {:output-to "resources/public/js/cljsbuild-main.js" :optimizations :whitespace :pretty-print true}}]})
As per Ring’s Getting Started, create a sample handler in src/com/icyrock/clojure/ring_main.clj
:
(ns com.icyrock.clojure.ring-main) (defn handler [request] {:status 200 :headers {"Content-Type" "text/html"} :body "Welcome to com.icyrock.clojure.ring-main" })
You can run the server now with:
$ lein ring server 2013-04-15 23:16:21.815:INFO:oejs.Server:jetty-7.6.1.v20120215 2013-04-15 23:16:21.966:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000 Started server on port 3000
which will open your browser. You can use lein ring server-headless
if you don’t want it to open your browser.
Compojure
The next step is to add some file serving to our Web server. Compojure allows for that. It’s a Clojure routing DSL that sits on top of Ring. Add this as a dependency and change the ring handler:
(defproject com.icyrock.clojurescript "0.1.0-SNAPSHOT" :description "icyrock.com ClojureScript" :url "https://icyrock.com/clojure-script" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.5.1"] [ring/ring-core "1.1.8"] [compojure "1.1.5"]] :plugins [[lein-cljsbuild "0.3.0"] [lein-ring "0.8.3"]] :ring {:handler com.icyrock.clojure.compojure-main/app :port 3000} :cljsbuild {:builds [{:source-paths ["src-cljs"] :compiler {:output-to "resources/public/js/cljsbuild-main.js" :optimizations :whitespace :pretty-print true}}]})
then make a site router (see the routes.clj from the example project):
(ns com.icyrock.clojure.compojure-main (:require [compojure.core :refer :all] [hiccup.middleware :refer (wrap-base-url)] [compojure.route :as route] [compojure.handler :as handler] [compojure.response :as response])) (defroutes main-routes (GET "/" [] "Welcome to com.icyrock.clojure.compojure-main") (route/resources "/") (route/not-found "Page not found")) (def app (-> (handler/site main-routes) (wrap-base-url)))
The important part above is the (route/resources "/")
. This allows for static resources to be served by ring. The default is to serve from the (first) public
on the classpath. Lein accepts the parameter called :resources-path
, which (as per this) defaults to resources
. Thus, if we create a folder resources/public
, the above would make ring serve all files from that folder under the root (i.e. /
) of the site.
As an example:
$ mkdir -p resources/public $ echo "I'm a resource" > resources/public/a-res.txt
Now do lein ring server
and browse to http://localhost:3000/a-res.txt
– you’ll get I'm a resource
text, as expected.
Auto-recompiling ClojureScript
Let’s add:
- jayq, which is a jQuery wrapper for ClojureScript
- jQuery externs that allow Google Closure to recognize and not obfuscate jQuery calls (see this)
- dommy, a small and fast ClojureScript templating engine
to our project.clj
:
(defproject com.icyrock.clojurescript "0.1.0-SNAPSHOT" :description "icyrock.com ClojureScript" :url "https://icyrock.com/clojure-script" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.5.1"] [ring/ring-core "1.1.8"] [compojure "1.1.5"] [hiccup "1.0.3"] [jayq "2.3.0"] [prismatic/dommy "0.1.0"]] :plugins [[lein-cljsbuild "0.3.0"] [lein-ring "0.8.3"]] :ring {:handler com.icyrock.clojure.compojure-main/app :port 3000} :cljsbuild {:builds [{:source-paths ["src-cljs"] :compiler {:output-to "resources/public/js/cljsbuild-main.js" :externs ["externs/jquery-1.9.1.js"] :optimizations :whitespace :pretty-print true}}]})
Now we can make a sample ClojureScript file. All these go to src-cljs
. This file is within that folder under com/icyrock/clojurescript/prb/hello_world.cljs
and the contents are simple:
(ns com.icyrock.clojurescript.prb.hello-world (:require [jayq.core :as $] [dommy.core :as dommy] [dommy.template :as tpl])) (defn hello-world [] (dommy/append! js/document.body [:h1 "Hello, world!"]) (let [body ($/$ js/document.body) para (-> ($/$ "<p>") ($/text "This is a sample paragraph")) link (tpl/node [:a {:href "https://icyrock.com/"} "icyrock.com"])] ($/append body para) ($/append body link)))
This is the main file that will be used to call child functions, just for separation:
(ns com.icyrock.clojurescript.main (:require [jayq.core :as $] [com.icyrock.clojurescript.prb.hello-world :refer [hello-world]])) ($/$ hello-world)
Let’s run lein-cljsbuild
in another terminal in auto-mode:
$ lein cljsbuild auto Compiling "resources/public/js/cljsbuild-main.js" from ["src-cljs"]... Successfully compiled "resources/public/js/cljsbuild-main.js" in 12.54909577 seconds.
Add a simple HTML to resources/public/main.htm
:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8" /> <title>com.icyrock.clojurescript.main</title> <script src="lib/jquery-1.9.1.min.js"></script> <script src="js/cljsbuild-main.js"></script> </head> <body> </body> </html>
Download the required files as needed:
- http://code.jquery.com/jquery-1.9.1.min.js into
lib/jquery-1.9.1.min.js
- http://code.google.com/p/closure-compiler/source/browse/contrib/externs/jquery-1.9.js into
externs/jquery-1.9.1.js
Browse to http://localhost:3000/main.htm and you’ll see something like this:
Note that further updates to the .cljs
files are automatically recompiled by Google Closure compiler, that is used by ClojureScript through lein-cljsbuild