I had a random thought about dev experience with helix. In cljs a function and a macro can have the same name. So was thinking defnc could emit both a defmacro <component-name> and (def <component-name> (fn [],,,)
This way you can do:
(defnc title [{:keys [label]}]
(d/h2 label))
(defnc another [{:keys [greeting]}]
(d/div
(title {:label greeting})))
which would emit:
(defnc another [{:keys [greeting]}]
(d/div
($ title {:label greeting})))
as far as I understand the cljs compiler will use the function version of title in the second case (can't use macro as a value)
This way $ would only be needed for js interop.
Any thoughts on this? Was it ever considered and abandoned?tried to play with it a bit and I don't think it will be possible because the emitted defmacro will be in the resulting cljs code, not in the clj code. would be happy to be wrong though
sounds about right to me… cljs compiler exists in jvm clojure world and emits code (as data). what you are suggesting might work for self-hosted cljs, but not cljs in general I think
At the end of the day, the reason for a $ macro to exist is simply to be able to emit calls to React.createElement() or the newer _jsx() at compile-time. And I like being exactly at this level for my components because it makes interop with external React libraries painless and easy-to-do compared with Reagent
One thing I would like to see (and have tried to add at some point) is autocomplete for props in react components. If there is one major advantage TS has over CLJS for React development, its autocomplete on all of your elements’ props. I have sketched a few JS functions capable of extracting fields and methods of objects, but not sure what exactly I need to do in order to extend CIDER to make use of this data.
I tried this out and I found you can "inject" a defmacro for each component, but it requires every .cljs component namespace to have a physical .clj file /namespace because the cljs compiler requires this. Even though the clojure macro will be present, the compiler code path needs teh physical namespace file. You will also need to do the (:require-macros [$component-namespace]) incantation to get it working. So basically, no go.
Yea, I actually had a similar thought which led me down this path to make the component invocations look like function calls.
I have an augmented defnc that emits malli-enabled instrumented versions, and then use the malli-clj-kondo preload (described here https://github.com/metosin/malli/blob/master/docs/clojurescript-function-instrumentation.md#dev-setup) to emit static checks for the components
https://github.com/metosin/malli/blob/master/docs/function-schemas.md#static-type-checking
but using $ prevents the linter from checking them
ok actually had one more crazy thought on how to make it work.. will have to try later tonight though! essentially instead of emitting
`(defmacro <component-name>)
it would create a macro in the compiler by invoking eval
(defmacro defnc [cmp-name]
(eval `(defmacro ~cmp-name [] ....))
so then cmp-name will be a macro in the compiler, not in the emitted cljs hmm, might just work@hifumi123 wanted to start a new thread about your type hinting/autocomplete idea
oh interesting, that makes sense
I think this could be a huge win for the cljs react community if we managed to get something working. Curious if we can sketch some ideas out. maybe clj-kondo static checks could assist here as the editors already use that data?
i dont think clj-kondo is the appropriate spot since editors use nrepl middleware to provide completions AFAICT
moreover, to dynamically infer fields of an object we would want to be able to inspect the prototype of objects