Reload In REPL (Shadow CLJS, Node.JS, No Front-end, Only Back-end)

Is it possible to reload the edited code (automatically or manually) in REPL? If so, how?

background

I decided to learn a bit of lisp by writing something that works. Long story short, I settled for ClojureScript and wrote a Telegram Bot. I have implemented this bot to learn a bit of Rust initially. And implemented a sub-set of that functionality in ClojureScript - and developed a small personal mindset (how it felt/tooling/etc) around ClojureScript.

But during this practice, I have only experienced the language itself, not the REPL - the REPLs that I have used before were from Elixir and a bit of F#.

I ran this command in one terminal:

$ npx shadow-cljs watch :cli

And in another terminal:

npx shadow-cljs node-repl

Which seemed to connect to the REPL running in the first terminal - because I could load (require) namespaces and call the functions. But after each change, I had to stop and restart the second terminal.

After hours of swimming in documentations/posts/etc I gave up and stopped working on the bot by finishing up the remaining parts.

Most likely, something is missing (maybe of the same kind as “to start with Common Lisp, you have to master Emacs” :grin:), but I expected to see some checklist style for setting up stuff - that’s why my preferred language to work with is Go. Nothing beats Go in terms of tooling and onboarding experience (and Elixir, too, comes close). I hope to see other good programming languages provide that someday!

If you have a REPL running, but no editor connected to it, you can still use (require '[namespace] :reload) to reload the namespace in question. Here is an illustration of that:

To connect your editor directly to the REPL, search for nREPL.

Also check out nbb which supports spinning up a REPL with:

npx nbb nrepl-server

and then you can connect directly to that Node.js REPL from your editor.

2 Likes

Thank you!

It turned out I needed (and did not know about) the :reload modifier. For instance (following your example) this (require '[ironsworn-companion.model.dice :as dice] :reload) did what I was looking for.

nbb is also very nice! I have installed it and played around with it. But I wanted to have full Clojure over my first working code base!

Which seemed to connect to the REPL running in the first terminal - because I could load (require) namespaces and call the functions. But after each change, I had to stop and restart the second terminal.

node-repl is entirely standalone, and not related to the :cli build in any way. It can however require any namespace that is on the classpath, it is just a separate node process.

To connect to an existing build you use npx shadow-cljs cljs-repl cli instead. Although node-repl is totally fine to use to. Since you have the build named cli I sort of presume that it is a script that is supposed to start and exit after running. The REPL/hot-reload websocket can get in the way of such workflows, since it keeps the process running. So, node-repl gives you the option of still using a REPL even though the :cli script may not be running.

1 Like

That does not seem to be the case (or I am missing something). Running node-repl:

$ npx shadow-cljs node-repl
shadow-cljs - config: /Users/blah/cljs-spike/shadow-cljs.edn
shadow-cljs - server version: 2.22.9 running at http://localhost:9630
shadow-cljs - nREPL server started on port 49668
cljs.user=> shadow-cljs - #4 ready!

But if we start the watch in one terminal:

$ npx shadow-cljs watch :cli
shadow-cljs - config: /Users/blah/cljs-spike/shadow-cljs.edn
shadow-cljs - server version: 2.22.9 running at http://localhost:9630
shadow-cljs - nREPL server started on port 49674
shadow-cljs - watching build :cli
[:cli] Configuring build.
[:cli] Compiling ...
[:cli] Build completed. (101 files, 4 compiled, 0 warnings, 3.61s)

And run node-repl in another terminal:

$ npx shadow-cljs node-repl
shadow-cljs - config: /Users/blah/cljs-spike/shadow-cljs.edn
shadow-cljs - connected to server
cljs.user=> shadow-cljs - #5 ready!

The output, in this case, indicates a new REPL is not started, and instead, it is connected to the currently running REPL.

I guess for me, either would be fine. Not sure how the reload works, though - does it load one module/namespace (similar to Erlang/Elixir)? or does it reloads the whole generated JavaScript file?

On hot reloading the application while it is running: I gave up on that part (for now, at least).

The watch or node-repl each start a shadow-cljs server, if not is not already running. This is not related to the CLJS REPL in any way. You can start the dedicated server via npx shadow-cljs server. It’ll do nothing but “serve” other commands. So, if you run that first and then npx shadow-cljs watch :cli, the second process will connect to the running server instead of starting a new one. That is what shadow-cljs - connected to server means. It is connected to the server, not the REPL. That is entirely different subject.

3 Likes

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