Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook for linting support with clj-kondo #3

Open
formsandlines opened this issue May 16, 2022 · 0 comments
Open

Hook for linting support with clj-kondo #3

formsandlines opened this issue May 16, 2022 · 0 comments

Comments

@formsandlines
Copy link

formsandlines commented May 16, 2022

Not really an issue with your codebase, but I wanted to share my efforts to provide better linting support for the def-cmptfn macro.

Here is my hook for clj-kondo. It is not perfect (I just got started with the hooks API), but works fine for me. Maybe some users will find it helpful or can improve on it to provide more useful error messages.

(ns hooks.tengen
  (:require [clj-kondo.hooks-api :as api]))

(defn def-cmptfn
  [{:keys [:node]}]
  (let [[var-name-node & more] (rest (:children node))

        _ (when-not (api/token-node? var-name-node)
            (throw (ex-info "Missing function name!" {})))

        [docstring-node
         params-node & body] (if (api/string-node? (first more))
                               more
                               (cons nil more))

        _ (when-not (api/vector-node? params-node)
            (throw (ex-info "Missing function parameters!" {})))

        let-node (let [body-map (->> body
                                     (partition 2)
                                     (map (fn [[k v]] [(api/sexpr k) v]))
                                     (into {}))
                       _ (when-not (== (count body) (* (count body-map) 2))
                           (throw (ex-info "Missing key or value argument!" {})))
                       _ (when-not (every? keyword? (keys body-map))
                           (throw (ex-info "Arguments must be key-value pairs!" {})))

                       bindings (api/vector-node
                                  (concat 
                                    ;; bind “magic bindings” before others
                                    (mapv api/token-node
                                          ['this-mounting? nil
                                           'this-cmpt nil])
                                    (:children (:let-mount body-map))
                                    (:children (:let-render body-map))))
                       exprs (remove nil? 
                                     ;; to prevent “unused bindings” warning
                                     [(api/vector-node
                                        [(api/token-node 'this-mounting?)
                                         (api/token-node 'this-cmpt)])
                                      (:render body-map)
                                      (:post-render body-map)
                                      (:unmount body-map)])]
                   (api/list-node
                     (list* (api/token-node 'let)
                            bindings
                            exprs)))

        fn-node (api/list-node
                  (list (api/token-node 'fn)
                        params-node
                        let-node))

        new-node (api/list-node
                   (remove nil?
                           (list (api/token-node 'def)
                                 var-name-node
                                 docstring-node
                                 fn-node)))]
    {:node new-node}))

I put it in hooks/tengen.clj and wire it up in my config like this:

{...
 :hooks {:analyze-call {taoensso.tengen.reagent/def-cmptfn
                        hooks.tengen/def-cmptfn}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant