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.
Note that you don't need secretary to use reitit. I highlight those as libraries which are solving similar problem in different ways
@tkjone Could you please recommend some library like that? I am new in this...
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?
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#)
)
)
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
I'll give this approach a try
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))))))))
just return
(js/Promise.
(fn [resolve reject]
(go (do-stuff)
(resolve "something"))))
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
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)))))))))
hey folks 👋 I wonder what are you using to manage configuration files/variables that depend on the environment?
Usually there is just some endpoint that returns the config map Or do you mean Node?
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 :-)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.
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
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.
> 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.
> 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
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.
Well this is #clojurescript so I thought getting those to the frontend would be the focus
The server would have already sucked those in with https://github.com/metosin/maailma or https://github.com/juxt/aero or whatever
And of course the frontend should not get stuff like API keys anyway
If you're using shadow-cljs, env vars usage is well documented.
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.
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.
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:
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.
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.
> 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 🙂
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.
> 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.