From b94e6243d0f557a2221e3369605933de5c98de74 Mon Sep 17 00:00:00 2001 From: Asko Nõmm Date: Wed, 24 Sep 2025 16:47:18 +0300 Subject: Fix issue where one could not use ints or longs in the $ macro, and add clj-kondo hook for better validation. --- resources/clj-kondo/config.edn | 2 ++ resources/clj-kondo/hooks/dompa.clj | 37 +++++++++++++++++++++++++++++++++++++ src/dompa/utils.cljc | 29 +++++++++++------------------ 3 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 resources/clj-kondo/config.edn create mode 100644 resources/clj-kondo/hooks/dompa.clj diff --git a/resources/clj-kondo/config.edn b/resources/clj-kondo/config.edn new file mode 100644 index 0000000..fa8d235 --- /dev/null +++ b/resources/clj-kondo/config.edn @@ -0,0 +1,2 @@ +{:hooks {:analyze-call {dompa.utils/$ hooks.dompa/$}} + :linters {:dompa.utils/$-arg-validation {:level :warning}}} \ No newline at end of file diff --git a/resources/clj-kondo/hooks/dompa.clj b/resources/clj-kondo/hooks/dompa.clj new file mode 100644 index 0000000..b0eee7a --- /dev/null +++ b/resources/clj-kondo/hooks/dompa.clj @@ -0,0 +1,37 @@ +(ns hooks.dompa + (:require [clj-kondo.hooks-api :as api])) + +(defn string-like? [x] + (or (api/string-node? x) + (api/token-node? x))) + +(defn $ [{:keys [node]}] + (let [[_ first-arg & rest-args] (:children node)] + (cond + ; if the first arg is string-like, then no sequences + ; are allowed, because you are only allowed to concat + ; strings, i.e. whatever (str) can do. + (or (api/string-node? first-arg) + (api/token-node? first-arg)) + (let [invalid-args (filter #(not (string-like? %)) rest-args)] + (doall + (for [invalid-arg invalid-args] + (api/reg-finding! + (assoc (meta invalid-arg) + :message (str "Invalid argument type. When creating a text node, " + "only literal values (strings, numbers and symbols) are allowed.") + :type :dompa.utils/$-arg-validation))))) + + ; if the first arg is a keyword, then the second argument can only be + ; a sequence or a map. + (and (api/keyword-node? first-arg) + (seq rest-args) + (not (or (api/map-node? (first rest-args)) + (api/list-node? (first rest-args))))) + (api/reg-finding! + (assoc (meta (first rest-args)) + :message (str "Invalid argument type. When creating a non-text node, " + "the second argument must be a sequence or a map. " + "In other words, the second argument must be an attribute map " + "or sequence of other nodes created with the $ macro.") + :type :dompa.utils/$-arg-validation))))) \ No newline at end of file diff --git a/src/dompa/utils.cljc b/src/dompa/utils.cljc index 0e17875..d76ba28 100644 --- a/src/dompa/utils.cljc +++ b/src/dompa/utils.cljc @@ -1,6 +1,5 @@ (ns dompa.utils - (:require [dompa.nodes :as nodes] - [criterium.core :as c])) + (:require [dompa.nodes :as nodes])) (defmacro defhtml {:clj-kondo/lint-as 'clojure.core/defn} @@ -9,7 +8,7 @@ `(defn ~name ~args (nodes/->html (vector ~@elements))))) -(defn flattench-xf [] +(defn ->flat-xf [] (fn [rf] (letfn [(step [result input] (if (sequential? input) @@ -20,15 +19,14 @@ ([result] (rf result)) ;; completion ([result input] (step result input)))))) ;; step -(defn flattench [children] - (into [] (flattench-xf) children)) +(defn ->flat [children] + (into [] (->flat-xf) children)) (defmacro $ - {:clj-kondo/lint-as 'clojure.core/list} [name & opts] `(if (string? ~name) {:node/name :dompa/text - :node/value (apply str ~name ~@opts)} + :node/value (str ~name ~@opts)} (let [opts# (list ~@opts) first-opt# (first opts#) attrs?# (and (map? first-opt#) @@ -37,18 +35,13 @@ children# (if attrs?# (rest opts#) opts#)] (cond-> {:node/name ~name} attrs?# (assoc :node/attrs attrs#) - (seq children#) (assoc :node/children (flattench children#)))))) - -(defn bench-n [] - (c/quick-bench - (dotimes [_ 1500] - ($ :div {:class "container"} - (map #($ %) ["a" "b" "c"]) - ($ "hello world"))))) + (seq children#) (assoc :node/children (->flat children#)))))) (defhtml test-page [] - ($ :div - (map #($ %) ["a" "b" "c"]) - ($ "hello world"))) + (let [n 123] + ($ :div + ($ "hello world" n)) + ($ "hello") + ($ :div "test"))) (test-page) -- cgit v1.2.3