feat(ssr): add Selmer dependency + Hiccup<->Selmer interop bridge (Phase 2 foundation)
The strangler foundation for migrating interactive SSR components from Hiccup to
Selmer (plain-HTML Alpine/HTMX attributes instead of mixed keyword/string encodings).
- project.clj: add [selmer "1.12.61"].
- auto-ap.ssr.selmer: render / render-str (selmer/render-file + string), hiccup->html
(Hiccup -> string for {{ frag|safe }}), raw (wrap a rendered fragment for embedding
in a Hiccup tree without double-escaping), render->hiccup.
- resources/templates/interop-smoke.html: proves render-file from the classpath and
that plain-HTML alpine attrs (x-model, @keydown, tippy?.show()) pass through verbatim.
- selmer_test: 4 tests / 8 assertions covering both interop directions; all green.
Proven via REPL + tests: a Hiccup component renders inside a Selmer template, and a
Selmer fragment renders inside a Hiccup tree. Both valid during the transition.
This commit is contained in:
43
src/clj/auto_ap/ssr/selmer.clj
Normal file
43
src/clj/auto_ap/ssr/selmer.clj
Normal file
@@ -0,0 +1,43 @@
|
||||
(ns auto-ap.ssr.selmer
|
||||
"Selmer rendering + the Hiccup<->Selmer interop bridge for the SSR form/wizard
|
||||
migration (see .claude/skills/ssr-form-migration). Interactive, attribute-heavy
|
||||
components render from Selmer templates with plain-HTML Alpine/HTMX attributes;
|
||||
the bridge lets a Selmer template embed Hiccup output and lets a Selmer fragment
|
||||
sit inside a Hiccup tree during the strangler transition.
|
||||
|
||||
Templates live under resources/templates/ and are referenced by classpath-relative
|
||||
path, e.g. (render \"templates/components/typeahead.html\" ctx)."
|
||||
(:require
|
||||
[hiccup.util :as hu]
|
||||
[hiccup2.core :as h2]
|
||||
[selmer.parser :as selmer]))
|
||||
|
||||
(defn hiccup->html
|
||||
"Render a Hiccup form to an HTML string so it can be embedded in a Selmer
|
||||
context value and emitted with the |safe filter: {{ frag|safe }}."
|
||||
[hiccup]
|
||||
(str (h2/html {} hiccup)))
|
||||
|
||||
(defn raw
|
||||
"Wrap an already-rendered HTML string (e.g. from `render`) so hiccup2 emits it
|
||||
verbatim instead of escaping it. Use to drop a Selmer fragment into a Hiccup tree:
|
||||
[:div (sel/raw (sel/render \"...\" ctx))]."
|
||||
[^String html]
|
||||
(hu/raw-string html))
|
||||
|
||||
(defn render
|
||||
"Render a Selmer template file (classpath-relative path) with `ctx`, returning an
|
||||
HTML string. Hiccup values in `ctx` should be pre-rendered via `hiccup->html` and
|
||||
referenced with |safe in the template."
|
||||
[template ctx]
|
||||
(selmer/render-file template ctx))
|
||||
|
||||
(defn render-str
|
||||
"Render a Selmer template given as a string (handy for tests/REPL)."
|
||||
[template ctx]
|
||||
(selmer/render template ctx))
|
||||
|
||||
(defn render->hiccup
|
||||
"Render a Selmer template file and wrap the result for safe embedding in Hiccup."
|
||||
[template ctx]
|
||||
(raw (render template ctx)))
|
||||
Reference in New Issue
Block a user