clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
athomasoriginal 2021-04-13T00:32:53.131600Z

For how browser routing works at a low level I highly recommend JS talks by any of the people who wrote the big JS routing libraries. They will explain the problem space context nicely. Then come back to cljs land with that knowledge and take a look at reitit and secretary to see how they handle the problem.

athomasoriginal 2021-04-13T00:35:12.134Z

Note that you don't need secretary to use reitit. I highlight those as libraries which are solving similar problem in different ways

Audrius 2021-04-13T05:50:01.134200Z

@tkjone Could you please recommend some library like that? I am new in this...

vanelsas 2021-04-13T07:41:01.138Z

I would like to display a date/time string in a Clojurescript/Reagent UI displaying the moment the code is build. And it should not change on reload. Can anyone explain how to accomplish that?

Karol Wójcik 2021-04-13T07:54:16.138900Z

Before compilation get from bash a date. Then slurp the file during the compilation on clj side and create a def out of it. Something like

(defmacro GEN_COMPILATION_DATE
  []
  (let [date# (slurp "file-with-date")]
   `(def COMPILATION_DATE ~date#)
   )
 )

vanelsas 2021-04-13T08:04:18.139200Z

Thanks! I had actually tried a macro approach in a cljs file (where the date/time was calculated once), but I could not get it to stay the same on reload

vanelsas 2021-04-13T08:05:13.139400Z

I'll give this approach a try

Joseph Rollins 2021-04-13T14:07:02.143500Z

Is there a simple way to convert a go block into a js/Promise which just resolves to whatever is put on the returned channel? I'm doing some interop with Firestore and it expects me to return a promise in a callback where I would like to use async to simplify some nested promise chains.

(defn update!
  [docRef f]
  (.runTransaction
   db
   (fn [tx]
     ;; doesn't work as runTransaction expects a Promise to be returned
     (go (let [doc (<p! (.get tx docRef))]
           (when (.-exists doc)
             (.update tx docRef (->> (.data doc)
                                     js->clj
                                     f
                                     clj->js))))))))

thheller 2021-04-13T14:09:06.143700Z

just return

(js/Promise.
  (fn [resolve reject]
    (go (do-stuff)
        (resolve "something"))))

thheller 2021-04-13T14:10:21.144700Z

as in you call resolve with the result when you are done. seems like it would be easier to just stay with promises and skip go completely though

➕ 1
Joseph Rollins 2021-04-13T14:13:40.145700Z

Thanks, you're probably right, it doesn't really get simpler if I have to wrap in a promise constructor anyway

(defn update!
  [docRef f]
  (.runTransaction
   db
   (fn [tx]
     (-> (.get tx docRef)
         (.then (fn [doc]
                  (when (.-exists doc)
                    (.update tx docRef (->> (.data doc)
                                            js->clj
                                            f
                                            clj->js)))))))))

wcalderipe 2021-04-13T15:49:26.146800Z

hey folks 👋 I wonder what are you using to manage configuration files/variables that depend on the environment?

nilern 2021-04-13T16:00:27.147800Z

Usually there is just some endpoint that returns the config map Or do you mean Node?

vanelsas 2021-04-13T16:00:46.147900Z

For those interested in something similar. I ended up using the following macro (the use of the time library seems a pragmatic choice)

(defmacro generate-build-timestamp []
  (let [date-time (tick.alpha.api/date-time)]
    (str date-time))
  )
Using the explanation here: https://code.thheller.com/blog/shadow-cljs/2019/10/12/clojurescript-macros.html I managed to get the output of the clj macro in cljs and use that in the UI. Macros in Clojurescript are quite tricky :-)

wcalderipe 2021-04-13T16:23:04.149500Z

Not sure if I understood your point. I mean at compile time the build should some variables depending on the environment. For example, API keys for production and development.

Empperi 2021-04-14T07:05:43.156500Z

Why not just include the config file into the HTML that is generated? I mean, it can be inline and just read it from there. Or you can request it on page load from the server as a resource. Or whatever. There's several options

p-himik 2021-04-14T07:09:44.156700Z

That's exactly what I pointed to above when I mentioned the <meta> tags. :) One could also use a <script> tag with a custom type attribute so it's not parsed and evaluated by the browser.

wcalderipe 2021-04-14T16:24:59.176900Z

> Indeed, but its never been a show stopper for me. I use figwheel and just locate the configs in each respective build file. Never been an issue Would you have an example hanging around? 🙂 > But you shouldn’t need to choose an approach based on the dev tool your using (figwheel, shadow-cljs, clj etc). So I personally would go with a choice that doesn’t bind you to any of these. Totally agree. I ended up using a config file. I have tried to use juxt/aero too because its #profile feature is very nice to have a single source of truth. However, I'd some issues with it. It seemed to only work with CLJS on node targets. I don't know. > Why not just include the config file into the HTML that is generated? For our context, that's not practical because our app is highly integrated with many third parties. Therefore, the configuration files are quite large.

athomasoriginal 2021-04-14T16:34:43.177100Z

> Would you have an example hanging around? This is going to be highly boring, but https://gist.github.com/athomasoriginal/fb6b7ef4da3298d63a5e0f4c0e62f859. So each of these is a fighwheel build config. I have one for each environment. I repeat the variables. Could this add maintenance headache? Yes. It hasn’t yet because these configs are less than what a backend would need and don’t change frequently. These files in the gist live in the root of my project directory

🙌 1
athomasoriginal 2021-04-14T16:35:42.177300Z

For another tool like shadow-cljs the process might be a bit different. If you were using vanilla clj tool I would just have multiple aliases and each alias specifies it’s own google closure compiler settings.

👍 1
nilern 2021-04-13T16:31:48.151300Z

Well this is #clojurescript so I thought getting those to the frontend would be the focus

nilern 2021-04-13T16:33:27.152100Z

The server would have already sucked those in with https://github.com/metosin/maailma or https://github.com/juxt/aero or whatever

nilern 2021-04-13T16:34:11.153Z

And of course the frontend should not get stuff like API keys anyway

p-himik 2021-04-13T16:34:43.153100Z

If you're using shadow-cljs, env vars usage is well documented.

p-himik 2021-04-13T16:35:44.153300Z

Also note that if you want to use different var values in different environments, you will have run a build per environment. This makes it impossible to use some deployment scenarios. Just FYI.

athomasoriginal 2021-04-13T16:39:47.154Z

As noted, the two most common ways are: • closure-defines • A configuration file tool (e.g. Aero) or a simple macro If we are strictly talking about front end, for example specifying a different public API key by environment (dev vs. prod) then I would say closure-defines is a solid choice. Some will argue that closure-defines has some interesting behavior which can lead to gotchas, but once you learn what they are they are relatively straightforward to overcome.

wcalderipe 2021-04-13T17:25:13.154400Z

Thanks for your replies, folks. I was probing to see what people are doing out there since I'm not a front expert. Before coming here, I played with clojure-defines to set a single env variable (e.g. prod and dev). Later, the code would do the branching. The downside is my compiled files are including too much info now. So, I'm thinking about having closure-defines for each specific API key the app has – which may turn big super fast. How can I pick Aero's config file from within shadow-cljs at compile time? :thinking_face:

p-himik 2021-04-13T17:29:50.154600Z

You can't, not without patching shadow-cljs itself. But even if you could, how would you use it in the CLJS files? Also, another FYI just in case - if the resulting JS bundle is something public and those API keys aren't supposed to be public, then don't do it. Route all thirdparty API requests through your backend instead. Otherwise, anyone will be able to just steal those API keys and use them themselves.

p-himik 2021-04-13T17:31:52.154800Z

Actually, I might be wrong on the "you can't" part, if the value is substituted during macro expansion. So shadow-cljs is not even in the picture. But I've never tested it. I've largely moved away from using env vars during compilation and instead I just serve the values via the <meta> tag.

wcalderipe 2021-04-13T18:01:11.155100Z

> But even if you could, how would you use it in the CLJS files? I don't know yet. At the end of the day, what's important is that keys and values are set (maybe using goog-define?) at compile time. The file only brings a nice organization. Let's say I could have prod.edn and staging.edn and load one of them at compile time (`shadow-cljs release`). But I may be overengineering here, so that's why I was asking how people handle this problem. > Also, another FYI just in case - if the resulting JS bundle is something public and those API keys aren't supposed to be public Thanks for caring. They're not secrets and can be exposed to public 🙂

p-himik 2021-04-13T18:03:44.155400Z

Here's the relevant documentation that I mentioned: https://shadow-cljs.github.io/docs/UsersGuide.html#shadow-env Yes, the keys in the :closure-defines map should be used in tandem with goog-define. I've never used #shadow/env myself though.

athomasoriginal 2021-04-13T18:14:49.155600Z

> The downside is my compiled files are including too much info now I’m not sure what you mean by this? > How can I pick Aero’s config file from within shadow-cljs at compile time same with this 😛 > The file only brings a nice organization. Indeed, but its never been a show stopper for me. I use figwheel and just locate the configs in each respective build file. Never been an issue 🤷 But you shouldn’t need to choose an approach based on the dev tool your using (figwheel, shadow-cljs, clj etc). So I personally would go with a choice that doesn’t bind you to any of these. In clojurescript its easier to do this because it comes with a a set of tools that can handle this and if that doesn’t work, dev tool agnostic solutions are abound 🙂 . Either way, answer to what I would do is the same.