Files
rose-ash/shared/static/wasm/sx/hs-integration.sx
giles 310b649fe7
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 17s
HS: behavior scoping + element ref + script tag registration (+5 tests)
2026-04-27 00:56:12 +00:00

151 lines
5.0 KiB
Plaintext

;; _hyperscript integration — wire _="..." attributes to compiled SX
;;
;; Entry points:
;; (hs-handler src) — compile source to callable (fn (me) ...)
;; (hs-activate! el) — activate hyperscript on a single element
;; (hs-boot!) — scan DOM, activate all _="..." elements
;; (hs-boot-subtree! root) — activate within a subtree (for HTMX swaps)
;; ── Compile source to a handler function ────────────────────────
;; Returns a function (fn (me) ...) that can be called with a DOM element.
;; Uses eval-expr-cek to turn the SX data structure into a live closure.
(begin
(define
hs-collect-vars
(fn
(sx)
(define vars (list))
(define
reserved
(list
(quote beingTold)
(quote me)
(quote it)
(quote event)
(quote you)
(quote yourself)))
(define
walk
(fn
(node)
(when
(list? node)
(when
(and
(> (len node) 1)
(= (first node) (quote set!))
(symbol? (nth node 1)))
(let
((name (nth node 1)))
(when
(and
(not (some (fn (v) (= v name)) vars))
(not (some (fn (v) (= v name)) reserved)))
(set! vars (cons name vars)))))
(for-each walk node))))
(walk sx)
vars))
(define
hs-handler
(fn
(src)
(let
((sx (hs-to-sx-from-source src)))
(let
((extra-vars (hs-collect-vars sx)))
(do
(for-each
(fn (v) (eval-expr-cek (list (quote define) v nil)))
extra-vars)
(let
((guarded (list (quote guard) (list (quote _e) (list (quote true) (list (quote if) (list (quote and) (list (quote list?) (quote _e)) (list (quote =) (list (quote first) (quote _e)) "hs-return")) (list (quote nth) (quote _e) 1) (list (quote raise) (quote _e))))) sx)))
(eval-expr-cek
(list
(quote fn)
(list (quote me))
(list
(quote let)
(list
(list (quote beingTold) (quote me))
(list (quote it) nil)
(list (quote event) nil))
guarded))))))))))
;; ── Activate a single element ───────────────────────────────────
;; Reads the _="..." attribute, compiles, and executes with me=element.
;; Marks the element to avoid double-activation.
(define
hs-register-scripts!
(fn
()
(for-each
(fn
(script)
(when
(not (dom-get-data script "hs-script-loaded"))
(let
((src (host-get script "innerHTML")))
(when
(and src (not (= src "")))
(guard
(_e (true nil))
(eval-expr-cek (hs-to-sx-from-source src)))
(dom-set-data script "hs-script-loaded" true)))))
(hs-query-all "script[type=text/hyperscript]"))))
;; ── Boot: scan entire document ──────────────────────────────────
;; Called once at page load. Finds all elements with _ attribute,
;; compiles their hyperscript, and activates them.
(define
hs-activate!
(fn
(el)
(do
(hs-register-scripts!)
(let
((src (dom-get-attr el "_")) (prev (dom-get-data el "hs-script")))
(when
(and src (not (= src prev)))
(when
(dom-dispatch el "hyperscript:before:init" nil)
(hs-log-event! "hyperscript:init")
(dom-set-data el "hs-script" src)
(dom-set-data el "hs-active" true)
(dom-set-attr el "data-hyperscript-powered" "true")
(let ((handler (hs-handler src))) (handler el))
(dom-dispatch el "hyperscript:after:init" nil)))))))
;; ── Boot subtree: for dynamic content ───────────────────────────
;; Called after HTMX swaps or dynamic DOM insertion.
;; Only activates elements within the given root.
(define
hs-deactivate!
(fn
(el)
(let
((unlisteners (or (dom-get-data el "hs-unlisteners") (list))))
(for-each (fn (u) (when u (u))) unlisteners)
(dom-set-data el "hs-unlisteners" (list))
(dom-set-data el "hs-active" false)
(dom-set-data el "hs-script" nil))))
(define
hs-boot!
(fn
()
(let
((elements (dom-query-all (host-get (host-global "document") "body") "[_]")))
(for-each (fn (el) (hs-activate! el)) elements))))
(define
hs-boot-subtree!
(fn
(root)
(let
((elements (dom-query-all root "[_]")))
(for-each (fn (el) (hs-activate! el)) elements))
(when (dom-get-attr root "_") (hs-activate! root))))