Fork me on GitHub
#fulcro
<
2021-08-26
>
Jakub Holý (HolyJak)09:08:07

What do you folks use to make #clj-kondo happy with Fulcro/RAD and Pathom code? There is https://raw.githubusercontent.com/clj-kondo/config/master/resources/clj-kondo.exports/clj-kondo/fulcro/config.edn but it only has defsc. Surely somebody has a more elaborate config?

tvaughan12:08:56

{:dependencies true
 :copy-configs true
 :config-paths ["com.wsscode/async" "com.wsscode/pathom"]
 :lint-as
 {clojure.test.check.properties/for-all clojure.core/let
  com.fulcrologic.fulcro.components/defsc clojure.core/defn
  com.fulcrologic.fulcro.routing.dynamic-routing/defrouter clojure.core/defn
  com.fulcrologic.fulcro.ui-state-machines/defstatemachine clojure.core/def}
 :hooks
 {:analyze-call {com.fulcrologic.fulcro.mutations/defmutation hooks.fulcro/defmutation}}}

🙏 3
tvaughan12:08:14

(ns hooks.fulcro
  ""
  {:author "Adam Helins"}
  (:require
   [clj-kondo.hooks-api :as hook]))

(defn defmutation
  [{:keys [node]}]
  (let [[_call
         sym
         & arg+] (:children node)
        docstring (first arg+)
        [[param+
          & fn-like+]
         docstring-2] (if (hook/string-node? docstring)
                        [(rest arg+)
                         docstring]
                        [arg+
                         nil])]
    {:node (hook/list-node (concat [(hook/token-node 'defn)
                                    sym]
                                   (when docstring-2
                                     [docstring-2])
                                   [param+
                                    (hook/vector-node (map #(let [[_sym
                                                                   arg+
                                                                   & body] (:children %)]
                                                              (hook/list-node (list* (hook/token-node 'fn)
                                                                                     arg+
                                                                                     body)))
                                                           fn-like+))]))}))

Jakub Holý (HolyJak)13:08:14

Thank you! Where do I place the hooks/fulcro.clj?

tvaughan13:08:17

.clj-kondo/
.clj-kondo/config.edn
.clj-kondo/com.wsscode
.clj-kondo/com.wsscode/pathom
.clj-kondo/com.wsscode/pathom/config.edn
.clj-kondo/com.wsscode/async
.clj-kondo/com.wsscode/async/config.edn
.clj-kondo/.gitignore
.clj-kondo/hooks
.clj-kondo/hooks/fulcro.clj

🙏 3
tvaughan13:08:38

com.wsscode was produced by clojure-lsp

😻 3
👍 3
tvaughan13:08:43

Sure. This could also live in the fulcro repo. Don't know which is preferable though

wilkerlucio13:08:01

in my computer I have this alias setup to pull in kondo export deps:

wilkerlucio13:08:02

alias kdeps="clj-kondo --copy-configs --dependencies --lint \"$(clojure -Spath -A:provided:dev)\" --parallel"

🙏 3
wilkerlucio13:08:50

I think Fulcro could do the same, to allow Kondo to pull the hooks in the same way

tony.kay13:08:57

namespace the hook properly and glad to add it to the library

👍 3
tvaughan13:08:19

Though I don't remember why I have that set

borkdude15:08:36

Hi all, I just merged an open PR for fulcro

borkdude15:08:40

to clj-kondo/config

borkdude15:08:50

please re-check that config and PR improvements as needed

borkdude15:08:55

we can also move that config to fulcro itself now that @U0CKQ19AQ gave the green light

👍 3
tony.kay16:08:11

yeah, it probably makes sense for additional contributions and edits of that to go on Fulcro’s dime, and not be noise on clj-kondo

borkdude16:08:56

@U0CKQ19AQ That also has the benefit that the config can be adapted per version of fulcro since it's attached to the artifact

tony.kay16:08:33

@U0522TWDA you mind cleaning that up (ns name to com.fulcrologic…) and sending a PR?

Jakub Holý (HolyJak)16:08:20

I have put that on my todo list 🙂

borkdude16:08:46

Note that I just merged a PR to clj-kondo/config for fulcro as well: https://github.com/clj-kondo/config/pull/12#issuecomment-906542425 Might be good to compare if something is missing on either side (and then deprecate the clj-kondo/config side)

👍 3
sheluchin16:08:28

Small nitpick, but I haven't seen any config for Fulcro's testing macros, like the => symbol Fulcro uses for assertions. I'm not sure how to fix it on my own, but seems topical to point out here. https://book.fulcrologic.com/#_behaviors_components_and_assertions

borkdude09:08:12

This has come up a couple of times. I think @adam678 has a complete config. I would suggest to either contribute it to fulcro or https://github.com/clj-kondo/config

sheluchin15:08:21

There is that one PR that looks like it has quite a few related improvements for Fulcro https://github.com/clj-kondo/config/pull/12

borkdude15:08:31

oh I forgot about that one

borkdude15:08:13

merged it

🙌 3
JAtkins16:08:37

Looks like the fulcro book online was republished without RAW docs. Section 18 is back to Dynamic routers.

tony.kay22:08:01

oopps. thanks…let me fix that

tony.kay22:08:04

should be fixed now

👍 3
Hukka17:08:23

I can't seem to get fulcro inspector working on the book.

book.js:267 Uncaught Error: Minified React error #200; visit  for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at Object.q.render (book.js:267)
    at book.js:5931
    at book.js:6128
is in console already at page load, and pressing any of those "Focus Inspector" buttons gives
VM95 :1176 Uncaught ReferenceError: book is not defined
    at HTMLButtonElement.onclick (VM95 :1176)
This is on Chrome, where fulcro inspector works on my own code

tony.kay22:08:48

Try now. It could have been I accidentally published an old book version

tony.kay22:08:15

oh wait…shoot, I really messed up the publish…give me a few

tony.kay22:08:43

Actually, it should be ok (now?). I republished without rebuilding js, but the js was the right version

Hukka09:08:15

Works now, thanks!

sheluchin19:08:20

I'm creating two entities in a single transact! call and using tempids for them (a list and an item in that list). The second mutation needs access to the server-generated realid of the first mutation. How should that be handled? Do I need to use :tempid->realid in ok-action to "patch" those persistent entities that need those realids?

Jakub Holý (HolyJak)20:08:57

What 2nd mutation? You mean y do (transact! [mut1 mut2])? I guess I would transact a single mutation and have its ok-action transact the 2nd mutation, with real ids.

sheluchin20:08:33

Yep, just like that. Okay, that makes sense. I just wasn't sure how to compose dependent mutations like that. Thanks.

👍 3
sheluchin20:08:34

@U0522TWDA I need some additional params for mut2 that are not required for mut1. I don't think there's anything wrong with just adding them to the param map, but do you happen to know how to access the param map inside the ok-action? I see that it's available in one form or another under various env keys like :transacted-ast, :transmitted-ast, and a bunch of others, but is there a "correct" one to use?

tony.kay22:08:57

@UPWHQK562 what do you mean by “Patching” the ids? Fulcro does that for you.

tony.kay22:08:34

There is never a need to manually rewrite tempids to realids if you implemented your server middleware correctly, though it is easy to mess this up (unfortunately).

tony.kay22:08:07

In fact, if you handle your own tempids you’ll probably miss something. See the list of what Fulcro does (for you) in that section. For example, rewriting the network queue is a very important aspect that is easy to not think about, and is very hard to do yourself (or in most other libraries)

tony.kay22:08:12

If you were using something else, you’d be sending these things with your own mechanism. A lot of ppl would just do an async request…well, now you cannot think about ordering of operations (the user quickly adds, then deletes something, before the ID is rewritten). If it’s a non-queue based async operation, you’re screwed. So, then you end up generating UUIDs on the client, but NOW you lose the ability to easily tell from a request (or app state) that something is new, and end up inventing some flag like “new?“, but you still haven’t fixed all the issues, because the nature of parallel processing in the async world means that the quick add/delete could see the delete be processed by the server before the add! Not to mention that you have to remember to remove the new? flag or you’re in a screwed up state..but it is only safe to do that after the server actually confirms the data was saved. These are the kinds of complexities Fulcro is trying to get out of your way. I highly recommend using the built-in tempid support.

Jakub Holý (HolyJak)07:08:55

@UPWHQK562 maybe if you explain a little more what/why you need? Do you use the built-in facilities for tempids? If I understand it well, your first mutation creates some entities and the second mutation then does some work on them (and thus needs their real IDs). I guess there are good reasons why you don't want a single server-side mutation that does both. As Tony said, Fulcro resolves the tmp to real IDs automatically but I am not sure what is the simplest way to get at them in the subsequent mutation.

sheluchin14:08:15

@U0522TWDA that's an accurate description of what I'm trying to do. I am indeed using Fulcro's tempid support as much as I understand how to: creating tempids in the UI and passing them to the mutation functions, and having the server-side mutations create realids, returning the tempid->realid map. Here's some sample code:

;;; ui.cljs
(let [item-list-id (tempid/tempid)
      item-id (tempid/tempid)]
  (comp/transact! this
   [(add-item-list
     {:item-list/id item-list-id})
    (add-item
     {:item/id item-id
      :item/item-list item-list-id})]))

;;; mutations.clj
(pc/defmutation add-item-list
  [{:keys [crux] :as env}
   {:item-list/keys [id] :as params}]
  {::pc/sym `add-item-list}
  (log/info "Creating" {:item-list/id id})
  (let [internal-id (UUID/randomUUID)
        updated-params (assoc params :item-list/id internal-id)]
    (m/add-item-list! crux updated-params)
    {:tempids {id internal-id}}))

(pc/defmutation add-item
  [{:keys [crux] :as env}
   {:item/keys [id text item-list ranges] :as params}]
  {::pc/sym `add-item}
  ;; FIXME: item-list here is the tempid, not resolved realid from
  ;;        add-item-list
  (log/info "Creating" {:item/id id :item/item-list item-list})
  (let [internal-id (UUID/randomUUID)
        updated-params (assoc params :item/id internal-id)]
    (m/add-item! crux updated-params)
    {:tempids {id internal-id}}))
The FIXME is where I'm having an issue - I need to persist the realid of the item-list instead of its tempid. There is not a good reason why I don't just do it in a single server-side mutation. I have got it working that way before, but I thought that having discrete mutations for adding the list and then an item to that list would be better than some compound mutation like add-list-and-list-item. Maybe I'm wrong in this case.