Skip to content

Commit acfc64a

Browse files
authored
Merge pull request #178 from mfikes/issue-177
ClojureScript Command Line post
2 parents d1a0dfe + 4482a36 commit acfc64a

File tree

4 files changed

+298
-0
lines changed

4 files changed

+298
-0
lines changed
Loading
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
= ClojureScript Command Line
2+
Mike Fikes
3+
2018-03-26
4+
:jbake-type: post
5+
:toc: macro
6+
:icons: font
7+
8+
ifdef::env-github,env-browser[:outfilesuffix: .adoc]
9+
10+
[[clojurescript-command-line-toc]]
11+
toc::[]
12+
13+
ClojureScript now has an exciting new command line capability which dramatically simplifies using ClojureScript for many common use cases. In this post, we'll take you through a quick tour of the capabilities being introduced with `cljs.main`.
14+
15+
[[clojurescript-compiler]]
16+
=== Standalone ClojureScript JAR
17+
18+
In order to try these examples, if you are on macOS or Linux, you can use the https://clojure.org/guides/getting_started[`clj`] command line tool, with `deps.edn` set up as:
19+
20+
[source,clojure]
21+
----
22+
{:deps {org.clojure/clojurescript {:mvn/version "1.10.238"}}}
23+
----
24+
25+
On Windows, you will need to obtain the https://github.com/clojure/clojurescript/releases/latest[standalone ClojureScript JAR].
26+
27+
[[starting-a-browser-repl]]
28+
== Starting a Browser REPL
29+
30+
Let's jump right in! You can start up a browser REPL by running the following command
31+
32+
[source,bash]
33+
----
34+
clj -m cljs.main
35+
----
36+
37+
On Windows:
38+
39+
[source,bash]
40+
----
41+
java -cp cljs.jar cljs.main
42+
----
43+
44+
When you do this, the REPL will start and will automatically launch a browser to connect back to it.
45+
46+
image::/images/content/news/2018-03-26-clojurescript-command-line/browser.png[Browser REPL Serving Default index.html,450]
47+
48+
You will see the following in the REPL terminal:
49+
50+
[source,text]
51+
----
52+
ClojureScript 1.10.238
53+
cljs.user=>
54+
----
55+
56+
We are now in an interactive environment, with the ability to control the page. Try the following in the REPL to cause an alert to pop up in your browser:
57+
58+
[source,clojure]
59+
----
60+
(js/alert "Hello CLJS!")
61+
----
62+
63+
[[creating-a-browser-app]]
64+
== Creating a Browser App
65+
66+
Now, let's cobble together a simple browser-based ClojureScript app and explore the ability of `cljs.main` to compile your app's source.
67+
68+
Note that in the previous section, `cljs.main` synthetically-generated an `index.html` for us. We'll want to create a custom `index.html` for our app in the current directory:
69+
70+
[source,html]
71+
----
72+
<html>
73+
<body>
74+
<canvas id="canvas" width="400" height="300"></canvas>
75+
<script src="out/main.js"></script>
76+
</body>
77+
</html>
78+
----
79+
80+
Add the following source in `src/my_app/core.cljs` (or `src\my_app\core.cljs` if on Windows).
81+
82+
[source,clojure]
83+
----
84+
(ns my-app.core)
85+
86+
(def ctx (-> js/document
87+
(.getElementById "canvas")
88+
(.getContext "2d")))
89+
90+
(defn draw-shape [x y radius color]
91+
(set! (.-fillStyle ctx) color)
92+
(.beginPath ctx)
93+
(.arc ctx x y radius 0 (* 2 Math/PI))
94+
(.fill ctx))
95+
96+
(draw-shape 150 150 100 "blue")
97+
----
98+
99+
Now, let's use `cljs.main` to first compile this source (using `-c`), and, when done, start up a browser REPL (using `-r`), and additionally watch for changes in our source (using `-w`):
100+
101+
[source,bash]
102+
----
103+
clj -m cljs.main -w src -c my-app.core -r
104+
----
105+
106+
On Windows:
107+
108+
[source,bash]
109+
----
110+
java -cp "cljs.jar;src" cljs.main -w src -c my-app.core -r
111+
----
112+
113+
Note that we are also adding our `src` directory to the classpath.
114+
115+
When this launches, you should see a blue circle in your browser.
116+
117+
image::/images/content/news/2018-03-26-clojurescript-command-line/blue-circle.png[Browser REPL Showing Blue Circle,400]
118+
119+
Try interacting with the app by drawing other circles. For example, try this in the REPL:
120+
121+
[source,clojure]
122+
----
123+
(my-app.core/draw-shape 350 200 50 "red")
124+
----
125+
126+
image::/images/content/news/2018-03-26-clojurescript-command-line/blue-red-circle.png[Browser REPL Showing Blue and Red Circle,400]
127+
128+
What if you change your source? Change the `2` to a `1` in the `draw-shape` implementation, and refresh your browser. Now instead of circles, the app will draw semi-circles.
129+
130+
[[creating-a-node-app]]
131+
== Creating a Node App
132+
133+
In the previous sections, we were relying on `cljs.main` to establish a browser REPL environment. But, `cljs.main` has a command line flag (`-re`) that allows you to specify an alternate REPL environment.
134+
135+
For example, if have Node installed, you can use `cljs.main` to launch a Node-based REPL by supplying `-re node`:
136+
137+
[source,bash]
138+
----
139+
clj -m cljs.main -re node
140+
----
141+
142+
On Windows:
143+
144+
[source,bash]
145+
----
146+
java -cp cljs.jar cljs.main -re node
147+
----
148+
149+
If you do this, you will be dropped directly into a Node-based REPL:
150+
151+
[source,text]
152+
----
153+
ClojureScript 1.10.238
154+
cljs.user=> (+ 2 3)
155+
5
156+
cljs.user=> (exists? js/require)
157+
true
158+
----
159+
160+
Let's make a small Node-based app. Replace the contents of our `my-app.core` namespace with
161+
162+
[source,clojure]
163+
----
164+
(ns my-app.core)
165+
166+
(defn square [x]
167+
(* x x))
168+
169+
(defn -main [& args]
170+
(-> args first js/parseInt square prn))
171+
----
172+
173+
With this in place, let's run this app using `cljs.main` to run `-main` in a specified namespace (using `-m`):
174+
175+
[source,bash]
176+
----
177+
clj -m cljs.main -re node -m my-app.core 5
178+
----
179+
180+
On Windows:
181+
182+
[source,bash]
183+
----
184+
java -cp "cljs.jar;src" cljs.main -re node -m my-app.core 5
185+
----
186+
187+
Running this will automatically compile our namespace, launch Node, and execute our `-main`, passing our command line argument `5`, thus causing it to print `25`.
188+
189+
What if we'd like to produce a standalone JavaScript file that we can use with Node to do the same?
190+
191+
First, add one helper to the end of `my-app.core`:
192+
193+
[source,clojure]
194+
----
195+
(set! *main-cli-fn* -main)
196+
----
197+
198+
Now we are going to compile a `simple` (using `-O`) build, targeting
199+
Node (using `-t`), specifying where we'd like our final output file (using `-o`):
200+
201+
[source,bash]
202+
----
203+
clj -m cljs.main -t node -O simple -o main.js -c my-app.core
204+
----
205+
206+
On Windows:
207+
208+
[source,bash]
209+
----
210+
java -cp "cljs.jar;src" cljs.main -t node -O simple -o main.js -c my-app.core
211+
----
212+
213+
With this, you can copy `main.js` to wherever you'd like and run
214+
215+
[source,bash]
216+
----
217+
node main.js 5
218+
----
219+
220+
and it will print `25`.
221+
222+
[[running-tests-in-nashorn]]
223+
== Running Tests in Nashorn
224+
225+
The built-in Nashorn environment is accessible using `cljs.main`, and with it there is no need for any external JavaScript environment. Let's use this to run some tests.
226+
227+
First, add a new file for a `my-app.core-test` namespace
228+
229+
[source,clojure]
230+
----
231+
(ns my-app.core-test
232+
(:require
233+
[my-app.core]
234+
[clojure.test :refer [deftest is]]))
235+
236+
(deftest square-test
237+
(is (== 25 (my-app.core/square 5))))
238+
----
239+
240+
Let's run these tests under Nashorn (by specifying `-re nashorn`). To do things a little differently, let's use `-i` to load a resource, and `-e` to evaluate a form that will kick off our tests:
241+
242+
[source,bash]
243+
----
244+
clj -m cljs.main -re nashorn -i src/my_app/core_test.cljs -e "(cljs.test/run-tests 'my-app.core-test)"
245+
----
246+
247+
On Windows
248+
249+
[source,bash]
250+
----
251+
java -cp "cljs.jar;src" cljs.main -re nashorn -i src\my_app\core_test.cljs -e "(cljs.test/run-tests 'my-app.core-test)"
252+
----
253+
254+
With this, you will see
255+
256+
[source,text]
257+
----
258+
Testing my-app.core-test
259+
260+
Ran 1 tests containing 1 assertions.
261+
0 failures, 0 errors.
262+
----
263+
264+
[[other-affordances]]
265+
== Other Affordances
266+
267+
The above took you through a quick tour covering most of the options available in `cljs.main`. There are other options available, and you can get help on them by running
268+
269+
[source,bash]
270+
----
271+
clj -m cljs.main -h
272+
----
273+
274+
On Windows:
275+
[source,bash]
276+
----
277+
java -cp cljs.jar cljs.main -h
278+
----
279+
280+
A couple of interesting options that might be useful are `-co` and `-ro`. They provide the ability to configure any compiler https://clojurescript.org/reference/compiler-options[compiler option] or https://clojurescript.org/reference/repl-options[REPL option], (which go under `-co`) and REPL-environment-specific options (which go under `-ro`). These can act as an "escape hatch" if you need to specify something for which `cljs.main` doesn't provide a command-line flag.
281+
282+
For example, the following will apply the `:repl-verbose` option (thus showing the JavaScript being emitted while using the REPL):
283+
284+
[source,bash]
285+
----
286+
clj -m cljs.main -co "{:repl-verbose true}" -re node -r
287+
----
288+
289+
On Windows:
290+
291+
[source,bash]
292+
----
293+
java -cp cljs.jar cljs.main -co "{:repl-verbose true}" -re node -r
294+
----
295+
296+
You can specify EDN directly on the command line, as shown above, or you can supply the names of files containing EDN. With this capability, you can pretty much use `cljs.main` to do anything you'd like with the ClojureScript compiler.
297+
298+
We hope you find the new `cljs.main` feature useful and that it simplifies many of the common tasks you need to accomplish with the ClojureScript compiler!

0 commit comments

Comments
 (0)