helix

dvingo 2023-10-17T01:17:19.511249Z

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?

dvingo 2023-10-17T01:53:33.934299Z

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

hifumi123 2023-10-17T06:19:33.620949Z

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

hifumi123 2023-10-17T06:21:09.428369Z

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

hifumi123 2023-10-17T06:22:33.722829Z

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.

dvingo 2023-10-18T16:13:19.928839Z

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.

dvingo 2023-10-17T14:13:22.497289Z

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

dvingo 2023-10-17T14:26:20.295569Z

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

dvingo 2023-10-17T14:28:43.239189Z

@hifumi123 wanted to start a new thread about your type hinting/autocomplete idea

dvingo 2023-10-18T16:10:56.024259Z

oh interesting, that makes sense

dvingo 2023-10-17T14:29:56.568819Z

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?

hifumi123 2023-10-17T19:55:23.605619Z

i dont think clj-kondo is the appropriate spot since editors use nrepl middleware to provide completions AFAICT

hifumi123 2023-10-17T19:55:59.514839Z

moreover, to dynamically infer fields of an object we would want to be able to inspect the prototype of objects