Skip to content

Instantly share code, notes, and snippets.

@brianium
Created March 25, 2025 17:11
Show Gist options
  • Save brianium/e708faac97596fbc907d471281ffb2ea to your computer and use it in GitHub Desktop.
Save brianium/e708faac97596fbc907d471281ffb2ea to your computer and use it in GitHub Desktop.
(ns alpine
(:require [squint.compiler :as compiler]
[hiccup2.core :as h]))
(defn js
"Compiles the given form to javascript and returns a string. Be sure to add $squint as an Alpine.magic alias if you
want to use the squint core library in alpine. If you are using squint client-side you might use the following:
```clojure
(ns app
(:require [\"squint-cljs/core.js\" :as core]
[\"alpinejs$default\" :as alpine]))
(alpine.magic \"squint\" (constantly core))
(alpine.start)
```"
[body]
(let [s (pr-str body)
compiled (compiler/compile-string* s {:elide-imports true
:core-alias "$squint"
:elide-exports true})]
(h/raw (:body compiled))))
;;; Alpine supported attributes
(def static-attributes
#{:x-data
:x-init
:x-effect
:x-if
":class"
":style"})
(def dynamic-attributes
#{:x-on
:x-bind
:x-show})
(defn get-dynamic-attribute
[attr]
(if-some [a (dynamic-attributes attr)]
a
(let [n (name attr)]
(when (re-matches #"^x-[^:]+(\:[^.]+)?(\.[^.]+)?$" n)
n))))
(defn event-shorthand
[attr]
(re-matches #"^@.+" attr))
(defn alpine-attribute
[attr]
(if-some [a (static-attributes attr)]
a
(or (get-dynamic-attribute attr)
(event-shorthand (name attr)))))
;;; Support ClojureScript in alpine attributes
(defn to-js
"Get all valid alpine attributes and convert them to js values "
[props]
(->> props
(keys)
(filter alpine-attribute)
(reduce (fn [p k]
(let [v (props k)]
(if (string? v)
(assoc p k v)
(assoc p k (js v))))) props)))
(defn alpineify
"postwalk function for a hiccup data structure to convert alpine attributes to js"
[node]
(if (and (not (map-entry? node)) (vector? node))
(let [[tag props & children] node]
(if (map? props)
(apply vector tag (to-js props) children)
node))
node))
(comment
(clojure.walk/postwalk
alpineify
[:div {:x-data '{:foo "bar"}}
[:button {"@click" '(set! foo "baz")} "Bazify"]
[:p {:x-show '(= foo "baz")} "I've been bazzed!"]])
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment