Skip to content

Commit 7127f5f

Browse files
mfikesswannodette
authored andcommitted
CLJS-2680: Passing :watch-fn via --compile-opts to cljs.main
1 parent 0a9009c commit 7127f5f

File tree

3 files changed

+87
-30
lines changed

3 files changed

+87
-30
lines changed

src/main/clojure/cljs/closure.clj

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,35 @@
5454
(defn random-string [length]
5555
(apply str (take length (repeatedly random-char))))
5656

57+
(defn- sym->var
58+
"Converts a namespaced symbol to a var, loading the requisite namespace if
59+
needed. For use with a function defined under a keyword in opts. The kw and
60+
ex-data arguments are used to form exceptions."
61+
[sym kw ex-data]
62+
(let [ns (namespace sym)
63+
_ (when (nil? ns)
64+
(throw
65+
(ex-info (str kw " symbol " sym " is not fully qualified")
66+
(merge ex-data {kw sym}))))
67+
var-ns (symbol ns)]
68+
(when (not (find-ns var-ns))
69+
(try
70+
(locking ana/load-mutex
71+
(require var-ns))
72+
(catch Throwable t
73+
(throw (ex-info (str "Cannot require namespace referred by " kw " value " sym)
74+
(merge ex-data {kw sym})
75+
t)))))
76+
77+
(find-var sym)))
78+
79+
(defn- opts-fn
80+
"Extracts a function from opts, by default expecting a function value, but
81+
converting from a namespaced symbol if needed."
82+
[kw opts]
83+
(when-let [fn-or-sym (kw opts)]
84+
(cond-> fn-or-sym (symbol? fn-or-sym) (sym->var kw {}))))
85+
5786
;; Closure API
5887
;; ===========
5988

@@ -2469,30 +2498,14 @@
24692498
(js-transforms js-module opts)
24702499

24712500
(symbol? preprocess)
2472-
(let [ns (namespace preprocess)
2473-
_ (when (nil? ns)
2474-
(throw
2475-
(ex-info (str "Preprocess symbol " preprocess " is not fully qualified")
2476-
{:file (:file js-module)
2477-
:preprocess preprocess})))
2478-
preprocess-ns (symbol ns)]
2479-
(when (not (find-ns preprocess-ns))
2480-
(try
2481-
(locking ana/load-mutex
2482-
(require preprocess-ns))
2483-
(catch Throwable t
2484-
(throw (ex-info (str "Cannot require namespace referred by :preprocess value " preprocess)
2485-
{:file (:file js-module)
2486-
:preprocess preprocess}
2487-
t)))))
2488-
2501+
(let [preprocess-var (sym->var preprocess :preprocess {:file (:file js-module)})]
24892502
(try
2490-
((find-var preprocess) js-module opts)
2503+
(preprocess-var js-module opts)
24912504
(catch Throwable t
24922505
(throw (ex-info (str "Error running preprocessing function " preprocess)
2493-
{:file (:file js-module)
2494-
:preprocess preprocess}
2495-
t)))))
2506+
{:file (:file js-module)
2507+
:preprocess preprocess}
2508+
t)))))
24962509

24972510
:else
24982511
(do
@@ -2895,8 +2908,13 @@
28952908
"Given a source directory, produce runnable JavaScript. Watch the source
28962909
directory for changes rebuilding when necessary. Takes the same arguments as
28972910
cljs.closure/build in addition to some watch-specific options:
2898-
- :watch-fn, a function of no arguments to run after a successful build.
2899-
- :watch-error-fn, a function receiving the exception of a failed build."
2911+
- :watch-fn, a function of no arguments to run after a successful build. May
2912+
be a function value or a namespaced symbol identifying a function,
2913+
in which case the associated namespace willl be loaded and the
2914+
symbol resolved.
2915+
- :watch-error-fn, a function receiving the exception of a failed build. May
2916+
be a function value or a namespaced symbol, loaded as
2917+
with :watch-fn."
29002918
([source opts]
29012919
(watch source opts
29022920
(if-not (nil? env/*compiler*)
@@ -2919,10 +2937,10 @@
29192937
(println "... done. Elapsed"
29202938
(/ (unchecked-subtract (System/nanoTime) start) 1e9) "seconds")
29212939
(flush))
2922-
(when-let [f (:watch-fn opts)]
2940+
(when-let [f (opts-fn :watch-fn opts)]
29232941
(f))
29242942
(catch Throwable e
2925-
(if-let [f (:watch-error-fn opts)]
2943+
(if-let [f (opts-fn :watch-error-fn opts)]
29262944
(f e)
29272945
(binding [*out* *err*]
29282946
(println (Throwables/getStackTraceAsString e)))))))

src/test/cljs_cli/cljs_cli/test.clj

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
[clojure.java.io :as io]
55
[clojure.java.shell :as shell :refer [with-sh-dir]]
66
[clojure.string :as str]
7-
[cljs-cli.util :refer [cljs-main output-is with-sources with-post-condition with-repl-env-filter]]))
7+
[cljs-cli.util :refer [cljs-main output-is with-sources with-in with-post-condition with-repl-env-filter repl-title]]
8+
[clojure.string :as string]))
89

910
(deftest eval-test
1011
(-> (cljs-main "-e" 3 "-e" nil "-e" 4)
@@ -96,3 +97,29 @@
9697
(output-is
9798
nil
9899
"{:ns cljs.user, :value 3}"))))
100+
101+
(deftest test-cljs-2680
102+
(with-repl-env-filter (complement #{"node"}) ; Exclude Node owing to CLJS-2684
103+
(with-sources {"src/foo/core.cljs" "(ns foo.core)"
104+
"src/bar/core.clj" "(ns bar.core) (defn watch [] (prn :watch-called))"}
105+
(with-in ":cljs/quit\n"
106+
(with-post-condition (fn [dir]
107+
(some #{":watch-called"}
108+
(str/split-lines (slurp (io/file dir "out" "watch.log")))))
109+
(-> (cljs-main "-co" "{:watch-fn bar.core/watch}" "--watch" "src" "-c" "foo.core" "-r")
110+
(output-is
111+
"Watch compilation log available at: out/watch.log"
112+
(repl-title)
113+
"cljs.user=>")))))
114+
(with-sources {"src/foo/core.cljs" "(ns foo.core"
115+
"src/bar/core.clj" "(ns bar.core) (defn watch-error [e] (prn :watch-error-called (.getMessage e)))"}
116+
(with-in ":cljs/quit\n"
117+
(with-post-condition (fn [dir]
118+
(let [log-contents (slurp (io/file dir "out" "watch.log"))]
119+
(and (str/includes? log-contents ":watch-error-called")
120+
(str/includes? log-contents "Unexpected EOF while reading"))))
121+
(-> (cljs-main "-co" "{:watch-error-fn bar.core/watch-error}" "--watch" "src" "-c" "foo.core" "-r")
122+
(output-is
123+
"Watch compilation log available at: out/watch.log"
124+
(repl-title)
125+
"cljs.user=>")))))))

src/test/cljs_cli/cljs_cli/util.clj

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
(ns cljs-cli.util
2+
(:refer-clojure :exclude [*in*])
23
(:require
34
[clojure.string :as string]
45
[clojure.java.io :as io]
56
[clojure.java.shell :as shell]
6-
[clojure.test :refer [is]])
7+
[clojure.test :refer [is]]
8+
[cljs.repl :as repl])
79
(:import
810
(java.io File)
911
(java.nio.file Files CopyOption)
@@ -13,13 +15,20 @@
1315
(def ^:dynamic *repl-env-filter* (constantly true))
1416
(def ^:dynamic *repl-opts* nil)
1517
(def ^:dynamic *sources* nil)
18+
(def ^:dynamic *in* nil)
1619
(def ^:dynamic *post-condition* nil)
1720

1821
(defmacro with-sources
1922
[sources & body]
2023
`(binding [*sources* ~sources]
2124
~@body))
2225

26+
27+
(defmacro with-in
28+
[in & body]
29+
`(binding [*in* ~in]
30+
~@body))
31+
2332
(defmacro with-post-condition
2433
[post-condition & body]
2534
`(binding [*post-condition* ~post-condition]
@@ -54,7 +63,7 @@
5463
(copy-uberjar temp-dir)
5564
(let [result (shell/with-sh-dir temp-dir
5665
#_(apply println "running:" args)
57-
(apply shell/sh args))]
66+
(apply shell/sh (if *in* (concat args [:in *in*]) args)))]
5867
(when *post-condition*
5968
(is (*post-condition* temp-dir)))
6069
result)
@@ -90,5 +99,8 @@
9099
(is (zero? (:exit result)))
91100
(maybe-print-result-err result)
92101
(when-not (:repl-env-filtered result)
93-
(is (= (apply str (map print-str (interleave expected-lines (repeat "\n"))))
94-
(:out result)))))
102+
(is (= (string/trim (apply str (map print-str (interleave expected-lines (repeat "\n")))))
103+
(string/trim (:out result))))))
104+
105+
(defn repl-title []
106+
(string/trim (with-out-str (repl/repl-title))))

0 commit comments

Comments
 (0)