clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
2021-03-10T09:42:16.277400Z

@joelittlejohn see https://swannodette.github.io/2014/12/17/whats-in-a-var/ which should give some relevant info. not sure if there's any better docs elsewhere

šŸ‘ 1
greensponge 2021-03-10T12:35:00.279900Z

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 https://shadow-cljs.github.io/docs/UsersGuide.html#shadow-env 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.

p-himik 2021-03-10T12:44:22.280Z

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.

p-himik 2021-03-10T12:45:56.280400Z

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

greensponge 2021-03-10T13:09:12.280600Z

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 @p-himik!

šŸ‘ 1
johnnyillinois 2021-03-10T17:09:59.283900Z

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

p-himik 2021-03-10T17:14:16.284Z

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.

thheller 2021-03-10T17:18:27.284600Z

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

nilern 2021-03-10T17:20:54.284800Z

https://cljs.github.io/api/cljs.test/async

ā˜ļø 1
johnnyillinois 2021-03-10T17:25:02.285Z

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ā€

johnnyillinois 2021-03-10T17:26:52.285200Z

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

johnnyillinois 2021-03-10T17:27:39.285400Z

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

thheller 2021-03-10T17:29:07.285600Z

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

johnnyillinois 2021-03-10T17:35:00.285800Z

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

nilern 2021-03-10T17:36:22.286Z

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

nilern 2021-03-10T17:40:17.286200Z

Even wrapping a Promise in my own monad worked https://github.com/nilern/taksi/blob/master/test/taksi/core_test.cljc#L87

šŸ™Œ 1
johnnyillinois 2021-03-10T17:49:51.286700Z

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

johnnyillinois 2021-03-10T17:59:18.286900Z

Also, if curious it is related to <https://github.com/day8/re-frame-test/blob/master/src/day8/re_frame/test.cljc> . 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

2021-03-10T20:25:10.288800Z

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.

2021-03-10T20:32:25.289800Z

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

"cljs.core.println.call(null,\"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.

2021-03-10T20:32:43.290300Z

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

p-himik 2021-03-10T21:02:55.290400Z

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.

2021-03-10T21:12:45.290600Z

Oh, I have a whole different window scope.

2021-03-10T21:12:55.290800Z

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

2021-03-10T21:13:11.291Z

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

p-himik 2021-03-10T21:14:12.291200Z

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

thheller 2021-03-10T21:26:57.291400Z

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

thheller 2021-03-10T21:28:49.291800Z

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

thheller 2021-03-10T21:29:17.292Z

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

2021-03-10T22:14:11.292700Z

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