clj-kondo

Jack Arrington 2025-12-03T09:10:43.413409Z

In my project (CLJS/CLJ fullstack app with a lot of .cljc files) I have a macro called cljs-only that only includes its body when in CLJS. Looks like this:

(defmacro cljs-only
  "Wraps code that should only be included in ClojureScript builds.
  In Clojure, expands to nil. In ClojureScript, expands to the body.

  Example:
    (cljs (some-frontend-func args))

  Instead of:
    #?(:cljs (some-frontend-func args))"
  [& body]
  (if (:ns &env) ;; &env is non-nil in ClojureScript compilation
    `(do ~@body)
    nil))
It's just a convenience thing, nicer than typing #?(:cljs (some-form)) a million times. However, this causes clj-kondo to go haywire, because if it sees use of js-specific imports inside the cljs-only macro, it gives me a ton of Unresolved symbol errors. Is there some way around this? A way to force clj-kondo to treat this macro as .cljs? I don't know a lot about hooks, but maybe they are applicable here?

borkdude 2025-12-03T09:18:54.097529Z

You could do this with hooks indeed :) Perhaps this macro even works by just copying it to .clj-kondo/your.org/your_app.clj and hooking it up with :hooks {:macroexpand {..../cljs-only .your-app/cljs-only}

borkdude 2025-12-03T09:19:10.632349Z

the newest clj-kondo has :ns in the env for CLJS compat I think so that should work

seancorfield 2025-12-03T14:28:23.393419Z

Is :hooks > :macroexpand the recommended "first choice" for dealing with macros in clj-kondo these days?

borkdude 2025-12-03T14:30:34.805019Z

Not necessarily. It's the easiest option, but not the most fine-grained option since you can lose location information

seancorfield 2025-12-03T14:31:59.431099Z

Ah, interesting. I may revisit some of the older hooks I have in my OSS projects since some of those hooks are more complex than the macro expansion they are trying to support 🙂

borkdude 2025-12-03T14:48:27.716409Z

if you went through the trouble of making analyze-call hooks, I would't downgrade them to macroexpand hooks

borkdude 2025-12-03T14:49:12.566989Z

analyze-call hooks are more reliable since no information is lost

👍🏻 1
lread 2025-12-03T15:25:10.391729Z

Then there is the choice to throw an exception from your hook or to create a finding. Creating the finding provides better positional resolution of the issue, so folks get those error squiggles in their editors where the issue really is. Recent example I worked on: • config: https://github.com/babashka/fs/blob/v0.5.30/resources/clj-kondo.exports/babashka/fs/config.edn • and hook: https://github.com/babashka/fs/blob/v0.5.30/resources/clj-kondo.exports/babashka/fs/babashka/fs.clj_kondo

lread 2025-12-03T15:32:06.191619Z

Maybe also of interest, that effort was the first time I also included some tests for a hook. https://github.com/babashka/fs/blob/v0.5.30/test/babashka/fs_linter_test.clj This seems like a good idea to me, and I'll likely add tests to other projects where I've created hooks.

borkdude 2025-12-03T21:11:16.804509Z

Clojurists Together members can vote for long term support again. I announced my plans for 2026 https://blog.michielborkent.nl/thanksgiving-2025.html If you are a member, voting would be much appreciated. Go vote!

➕ 1