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?well the "obvious" answer most likely is that you are looking at the wrong piece of code
more likely that you are passing an incorrect argument to a function somewhere
like where/how are you calling slate?
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]])))
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
(which led me to wonder if there were any compiler settings I could tweak via the shadow config)
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
I will check!
Yep - it's what I expect
@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 ?
@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.
@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.
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.
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.
Unfortunately would need to modify ns
form to add :requires
as well, so I imagine that would make it more difficult to integrate ?
yes, as soon as it affects more than just the form you are processing things get tricky
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
since being able to store the traces and looking at them remotely is probably the hardest part
but nothing practical regarding the tracing is implemented yet
what are the main missing pieces ? instrumenting an entire codebase without manually adding tap ?
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
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
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)
at least for me
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.
yeah dunno how that would look
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.
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 ?
well kind of a bad idea yeah
in :compile-prepare
all dependencies are already resolved. so you cannot add a new require or so for example.
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
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
shadow-cljs will actually get in your way if you try to do that more than cljs.main would
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
all good 👍:skin-tone-2:Thanks for all the info. I will think some more about it.
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?(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)
I can almost guarantee that you are looking at the wrong place
this is almost certainly not a bug in clojurescript or shadow-cljs
it is far more likely that something in your code is either passing some incorrect data or calling a wrong function or so
I cannot possibly help you with that without seeing more code
digging into the tools before you debug your own code is not useful
make sure that all the data you are passing around is exactly what you are supposed to pass around
of course this could be a problem in shadow-cljs or cljs but 99% of the time it isn't
> 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 😂 )
so stop looking at library implementation code and investigate your own code first
this also all appears to be using react hooks so make sure you are actually using a modern reagent version with functional components
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?
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.
But when I “Disable Fast Refresh” in the React Native Debug Menu, the refresh also stops so the app isn’t updated
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
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
the core essential part of the render-root
call. if you do not use that you are on your own and I cannot help.
In fact, I am using render-root
and also added {:dev/after-load true}
but the refresh doesn’t happen
https://code.thheller.com/blog/shadow-cljs/2019/08/25/hot-reload-in-clojurescript.html
check "things to avoid" ... you might be doing one of those
@thheller I have goog.object/get
in a reframe handler. Does that come under “missing :ns require”?
And of course I have js/
Other than that, I’m not using defonce at all
and am using the init-fn
{: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}}
}}}
(defn
init
{:dev/after-load true}
[]
(dispatch [:register-db])
(render-root "Humboi" (as-element [root-comp])))
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
one common issue is storing functions in your app database for example
those are not hot-reloaded and will you will end up calling "old" code instead of the new
(defn
init
{:dev/after-load true}
[]
(render-root "Humboi" (as-element [view])))
Change to this ^^, but still no hot-reloadingI’m not storing any functions in the app database
> this is impossible to debug without seeing more code
add a (js/console.log "foo")
to the init
fn to check if its actually getting called
if so it is all up to your code
no it’s not being called
is the websocket connected?
how does one tell?
log should say so
or open the UI http://localhost:9630/runtimes should be listed there
new metro logs have stopped ever since I turned off live reload in the emulator
Yes there’s one “21 minutes ago”
there must be at least 2? the first #1 is the CLJ runtime
yes, that is missing the react-native runtime. so the websocket is not connected.
how do I connect the websocket?
I assume you have the watch
running so it should connect automatically
if not look for the error message in the log
there’s no error message in the shadow log
not talking about the shadow log
react-native log
the last log is the following:
giving up trying to connect
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
either by setting :local-ip "1.2.3.4"
in your build config or shadow-cljs watch app --config-merge '{:local-ip ...}'
I removed some :devtools from shadow config and it works now. Thanks for your time @thheller 🙂 The periodic websocket disconnects are gone as well.
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!@noorvir.aulakh creating the index.html
is your job. shadow-cljs does not handle html generation.
I tried that too. This is the index.html file I have in the public folder right not:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<link href="/css/styles.css" rel="stylesheet" type="text/css">
</head>
<body class="body-container">
<div id="app"></div>
<script src="js/app.js" type="text/javascript"></script>
</body>
</html>
Am I supposed to call an init method somewhere?with your build config yes
you can swap :entries [<http://noorvir.com|noorvir.com>.core]
to :init-fn <http://noorvir.com|noorvir.com>.core/init
then the code will call that fn on load
how do you handle this in dev? this is not specific to release
? what did your :handler
do?
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}))
so in dev you have a server but in production you don't? I'm confused
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.
Thanks a lot for the help! 🙌 🙏
yeah if you don't do anything server-side then just a regular index.html
and :dev-http {3000 "public"}
is enough
I thought there was a page/app somewhere that allowed to see what js output
unfortunately I can't find
and unfortunately I didn't save
ok, found it