Hi, I am trying to use https://github.com/vasturiano/force-graph from shadow-cljs and am having a problem. I install the library all right with npm, but when I try to reproduce
import ForceGraph from 'force-graph';
with
(:require ["force-graph" :as fg])
then fg/ForceGraph
is nil (same if I use :refer
)(it works fine if I use it from a <script>
tag)
The error I get is shadow-cljs - [reload failed] module$node_modules$force_graph$dist$force_graph_common.ForceGraph is not a constructor
which makes sense given that force_graph.common does not contain ForceGraph
When you use (:require ["force-graph" :as fg])
, what's the output of (js/console.log fg)
?
A function object that seems to be quite empty
So it's a function. Maybe just (:require ["force-graph" :as ForceGraph])
then?
Alternatively, if that doesn't work, (:require ["force-graph" :default ForceGraph])
(but in this case I would've expected that fg
object from before to have the default
key).
For future reference, this is the relevant imports translation table: https://shadow-cljs.github.io/docs/UsersGuide.html#_using_npm_packages
Note that in a few odd cases it doesn't work, so js/console.log
is still useful.
It worked, thanks ! It's also that I was calling it wrongly 😅
(fg) indeed outputs what I wanted
So was it :as
or :default
?
:as
Starting a mini series on my channel focusing on Clojurescript. Tune in for some laughing and learning you some lisp languages.. That's what I'm all about! 😜✌️ Also open to any requests for tutorials ^~^ https://youtu.be/ZMB-M_XDotE
Hi, I am trying to migrate ClojureScript tests from “Chrome Headless” to jsdom using Karma and shadow-cljs as test runners.
The regular tests which require access to DOM or browser API work fine. But async test where cljs.core.async/go
is used doesn’t work. Basically, nothing inside go
is executed.
Does anyone have some idea what could be wrong? Did I miss some configuration? Is it only jsdom
issue or maybe it is cljs.core.async
interoperability issue?
I have put a simple test example below
(ns async-tests
(:require [cljs.test :refer [deftest async]]
[cljs.core.async :refer [go <! timeout]]))
(deftest async-go-test
(async done
(.log js/console "Before go is printed")
(go
(.log js/console "After go is never printed")
(<! (timeout 1))
(done))))
The result I get in console is
LOG: 'Testing async-tests'
LOG: 'Before go is printed'
WebKit 537.36 (undefined 0.0.0): Executed 159 of 185 SUCCESS (0 secs / 0.589 secs)
WebKit 537.36 (undefined 0.0.0) ERROR
Disconnected, because no message in 30000 ms.
I have create a StackOverflow issue with few more details https://stackoverflow.com/questions/65669171/clojurescript-cljs-core-async-go-doesnt-work-inside-jsdomjs/setTimeout
works fine, it is the first thing I have tried to replace in tests, but later realised that it will not be so good to abandon cljs.core.async
because of jsdom
you could just override whatever the nextTick detection logic does and make it either use setImmediate from node or Promise microtasks or setTimeout. it is the detection that is the problem I think
Thanks, I’ll try that. I think it could be good enough to unblock using of jsdom with cljs.async but probably it would be nice if the problem is fixed in one of the parts of this issue (jsdom or goog.async)
(set! (.. js/window -goog -async -nextTick) js/setTimeout)
does it job and makes cljs.core.async/go
to work
@thheller looking back on workaround you have proposed and which works what is you opinion about this defect. Is it more related to clojure/goog/async
or jsdom
or is it just colliding of tools and it would be hard to fix it inside any of them?
I don't know. I haven't looked at the code of nextTick
in detail. I'd put the blame on jsdom
in case it "pretends" to emulate something when it doesn't
I have created a small Node demo where only jsdom
and google-closure-library
are used https://github.com/Valdermeyder/jsdom-google-closure-library and they work fine together (unless I did some terrible mistake).
My next assumptions that it could be either karma
connector for jsdom
or something related to the clojurescript
ecosystem. I’ll try to create a simple clojurescript
demo some time later to check that
Does (or should) require (the function, not the ns declaration arg) work differently in cljs than cj? When I do something like (require '[clojure.pprint :as pprint])
and then invoke (pprint/pprint "test")
I get a warning in my repl along the lines of:
------ WARNING - :undeclared-ns ------------------------------------------------
Resource: <eval>:1:2
No such namespace: pprint, could not locate pprint.cljs, pprint.cljc, or JavaScript source providing "pprint"
--------------------------------------------------------------------------------
------ WARNING - :undeclared-var -----------------------------------------------
Resource: <eval>:1:2
Use of undeclared Var pprint/pprint
--------------------------------------------------------------------------------
This seems to happen with any dynamically required ns. If I add the required namespace to my ns in the :require block it works. Is this expected behavior?@markbastian "dynamic require" in CLJS is not supported
I'm guessing that the async mechanism used by core.async is either somehow affected by jsdom and doesn't execute at all
https://github.com/google/closure-library/blob/master/closure/goog/async/nexttick.js this is the underlying code doing a bunch of browser detection stuff
maybe jsdom "pretends" that something is there but doesn't actually do anything
is there any way around the dynamic imports issue in a browser context? If a npm dependency is a hard requirement, is there any way to isolate that and work with it in javascript but still have your application primarily built in clojurescript?
thank you for the response. I’ll create an issue for “jsdom”, maybe they can help as well to find the exact reason from their side. I also thought about order of .js files which are loaded by karma but I’m not sure if it makes sense at all
you can use webpack via https://code.thheller.com/blog/shadow-cljs/2020/05/08/how-about-webpack-now.html#option-2-js-provider-external
thank you so much!!!
The ticket to jsdom wouldn’t be so easy. I have to figure out how to reproduce the behaviour without karma and other tools. I’ll see what I can do with that
you could first try to isolate whether it actually is core.async or the nexttick stuff
try
(ns async-tests
(:require [cljs.test :refer [deftest async]]
[goog.async.nextTick]))
(deftest async-go-test
(async done
(.log js/console "Before go is printed")
(js/goog.async.nextTick
(fn []
(.log js/console "After go is never printed")
(done)))))
seems like it is nextTick
I got the same error as before with go
Thanks for pointing that. With nextTick
it should be easier to create a reproducible example as it is js and should run in node environment without all other tools I have right now.
also try with js/setTimeout
just to be sure
Is there a good way to capture the name of a function or it’s namespace when you throw an error inside a go block and propagate it?