I looked at the remote.md doc, studied its code and briefly glanced over shadow-cljs code.
I'd like to propose a possible way how to make Dirac play with Shadow.
Initial assumptions:
shadow.remote relay running.dirac-tool is a shadow.remote tool connecting to the relay.Typical Dirac workflow:
dirac in a new terminal sesssiondirac launches Chrome with Dirac DevTools, it also starts playground project in the backgrounddirac JVM processdirac-tooldirac-tool will attempt connection to the relay and list available runtimes supporting cljs compilationdirac :lsdirac :ls with figwheel-main "ClojureScript compiler" present, it is selected as indicated by ->, btw. dirac/f270d0e8.1 is the default compiler provided by dirac, also added shadow/build1 as an example.> dirac :ls
Listing all Dirac sessions currently connected to your nREPL server:
-> #1 figwheel-main | http://localhost:9500/ | Chrome/81.0.4028.0 | Mac/10.15.3 [f270d0e8]
Listing all ClojureScript compilers currently available in your nREPL server:
#1 dirac/f270d0e8.1
-> #2 figwheel.main/dev
#3 shadow/build1
dirac :switch, assume he switched to some shadow runtime (e.g. dirac :switch "shadow/build1").shadow.remote api.Please note: Dirac nREPL and Dirac Agent are running in a separate process (of dirac) and are not in any way interfering with the initial shadow-cljs setup.
dirac-tool needs following services from (supported) runtimes:
:list-cljs-compiler-runtimes:compile-with-cljs-compiler-runtime:shadow-bridgeThis is a message for relay? Or it could be a broadcast and each runtime supporting :compile-with-cljs-compiler-runtime can answer independently?
In the first case it could look like this:
{:op :list-cljs-compiler-runtimes}
; >>>
{:cljs-compiler-runtimes [
{:name "build1" :id 123}
{:name "build2" :id 456}
]}
These runtimes would be presented in dirac :ls as
Listing all ClojureScript compilers currently available in your nREPL server:
-> #1 shadow/build2
#2 shadow/build1
Dirac needs to pass in the cljs code text and also optional :locals and maybe other compiler-env settings, this should be passed through to cljs.analyzer/analyze.
Dirac expectes compiler js code text back with source mapping info - or alternatively some error info which should be presented to the user.
{:op :compile-with-cljs-compiler-runtime
:runtime-id 123
:code-text "(+ 1 2)"
:compiler-env {
:locals { ... }
...
}
}
; >>> success
{:result :success
:compilation-feedback [
{:kind "warning" :text "some compilation warning"}
...
]
:ns <effective-ns-symbol>
:js "js code text"
:js-source-map "json text"}
; >>> or in case of a failure
{:result :compilation-error
:ex "exception message"
:root-ex "root exception message if avail"
:details <serialized stack trace somehow>
}
Dirac sends a command as plain text. And shadow cljs will interpret it on its side. Potentially returning some output.
{:op :shadow-bridge
:command "watch build3"}
; >>>
{:result :success
:feedback [
{:kind :stdout "something printed"}
{:kind :stderr "something printed on stderr"}]
}
A note on stdout/err feedback. Dirac has currently infrastructure to stream back stdout/err produced during compilation (or other operations) back to DevTools console. It would be nice to support this as well. So for example :compile-with-cljs-compiler-runtime could trigger multiple reponses some of them could be just reporting printed output back.
@thheller If you agree with this concept, I would propose following steps:
shadow.remote)Just for record. Dirac 1.6.0 added support for REPL via cljs_eval:
https://github.com/binaryage/dirac/releases/tag/v1.6.0
This might be good enough solution for now.
@darwin A big 馃憤 for being able to see cljs variables in the variables list panel, really helpful when debugging!
I find it's not possible to eval cljs code when paused in a breakpoint - I guess it's an intrinsic limitation of the "loopback" mode?
> (+ 1 2)
VM1493:1 Uncaught SyntaxError: await is only valid in async function
I'm not 100% sure but I would say that it is not intrinsic limitation. When page is stopped on a breakpoint dirac uses special debugger api for evaluating expressions in the context of the page. Maybe I just need to enable some flag to allow async code as well. I noticed that there is relative new support in devtools console for top-level await expressions. Maybe I would need to do something similar.
Thanks @darwin . I just found your doc page https://github.com/binaryage/dirac/blob/master/docs/about-repls.md and it's really great read about the design of dirac and clojure repls as well.
@darwin IIUC this doesn't seem to be able to work. cljs_eval('"foo"') sends the content to shadow-cljs server (through the websocket connection) to parse, which the server compiles down to js code and sends it back to the page to execute. When the page is paused by a breakpoint, there is no way for this to happen.
What's more shadow-cljs server sends ping requests to the client and in a breakpoint the client is not able to respond with a pong, so the server would disconnect the client.
Good points. Let's move the discussion to dirac issues.
Most helpful comment
Just for record. Dirac 1.6.0 added support for REPL via
cljs_eval:https://github.com/binaryage/dirac/releases/tag/v1.6.0
This might be good enough solution for now.