shadow-cljs (since about 2.0.15 ) supports compiling builds that make use of the self-hosted ClojureScript Compiler. Previously this was not supported since the build needs to be modified in a few places to ensure that all the required files are available. shadow-cljs itself continues to use the JVM compiler.

This feature is split into two parts. First you need a “host” build which will be your “app” (currently limited to :browser builds). This build will host the compiler and then use the support files generated by the second :bootstrap build. These support files include the CLJS analyzer cache, macro JS files, .cljs files, etc.

The :bootstrap build itself will generate an “index” with useful information for the compiler. When the “host” build starts up it calls the provided shadow.cljs.bootstrap.browser/init function to load the index and fetch all resources necessary to start using the compiler (which usually involves the analyzer data for cljs.core and the cljs.core$macros JS file).

Once init completes the compiler can be used. The provided shadow.cljs.bootstrap.browser/load function takes care of properly loading dependencies. The CLJS analyzer will call this function whenever a dependency is required and it will load the analyzer data, macro and JS files. Only namespaces pre-compiled by the :bootstrap build will be available.

The “host” can look something like this:

(ns demo.selfhost.simple (:require [cljs.js :as cljs] [cljs.env :as env] [shadow.cljs.bootstrap.browser :as boot])) (defn print-result [{:keys [error value] :as result}] (js/console.log "result" result) (set! (.-innerHTML (js/document.getElementById "dump")) value)) (def code " (ns simpleexample.core (:require [clojure.string :as str] [reagent.core :as r])) (defonce timer (r/atom (js/Date.))) (defonce time-color (r/atom \"#f34\")) (defonce time-updater (js/setInterval #(reset! timer (js/Date.)) 1000)) (defn greeting [message] [:h1 message]) (defn clock [] (let [time-str (-> @timer .toTimeString (str/split \" \") first)] [:div.example-clock {:style {:color @time-color}} time-str])) (defn color-input [] [:div.color-input \"Time color: \" [:input {:type \"text\" :value @time-color :on-change #(reset! time-color (-> % .-target .-value))}]]) (defn simple-example [] [:div [greeting \"Hello world, it is now\"] [clock] [color-input]]) (r/render [simple-example] (js/document.getElementById \"app\"))" ) (defonce compile-state-ref (env/default-compiler-env)) (defn compile-it [] (cljs/eval-str compile-state-ref code "[test]" {:eval cljs/js-eval :load (partial boot/load compile-state-ref)} print-result)) (defn start [] (boot/init compile-state-ref {:path "/bootstrap"} compile-it)) (defn stop [])

The shadow-cljs.edn config

{:dependencies [[reagent "0.8.0-alpha1" :exclusions [cljsjs/create-react-class]] :source-paths ["src"] :builds {:bootstrap-host {:target :browser :output-dir "out/demo-selfhost/public/simple/js" :asset-path "/simple/js" :compiler-options {:optimizations :simple} :modules {:base {:entries [demo.selfhost.simple]}} :devtools {:http-root "out/demo-selfhost/public" :http-port 8700 :before-load demo.selfhost.simple/stop :after-load demo.selfhost.simple/start}} :bootstrap-support {:target :bootstrap :output-dir "out/demo-selfhost/public/bootstrap" :exclude #{cljs.js} :entries [cljs.js demo.macro reagent.core] :macros []}}}

The config option for the :bootstrap build are

:entries a sequence of namespaces you want to have available for the self-hosted compiler

a sequence of namespaces you want to have available for the self-hosted compiler :exclude for macro namespaces that are not self-host compatible as they would otherwise break the build. This would include things like cljs.core.async.macros , cljs.js , etc.

for namespaces that are not self-host compatible as they would otherwise break the build. This would include things like , , etc. :macros will usually be optional since all macros used by the :entries will already be included.

Everything is written to :output-dir . The path where those files are available must be passed to the boot/init function.

@mhuebert created a standalone example and the shadow-cljs repo itself contains the example above.

You can try it by running

npm install -g shadow-cljs git clone https://github.com/thheller/shadow-cljs.git cd shadow-cljs shadow-cljs watch bootstrap-host bootstrap-support open http://localhost:8700

Usually shadow-cljs does not require lein but it is required in this case since I’m using lein to build the shadow-cljs project. The standalone example is probably better suited for testing.