Skip to content

Commit db2a28b

Browse files
authored
Fix #602: class and id shortcuts (#639)
1 parent d1b2a52 commit db2a28b

File tree

4 files changed

+62
-4
lines changed

4 files changed

+62
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
[Squint](https://github.com/squint-cljs/squint): Light-weight ClojureScript dialect
44

5+
## Unreleased
6+
7+
- Fix #602: support hiccup-style shorthand for id and class attributes in `#jsx` and `#html`
8+
59
## v0.8.140 (2025-03-09)
610

711
- Fix [#635](https://github.com/squint-cljs/squint/issues/635): `range` fixes

src/squint/compiler_common.cljc

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,33 @@ break;}" body)
11791179
(defn void-tag? [tag-name]
11801180
(contains? void-tags tag-name))
11811181

1182+
(defn- parse-tag
1183+
"From hiccup, thanks @weavejester"
1184+
[^String tag]
1185+
(let [id-index (let [index (.indexOf tag "#")] (when (pos? index) index))
1186+
class-index (let [index (.indexOf tag ".")] (when (pos? index) index))]
1187+
[(cond
1188+
id-index (.substring tag 0 id-index)
1189+
class-index (.substring tag 0 class-index)
1190+
:else tag)
1191+
(when id-index
1192+
(if class-index
1193+
(.substring tag (unchecked-inc-int id-index) class-index)
1194+
(.substring tag (unchecked-inc-int id-index))))
1195+
(when class-index
1196+
(.substring tag (unchecked-inc-int class-index)))]))
1197+
1198+
(defn- merge-attrs [attrs short-attrs]
1199+
(let [attrs (if-let [c (:class attrs)]
1200+
(if-let [sc (:class short-attrs)]
1201+
(assoc attrs :class (str sc " " c))
1202+
attrs)
1203+
attrs)
1204+
attrs (if-let [id (:id short-attrs (:id attrs))]
1205+
(assoc attrs :id id)
1206+
attrs)]
1207+
attrs))
1208+
11821209
(defn emit-vector [expr env]
11831210
(if (and (:jsx env)
11841211
(let [f (first expr)]
@@ -1198,9 +1225,17 @@ break;}" body)
11981225
tag-name* (if fragment?
11991226
(symbol "")
12001227
tag-name)
1201-
tag-name (if (and (not fragment?) keyw?)
1202-
(subs (str tag) 1)
1203-
(emit tag-name* (expr-env (dissoc env :jsx))))
1228+
[tag-name id class]
1229+
(if (and (not fragment?) keyw?)
1230+
(parse-tag (subs (str tag) 1))
1231+
[(emit tag-name* (expr-env (dissoc env :jsx)))])
1232+
classes (when class (str/replace class "." " "))
1233+
short-attrs (cond-> nil
1234+
classes (assoc :class classes)
1235+
id (assoc :id id))
1236+
attrs (if attrs
1237+
(merge-attrs attrs short-attrs)
1238+
short-attrs)
12041239
html? (:html env)
12051240
outer-html? (:outer-html (meta expr))]
12061241
(if (and (not html?) (:jsx env) (:jsx-runtime env))

test/squint/html_test.cljs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,18 @@
125125
)
126126
(p/catch #(is false "nooooo"))
127127
(p/finally done))))
128+
129+
(deftest html-id-class-shortcut-test
130+
(t/async done
131+
(let [js (squint.compiler/compile-string
132+
"[(str #html [:div.container])
133+
(str #html [:a#foo.bar.baz {:class \"quux\"}])]"
134+
{:repl true :elide-exports true :context :return})
135+
js (str/replace "(async function() { %s } )()" "%s" js)]
136+
(-> (js/eval js)
137+
(.then
138+
#(doseq [[k v] (map vector ["<div class=\"container\"></div>"
139+
"<a class=\"bar baz quux\" id=\"foo\"></a>"] %)]
140+
(is (html= k v))))
141+
(.catch #(is false "nooooo"))
142+
(.finally done)))))

test/squint/jsx_test.cljs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@
7676
(is (= "<div><div>Hello</div></div>" (test-jsx "#jsx [:div [:<> [:div \"Hello\"]]]"))))
7777

7878
(testing "fragment"
79-
(is (= "<div><div>Hello</div></div>" (test-jsx "#jsx [:div [:<> [:div \"Hello\"]]]")))))
79+
(is (= "<div><div>Hello</div></div>" (test-jsx "#jsx [:div [:<> [:div \"Hello\"]]]"))))
80+
(testing "class shorthand"
81+
(is (= "<div class=\"container\"></div>" (test-jsx "#jsx [:div.container]"))))
82+
(testing "id and classes shorthand"
83+
(is (= "<div class=\"foo bar\" id=\"my-id\"></div>" (test-jsx "#jsx [:div#my-id.foo.bar]")))))
8084

8185
;; uncomment to test how JSX behaves
8286
#_(prn (test-jsx* "<div className={[\"dude\", \"bar\"]}></div>"))

0 commit comments

Comments
 (0)