Fork me on GitHub
#shadow-cljs
<
2023-07-05
>
Rachel Westmacott10:07:09

is there a macro for eliding development code?

Rachel Westmacott10:07:57

something like (shadow/when-dev <perform extra expensive validation>)

Rachel Westmacott10:07:35

this sounds handy, thank you

Rachel Westmacott10:07:21

and now I know what to look for I see it confirmed in the https://shadow-cljs.github.io/docs/UsersGuide.html too - awesome, thank you gratitude

🎉 1
Rachel Westmacott10:07:52

(I guess I was hoping for a macro to elide the code completely, but this sounds close enough)

Roman Liutikov10:07:13

it's gonna be removed by Google Closure compiler as dead code, so not on Clojure level, but when generated JS gets compiled by Google Closure

Rachel Westmacott10:07:53

ah, okay, well I guess that's exactly what i wanted then. thank you, I'd not appreciated that

Lone Ranger14:07:02

I'm getting a lot of this during shadow compilation:

The body of an ES6 module cannot reference this.
The code works fine though. Any thoughts?

thheller15:07:08

where do you get this? not an error I have ever seen before.

Lone Ranger16:07:08

I'm compiling TypeScript code to JavaScript that then gets consumed by ShadowCljs. Any code that has async stuff will get generated with code up to that looks like this:

Lone Ranger16:07:25

the ts config targets ES6 because that's the only way I could get the imports to work 😅 and the thing to compile

thheller05:07:26

that still doesn't tell me who or what gives the the error?

Lone Ranger14:07:44

Also can anyone comment if this build for :app looks correct (for purposes of including externs?) Ultimately I'd prefer to just include all the generated code as externs but I'm trying some hand written ones because I'm not sure how to programmatically include everything under the /src/js/gen folder

thheller15:07:01

why do you want to include externs in the first place?

thheller15:07:16

note that :deps is a top-level only key. it has no effect in the build config whatsoever

thheller15:07:16

why do you turn caching off?

thheller15:07:15

and don't even think about using your code for externs, thats not how externs work

Lone Ranger16:07:49

I wanted to include externs because things were getting renamed and I didn't want them to be

Lone Ranger17:07:04

regarding the caching, I've been bitten enough times by not clearing out the .shadow-cljs file that I turn off cacheing to try and prevent that from being an issue. Creates a more consistent experience across development environments

Lone Ranger17:07:24

It's more like a superstition probably

thheller05:07:52

well, unless you are doing some weird macro things caching should be fine

thheller05:07:02

at least I haven't heard or any bugs for many years

thheller05:07:44

neither have I cleared out .shadow-cljs, nor should you. I mean I'm all ears if you have a reproducible problem, just not aware of any.

thheller05:07:26

unless of course you do weird macro stuff, then all bets are off.

thheller05:07:38

how do you include the JS? the :js-package-dirs suggests (:require ["some-dir/that.js"]), in which cases the code doesn't even run through :advanced and externs will do absolutely nothing for that code

thheller05:07:32

if its however included via (:require ["/some-dir/that.js"]) (i.e. absolute or relative path), then it might go through :advanced (and you wouldn't need the :js-package-dirs entry

Lone Ranger19:07:44

@U05224H0W I use the (:require ["/path/to/js" :as js-code]) notation. I actually think what I'm seeing might be more a result of mixing "dot-attributes" and string access a little too carelessly. I try to use accessors when coding in typescript, but keywords when coding in clojurescript, and I think that I haven't been careful enough during serialization. At least that's where I'm at with it currently.

Lone Ranger19:07:37

@U05224H0W no macro stuff. Actually I switched to TypeScript to avoid writing macros.

thheller19:07:38

(:require ["/path/to/js" :as js-code]) is kind of dangerous. I don't really recommend doing that

Ben Lieberman16:07:10

Am I totally imagining things or is there a different way to use .cljc besides a build hook or AOT compiling and putting it on the classpath?

thheller16:07:15

I do not understand that question one bit

thheller16:07:22

what does AOT have to do with .cljc files?

thheller16:07:29

you use them like any other source file?

Ben Lieberman16:07:27

Sorry, bad phrasing and not an area I'm too familiar with. Basically I am using some Java classes but couldn't require the code in a .cljs file. But I used a macro and it works now.

thheller16:07:04

I'm lost. what do java classes have to do with cljs compilation?

thheller16:07:19

yes, macros run in CLJ, so they can load java code. still not sure what for though.

Ben Lieberman16:07:30

I was working with javax.crypto and the like, classes and APIs I know how to use. I don't know how to use equivalent functionality in JS so I did it in clj.

AustinP17:07:21

Does anyone have experience containerizing a cljs only, shadow-cljs project? Im trying to figure out how to correctly run shadow-cljs "in production" after building a release of my app. I've poured over the User Guide, and it seems like maybe running the watch is just the best/right way? The 'shadow-cljs server' command also seems to work, but gives the red warning banner about "stale output, is the watch running?". We are using shadow-cljs in conjunction with webpack if that makes a difference. Should i just be looking at how to run a client server only JS app in a container instead and leave shadow out after the release build?

Lone Ranger17:07:49

sounds maybe like you're talking about a node.js/backend style project?

Lone Ranger17:07:10

I'm just asking because there may be two senses of the word "server" here -- there is the shadow-cljs server (at least if you are targeting the browser), and then if you are making a server style application (aka with express or something), that's something else

AustinP17:07:24

Ahh gotcha, yes to clarify im talking about the shadow-cljs server, to run our code in the browser. We have a webapp project, thats just shadow-cljs, no deps.edn or leiningen. Feel like we shouldnt need to build a whole uberjar for cljs code. I can copy the release build into the container of course, but it feels wrong to run a shadow watch as the container entrypoint, when we dont need the hot-reloader.

Lone Ranger17:07:12

So are you serving the frontend code from the same server as the API/app server or those seperate concerns?

Lone Ranger17:07:42

For instance, all my frontend code is separate from my backend code, so in my case, I can just run regular shadow-cljs watch dev , and I just use some dev settings to configure the root URL of the API server I want to point to (and run the API server a different way, perhaps with a second shadow-cljs process or just a standalone node process)

Lone Ranger17:07:55

oh wait I don't even do that, I just have the dev apis commented out and I just comment them back in lol

Lone Ranger17:07:50

now if you server the frontend code as static files from the same server as the API server, then you need to configure some stuff in the shadow-cljs config

AustinP18:07:33

There is no API/app server, its just the standalone shadow-cljs code

AustinP18:07:40

I was thinking of just serving them as static files, but it seems like there needs to be some kind of shadow server still running, since opening the index.html after a release build doesnt show the webapp as expected, just the 'reagent/ref-frame code goes here' splash screen

AustinP18:07:07

or it shows the shadow-cljs stale output banner, which isnt ideal.

Lone Ranger18:07:34

so this is a front end app only?

Lone Ranger18:07:13

ok so if you check out my build above

Lone Ranger18:07:31

I have :output-dir "resources/public/js/compiled/" set

Lone Ranger18:07:36

to test my code

Lone Ranger18:07:43

I do shadow cljs release app

Lone Ranger18:07:58

then in a terminal window, I navigate to resources/public

Lone Ranger18:07:16

then I do python3 -m http.server 9500

Lone Ranger18:07:45

then when you go to localhost:9500 in the browser, it should load up the index.html located at resources/public/index.html

Lone Ranger18:07:19

In the bottom of my index.html, I have this:

<script src="js/compiled/app.js"></script>

Lone Ranger18:07:59

that corresponds the the compiled code that should be in resources/public/js/compiled (according to the :output-dir setting)

Lone Ranger18:07:33

in my <head> tag, I load in the CSS as per normal:

<link href="css/screen.css" type="text/css" rel="stylesheet">

Lone Ranger18:07:47

I use SCSS to compile all my CSS into a single stylesheet

Lone Ranger18:07:02

so to be clear, that's how I TEST the production deployment, I don't use Python's simple server for production, I just stick the static files in cloud buckets and wire up the domain routes. You could do the same thing with Nginx or Apache on a traditional webserver, though

AustinP18:07:42

hmmm okay. maybe i just need a static webserver docker image to serve the compiled release app. my index.html has the same script tags to get to the compiled app.js as you. I just keep getting the static reagent/reframe splash screen instead of my compiled app code

Lone Ranger19:07:39

did you make a custom index.html?

Lone Ranger19:07:11


<html lang="en">
  <head profile="">
    <meta charset="UTF-8">
    <title>MyApp</title>
    <link href="css/screen.css" type="text/css" rel="stylesheet">
  </head>
  <body>
    <div id="app"></div>
    <script src="js/compiled/app.js"></script>
  </body>
</html>
should look something like that

AustinP19:07:44

nothing custom, it looks basically like that, plus more css lines and another script line for the webpack bundle. Nginx might just be the move, trying to get it working rn

AustinP19:07:07

yeah i was overthinking it, a static webserver docker image got it up. Wish the shadow-cljs reconnecting banner didnt show though

Lone Ranger19:07:34

do you still have the shadow server running? Also might need to hard refresh to make sure you're getting the correct files if you're serving from the same port

Lone Ranger19:07:10

I tend to go out of my way to make sure caches are purged before testing because I've been bitten by that enough times

hifumi12321:07:22

FWIW i deploy my frontend by just serving (pre-compressed) static files with nginx

hifumi12321:07:31

pure shadow-cljs, no leiningen or deps.edn

hifumi12321:07:58

I recommend reading a lot on paths since this setup is sufficient unless you want to use Node.js + SSR

hifumi12321:07:39

my dockerfile is little more than having a "build" stage where I install java and node in alpine so I can npx shadow-cljs release app, then run gzip and brotli on the outputted JS, and finally, in a "server" stage, copy the files over and launch nginx

hifumi12321:07:57

this image then gets deployed to a kubernetes cluster (and since the frontend is trivially stateless + requires at most 5 MB of RAM, it suffices to have 1 replica and reserve most of your hardware for backend and microservices)

thheller05:07:37

just to be absolutely clear: you do no NEVER run shadow-cljs in production environments. never ever, don't even think about it.

thheller05:07:13

you create a release build and then serve the assets with any webserver you chose. doesn't matter, shadow-cljs is no longer needed in release.

thheller05:07:35

and if its showing a "reconnecting banner" it is not a release build, since only watch builds have the code for that