ring-cljsbuild

Ring middleware that compiles ClojureScript and serves the JS

EPL-1.0 License

Downloads
1.6K
Stars
6
Committers
1

ring-cljsbuild

Ring middleware interface to cljsbuild. It compiles ClojureScript and serves the JS directly from your ring-clojure webserver, instead of requiring a separate lein command and separate JVM as lein-cljsbuild does.

Under the hood, it uses the same cljsbuild library that lein-cljsbuild uses, so compiler options are specified in the exact same way. In addition, there are ring-cljsbuild specific options.

Benefits (vs. lein-cljsbuild)

  • No extra JVM process / terminal window.
  • cljsbuild configuration changes do not require a JVM restart.
  • HTTP requests are blocked until cljs compiler is done, so you can be certain
    the client received the latest compiled js.
  • cljs compiler options can live in same file that serves and calls the resultant js.
    See e.g. server.clj.
  • (optional) log4j logging so cljs compiler errors logged in same place as
    server-side errors, so there are fewer terminal windows to monitor.
  • (on supported systems) uses modern inotify API so file changes are instantly
    detected and recompilation is kicked off, rather than polling every 100ms as
    lein-cljsbuild does.

Drawbacks

  • It slightly bloats the server because it adds all the ClojureScript compiler code as a
    dependency.

Usage

Add ring-cljsbuild as a dependency to project.clj. As with lein-cljsbuild, you will also want to add a specific clojurescript version dependency.

(defproject my-project "0.0.1"
  :dependencies [[org.clojure/clojurescript "0.0-XXXX"]]
                 [ring-cljsbuild "X.X.X"]])

Next require wrap-cljsbuild middleware.

(ns my-ring-server
  (:require [[ring-cljsbuild.core :refer [wrap-cljsbuild]]]))

Then add a call to wrap-cljsbuild.

(def app
  (-> #'handler
      (wrap-cljsbuild "/cljsbuild/"
                      {:id :myapp
                       :auto true ;; recompile when files change on disk
                       :java-logging false ;; log to stdout instead of log4j
                       :main-js-name "main.js"
                       :source-map true
                       :cljsbuild {:source-paths ["src-cljs"]
                                   :incremental true
                                   :compiler {:optimizations :none
                                              :cache-analysis true
                                              :pretty-print false
                                              :warnings true
                                              :main "ring-cljsbuild.test.client"}}))))

The first arg to wrap-cljsbuild is the URL prefix from which the compiled javascript will be served. Here the webserver will respond to "GET /cljsbuild/main.js" with the output of the clojurescript compiler.

The second arg is a map of options. Within this map, :cljsbuild takes arguments the same way lein-cljsbuild does, and within that, :compiler takes a map of options documented here.

Finally, render a page that references the javascript.

(defn render-index [req]
  (html5
    [:body
     (include-js "/cljsbuild/main.js")]))

Advanced compilation

You can dynamically serve optimized compiles and unomptimized compiles from the same webserver, via multiple calls to wrap-cljsbuild with different URL prefixes. Then you can decide in the request handler whether to serve optimized or unoptimized, perhaps by looking at a URL parameter. (TODO: provide example of this).

Implementation Issues

  • Crossovers are not supported (but these seem to be a deprecated concept
    anyway). cljc works fine.

Related work and discussion

License

Source Copyright © Aaron Iba, 2014-2015. Distributed under the Eclipse Public License, the same as Clojure uses. See LICENSE.