Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions src/huff2/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@

(def ^:dynamic *escape?* true)

(def ^:private char->replacement {\& "&amp;", \< "&lt;", \> "&gt;", \" "&quot;", \\ "&#39;"})
(def ^:private char->replacement {\& "&amp;", \< "&lt;", \> "&gt;", \" "&quot;"})

(defn maybe-escape-html
"1. Change special characters into HTML character entities when *escape?*
Expand All @@ -121,9 +121,9 @@
(defmethod emit :primative [append! {:keys [value]} _opts]
(maybe-escape-html append! value))

(defn- empty-or-div [seen] (if (empty? seen) "div" (str/join seen)))
(defn empty-or-div [seen] (if (empty? seen) "div" (str/join seen)))

(defn- emit-style [append! s]
(defn emit-style [append! s]
(append! "style=\"")
(cond
(map? s) (doseq [[k v] (sort-by first s)]
Expand Down Expand Up @@ -156,11 +156,13 @@
(step \.) ;; move "seen " into the right place
(map [:tag :id :class])))

(defn- tag->tag+id+classes [tag]
(defn tag->tag+id+classes [tag]
(mapv (comp tag->tag+id+classes* keyword) (str/split (name tag) #">")))

(defn- emit-attrs [append! attrs]
(doseq [[k value] attrs]
(defn emit-attrs [append! attrs {:keys [attr-mapper]}]
(doseq [[k value] (if attr-mapper
(map attr-mapper attrs)
attrs)]
(when-not
(or (contains? #{"" nil false} value)
(and (coll? value) (empty? value)))
Expand Down Expand Up @@ -194,7 +196,7 @@
(append! "<")
(append! ^String (name tag))
(when (or tag-id (not-empty tag-classes'))
(emit-attrs append! {:id tag-id :class tag-classes'}))
(emit-attrs append! {:id tag-id :class tag-classes'} opts))
(if (contains? void-tags (name tag))
(append! " />")
(append! ">"))))
Expand All @@ -215,15 +217,16 @@
(string? %) (concat [%] final-tag-classes)
(coll? %) (concat % final-tag-classes)
(nil? %) final-tag-classes)
(map stringify)
(remove str/blank?))))
;; attrs go on the last tag-info:
tag-infos' (update tag-infos (dec (count tag-infos)) (fn [l] (conj (vec l) attrs)))]
(doseq [[tag tag-id tag-classes & [attrs]] tag-infos']
(append! "<")
(append! ^String (name tag))
(if attrs
(emit-attrs append! attrs)
(emit-attrs append! {:id tag-id :class (remove str/blank? tag-classes)}))
(emit-attrs append! attrs opts)
(emit-attrs append! {:id tag-id :class (remove str/blank? tag-classes)} opts))
(if (contains? void-tags (name tag))
(append! " />")
(append! ">")))
Expand Down Expand Up @@ -269,16 +272,16 @@

Can I extend this by adding new types of nodes? Yes: see: [[huff2.extension]]!"
([h] (html {} h))
([{:keys [allow-raw *explainer *parser] :or {allow-raw false
*explainer explainer
*parser parser} :as _opts} h]
([{:keys [allow-raw *explainer *parser attr-mapper]
:or {allow-raw false, *explainer explainer, *parser parser}
:as _opts} h]
(let [parsed (*parser h)]
(if (= parsed :malli.core/invalid)
(let [{:keys [value]} (*explainer h)]
(throw (ex-info "Invalid huff form passed to html. See [[hiccup-schema]] for more info" {:value value})))
(let [sb (StringBuilder.)
append! (fn append! [& strings] (doseq [s strings :when s] (.append ^StringBuilder sb s)))]
(emit append! parsed {:allow-raw allow-raw :parser *parser})
(emit append! parsed {:allow-raw allow-raw :parser *parser :attr-mapper attr-mapper})
(raw-string (str sb)))))))

(defn page
Expand Down
31 changes: 22 additions & 9 deletions test/huff/core2_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
(is (= (h/html [:div.c1#id1.c2 {:class ["c3"] :style {:border "1px solid red"}} "x"])
(h/html [:div.c1#id1.c2 {:class "c3" :style {:border "1px solid red"}} "x"])))
(is (= (h/html [:div {:class nil}]) (h/html [:div])))
(is (= (h/html [:div.a]) (h/html [:div {:class ["a" nil "" ""]}]))))
(is (= (h/html [:div.a]) (h/html [:div {:class ["a" nil "" ""]}])))
(is (= "<div class=\"flex flex-auto\"></div>"
(str (h/html [:div {:class [:flex :flex-auto]}]))))
(is (= (h/html [:div {:class ["flex" "flex-auto"]}])
(h/html [:div {:class [:flex :flex-auto]}]))))

(defn as-string [f]
(let [sb (StringBuilder.)
Expand Down Expand Up @@ -73,10 +77,10 @@


(deftest attr-emission-test
(is (= " id=\"x\" class=\"x y\"" (as-string #(#'h/emit-attrs % {:id "x" :class ["x" "y"]}))))
(is (= " id=\"x\" class=\"x y\"" (as-string #(#'h/emit-attrs % {:id "x" :class "x y"}))))
(is (= " id=\"x\"" (as-string #(#'h/emit-attrs % {:id "x" :class []}))))
(is (= " x-data=\"{open: false}\"" (as-string #(#'h/emit-attrs % {:x-data "{open: false}"})))))
(is (= " id=\"x\" class=\"x y\"" (as-string #(h/emit-attrs % {:id "x" :class ["x" "y"]} {}))))
(is (= " id=\"x\" class=\"x y\"" (as-string #(h/emit-attrs % {:id "x" :class "x y"} {}))))
(is (= " id=\"x\"" (as-string #(h/emit-attrs % {:id "x" :class []} {}))))
(is (= " x-data=\"{open: false}\"" (as-string #(h/emit-attrs % {:x-data "{open: false}"} {})))))

(deftest page-test
(is (= (h/page {:allow-raw true} [:h1 "hi"]) "<!doctype html><h1>hi</h1>")))
Expand All @@ -86,8 +90,8 @@
(is (= "<div>&lt;</div>" (str (h/html [:div "<"]))))
(is (= "<div>&gt;</div>" (str (h/html [:div ">"]))))
(is (= "<div>&quot;</div>" (str (h/html [:div "\""]))))
(is (= "<div>&#39;</div>" (str (h/html [:div "\\"]))))
(is (= "<div>&amp;</div><div>&lt;</div><div>&gt;</div><div>&quot;</div><div>&#39;</div>"
(is (= "<div>\\</div>" (str (h/html [:div "\\"]))))
(is (= "<div>&amp;</div><div>&lt;</div><div>&gt;</div><div>&quot;</div><div>\\</div>"
(str (h/html [:<>
[:div "&"]
[:div "<"]
Expand All @@ -114,8 +118,8 @@
(str (h/html [:h1 {:style {:width (-> 20 h/px) :opacity 1}}])))))

(deftest dot-shortcut-for-div-test
(is (= (#'h/tag->tag+id+classes :.)
(#'h/tag->tag+id+classes :div))))
(is (= (h/tag->tag+id+classes :.)
(h/tag->tag+id+classes :div))))

(deftest lists-work-for-spreading
(is (= "<div><span>ok</span></div>" (str (h/html [:div '([:span "ok"])]))))
Expand Down Expand Up @@ -180,3 +184,12 @@
(str (h/html [:div {:style {:width (-> 10 h/vmin)}}]))))
(is (= "<div style=\"width:10vmax;\"></div>"
(str (h/html [:div {:style {:width (-> 10 h/vmax)}}])))))

(deftest attr-mapper-test
(is (= "<div id=\"Capitalized\"><span id=\"lowercased\">text</span></div>"
(str (h/html
{:attr-mapper (fn [[k v]]
[k (if (keyword? v)
(str/lower-case (name v))
v)])}
[:div {:id "Capitalized"} [:span {:id :LoWerCaSEd} "text"]])))))
4 changes: 2 additions & 2 deletions test/huff/hiccup22_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
))

(deftest kw-tag-parsing
(are [x y] (= (#'h/tag->tag+id+classes x) y)
(are [x y] (= (h/tag->tag+id+classes x) y)
:div [["div" nil []]]
:div.a [["div" nil ["a"]]]
:div.a#d [["div" "d" ["a"]]]
Expand Down Expand Up @@ -64,7 +64,7 @@

(deftest kw-tag-validity
(is (= "can't have 2 #'s in a tag."
(try (#'h/tag->tag+id+classes :div#id1#id2)
(try (h/tag->tag+id+classes :div#id1#id2)
(catch Exception e (ex-message e))))))

(deftest tag-names
Expand Down