I'm trying to learn squint by porting a ClojureScript library. I have a question about rendering HTML. I have a macro helpers in a cljc that look like this:
(ns fizz.impl
(:require [borkdude.html :refer [html]])
(defn make-component*
([args body]
(make-component* {} args body))
([{:keys [observed-attributes]}
args
body]
`{:fizz.element/body-html (fn ~args (html ~body))
:fizz.element/observed-attributes ~observed-attributes}))
This works in Clojure and the normal clojurescript compiler. It's part of a library that can SSR web components. I'd prefer not to use the #html reader tag on the backend.
However, when trying to make this work in squint, I get the error:
the "path" argument must be of type string or an instance of Buffer or URL
My first thought was that I need to use require-macros :
(ns fizz.impl
#?(:squint (:require-macros [borkdude.html :refer [html]])) ;; also tried with :cljs
#?(:clj (:require [borkdude.html :refer [html]])))
However, this results in the same error.
What's the idiomatic squint way of solving this problem?I went in a different direction, but I'd circle back around to this if it were available
Where did you put borkdude/html?
squint doesn't support CLJ(S) libraries from deps.edn etc yet, unfortunately, but if you (for now) copy borkdude.html into src/borkdude/html.cljc then it might work, I haven't tested this
I'll give that a try.
I'm using deps.edn, and html is added there as a dependency
yes, this won't do anything at the moment
you still will need :require-macros
Got it. I can compile now, but this test:
(ns fizz.element-test
(:require-macros [borkdude.html :refer [html]])
(:require ["vitest" :refer [test expect]]))
(test "vendor borkdude html"
(.toEqual (expect (html [:p]))
"<p></p>"))
Produces the screenshotted error. I'm trying to rule out an interaction with vitenot sure what this is, perhaps you can show the compiled JS ?
Compiled cljs:
import * as squint_core from 'squint-cljs/core.js';
import { test, expect } from 'vitest';
test("vendor borkdude html", expect(squint_core.str(borkdude.html.Html@1a)).toEqual("<p></p>"));
It seems isn't calling the macro
oh yeah I see
it is calling the macro, but the macro is returning a deftype instance that is serialized the wrong way
This wasn't really written with squint in mind, in squint you're expected to use the #html reader but ... perhaps I do have a workaround..
In squint there is a special form called squint-compiler-html which you can use for this. Note that this is currently only an implementation detail which might change, but this should work:
(#?(:squint squint-compiler-html :default borkdude.html/html) [:a ...])
or soI could probably make this a little programmer-friendly
by exposing this in a stable manner
but you could try to test it right now
I'll give it a shot.
What I'm after isn't so much using the html library, but being able to render a web component on the server and on the client (using squint) in the same way. I may need to back up and re-think that a bit.
I'll try the special form and see if that does the trick.
why wouldn't #html work both on the server and client?
or is the issue that your library should be usable from regular CLJS?
Regular cljs isn't really a primary objective
so both server and client are running squint?
Server is running regular jvm clojure; client is using squint.
Previously, I was calling html inside of a macro. I think I can lift it.
That changes my main API function from:
(define test-element-1
{:observed-attributes #{:a :b}}
[{:keys [a b]}]
[:div
[:p a]
[:p b]]))
to
(define test-element-1
{:observed-attributes #{:a :b}}
[{:keys [a b]}]
#html [:div
[:p a]
[:p b]]))
Trying that right now.ah JVM Clojure, that's useful information
#?(:clj (:require [borkdude.html :refer [html] :rename {html squint-compiler-html}]))
This hack may work to use just (squint-compiler-html [:div [:p a] ....) in both places
If this works, I can probably rename this to be more API friendly or soAh, nice, I'll try that.
I have it working on the Clojure side, but not the squint side yet. Here's the macro helper:
(ns fizz.impl
#?(:clj (:require [borkdude.html :refer [html]
:rename {html squint-compiler-html}])))
(defn make-component*
([args body]
(make-component* {} args body))
([{:keys [observed-attributes]}
args
body]
`{:fizz.element/body-html (fn ~args (squint-compiler-html ~body))
:fizz.element/observed-attributes ~observed-attributes}))
This macro-expands as:
(macroexpand-1
'(define test-element-1
{:observed-attributes #{:a :b}}
[{:keys [a b]}]
[:div
[:p a]
[:p b]])))
==> (def test-element-1
#:fizz.element{:observed-attributes #{:b :a},
:body-html
(clojure.core/fn
[{:keys [a b]}]
(borkdude.html/html [:div [:p a] [:p b]]))})
However, it compiles as:
return fizz.impl.squint_compiler_html(["p", ["span", squint_core.str("a is ", a3)]]);
}), "fizz.element/observed-attributes": ["a"] });
This gives an undefined error. I'm assuming this is because the compiler is https://github.com/squint-cljs/squint/blob/1c3e1b034c4d055a1be85b78499f698daca4f9f9/src/squint/compiler.cljc#L45 squint-compiler-html to be a symbolelse it gets qualified as fizz.impl/squint-compiler-html as you can see
but this might break the JVM Clojure behavior. I'll check back here tomorrow
No problem, I'm much further than I was before. Thanks! I did try that, but it breaks on the JVM side. I'll keep brainstorming.
ok, let me now take a look at this...
if this is still useful to you