Fork me on GitHub

i've got a question about the new hooks API. i'm trying to write a hook for defstate macro from lib. this macro supports adding certain i would like to lint this metadata node as well, but i cant figure out how to access it through the hooks API. it seems not be parsed into rewrite-clj node. example:

(ns example
  (:require [mount.core :as mount]))

  ^{:on-reload :noop}
  :start (do (prn "start") {:bar "buzz"}))
and in hooks ns:
(ns hooks.defstate
  (:require [clj-kondo.hooks-api :as api]))

(defn defstate [arg]
  (prn arg))
clj-kondo lint produces: {:node <list: (mount/defstatefoo:start(do(prn"start"){:bar"buzz"}))>} is this intended behavior? is it possible to access {:on-reload :noop} map?


@epransky The metadata node is accessible by fetching :meta on the node


I've deviated from vanilla rewrite-clj here to make parsing easier and not check for metadata node everywhere


thanks. if i understand correctly i should expect (:meta (:node arg)) to return {:on-reload :noop} ?


@epransky In the form (defn ^{:cool true} foo []) the metadata node for {:cool true} is part of the node for the symbol foo


just like in normal clojure where you expect the metadata to be attached


however, your defstate example looks a bit weird, keywords can't have metadata in Clojure


But maybe that's what you are trying to lint. The metadata should probably go onto the symbol


yes you are right. mistake by me. double-checking


Try: (doseq [c (:children (:node arg))] (prn c '-> (:meta c))) or something similar


ah, found it. after fixing the mistake to:

^{:on-reload :noop}
  :start (do (prn "start") {:bar "buzz"}))
(:meta (second (:children node)))
thank you!


You can put meta after foo but without ^


thanks. appreciate the help


@U04V15CAJ just to follow up on this, i thought about it and IMO we should be able to lint the metadata forms. like what if im trying to warn a user that the metadata declaration is not in the correct position, such as the mistake i initially made? i cant see a way for me to achieve this functionality if i need to rely on correct placement of the form. wdyt?


well, you can check for :meta on the symbol node and if it's not there, emit a warning?


what if meta is optional?


then lint accordingly?


is your question: what logic should I use for my problem, or is it a matter of clj-kondo not enabling you somehow to do it?


but the issue is that there is a form being passed which should not be there and the user might think they've configured some metadata when they havent. it seems that this is what linting is for. i actually think my mistake above illustrates perfectly:

  ^{:on-reload :noop}
  :start (do (prn "start") {:bar "buzz"}))
the metadata here is an optional functionality of the defstate API. i accidentally put it after the symbol instead of before. shouldnt linting help me out here to notify me that i cant put metadata on a keyword, as you pointed out?


yeah, so you can check for :meta on the keyword and then emit a warning right?


yeah. its possible. just seems a bit roundabout instead of just parsing the meta as a node of its own, but i guess its workable


what do you mean with "instead of just parsing the meta as a node of its own"? I'm not sure if I understand


im not sure what im thinking about is possible or logical, but when i started using the hooks API, i expected to the meta form to be a node in the children list of the macro. instead, what's necessary is to check each child node to see if it has meta and extract it. just seems like a lot of extra work than if the meta was first class in this situation


Ah that. Well, what rewrite-clj has by default is even more annoying: if you have ^:foo [] then the structure is the metadata node is the parent of the vector node. It's now structured this way because if it wasn't you'd constantly be checking if a node is metadata and if so, go down into the children for the thing you're actually looking for.


e.g. when clj-kondo analysis (defn foo []) you expect the second thing to be a symbol, but when users write (^:foo defn ^:bar foo []) then clj-kondo can still expect the name of the function to be the second child.


So that's the reason it is like it is now and that's unlikely going to change


i see. i agree clj-kondo is better. but i guess my issue is just that intuitively i see a certain number of forms in a list and i expect the children of the parent node to have the same number of forms. but i get the thought and as long as its documented i think ppl will learn to work with it


the way clj-kondo structures it also matches how it works in clojure itself: the second child is the symbol and if you want metadata from that, you call meta on it


ya, I do like clj-kondo’s rewrite-clj metadata node scheme better than rewrite-clj’s.


yeah i get the thought. thanks for explaining