Fork me on GitHub
#clojurescript
<
2022-11-02
>
quan xing04:11:06

I'm new to ClojureScript, which should I learn frameworks or libraries for real world's project. I've been working on Clojure projects for several months now.

localshred14:11:35

I’ve used this stack for a few years now and really enjoy the power and expressiveness • shadow-cljs for build/compile management • Reagent for a great react wrapper • Hiccup for a data-first approach to html • Garden for css • re-frame for a redux-like evented state manager • bidi for SPA routing

quan xing01:11:30

Thank you. @U5RFD1733 Should I study in the order you gave me

localshred02:11:34

you're welcome! enjoy!

Rupert (All Street)12:11:29

@U025AG2H55F - Consider doing a server side rendered UI Do check if you really want to do client side UI. Hiccup + htmx + HyperScript allows you to now build brilliant interactive User interfaces with just server side programming. It wouldn't be a good approach for things like 3D browser graphics, but for any standard business dashboard/Crud/Webapp UI it works very well and is performant. It also means that you have one codebase and don't need to build APIs from frontend to backend. Client side rendered UI If you are going to do a ClojreScript UI, then do check out #CNMR41NKB . With just a few hooks you can build very capable UIs and it's also very compatible with the whole react ecosystem (so you can easily use JavaScript react libraries like Ant).

quan xing07:11:24

What is HyperScript? the new programming language? What is uix?

Rupert (All Street)16:11:44

• hypercript is a small language for doing simple ui interactions - it can be embedded in html/hiccup. https://hyperscript.org/docs/ • Uix is a popular thin clojurescript wrapper around react - alternative to reagent and reframe. https://github.com/roman01la/uix

ingesol07:11:27

Hi! I’m trying to get DCE working. I have a namespace like this:

(ns app.main
  (:require [malli.dev.cljs :as md]))

(defn instrument! []
  (when ^boolean js/goog.DEBUG
    (md/start!)))

(defn ^:export on-reload []
  (instrument!)
  (load-app))
With this, I expect my shadow-cljs release build to not contain any traces of malli, but it’s still there. Did I misunderstand something?

thheller07:11:15

requiring the namespace brings it into the build. if the code is not written in a way that is DCE compatible it stays

thheller07:11:46

you should generally load dev-only namespaces via the :preloads option. those are only added for dev builds

ingesol08:11:02

Seems that malli instrumentation might not be suited for DCE then. Been experimenting a bit using preloads, and there are issues with reloading. I think that with preloads the instrumentation runs too soon, it needs to run as the first thing in the after-load handler. But this is more of a malli thing, so I should probably bring it over there.

thheller08:11:57

there have been previous discussions about this, so should be findable somewhere

thheller08:11:32

basically your preload ns just needs to require all your application namespaces

thheller08:11:38

that ensures they are loaded before the preload

thheller08:11:26

note that this pos is very old. a lof has happened in this area, not sure what the current state it though

ingesol08:11:25

You know what, I think that actually works. Adding a require for app.main in the preload ns and sprinkling a couple of {:dev/always true} around seems to make everything reload as expected. This is awesome and solves a major issue for me, thanks!

ingesol08:11:09

There actually is a mention of the preloads technique in https://github.com/metosin/malli/blob/master/docs/clojurescript-function-instrumentation.md. If this proves to work in a stable way for me, I might post a PR to document this properly

👍 2
dvingo14:11:23

I just updated that doc recently for release builds: https://github.com/metosin/malli/blob/master/docs/clojurescript-function-instrumentation.md#releaes-builds I was trying the :preloads technique (calling ! there) but wasn't getting proper recompilation. I wasn't including the app.main ns like you tried though, so yea if you find it's working, PR with an example would be great!

👍 1
krapjost10:11:50

Hello. I'm trying to learn clojure & clojurescript with shadow-grove. And i wanted to add few aliases on shadow-css, So i cloned it on my local machine and use it as my project's dependency. It worked perfectly fine yesterday, but i confront following error today and i'm struggle through whole day with it.

------ WARNING #1 - :undeclared-var --------------------------------------------
 File: /home/krap/project/pulp/src/main/pulp/ui/root.cljs:7:21
--------------------------------------------------------------------------------
   4 | 
   5 | (defc h1 [text]
   6 |   (render
   7 |    (<< [:h1 {:class (css :text-5xl)}
---------------------------^----------------------------------------------------
 Use of undeclared Var shadow.css/css
--------------------------------------------------------------------------------
The code i trying to execute.
(ns pulp.ui.root
  (:require
   [shadow.grove :as sg :refer (<< defc css)]))

(defc h1 [text]
  (render
   (<< [:h1 {:class (css :text-5xl)}
        text])))
My project's deps.edn
{:paths ["src/main"]
 :deps {thheller/shadow-css {:local/root "../clj/shadow-css"}
        thheller/shadow-grove {:local/root "../clj/shadow-grove"}
        thheller/shadow-cljs {:mvn/version "2.20.7"}}}
It would be much appreciated if someone give me some tips to solve this issue. I could't even guess by myself.

zeitstein10:11:57

It should be [shadow.css :refer (css)] in :require Also, come to #shadow-grove 🙂

😀 1
krapjost10:11:34

That's the problem! Thank you! It works!! but why? Isn't shadow-grove using shadow-css inside too? I don't get it.😵

zeitstein10:11:43

No, it's a separate library. (You can see you're including it separately in deps.edn.) shadow-css is not tied to shadow-grove in any way.

zeitstein10:11:53

Also, you can add new aliases without cloning the entire library.

krapjost10:11:06

Thank you zeitstein. I'll go look into it.

krapjost10:11:43

How can i do that? You mean i can add tailwind aliases without cloning shadow-css?

zeitstein11:11:05

Yes. You can modify the https://github.com/thheller/shadow-css#building-css step, something like:

(-> (cb/start)
    (update :aliases merge my-aliases)
    ...)

💚 1
krapjost11:11:26

Oh i got it. Thank you very much!!

blob_thumbs_up 1
thheller16:11:55

there actually is a shadow.grove/css macro. so that should be fine. maybe your local clone of shadow-grove is too old though?

1
thheller16:11:13

but yes, no need to clone/fork shadow-css to add your own aliases

zeitstein18:11:34

> there actually is a shadow.grove/css macro Forgot about that.

krapjost05:11:31

Thank you for answering me!

krapjost10:11:26

The classpath related to shadow-css is following.. which i could found via shadow-cljs classpath | grep css

... /home/krap/project/clj/shadow-css/src/main:/home/krap/project/clj/shadow-grove/src/main ... 
... :/home/krap/.m2/repository/com/thheller/shadow-css/0.2.0/shadow-css-0.2.0.jar: ...

Rivka10:11:58

Hello, How can I require a non clj file (html, png...) from my cljs code like we import in JS?

p-himik10:11:23

You can't. The most common approach is also the most simple and direct one - just refer to those files via URLs in the necessary contexts.

Yehonathan Sharvit13:11:58

Even with goog.require or foreign-libs?

p-himik13:11:14

Static resources aren't libraries. They are just files. You have to either embed them as data or request them from some server. For the former, in the CLJS world you can use a simple macro. For the latter, there's no simple solution. In the JS world, there's an easy solution though, that does a lot under the hood.

Panel09:11:25

Any tips on including a large edn file (~5-10mb) ? I went the macro way and it works great but it slows the re-compile on save by 2-3sec with shadow.

p-himik10:11:46

Such large files I would definitely get via a separate request or maybe embed as the very last item in the HTML. Either AJAX or some relevant tag in the HTML. Can be <script> with a custom type if it's not some data that a browser can read.

Panel10:11:21

Could you avec a link tag that point to some edn ?

Panel10:11:12

It’s not for prod, just to dev in the browser without involving some backend

p-himik10:11:56

You sure can. But you'll have to parse that edn yourself at run time. The script tag will store it as plain text.

Yehonathan Sharvit12:11:53

What happens under the hood with the js solution?

p-himik20:11:13

I'm only superficially familiar with webpack loaders. Some solutions bundle the data into the relevant JS module and add the data itself, like e.g. CSS styles, to the HTML when that module is loaded. Others do something similar but instead using the data itself, they refer to it via a URL. So e.g. you can have require('/public/css/root.css') somewhere in the code and, given an appropriate configuration that knows about the server routing, during compile time it would be transformed into some code that injects the right <link ...> tag into the HTML.

Petrus Theron12:11:47

How do I squelch ClojureScript type inference warnings for certain JavaScript objects, specifically Firestore documents? I’ve tried adding ^js metadata in front of (if ^js (.-exists doc) ...) but Shadow-cljs still complains. Is there an “any” type I can add to it?

------ WARNING #1 - :infer-warning ---------------------------------------------
 File: /Users/petrus/Projects/myproject/src/project/functions/core.cljs:147:3
--------------------------------------------------------------------------------
 144 |   (.set *doc (clj->js m)))
 145 |
 146 | (defn resolve-ticket! [tid]
 147 |   (go
---------^----------------------------------------------------------------------
 Cannot infer target type in expression (. inst_36995 -exists)
--------------------------------------------------------------------------------
 148 |     (let [*ticket (->*ticket tid)
 149 |           doc     (<p! (<get *ticket))]

p-himik12:11:10

Move that code outside of the go block. go loses the ^js metadata.

Petrus Theron12:11:35

Thanks, but I can’t move it out, the (.-exists snapshot) depends on consuming a Firestore promise:

(go (let [doc (<p! (-> db (.collection "mycoll") (.doc "123") (.get)))]
      (if (.-exists doc)                                    ;; <-- infer error.
        (do-something)
        (or-not))))

p-himik12:11:23

You can move it out - just extract it into a function and call that function in go. I didn't mean to not use go at all - I meant just not having nested forms.

p-himik12:11:40

The go macro rewrites the code, and that rewriting algorithm is simply not aware of ^js.

Petrus Theron12:11:13

(defn my-thing []
  (go (if (async-thing)
         true
         false) ;; how do I get at true or false from caller?

(my-thing) ;; <-- this is channel, so have to:
(go (<! (my-thing))) ;; same problem, no?
Can you show a minimal example? As far as I can tell, moving the promise logic into a function just moves the problem into a function. go-functions are “coloured”: go macro returns a channel, so to consume result of a go-block, the caller has to wrap the call in go-block too. At least, that is my understanding.

p-himik12:11:59

From your previous code block:

(defn doc-exists? [^js doc]
  (.-exists doc))

(go (let [doc (<p! (-> db (.collection "mycoll") (.doc "123") (.get)))]
      (if (doc-exists? doc)
        (do-something)
        (or-not))))
I would also do the same for the rest of the JS interop within the go block, even if they don't produce the warning. Unless those come from goog - in which case ^js is not needed.

Petrus Theron13:11:48

Ah, now I understand. By moving the the metadata into a function it won’t get rewritten by the macro. Thanks!

👍 1
Petrus Theron14:11:00

Hmm, I’m still getting inference warnings even if I move the Firestore Node calls to functions.

p-himik14:11:43

Well, that's because you didn't tell the compiler that *coll is actually a ^js.

p-himik14:11:19

Should be:

(defn doc [^js *coll id]
  (.doc *coll id))
or
(defn doc [*coll id]
  (.doc ^js *coll id))
whichever you prefer.

Petrus Theron14:11:38

Soo much better without the warnings