Share your thoughts in the 2025 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

+1 vote
ago in ClojureScript by

Running cljs.main with just --repl doesn't preload whatever is on :preloads.

Given that the quickest way of trying ClojureScript is by running a command like :

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.12.134"}}}}' -M -m cljs.main --repl

it would be great (and I think expected) to be able to run a repl with some dev tooling, like :

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.12.134"} binaryage/devtools {:mvn/version "1.0.7"}}}}' -M -m cljs.main -co '{:preloads [devtools.preload]}' --repl

but it doesn't work.

It reads the file (it complains if you misspell the preload namespace) but then nothing gets loaded.

The problem seems to be related to ClojureScript not compiling the preload namespace when you run with just --repl

If you create a small single file project (like on the start guide) and then do :

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.12.134"} binaryage/devtools {:mvn/version "1.0.7"}}}}' -M -m cljs.main -co '{:preloads [devtools.preload]}' --compile hello-world.core --repl

it works.
After the compile generates the out folder you can run just with --repl and the preload will work.

ago by
Thinking a little bit more about this it doesn't make complete sense to have "preloads" without something to "load" first in the case of starting with `--repl` and no main.
 
But since I have seen preloads mostly used to have dev tooling available it is still nice to have a way of preloading in --repl mode so it gets automatically reloaded after a runtime refresh.

1 Answer

+1 vote
ago by

I just figured that the preloads do get compiled, it is just that the goog.requires aren't being added to the on-the-fly generated main.js.

Leaving a possible patch here in case there is interest :

diff --git a/src/main/clojure/cljs/cli.clj b/src/main/clojure/cljs/cli.clj
index da4f2761..9087151c 100644
--- a/src/main/clojure/cljs/cli.clj
+++ b/src/main/clojure/cljs/cli.clj
@@ -321,7 +321,8 @@ present"
         reopts (merge repl-env-options (select-keys opts [:main :output-dir]))
         _      (when (or ana/*verbose* (:verbose opts))
                  (util/debug-prn "REPL env options:" (pr-str reopts)))
-        renv   (apply (target->repl-env (:target options) repl-env) (mapcat identity reopts))]
+        renv   (-> (apply (target->repl-env (:target options) repl-env) (mapcat identity reopts))
+                   (assoc :compiler-opts opts))]
     (repl/repl* renv
       (assoc opts
         ::repl/fast-initial-prompt?
diff --git a/src/main/clojure/cljs/repl/browser.clj b/src/main/clojure/cljs/repl/browser.clj
index 2c907c6d..9b333127 100644
--- a/src/main/clojure/cljs/repl/browser.clj
+++ b/src/main/clojure/cljs/repl/browser.clj
@@ -182,7 +182,7 @@
 
 (defn send-static
   [{path :path :as request} conn
-   {:keys [static-dir output-dir host port gzip?] :or {output-dir "out"} :as opts}]
+   {:keys [static-dir output-dir host port gzip? compiler-opts] :or {output-dir "out"} :as opts}]
   (let [output-dir (when-not (.isAbsolute (io/file output-dir)) output-dir)]
     (if (and static-dir (not= "/favicon.ico" path))
       (let [path (if (= "/" path) "/index.html" path)
@@ -224,7 +224,11 @@
                                       clojure.browser.repl/PORT ~port}
                                   (merge (:closure-defines @browser-state))
                                   cljsc/normalize-closure-defines
-                                  json/write-str)]
+                                  json/write-str)
+                preloads (when-let [preloads (:preloads compiler-opts)]
+                           (mapv (fn [ns-symb]
+                                   (str "document.write('<script>goog.require(\"" (munge ns-symb) "\");</script>');"))
+                                 preloads))]
             (server/send-and-close conn 200
               (str "var CLOSURE_UNCOMPILED_DEFINES = " closure-defines ";\n"
                    "var CLOSURE_NO_DEPS = true;\n"
@@ -233,6 +237,7 @@
                    (when (.exists (io/file output-dir "cljs_deps.js"))
                      (str "document.write('<script src=\"" output-dir "/cljs_deps.js\"></script>');\n"))
                    "document.write('<script src=\"" output-dir "/brepl_deps.js\"></script>');\n"
+                   (when preloads (str/join "\n" preloads))
                    "document.write('<script>goog.require(\"clojure.browser.repl.preload\");</script>');\n")
               "text/javascript" "UTF-8"))

It is basically adding the compiler options to the repl environment, so they are available when generating main.js, then adding one goog.require per preload.

...