@@ -0,0 +1,68 @@
(ns analyze.core
(:require [clj-kondo.core :as clj-kondo]
[clojure.set :as set]))
; ; checks re-frame's :<- syntax
; ; to mark dependency subscriptions as used
(def analyze-reg-sub
" (require '[clj-kondo.hooks-api :as api])
(fn [{node :node}]
(let [[_ kw & children] (:children node)
reg-sub-kw (api/reg-keyword! kw 're-frame.core/reg-sub)
reg-dep-kw! #(-> % :children first (api/reg-keyword! 're-frame.core/subscribe))
sub-kws (map #(if (api/vector-node? %) (reg-dep-kw! %) %)
(butlast children))]
{:node (api/list-node
`(do ~reg-sub-kw ~@sub-kws ~(last children)))}))" )
; ; requires subscribtion name (keyword)
; ; to be statically defined at the call site
; ; i.e. `(subscribe [:my/sub])`
(def analyze-subscribe
" (require '[clj-kondo.hooks-api :as api])
(fn [{node :node}]
(let [[_ query] (:children node)
[kw & children] (:children query)]
{:node (api/list-node
`(do ~(api/reg-keyword! kw 're-frame.core/subscribe)
~@children))}))" )
(def out
(clj-kondo/run!
{:lint [" src" ]
:config {:output {:analysis {:keywords true }}
:hooks {:__dangerously-allow-string-hooks__ true
:analyze-call {'re-frame.core/reg-sub analyze-reg-sub
're-frame.core/subscribe analyze-subscribe}}}}))
(defn get-keywords-usages [var-name]
(->> (:analysis out)
:keywords
(filter #(= var-name (:def %)))
(map #(assoc % :kw (if (:ns %)
(keyword (str (:ns %)) (:name %))
(keyword (:name %)))))))
(defn find-unused-keywords [defs usages]
(let [defs-set (into #{} (map :kw ) defs )
usages-set (into #{} (map :kw ) usages)
unused (set/difference defs-set usages-set)]
(filter (comp unused :kw ) defs )))
(defn find-usage-of-undefined-keywords [defs usages]
(let [defs-set (into #{} (map :kw ) defs )
usages-set (into #{} (map :kw ) usages)
undefined (set/difference usages-set defs-set )]
(filter (comp undefined :kw ) defs )))
(defn fmt-message [warning-type {:keys [kw filename row]}]
(-> (case warning-type
:unused-subscription " Unused subscription" )
(str " " kw " at line " row " in " filename)))
(let [subs [(get-keywords-usages 're-frame.core/reg-sub)
(get-keywords-usages 're-frame.core/subscribe)]
unused-subs (apply find-unused-keywords subs)
undefined-subs (apply find-usage-of-undefined-keywords subs)]
(doseq [sub unused-subs]
(println (fmt-message :unused-subscription sub))))