Fork me on GitHub
#beginners
<
2024-04-23
>
Akiz14:04:40

Is there any easy way to compare in REPL that the function returns the same results for CLJ and CLJS? Basically something like (= (run-in-shadow-cljs (cljc-ns/fun data)) (run-in-clj (cljc-ns/fun data)) ?

andy.fingerhut14:04:13

I am not aware of one. Less-than-easy ways include (a) "shelling out" from a CLJ REPL to a CLJS REPL, or vice versa, or (b) running a CLJ program that prints output to a file, and a similar CLJS program that also prints output to a file, and then comparing those output files to each other. Because of things like map keys or set elements being printed in possibly different orders between CLJ and CLJS, you would probably want a more "intelligent" difference detector than command line diff

jpmonettas14:04:52

if you connect to the nrepl server that shadow-cljs starts you can then call shadow.cljs.devtools.api/cljs-eval

jpmonettas14:04:26

so you should be able to eval (foo 5) on the repl, and also eval (shadow.cljs.devtools.api/cljs-eval :app-build-id "(foo 5)" {:ns 'some-ns}) on the same repl. The first one will run it in Clojure world while the second one will do it in ClojureScript

jpmonettas14:04:53

like this, assuming you have shadow-cljs with a build id of :dev-test listening for nrepl connections on port 7123:

clj -Sdeps '{:deps {nrepl/nrepl {:mvn/version "1.1.1"}}}'                                                                    
Clojure 1.11.1                                                                                                                    
user=> (require '[nrepl.core :as nrepl])                                                                                          
nil                                                                                                                               
user=> (def conn (nrepl/connect :port 7123))                                                                                      
#'user/conn                                                                                                                       
user=> (def cli (nrepl/client conn 1000))                                                                                         
#'user/cli                                                                                                                        

user=> (nrepl/response-values (nrepl/message cli {:op "eval" :code "(+ 1 2)"}))                                                   
[3]                             

user=> (nrepl/response-values (nrepl/message cli {:op "eval" :code "(-> ((requiring-resolve 'shadow.cljs.devtools.api/cljs-eval) :dev-test \"js/window\" {}) :results first)"}))
["#object[Window [object Window]]"]

jpmonettas14:04:47

your function could be :

(defn same? [code build-id]  
  (= (-> (nrepl/response-values (nrepl/message cli {:op "eval" :code code}))
         first)
     (-> (nrepl/response-values (nrepl/message cli
                                               {:op "eval"
                                                :code (format "(-> ((requiring-resolve 'shadow.cljs.devtools.api/cljs-eval) %s %s {}) :results first)"
                                                              build-id
                                                              (pr-str code))}))
         first
         read-string)))

user=> (same? "(+ 1 2)" :dev-test)
true

Akiz14:04:33

@U0739PUFQ Thanks, that’s exactly what I needed. I didn’t find such a quick todo anywhere :-). Thanks a lot!

gratitude 1
Akiz16:04:02

@U0739PUFQ Do you know how to call a function in a specific namespace? I have no problem with core functions...

jpmonettas16:04:27

yes, in cljs land the last argument to shadow.cljs.devtools.api/cljs-eval is a map, and can be like {:ns "com.your-ns"}

jpmonettas16:04:08

and for the Clojure part you can do (nrepl/message cli {:op "eval" :code code :ns "com.your-ns"})

Akiz16:04:33

for some reason no other namespace than user I have trieed used so far works… (nrepl/response-values (nrepl/message cli {:op "eval" :code "(+ 1 2)" :ns (str *ns*)})) => nil

jpmonettas16:04:51

does that ns exists in the remote runtime? this seams to work

user=> (nrepl/response-values (nrepl/message cli {:op "eval" :code "(str *ns*)"}))          
["shadow.user"]
user=> (nrepl/response-values (nrepl/message cli {:op "eval" :code "(str *ns*)" :ns "user"}))          
["user"]

jpmonettas16:04:40

but if you eval in a non-existing ns it will return nil

user=> (nrepl/response-values (nrepl/message cli {:op "eval" :code "(str *ns*)" :ns "foo"}))
nil

jpmonettas16:04:05

I would guess that the ns you are sending in your :ns key hasn't been required/created on the remote process yet

👍 1
Akiz17:04:09

Yes. After I require it in a message, I can use it. I mistakenly thought I could use everything that is returned by (all-ns).

Akiz17:04:35

Still I’m not able to call the things that are defined there and it returns nil.

jpmonettas17:04:25

you don't need to call everything through the nrepl client, you can also connect a clj repl with npx shadow-cljs clj-repl :your-build-id Does it make sense?

Akiz18:04:37

Thank you. So I can find pure CLJC files, but if anything contains CLJ / CLJS, it is not to be found.

Akiz18:04:32

> (require '[pure-cljc-namespace])
nil
> (require '[cljc-namespace-requiring-clj-and-cljs])
Execution error (FileNotFoundException) at ….
Could not locate ….

jpmonettas18:04:28

are the "Could not locate ..." on the classpath?

jpmonettas18:04:22

also you can't require a .cljs file from a clj repl

Akiz18:04:08

It fails on :clj dependencies (I use reader macro in cljc files). I guess that classpath must be configured somewhere outside of deps.edn in this case?

jpmonettas18:04:55

who is creating your classpath? shadow-cljs via its :source-paths and :dependencies or deps.edn via :deps and :paths ?

Akiz18:04:22

via :deps and :paths Now there is a problem just with dependencies, not my own files execution error (FileNotFoundException) at .....eval75518$loading (json.clj:1). Could not locate jsonista/core__init.class, jsonista/core.clj or jsonista/core.cljc on classpath.

Akiz18:04:56

Argh, i see

Akiz18:04:10

I am managing CLJ dependencies via project.clj 🙂

😅 1