shadow-cljs

https://github.com/thheller/shadow-cljs | https://github.com/sponsors/thheller | https://www.patreon.com/thheller
2021-03-21T11:06:05.095700Z

Hey all - I think this is a shadow question: I have integrated slate using shadow's automatic npm imports. I see this error:

Uncaught TypeError: a is undefined
createGenerator shared.js:27
levels index.js:757
levels: function* levels(editor) {
.....
    yield* levels;
  },
Does anyone know if there some shadow setting I have missed? Or something else obvious?

thheller 2021-03-21T11:12:01.096400Z

well the "obvious" answer most likely is that you are looking at the wrong piece of code

thheller 2021-03-21T11:12:19.096800Z

more likely that you are passing an incorrect argument to a function somewhere

thheller 2021-03-21T11:12:48.097200Z

like where/how are you calling slate?

2021-03-21T11:16:25.098600Z

In this particular case, the error occurs when I click the slate widget. This is how I have create the slate component:

(defn text-editor* []
  (r/as-element
    (let [editor (slate-react/withReact (slate/createEditor))
          [value setValue!] (react/useState  [{:type "paragraph"
                                             :children [{ :text "A line of text in a paragraph." }]
                                             }])]
      [:> slate-react/Slate {:editor editor
                             :value value
                             :onChange setValue!}
       [:> slate-react/Editable]])))

2021-03-21T11:20:32.101500Z

I cannot see a call to createGenerator and I notice that the fn above createGenerator in the callstack itself a generator (`yield return..`) - I am guessing google closure does something to insert the call to createGenerator

2021-03-21T11:22:56.102100Z

(which led me to wonder if there were any compiler settings I could tweak via the shadow config)

thheller 2021-03-21T11:23:34.102400Z

and you did verify that editor is actually what you expect? I mean log it or so. I don't have a clue what any of this does but the code looks very suspicious

2021-03-21T11:24:11.102600Z

I will check!

2021-03-21T11:31:47.103100Z

Yep - it's what I expect

superstructor 2021-03-21T12:00:34.106600Z

@thheller Would you consider providing a new build hook that gives access to change/replace forms ? i.e. after the reader step but prior to compilation, ideally before macro expansion ? Is it even a feasible idea or difficult to provide ?

thheller 2021-03-21T13:13:22.108700Z

@jamesmintram I cannot say without an reproducible example. much of the code you posted looks very suspicious though. for example in the react/useState you are using a CLJS datastructure that you then hand to the slate-react/Slate via :value. so unless slate-react is some kind of CLJS wrapper for slate that will likely not work. also the (slate/createEditor) would recreate the editor on every render.

thheller 2021-03-21T13:15:24.110600Z

@superstructor what is the use case? it would be fairly easy to provide such a hook but depending on what it wants to do might be rather tricky to integrate. eg. caching might be affected.

superstructor 2021-03-21T13:16:16.110800Z

The use case is for debugging/tracing tooling without having to insert macros throughout the source code, so to do the transformation instead in dev builds.

superstructor 2021-03-21T13:17:01.111Z

Like, for example adding fn-traced (re-frame-debux) macro to trace all the values of all the forms in all functions throughout an entire codebase; or on specific namespaces with a metadata tag.

superstructor 2021-03-21T13:17:44.111200Z

Unfortunately would need to modify ns form to add :requires as well, so I imagine that would make it more difficult to integrate ?

thheller 2021-03-21T13:22:07.111500Z

yes, as soon as it affects more than just the form you are processing things get tricky

thheller 2021-03-21T13:22:55.111700Z

I spent quite a bit of time thinking about an integrated tracing solution in shadow-cljs myself and the UI and tap> integration is basically the first step in that direction

thheller 2021-03-21T13:23:26.111900Z

since being able to store the traces and looking at them remotely is probably the hardest part

thheller 2021-03-21T13:24:40.112300Z

but nothing practical regarding the tracing is implemented yet

superstructor 2021-03-21T13:26:34.112500Z

what are the main missing pieces ? instrumenting an entire codebase without manually adding tap ?

thheller 2021-03-21T13:29:14.112700Z

well I don't know. I don't really mind adding an extra require and calling something as long as that something doesn't get in the way in release builds and just seamlessly disappears

thheller 2021-03-21T13:30:08.112900Z

so once that exists there could be an extra convenience thing that allows you to skip that extra require and uses metadata or so to control what is traced

thheller 2021-03-21T13:31:06.113100Z

tracing the entire codebase will IMHO give you way too much data you don't really care about. it is more about tracing app semantics (eg. event A -> B -> C) than tracing (+ x 5)

thheller 2021-03-21T13:31:12.113300Z

at least for me

superstructor 2021-03-21T13:32:27.113500Z

yes, agreed. Our main use case is tracing re-frame event handler functions, and probably only namespaces tagged by the developer by metadata. But also wouldn't want to design a re-frame only solution for shadow-cljs, obviously.

thheller 2021-03-21T13:35:21.113700Z

yeah dunno how that would look

superstructor 2021-03-21T13:35:51.113900Z

Ideally, we'd like to be able to • look at the namespace metadata for a ^:re-frame/trace • If it is there, then look for all reg-event-* forms in that ns • Replace all the fn or defn forms in those forms with fn-traced or defn-traced from re-frame-debux • Or, if macroexpansion has already run just do the equivalent of the re-frame-debux macroexpansion inline, but we would still need to refer to some helper fns / global state to store the tracing via requires.

superstructor 2021-03-21T13:37:08.114100Z

In :compile-prepare I see a bunch of files in the build-state . Can one just replace those files in their entirety with a different file (i.e. read the file, modify it, write it out to a tmp location) then return the new tmp files in the build state ? or is that a really bad idea ?

thheller 2021-03-21T13:39:31.114300Z

well kind of a bad idea yeah

thheller 2021-03-21T13:39:51.114500Z

in :compile-prepare all dependencies are already resolved. so you cannot add a new require or so for example.

thheller 2021-03-21T13:42:43.114700Z

but I honestly don't see a problem with the current re-frame tracing stuff apart from how the macros work. you could rewrite those fairly simply so they don't rely on switching classpath dependencies or shadow-cljs :ns-aliases

thheller 2021-03-21T13:47:47.114900Z

but yeah I don't have an answer for you if your goal is to get rid of some extra require/forms and only control it via metadata instead

thheller 2021-03-21T13:48:18.115100Z

shadow-cljs will actually get in your way if you try to do that more than cljs.main would

thheller 2021-03-21T13:49:22.115300Z

some of the stuff shadow-cljs does favors more repeatable/predictable builds so sacrifices some more dynamic aspects of the compilation that would technically be possible

superstructor 2021-03-21T13:53:37.115500Z

all good 👍:skin-tone-2:Thanks for all the info. I will think some more about it.

2021-03-21T14:43:47.115700Z

Thanks. I think I have found something interesting (maybe not!) but inside the function that fails, I see

arguments: undefined
arguments: Arguments(2) [{…}, {…}, callee: ƒ, Symbol(Symbol.iterator): ƒ]
inside of chrome's Scope Local display. Clearly something weird there. Should I be asking about this in the more general clojurescript or could this be some weird compilation/module bundling thing caused by shadow?

2021-03-21T14:45:10.115900Z

(I've updated the code to use r/with-let and clj->js on the maps provided. Strangely, the observed behaviour did not change (I originally though there was some magic happening doing the clj->js conversion behind the scenes)

thheller 2021-03-21T14:50:44.116100Z

I can almost guarantee that you are looking at the wrong place

thheller 2021-03-21T14:50:52.116300Z

this is almost certainly not a bug in clojurescript or shadow-cljs

thheller 2021-03-21T14:51:31.116600Z

it is far more likely that something in your code is either passing some incorrect data or calling a wrong function or so

thheller 2021-03-21T14:51:42.116800Z

I cannot possibly help you with that without seeing more code

thheller 2021-03-21T14:51:51.117Z

digging into the tools before you debug your own code is not useful

thheller 2021-03-21T14:52:25.117200Z

make sure that all the data you are passing around is exactly what you are supposed to pass around

thheller 2021-03-21T14:53:05.117400Z

of course this could be a problem in shadow-cljs or cljs but 99% of the time it isn't

2021-03-21T14:53:27.117600Z

> I can almost guarantee that you are looking at the wrong place > > > Ok. Thanks. I'll pull it into a minimal example. (it will probably start working then 😂 )

thheller 2021-03-21T14:53:32.117800Z

so stop looking at library implementation code and investigate your own code first

thheller 2021-03-21T14:53:59.118100Z

this also all appears to be using react hooks so make sure you are actually using a modern reagent version with functional components

👍 1
zendevil 2021-03-21T15:09:23.120400Z

Not sure if this is a shadow-cljs issue or a react-native issue, but I’ve found that when I make a change in my app and save it, and after the shadow-cljs compiler compiles the code, instead of getting a refresh (live reload is on), I’m seeing the whole js bundle reloading each time. Is there a way to prevent the whole bundle reloading each time?

thheller 2021-03-21T15:10:41.121400Z

that is likely the default built-in react-native reloading mechanism. you should turn that off. no clue how. used to be an option in the react-native menu thingy on the device.

zendevil 2021-03-21T15:11:59.122300Z

But when I “Disable Fast Refresh” in the React Native Debug Menu, the refresh also stops so the app isn’t updated

thheller 2021-03-21T15:12:48.123100Z

shadow-cljs provides it own hot-reloading and you need to turn off all built-in reloading from react-native as they will otherwise interfere

thheller 2021-03-21T15:13:50.124Z

I cannot help you debug hot-reload issues other than point at the example that has it built-in and working https://github.com/thheller/reagent-react-native/blob/master/src/main/test/app.cljs#L33-L39

thheller 2021-03-21T15:14:11.124600Z

the core essential part of the render-root call. if you do not use that you are on your own and I cannot help.

zendevil 2021-03-21T15:16:26.125500Z

In fact, I am using render-root and also added {:dev/after-load true} but the refresh doesn’t happen

thheller 2021-03-21T15:18:49.126700Z

check "things to avoid" ... you might be doing one of those

zendevil 2021-03-21T15:37:37.127400Z

@thheller I have goog.object/get in a reframe handler. Does that come under “missing :ns require”?

zendevil 2021-03-21T15:39:02.127800Z

And of course I have js/

zendevil 2021-03-21T15:39:24.128200Z

Other than that, I’m not using defonce at all

zendevil 2021-03-21T15:39:37.128400Z

and am using the init-fn

zendevil 2021-03-21T15:40:10.128700Z

{:deps true
  :builds
 {:test
  {:target    :npm-module
   :output-dir "test-out"
   :entries [humboi.core-test]}
  :dev
  {:target     :react-native
   :init-fn    humboi.core/init
   :output-dir "app"
   :compiler-options {:closure-defines
                      {"re_frame.trace.trace_enabled_QMARK_" true}}
   }}}

zendevil 2021-03-21T15:40:16.129Z

(defn
  init
  {:dev/after-load true}
  []
  (dispatch [:register-db])
  (render-root "Humboi" (as-element [root-comp])))

thheller 2021-03-21T15:40:58.129800Z

this is impossible to debug without seeing more code. I don't have a clue what the :register-db thing does or the root-comp

thheller 2021-03-21T15:41:28.130200Z

one common issue is storing functions in your app database for example

thheller 2021-03-21T15:41:50.130900Z

those are not hot-reloaded and will you will end up calling "old" code instead of the new

zendevil 2021-03-21T15:41:56.131Z

(defn
  init
  {:dev/after-load true}
  []
  (render-root "Humboi" (as-element [view])))
Change to this ^^, but still no hot-reloading

zendevil 2021-03-21T15:42:49.131500Z

I’m not storing any functions in the app database

thheller 2021-03-21T15:43:54.131900Z

> this is impossible to debug without seeing more code

thheller 2021-03-21T15:44:24.132700Z

add a (js/console.log "foo") to the init fn to check if its actually getting called

thheller 2021-03-21T15:44:37.133Z

if so it is all up to your code

zendevil 2021-03-21T15:45:19.133200Z

no it’s not being called

thheller 2021-03-21T15:45:53.133400Z

is the websocket connected?

zendevil 2021-03-21T15:46:11.133700Z

how does one tell?

thheller 2021-03-21T15:46:35.133900Z

log should say so

thheller 2021-03-21T15:46:58.134300Z

or open the UI http://localhost:9630/runtimes should be listed there

zendevil 2021-03-21T15:47:18.134900Z

new metro logs have stopped ever since I turned off live reload in the emulator

zendevil 2021-03-21T15:48:16.135600Z

Yes there’s one “21 minutes ago”

thheller 2021-03-21T15:48:41.136100Z

there must be at least 2? the first #1 is the CLJ runtime

zendevil 2021-03-21T15:49:30.136400Z

thheller 2021-03-21T15:49:54.137100Z

yes, that is missing the react-native runtime. so the websocket is not connected.

zendevil 2021-03-21T15:50:25.137500Z

how do I connect the websocket?

thheller 2021-03-21T15:50:51.138100Z

I assume you have the watch running so it should connect automatically

thheller 2021-03-21T15:51:00.138400Z

if not look for the error message in the log

zendevil 2021-03-21T15:51:18.138700Z

there’s no error message in the shadow log

thheller 2021-03-21T15:51:27.138900Z

not talking about the shadow log

thheller 2021-03-21T15:51:32.139100Z

react-native log

zendevil 2021-03-21T15:52:04.139400Z

the last log is the following:

giving up trying to connect

thheller 2021-03-21T15:52:34.139900Z

didn't we already talk about the IP stuff before? it might be picking an incorrect IP address so you need to tell it the correct one

thheller 2021-03-21T15:52:58.140500Z

either by setting :local-ip "1.2.3.4" in your build config or shadow-cljs watch app --config-merge '{:local-ip ...}'

zendevil 2021-03-21T15:59:13.141800Z

I removed some :devtools from shadow config and it works now. Thanks for your time @thheller 🙂 The periodic websocket disconnects are gone as well.

noorvir 2021-03-21T16:57:16.147600Z

I’m a newbie so apologies if this is naive: how do you release a reagent app built with shadow-cljs release app ? I create an app template using create-reagent-template. I’m using shadow-cljs with the lein flag set to true. The dev setup works using shadow-cljs watch app but I noticed there’s no index.html file generated. Similarly, when I build the release version, there’s not index.html . I’m failing to understand what is the entry point of the reagent app. I’m trying to deploy to Firebase Hosting but predictably it complains with a 404. Here’s my shadow-cljs.edn :

{:lein         true
 :builds       {:app {:target     :browser
                      :output-dir "public/js"
                      :asset-path "/js"
                      :modules    {:app {:entries [noorvir.com.core]}}
                      :devtools   {:after-load noorvir.com.core/mount-root}}}
 :dev-http     {3000 {:root    "public"
                      :handler noorvir.com.handler/app}}}
Thanks in advance!

thheller 2021-03-21T16:58:45.148100Z

@noorvir.aulakh creating the index.html is your job. shadow-cljs does not handle html generation.

noorvir 2021-03-21T17:00:16.148800Z

I tried that too. This is the index.html file I have in the public folder right not:

<!DOCTYPE html>
&lt;html&gt;
&lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;meta content="width=device-width, initial-scale=1" name="viewport"&gt;
    &lt;link href="/css/styles.css" rel="stylesheet" type="text/css"&gt;
&lt;/head&gt;
&lt;body class="body-container"&gt;
&lt;div id="app"&gt;&lt;/div&gt;
&lt;script src="js/app.js" type="text/javascript"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
Am I supposed to call an init method somewhere?

thheller 2021-03-21T17:00:56.149200Z

with your build config yes

thheller 2021-03-21T17:01:17.149600Z

you can swap :entries [<http://noorvir.com|noorvir.com>.core] to :init-fn <http://noorvir.com|noorvir.com>.core/init

thheller 2021-03-21T17:01:25.149900Z

then the code will call that fn on load

thheller 2021-03-21T17:02:46.150600Z

how do you handle this in dev? this is not specific to release? what did your :handler do?

noorvir 2021-03-21T17:07:27.151200Z

Ok trying this out now. This is the handler that was generated by the template:

(def app
  (reitit-ring/ring-handler
    (reitit-ring/router
      [["/" {:get {:handler index-handler}}]
       ["/items"
        ["" {:get {:handler index-handler}}]
        ["/:item-id" {:get {:handler    index-handler
                            :parameters {:path {:item-id int?}}}}]]
       ["/about" {:get {:handler index-handler}}]
       ["/cards" {:get {:handler cards-handler}}]])
    (reitit-ring/routes
      (reitit-ring/create-resource-handler {:path "/" :root "/public"})
      (reitit-ring/create-default-handler))
    {:middleware middleware}))

thheller 2021-03-21T17:08:19.151600Z

so in dev you have a server but in production you don't? I'm confused

noorvir 2021-03-21T17:38:16.152600Z

I was for serving files for local dev I assumed. https://shadow-cljs.github.io/docs/UsersGuide.html#dev-http It did seem a little odd to me that there should be two different entry points.

noorvir 2021-03-21T17:38:37.153100Z

Thanks a lot for the help! 🙌 🙏

thheller 2021-03-21T18:54:19.153200Z

yeah if you don't do anything server-side then just a regular index.html and :dev-http {3000 "public"} is enough

👍 1
2021-03-21T20:19:33.153900Z

I thought there was a page/app somewhere that allowed to see what js output

2021-03-21T20:19:43.154100Z

unfortunately I can't find

2021-03-21T20:20:02.154300Z

and unfortunately I didn't save

2021-03-21T20:22:06.154500Z

ok, found it