Fork me on GitHub
#shadow-cljs
<
2024-01-20
>
p-himik13:01:29

Is there a way to check if some top-level code is being executed during a hot reload? I'd like to add some assertions that should only be triggered on the very first load or in a production build, but not on hot reload during development.

thheller19:01:12

:init-fn exists so that there are never any side-effects during loading of a namespace. if you still have them you need to build something yourself to prevent that loading, or you something like defonce

p-himik20:01:39

Yeah, I'm using defonce. That's for global addEventListener which I want to be overriden during development without any issues and with a warning/error in production or when it happens in dev but during initial load.

thheller20:01:26

my tip is adding this in :init-fn via (js/document.addEventListener "whatever" (fn [e] (the-actual-event-handler e))

thheller20:01:50

with just (defn the-actual-event-handler [e] ...)

thheller20:01:11

that can be changed and hot-reloaded just fine with no need to ever add the event listener again

thheller20:01:58

note the extra wrapper fn to ensure this works, just (js/document.addEventListener "whatever" the-actual-event-handler) doesnt work

p-himik20:01:43

Mm, hmm, right. Will probably go that route, thanks. Re-frame has a similar problem of having to warn people about overriding events. But they decided to just check for DEBUG and never throw.

thheller20:01:34

yeah, global side effects are a problem for hot-reload

p-himik21:01:10

And given that some side-effecting things have to be top-level, is there a good pattern to make sure that some code gets executed before everything else? Currently, the main ns just requires the ns with such code as the very first one. But that's kinda brittle.

thheller21:01:18

there is no way to order besides the require order no

p-himik21:01:41

Maybe compiling that code as a separate module and mentioning it first in index.html? :) Quite a hassle though, probably not worth it.

thheller21:01:37

you can specify it in the build :entries, they also respect order

thheller21:01:07

(assuming the order can be maintained and that ns not requiring others to move them in front)

šŸ‘ 1
p-himik21:01:36

That actually sounds like the best approach so far. And :init-fn can also be specified then, right? Something like

:modules {:main {:entries [app.instrumentation app.core]
                 :init-fn app.core/init}}

thheller21:01:22

app.core is redundant, :init-fn adds that

thheller21:01:31

but yes, :init-fn is added last

thheller21:01:56

if you want this to be dev only use :preloads

p-himik21:01:51

Ah, cool, thanks! Yeah, it must be in prod as well.

Mandimby Raveloarinjaka15:01:04

Hello, I wanted to upgrade a dependency of a webapp from "materialize-css" to "@materializecss/materialize". In order to avoid going through all the code and replace the require statements I added a resolve js-options to keep the same name (and also switch to a cdn instead of installing it locally). "materialize-css" {:target :global :global "@materializecss/materialize"} However I end up with an error during the compilation. Any idea on what I a missing? Closure compilation failed with 1 errors --- global$module$materialize_css.js:2 Parse error. Character '@' (U+0040) is not a valid identifier start char

thheller19:01:00

:global resolves to a global variable name, you want :require there to basically redirect those requires

thheller19:01:46

otherwise it'll literally emit @materializecss/materialize as a JS variable name, which is not valid and why you are getting that error

Mandimby Raveloarinjaka21:01:03

As I said I wanted to see if I can use the cdn version too... But I guess I am not familiar enough with the javascript ecosystem to make it work. Indeed redirecting "require" works too if I use the non package. Thank you very much for your quick answer

Shako Farhad19:01:18

Has anyone gotten https://github.com/cjohansen/portfolio up and running with shadow-cljs and reagent? I have managed to connect enough up to get the portfolio UI to show me my button docs, but the button itself is not visible and the css files are not being found (404 in the console logs), despite setting them in the portfolio initial config path vector. Folder structure: portfolio-resources/public/index.html | js/compiled resources/public/css | index.html | js/compiled src/app/core src/portfolio/core I use shadow-cljs with deps.edn for deps and :portfolio alias.

Shako Farhad19:01:55

I think I found the problem. Initially I had this in my shadow-cljs.edn file.

:dev-http {9800 ["public" "classpath:public"]}
I then changed it to this, but this just made shadow-cljs serve the index.html in resources/public
:dev-http {9800 ["resources/public" "classpath:public"]}
I then removed index.html from resources/public and everything worked. But this is a problem. Our production index.html is in resources/public. I can't remove it just to have portfolio work. Portfolio needs to be able to see my css files etc in resources/public but without serving the index.html in the folder. Update: I added each folder individually like this and now it works.
:dev-http {9800 ["resources/public/css" "resources/public/fonts"
                 "resources/public/3D" "resources/public/assets"
                 "resources/public/images" "classpath:public"]}
Thank you for your time.

thheller19:01:37

this is most definitely the wrong solution

thheller19:01:02

adding all those paths as "roots" will mean that everything in those folders is accessible at the root of the server

thheller19:01:22

eg. resources/public/css/main.css becomes

thheller19:01:37

:dev-http {9800 ["public" "classpath:public"]} seems fine to me? you could also do :dev-http {9800 ["public" "resources/public" "classpath:public"]}

thheller19:01:08

it tries left to right until the first match, so that will take public/index.html over resources/public/index.html, but still serve the images from resources/public and so on

Shako Farhad19:01:00

You are right. I had to shift everything around to make this work with this. Is there any way to serve "resources/public" but not index.html that is in the folder? Like exlude that file? I thought maybe I could exclude the index.html by making shadow-cljs look for a index.html with a name that doesn't exist. I found this in the docs, but don't know how to use it properly.

:push-state/index
(optional) The file to serve. Defaults to index.html.

thheller20:01:26

what else do you want to serve? I mean some html is needed?

thheller20:01:34

if you want you can setup your own server and do whatever you want

thheller20:01:57

there is no need to use :dev-http at all, it is entirely optional. if you want your http server to do something specific you should create your own

Shako Farhad20:01:16

I want my index.html that is in portfolio-resources/public to access everything in resources/public excluding html and js files. Those don't matter. But I think the easiest thing for me to do is actually to just create symlink from resources/public folders into portfolio-resources/public. That way it will all be sorted and I don't need to duplicate folders and files. Update: Creating symlinks works great. Now I just need

:dev-http {9800 ["classpath:public"]}

thheller20:01:43

just add the 3 entries

thheller20:01:47

as I that the ordering matters

thheller20:01:06

if you use portfolio-resources/public as the first entry it will use that index.html over the later ones

thheller20:01:18

so I'm not sure what your problem is now

Shako Farhad20:01:46

I tried

:dev-http {9800 ["resources/public" "classpath:public" "portfolio-resources/public"]}
But it still loads the index.html that is in "resources/public" and not "portfolio-resources/public"

thheller20:01:33

because you are not listening to me

thheller20:01:42

LEFT to RIGHT

thheller20:01:57

if you put it last then resources/public wins and that index.html is taken

thheller20:01:37

:dev-http {9800 ["portfolio-resources/public" "resources/public" "classpath:public"]}

thheller20:01:00

> ordering matters

Shako Farhad20:01:24

I am sorry for misunderstanding!

Shako Farhad20:01:08

Yes, that works perfectly. I for some reason thought that the last index.html it finds is what it serves. šŸ˜…

Shako Farhad20:01:24

Thank you very much. As always, your help is much appreciated. Thank you!

thheller20:01:35

first one wins for everything, images, js, css and index.html

Shako Farhad20:01:57

I understand. That is perfect!

James Amberger22:01:41

How can I connect to the repl if Iā€™m serving the app from a non-shadow-cljs server?

āœ… 1
thheller22:01:28

same way as with a shadow-cljs server (assuming you are talking about the http server)

thheller22:01:37

the REPL runs over its own server shadow-cljs always starts (you'll see a websocket connect to it, default on port 9630)

James Amberger00:01:41

Makes sense now thanks