cljs.main, the Figwheel allows you to
run a -main function in a namespace from the command line. Figwheel
provides additional functionality to facilitate asynchronous execution
and process failure.If you have a ClojureScript namespace on the classpath with a -main
function in it, you can execute that function from the command line.
For example if you have the following namespace:
(ns example.hello)
(defn -main [& args]
(println "ARGS:" (pr-str args)))
You can execute it from the command line like this:
$ clj -m figwheel.main -m example.hello hi there
You will see the command will compile and eventually print out:
ARGS: ("hi" "there")
You may wonder why you might need a feature like this in the first place. Being able to run one-off arbitrary scripts can be a major boon to your tool chain. You will most likely use a main script to run tests, but it could also be useful for running tooling like Webpack from Node.
Being able to accept arbitrary arguments at the end of the command line is why CLI option order is important in
clojure.main,cljs.mainandfigwheel.main. I know that this can be confusing at times, but being able to provide scripts and main scripts with arbitrary options, including ones that could mistakenly be recognized byfigwheel.main, is very important for flexible expression. And is the reason whyfigwheel.maincan reuse many of the same CLI args asclojure.main.
--mainSince the -m and -b options cannot be used at the same time you
may be left wondering how to supply the options that you would
normally supply with the --build option to the --main option.
You can do this by providing your [build-id].cljs.edn file to the -co
option. Figwheel will pick up both the metadata config along with the CLJS
compiler config.
For example the following will allow you to use the config in your
dev.cljs.edn file:
$ clj -m figwheel.main -co dev.cljs.edn -m example.hello
Only available in
figwheel.main>= 0.1.9-SNAPSHOT
This --main CLI option behavior is unfortunately hampered in its
ability to be useful because many ClojureScript tasks (including
running tests) are asynchronous. Not only that, but we'd prefer that if
a command line task execution fails in ClojureScript we'd like that
failure to be communicated by returning a non-zero exit status from
the clj process.
The good news is that currently if you throw a JavaScript Error from
synchronous code in your -main function the Clojure process will
exit with a non-zero exit status.
This means that when you are running test code that is synchronous, you can determine if the tests failed at the end of the run and throw an error to cause a non-zero exit from the Clojure process.
What do we do when the process is asynchronous?
figwheel.main extends the cljs.main behavior to provide a means of
waiting for an asynchronous process to complete or throw an error.
The following 3 tools help you do this:
-main
function returns a vector
[:figwheel.main.result/async-wait optional-timeout optional-timeout-value]figwheel.main.result/send ClojureScript function will send
back a value to the blocked Figwheel processfigwheel.main.result/throw-ex ClojureScript function will send
back an exception to the blocked Figwheel processFigwheel will look at the value returned by the -main function and
alter its behavior if it receives something like
[:figwheel.main.result/async-wait 5000]. This value will cause the
Clojure process to block and wait for a result or timeout after 5
seconds.
For Example:
(ns example.hello)
(defn -main [& args]
[:figwheel.main.async-result/wait 5000])
When you run the above namespace with the following:
$ clj -m figwheel.main -m example.hello
The Clojure process will block for 5 seconds while it waits for some asynchronous result to be sent back. In this example we are not sending back a result value, so the process will throw a timed-out exception which will cause a non-zero exit.
Now let's modify the above -main function to send back an
asynchronous result.
(ns example.hello
(:require [figwheel.main.async-result :as async-result]))
(defn -main [& args]
(js/setTimeout #(async-result/send args) 3000)
[:figwheel.main.async-result/wait 5000])
Again run the -main function with:
$ clj -m figwheel.main -m example.hello
Now the Clojure process will block for 3 seconds and ultimately print
out the value of args sent by the figwheel.main.async-result/send
function.
Let's modify the namespace one more time to send an asynchronous failure:
(ns example.hello
(:require [figwheel.main.async-result :as async-result]))
(defn -main [& args]
(js/setTimeout #(async-result/throw-ex (ex-info "This failed in ClojureScript!" {})) 3000)
[:figwheel.main.async-result/wait 5000])
After you run the above example, the Clojure process will block for 3 seconds and then fail with an exception.
The figwheel.main.async-result/send and
figwheel.main.async-result/throw-ex functions are single-use actions
that are only intended to be used in a --main script. The first
asynchronous value or exception that is received will unblock the
process.
The returned :figwheel.main.async-result/wait message has two
optional parameters.
timeout defaults to 5000 millisecondstimeout-value defaults to :figwheel.main.async-result/timed-outWhen Figwheel runs a -main script it prints the final result,
cljs.main does not do this.
Can you improve this documentation? These fine people already did:
Bruce Hauman, Jindrich Mynarz & ledEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |