Fork me on GitHub
#shadow-cljs
<
2019-10-24
>
David Pham04:10:22

This is a crazy good new feature!! I will love it. (Can you maybe put colors in pretty print?)

thheller08:10:34

yeah, but more important things first 😛

superstructor09:10:35

Awesome feature. Thanks for your efforts @thheller! 🙂 We'll look at feasibility of direct integration of shadow-cljs in re-frame-10x at some point.

thheller09:10:49

@superstructor although I'm working on this inside the shadow-cljs project for now all the underlying shadow.remote stuff is actually meant to be standalone at some point.

thheller09:10:08

and re-frame-10x could act as a "runtime" itself too

👏 4
thheller09:10:40

but I'll get into more details on that in the future. just wanted to show an early preview 🙂

👍 4
Shuai Lin09:10:31

Anyone here has experience making shadow-cljs working together with dirac ? They seems not "compatible" with each other ...

thheller09:10:03

Currently they are not compatible that is correct yes

thheller09:10:32

I'll happily help anyone that is interested to work on this

thheller09:10:47

but I don't have time to dig into this too much myself for now

thheller10:10:25

I don't know what it needs but currently is makes direct use of the cljs.analyzer API I believe which means shadow-cljs can't intercept anything

Shuai Lin10:10:40

@thheller I have very shallow knowledge about both, AFAIK 1) shadow-cljs has its own nrepl middleware (not using piggieback) to bootstrap a CLJS repl 2) and so does diract - it uses its own implementation Is that the major blocker?

thheller10:10:52

I think so at least. I don't really know what the dirac middleware does

Shuai Lin10:10:08

@thheller IIUC 1. for a normal cljs repl, it's like clj repl -> piggleback mw (or whatever) -> websocket in browser/node. 2. For diract its clj repl -> dirac mw -> dirac agent -> websocket in chrome devtools -> program being debugged

Shuai Lin10:10:04

The thing makes dirac shine is it can evaluate exprs even when a breakpoint is hit and the program suspended, because its eval goes through chrome devtools. While others like shadow-cljs/figwheel can't do that because their eval are based on "user-mode" websocket code which is a no-go when bp is hit Ofc there are other stuff from diract like pretty-printing cljs var names, but this is the biggest sell for me

thheller10:10:15

in theory it should be possible to make this use the shadow-cljs API

thheller10:10:44

but given that it doesn't use any of the default cljs.repl stuff that might be a lot of work

thheller10:10:16

the stuff I'm currently working on may actually be relevant for this but it is a bit too early

Shuai Lin10:10:28

what is it 🙂 ?

thheller10:10:24

the shadow.remote stuff would be relevant for dirac. right now it isn't capable of eval but it will hopefully be soon.

danielneal12:10:55

This looks awesome!!!! Going to try out this afternoon hopefully, think it will be a game changer for react native debugging

thheller12:10:30

I haven't actually tested react-native yet since the test project I had for this doesn't work anymore for some reason

thheller12:10:50

but it should work

souenzzo13:10:22

[SOLVED] I'm trying to require static resources like this https://facebook.github.io/react-native/docs/images#static-image-resources Should I use js/require or import "as a library" in :require from ns? - Use (js/require "./../../resources/img.png")

👍 4
danielneal14:10:47

@thheller works on react native a treat - got our whole re-frame db in there, it’s bloody brilliant!!

🎉 20
👍 4
David Pham14:10:56

Is it possible to write a NodeJS server with Shadow-CLJS?

mruzekw15:10:06

@neo2551 Yes, you want :target :node-script in your build

darwin16:10:55

@thheller I would like to look into integrating dirac with shadow at some point, basically all I need is access to cljs compiler which is used by shadow-cljs - with figwheel I run dirac nrepl middleware in the same JVM as figwheel, so it can query figwheel’s state and use all “builds” and their relevant :compiler-env states

darwin16:10:30

it is a bit fragile and hacky, because I touch internal figwheel’s state, but it works: https://github.com/binaryage/dirac/blob/master/src/nrepl/dirac/nrepl/figwheel.clj

thheller16:10:24

yeah I looked at that in the past but couldn't quite figure out how it all worked together

darwin16:10:37

btw. that code is a bit more involved because I also let user call figwheel repl api through dirac repl command: https://github.com/binaryage/dirac/tree/master/examples/figwheel-main#you-can-control-figwheel-main-via-dirac

thheller16:10:46

do you only need the compiler-env to read or do you actually modify it?

darwin16:10:13

cljs compiler could modify it (and typically will)

darwin16:10:19

e.g. when you def something

thheller16:10:36

yeah thats the main issue. if you use the compiler directly shadow-cljs can't do anything

thheller16:10:11

and shadow-cljs wraps the default compiler to make its additions

darwin16:10:31

well, you have to do it yourself to get compiled js code to be sent to client

darwin16:10:48

dirac must do the same thing, whatever it is

darwin16:10:03

please note that I use compiler API at lower level, because compilation env could be affected by locals on stack, so I don’t only give it cljs code string, but also prepare env with locals on stack (when stopped on a breakpoint)

thheller16:10:02

hmm probably not the approach I would have taken

thheller16:10:58

wouldn't it be easier to just eval (fn [the locals listed as args] (whatever-you-need-to-eval-using locals))

valtteri16:10:05

I wrote a simple health-checker using Serverless Framework and shadow-cljs. https://github.com/vharmain/serverless-healthcheck

thheller16:10:07

so when you eval the generated JS you get a function

thheller16:10:16

that you then call with the locals if you already have them?

thheller16:10:55

no need to mess with the compiler state for that 😛

darwin16:10:48

not sure if this would work well, at least it would mess with stack, in case of another breakpoint in that code, user would see that wrapper function

darwin16:10:28

but :locals is supported cljs state config, why not use it? I have to mess with env anyways, for example to set current ns

darwin16:10:20

that wrapper function could also interfere with return statements maybe, not sure if this would be a real issue

darwin16:10:44

thrown exceptions would have it on stack, etc.

thheller16:10:46

yeah, again I don't really know what you are doing

thheller16:10:05

from that I'm seeing in that file you won't have much luck integrating shadow-cljs

thheller16:10:20

as it pretty much replaces all of the cljs.repl API and doesn't support it

darwin16:10:56

it does what piggieback does, but adds a bunch of tweaks

thheller16:10:19

shadow-cljs doesn't support piggieback either and replaces that itself

thheller16:10:42

nothing from cljs.repl is supported. some of it could be but most of it can't

darwin16:10:13

here it is passing the compiler-env, which it got from figwheel (or created by dirac): https://github.com/binaryage/dirac/blob/master/src/nrepl/dirac/nrepl/eval.clj#L238

thheller16:10:05

none of those things are supported 😛

darwin16:10:27

but at some point you have to use cljs compiler and pass some compiler-env to it, no?

darwin16:10:32

why not exposing it to me?

thheller16:10:30

because it isn't "just" the CLJS compiler

thheller16:10:45

shadow-cljs modifies it. the analyze as well as the "emit"

darwin16:10:24

ok, let me ask differnt way, when you receive user input as string, you have to turn it into js code, and then send it back to client for js eval - turning it into js code could be some fn call [cljs-code-string env] -> js-code-string

darwin16:10:30

why not exposing this api?

darwin16:10:52

in your current impl env is probably always empty, but you can pass-through it for me down to cljs-compiler

thheller16:10:13

I can provide that extremely easily yes

thheller16:10:40

that is indeed how the entire REPL "compiler" code works

thheller16:10:34

but it works a bit differently API wise ...

thheller16:10:41

if you clone the shadow-cljs repo and run lein with-profiles +cljs repl

thheller16:10:07

then (require '[shadow.cljs.repl-test :as x]) then (test-repl-anon-fn)

thheller16:10:26

just for a "sample", this isn't an actual test

thheller16:10:41

but this outputs the "repl state" which will include the generated JS

thheller16:10:19

there is no actual "eval" anywhere the REPL code .. you can process a bajillion things without ever evaling anything

thheller16:10:30

since that doesn't affect the generated code at all

thheller16:10:49

but this API isn't exposed since no one asked for it before

4
thheller16:10:23

all it does it generate instructions which the runtime will then execute

darwin16:10:52

you mean runtime on the client side (js), right?

thheller16:10:57

to save you the time to eval. this is what you'd get back

:repl-actions
 [{:type :repl/invoke,
   :name "<eval>",
   :js "(function (foo){\r\nreturn foo;\r\n})",
   :source "(fn [foo] foo)",
   :source-map-json
   "{\"version\":3,\r\n \"file\":\"<eval>\",\r\n \"sources\":[\"<eval>\"],\r\n \"lineCount\":1,\r\n \"mappings\":\"AAAA,AAAKA;AAAL,AAAUA\",\r\n \"names\":[\"foo\"],\r\n \"sourcesContent\":[\"(fn [foo] foo)\"]}\r\n",
   :warnings []}]}

darwin16:10:20

will be back in 10mins

darwin17:10:01

I could probably use this api, but I would need some additional support. Current ns could be “emulated” by emitting in-ns code, but :locals would be better than wrapping it with fn, also need support for source maps somehow

thheller17:10:34

{:shadow.cljs.repl/repl-state true,
 :current-ns cljs.user,
 :repl-sources
 [[:shadow.build.classpath/resource "goog/base.js"]
  [:shadow.build.classpath/resource "goog/debug/error.js"]
  [:shadow.build.classpath/resource "goog/dom/nodetype.js"]
  [:shadow.build.classpath/resource "goog/asserts/asserts.js"]
  [:shadow.build.classpath/resource "goog/reflect/reflect.js"]
  [:shadow.build.classpath/resource "goog/math/long.js"]
  [:shadow.build.classpath/resource "goog/math/integer.js"]
  [:shadow.build.classpath/resource "goog/string/internal.js"]
  [:shadow.build.classpath/resource "goog/string/string.js"]
  [:shadow.build.classpath/resource "goog/object/object.js"]
  [:shadow.build.classpath/resource "goog/array/array.js"]
  [:shadow.build.classpath/resource "goog/structs/structs.js"]
  [:shadow.build.classpath/resource "goog/functions/functions.js"]
  [:shadow.build.classpath/resource "goog/math/math.js"]
  [:shadow.build.classpath/resource "goog/iter/iter.js"]
  [:shadow.build.classpath/resource "goog/structs/map.js"]
  [:shadow.build.classpath/resource "goog/uri/utils.js"]
  [:shadow.build.classpath/resource "goog/uri/uri.js"]
  [:shadow.build.classpath/resource "goog/string/stringbuffer.js"]
  [:shadow.build.classpath/resource "cljs/core.cljs"]
  [:shadow.build.classpath/resource "clojure/walk.cljs"]
  [:shadow.build.classpath/resource "cljs/spec/gen/alpha.cljs"]
  [:shadow.build.classpath/resource "clojure/string.cljs"]
  [:shadow.build.classpath/resource "cljs/spec/alpha.cljs"]
  [:shadow.build.classpath/resource "goog/string/stringformat.js"]
  [:shadow.build.classpath/resource "cljs/repl.cljs"]
  [:shadow.cljs.repl/resource "cljs/user.cljs"]],
 :repl-actions
 [{:type :repl/invoke,
   :name "<eval>",
   :js "(function (foo){\r\nreturn foo;\r\n})",
   :source "(fn [foo] foo)",
   :source-map-json
   "{\"version\":3,\r\n \"file\":\"<eval>\",\r\n \"sources\":[\"<eval>\"],\r\n \"lineCount\":1,\r\n \"mappings\":\"AAAA,AAAKA;AAAL,AAAUA\",\r\n \"names\":[\"foo\"],\r\n \"sourcesContent\":[\"(fn [foo] foo)\"]}\r\n",
   :warnings []}]}

thheller17:10:15

that is the full repl-state. note the :current-ns. you can just assoc :current-ns 'whatever.sym

darwin17:10:06

ok, :source-map-json looks like what I need

darwin17:10:13

so what about adding :locals support in your repl? IMO you should just pass it through to analyzer

darwin17:10:31

in dirac I inline source maps with generated js

thheller17:10:13

you don't even need that

darwin17:10:41

and maybe something else I’m not aware of right now, I wrote this code 3+ years ago, and I don’t have the context in my head

thheller17:10:10

yeah me throwing random bits of information at you probably doesn't help

thheller17:10:52

write an empty function that you need and I'll provide the implementation

thheller17:10:24

like (defn please-generate-js-for-me [in-this-ns the-cljs-code-as-string])

thheller17:10:58

maybe the extra locals as a map or so

thheller17:10:25

nothing compiler-env related, I can set all that up myself if needed

darwin17:10:52

what happens when cljs compiler throws? e.g. clojure macro throws?

thheller17:10:06

you get the exception?

darwin17:10:26

currently dirac is able to communicate it to client side with a stack trace

darwin17:10:38

also dirac can capture all println stuff done in macros, etc

darwin17:10:45

and send it to client side

thheller17:10:00

then you capture *out*?

darwin17:10:19

yes, but at lower level

darwin17:10:23

at Java level

thheller17:10:00

sorry I don't really want to get into this too deeply now

thheller17:10:09

if you clearly define what you need I can set that up for you

thheller17:10:54

talking directly to the cljs.compiler just isn't going to work since shadow-cljs can't do its stuff that way

darwin17:10:20

ok, will think about it a bit…

thheller17:10:40

but the same stuff is still possible, just accessed differently

Derek17:10:30

What happens to calls to tap> in a production build?

thheller17:10:01

right now they are left as is but don't do anything

thheller17:10:41

you could potentially even strip them from completely using https://shadow-cljs.github.io/docs/UsersGuide.html#_code_stripping

thheller17:10:01

:strip-type-prefixes #{"cljs.core.tap_GT_"}

thheller17:10:13

haven't tried that yet 😛

thheller17:10:40

but I'd like a tap> variant that is automatically removed in release builds

👍 8
thheller17:10:07

(tap> something) will just do nothing so thats totally fine without any overhead

thheller17:10:26

(tap> {:some thing :foor "bar"}) or so would however still construct that map, and then throw it away which is overhead we don't need/want