shadow-cljs 2025-06-19

I often fire up a node REPL in CLJS to test how the official CLJS works.

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "RELEASE"} -M cljs.main -re node
How can I do the same for shadow-cljs, without creating a project?

Perhaps cd $(mktemp -d) instead of the first three lines?

npm ok or clj preferred?

clj -Sdeps '{:deps {thheller/shadow-cljs {:mvn/version "RELEASE"}}' -M shadow.cljs.devtools.cli node-repl

$ clj -Sdeps '{:deps {thheller/shadow-cljs {:mvn/version "RELEASE"}}}' -M shadow.cljs.devtools.cli node-repl
Execution error (FileNotFoundException) at java.io.FileInputStream/open0 (FileInputStream.java:-2).
shadow.cljs.devtools.cli (No such file or directory)

Full report at:
/var/folders/j9/xmjlcym958b1fr0npsp9msvh0000gn/T/clojure-3130962719049242397.edn

seems like that didn't add the dependency then?

Ah, -m was missing:

clj -Sdeps '{:deps {thheller/shadow-cljs {:mvn/version "RELEASE"}}}' -M -m shadow.cljs.devtools.cli node-repl

now it's printing in a loop about a missing "ws" dependency

right, yeah thats required

I'll write a bb script to create a temp npm project then where this is launched

npx create-cljs-project foo; cd foo; npx shadow-cljs node-repl

is there a way to get rlwrap support, should I wrap it in rlwrap myself?

my script:

#!/usr/bin/env bash

cd /tmp
mkdir -p tmp-shadow
cd tmp-shadow
npx create-cljs-project foo; cd foo; rlwrap npx shadow-cljs node-repl

What's the accepted way of setting up shadow-cljs [lifecycle hooks](https://shadow-cljs.github.io/docs/UsersGuide.html#_lifecycle_hooks) so that they can talk to Electron? To make hot reloading with electron work, after every reload of the JS files, I need to call some methods on the open Electron window. The problem is, I don't think the electron renderer code (which is what I'd want the hooks to be on, obviously, since that's where all the application code actually lives) has access to the Electron window. And if I put the hook functions in the electron main code, shadow-cljs won't call them, because they won't be in one of the modules loaded in the build of the electron renderer code, where the actual hooks are. Here's my shadow-cljs.edn:

;; shadow-cljs configuration
{:source-paths ["src/" "../src/cljs" "../src/cljc" "../src/commonjs" "../src/clj"]

 :dependencies [[reagent "1.1.1"]
                [reagent-utils "0.3.4"]
                [cljsjs/react "18.2.0-1"]
                [cljsjs/react-dom "18.2.0-1"]
                [hiccup "1.0.5"]
                [re-frame "1.4.2"]
                [functionalbytes/sibiro "0.1.4"]
                [kibu/pushy "0.3.8"]
                [environ "1.2.0"]
                [clj-commons/cljss "1.6.4"]
                [org.clojure/test.check "1.1.1"]
                [com.cognitect/transit-cljs "0.8.280"]
                [lambdaisland/fetch "1.5.83"]]

 :builds
 {:main
  {:target :npm-module
   :output-dir "resources/public/generated/"
   :runtime :node
   :entries [electron.main
             electron.preload]}

  :renderer
  {:target :browser
   :output-dir "resources/public/generated/dev/"
   :modules {:main {:init-fn yact.core/init!}}
   :devtools {:reload-strategy :full
              :ignore-warnings true}}}}

not quite sure I understand the question

there 2 separate builds, each will individually reload their own files

so the main stuff reloads its code and the renderer reloads its code?

yeah. and that's kind of the root of the problem

because I need to call some methods on the electron window whenever code is reloaded, to make it actually update

so since the two builds are separate, if I want to make that happen for :renderer, I'd have to put hooks on that build

you don't put hooks on builds really. you put them in the code used by the build

well yeah I could do that too, you can use metadata to mark hooks, or put them in the :builds > :devtools config according to the manual

so I'd assume you have namespace that is only used by the main process and one only used by the renderer thing

looks like electron.main and yact.core

yeah, and the problem is that I need to run code in the main process whenever yact.core is reloaded

then you need to communicate that back to the main process I guess? not sure why you'd need that but its been many many years since I last look at electron, so no clue what is needed

> then you need to communicate that back to the main process I guess? that's what I was hoping to avoid, yeah. wondering if there was like, a better / more official way to do this

shadow-cljs just reloads the code and calls a function you specified. what that function does is entirely up to you

if you want to setup a repro that shows why this is all needed I can maybe make suggestions

otherwise I can't help much since I'm only guessing blindly

https://github.com/thheller/electron-cljs < I'm basing the whole thing off this project

(which I'm just realizing was you lmao)

so it should reproduce the issues. It looks like for setting the title it uses IPC. so I guess for hot reloading I need to do that too

felt like this was much longer ago 😛

😂 1

the reason it's necessary is that without it, the code is reloaded – as in, I see the compiler warnings and so on popup in the electron window and all that – but nothing actually changes

anything to do with javascript is so mad hatter logic I can't blame you

it probably gave you early onset dementia even looking at electron

"nothing actually changes" is entirely in your control. if its using something react based you need to re-render

the code in that repo doesn't do any hot-reload at all

I didn't have to do that in the web version, probably bc my coworker already had it set up

but yeah when I do an event that triggers a full screen reload... boom, the new code is in effect

neat, okay

(defn ui []
  [:div "hello world"])

(defn ^:dev/after-load start []
  (reagent/render [ui] dom-root))

something like that. again guessing totally blind, so absolutely no clue what you actually need

I get the gist, I think I can go from here. Thank you!

sweet, it works

for the renderer it'll be pretty much the same as the web version

for the main process things likely are a bit trickier since electron tends to hold a lot of state in some callbacks and stuff

but could be find to never actually need to reload anything there, given there is no "render"

yeah I'm not worried about the main process really; I won't be changing it enough to matter and I'm happy to reload electron when I do

and yes as it turns out, rerendering react was something I just hadn't noticed my coworker had set up for the web build (which uses figwheel... don't ask...) so I'd assumed it was a part of code hot reloading

it works now!

👍 1