Note, this was written for Ubuntu linux. Other distros, Mac and especially Windows will have differences in how you install these, but each of them have good instructions.
You need 3 things to get shadow-cljs up and running.
https://github.com/nodesource/distributions/blob/master/README.md#installation-instructions
from the command line, add the node repository, and install node. Here, we’re installing the LTS variant.
# Using Ubuntu
curl -sL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs
This will install shadow-cljs globally. It’s the most convenient way to use it.
sudo npm install -g shadow-cljs
npm init
npm install --save-dev shadow-cljs
shadow-cljs init
shadow-cljs watch app
and watch the build logFrom the terminal:
Create the directory you want to make your project in.
Run npm init
to initiate the project. This will create a
package.json file, where your npm dependencies will live
Run npm install --save-dev shadows-cljs
. This installs
shadow-cljs locally (in the previous section you installed it globally).
the --save-dev
flag puts the dependency in your
package.json file.
Run shadow-cljs init
to initiate the shadow-cljs
project. This will create a shadow-cljs.edn, which is where all your
clojurescript dependencies go, and also where your build
instructions live. It tells shadow-cljs where to run the dev-server,
where to look for the main function, and where to output the compiled
javascript.
Update shadow-cljs.edn using the below config. Here’s an explanation of the parts
main
function in the file (relative to the root)
src/main/my_project/main.cljsshadow-cljs watch app
.
:after-load
will tell it to run the reload
function in src/main/my_project/mains.cljs every time you
reload the application (which is every time you save a source file
you’re editing)Create a file public/index.html and put the below in it.
script
is pointing our compiled js from the
shadow-cljs.edn file we configured aboveCreate the file src/main/my-project/main.cljs and put the below code in it.
app
is your app - it’s what is turned into html and
rendered in the pagemount
uses reagent to get the app
and
render it in the html index page we set up - specifically under the div
with if app
we set up in that filemain
is what’s called on initialization of the app.
Here it just calls mount
. This is one of the things we
pointed shadow-cljs.edn toreload
does the same thing, but is only called when
the app is reloaded. Again, this is what pointed shadow-cljs to.Run shadow-cljs watch app
from the project root
directory. This will launch the application
Watch the build script - on the first launch it will update any dependencies necessary (including adding them to your package.json), launch all the infrastructure, and compile your clojurescript files to js, outputting them to where you instructed it. It will take a while to actually launch, because it has to compile everything. On reload (when you edit and save a .cljs file) it will only have to compile the changed files, so will be much quicker.
Once the build is complete, navigate your browser to
http://localhost:9090/. You should see “hello world” in an h1 block
(from your app
function in main.cljs).
Next, make sure the ‘reload’ is working properly - while the app is running, go to main.cljs and change the text in the h1 div from “hello world” to something else. When you save, your terminal should show that the project is recompiling, and your browser should quickly reflect the changed text.
For actually learning how build a clojurescript application with reagent and re-frame, I recommend Eric Normands various courses. They’re not free, but the quality is excellent, and he explains how all the pieces work very well.
{
"name": "qniform",
"version": "1.0.0",
"description": "",
"main": "index.js",
"author": "",
"license": "ISC",
"devDependencies": {
"shadow-cljs": "^2.20.1"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
;; shadow-cljs configuration
:source-paths
{"src/dev"
["src/main"
"src/test"]
:dependencies
"1.0.0-alpha2"]
[[reagent "1.0.0"]]
[re-frame
:dev-http {9090 "public/"}
:builds
:app {:output-dir "public/compiledjs/"
{:asset-path "compiledjs"
:target :browser
:modules {:main {:init-fn my-project.main/main}}
:devtools {:after-load my-project.main/reload}}}}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
</head>
<body>
<div id="app"></div>
<script src="/compiledjs/main.js"></script>
</body>
</html>
ns my-project.main
(:require [reagent.core :as r]
(:as rd]))
[reagent.dom
defn app []
(:div
[:h1 "hello world"]])
[
defn mount []
(
(rd/render [app]"app")))
(.getElementById js/document
defn main []
(
(mount))
defn reload []
( (mount))