The goal is to have the spec-related code conditionally elided from the below for prod builds:
(ns my-app.core
(:require [clojure.spec.alpha :as s]
[my-app.config :as config]))
(defn new-machine
[opts]
[(merge {:state/name "A"}
(when config/debug
{:state/spec (s/keys :req [::foo ::bar])}))
(merge {:state/name "B"}
(when config/debug
{:state/spec (s/keys :req [::baz])}))
])
(when config/debug
(s/def ::foo int?)
(s/def ::bar int?)
(s/def ::baz int?))
(new-machine nil)
The my-app.config/debug
symbol is defined as a constant Boolean in a namespace which is loaded from one path for dev and another for prod (where the only difference is that the value is true
or false
):
(ns my-app.config)
(def ^:const debug false)
We use a constant Boolean in order to take advantage of aggressive elision that occurs owing to CLJS-2267. Since this occurs at the ClojureScript level, it can even eliminate code in cases where using goog.DEBUG
is not effective.
With the above, under :advanced
, this compiles down to the same JavaScript as if the code were:
(ns my-app.core)
(defn new-machine
[opts]
[(merge {:state/name "A"}
nil)
(merge {:state/name "B"}
nil)
])
(new-machine nil)
One disadvantage of this approach is that it may be susceptible to compilation caching. In other words, you may need to either do a clean build when switching between dev and prod builds, or ensure that :output-dir
differs between dev and prod configuration.