This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-09-08
Channels
- # announcements (40)
- # babashka (14)
- # babashka-sci-dev (7)
- # beginners (50)
- # calva (8)
- # cider (25)
- # clj-kondo (7)
- # cljdoc (8)
- # cljs-dev (14)
- # clojars (6)
- # clojure (56)
- # clojure-australia (1)
- # clojure-berlin (2)
- # clojure-dev (16)
- # clojure-europe (18)
- # clojure-nl (1)
- # clojure-norway (5)
- # clojure-uk (7)
- # clojurescript (100)
- # cursive (57)
- # data-science (9)
- # datomic (6)
- # emacs (11)
- # figwheel (2)
- # fulcro (14)
- # helix (2)
- # hyperfiddle (9)
- # introduce-yourself (1)
- # lsp (20)
- # malli (14)
- # meander (34)
- # minecraft (1)
- # missionary (8)
- # off-topic (37)
- # pedestal (4)
- # polylith (18)
- # portal (3)
- # re-frame (5)
- # ring (33)
- # shadow-cljs (32)
- # spacemacs (6)
- # vim (16)
Has anybody here tried using shadow-cljs's ESM output with https://razzlejs.org? They have examples for being used with other compile-to-js languages like https://github.com/jaredpalmer/razzle/tree/master/examples/with-reason-react and https://github.com/jaredpalmer/razzle/tree/master/examples/with-elm, but I think the whole Google Closure thing that thkeller talks about https://github.com/thheller/shadow-cljs/blob/master/doc/esm.md is getting in the way again lol... Things seem to work fine if I just write components in pure CLJS (I'm using https://github.com/lilactown/helix), but interop with JS React libraries that use hooks doesn't work. The browser says that the hook is called improperly either because 1. it's' not used in a function component, 2. it's violating the rules of hooks, or 3. there are multiple copies of react in existence. The same code with :browser target without going through Razzle just works though, so I suspect it's somehow the "multiple copies of react" thing.
not a clue what razzle
is but it might be expecting to build the output and as such gets confused by shadow-cljs already doing that. see https://shadow-cljs.github.io/docs/UsersGuide.html#_third_party_tool_integration
Razzle is a SSR library like Next or Remix, but without the whole framework attached. The js-provider option did fix the hooks thing. Thanks!
Another question though: does shadow-cljs watch
inject code into the output? And is there a way to change this behavior? I get all kinds of issues with the framework trying to run what seems to be stuff watch
is doing in the browser on node lol, like saying WebSocket() is undefined
you can set :devtools {:enabled false}
or set :runtime :custom
to stop that (in the build config)
is there a macro to create a go-block and a channel in one operation?
preferably coupled with a return macro or something like that
also whats the convention for channels that should just block until all operations are done and not pass any information
> is there a macro to create a go-block and a channel in one operation?
Hard to understand what you mean. What would the macro expand to?
Also, just in case - go
itself creates a channel within it that it returns to the caller.
> also whats the convention for channels that should just block until all operations are done and not pass any information
Just return something that's not nil
. Maybe true
- up to you.
Or close!
that channel - that's what go
does when its body results in nil
.
sorry was away for dinner - aha! so how do i put stuff on the channel that go creates?
That would be the result of the go
's contents - you don't put anything there explicitly.
okay so if i just put true on the last line or even a <! or <p! that will be put on the channel?
wonderful!
while i'm at it I have another question about core async
i have a function that is called by a framework
the framework expect my function to return a promise
the framework is firebase cloud functions
to accomplish this i do this
(def go-idkollen-se-callback
(functions/https
(fn [req res]
(js/Promise.
(fn [_resolve _reject]
(go
(try
now i can do the actual thing i want to do beneath the try
when im done i call resolve or if i catch an error i forward it to reject
basically, i wonder if i can convert the channel that is created by the go block into a promise
Pretty sure your current solution is the way to go, assuming you even need core.async
.
i do a lot of async operations in my cloud function
thats why im converting my code from nestled promises
Is there a way to pre-inject or monkey patch code in ClojureScript in any sensible way? I'm struggling with a strange figwheel issue (although this isn't a FW question directly) where goog.debug.Logger
is not in the expected place. (the desired object in the version I am using is in goog.log.Logger
). One workaround is while debugging I can set goog.debug.Logger = goog.log.Logger
in the javascript console. Curious if anyone can think of a good way to monkeypatch this in.
Also, yes, I realize this is probably not the right way to solve this. (while the correct solution is also welcome, I'm specifically asking about the technique of monkeypatching javascript code before the user submitted code executes -- because by then it will be too late)
(turns out the real answer was just a version conflict)
Hi, I'm trying to use nbb (https://github.com/babashka/nbb/blob/main/examples/puppeteer/example.cljs)to write a script to migrate mysql data using https://github.com/sidorares/node-mysql2#using-promise-wrapper (snippets above). I have it working with JavaScript, but I'm having trouble translating to CloureScript.
Ok! I will post there next time. Thanks!
I'm looking at your example now. The confusion probably is that for
in clojure is not an imperative loop like .forEach
in JS
for
generates a lazy seq of results. if you want to cause side effects, you can use doseq
Ok, I will start down the doseq
path first!
I wasn't sure if I needed to use another utility from promesa.
you could maybe use chain
+ map
to create functions that are executed serially, but this isn't what the JS example is doing either
Oh interesting, I wil look at that.
doseq
works though. It looks like that was all I needed to change.
Is this the best approach to deal with async code like this or is there a more idiomatic Clojure way?
(apply p/chain (map (fn [i] #(p/do (println "Waiting" i "seconds") (p/delay (* i 1000)))) (range 5)))
Hmm, I would just use async/await, but it doesn't look like that is easily available in ClojureScript.
True. It is available in #cherry (experimental CLJS compiler) and #clavascript (experimental CLJS syntax to JS compiler)
Got it. I'll try that too and also check out the compilers you mentioned. Appreciate it!
This would probably also work:
(p/all (map (fn [table] (.execute conn ...)) table-names)
(read-string (pr-str (js/Error. "my specific error"))) this fails, and passing a :default doesn't help because it fails before defaults happen, I guess because it interprets the inside as an invalid symbol, is there some workaround and is this like expected behaviour?
❯ clj -A:cljs -M -m cljs.main -re node -r
ClojureScript 1.10.773
cljs.user=> (js/Error. "my specific error")
#object[Error Error: my specific error]
cljs.user=> (pr-str (js/Error. "my specific error"))
"#object[Error Error: my specific error]"
cljs.user=> (read-string (pr-str (js/Error. "my specific error")))
WARNING: Use of undeclared Var cljs.user/read-string at line 1 <cljs repl>
Execution error (TypeError) at (<cljs repl>:1).
Cannot read properties of undefined (reading 'call')
there is no such thing as read-string
in ClojureScript. But also what you want to read is not readable: #object[Error Error: my specific error]
is not a readable formcljs.user=> (require 'cljs.reader)
nil
cljs.user=> (cljs.reader/read-string (pr-str (js/Error. "my specific error")))
Execution error (ExceptionInfo) at (<cljs repl>:1).
Invalid symbol: Error:.
Yes, but it's what errors seem to serialize to by default, sorry I should have declared the import
the error is a javascript native object. I don’t think it serializes at all. That’s just a string representation of it
how would you do this in pure js? ie, make an error, and then eval it to get back an error object
I get you but a lot of the time in cljs you can just write fairly arbitrary stuff to edn and pull it back out with then defaulttagreader, it seems weird to me that a "primitive" would cause an error, but I just wanted to check that I'm not doing anything mad and this is just what happens
I guess I need to find the hook for pr-str for error, and modify that then at least these datatructures will lossy roundtrip without error
You could hack on the printer or you could walk your datastructure and replace them with clojure data
it does say that they are serializable. I don’t see how to render to a string and back. But if you can figure that out you can walk and turn them into some special data structure that includes the serialized form. And then on the other side can rehydrate them with a walk that undoes the serializing side
Interop question. I’m trying to port a simple https://reactflow.dev/docs/examples/overview/ example to cljs. One of its features is custom styling. Here’s the js example:
export const nodes = [
{
id: '1',
type: 'input',
data: {
label: (
<>
Welcome to <strong>React Flow!</strong>
</>
),
},
position: { x: 250, y: 0 },
}]
This provides an HTML-styled node as shown.
I can’t seem to get this interop working in cljs. I’ve tried something like the following:
{:id "2"
:data {:label "<div>Default <strong>Node</strong></div>"}
:position {:x 100 :y 125}}
This data eventually goes through a clj->js transform and the node just displays the string (with div). I’ve also tried hiccup-style input, which doesn’t work. Anyone know how to make this work?>
data: {
> label: (
> <div>
> Welcome to <strong>React Flow!</strong>
> </div>
> ),
> },
This is JSX, not JS. The (<div>...</div>)
part gets transpiled into a React Component. So your library is expecting a React Component here, not a string of html.I would try this:
{:id "2"
:data {:label (reagent/as-element [:div "Default Node"])}
:position {:x 100 :y 125}}
Does that survive the cljs->js conversion?
Documented function forms are
(element tag-or-text)
(element tag & children)
So using the first form (`tag-or-text`), with div
as a tagname, and a target of an empty div element, I can came up with these options, which both fail.
(element "div")
Interprets as text instead of tag.
Returns a text node containing string "div".
(element :div)
Throws:
Execution error (Error) at (<cljs repl>:1).
No protocol method DOMBuilder.-element defined for type cljs.core/Keyword: :div
I'm even more confused after trying to make sense of the source. https://github.com/clojure/clojurescript/blob/r1.11.60/src/main/cljs/clojure/browser/dom.cljs#L13-L15
(extend-protocol DOMBuilder
string
(-element
([this]
(log "string (-element " this ")")
(cond (keyword? this) (gdom/createElement (name this))
:else (gdom/createTextNode (name this))))
...
defn element
([tag-or-text]
(log "(element " tag-or-text ")")
(-element tag-or-text))
...
Seems we have a protocol providing a -element
function, which accepts one of string
, PersistenVector
, or js/Element
. In the case of string
it tests if it is a keyword
. But how can something be a keyword if we already know it is a string?
For good measure I tried (dom/element ":div")
, which unsurprisingly just gave me a text node.you probably want to look for other ways to work with the DOM. I doubt anyone uses clojure.browser.dom and its not exactly "maintained"
if you ask me these namespaces should have been removed a decade ago. but they are staying for historic reasons just in case someone might be using them. if you ask me nowadays its better to look elsewhere.
the test for keyword is there for historic reasons as well. very long time ago keywords were just represented as a string and didn't have their own type
A deprecation notice sure would be nice, if they're just going to abandon something, but still ship the eroding code.
Any advice for chunking cljs_base.js
into smaller files? The bulk of the size comes from the webpack index.bundle.js
file. I'm using code-splitting to get the rest of the size reasonable but it's still wild to make users load 5.5 MB before the first paint
I am not too up on the latest in javascript development, but I have a little toy app with some clojurescript which uses reagent. with the :optimizations :advanced
option to the cljs compiler the output is a single js file that is 0.4 megabytes
That is with advanced compilation 😅 In this case I'm using code splitting [1], which is why there are 4 files.
:modules { :main {:entries #{app.core}
:output-to "resources/public/js/compiled/app.js"}
:init {:entries #{app.lib.actions.init}
:output-to "resources/public/js/compiled/init.js"}
:dom {:entries #{app.dom}
:output-to "resources/public/js/compiled/dom.js"}
:cljs-base {:output-to "resources/public/js/compiled/cljs_base.js"}
}
The problem is that a lot of the npm code being used in the app takes up like 4.5 MB by itself, and all that crap gets stuffed into the cljs-base
module. But a lot of it isn't needed initially, so I'm trying to figure out how to defer loading it until after the first paint -- or to break it into smaller chunks that are more TCP friendly.
[1] https://clojurescript.org/guides/code-splittinghttps://github.com/clojure/clojurescript-site/commit/8affedc604c88dcab75398c1454c390d34df3a7b implies you can I think
The screenshot I showed you was with advanced compilation. The files are much larger without advanced compilation. One of the problems is that the npm code itself takes up 4.9M -- and this has already been compressed by webpack.
the npm code is included into that cljs_base.js
file from earlier via advanced compilation. So only 0.6 MB accounts for the app code, while 4.9MB (presumably) is from dependencies.
Now the thing is, a good portion of that code is not needed on page load. The majority of it could be loaded asyncronously on-demand (from a dependency ordering perspective). I'm just not sure how to accomplish this technically yet.
you could use a shadow-cljs flag to look at the output of the build report and see what's taking up space.
Without knowing too much about your app, it seems like you might want to employ more aggressive code-splitting. Maybe consider splitting by routes if that is an option or splitting out the components w/ heavy dependencies into their own files
https://github.com/jjtolton/rocinante/blob/master/src/clj/new/rocinante/src/js/index.js more or less @UGGU8TSMC
Antd and materialui are pretty hefty
Yeah going with aggressive code splitting and side loading the npm deps on demand instead of advanced compiling them
Is this a bug?