I'm trying to setup a basic Clojure backend + ClojureScript frontend, using shadow-cljs. Is there a minimal example/tutorial out there that shows exactly what's needed in my deps.edn and shadow-cljs.edn? My current goal: 1. Be able to open the folder in VSCode and Jack-in with Calva to both the CLJ and CLJS Repls, if possible without having to start one or more repls/watches on the command line prior to Jacking in That's it really...
I’m not sure what you mean by that the shadow repl is realized via a dev http server. The dev http server is rather a convenience that you can use instead of your own server. The important part is that your app is started. This realizes its repl. The app could be running in any JS environment, browser or otherwise. It doesn’t need to do anything. It can be created from scratch by you via the repl just like a JVM Clojure app.
yes, :dev-http is a purely static http server. it is not related to the REPL in any way. the websocket used for the REPL is a separate server, usually at :9630 and not related to whatever served your html
Okay fair enough. But is it correct to say that the websocket needs to be injected into the cljs output and shipped to the browser with it?
I got the impression from the thread that OP did not see the connection between the browser opening and the REPL (browser that is in this case) in the first place and I felt the the why of that was left unaddressed
the websocket code is injected into the generated .js directly. it will execute and connect once that .js file is loaded. how that is loaded is not of concern to shadow-cljs, it just needs to be a compatible runtime (e.g. browser)
Ah! That makes sense. The clojurescript repl needs to run, well, in a JavaScript runtime. Whether that’s node or the browser doesn’t matter. So when my :build targets the browser, then of course the repl itself won’t start until JavaScript starts running which means I need to load a page that runs the generated js. If I understand/reworded this right?
correct
Great! Thanks for all the explanations
I’ll look into adding a button to that start-your-app progress popup that will add some more context. May use your rewordings a bit @patrix.
Sure thing!
For that setup, you only need {:deps {thheller/shadow-cljs {:mvn/version "..."}}} in deps.edn and {:deps true} in shadow-cljs.edn.
Hm so there must he something wrong with my environment, because calva is stuck waiting for the shadow-cljs repl to be ready and never connects to it.
Have you loaded the app in the browser?
Here’s a mini-example I made: https://github.com/PEZ/shadow-w-backend
Thanks, I’ll check those in a couple of hours.
Alright, going to try this simple example first before I read those 2 posts and check PEZ's repo:
@p-himik
> For that setup, you only need {:deps {thheller/shadow-cljs {:mvn/version "..."}}} in deps.edn and {:deps true} in shadow-cljs.edn.
When I try this super minimal example, I get this error from Calva when it tries to connect to cljs:
> ; Error while connecting cljs REPL: TypeError: Cannot read properties of undefined (reading 'label'):
@pez > Have you loaded the app in the browser? Not sure, I think there is no app yet, at least not when I start from scratch. I have an empty repository. Will check out your repo...
@pez I cloned your repo, and as per the README, I started a new REPL selecting only shadow-cljs in the Calva menu rather than deps.edn + shadow-cljs .
Now vscode is stuck here (see screenshot)...
Oh I see, I had to load the browser on localhost:8700 to see the page and now Calva is connected. Hmm. That wasn't an obvious step.
I remember long ago I was automatically getting some page to load (not the app itself but some shadow-cljs or reframe console or something) in the browser (though that was using the more complex/complete Luminus template).
@patrix Ah, I missed that you also mentioned CLJS REPL.
In that case, the most bare-bones setup, in addition to that deps.edn, requires shadow-cljs.edn containing
{:deps true
:dev-http {8000 "public"}
:builds {:main {:target :browser
:modules {:main {:init-fn my-app.core/main}}}}}
a public/index.html file containing
and a src/my_app/core.cljs file containing
(ns my-app.core)
(defn main []
(js/console.log "Hello, world!"))
Then:
1. Jack-in, select deps.edn + shadow-cljs
2. Wait for shadow-cljs to compile the app, open the printed URL (should be )
3. Open the JS console in that tab just so you can see stuff being printed there - confirm that it has Hello, world!
4. In the REPL run (require '[shadow.cljs.devtools.api :as shadow]) (apparently, Calva does not require shadow.user even though its REPL knows about shadow-cljs)
5. Then run (shadow/repl :main)
6. Type anything and see that it worksCalva does tell you to start your ClojureScript app. 😀 I think I may have edited that Luminus template to auto-open the browser, but generally Calva doesn’t know where the app should be run.
@pez > Calva does tell you to start your ClojureScript app. It wasn't obvious what it meant (start what app, how, and where? Especially when it's barebones and I haven't define any app/port/namespaces yet..) @p-himik Will try that expanded example! Thanks @thheller I've tried that blog post (for the 2nd time, the first time I think I tried to be too clever and integrate it into an existing repo that had a demo simple ring app), and seems to be working somewhat though Calva does complain about missing nrepl middldewares. That's something I can work on though. Appreciate the help all of you.. I think you set me on the right path now and will report back!
Ok, combining all the advice above, I've been able to run both REPLs, and no complaints from calva about missing middleware either. I now see where the shadow-cljs dashboard is mentioned in the jack-in terminal window, as well as the shadow-cljs dev-server I've removed the dev-http part of shadow-cljs to use my existing ring + jetty server, seems to work fine as well!!! WHEW.. thanks @p-himik, @pez, @thheller!
> It wasn't obvious what it meant (start what app, how, and where? Especially when it's barebones and I haven't define any app/port/namespaces yet..) > FWIW, I had the exact same issue before, with the exact same questions running through my mind. I'm not a web dev by trade, so I thought maybe it's just a me problem. It's kind of interesting that, unlike a pure clojure application that I can build interactively from zero, a full stack application requires a degree of scaffolding already present before I can have a functional repl environment to work in (a backend serving up my application, an HTML file, and a clojurescript entry point)
I think that part of it comes from that Calva tries to help you all the way, so it can feel stuck when it has connected the server repl and is waiting for a frontend repl to connect too. Unless you are in the know about what needs to happen. That’s part of the reason why my example project has a README that tries to encourage you to do the steps from the prompt and better understand what it is you get help with. For Luminus I added some post-repl-start-code that opens the browser on the app address. But as we learn here that also has drawbacks because some time later you’re building on a project that doesn’t have this code…
I’ve also written a bit about the mechanics involved here: https://blog.agical.se/en/posts/shadow-cljs-clojure-cljurescript-calva-nrepl-basics/ (And that article links to a Youtube video with about the same information.)
It wasn't obvious what it meant (start what app, how, and where? Especially when it's barebones and I haven't define any app/port/namespaces yet..)So I am by no means particularly knowledgeable about how this stuff really works, but the way I conceptualize it is that CLJS build tools that provide a REPL (for browser CLJS) do this via injecting a Websocket into the Website when they run a development HTTP server. That is why a "shadow-cljs" REPL according to my understanding needs to have the browser window open. Also you need to have a JS runtime for the REPL to work. When you do a browser REPL, that is an open browser window. So a shadow REPL is realized via a dev HTTP server which serves your index.html and injects the REPL websocket (?) in there. This carries you all the way through SPA development, I don't actually know how you would realize this in an actual multi-page app. Probably quite the same
FWIW npx shadow-cljs browser-repl gets you a REPL that opens a window on its own. (shadow/browser-repl) from the CLJ REPL. this isn't coupled to any build, so even works without any build config. if you just want to poke at stuff from the REPL thats enough