Fork me on GitHub

@joelittlejohn see which should give some relevant info. not sure if there's any better docs elsewhere

👍 3

Hello everyone, I have a CLJS leiningen project that uses shadow-cljs, I am testing how environment variables would work if I used them for my use-case. Reading the docs I might end up with some solution using --config-merge as recommended, but I just want to understand the environment var interaction, and if it's even possible to use them. lein project.clj shadow excerpt:

:shadow-cljs {:nrepl {:port 8777}
:builds {:app {:target :browser
:dev {...}
:release {:closure-defines {some.config/API_URL #shadow/env ["API_URL" :default "some default"]}
Now if I just set API_URL to a value, it works and it's great, but I've got to conform to my company's (fairly common) DevOps setup where they have dev/qa/prod environments running in Docker containers. Variables like the URL here in this example is controlled / injected into Docker containers (I don't control this, but it probably ends up looking something like: docker run -d [other configs] -e SOME_VAR=SOME_VALUE some-artifact) I did above based on how I (mis)understood I get this error: No reader function for tag shadow/env, which looks like I've forgotten to install or configure something properly, what am I doing wrong? Perhaps this isn't supposed (or can't?) be used in the lein project file in this way? And finally, am I stupid? I'm so snowed into this environment variable line of thinking I no longer know if there's a better way to solve this problem for CLJS.


Shadow-cljs does not register shadow/env as a global reader tag. It can be used only within shadow-cljs.edn. Apart from that, using :closure-defines in your case will work if and only if you release the app with the very same API_URL env var as the one you will use during runtime. :closure-defines are compile time constants, not run time. If you need to move the same compiled app between environments, you cannot use :closure-defines.


To solve a similar task in my apps, I: - Ship frontend and backend together (I know that some people separate them for some reason I can't quite comprehend) - Make backend serve index.html with the relevant env vars' values embedded there within the <meta> tags - Read those tags' values in CLJS code so the values can be changed in run time


That's good information, it seems I might be approaching this problem from the wrong angle after all. I will check if the <meta> approach can work for me as well, thanks again @U2FRKM4TW!

👍 3

Suppose I’m running clojurescript tests using my browser’s javascript runtime, there is some async component I’m testing. I’d like my test to wait (“on the main thread” because that is what the test uses) for an event to finish. Is there a way to do this? core.async has channels but I don’t think you can wait on the main thread for them


You can't wait in a JavaScript at all. Well, at least in a browser, but I'm pretty sure you can't wait in NodeJS either. I think the test runner must have some async support for you to be able to properly run async tests.


Yeah, I believe the cljs.test/async is the right answer but I’m not sure the libraries I’m using macros are “playing well together”


I think it sets a timeout so that anything from core.async to js/Promise should work


Those examples might help. It turns out the library uses bindings which gets “reset” when using go blocks, so it’s still not straightforward the best approach


Also, if curious it is related to . I believe the wait-for api only wants nested wait-for to handle synchronization, given the implementation it won’t work nicely if you try using some other async control flow


javascript is single threaded so waiting/blocking the main thread would deadlock you since no other work would ever happen


Agreed, but I more mean waiting on the main isolate (?) if I’m using the term correctly.


(i.e. javascript does have mechanisms to model work concurrently)


depends on what async mechanism the code uses. if you get a promise you can .then it and use cljs.test/async as the others mentioned


Yeah, i think cljs.test/async is the right answer


Is there any way to create a second copy of the window.cljs object? (Basically, I have code generated with compile-str, that I would like to run in a different instance of clojurescript, if that makes any sense.


Like, if I run compile-str on (println "42"), I get:

",\"42\");\n\n//# sourceURL=UNTITLED/cljs.js\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVU5USVRMRUQvY2xqcy5qcyIsInNvdXJjZXMiOlsiY2xqcy5jbGpzIl0sImxpbmVDb3VudCI6NCwibWFwcGluZ3MiOiJBQUFBLDRCQUFBLDVCQUFDQSIsIm5hbWVzIjpbImNsanMuY29yZS9wcmludGxuIl0sInNvdXJjZXNDb250ZW50IjpbIihwcmludGxuIFwiNDJcIikiXX0="
I'd like to then that javascript string and eval it in a different context.


And so the question is where I can get a (new) copy of the cljs javascript object.


I don't think it's possible, or at least feasible in the way you describe. If your compiled code can be run in a web worker, each worker has its own global scope. Perhaps, that would be enough. Perhaps iframes could also help here, but they're rather hard to work with and it might be straight impossible due to security reasons.


Oh, I have a whole different window scope.


Like, imagine if I compiled in one tab, and then ran it in another tab.


(Or at least in this case wanted to run it*)


Then why do you need a copy? Why not just send the compiled JS code there and eval it?


the self-hosted code really should eval in the context it is compiled in if you want support for macros and such. if that is not so important you can just eval all the sources the build needs in the new context


the default CLJS node-repl is implemented that way. it just loads a basic JS file that sets up the socket connection and takes very simple commands. the CLJ side then just basically sends the JS output over there to eval


you could do the same principle from window -> window or window -> worker


@U05224H0W I mostly need the cljs runtime for stuff like println.