clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
neilyio 2020-11-19T00:39:55.269Z

I'm trying to set a custom error handler in the browser... but setting window.onerror doesn't seem to take effect right away. Does anyone know why? Here are two cases:

(defn error-handler [message url line column e]
  (js/console.log "MY CUSTOM ERROR MESSAGE")
  (js/console.error e)
  true)

(set! (.-onerror js/window) error-handler)

(defn fail []
  (throw (ex-info "Big error" {})))

(fail) ;; error-handler is not run.
outputs:

neilyio 2020-11-19T00:41:35.270500Z

But delaying the call to fail with setTimeout produces the expected result:

(defn error-handler [message url line column e]
  (js/console.log "MY CUSTOM ERROR MESSAGE")
  (js/console.error e)
  true)

(set! (.-onerror js/window) error-handler)

(defn fail []
  (throw (ex-info "Big error" {})))

(.setTimeout js/window fail 3) ;; error-handler is run!
outputs:

thheller 2020-11-19T08:48:58.282800Z

shadow-cljs catches errors during load so it never gets to your onerror. the settimeout takes it out of the load phase and therefore your handler triggers. never throw during load 😉

p-himik 2020-11-19T11:13:56.283400Z

As I mentioned - for some reason, it works for me.

(ns clj-playground.core)

(defn error-handler [message url line column e]
  (js/console.log "MY CUSTOM ERROR MESSAGE")
  (js/console.error e)
  true)

(set! (.-onerror js/window) error-handler)

(defn fail []
  (throw (ex-info "Big error" {})))

(fail)
gives
main.js:2226 failed to load clj_playground.core.js #error {:message "Big error", :data {}}
env.evalLoad @ main.js:2226
(anonymous) @ main.js:2393
core.cljs:4 MY CUSTOM ERROR MESSAGE

p-himik 2020-11-19T11:18:02.283700Z

Updated shadow-cljs to 2.11.7 - still works, although e is now rendered differently in the console.

p-himik 2020-11-19T11:40:57.283900Z

Also, what if it's not my code that throws an error during load but something that I :require? I think wanting to be able to instrument such scenarios is a perfectly valid thing.

neilyio 2020-11-19T16:23:09.302200Z

That's exactly what I was trying to do, it was a whole headache to try and figure out what was going on... thanks for clarifying @thheller

thheller 2020-11-19T16:24:23.302400Z

you can use :devtools {:loader-mode :script} then shadow-cljs won't catch errors

thheller 2020-11-19T16:24:31.302600Z

release builds also never catch any errors

thheller 2020-11-19T16:24:46.302800Z

but with that said ... throwing during load is a really really bad idea so don't do it 😛

neilyio 2020-11-19T16:37:05.303Z

Agreed, it's more just a development workflow thing. Sometimes it can help if I'm debugging something strange. Glad there's an option for it.

p-himik 2020-11-19T00:47:43.271Z

It works for me just fine without setTimeout. No idea why it would work for you differently. BTW you can use js/setTimeout instead of (.setTimeout js/window ...).

p-himik 2020-11-19T00:56:50.271300Z

Huh, but if I try the analogous JS code in a JS console I get the behavior that you see for some reason:

window.onerror = (message,url,line,column,e) => {console.log('my handler'); console.error(e);}

throw new Error('hi')
// Uncaught Error: hi

setTimeout(() => {throw new Error('hi')}, 0)
// my handler
// Uncaught Error: hi

neilyio 2020-11-19T00:58:13.271500Z

Thanks for the tip about setTimeout. I have no idea what's going on either... but glad I'm not crazy.

p-himik 2020-11-19T00:59:18.271700Z

Well at least there's an explanation for what I see in JS: https://stackoverflow.com/a/17534068/564509 Also the comments are interesting - try using (js-debugger) to see the call trace.

pinealan 2020-11-19T02:14:26.277100Z

How/where in source code is persistent data structure implemented in clojurescipt? I’ve tried cljs website faqs and googling and neither showed in-depth explanations. Any pointers are appreciated!

dpsutton 2020-11-19T02:20:25.277200Z

you can see the implementation of clojurescript's persistenthashmap here: https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L7879

pinealan 2020-11-19T02:24:03.277500Z

Thanks!

dpsutton 2020-11-19T02:24:56.277700Z

you're welcome. it can be pretty dense at first

pinealan 2020-11-19T02:25:18.277900Z

Also do I understand correctly that much more of cljs in implemented in itself than Clojure? That’s the feeling I got from skimming the cljs.core namespace, where I can see a lot of language feature protocols are defined, whereas in the Clojure counterpart they are in Java

dpsutton 2020-11-19T02:25:43.278100Z

and this can be helpful

dpsutton 2020-11-19T02:28:00.278700Z

yeah Rich Hickey (the creator) wrote about this in the history of clojure paper (which is an excellent read). Clojurescript is a "clojure in clojure" kinda language that he felt really demonstrated how good the choices made in the language was

dpsutton 2020-11-19T02:29:41.278900Z

pinealan 2020-11-19T02:30:09.279300Z

ya I’ve read that one, was always wondering to what degree is clojurescript “written in itself” thanks a lot for pointing me to the source code, it explains a lot

dpsutton 2020-11-19T02:30:31.279500Z

dpsutton 2020-11-19T02:30:46.279900Z

ah ok. then i won't keep sending screenshots. sorry about that 🙂

dpsutton 2020-11-19T02:31:18.280100Z

yeah no nasty interfaces in clojurescript. you get to see the protocols

pinealan 2020-11-19T02:31:38.280300Z

lol no worries, love your enthusiasm

Jason 2020-11-19T12:18:35.284700Z

Is there a reason that clojure every is not defined in clojurescript?

thheller 2020-11-19T12:19:34.284900Z

Clojure 1.10.1
user=> every
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: every in this context

thheller 2020-11-19T12:19:38.285100Z

you mean every?

Jason 2020-11-19T12:22:02.285800Z

whoops. i got the wrong clojure every

Jason 2020-11-19T12:37:25.291100Z

Pardon my cljs newbness, but I'm trying to make sure that all the members of set-a are in set-b. every? is not the answer. I then thought of subset from clj but it's not in cljs. Am I missing something?

lukas.rychtecky 2020-11-19T12:38:18.291400Z

Do you mean this function? clojure.set/subset?

Jason 2020-11-19T12:38:48.291800Z

yes

Jason 2020-11-19T12:39:51.292700Z

is that in the cljs clojure.core? It's not listed in the API docs

lukas.rychtecky 2020-11-19T12:40:34.293100Z

Jason 2020-11-19T12:40:40.293400Z

ah, but i see it in the source

lukas.rychtecky 2020-11-19T12:41:08.293800Z

It’s in set namespace

Jason 2020-11-19T12:43:06.294700Z

yes, just as it is in clj, but it's not listed in https://cljs.github.io/api/ which confused me

lukas.rychtecky 2020-11-19T12:43:46.294900Z

I see it https://cljs.github.io/api/clojure.set/#subsetQMARK

Jason 2020-11-19T12:45:58.296400Z

as i said above, once i thought to look in the source, i saw it. thank you for responding

👍 1
xceno 2020-11-19T13:50:44.298100Z

Can I completely disable any optimizations the google closure compiler does to a single npm-dependency when using shadow-cljs? It somehow destroys a function call inside a web worker, and it seems I don't have any other options left right now

2020-11-19T13:58:17.299400Z

I’m not sure I understand your issue, but is it something you could resolve by downloading the source to your npm dependency and building it locally?

xceno 2020-11-19T15:29:58.300900Z

Well my issue is a bit weird, but here are the details: I use a library that converts a function to a string, so it can instantiate a web-worker: https://github.com/antvis/Graphin/blob/a87b8f64e2303aad42c94a72d2dd861e1a7ea234/packages/graphin/src/layout/basic/forceWithWorker.ts#L70 What happens is, that this function here: https://github.com/antvis/Graphin/blob/a87b8f64e2303aad42c94a72d2dd861e1a7ea234/packages/graphin/src/layout/basic/worker.ts#L6 Gets converted/compiled to the following code:

(... Some other stuff);function(){onmessage=...
So the worker instantly crashes, because this top level function has no name. The error is: "Uncaught SyntaxError: Function statements require a function name"

xceno 2020-11-19T15:32:55.301400Z

I've tried it with webpack in a typescript project and can't replicate the issue there. In my clj project I've also played around with externs, reducing compiler-optimizations, etc. but to no avail. Therefore I came to the conclusion that something else in the closure compiler must be the issue, but I can't figure it out

thheller 2020-11-19T15:55:26.301600Z

@rob703 it appears the library does some runtime eval so it is likely that it calls something that was renamed and not expecting it to be minified/renamed. hard to say but there is no way to disable optimizations

p-himik 2020-11-19T17:06:33.303200Z

But maybe it's possible to at least add some externs for the stuff that eval expects to be there.

xceno 2020-11-19T17:42:54.303600Z

I thought the same about externs, but since it's an anonymous function there's not much to do in term of named externs. Anyway, thanks guys! I'll try to figure something out, it may just end up as yet another workaround. Maybe I compile it separately and load it on app start or something like that

devn 2020-11-19T20:14:24.304200Z

barebones hot reload: what are people using these days?

2020-11-19T21:06:33.304700Z

figwheel-main is pretty minimal.

devn 2020-11-19T21:12:53.304800Z

kind of where i was headed, thanks

devn 2020-11-19T21:13:36.305Z

i briefly had the “well, a filesystem watcher that calls a build script…” thought

devn 2020-11-19T21:13:39.305200Z

and then thought better of it

dominicm 2020-11-19T21:16:24.305400Z

There's a little more to it annoyingly :)

dominicm 2020-11-19T21:16:49.305600Z

I think you could make a lighter figwheel, but nobody really wants to! (it's a lot of work)