Starting a new ClojureScript project in 2022. Setup suggestions

The part that makes me mad is making these kind of statements in a thread that is likely to draw beginners. What kind of image does it set when experienced Clojurians basically tell you to not bother with CLJS? I get that you are not a fan and thats fine. Does it need to come up and derail a thread of someone actually trying to get started regardless? This is not the first time, and you are not the only one.

Luckily I don’t get angry for very long. Heck, I even agree with you and I’m not a fan of the JS ecosystem either. It is still a useful tool though, and pretending the “Host” doesn’t exist will limit your options drastically, exactly as in CLJ and saying you’d never use Java libs because you dislike Java.

As far as shadow-cljs docs are concerned I recommend starting with the quickstart and the guide introduction. Can’t cover everything and I’m working on it. PRs are very welcome in the docs department. Same goes for figwheel and the official clojurescript.org site I assume.

4 Likes

One additional thing I haven’t seen mentioned about ClojureScript vs Javascript is the difference in stability:

I have a Clojurescript project with most of its code probably more than 5 years old, most of it I haven’t looked at in a long time. Very rarely/never do I have to go in and change anything, even when I update ClojureScript. Things are just stable.

But I also have a static website being generated by Gatsby. I only regenerate it every couple of months, and each time I do there seems to be something that needs changing, or some dependency that does not exist anymore. Because I don’t keep up with the Javascript world enough, this is painful each time.

This is just an anecdote, so not sure how generally true this is, but at least my feeling is that while getting started with Javascript or Typescript might be easier, long term ClojureScript seems to be much simpler.

5 Likes

The Clojurescript tooling is also relatively simple. Similar to Clojure, it’s surprising how simple it is, you don’t even need a bunch of tool for any of this, just a REPL. You can call the Clojurescript compiler from the Clojure REPL, it’s all really quite simple.

Things get hairy because JavaScript never had a module system, and that’s where everything gets weird, and then the various JS runtimes, with accessing different runtime APIs, the browser object, and then the need for ridiculous level of compression and bundling to optimize delivery on page load, all inherent to JS really and not specific to Clojurescript.

I think the reason it’s not as easy are also just #2 and #4. There’s a lack of good up to date guides/tutorials for various situations you might encounter, and also they’re not easy to discover.

Actually didn’t mean it as that haha. I wrote the option because I think it’s a problem myself. Even if more experienced Clojure users all use their own different style, I think there would be benefits if there was a very clear “common track” for beginners and intermediate users, and if somehow at least the messaging could be more aligned for them. I’m sure that would help, I know beginners secretly get obsessed wanting to “learn the best way”. But sometimes what they need is to stick to the easiest way, until they get better.

Haha, that’s my biggest gripe with it as well. They copied the java command line style, which is the worse style I had ever seen before and always hated, but oh well. I would have preferred they’d stick to more standard Unix command line conventions personally. At least it’s just mostly cosmetic, I don’t like the syntax, but I do like the abstractions that tools.deps has setup.

Thanks for the feedback, #2 and #3 are things that the community can fix, if after you figure it out, you write a new more up to date blog about it for example, eventually, that should replace the old guides and rank higher in Google.

Make sure you’re explicit about what versions your blog is about though, so people similarly don’t think it’s “up to date” in the future when it might no longer be.

Good Luck with the rest of your project.

2 Likes

So, tonight I tried to help a beginner (on the Clojurians Slack), who had just created a Reagent app using the Leiningen Figwheel-Main template and had started a REPL from the terminal and was trying to connect to it from Emacs.

After a bit of back and forth and both of us reading various docs, we concluded that despite that being a good workflow for Clojure (starting a REPL, connecting to it from Emacs or another editor), it seems that ClojureScript tooling expects you to rely on jacking in and therefore letting the editor start your REPL.

He got that working and seemed happy at that point.

I tried from VS Code/Calva and got a “mysterious” error about the wrong number of arguments passed to an api/start function. So I deleted my test project and tried again, specifying +deps, since I prefer the Clojure CLI anyway, and this time VS Code/Calva was able to jack-in, start both a Clojure and a ClojureScript REPL and fire up the page in the browser.

Changing the source code and saving triggered a recompile and hot-reload in the browser (cool). Evaluating code (in VS Code) to modify the r/atom in the app-state worked and caused an immediate update in the browser (nice).

It was a bonus for me to discover that creating .clj files in the repo meant I could evaluate those as Clojure and that’s how I figured out there were two REPLs and Calva would pick the correct one (this was not obvious to me from either the Figwheel, Figwheel template, or Calva docs to be honest).

It took a couple of hours to go through all of this and it was pretty frustrating, based on the state of the docs and the error messages (in addition to the above, I had several false starts with Calva and Figwheel because it wasn’t clear to me that you should not select the :build/etc aliases when jacking in because those run their own :main-opts and that interferes with what Calva is trying to do… I eventually found that warning and stopped selecting aliases! Again, my intuition was to try to jack-in with the same options the template told me to use on the command-line!).

I really want to use re-frame but its template only seems to generate shadow-cljs projects so maybe over the weekend, I’ll try to figure out what I need to add to a reagent project to turn it into a re-frame project, while using figwheel-main

2 Likes

Converting this to a re-frame project wasn’t too bad: I created a scratch re-frame project with Leiningen (even tho’ it produced a shadow-cljs file) and then copied the source files into my Reagent project, added the re-frame dependency, and modified my main ns to invoke the init function for the newly-added re-frame core (but I’d love to be pointed to some docs that explain how to “upgrade” a Reagent project to a re-frame project – while still keeping all the build stuff you already have!).

I do still feel a bit like a monkey flailing around in the dark, just hoping that random stuff works…

Yes.

http://sdi.thoughtstorms.info/?p=1563

1 Like

Ya, the issue here is Clojurescript has no real REPL.

The actual REPL is in Clojure. The Clojure REPL will compile code into JavaScript and then send JavaScript to the JavaScript runtime to be evaluated.

The browser actually connects to the Clojurescript REPL. So in Clojure you start a Clojurescript REPL server, and from the browser you have JavaScript code (previously compiled from Clojurescript) which connects to the Clojurescript REPL server which is running in Clojure.

Now to make matters worse, this Clojure based Clojurescript REPL is not an nREPL, so if you want to use nRepl based tooling, you need to use piggyback, which is a nRepl middleware that under the hood use a Clojurescript REPL.

I recommend trying it raw, without any other tooling as described here: ClojureScript - REPL and Evaluation

When you use figwheel-main, you’re actually breaking standard Clojure convention, and instead of REPL-driven hot-reloads, you’ve switched to file based hot-reloads.

Figwheel-main will watch your files, and reload them, it’s more like traditional JS hot-reloading. The benefits over a REPL are that it also will reload JS and CSS files.

Now it’ll also set you up with a Clojurescript browser REPL, so you can get both benefits at ounce.

And it adds a few other extra bells and whistles.

But if you’re someone that like more minimal tooling, you can go with just a standard Clojurescript REPL and not use figwheel-main at all.

Edit: And I forgot to mention, Figwheel uses its own REPL implementation, not the standard cljs.repl one, so editors like Cider need to have special support for it as well.

1 Like

At this point I have eval via the REPL (from VS Code/Calva) working “live” for both clj and cljs, I have hot-reload when files are saved, and I have automatic test running in the browser (based on file save / hot reload). I can also run tests inside Calva using my regular hot keys and REPL snippets (for both clj and cljs).

I wouldn’t have figured it out from the docs so it took a bit of trial and error and guesswork – but this seems like a pretty good workflow at this point.

I can eval re-frame event dispatch from RCFs (comment blocks) without saving files and the UI updates immediately. I need to figure out a reasonable workflow to save/restore “db” state to quickly get back to where I was if I save because hot-reload resets the application state but that’s a relatively minor wart, given all the live eval options I now have.

I’ll probably run through this a couple more times and then write a blog post about it.

4 Likes

Going a similar route (figwheel main and the like), I think cider’s approach makes it pretty clear what is going on. There is a cider-jack-in-cljs that spins up the clj repl by presenting a selection option (including figwheel, figwheel-main, shadow, etc), then a dialogue for connecting to sibling-cljs repl asks which figwheel-main build to use (depending on your .cljs.edn files available, e.g. prod/dev/test) and it launches the cljs browser repl with those options. Then the browser pops up, the cider buffer indicates it’s launching the browser repl, everything connects, and you’re in. You can still eval code in the buffers without saving and have it sent to the browser repl (e.g. live eval), or copy/paste into the cljs repl (I do this quite a bit out of habit). You can do the browser-repl launch/connect process manually too and it should still work.

I think you may be looking at using defonce for maintaining state or going into the reloaded workflow for database stuff (I haven’t used these yet). Definitely a lot more surface area to deal with coming from a legacy clj repl setup.

1 Like

I need to read up on how re-frame manages its db state but, yes, that sounds likely based on my earlier experiment with Reagent.

It seems to be “just” a def but it turned out it was the call to init that was resetting things and I had that inline in my main ns – so now that call to init is in a defonce and it all “works”. I’ll figure out later what the recommended approach is :slight_smile:

re-frame-10x might be useful to you:

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.